diff options
author | Simon Sapin <simon.sapin@exyr.org> | 2014-08-16 12:24:34 +0100 |
---|---|---|
committer | Simon Sapin <simon.sapin@exyr.org> | 2014-08-16 12:24:34 +0100 |
commit | 444ff8425922d350120eb5412c96c26060b55c88 (patch) | |
tree | 53d7e8a5d5c163d084117944bd3fdd4eb71de13d | |
parent | ba592364b7655b66a4d384e8deabcde88755825f (diff) | |
parent | 4ca385ba10faeaf32b5aeb2cbd293542c8d8a3c9 (diff) | |
download | servo-444ff8425922d350120eb5412c96c26060b55c88.tar.gz servo-444ff8425922d350120eb5412c96c26060b55c88.zip |
Merge pull request #3078 from SimonSapin/style-cleanup
Various refactoring and fixes in the style crate
23 files changed, 855 insertions, 881 deletions
diff --git a/Makefile.in b/Makefile.in index 1737027c30f..cd066e6b309 100644 --- a/Makefile.in +++ b/Makefile.in @@ -348,7 +348,7 @@ RFLAGS_style = $(addprefix -L $(B)src/,$(DEPS_SUBMODULES)) -L $(B)src/components MAKO_ZIP = $(S)src/components/style/Mako-0.9.1.zip MAKO_style = $(S)src/components/style/properties/mod.rs MAKO_SRC_style = $(MAKO_style).mako -SRC_style = $(call rwildcard,$(S)src/components/style/,*.rs) $(call rwildcard,$(S)src/compontents/style/properties/*.rs) $(MAKO_style) +SRC_style = $(call rwildcard,$(S)src/components/style/,*.rs) $(call rwildcard,$(S)src/compontents/style/properties/*.rs) $(MAKO_style) $(S)src/components/style/user-agent.css CRATE_style = $(S)src/components/style/style.rs DONE_style = $(B)src/components/style/libstyle.dummy @@ -364,7 +364,7 @@ DEPS_layout_traits = $(CRATE_layout_traits) $(SRC_layout_traits) $(DONE_script_t RFLAGS_layout = $(addprefix -L $(B)src/,$(DEPS_SUBMODULES)) -L $(B)src/components/gfx -L $(B)src/components/util -L $(B)src/components/net -L $(B)src/components/script -L $(B)src/components/style -L $(B)src/components/msg -L$(B)src/components/macros -L$(B)src/components/layout_traits -L $(B)src/components/script_traits -SRC_layout = $(call rwildcard,$(S)src/components/layout/,*.rs) $(S)src/components/layout/css/user-agent.css +SRC_layout = $(call rwildcard,$(S)src/components/layout/,*.rs) CRATE_layout = $(S)src/components/layout/layout.rs DONE_layout = $(B)src/components/layout/liblayout.dummy diff --git a/mk/check.mk b/mk/check.mk index efb275919f3..0431ac6f190 100644 --- a/mk/check.mk +++ b/mk/check.mk @@ -29,7 +29,7 @@ servo-test-$(1): $$(DEPS_$(1)) .PHONY: check-servo-$(1) check-servo-$(1): servo-test-$(1) @$$(call E, check: $(1)) - $$(Q)./servo-test-$(1) + $$(Q)./servo-test-$(1) $(TESTNAME) endef $(foreach lib_crate,$(SERVO_LIB_CRATES),\ diff --git a/src/components/gfx/font_cache_task.rs b/src/components/gfx/font_cache_task.rs index 88455f92b2e..8444fead414 100644 --- a/src/components/gfx/font_cache_task.rs +++ b/src/components/gfx/font_cache_task.rs @@ -70,7 +70,7 @@ impl FontFamily { /// Commands that the FontContext sends to the font cache task. pub enum Command { GetFontTemplate(String, FontTemplateDescriptor, Sender<Reply>), - AddWebFont(Vec<Url>, String, Sender<()>), + AddWebFont(String, Url, Sender<()>), Exit(Sender<()>), } @@ -105,21 +105,19 @@ impl FontCache { result.send(GetFontTemplateReply(font_template)); } - AddWebFont(urls, family_name, result) => { - for url in urls.iter() { - let maybe_resource = load_whole_resource(&self.resource_task, url.clone()); - match maybe_resource { - Ok((_, bytes)) => { - if !self.web_families.contains_key(&family_name) { - let family = FontFamily::new(); - self.web_families.insert(family_name.clone(), family); - } - let family = self.web_families.get_mut(&family_name); - family.add_template(format!("{}", url).as_slice(), Some(bytes)); - }, - Err(msg) => { - fail!("{}: url={}", msg, url); + AddWebFont(family_name, url, result) => { + let maybe_resource = load_whole_resource(&self.resource_task, url.clone()); + match maybe_resource { + Ok((_, bytes)) => { + if !self.web_families.contains_key(&family_name) { + let family = FontFamily::new(); + self.web_families.insert(family_name.clone(), family); } + let family = self.web_families.get_mut(&family_name); + family.add_template(format!("{}", url).as_slice(), Some(bytes)); + }, + Err(msg) => { + fail!("{}: url={}", msg, url); } } result.send(()); @@ -264,9 +262,9 @@ impl FontCacheTask { } } - pub fn add_web_font(&mut self, urls: Vec<Url>, family: &str) { + pub fn add_web_font(&mut self, family: String, url: Url) { let (response_chan, response_port) = channel(); - self.chan.send(AddWebFont(urls, family.to_string(), response_chan)); + self.chan.send(AddWebFont(family, url, response_chan)); response_port.recv(); } diff --git a/src/components/layout/css/matching.rs b/src/components/layout/css/matching.rs index 3c7ee0d9a4c..c1d766c32ca 100644 --- a/src/components/layout/css/matching.rs +++ b/src/components/layout/css/matching.rs @@ -19,13 +19,13 @@ use servo_util::str::DOMString; use std::mem; use std::hash::{Hash, sip}; use std::slice::Items; -use style::{After, Before, ComputedValues, MatchedProperty, Stylist, TElement, TNode, cascade}; +use style::{After, Before, ComputedValues, DeclarationBlock, Stylist, TElement, TNode, cascade}; use sync::Arc; pub struct ApplicableDeclarations { - pub normal: SmallVec16<MatchedProperty>, - pub before: Vec<MatchedProperty>, - pub after: Vec<MatchedProperty>, + pub normal: SmallVec16<DeclarationBlock>, + pub before: Vec<DeclarationBlock>, + pub after: Vec<DeclarationBlock>, /// Whether the `normal` declarations are shareable with other nodes. pub normal_shareable: bool, @@ -51,11 +51,11 @@ impl ApplicableDeclarations { #[deriving(Clone)] pub struct ApplicableDeclarationsCacheEntry { - pub declarations: Vec<MatchedProperty>, + pub declarations: Vec<DeclarationBlock>, } impl ApplicableDeclarationsCacheEntry { - fn new(slice: &[MatchedProperty]) -> ApplicableDeclarationsCacheEntry { + fn new(slice: &[DeclarationBlock]) -> ApplicableDeclarationsCacheEntry { let mut entry_declarations = Vec::new(); for declarations in slice.iter() { entry_declarations.push(declarations.clone()); @@ -81,11 +81,11 @@ impl Hash for ApplicableDeclarationsCacheEntry { } struct ApplicableDeclarationsCacheQuery<'a> { - declarations: &'a [MatchedProperty], + declarations: &'a [DeclarationBlock], } impl<'a> ApplicableDeclarationsCacheQuery<'a> { - fn new(declarations: &'a [MatchedProperty]) -> ApplicableDeclarationsCacheQuery<'a> { + fn new(declarations: &'a [DeclarationBlock]) -> ApplicableDeclarationsCacheQuery<'a> { ApplicableDeclarationsCacheQuery { declarations: declarations, } @@ -141,14 +141,14 @@ impl ApplicableDeclarationsCache { } } - fn find(&self, declarations: &[MatchedProperty]) -> Option<Arc<ComputedValues>> { + fn find(&self, declarations: &[DeclarationBlock]) -> Option<Arc<ComputedValues>> { match self.cache.find_equiv(&ApplicableDeclarationsCacheQuery::new(declarations)) { None => None, Some(ref values) => Some((*values).clone()), } } - fn insert(&mut self, declarations: &[MatchedProperty], style: Arc<ComputedValues>) { + fn insert(&mut self, declarations: &[DeclarationBlock], style: Arc<ComputedValues>) { self.cache.insert(ApplicableDeclarationsCacheEntry::new(declarations), style) } } @@ -309,7 +309,7 @@ pub trait MatchMethods { trait PrivateMatchMethods { fn cascade_node_pseudo_element(&self, parent_style: Option<&Arc<ComputedValues>>, - applicable_declarations: &[MatchedProperty], + applicable_declarations: &[DeclarationBlock], style: &mut Option<Arc<ComputedValues>>, applicable_declarations_cache: &mut ApplicableDeclarationsCache, @@ -324,7 +324,7 @@ trait PrivateMatchMethods { impl<'ln> PrivateMatchMethods for LayoutNode<'ln> { fn cascade_node_pseudo_element(&self, parent_style: Option<&Arc<ComputedValues>>, - applicable_declarations: &[MatchedProperty], + applicable_declarations: &[DeclarationBlock], style: &mut Option<Arc<ComputedValues>>, applicable_declarations_cache: &mut ApplicableDeclarationsCache, diff --git a/src/components/layout/css/select.rs b/src/components/layout/css/select.rs deleted file mode 100644 index 230feb86e99..00000000000 --- a/src/components/layout/css/select.rs +++ /dev/null @@ -1,18 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use style::{Stylesheet, Stylist, UserAgentOrigin, with_errors_silenced}; -use url::Url; - - -pub fn new_stylist() -> Stylist { - let mut stylist = Stylist::new(); - let ua_stylesheet = with_errors_silenced(|| Stylesheet::from_bytes( - include_bin!("user-agent.css"), - Url::parse("chrome:///user-agent.css").unwrap(), - None, - None)); - stylist.add_stylesheet(ua_stylesheet, UserAgentOrigin); - stylist -} diff --git a/src/components/layout/layout.rs b/src/components/layout/layout.rs index 328e167b680..0157ceb8e41 100644 --- a/src/components/layout/layout.rs +++ b/src/components/layout/layout.rs @@ -62,7 +62,6 @@ pub mod extra; pub mod css { mod node_util; - pub mod select; pub mod matching; pub mod node_style; } diff --git a/src/components/layout/layout_task.rs b/src/components/layout/layout_task.rs index 0edb9436771..e4cba397379 100644 --- a/src/components/layout/layout_task.rs +++ b/src/components/layout/layout_task.rs @@ -6,7 +6,6 @@ //! rendered. use css::matching::{ApplicableDeclarations, MatchMethods}; -use css::select::new_stylist; use css::node_style::StyledNode; use construct::{FlowConstructionResult, NoConstructionResult}; use context::{LayoutContext, SharedLayoutContext}; @@ -57,7 +56,7 @@ use std::comm::{channel, Sender, Receiver, Select}; use std::mem; use std::ptr; use style::{AuthorOrigin, Stylesheet, Stylist}; -use style::CSSFontFaceRule; +use style::iter_font_face_rules; use sync::{Arc, Mutex}; use url::Url; @@ -347,7 +346,7 @@ impl LayoutTask { screen_size: screen_size, display_list: None, - stylist: box new_stylist(), + stylist: box Stylist::new(), parallel_traversal: parallel_traversal, time_profiler_chan: time_profiler_chan, opts: opts.clone(), @@ -491,22 +490,9 @@ impl LayoutTask { fn handle_add_stylesheet(&mut self, sheet: Stylesheet) { // Find all font-face rules and notify the font cache of them. // GWTODO: Need to handle unloading web fonts (when we handle unloading stylesheets!) - // GWTODO: Need to handle font-face nested within media rules. - for rule in sheet.rules.iter() { - match rule { - &CSSFontFaceRule(ref font_face_rule) => { - let mut font_urls = vec!(); - for source_line in font_face_rule.source_lines.iter() { - for source in source_line.sources.iter() { - font_urls.push(source.url.clone()); - } - } - self.font_cache_task.add_web_font(font_urls, font_face_rule.family.as_slice()); - }, - _ => {} - } - } - + iter_font_face_rules(&sheet, |family, url| { + self.font_cache_task.add_web_font(family.to_string(), url.clone()); + }); self.stylist.add_stylesheet(sheet, AuthorOrigin); } diff --git a/src/components/layout/wrapper.rs b/src/components/layout/wrapper.rs index 23332236e1f..e5d450d93f7 100644 --- a/src/components/layout/wrapper.rs +++ b/src/components/layout/wrapper.rs @@ -265,13 +265,11 @@ impl<'ln> TNode<LayoutElement<'ln>> for LayoutNode<'ln> { } fn match_attr(&self, attr: &AttrSelector, test: |&str| -> bool) -> bool { - let name = unsafe { - let element: JS<Element> = self.node.transmute_copy(); - if element.html_element_in_html_document_for_layout() { - attr.lower_name.as_slice() - } else { - attr.name.as_slice() - } + assert!(self.is_element()) + let name = if self.is_html_element_in_html_document() { + attr.lower_name.as_slice() + } else { + attr.name.as_slice() }; match attr.namespace { SpecificNamespace(ref ns) => { @@ -283,6 +281,15 @@ impl<'ln> TNode<LayoutElement<'ln>> for LayoutNode<'ln> { AnyNamespace => false, } } + + fn is_html_element_in_html_document(&self) -> bool { + unsafe { + self.is_element() && { + let element: JS<Element> = self.node.transmute_copy(); + element.html_element_in_html_document_for_layout() + } + } + } } pub struct LayoutNodeChildrenIterator<'a> { diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index 8d867b573be..cdb2f4f1f2b 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -4,7 +4,6 @@ //! Element nodes. -use cssparser::tokenize; use dom::attr::{Attr, ReplacedAttr, FirstSetAttr, AttrHelpersForLayout}; use dom::attr::{AttrValue, StringAttrValue, UIntAttrValue, AtomAttrValue}; use dom::attrlist::AttrList; @@ -31,7 +30,7 @@ use dom::nodelist::NodeList; use dom::virtualmethods::{VirtualMethods, vtable_for}; use layout_interface::ContentChangedDocumentDamage; use layout_interface::MatchSelectorsDocumentDamage; -use style::{matches_compound_selector, NamespaceMap, parse_selector_list}; +use style::{matches, parse_selector_list_from_str}; use style; use servo_util::atom::Atom; use servo_util::namespace; @@ -775,21 +774,13 @@ impl<'a> ElementMethods for JSRef<'a, Element> { // http://dom.spec.whatwg.org/#dom-element-matches fn Matches(&self, selectors: DOMString) -> Fallible<bool> { - let namespace = NamespaceMap::new(); - match parse_selector_list(tokenize(selectors.as_slice()).map(|(token, _)| token).collect(), - &namespace) { - None => return Err(Syntax), - Some(ref selectors) => { + match parse_selector_list_from_str(selectors.as_slice()) { + Err(()) => Err(Syntax), + Ok(ref selectors) => { let root: &JSRef<Node> = NodeCast::from_ref(self); - for selector in selectors.iter() { - let mut shareable = false; - if matches_compound_selector(&*selector.compound_selectors, root, &mut shareable) { - return Ok(true); - } - } + Ok(matches(selectors, root)) } } - Ok(false) } } diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index 5850779692c..9ef327f4fa4 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -4,7 +4,6 @@ //! The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements. -use cssparser::tokenize; use dom::attr::Attr; use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods; @@ -48,7 +47,7 @@ use layout_interface::{ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery, C LayoutChan, ReapLayoutDataMsg, TrustedNodeAddress, UntrustedNodeAddress}; use servo_util::geometry::Au; use servo_util::str::{DOMString, null_str_as_empty}; -use style::{parse_selector_list, matches_compound_selector, NamespaceMap}; +use style::{parse_selector_list_from_str, matches}; use js::jsapi::{JSContext, JSObject, JSRuntime}; use js::jsfriendapi; @@ -611,21 +610,16 @@ impl<'m, 'n> NodeHelpers<'m, 'n> for JSRef<'n, Node> { // http://dom.spec.whatwg.org/#dom-parentnode-queryselector fn query_selector(&self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> { // Step 1. - let namespace = NamespaceMap::new(); - match parse_selector_list(tokenize(selectors.as_slice()).map(|(token, _)| token).collect(), &namespace) { + match parse_selector_list_from_str(selectors.as_slice()) { // Step 2. - None => return Err(Syntax), + Err(()) => return Err(Syntax), // Step 3. - Some(ref selectors) => { + Ok(ref selectors) => { let root = self.ancestors().last().unwrap_or(self.clone()); - for selector in selectors.iter() { - assert!(selector.pseudo_element.is_none()); - for node in root.traverse_preorder().filter(|node| node.is_element()) { - let mut _shareable: bool = false; - if matches_compound_selector(selector.compound_selectors.deref(), &node, &mut _shareable) { - let elem: &JSRef<Element> = ElementCast::to_ref(&node).unwrap(); - return Ok(Some(Temporary::from_rooted(elem))); - } + for node in root.traverse_preorder() { + if node.is_element() && matches(selectors, &node) { + let elem: &JSRef<Element> = ElementCast::to_ref(&node).unwrap(); + return Ok(Some(Temporary::from_rooted(elem))); } } } @@ -636,23 +630,15 @@ impl<'m, 'n> NodeHelpers<'m, 'n> for JSRef<'n, Node> { // http://dom.spec.whatwg.org/#dom-parentnode-queryselectorall fn query_selector_all(&self, selectors: DOMString) -> Fallible<Temporary<NodeList>> { // Step 1. - let mut nodes = vec!(); + let nodes; let root = self.ancestors().last().unwrap_or(self.clone()); - let namespace = NamespaceMap::new(); - match parse_selector_list(tokenize(selectors.as_slice()).map(|(token, _)| token).collect(), &namespace) { + match parse_selector_list_from_str(selectors.as_slice()) { // Step 2. - None => return Err(Syntax), + Err(()) => return Err(Syntax), // Step 3. - Some(ref selectors) => { - for selector in selectors.iter() { - assert!(selector.pseudo_element.is_none()); - for node in root.traverse_preorder().filter(|node| node.is_element()) { - let mut _shareable: bool = false; - if matches_compound_selector(selector.compound_selectors.deref(), &node, &mut _shareable) { - nodes.push(node.clone()) - } - } - } + Ok(ref selectors) => { + nodes = root.traverse_preorder().filter( + |node| node.is_element() && matches(selectors, node)).collect() } } let window = window_from_node(self).root(); @@ -1997,29 +1983,32 @@ impl<'a> style::TNode<JSRef<'a, Element>> for JSRef<'a, Node> { fn parent_node(&self) -> Option<JSRef<'a, Node>> { (self as &NodeHelpers).parent_node().map(|node| *node.root()) } + fn prev_sibling(&self) -> Option<JSRef<'a, Node>> { (self as &NodeHelpers).prev_sibling().map(|node| *node.root()) } + fn next_sibling(&self) -> Option<JSRef<'a, Node>> { (self as &NodeHelpers).next_sibling().map(|node| *node.root()) } + fn is_document(&self) -> bool { (self as &NodeHelpers).is_document() } + fn is_element(&self) -> bool { (self as &NodeHelpers).is_element() } + fn as_element(&self) -> JSRef<'a, Element> { let elem: Option<&JSRef<'a, Element>> = ElementCast::to_ref(self); assert!(elem.is_some()); *elem.unwrap() } + fn match_attr(&self, attr: &style::AttrSelector, test: |&str| -> bool) -> bool { let name = { - let elem: Option<&JSRef<'a, Element>> = ElementCast::to_ref(self); - assert!(elem.is_some()); - let elem: &ElementHelpers = elem.unwrap() as &ElementHelpers; - if elem.html_element_in_html_document() { + if self.is_html_element_in_html_document() { attr.lower_name.as_slice() } else { attr.name.as_slice() @@ -2034,6 +2023,13 @@ impl<'a> style::TNode<JSRef<'a, Element>> for JSRef<'a, Node> { style::AnyNamespace => false, } } + + fn is_html_element_in_html_document(&self) -> bool { + let elem: Option<&JSRef<'a, Element>> = ElementCast::to_ref(self); + assert!(elem.is_some()); + let elem: &ElementHelpers = elem.unwrap() as &ElementHelpers; + elem.html_element_in_html_document() + } } pub trait DisabledStateHelpers { diff --git a/src/components/style/errors.rs b/src/components/style/errors.rs index eb142d887fe..f04f4969293 100644 --- a/src/components/style/errors.rs +++ b/src/components/style/errors.rs @@ -27,21 +27,6 @@ impl<T, I: Iterator<Result<T, SyntaxError>>> Iterator<T> for ErrorLoggerIterator /// Set a `RUST_LOG=style::errors` environment variable /// to log CSS parse errors to stderr. pub fn log_css_error(location: SourceLocation, message: &str) { - // Check this first as it’s cheaper than local_data. - if log_enabled!(::log::INFO) { - if silence_errors.get().is_none() { - // TODO eventually this will got into a "web console" or something. - info!("{:u}:{:u} {:s}", location.line, location.column, message) - } - } -} - - -local_data_key!(silence_errors: ()) - -pub fn with_errors_silenced<T>(f: || -> T) -> T { - silence_errors.replace(Some(())); - let result = f(); - silence_errors.replace(None); - result + // TODO eventually this will got into a "web console" or something. + info!("{:u}:{:u} {:s}", location.line, location.column, message) } diff --git a/src/components/style/font_face.rs b/src/components/style/font_face.rs index 228c5d459ae..81e1dadf0a8 100644 --- a/src/components/style/font_face.rs +++ b/src/components/style/font_face.rs @@ -6,37 +6,54 @@ use cssparser::ast::*; use cssparser::parse_declaration_list; use errors::{ErrorLoggerIterator, log_css_error}; use std::ascii::StrAsciiExt; -use parsing_utils::one_component_value; -use stylesheets::{CSSRule, CSSFontFaceRule}; +use parsing_utils::{BufferedIter, ParserIter, parse_slice_comma_separated}; +use properties::longhands::font_family::parse_one_family; +use properties::computed_values::font_family::FamilyName; +use stylesheets::{CSSRule, CSSFontFaceRule, CSSStyleRule, CSSMediaRule}; +use media_queries::{Device, Screen}; use url::{Url, UrlParser}; -#[deriving(PartialEq)] -pub enum FontFaceFormat { - UnknownFormat, - WoffFormat, - TtfFormat, - SvgFormat, - EotFormat, + +static SUPPORTED_FORMATS: &'static [&'static str] = &["truetype", "opentype"]; + + +pub fn iter_font_face_rules_inner(rules: &[CSSRule], callback: |family: &str, source: &Url|) { + let device = &Device { media_type: Screen }; // TODO, use Print when printing + for rule in rules.iter() { + match *rule { + CSSStyleRule(_) => {}, + CSSMediaRule(ref rule) => if rule.media_queries.evaluate(device) { + iter_font_face_rules_inner(rule.rules.as_slice(), |f, s| callback(f, s)) + }, + CSSFontFaceRule(ref rule) => { + for source in rule.sources.iter() { + if source.format_hints.is_empty() || source.format_hints.iter().any( + |f| SUPPORTED_FORMATS.iter().any( + |s| f.as_slice().eq_ignore_ascii_case(*s))) { + callback(rule.family.as_slice(), &source.url) + } + } + }, + } + } } -pub struct FontFaceSource { - pub url: Url, - pub format_hints: Vec<FontFaceFormat>, +enum Source { + UrlSource(UrlSource), + LocalSource(String), } -pub struct FontFaceSourceLine { - pub sources: Vec<FontFaceSource> +pub struct UrlSource { + pub url: Url, + pub format_hints: Vec<String>, } pub struct FontFaceRule { pub family: String, - pub source_lines: Vec<FontFaceSourceLine>, + pub sources: Vec<UrlSource>, // local() is not supported yet } pub fn parse_font_face_rule(rule: AtRule, parent_rules: &mut Vec<CSSRule>, base_url: &Url) { - let mut maybe_family = None; - let mut source_lines = vec!(); - if rule.prelude.as_slice().skip_whitespace().next().is_some() { log_css_error(rule.location, "@font-face prelude contains unexpected characters"); return; @@ -50,144 +67,36 @@ pub fn parse_font_face_rule(rule: AtRule, parent_rules: &mut Vec<CSSRule>, base_ } }; + let mut maybe_family = None; + let mut maybe_sources = None; + for item in ErrorLoggerIterator(parse_declaration_list(block.move_iter())) { match item { DeclAtRule(rule) => log_css_error( rule.location, format!("Unsupported at-rule in declaration list: @{:s}", rule.name).as_slice()), - Declaration(Declaration{ location: location, name: name, value: value, important: _}) => { - + Declaration(Declaration{ location, name, value, important }) => { + if important { + log_css_error(location, "!important is not allowed on @font-face descriptors"); + continue + } let name_lower = name.as_slice().to_ascii_lower(); match name_lower.as_slice() { "font-family" => { - // FIXME(#2802): Share code with the font-family parser. - match one_component_value(value.as_slice()) { - Some(&String(ref string_value)) => { - maybe_family = Some(string_value.clone()); + let iter = &mut BufferedIter::new(value.as_slice().skip_whitespace()); + match parse_one_family(iter) { + Ok(FamilyName(name)) => { + maybe_family = Some(name); }, - _ => { - log_css_error(location, format!("Unsupported font-family string {:s}", name).as_slice()); - } + // This also includes generic family names: + _ => log_css_error(location, "Invalid font-family in @font-face"), } }, "src" => { - let mut iter = value.as_slice().skip_whitespace(); - let mut sources = vec!(); - let mut syntax_error = false; - - 'outer: loop { - - // url() or local() should be next - let maybe_url = match iter.next() { - Some(&URL(ref string_value)) => { - let maybe_url = UrlParser::new().base_url(base_url).parse(string_value.as_slice()); - let url = maybe_url.unwrap_or_else(|_| Url::parse("about:invalid").unwrap()); - Some(url) - }, - Some(&Function(ref string_value, ref _values)) => { - match string_value.as_slice() { - "local" => { - log_css_error(location, "local font face is not supported yet - skipping"); - None - }, - _ => { - log_css_error(location, format!("Unexpected token {}", string_value).as_slice()); - syntax_error = true; - break; - } - } - }, - _ => { - log_css_error(location, "Unsupported declaration type"); - syntax_error = true; - break; - } - }; - - let mut next_token = iter.next(); - - match maybe_url { - Some(url) => { - let mut source = FontFaceSource { - url: url, - format_hints: vec!(), - }; - - // optional format, or comma to start loop again - match next_token { - Some(&Function(ref string_value, ref values)) => { - match string_value.as_slice() { - "format" => { - let mut format_iter = values.as_slice().skip_whitespace(); - - loop { - let fmt_token = format_iter.next(); - match fmt_token { - Some(&String(ref format_hint)) => { - let hint = match format_hint.as_slice() { - "embedded-opentype" => EotFormat, - "woff" => WoffFormat, - "truetype" | "opentype" => TtfFormat, - "svg" => SvgFormat, - _ => UnknownFormat, - }; - source.format_hints.push(hint); - }, - _ => { - log_css_error(location, "Unexpected token"); - syntax_error = true; - break 'outer; - } - } - - let comma_token = format_iter.next(); - match comma_token { - Some(&Comma) => {}, - None => { - break; - } - _ => { - log_css_error(location, "Unexpected token"); - syntax_error = true; - break 'outer; - } - } - } - }, - _ => { - log_css_error(location, - format!("Unsupported token {}", string_value).as_slice()); - syntax_error = true; - break; - } - } - next_token = iter.next(); - }, - _ => {} - } - - sources.push(source); - }, - None => {}, - } - - // after url or optional format, comes comma or end - match next_token { - Some(&Comma) => {}, - None => break, - _ => { - log_css_error(location, "Unexpected token type"); - syntax_error = true; - break; - } - } - } - - if !syntax_error && sources.len() > 0 { - let source_line = FontFaceSourceLine { - sources: sources - }; - source_lines.push(source_line); - } + match parse_slice_comma_separated( + value.as_slice(), |iter| parse_one_url_src(iter, base_url)) { + Ok(sources) => maybe_sources = Some(sources), + Err(()) => log_css_error(location, "Invalid src in @font-face"), + }; }, _ => { log_css_error(location, format!("Unsupported declaration {:s}", name).as_slice()); @@ -197,11 +106,78 @@ pub fn parse_font_face_rule(rule: AtRule, parent_rules: &mut Vec<CSSRule>, base_ } } - if maybe_family.is_some() && source_lines.len() > 0 { - let font_face_rule = FontFaceRule { - family: maybe_family.unwrap(), - source_lines: source_lines, - }; - parent_rules.push(CSSFontFaceRule(font_face_rule)); + match (maybe_family, maybe_sources) { + (Some(family), Some(sources)) => parent_rules.push(CSSFontFaceRule(FontFaceRule { + family: family, + sources: sources, + })), + (None, _) => log_css_error(rule.location, "@font-face without a font-family descriptor"), + _ => log_css_error(rule.location, "@font-face without an src descriptor"), + } +} + + +/// local() is not supported yet +fn parse_one_url_src(iter: ParserIter, base_url: &Url) -> Result<UrlSource, ()> { + match parse_one_src(iter, base_url) { + Ok(UrlSource(source)) => Ok(source), + _ => Err(()) + } +} + + +fn parse_one_src(iter: ParserIter, base_url: &Url) -> Result<Source, ()> { + let url = match iter.next() { + // Parsing url() + Some(&URL(ref url)) => { + UrlParser::new().base_url(base_url).parse(url.as_slice()).unwrap_or_else( + |_error| Url::parse("about:invalid").unwrap()) + }, + // Parsing local() with early return() + Some(&Function(ref name, ref arguments)) => { + if name.as_slice().eq_ignore_ascii_case("local") { + let iter = &mut BufferedIter::new(arguments.as_slice().skip_whitespace()); + match parse_one_family(iter) { + Ok(FamilyName(name)) => return Ok(LocalSource(name)), + _ => return Err(()) + } + } + return Err(()) + }, + _ => return Err(()) + }; + + // Parsing optional format() + let format_hints = match iter.next() { + Some(&Function(ref name, ref arguments)) => { + if !name.as_slice().eq_ignore_ascii_case("format") { + return Err(()) + } + try!(parse_slice_comma_separated(arguments.as_slice(), parse_one_format)) + } + Some(component_value) => { + iter.push_back(component_value); + vec![] + } + None => vec![], + }; + + Ok(UrlSource(UrlSource { + url: url, + format_hints: format_hints, + })) +} + + +fn parse_one_format(iter: ParserIter) -> Result<String, ()> { + match iter.next() { + Some(&String(ref value)) => { + if iter.next().is_none() { + Ok(value.clone()) + } else { + Err(()) + } + } + _ => Err(()) } } diff --git a/src/components/style/node.rs b/src/components/style/node.rs index 9e8da53100a..85a4429e767 100644 --- a/src/components/style/node.rs +++ b/src/components/style/node.rs @@ -18,6 +18,7 @@ pub trait TNode<E:TElement> : Clone { fn is_element(&self) -> bool; fn as_element(&self) -> E; fn match_attr(&self, attr: &AttrSelector, test: |&str| -> bool) -> bool; + fn is_html_element_in_html_document(&self) -> bool; } pub trait TElement { diff --git a/src/components/style/parsing_utils.rs b/src/components/style/parsing_utils.rs index df355629971..3afd7ba0353 100644 --- a/src/components/style/parsing_utils.rs +++ b/src/components/style/parsing_utils.rs @@ -4,18 +4,78 @@ use std::ascii::StrAsciiExt; -use cssparser::ast::{ComponentValue, Ident, SkipWhitespaceIterable}; +use cssparser::ast::{ComponentValue, Ident, Comma, SkipWhitespaceIterable, SkipWhitespaceIterator}; -pub fn one_component_value<'a>(input: &'a [ComponentValue]) -> Option<&'a ComponentValue> { +pub fn one_component_value<'a>(input: &'a [ComponentValue]) -> Result<&'a ComponentValue, ()> { let mut iter = input.skip_whitespace(); - iter.next().filtered(|_| iter.next().is_none()) + match iter.next() { + Some(value) => if iter.next().is_none() { Ok(value) } else { Err(()) }, + None => Err(()) + } } -pub fn get_ident_lower(component_value: &ComponentValue) -> Option<String> { +pub fn get_ident_lower(component_value: &ComponentValue) -> Result<String, ()> { match component_value { - &Ident(ref value) => Some(value.as_slice().to_ascii_lower()), - _ => None, + &Ident(ref value) => Ok(value.as_slice().to_ascii_lower()), + _ => Err(()), + } +} + + +pub struct BufferedIter<E, I> { + iter: I, + buffer: Option<E>, +} + +impl<E, I: Iterator<E>> BufferedIter<E, I> { + pub fn new(iter: I) -> BufferedIter<E, I> { + BufferedIter { + iter: iter, + buffer: None, + } + } + + #[inline] + pub fn push_back(&mut self, value: E) { + assert!(self.buffer.is_none()); + self.buffer = Some(value); + } +} + +impl<E, I: Iterator<E>> Iterator<E> for BufferedIter<E, I> { + #[inline] + fn next(&mut self) -> Option<E> { + if self.buffer.is_some() { + self.buffer.take() + } + else { + self.iter.next() + } + } +} + +pub type ParserIter<'a, 'b> = &'a mut BufferedIter<&'b ComponentValue, SkipWhitespaceIterator<'b>>; + + +#[inline] +pub fn parse_slice_comma_separated<T>(input: &[ComponentValue], + parse_one: |ParserIter| -> Result<T, ()>) + -> Result<Vec<T>, ()> { + parse_comma_separated(&mut BufferedIter::new(input.skip_whitespace()), parse_one) +} + +#[inline] +pub fn parse_comma_separated<T>(iter: ParserIter, + parse_one: |ParserIter| -> Result<T, ()>) + -> Result<Vec<T>, ()> { + let mut values = vec![try!(parse_one(iter))]; + for component_value in iter { + match component_value { + &Comma => values.push(try!(parse_one(iter))), + _ => return Err(()) + } } + Ok(values) } diff --git a/src/components/style/properties/common_types.rs b/src/components/style/properties/common_types.rs index 6672daa1894..fc30a4036fe 100644 --- a/src/components/style/properties/common_types.rs +++ b/src/components/style/properties/common_types.rs @@ -40,32 +40,32 @@ pub mod specified { static AU_PER_PC: CSSFloat = AU_PER_PT * 12.; impl Length { #[inline] - fn parse_internal(input: &ComponentValue, negative_ok: bool) -> Option<Length> { + fn parse_internal(input: &ComponentValue, negative_ok: bool) -> Result<Length, ()> { match input { &Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => Length::parse_dimension(value.value, unit.as_slice()), - &Number(ref value) if value.value == 0. => Some(Au_(Au(0))), - _ => None + &Number(ref value) if value.value == 0. => Ok(Au_(Au(0))), + _ => Err(()) } } #[allow(dead_code)] - pub fn parse(input: &ComponentValue) -> Option<Length> { + pub fn parse(input: &ComponentValue) -> Result<Length, ()> { Length::parse_internal(input, /* negative_ok = */ true) } - pub fn parse_non_negative(input: &ComponentValue) -> Option<Length> { + pub fn parse_non_negative(input: &ComponentValue) -> Result<Length, ()> { Length::parse_internal(input, /* negative_ok = */ false) } - pub fn parse_dimension(value: CSSFloat, unit: &str) -> Option<Length> { + pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Length, ()> { match unit.to_ascii_lower().as_slice() { - "px" => Some(Length::from_px(value)), - "in" => Some(Au_(Au((value * AU_PER_IN) as i32))), - "cm" => Some(Au_(Au((value * AU_PER_CM) as i32))), - "mm" => Some(Au_(Au((value * AU_PER_MM) as i32))), - "pt" => Some(Au_(Au((value * AU_PER_PT) as i32))), - "pc" => Some(Au_(Au((value * AU_PER_PC) as i32))), - "em" => Some(Em(value)), - "ex" => Some(Ex(value)), - _ => None + "px" => Ok(Length::from_px(value)), + "in" => Ok(Au_(Au((value * AU_PER_IN) as i32))), + "cm" => Ok(Au_(Au((value * AU_PER_CM) as i32))), + "mm" => Ok(Au_(Au((value * AU_PER_MM) as i32))), + "pt" => Ok(Au_(Au((value * AU_PER_PT) as i32))), + "pc" => Ok(Au_(Au((value * AU_PER_PC) as i32))), + "em" => Ok(Em(value)), + "ex" => Ok(Ex(value)), + _ => Err(()) } } #[inline] @@ -81,23 +81,23 @@ pub mod specified { } impl LengthOrPercentage { fn parse_internal(input: &ComponentValue, negative_ok: bool) - -> Option<LengthOrPercentage> { + -> Result<LengthOrPercentage, ()> { match input { &Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => Length::parse_dimension(value.value, unit.as_slice()).map(LP_Length), &ast::Percentage(ref value) if negative_ok || value.value >= 0. - => Some(LP_Percentage(value.value / 100.)), - &Number(ref value) if value.value == 0. => Some(LP_Length(Au_(Au(0)))), - _ => None + => Ok(LP_Percentage(value.value / 100.)), + &Number(ref value) if value.value == 0. => Ok(LP_Length(Au_(Au(0)))), + _ => Err(()) } } #[allow(dead_code)] #[inline] - pub fn parse(input: &ComponentValue) -> Option<LengthOrPercentage> { + pub fn parse(input: &ComponentValue) -> Result<LengthOrPercentage, ()> { LengthOrPercentage::parse_internal(input, /* negative_ok = */ true) } #[inline] - pub fn parse_non_negative(input: &ComponentValue) -> Option<LengthOrPercentage> { + pub fn parse_non_negative(input: &ComponentValue) -> Result<LengthOrPercentage, ()> { LengthOrPercentage::parse_internal(input, /* negative_ok = */ false) } } @@ -110,23 +110,23 @@ pub mod specified { } impl LengthOrPercentageOrAuto { fn parse_internal(input: &ComponentValue, negative_ok: bool) - -> Option<LengthOrPercentageOrAuto> { + -> Result<LengthOrPercentageOrAuto, ()> { match input { &Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => Length::parse_dimension(value.value, unit.as_slice()).map(LPA_Length), &ast::Percentage(ref value) if negative_ok || value.value >= 0. - => Some(LPA_Percentage(value.value / 100.)), - &Number(ref value) if value.value == 0. => Some(LPA_Length(Au_(Au(0)))), - &Ident(ref value) if value.as_slice().eq_ignore_ascii_case("auto") => Some(LPA_Auto), - _ => None + => Ok(LPA_Percentage(value.value / 100.)), + &Number(ref value) if value.value == 0. => Ok(LPA_Length(Au_(Au(0)))), + &Ident(ref value) if value.as_slice().eq_ignore_ascii_case("auto") => Ok(LPA_Auto), + _ => Err(()) } } #[inline] - pub fn parse(input: &ComponentValue) -> Option<LengthOrPercentageOrAuto> { + pub fn parse(input: &ComponentValue) -> Result<LengthOrPercentageOrAuto, ()> { LengthOrPercentageOrAuto::parse_internal(input, /* negative_ok = */ true) } #[inline] - pub fn parse_non_negative(input: &ComponentValue) -> Option<LengthOrPercentageOrAuto> { + pub fn parse_non_negative(input: &ComponentValue) -> Result<LengthOrPercentageOrAuto, ()> { LengthOrPercentageOrAuto::parse_internal(input, /* negative_ok = */ false) } } @@ -139,24 +139,24 @@ pub mod specified { } impl LengthOrPercentageOrNone { fn parse_internal(input: &ComponentValue, negative_ok: bool) - -> Option<LengthOrPercentageOrNone> { + -> Result<LengthOrPercentageOrNone, ()> { match input { &Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => Length::parse_dimension(value.value, unit.as_slice()).map(LPN_Length), &ast::Percentage(ref value) if negative_ok || value.value >= 0. - => Some(LPN_Percentage(value.value / 100.)), - &Number(ref value) if value.value == 0. => Some(LPN_Length(Au_(Au(0)))), - &Ident(ref value) if value.as_slice().eq_ignore_ascii_case("none") => Some(LPN_None), - _ => None + => Ok(LPN_Percentage(value.value / 100.)), + &Number(ref value) if value.value == 0. => Ok(LPN_Length(Au_(Au(0)))), + &Ident(ref value) if value.as_slice().eq_ignore_ascii_case("none") => Ok(LPN_None), + _ => Err(()) } } #[allow(dead_code)] #[inline] - pub fn parse(input: &ComponentValue) -> Option<LengthOrPercentageOrNone> { + pub fn parse(input: &ComponentValue) -> Result<LengthOrPercentageOrNone, ()> { LengthOrPercentageOrNone::parse_internal(input, /* negative_ok = */ true) } #[inline] - pub fn parse_non_negative(input: &ComponentValue) -> Option<LengthOrPercentageOrNone> { + pub fn parse_non_negative(input: &ComponentValue) -> Result<LengthOrPercentageOrNone, ()> { LengthOrPercentageOrNone::parse_internal(input, /* negative_ok = */ false) } } diff --git a/src/components/style/properties/mod.rs.mako b/src/components/style/properties/mod.rs.mako index 5dc6a22012d..e699c392a77 100644 --- a/src/components/style/properties/mod.rs.mako +++ b/src/components/style/properties/mod.rs.mako @@ -18,7 +18,7 @@ pub use geom::SideOffsets2D; use errors::{ErrorLoggerIterator, log_css_error}; pub use parsing_utils::*; pub use self::common_types::*; -use selector_matching::MatchedProperty; +use selector_matching::DeclarationBlock; pub use self::property_bit_field::PropertyBitField; @@ -118,12 +118,13 @@ pub mod longhands { ${caller.body()} % if derived_from is None: pub fn parse_declared(input: &[ComponentValue], base_url: &Url) - -> Option<DeclaredValue<SpecifiedValue>> { + -> Result<DeclaredValue<SpecifiedValue>, ()> { match CSSWideKeyword::parse(input) { - Some(Some(keyword)) => Some(CSSWideKeyword(keyword)), - Some(None) => Some(CSSWideKeyword(${ - "Inherit" if THIS_STYLE_STRUCT.inherited else "Initial"})), - None => parse_specified(input, base_url), + Ok(InheritKeyword) => Ok(Inherit), + Ok(InitialKeyword) => Ok(Initial), + Ok(UnsetKeyword) => Ok(${ + "Inherit" if THIS_STYLE_STRUCT.inherited else "Initial"}), + Err(()) => parse_specified(input, base_url), } } % endif @@ -136,7 +137,7 @@ pub mod longhands { ${caller.body()} % if derived_from is None: pub fn parse_specified(_input: &[ComponentValue], _base_url: &Url) - -> Option<DeclaredValue<SpecifiedValue>> { + -> Result<DeclaredValue<SpecifiedValue>, ()> { parse(_input, _base_url).map(super::SpecifiedValue) } % endif @@ -147,7 +148,7 @@ pub mod longhands { <%self:longhand name="${name}" derived_from="${derived_from}" experimental="${experimental}"> ${caller.body()} - pub fn parse(input: &[ComponentValue], base_url: &Url) -> Option<SpecifiedValue> { + pub fn parse(input: &[ComponentValue], base_url: &Url) -> Result<SpecifiedValue, ()> { one_component_value(input).and_then(|c| from_component_value(c, base_url)) } </%self:longhand> @@ -170,13 +171,13 @@ pub mod longhands { ${to_rust_ident(values.split()[0])} } pub fn from_component_value(v: &ComponentValue, _base_url: &Url) - -> Option<SpecifiedValue> { + -> Result<SpecifiedValue, ()> { get_ident_lower(v).and_then(|keyword| { match keyword.as_slice() { % for value in values.split(): - "${value}" => Some(${to_rust_ident(value)}), + "${value}" => Ok(${to_rust_ident(value)}), % endfor - _ => None, + _ => Err(()), } }) } @@ -201,7 +202,7 @@ pub mod longhands { } #[inline] pub fn get_initial_value() -> computed_value::T { ${initial_value} } #[inline] pub fn from_component_value(v: &ComponentValue, _base_url: &Url) - -> Option<SpecifiedValue> { + -> Result<SpecifiedValue, ()> { specified::${type}::${parse_method}(v) } </%self:single_component_value> @@ -244,14 +245,14 @@ pub mod longhands { % endfor pub fn parse_border_width(component_value: &ComponentValue, _base_url: &Url) - -> Option<specified::Length> { + -> Result<specified::Length, ()> { match component_value { &Ident(ref value) => { match value.as_slice().to_ascii_lower().as_slice() { - "thin" => Some(specified::Length::from_px(1.)), - "medium" => Some(specified::Length::from_px(3.)), - "thick" => Some(specified::Length::from_px(5.)), - _ => None + "thin" => Ok(specified::Length::from_px(1.)), + "medium" => Ok(specified::Length::from_px(3.)), + "thick" => Ok(specified::Length::from_px(5.)), + _ => Err(()) } }, _ => specified::Length::parse_non_negative(component_value) @@ -267,7 +268,7 @@ pub mod longhands { #[inline] pub fn get_initial_value() -> computed_value::T { Au::from_px(3) // medium } - pub fn parse(input: &[ComponentValue], base_url: &Url) -> Option<SpecifiedValue> { + pub fn parse(input: &[ComponentValue], base_url: &Url) -> Result<SpecifiedValue, ()> { one_component_value(input).and_then(|c| parse_border_width(c, base_url)) } #[inline] @@ -346,7 +347,7 @@ pub mod longhands { pub fn get_initial_value() -> computed_value::T { computed::LPA_Auto } #[inline] pub fn from_component_value(v: &ComponentValue, _base_url: &Url) - -> Option<SpecifiedValue> { + -> Result<SpecifiedValue, ()> { specified::LengthOrPercentageOrAuto::parse_non_negative(v) } pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context) @@ -387,18 +388,18 @@ pub mod longhands { } /// normal | <number> | <length> | <percentage> pub fn from_component_value(input: &ComponentValue, _base_url: &Url) - -> Option<SpecifiedValue> { + -> Result<SpecifiedValue, ()> { match input { &ast::Number(ref value) if value.value >= 0. - => Some(SpecifiedNumber(value.value)), + => Ok(SpecifiedNumber(value.value)), &ast::Percentage(ref value) if value.value >= 0. - => Some(SpecifiedLength(specified::Em(value.value / 100.))), + => Ok(SpecifiedLength(specified::Em(value.value / 100.))), &Dimension(ref value, ref unit) if value.value >= 0. => specified::Length::parse_dimension(value.value, unit.as_slice()) .map(SpecifiedLength), &Ident(ref value) if value.as_slice().eq_ignore_ascii_case("normal") - => Some(SpecifiedNormal), - _ => None, + => Ok(SpecifiedNormal), + _ => Err(()), } } pub mod computed_value { @@ -474,14 +475,14 @@ pub mod longhands { /// baseline | sub | super | top | text-top | middle | bottom | text-bottom /// | <percentage> | <length> pub fn from_component_value(input: &ComponentValue, _base_url: &Url) - -> Option<SpecifiedValue> { + -> Result<SpecifiedValue, ()> { match input { &Ident(ref value) => { match value.as_slice().to_ascii_lower().as_slice() { % for keyword in vertical_align_keywords: - "${keyword}" => Some(Specified_${to_rust_ident(keyword)}), + "${keyword}" => Ok(Specified_${to_rust_ident(keyword)}), % endfor - _ => None, + _ => Err(()), } }, _ => specified::LengthOrPercentage::parse_non_negative(input) @@ -552,12 +553,12 @@ pub mod longhands { // normal | none | [ <string> ]+ // TODO: <uri>, <counter>, attr(<identifier>), open-quote, close-quote, no-open-quote, no-close-quote - pub fn parse(input: &[ComponentValue], _base_url: &Url) -> Option<SpecifiedValue> { + pub fn parse(input: &[ComponentValue], _base_url: &Url) -> Result<SpecifiedValue, ()> { match one_component_value(input) { - Some(&Ident(ref keyword)) => { + Ok(&Ident(ref keyword)) => { match keyword.as_slice().to_ascii_lower().as_slice() { - "normal" => return Some(normal), - "none" => return Some(none), + "normal" => return Ok(normal), + "none" => return Ok(none), _ => () } }, @@ -568,10 +569,10 @@ pub mod longhands { match component_value { &String(ref value) => content.push(StringContent(value.clone())), - _ => return None // invalid/unsupported value + _ => return Err(()) // invalid/unsupported value } } - Some(Content(content)) + Ok(Content(content)) } </%self:longhand> // CSS 2.1, Section 13 - Paged media @@ -593,14 +594,16 @@ pub mod longhands { #[inline] pub fn get_initial_value() -> SpecifiedValue { None } - pub fn from_component_value(component_value: &ComponentValue, base_url: &Url) -> Option<SpecifiedValue> { + pub fn from_component_value(component_value: &ComponentValue, base_url: &Url) + -> Result<SpecifiedValue, ()> { match component_value { &ast::URL(ref url) => { let image_url = parse_url(url.as_slice(), base_url); - Some(Some(image_url)) + Ok(Some(image_url)) }, - &ast::Ident(ref value) if value.as_slice().eq_ignore_ascii_case("none") => Some(None), - _ => None, + &ast::Ident(ref value) if value.as_slice().eq_ignore_ascii_case("none") + => Ok(None), + _ => Err(()), } } </%self:single_component_value> @@ -643,36 +646,29 @@ pub mod longhands { // FIXME(#1997, pcwalton): Support complete CSS2 syntax. pub fn parse_horizontal_and_vertical(horiz: &ComponentValue, vert: &ComponentValue) - -> Option<SpecifiedValue> { - let horiz = match specified::LengthOrPercentage::parse_non_negative(horiz) { - None => return None, - Some(value) => value, - }; - - let vert = match specified::LengthOrPercentage::parse_non_negative(vert) { - None => return None, - Some(value) => value, - }; + -> Result<SpecifiedValue, ()> { + let horiz = try!(specified::LengthOrPercentage::parse_non_negative(horiz)); + let vert = try!(specified::LengthOrPercentage::parse_non_negative(vert)); - Some(SpecifiedValue { + Ok(SpecifiedValue { horizontal: horiz, vertical: vert, }) } - pub fn parse(input: &[ComponentValue], _: &Url) -> Option<SpecifiedValue> { + pub fn parse(input: &[ComponentValue], _: &Url) -> Result<SpecifiedValue, ()> { let mut input_iter = input.skip_whitespace(); let horizontal = input_iter.next(); let vertical = input_iter.next(); if input_iter.next().is_some() { - return None + return Err(()) } match (horizontal, vertical) { (Some(horizontal), Some(vertical)) => { parse_horizontal_and_vertical(horizontal, vertical) } - _ => None + _ => Err(()) } } </%self:longhand> @@ -693,11 +689,11 @@ pub mod longhands { RGBA { red: 0., green: 0., blue: 0., alpha: 1. } /* black */ } pub fn parse_specified(input: &[ComponentValue], _base_url: &Url) - -> Option<DeclaredValue<SpecifiedValue>> { + -> Result<DeclaredValue<SpecifiedValue>, ()> { match one_component_value(input).and_then(Color::parse) { - Some(RGBA(rgba)) => Some(SpecifiedValue(rgba)), - Some(CurrentColor) => Some(CSSWideKeyword(Inherit)), - None => None, + Ok(RGBA(rgba)) => Ok(SpecifiedValue(rgba)), + Ok(CurrentColor) => Ok(Inherit), + Err(()) => Err(()), } } </%self:raw_longhand> @@ -722,61 +718,48 @@ pub mod longhands { pub type T = Vec<FontFamily>; } pub type SpecifiedValue = computed_value::T; - #[inline] pub fn get_initial_value() -> computed_value::T { vec!(FamilyName("serif".to_string())) } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + vec![FamilyName("serif".to_string())] + } /// <familiy-name># /// <familiy-name> = <string> | [ <ident>+ ] /// TODO: <generic-familiy> - pub fn parse(input: &[ComponentValue], _base_url: &Url) -> Option<SpecifiedValue> { - from_iter(input.skip_whitespace()) + pub fn parse(input: &[ComponentValue], _base_url: &Url) -> Result<SpecifiedValue, ()> { + parse_slice_comma_separated(input, parse_one_family) } - pub fn from_iter<'a>(mut iter: SkipWhitespaceIterator<'a>) -> Option<SpecifiedValue> { - let mut result = vec!(); - macro_rules! add( - ($value: expr, $b: expr) => { - { - result.push($value); - match iter.next() { - Some(&Comma) => (), - None => $b, - _ => return None, - } - } + pub fn parse_one_family<'a>(iter: ParserIter) -> Result<FontFamily, ()> { + // TODO: avoid copying strings? + let mut idents = match iter.next() { + Some(&String(ref value)) => return Ok(FamilyName(value.clone())), + Some(&Ident(ref value)) => { +// match value.as_slice().to_ascii_lower().as_slice() { +// "serif" => return Ok(Serif), +// "sans-serif" => return Ok(SansSerif), +// "cursive" => return Ok(Cursive), +// "fantasy" => return Ok(Fantasy), +// "monospace" => return Ok(Monospace), +// _ => { + vec![value.as_slice()] +// } +// } } - ) - 'outer: loop { - match iter.next() { - // TODO: avoid copying strings? - Some(&String(ref value)) => add!(FamilyName(value.clone()), break 'outer), - Some(&Ident(ref value)) => { - match value.as_slice().to_ascii_lower().as_slice() { -// "serif" => add!(Serif, break 'outer), -// "sans-serif" => add!(SansSerif, break 'outer), -// "cursive" => add!(Cursive, break 'outer), -// "fantasy" => add!(Fantasy, break 'outer), -// "monospace" => add!(Monospace, break 'outer), - _ => { - let mut idents = vec!(value.as_slice()); - loop { - match iter.next() { - Some(&Ident(ref value)) => idents.push(value.as_slice()), - Some(&Comma) => { - result.push(FamilyName(idents.connect(" "))); - break - }, - None => { - result.push(FamilyName(idents.connect(" "))); - break 'outer - }, - _ => return None, - } - } - } - } + _ => return Err(()) + }; + for component_value in iter { + match component_value { + &Ident(ref value) => { + idents.push(value.as_slice()); + iter.next(); + }, + _ => { + iter.push_back(component_value); + break } - _ => return None, } } - Some(result) + Ok(FamilyName(idents.connect(" "))) } </%self:longhand> @@ -795,30 +778,30 @@ pub mod longhands { } /// normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 pub fn from_component_value(input: &ComponentValue, _base_url: &Url) - -> Option<SpecifiedValue> { + -> Result<SpecifiedValue, ()> { match input { &Ident(ref value) => { match value.as_slice().to_ascii_lower().as_slice() { - "bold" => Some(SpecifiedWeight700), - "normal" => Some(SpecifiedWeight400), - "bolder" => Some(Bolder), - "lighter" => Some(Lighter), - _ => None, + "bold" => Ok(SpecifiedWeight700), + "normal" => Ok(SpecifiedWeight400), + "bolder" => Ok(Bolder), + "lighter" => Ok(Lighter), + _ => Err(()), } }, &Number(ref value) => match value.int_value { - Some(100) => Some(SpecifiedWeight100), - Some(200) => Some(SpecifiedWeight200), - Some(300) => Some(SpecifiedWeight300), - Some(400) => Some(SpecifiedWeight400), - Some(500) => Some(SpecifiedWeight500), - Some(600) => Some(SpecifiedWeight600), - Some(700) => Some(SpecifiedWeight700), - Some(800) => Some(SpecifiedWeight800), - Some(900) => Some(SpecifiedWeight900), - _ => None, + Some(100) => Ok(SpecifiedWeight100), + Some(200) => Ok(SpecifiedWeight200), + Some(300) => Ok(SpecifiedWeight300), + Some(400) => Ok(SpecifiedWeight400), + Some(500) => Ok(SpecifiedWeight500), + Some(600) => Ok(SpecifiedWeight600), + Some(700) => Ok(SpecifiedWeight700), + Some(800) => Ok(SpecifiedWeight800), + Some(900) => Ok(SpecifiedWeight900), + _ => Err(()), }, - _ => None + _ => Err(()) } } pub mod computed_value { @@ -890,7 +873,7 @@ pub mod longhands { /// <length> | <percentage> /// TODO: support <absolute-size> and <relative-size> pub fn from_component_value(input: &ComponentValue, _base_url: &Url) - -> Option<SpecifiedValue> { + -> Result<SpecifiedValue, ()> { specified::LengthOrPercentage::parse_non_negative(input).map(|value| { match value { specified::LP_Length(value) => value, @@ -927,30 +910,34 @@ pub mod longhands { none } /// none | [ underline || overline || line-through || blink ] - pub fn parse(input: &[ComponentValue], _base_url: &Url) -> Option<SpecifiedValue> { + pub fn parse(input: &[ComponentValue], _base_url: &Url) -> Result<SpecifiedValue, ()> { let mut result = SpecifiedValue { underline: false, overline: false, line_through: false, }; + match one_component_value(input) { + Ok(&Ident(ref value)) + if value.as_slice().eq_ignore_ascii_case("none") => return Ok(result), + _ => {} + } let mut blink = false; let mut empty = true; for component_value in input.skip_whitespace() { match get_ident_lower(component_value) { - None => return None, - Some(keyword) => match keyword.as_slice() { - "underline" => if result.underline { return None } + Err(()) => return Err(()), + Ok(keyword) => match keyword.as_slice() { + "underline" => if result.underline { return Err(()) } else { empty = false; result.underline = true }, - "overline" => if result.overline { return None } + "overline" => if result.overline { return Err(()) } else { empty = false; result.overline = true }, - "line-through" => if result.line_through { return None } + "line-through" => if result.line_through { return Err(()) } else { empty = false; result.line_through = true }, - "blink" => if blink { return None } + "blink" => if blink { return Err(()) } else { empty = false; blink = true }, - "none" => return if empty { Some(result) } else { None }, - _ => return None, + _ => return Err(()), } } } - if !empty { Some(result) } else { None } + if !empty { Ok(result) } else { Err(()) } } </%self:longhand> @@ -1070,7 +1057,7 @@ pub mod shorthands { pub ${sub_property.ident}: Option<${sub_property.ident}::SpecifiedValue>, % endfor } - pub fn parse(input: &[ComponentValue], base_url: &Url) -> Option<Longhands> { + pub fn parse(input: &[ComponentValue], base_url: &Url) -> Result<Longhands, ()> { ${caller.body()} } } @@ -1080,7 +1067,7 @@ pub mod shorthands { <%self:shorthand name="${name}" sub_properties="${ ' '.join(sub_property_pattern % side for side in ['top', 'right', 'bottom', 'left'])}"> - let mut iter = input.skip_whitespace().map(|c| ${parser_function}(c, base_url)); + let mut iter = input.skip_whitespace().map(|c| ${parser_function}(c, base_url).ok()); // zero or more than four values is invalid. // one value sets them all // two values set (top, bottom) and (left, right) @@ -1092,13 +1079,13 @@ pub mod shorthands { let left = iter.next().unwrap_or(right); if top.is_some() && right.is_some() && bottom.is_some() && left.is_some() && iter.next().is_none() { - Some(Longhands { + Ok(Longhands { % for side in ["top", "right", "bottom", "left"]: ${to_rust_ident(sub_property_pattern % side)}: ${side}, % endfor }) } else { - None + Err(()) } </%self:shorthand> </%def> @@ -1116,46 +1103,46 @@ pub mod shorthands { for component_value in input.skip_whitespace() { if color.is_none() { match background_color::from_component_value(component_value, base_url) { - Some(v) => { + Ok(v) => { color = Some(v); any = true; continue }, - None => () + Err(()) => () } } if image.is_none() { match background_image::from_component_value(component_value, base_url) { - Some(v) => { + Ok(v) => { image = Some(v); any = true; continue }, - None => (), + Err(()) => (), } } if repeat.is_none() { match background_repeat::from_component_value(component_value, base_url) { - Some(v) => { + Ok(v) => { repeat = Some(v); any = true; continue }, - None => () + Err(()) => () } } if attachment.is_none() { match background_attachment::from_component_value(component_value, base_url) { - Some(v) => { + Ok(v) => { attachment = Some(v); any = true; continue }, - None => () + Err(()) => () } } @@ -1165,17 +1152,17 @@ pub mod shorthands { match background_position::parse_horizontal_and_vertical( saved_component_value, component_value) { - Some(v) => { + Ok(v) => { position = Some(v); any = true; continue }, - None => (), + Err(()) => (), } } // If we get here, parsing failed. - return None + return Err(()) } None => { // Save the component value. @@ -1185,7 +1172,7 @@ pub mod shorthands { } if any && last_component_value.is_none() { - Some(Longhands { + Ok(Longhands { background_color: color, background_image: image, background_position: position, @@ -1193,14 +1180,14 @@ pub mod shorthands { background_attachment: attachment, }) } else { - None + Err(()) } </%self:shorthand> ${four_sides_shorthand("margin", "margin-%s", "margin_top::from_component_value")} ${four_sides_shorthand("padding", "padding-%s", "padding_top::from_component_value")} - pub fn parse_color(value: &ComponentValue, _base_url: &Url) -> Option<specified::CSSColor> { + pub fn parse_color(value: &ComponentValue, _base_url: &Url) -> Result<specified::CSSColor, ()> { specified::CSSColor::parse(value) } ${four_sides_shorthand("border-color", "border-%s-color", "parse_color")} @@ -1209,9 +1196,9 @@ pub mod shorthands { ${four_sides_shorthand("border-width", "border-%s-width", "parse_border_width")} pub fn parse_border(input: &[ComponentValue], base_url: &Url) - -> Option<(Option<specified::CSSColor>, + -> Result<(Option<specified::CSSColor>, Option<border_top_style::SpecifiedValue>, - Option<specified::Length>)> { + Option<specified::Length>), ()> { let mut color = None; let mut style = None; let mut width = None; @@ -1219,25 +1206,25 @@ pub mod shorthands { for component_value in input.skip_whitespace() { if color.is_none() { match specified::CSSColor::parse(component_value) { - Some(c) => { color = Some(c); any = true; continue }, - None => () + Ok(c) => { color = Some(c); any = true; continue }, + Err(()) => () } } if style.is_none() { match border_top_style::from_component_value(component_value, base_url) { - Some(s) => { style = Some(s); any = true; continue }, - None => () + Ok(s) => { style = Some(s); any = true; continue }, + Err(()) => () } } if width.is_none() { match parse_border_width(component_value, base_url) { - Some(w) => { width = Some(w); any = true; continue }, - None => () + Ok(w) => { width = Some(w); any = true; continue }, + Err(()) => () } } - return None + return Err(()) } - if any { Some((color, style, width)) } else { None } + if any { Ok((color, style, width)) } else { Err(()) } } @@ -1286,7 +1273,7 @@ pub mod shorthands { // font-style, font-weight and font-variant. // Leaves the values to None, 'normal' is the initial value for each of them. match get_ident_lower(component_value) { - Some(ref ident) if ident.as_slice().eq_ignore_ascii_case("normal") => { + Ok(ref ident) if ident.as_slice().eq_ignore_ascii_case("normal") => { nb_normals += 1; continue; } @@ -1294,25 +1281,25 @@ pub mod shorthands { } if style.is_none() { match font_style::from_component_value(component_value, base_url) { - Some(s) => { style = Some(s); continue }, - None => () + Ok(s) => { style = Some(s); continue }, + Err(()) => () } } if weight.is_none() { match font_weight::from_component_value(component_value, base_url) { - Some(w) => { weight = Some(w); continue }, - None => () + Ok(w) => { weight = Some(w); continue }, + Err(()) => () } } if variant.is_none() { match font_variant::from_component_value(component_value, base_url) { - Some(v) => { variant = Some(v); continue }, - None => () + Ok(v) => { variant = Some(v); continue }, + Err(()) => () } } match font_size::from_component_value(component_value, base_url) { - Some(s) => { size = Some(s); break }, - None => return None + Ok(s) => { size = Some(s); break }, + Err(()) => return Err(()) } } #[inline] @@ -1323,29 +1310,29 @@ pub mod shorthands { } } if size.is_none() || (count(&style) + count(&weight) + count(&variant) + nb_normals) > 3 { - return None + return Err(()) } let mut copied_iter = iter.clone(); match copied_iter.next() { Some(&Delim('/')) => { iter = copied_iter; line_height = match iter.next() { - Some(v) => line_height::from_component_value(v, base_url), - _ => return None, + Some(v) => line_height::from_component_value(v, base_url).ok(), + _ => return Err(()), }; - if line_height.is_none() { return None } + if line_height.is_none() { return Err(()) } } _ => () } - let family = font_family::from_iter(iter); - if family.is_none() { return None } - Some(Longhands { + let family = try!(parse_comma_separated( + &mut BufferedIter::new(iter), font_family::parse_one_family)); + Ok(Longhands { font_style: style, font_variant: variant, font_weight: weight, font_size: size, line_height: line_height, - font_family: family + font_family: Some(family) }) </%self:shorthand> @@ -1459,20 +1446,20 @@ pub fn parse_property_declaration_list<I: Iterator<Node>>(input: I, base_url: &U } -#[deriving(Clone)] pub enum CSSWideKeyword { - Initial, - Inherit, + InitialKeyword, + InheritKeyword, + UnsetKeyword, } impl CSSWideKeyword { - pub fn parse(input: &[ComponentValue]) -> Option<Option<CSSWideKeyword>> { + pub fn parse(input: &[ComponentValue]) -> Result<CSSWideKeyword, ()> { one_component_value(input).and_then(get_ident_lower).and_then(|keyword| { match keyword.as_slice() { - "initial" => Some(Some(Initial)), - "inherit" => Some(Some(Inherit)), - "unset" => Some(None), - _ => None + "initial" => Ok(InitialKeyword), + "inherit" => Ok(InheritKeyword), + "unset" => Ok(UnsetKeyword), + _ => Err(()) } }) } @@ -1482,7 +1469,11 @@ impl CSSWideKeyword { #[deriving(Clone)] pub enum DeclaredValue<T> { SpecifiedValue(T), - CSSWideKeyword(CSSWideKeyword), + Initial, + Inherit, + // There is no Unset variant here. + // The 'unset' keyword is represented as either Initial or Inherit, + // depending on whether the property is inherited. } #[deriving(Clone)] @@ -1521,12 +1512,12 @@ impl PropertyDeclaration { return ValidOrIgnoredDeclaration } match longhands::${property.ident}::parse_declared(value, base_url) { - Some(value) => { + Ok(value) => { seen.set_${property.ident}(); result_list.push(${property.camel_case}Declaration(value)); ValidOrIgnoredDeclaration }, - None => InvalidValue, + Err(()) => InvalidValue, } }, % else: @@ -1540,45 +1531,53 @@ impl PropertyDeclaration { return ValidOrIgnoredDeclaration } match CSSWideKeyword::parse(value) { - Some(Some(keyword)) => { + Ok(InheritKeyword) => { % for sub_property in shorthand.sub_properties: if !seen.get_${sub_property.ident}() { seen.set_${sub_property.ident}(); - result_list.push(${sub_property.camel_case}Declaration( - CSSWideKeyword(keyword))); + result_list.push( + ${sub_property.camel_case}Declaration(Inherit)); + } + % endfor + ValidOrIgnoredDeclaration + }, + Ok(InitialKeyword) => { + % for sub_property in shorthand.sub_properties: + if !seen.get_${sub_property.ident}() { + seen.set_${sub_property.ident}(); + result_list.push( + ${sub_property.camel_case}Declaration(Initial)); } % endfor ValidOrIgnoredDeclaration }, - Some(None) => { + Ok(UnsetKeyword) => { % for sub_property in shorthand.sub_properties: if !seen.get_${sub_property.ident}() { seen.set_${sub_property.ident}(); result_list.push(${sub_property.camel_case}Declaration( - CSSWideKeyword( - ${"Inherit" if sub_property.style_struct.inherited else "Initial"} - ) + ${"Inherit" if sub_property.style_struct.inherited else "Initial"} )); } % endfor ValidOrIgnoredDeclaration }, - None => match shorthands::${shorthand.ident}::parse(value, base_url) { - Some(result) => { + Err(()) => match shorthands::${shorthand.ident}::parse(value, base_url) { + Ok(result) => { % for sub_property in shorthand.sub_properties: if !seen.get_${sub_property.ident}() { seen.set_${sub_property.ident}(); result_list.push(${sub_property.camel_case}Declaration( match result.${sub_property.ident} { Some(value) => SpecifiedValue(value), - None => CSSWideKeyword(Initial), + None => Initial, } )); } % endfor ValidOrIgnoredDeclaration }, - None => InvalidValue, + Err(()) => InvalidValue, } } }, @@ -1788,7 +1787,7 @@ impl<T: Send + Share + Clone> ArcExperimental<T> for Arc<T> { } /// Fast path for the function below. Only computes new inherited styles. -fn cascade_with_cached_declarations(applicable_declarations: &[MatchedProperty], +fn cascade_with_cached_declarations(applicable_declarations: &[DeclarationBlock], shareable: bool, parent_style: &ComputedValues, cached_style: &ComputedValues, @@ -1824,9 +1823,9 @@ fn cascade_with_cached_declarations(applicable_declarations: &[MatchedProperty], (*specified_value).clone(), context ), - CSSWideKeyword(Initial) + Initial => longhands::${property.ident}::get_initial_value(), - CSSWideKeyword(Inherit) => { + Inherit => { // This is a bit slow, but this is rare so it shouldn't // matter. // @@ -1893,7 +1892,7 @@ fn cascade_with_cached_declarations(applicable_declarations: &[MatchedProperty], /// this is ignored. /// /// Returns the computed values and a boolean indicating whether the result is cacheable. -pub fn cascade(applicable_declarations: &[MatchedProperty], +pub fn cascade(applicable_declarations: &[DeclarationBlock], shareable: bool, parent_style: Option< &ComputedValues >, cached_style: Option< &ComputedValues >) @@ -1934,8 +1933,8 @@ pub fn cascade(applicable_declarations: &[MatchedProperty], ($style_struct_getter: ident, $property: ident, $declared_value: expr) => { match *$declared_value { SpecifiedValue(specified_value) => specified_value, - CSSWideKeyword(Initial) => longhands::$property::get_initial_value(), - CSSWideKeyword(Inherit) => inherited_style.$style_struct_getter().$property.clone(), + Initial => longhands::$property::get_initial_value(), + Inherit => inherited_style.$style_struct_getter().$property.clone(), } }; ) @@ -1950,8 +1949,8 @@ pub fn cascade(applicable_declarations: &[MatchedProperty], context.font_size = match *value { SpecifiedValue(specified_value) => computed::compute_Au_with_font_size( specified_value, context.inherited_font_size), - CSSWideKeyword(Initial) => longhands::font_size::get_initial_value(), - CSSWideKeyword(Inherit) => context.inherited_font_size, + Initial => longhands::font_size::get_initial_value(), + Inherit => context.inherited_font_size, } } ColorDeclaration(ref value) => { @@ -2031,9 +2030,9 @@ pub fn cascade(applicable_declarations: &[MatchedProperty], (*specified_value).clone(), &context ), - CSSWideKeyword(Initial) + Initial => longhands::${property.ident}::get_initial_value(), - CSSWideKeyword(Inherit) => { + Inherit => { // This is a bit slow, but this is rare so it shouldn't // matter. // diff --git a/src/components/style/selector_matching.rs b/src/components/style/selector_matching.rs index 86b3cd9e058..0747495b14f 100644 --- a/src/components/style/selector_matching.rs +++ b/src/components/style/selector_matching.rs @@ -3,10 +3,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::collections::hashmap::HashMap; -use std::ascii::StrAsciiExt; +use std::hash::Hash; use std::num::div_rem; use sync::Arc; +use url::Url; + use servo_util::atom::Atom; use servo_util::namespace; use servo_util::smallvec::VecLike; @@ -16,7 +18,7 @@ use media_queries::{Device, Screen}; use node::{TElement, TNode}; use properties::{PropertyDeclaration, PropertyDeclarationBlock}; use selectors::*; -use stylesheets::{Stylesheet, iter_style_rules}; +use stylesheets::{Stylesheet, iter_stylesheet_style_rules}; pub enum StylesheetOrigin { UserAgentOrigin, @@ -50,7 +52,10 @@ struct SelectorMap { // TODO: Tune the initial capacity of the HashMap id_hash: HashMap<Atom, Vec<Rule>>, class_hash: HashMap<Atom, Vec<Rule>>, - element_hash: HashMap<Atom, Vec<Rule>>, + local_name_hash: HashMap<Atom, Vec<Rule>>, + /// Same as local_name_hash, but keys are lower-cased. + /// For HTML elements in HTML documents. + lower_local_name_hash: HashMap<Atom, Vec<Rule>>, // For Rules that don't have ID, class, or element selectors. universal_rules: Vec<Rule>, /// Whether this hash is empty. @@ -62,7 +67,8 @@ impl SelectorMap { SelectorMap { id_hash: HashMap::new(), class_hash: HashMap::new(), - element_hash: HashMap::new(), + local_name_hash: HashMap::new(), + lower_local_name_hash: HashMap::new(), universal_rules: vec!(), empty: true, } @@ -74,7 +80,7 @@ impl SelectorMap { /// Sort the Rules at the end to maintain cascading order. fn get_all_matching_rules<E:TElement, N:TNode<E>, - V:VecLike<MatchedProperty>>( + V:VecLike<DeclarationBlock>>( &self, node: &N, matching_rules_list: &mut V, @@ -111,13 +117,16 @@ impl SelectorMap { None => {} } - // HTML elements in HTML documents must be matched case-insensitively. - // TODO(pradeep): Case-sensitivity depends on the document type. - SelectorMap::get_matching_rules_from_hash_ignoring_case(node, - &self.element_hash, - element.get_local_name().as_slice(), - matching_rules_list, - shareable); + let local_name_hash = if node.is_html_element_in_html_document() { + &self.lower_local_name_hash + } else { + &self.local_name_hash + }; + SelectorMap::get_matching_rules_from_hash(node, + local_name_hash, + element.get_local_name(), + matching_rules_list, + shareable); SelectorMap::get_matching_rules(node, self.universal_rules.as_slice(), @@ -125,12 +134,16 @@ impl SelectorMap { shareable); // Sort only the rules we just added. - sort::quicksort(matching_rules_list.vec_mut_slice_from(init_len)); + sort::quicksort_by(matching_rules_list.vec_mut_slice_from(init_len), compare); + + fn compare(a: &DeclarationBlock, b: &DeclarationBlock) -> Ordering { + (a.specificity, a.source_order).cmp(&(b.specificity, b.source_order)) + } } fn get_matching_rules_from_hash<E:TElement, N:TNode<E>, - V:VecLike<MatchedProperty>>( + V:VecLike<DeclarationBlock>>( node: &N, hash: &HashMap<Atom, Vec<Rule>>, key: &Atom, @@ -144,83 +157,45 @@ impl SelectorMap { } } - fn get_matching_rules_from_hash_ignoring_case<E:TElement, - N:TNode<E>, - V:VecLike<MatchedProperty>>( - node: &N, - hash: &HashMap<Atom, Vec<Rule>>, - key: &str, - matching_rules: &mut V, - shareable: &mut bool) { - // FIXME: Precache the lower case version as an atom. - match hash.find(&Atom::from_slice(key.to_ascii_lower().as_slice())) { - Some(rules) => { - SelectorMap::get_matching_rules(node, rules.as_slice(), matching_rules, shareable) - } - None => {} - } - } - /// Adds rules in `rules` that match `node` to the `matching_rules` list. fn get_matching_rules<E:TElement, N:TNode<E>, - V:VecLike<MatchedProperty>>( + V:VecLike<DeclarationBlock>>( node: &N, rules: &[Rule], matching_rules: &mut V, shareable: &mut bool) { for rule in rules.iter() { if matches_compound_selector(&*rule.selector, node, shareable) { - // TODO(pradeep): Is the cloning inefficient? - matching_rules.vec_push(rule.property.clone()); + matching_rules.vec_push(rule.declarations.clone()); } } } /// Insert rule into the correct hash. - /// Order in which to try: id_hash, class_hash, element_hash, universal_rules. + /// Order in which to try: id_hash, class_hash, local_name_hash, universal_rules. fn insert(&mut self, rule: Rule) { self.empty = false; match SelectorMap::get_id_name(&rule) { Some(id_name) => { - match self.id_hash.find_mut(&id_name) { - Some(rules) => { - rules.push(rule); - return; - } - None => {} - } - self.id_hash.insert(id_name, vec!(rule)); + self.id_hash.find_push(id_name, rule); return; } None => {} } match SelectorMap::get_class_name(&rule) { Some(class_name) => { - match self.class_hash.find_mut(&class_name) { - Some(rules) => { - rules.push(rule); - return; - } - None => {} - } - self.class_hash.insert(class_name, vec!(rule)); + self.class_hash.find_push(class_name, rule); return; } None => {} } - match SelectorMap::get_element_name(&rule) { - Some(element_name) => { - match self.element_hash.find_mut(&element_name) { - Some(rules) => { - rules.push(rule); - return; - } - None => {} - } - self.element_hash.insert(element_name, vec!(rule)); + match SelectorMap::get_local_name(&rule) { + Some(LocalNameSelector { name, lower_name }) => { + self.local_name_hash.find_push(name, rule.clone()); + self.lower_local_name_hash.find_push(lower_name, rule); return; } None => {} @@ -258,14 +233,12 @@ impl SelectorMap { } /// Retrieve the name if it is a type selector, or None otherwise. - fn get_element_name(rule: &Rule) -> Option<Atom> { + fn get_local_name(rule: &Rule) -> Option<LocalNameSelector> { let simple_selector_sequence = &rule.selector.simple_selectors; for ss in simple_selector_sequence.iter() { match *ss { - // HTML elements in HTML documents must be matched case-insensitively - // TODO: case-sensitivity depends on the document type LocalNameSelector(ref name) => { - return Some(Atom::from_slice(name.as_slice().to_ascii_lower().as_slice())); + return Some(name.clone()) } _ => {} } @@ -284,12 +257,19 @@ pub struct Stylist { impl Stylist { #[inline] pub fn new() -> Stylist { - Stylist { + let mut stylist = Stylist { element_map: PerPseudoElementSelectorMap::new(), before_map: PerPseudoElementSelectorMap::new(), after_map: PerPseudoElementSelectorMap::new(), rules_source_order: 0u, - } + }; + let ua_stylesheet = Stylesheet::from_bytes( + include_bin!("user-agent.css"), + Url::parse("chrome:///user-agent.css").unwrap(), + None, + None); + stylist.add_stylesheet(ua_stylesheet, UserAgentOrigin); + stylist } pub fn add_stylesheet(&mut self, stylesheet: Stylesheet, origin: StylesheetOrigin) { @@ -325,7 +305,7 @@ impl Stylist { }; map.$priority.insert(Rule { selector: selector.compound_selectors.clone(), - property: MatchedProperty { + declarations: DeclarationBlock { specificity: selector.specificity, declarations: $style_rule.declarations.$priority.clone(), source_order: rules_source_order, @@ -337,7 +317,7 @@ impl Stylist { ); let device = &Device { media_type: Screen }; // TODO, use Print when printing - iter_style_rules(stylesheet.rules.as_slice(), device, |style_rule| { + iter_stylesheet_style_rules(&stylesheet, device, |style_rule| { append!(style_rule, normal); append!(style_rule, important); rules_source_order += 1; @@ -353,7 +333,7 @@ impl Stylist { /// in `css::matching::PrivateMatchMethods::candidate_element_allows_for_style_sharing`. pub fn push_applicable_declarations<E:TElement, N:TNode<E>, - V:VecLike<MatchedProperty>>( + V:VecLike<DeclarationBlock>>( &self, element: &N, style_attribute: Option<&PropertyDeclarationBlock>, @@ -382,7 +362,7 @@ impl Stylist { // Step 2: Normal style attributes. style_attribute.map(|sa| { shareable = false; - applicable_declarations.vec_push(MatchedProperty::from_declarations(sa.normal.clone())) + applicable_declarations.vec_push(DeclarationBlock::from_declarations(sa.normal.clone())) }); // Step 3: Author-supplied `!important` rules. @@ -393,7 +373,7 @@ impl Stylist { // Step 4: `!important` style attributes. style_attribute.map(|sa| { shareable = false; - applicable_declarations.vec_push(MatchedProperty::from_declarations(sa.important.clone())) + applicable_declarations.vec_push(DeclarationBlock::from_declarations(sa.important.clone())) }); // Step 5: User and UA `!important` rules. @@ -446,22 +426,22 @@ struct Rule { // that it matches. Selector contains an owned vector (through // CompoundSelector) and we want to avoid the allocation. selector: Arc<CompoundSelector>, - property: MatchedProperty, + declarations: DeclarationBlock, } /// A property declaration together with its precedence among rules of equal specificity so that /// we can sort them. #[deriving(Clone)] -pub struct MatchedProperty { +pub struct DeclarationBlock { pub declarations: Arc<Vec<PropertyDeclaration>>, source_order: uint, specificity: u32, } -impl MatchedProperty { +impl DeclarationBlock { #[inline] - pub fn from_declarations(declarations: Arc<Vec<PropertyDeclaration>>) -> MatchedProperty { - MatchedProperty { + pub fn from_declarations(declarations: Arc<Vec<PropertyDeclaration>>) -> DeclarationBlock { + DeclarationBlock { declarations: declarations, source_order: 0, specificity: 0, @@ -469,34 +449,12 @@ impl MatchedProperty { } } -impl PartialEq for MatchedProperty { - #[inline] - fn eq(&self, other: &MatchedProperty) -> bool { - let this_rank = (self.specificity, self.source_order); - let other_rank = (other.specificity, other.source_order); - this_rank == other_rank - } +pub fn matches<E:TElement, N:TNode<E>>(selector_list: &SelectorList, element: &N) -> bool { + get_selector_list_selectors(selector_list).iter().any(|selector| + selector.pseudo_element.is_none() && + matches_compound_selector(&*selector.compound_selectors, element, &mut false)) } -impl Eq for MatchedProperty {} - -impl PartialOrd for MatchedProperty { - #[inline] - fn partial_cmp(&self, other: &MatchedProperty) -> Option<Ordering> { - let this_rank = (self.specificity, self.source_order); - let other_rank = (other.specificity, other.source_order); - this_rank.partial_cmp(&other_rank) - } -} - -impl Ord for MatchedProperty { - #[inline] - fn cmp(&self, other: &MatchedProperty) -> Ordering { - let this_rank = (self.specificity, self.source_order); - let other_rank = (other.specificity, other.source_order); - this_rank.cmp(&other_rank) - } -} /// Determines whether the given element matches the given single or compound selector. /// @@ -504,7 +462,7 @@ impl Ord for MatchedProperty { /// `shareable` to false unless you are willing to update the style sharing logic. Otherwise things /// will almost certainly break as nodes will start mistakenly sharing styles. (See the code in /// `main/css/matching.rs`.) -pub fn matches_compound_selector<E:TElement, +fn matches_compound_selector<E:TElement, N:TNode<E>>( selector: &CompoundSelector, element: &N, @@ -645,11 +603,10 @@ fn matches_simple_selector<E:TElement, shareable: &mut bool) -> bool { match *selector { - // TODO: case-sensitivity depends on the document type - // TODO: intern element names - LocalNameSelector(ref name) => { + LocalNameSelector(LocalNameSelector { ref name, ref lower_name }) => { + let name = if element.is_html_element_in_html_document() { lower_name } else { name }; let element = element.as_element(); - element.get_local_name().as_slice().eq_ignore_ascii_case(name.as_slice()) + element.get_local_name() == name } NamespaceSelector(ref namespace) => { @@ -934,11 +891,30 @@ fn matches_last_child<E:TElement,N:TNode<E>>(element: &N) -> bool { } +trait FindPush<K, V> { + fn find_push(&mut self, key: K, value: V); +} + +impl<K: Eq + Hash, V> FindPush<K, V> for HashMap<K, Vec<V>> { + fn find_push(&mut self, key: K, value: V) { + match self.find_mut(&key) { + Some(vec) => { + vec.push(value); + return + } + None => {} + } + self.insert(key, vec![value]); + } +} + + #[cfg(test)] mod tests { use servo_util::atom::Atom; use sync::Arc; - use super::{MatchedProperty, Rule, SelectorMap}; + use super::{DeclarationBlock, Rule, SelectorMap}; + use selectors::LocalNameSelector; /// Helper method to get some Rules from selector strings. /// Each sublist of the result contains the Rules for one StyleRule. @@ -949,11 +925,11 @@ mod tests { let namespaces = NamespaceMap::new(); css_selectors.iter().enumerate().map(|(i, selectors)| { - parse_selector_list(tokenize(*selectors).map(|(c, _)| c).collect(), &namespaces) + parse_selector_list(tokenize(*selectors).map(|(c, _)| c), &namespaces) .unwrap().move_iter().map(|s| { Rule { selector: s.compound_selectors.clone(), - property: MatchedProperty { + declarations: DeclarationBlock { specificity: s.specificity, declarations: Arc::new(vec!()), source_order: i, @@ -966,9 +942,10 @@ mod tests { #[test] fn test_rule_ordering_same_specificity(){ let rules_list = get_mock_rules(["a.intro", "img.sidebar"]); - let rule1 = rules_list[0][0].clone(); - let rule2 = rules_list[1][0].clone(); - assert!(rule1.property < rule2.property, "The rule that comes later should win."); + let a = &rules_list[0][0].declarations; + let b = &rules_list[1][0].declarations; + assert!((a.specificity, a.source_order).cmp(&(b.specificity, b.source_order)) == Less, + "The rule that comes later should win."); } #[test] @@ -986,12 +963,18 @@ mod tests { } #[test] - fn test_get_element_name(){ + fn test_get_local_name(){ let rules_list = get_mock_rules(["img.foo", "#top", "IMG", "ImG"]); - assert_eq!(SelectorMap::get_element_name(&rules_list[0][0]), Some(Atom::from_slice("img"))); - assert_eq!(SelectorMap::get_element_name(&rules_list[1][0]), None); - assert_eq!(SelectorMap::get_element_name(&rules_list[2][0]), Some(Atom::from_slice("img"))); - assert_eq!(SelectorMap::get_element_name(&rules_list[3][0]), Some(Atom::from_slice("img"))); + let check = |i, names: Option<(&str, &str)>| { + assert!(SelectorMap::get_local_name(&rules_list[i][0]) + == names.map(|(name, lower_name)| LocalNameSelector { + name: Atom::from_slice(name), + lower_name: Atom::from_slice(lower_name) })) + }; + check(0, Some(("img", "img"))); + check(1, None); + check(2, Some(("IMG", "img"))); + check(3, Some(("ImG", "img"))); } #[test] @@ -999,9 +982,9 @@ mod tests { let rules_list = get_mock_rules([".intro.foo", "#top"]); let mut selector_map = SelectorMap::new(); selector_map.insert(rules_list[1][0].clone()); - assert_eq!(1, selector_map.id_hash.find(&Atom::from_slice("top")).unwrap()[0].property.source_order); + assert_eq!(1, selector_map.id_hash.find(&Atom::from_slice("top")).unwrap()[0].declarations.source_order); selector_map.insert(rules_list[0][0].clone()); - assert_eq!(0, selector_map.class_hash.find(&Atom::from_slice("intro")).unwrap()[0].property.source_order); + assert_eq!(0, selector_map.class_hash.find(&Atom::from_slice("intro")).unwrap()[0].declarations.source_order); assert!(selector_map.class_hash.find(&Atom::from_slice("foo")).is_none()); } } diff --git a/src/components/style/selectors.rs b/src/components/style/selectors.rs index 29fff94acf5..44b098fa329 100644 --- a/src/components/style/selectors.rs +++ b/src/components/style/selectors.rs @@ -3,12 +3,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::{cmp, iter}; -use std::ascii::StrAsciiExt; -use std::vec; +use std::ascii::{StrAsciiExt, OwnedStrAsciiExt}; use sync::Arc; use cssparser::ast::*; -use cssparser::parse_nth; +use cssparser::{tokenize, parse_nth}; use servo_util::atom::Atom; use servo_util::namespace::Namespace; @@ -59,7 +58,7 @@ pub enum Combinator { pub enum SimpleSelector { IDSelector(Atom), ClassSelector(Atom), - LocalNameSelector(Atom), + LocalNameSelector(LocalNameSelector), NamespaceSelector(Namespace), // Attribute selectors @@ -94,6 +93,12 @@ pub enum SimpleSelector { } #[deriving(PartialEq, Clone)] +pub struct LocalNameSelector { + pub name: Atom, + pub lower_name: Atom, +} + +#[deriving(PartialEq, Clone)] pub struct AttrSelector { pub name: String, pub lower_name: String, @@ -107,21 +112,31 @@ pub enum NamespaceConstraint { } -type Iter = iter::Peekable<ComponentValue, vec::MoveItems<ComponentValue>>; +pub fn parse_selector_list_from_str(input: &str) -> Result<SelectorList, ()> { + let namespaces = NamespaceMap::new(); + let iter = tokenize(input).map(|(token, _)| token); + parse_selector_list(iter, &namespaces).map(|s| SelectorList { selectors: s }) +} +/// Re-exported to script, but opaque. +pub struct SelectorList { + selectors: Vec<Selector> +} + +/// Public to the style crate, but not re-exported to script +pub fn get_selector_list_selectors<'a>(selector_list: &'a SelectorList) -> &'a [Selector] { + selector_list.selectors.as_slice() +} /// Parse a comma-separated list of Selectors. /// aka Selector Group in http://www.w3.org/TR/css3-selectors/#grouping /// /// Return the Selectors or None if there is an invalid selector. -pub fn parse_selector_list(input: Vec<ComponentValue>, namespaces: &NamespaceMap) - -> Option<Vec<Selector>> { - let iter = &mut input.move_iter().peekable(); - let first = match parse_selector(iter, namespaces) { - None => return None, - Some(result) => result - }; - let mut results = vec!(first); +pub fn parse_selector_list<I: Iterator<ComponentValue>>( + iter: I, namespaces: &NamespaceMap) + -> Result<Vec<Selector>, ()> { + let iter = &mut iter.peekable(); + let mut results = vec![try!(parse_selector(iter, namespaces))]; loop { skip_whitespace(iter); @@ -130,29 +145,25 @@ pub fn parse_selector_list(input: Vec<ComponentValue>, namespaces: &NamespaceMap Some(&Comma) => { iter.next(); } - _ => return None, - } - match parse_selector(iter, namespaces) { - Some(selector) => results.push(selector), - None => return None, + _ => return Err(()), } + results.push(try!(parse_selector(iter, namespaces))); } - Some(results) + Ok(results) } +type Iter<I> = iter::Peekable<ComponentValue, I>; + /// Build up a Selector. /// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ; /// -/// None means invalid selector. -fn parse_selector(iter: &mut Iter, namespaces: &NamespaceMap) - -> Option<Selector> { - let (first, pseudo_element) = match parse_simple_selectors(iter, namespaces) { - None => return None, - Some(result) => result - }; +/// `Err` means invalid selector. +fn parse_selector<I: Iterator<ComponentValue>>( + iter: &mut Iter<I>, namespaces: &NamespaceMap) + -> Result<Selector, ()> { + let (first, mut pseudo_element) = try!(parse_simple_selectors(iter, namespaces)); let mut compound = CompoundSelector{ simple_selectors: first, next: None }; - let mut pseudo_element = pseudo_element; while pseudo_element.is_none() { let any_whitespace = skip_whitespace(iter); @@ -164,21 +175,17 @@ fn parse_selector(iter: &mut Iter, namespaces: &NamespaceMap) Some(&Delim('~')) => { iter.next(); LaterSibling }, Some(_) => { if any_whitespace { Descendant } - else { return None } + else { return Err(()) } } }; - match parse_simple_selectors(iter, namespaces) { - None => return None, - Some((simple_selectors, pseudo)) => { - compound = CompoundSelector { - simple_selectors: simple_selectors, - next: Some((box compound, combinator)) - }; - pseudo_element = pseudo; - } - } + let (simple_selectors, pseudo) = try!(parse_simple_selectors(iter, namespaces)); + compound = CompoundSelector { + simple_selectors: simple_selectors, + next: Some((box compound, combinator)) + }; + pseudo_element = pseudo; } - Some(Selector { + Ok(Selector { specificity: compute_specificity(&compound, &pseudo_element), compound_selectors: Arc::new(compound), pseudo_element: pseudo_element, @@ -245,43 +252,39 @@ fn compute_specificity(mut selector: &CompoundSelector, /// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]* /// | [ HASH | class | attrib | pseudo | negation ]+ /// -/// None means invalid selector -fn parse_simple_selectors(iter: &mut Iter, namespaces: &NamespaceMap) - -> Option<(Vec<SimpleSelector>, Option<PseudoElement>)> { +/// `Err(())` means invalid selector +fn parse_simple_selectors<I: Iterator<ComponentValue>>( + iter: &mut Iter<I>, namespaces: &NamespaceMap) + -> Result<(Vec<SimpleSelector>, Option<PseudoElement>), ()> { let mut empty = true; - let mut simple_selectors = match parse_type_selector(iter, namespaces) { - InvalidTypeSelector => return None, - NotATypeSelector => vec!(), - TypeSelector(s) => { empty = false; s } + let mut simple_selectors = match try!(parse_type_selector(iter, namespaces)) { + None => vec![], + Some(s) => { empty = false; s } }; let mut pseudo_element = None; loop { - match parse_one_simple_selector(iter, namespaces, /* inside_negation = */ false) { - InvalidSimpleSelector => return None, - NotASimpleSelector => break, - SimpleSelectorResult(s) => { simple_selectors.push(s); empty = false }, - PseudoElementResult(p) => { pseudo_element = Some(p); empty = false; break }, + match try!(parse_one_simple_selector(iter, namespaces, /* inside_negation = */ false)) { + None => break, + Some(SimpleSelectorResult(s)) => { simple_selectors.push(s); empty = false }, + Some(PseudoElementResult(p)) => { pseudo_element = Some(p); empty = false; break }, } } - if empty { None } // An empty selector is invalid - else { Some((simple_selectors, pseudo_element)) } + if empty { Err(()) } // An empty selector is invalid + else { Ok((simple_selectors, pseudo_element)) } } -enum TypeSelectorParseResult { - InvalidTypeSelector, - NotATypeSelector, - TypeSelector(Vec<SimpleSelector>), // Length 0 (*|*), 1 (*|E or ns|*) or 2 (|E or ns|E) -} - -fn parse_type_selector(iter: &mut Iter, namespaces: &NamespaceMap) - -> TypeSelectorParseResult { +/// * `Err(())`: Invalid selector, abort +/// * `Ok(None)`: Not a type selector, could be something else. `iter` was not consumed. +/// * `Ok(Some(vec))`: Length 0 (`*|*`), 1 (`*|E` or `ns|*`) or 2 (`|E` or `ns|E`) +fn parse_type_selector<I: Iterator<ComponentValue>>( + iter: &mut Iter<I>, namespaces: &NamespaceMap) + -> Result<Option<Vec<SimpleSelector>>, ()> { skip_whitespace(iter); - match parse_qualified_name(iter, /* in_attr_selector = */ false, namespaces) { - InvalidQualifiedName => InvalidTypeSelector, - NotAQualifiedName => NotATypeSelector, - QualifiedName(namespace, local_name) => { + match try!(parse_qualified_name(iter, /* in_attr_selector = */ false, namespaces)) { + None => Ok(None), + Some((namespace, local_name)) => { let mut simple_selectors = vec!(); match namespace { SpecificNamespace(ns) => simple_selectors.push(NamespaceSelector(ns)), @@ -289,121 +292,115 @@ fn parse_type_selector(iter: &mut Iter, namespaces: &NamespaceMap) } match local_name { Some(name) => { - let name_atom = Atom::from_slice(name.as_slice()); - simple_selectors.push(LocalNameSelector(name_atom)) + simple_selectors.push(LocalNameSelector(LocalNameSelector { + name: Atom::from_slice(name.as_slice()), + lower_name: Atom::from_slice(name.into_ascii_lower().as_slice()) + })) } None => (), } - TypeSelector(simple_selectors) + Ok(Some(simple_selectors)) } } } enum SimpleSelectorParseResult { - InvalidSimpleSelector, - NotASimpleSelector, SimpleSelectorResult(SimpleSelector), PseudoElementResult(PseudoElement), } -// Parse a simple selector other than a type selector -fn parse_one_simple_selector(iter: &mut Iter, namespaces: &NamespaceMap, inside_negation: bool) - -> SimpleSelectorParseResult { +/// Parse a simple selector other than a type selector. +/// +/// * `Err(())`: Invalid selector, abort +/// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed. +/// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element +fn parse_one_simple_selector<I: Iterator<ComponentValue>>( + iter: &mut Iter<I>, namespaces: &NamespaceMap, inside_negation: bool) + -> Result<Option<SimpleSelectorParseResult>, ()> { match iter.peek() { Some(&IDHash(_)) => match iter.next() { - Some(IDHash(id)) => SimpleSelectorResult(IDSelector(Atom::from_slice(id.as_slice()))), + Some(IDHash(id)) => Ok(Some(SimpleSelectorResult( + IDSelector(Atom::from_slice(id.as_slice()))))), _ => fail!("Implementation error, this should not happen."), }, Some(&Delim('.')) => { iter.next(); match iter.next() { - Some(Ident(class)) => SimpleSelectorResult(ClassSelector(Atom::from_slice(class.as_slice()))), - _ => InvalidSimpleSelector, + Some(Ident(class)) => Ok(Some(SimpleSelectorResult( + ClassSelector(Atom::from_slice(class.as_slice()))))), + _ => Err(()), } } Some(&SquareBracketBlock(_)) => match iter.next() { Some(SquareBracketBlock(content)) - => match parse_attribute_selector(content, namespaces) { - None => InvalidSimpleSelector, - Some(simple_selector) => SimpleSelectorResult(simple_selector), - }, + => Ok(Some(SimpleSelectorResult(try!(parse_attribute_selector(content, namespaces))))), _ => fail!("Implementation error, this should not happen."), }, Some(&Colon) => { iter.next(); match iter.next() { Some(Ident(name)) => match parse_simple_pseudo_class(name.as_slice()) { - None => { + Err(()) => { match name.as_slice().to_ascii_lower().as_slice() { // Supported CSS 2.1 pseudo-elements only. // ** Do not add to this list! ** - "before" => PseudoElementResult(Before), - "after" => PseudoElementResult(After), + "before" => Ok(Some(PseudoElementResult(Before))), + "after" => Ok(Some(PseudoElementResult(After))), // "first-line" => PseudoElementResult(FirstLine), // "first-letter" => PseudoElementResult(FirstLetter), - _ => InvalidSimpleSelector + _ => Err(()) } }, - Some(result) => SimpleSelectorResult(result), - }, - Some(Function(name, arguments)) => match parse_functional_pseudo_class( - name, arguments, namespaces, inside_negation) { - None => InvalidSimpleSelector, - Some(simple_selector) => SimpleSelectorResult(simple_selector), + Ok(result) => Ok(Some(SimpleSelectorResult(result))), }, + Some(Function(name, arguments)) + => Ok(Some(SimpleSelectorResult(try!(parse_functional_pseudo_class( + name, arguments, namespaces, inside_negation))))), Some(Colon) => { match iter.next() { - Some(Ident(name)) => match parse_pseudo_element(name) { - Some(pseudo_element) => PseudoElementResult(pseudo_element), - _ => InvalidSimpleSelector, - }, - _ => InvalidSimpleSelector, + Some(Ident(name)) + => Ok(Some(PseudoElementResult(try!(parse_pseudo_element(name))))), + _ => Err(()), } } - _ => InvalidSimpleSelector, + _ => Err(()), } } - _ => NotASimpleSelector, + _ => Ok(None), } } -enum QualifiedNameParseResult { - InvalidQualifiedName, - NotAQualifiedName, - // Namespace URL, local name. None means '*' - QualifiedName(NamespaceConstraint, Option<String>) -} - -fn parse_qualified_name(iter: &mut Iter, in_attr_selector: bool, namespaces: &NamespaceMap) - -> QualifiedNameParseResult { - #[inline] - fn default_namespace(namespaces: &NamespaceMap, local_name: Option<String>) - -> QualifiedNameParseResult { - QualifiedName(match namespaces.default { +/// * `Err(())`: Invalid selector, abort +/// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed. +/// * `Ok(Some((namespace, local_name)))`: `None` for the local name means a `*` universal selector +fn parse_qualified_name<I: Iterator<ComponentValue>>( + iter: &mut Iter<I>, in_attr_selector: bool, namespaces: &NamespaceMap) + -> Result<Option<(NamespaceConstraint, Option<String>)>, ()> { + let default_namespace = |local_name| { + let namespace = match namespaces.default { Some(ref ns) => SpecificNamespace(ns.clone()), None => AnyNamespace, - }, local_name) - } + }; + Ok(Some((namespace, local_name))) + }; - #[inline] - fn explicit_namespace(iter: &mut Iter, in_attr_selector: bool, namespace: NamespaceConstraint) - -> QualifiedNameParseResult { + let explicit_namespace = |iter: &mut Iter<I>, namespace| { assert!(iter.next() == Some(Delim('|')), "Implementation error, this should not happen."); match iter.peek() { Some(&Delim('*')) if !in_attr_selector => { iter.next(); - QualifiedName(namespace, None) + Ok(Some((namespace, None))) }, Some(&Ident(_)) => { let local_name = get_next_ident(iter); - QualifiedName(namespace, Some(local_name)) + Ok(Some((namespace, Some(local_name)))) }, - _ => InvalidQualifiedName, + _ => Err(()), } - } + }; match iter.peek() { Some(&Ident(_)) => { @@ -411,40 +408,39 @@ fn parse_qualified_name(iter: &mut Iter, in_attr_selector: bool, namespaces: &Na match iter.peek() { Some(&Delim('|')) => { let namespace = match namespaces.prefix_map.find(&value) { - None => return InvalidQualifiedName, // Undeclared namespace prefix + None => return Err(()), // Undeclared namespace prefix Some(ref ns) => (*ns).clone(), }; - explicit_namespace(iter, in_attr_selector, SpecificNamespace(namespace)) + explicit_namespace(iter, SpecificNamespace(namespace)) }, - _ if in_attr_selector => QualifiedName( - SpecificNamespace(namespace::Null), Some(value)), - _ => default_namespace(namespaces, Some(value)), + _ if in_attr_selector => Ok(Some( + (SpecificNamespace(namespace::Null), Some(value)))), + _ => default_namespace(Some(value)), } }, Some(&Delim('*')) => { iter.next(); // Consume '*' match iter.peek() { - Some(&Delim('|')) => explicit_namespace(iter, in_attr_selector, AnyNamespace), + Some(&Delim('|')) => explicit_namespace(iter, AnyNamespace), _ => { - if !in_attr_selector { default_namespace(namespaces, None) } - else { InvalidQualifiedName } + if !in_attr_selector { default_namespace(None) } + else { Err(()) } }, } }, - Some(&Delim('|')) => explicit_namespace( - iter, in_attr_selector, SpecificNamespace(namespace::Null)), - _ => NotAQualifiedName, + Some(&Delim('|')) => explicit_namespace(iter, SpecificNamespace(namespace::Null)), + _ => Ok(None), } } fn parse_attribute_selector(content: Vec<ComponentValue>, namespaces: &NamespaceMap) - -> Option<SimpleSelector> { + -> Result<SimpleSelector, ()> { let iter = &mut content.move_iter().peekable(); - let attr = match parse_qualified_name(iter, /* in_attr_selector = */ true, namespaces) { - InvalidQualifiedName | NotAQualifiedName => return None, - QualifiedName(_, None) => fail!("Implementation error, this should not happen."), - QualifiedName(namespace, Some(local_name)) => AttrSelector { + let attr = match try!(parse_qualified_name(iter, /* in_attr_selector = */ true, namespaces)) { + None => return Err(()), + Some((_, None)) => fail!("Implementation error, this should not happen."), + Some((namespace, Some(local_name))) => AttrSelector { namespace: namespace, lower_name: local_name.as_slice().to_ascii_lower(), name: local_name, @@ -456,7 +452,7 @@ fn parse_attribute_selector(content: Vec<ComponentValue>, namespaces: &Namespace skip_whitespace(iter); match iter.next() { Some(Ident(value)) | Some(String(value)) => value, - _ => return None, + _ => return Err(()) } }};) let result = match iter.next() { @@ -471,93 +467,92 @@ fn parse_attribute_selector(content: Vec<ComponentValue>, namespaces: &Namespace Some(PrefixMatch) => AttrPrefixMatch(attr, (get_value!())), // [foo^=bar] Some(SubstringMatch) => AttrSubstringMatch(attr, (get_value!())), // [foo*=bar] Some(SuffixMatch) => AttrSuffixMatch(attr, (get_value!())), // [foo$=bar] - _ => return None + _ => return Err(()) }; skip_whitespace(iter); - if iter.next().is_none() { Some(result) } else { None } + if iter.next().is_none() { Ok(result) } else { Err(()) } } -fn parse_simple_pseudo_class(name: &str) -> Option<SimpleSelector> { +fn parse_simple_pseudo_class(name: &str) -> Result<SimpleSelector, ()> { match name.to_ascii_lower().as_slice() { - "any-link" => Some(AnyLink), - "link" => Some(Link), - "visited" => Some(Visited), - "hover" => Some(Hover), - "disabled" => Some(Disabled), - "enabled" => Some(Enabled), - "first-child" => Some(FirstChild), - "last-child" => Some(LastChild), - "only-child" => Some(OnlyChild), - "root" => Some(Root), - "first-of-type" => Some(FirstOfType), - "last-of-type" => Some(LastOfType), - "only-of-type" => Some(OnlyOfType), -// "empty" => Some(Empty), - _ => None + "any-link" => Ok(AnyLink), + "link" => Ok(Link), + "visited" => Ok(Visited), + "hover" => Ok(Hover), + "disabled" => Ok(Disabled), + "enabled" => Ok(Enabled), + "first-child" => Ok(FirstChild), + "last-child" => Ok(LastChild), + "only-child" => Ok(OnlyChild), + "root" => Ok(Root), + "first-of-type" => Ok(FirstOfType), + "last-of-type" => Ok(LastOfType), + "only-of-type" => Ok(OnlyOfType), +// "empty" => Ok(Empty), + _ => Err(()) } } fn parse_functional_pseudo_class(name: String, arguments: Vec<ComponentValue>, namespaces: &NamespaceMap, inside_negation: bool) - -> Option<SimpleSelector> { + -> Result<SimpleSelector, ()> { match name.as_slice().to_ascii_lower().as_slice() { // "lang" => parse_lang(arguments), "nth-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthChild(a, b)), "nth-last-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastChild(a, b)), "nth-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthOfType(a, b)), "nth-last-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastOfType(a, b)), - "not" => if inside_negation { None } else { parse_negation(arguments, namespaces) }, - _ => None + "not" => if inside_negation { Err(()) } else { parse_negation(arguments, namespaces) }, + _ => Err(()) } } -fn parse_pseudo_element(name: String) -> Option<PseudoElement> { +fn parse_pseudo_element(name: String) -> Result<PseudoElement, ()> { match name.as_slice().to_ascii_lower().as_slice() { // All supported pseudo-elements - "before" => Some(Before), - "after" => Some(After), + "before" => Ok(Before), + "after" => Ok(After), // "first-line" => Some(FirstLine), // "first-letter" => Some(FirstLetter), - _ => None + _ => Err(()) } } -//fn parse_lang(arguments: vec!(ComponentValue)) -> Option<SimpleSelector> { +//fn parse_lang(arguments: vec!(ComponentValue)) -> Result<SimpleSelector, ()> { // let mut iter = arguments.move_skip_whitespace(); // match iter.next() { // Some(Ident(value)) => { // if "" == value || iter.next().is_some() { None } -// else { Some(Lang(value)) } +// else { Ok(Lang(value)) } // }, -// _ => None, +// _ => Err(()), // } //} -// Level 3: Parse ONE simple_selector +/// Level 3: Parse **one** simple_selector fn parse_negation(arguments: Vec<ComponentValue>, namespaces: &NamespaceMap) - -> Option<SimpleSelector> { + -> Result<SimpleSelector, ()> { let iter = &mut arguments.move_iter().peekable(); - Some(Negation(match parse_type_selector(iter, namespaces) { - InvalidTypeSelector => return None, - TypeSelector(s) => s, - NotATypeSelector => { - match parse_one_simple_selector(iter, namespaces, /* inside_negation = */ true) { - SimpleSelectorResult(s) => vec!(s), - _ => return None + match try!(parse_type_selector(iter, namespaces)) { + Some(type_selector) => Ok(Negation(type_selector)), + None => { + match try!(parse_one_simple_selector(iter, namespaces, /* inside_negation = */ true)) { + Some(SimpleSelectorResult(simple_selector)) => Ok(Negation(vec![simple_selector])), + _ => Err(()) } }, - })) + } } /// Assuming the next token is an ident, consume it and return its value #[inline] -fn get_next_ident(iter: &mut Iter) -> String { +fn get_next_ident<I: Iterator<ComponentValue>>(iter: &mut Iter<I>) -> String { match iter.next() { Some(Ident(value)) => value, _ => fail!("Implementation error, this should not happen."), @@ -566,7 +561,7 @@ fn get_next_ident(iter: &mut Iter) -> String { #[inline] -fn skip_whitespace(iter: &mut Iter) -> bool { +fn skip_whitespace<I: Iterator<ComponentValue>>(iter: &mut Iter<I>) -> bool { let mut any_whitespace = false; loop { if iter.peek() != Some(&WhiteSpace) { return any_whitespace } @@ -585,14 +580,12 @@ mod tests { use namespaces::NamespaceMap; use super::*; - fn parse(input: &str) -> Option<Vec<Selector>> { + fn parse(input: &str) -> Result<Vec<Selector>, ()> { parse_ns(input, &NamespaceMap::new()) } - fn parse_ns(input: &str, namespaces: &NamespaceMap) -> Option<Vec<Selector>> { - parse_selector_list( - cssparser::tokenize(input).map(|(v, _)| v).collect(), - namespaces) + fn parse_ns(input: &str, namespaces: &NamespaceMap) -> Result<Vec<Selector>, ()> { + parse_selector_list(cssparser::tokenize(input).map(|(v, _)| v), namespaces) } fn specificity(a: u32, b: u32, c: u32) -> u32 { @@ -601,16 +594,18 @@ mod tests { #[test] fn test_parsing() { - assert!(parse("") == None) - assert!(parse("e") == Some(vec!(Selector{ + assert!(parse("") == Err(())) + assert!(parse("EeÉ") == Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { - simple_selectors: vec!(LocalNameSelector(Atom::from_slice("e"))), + simple_selectors: vec!(LocalNameSelector(LocalNameSelector { + name: Atom::from_slice("EeÉ"), + lower_name: Atom::from_slice("eeÉ") })), next: None, }), pseudo_element: None, specificity: specificity(0, 0, 1), }))) - assert!(parse(".foo") == Some(vec!(Selector{ + assert!(parse(".foo") == Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(ClassSelector(Atom::from_slice("foo"))), next: None, @@ -618,7 +613,7 @@ mod tests { pseudo_element: None, specificity: specificity(0, 1, 0), }))) - assert!(parse("#bar") == Some(vec!(Selector{ + assert!(parse("#bar") == Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(IDSelector(Atom::from_slice("bar"))), next: None, @@ -626,9 +621,11 @@ mod tests { pseudo_element: None, specificity: specificity(1, 0, 0), }))) - assert!(parse("e.foo#bar") == Some(vec!(Selector{ + assert!(parse("e.foo#bar") == Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { - simple_selectors: vec!(LocalNameSelector(Atom::from_slice("e")), + simple_selectors: vec!(LocalNameSelector(LocalNameSelector { + name: Atom::from_slice("e"), + lower_name: Atom::from_slice("e") }), ClassSelector(Atom::from_slice("foo")), IDSelector(Atom::from_slice("bar"))), next: None, @@ -636,11 +633,13 @@ mod tests { pseudo_element: None, specificity: specificity(1, 1, 1), }))) - assert!(parse("e.foo #bar") == Some(vec!(Selector{ + assert!(parse("e.foo #bar") == Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(IDSelector(Atom::from_slice("bar"))), next: Some((box CompoundSelector { - simple_selectors: vec!(LocalNameSelector(Atom::from_slice("e")), + simple_selectors: vec!(LocalNameSelector(LocalNameSelector { + name: Atom::from_slice("e"), + lower_name: Atom::from_slice("e") }), ClassSelector(Atom::from_slice("foo"))), next: None, }, Descendant)), @@ -651,7 +650,7 @@ mod tests { // Default namespace does not apply to attribute selectors // https://github.com/mozilla/servo/pull/1652 let mut namespaces = NamespaceMap::new(); - assert!(parse_ns("[Foo]", &namespaces) == Some(vec!(Selector{ + assert!(parse_ns("[Foo]", &namespaces) == Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(AttrExists(AttrSelector { name: "Foo".to_string(), @@ -666,7 +665,7 @@ mod tests { // Default namespace does not apply to attribute selectors // https://github.com/mozilla/servo/pull/1652 namespaces.default = Some(namespace::MathML); - assert!(parse_ns("[Foo]", &namespaces) == Some(vec!(Selector{ + assert!(parse_ns("[Foo]", &namespaces) == Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(AttrExists(AttrSelector { name: "Foo".to_string(), @@ -679,11 +678,13 @@ mod tests { specificity: specificity(0, 1, 0), }))) // Default namespace does apply to type selectors - assert!(parse_ns("e", &namespaces) == Some(vec!(Selector{ + assert!(parse_ns("e", &namespaces) == Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!( NamespaceSelector(namespace::MathML), - LocalNameSelector(Atom::from_slice("e")), + LocalNameSelector(LocalNameSelector { + name: Atom::from_slice("e"), + lower_name: Atom::from_slice("e") }), ), next: None, }), @@ -691,7 +692,7 @@ mod tests { specificity: specificity(0, 0, 1), }))) // https://github.com/mozilla/servo/issues/1723 - assert!(parse("::before") == Some(vec!(Selector{ + assert!(parse("::before") == Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(), next: None, @@ -699,11 +700,13 @@ mod tests { pseudo_element: Some(Before), specificity: specificity(0, 0, 1), }))) - assert!(parse("div :after") == Some(vec!(Selector{ + assert!(parse("div :after") == Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(), next: Some((box CompoundSelector { - simple_selectors: vec!(LocalNameSelector(Atom::from_slice("div"))), + simple_selectors: vec!(LocalNameSelector(LocalNameSelector { + name: Atom::from_slice("div"), + lower_name: Atom::from_slice("div") })), next: None, }, Descendant)), }), diff --git a/src/components/style/style.rs b/src/components/style/style.rs index 1c52bbb2771..cd3950b3bc2 100644 --- a/src/components/style/style.rs +++ b/src/components/style/style.rs @@ -30,22 +30,17 @@ extern crate servo_util = "util"; // Public API -pub use stylesheets::{Stylesheet, CSSRule, StyleRule, CSSFontFaceRule}; +pub use stylesheets::{Stylesheet, iter_font_face_rules}; pub use selector_matching::{Stylist, StylesheetOrigin, UserAgentOrigin, AuthorOrigin, UserOrigin}; -pub use selector_matching::{MatchedProperty, matches_compound_selector}; +pub use selector_matching::{DeclarationBlock, matches}; pub use properties::{cascade, cascade_anonymous}; pub use properties::{PropertyDeclaration, ComputedValues, computed_values, style_structs}; pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes pub use properties::{CSSFloat, DeclaredValue, PropertyDeclarationParseResult}; pub use properties::longhands; -pub use errors::with_errors_silenced; pub use node::{TElement, TNode}; -pub use selectors::{PseudoElement, Before, After, AttrSelector, SpecificNamespace, AnyNamespace}; -pub use selectors::{NamespaceConstraint, Selector, CompoundSelector, SimpleSelector, Combinator}; -pub use selectors::{parse_selector_list}; -pub use namespaces::NamespaceMap; -pub use media_queries::{MediaRule, MediaQueryList, MediaQuery, Device, MediaType, MediaQueryType}; -pub use font_face::{FontFaceFormat, FontFaceRule, FontFaceSource,FontFaceSourceLine, TtfFormat}; +pub use selectors::{PseudoElement, Before, After, SelectorList, parse_selector_list_from_str}; +pub use selectors::{AttrSelector, NamespaceConstraint, SpecificNamespace, AnyNamespace}; mod stylesheets; mod errors; diff --git a/src/components/style/stylesheets.rs b/src/components/style/stylesheets.rs index a1e78bc2a18..cc2f1945ca9 100644 --- a/src/components/style/stylesheets.rs +++ b/src/components/style/stylesheets.rs @@ -16,13 +16,13 @@ use errors::{ErrorLoggerIterator, log_css_error}; use namespaces::{NamespaceMap, parse_namespace_rule}; use media_queries::{MediaRule, parse_media_rule}; use media_queries; -use font_face::{FontFaceRule, parse_font_face_rule}; +use font_face::{FontFaceRule, parse_font_face_rule, iter_font_face_rules_inner}; pub struct Stylesheet { /// List of rules in the order they were found (important for /// cascading order) - pub rules: Vec<CSSRule>, + rules: Vec<CSSRule>, } @@ -129,12 +129,12 @@ pub fn parse_style_rule(rule: QualifiedRule, parent_rules: &mut Vec<CSSRule>, let QualifiedRule{location: location, prelude: prelude, block: block} = rule; // FIXME: avoid doing this for valid selectors let serialized = prelude.iter().to_css(); - match selectors::parse_selector_list(prelude, namespaces) { - Some(selectors) => parent_rules.push(CSSStyleRule(StyleRule{ + match selectors::parse_selector_list(prelude.move_iter(), namespaces) { + Ok(selectors) => parent_rules.push(CSSStyleRule(StyleRule{ selectors: selectors, declarations: properties::parse_property_declaration_list(block.move_iter(), base_url) })), - None => log_css_error(location, format!( + Err(()) => log_css_error(location, format!( "Invalid/unsupported selector: {}", serialized).as_slice()), } } @@ -164,3 +164,15 @@ pub fn iter_style_rules<'a>(rules: &[CSSRule], device: &media_queries::Device, } } } + +#[inline] +pub fn iter_stylesheet_style_rules(stylesheet: &Stylesheet, device: &media_queries::Device, + callback: |&StyleRule|) { + iter_style_rules(stylesheet.rules.as_slice(), device, callback) +} + + +#[inline] +pub fn iter_font_face_rules(stylesheet: &Stylesheet, callback: |family: &str, sources: &Url|) { + iter_font_face_rules_inner(stylesheet.rules.as_slice(), callback) +} diff --git a/src/components/layout/css/user-agent.css b/src/components/style/user-agent.css index d73560abe0e..d73560abe0e 100644 --- a/src/components/layout/css/user-agent.css +++ b/src/components/style/user-agent.css diff --git a/src/components/util/sort.rs b/src/components/util/sort.rs index 90ba2469544..32dc52f6574 100644 --- a/src/components/util/sort.rs +++ b/src/components/util/sort.rs @@ -4,7 +4,7 @@ //! In-place sorting. -fn quicksort_helper<T:Ord + Eq + PartialOrd + PartialEq>(arr: &mut [T], left: int, right: int) { +fn quicksort_helper<T>(arr: &mut [T], left: int, right: int, compare: fn(&T, &T) -> Ordering) { if right <= left { return } @@ -17,11 +17,11 @@ fn quicksort_helper<T:Ord + Eq + PartialOrd + PartialEq>(arr: &mut [T], left: in let v: *mut T = &mut arr[right as uint]; loop { i += 1; - while arr[i as uint] < (*v) { + while compare(&arr[i as uint], &*v) == Less { i += 1 } j -= 1; - while (*v) < arr[j as uint] { + while compare(&*v, &arr[j as uint]) == Less { if j == left { break } @@ -31,11 +31,11 @@ fn quicksort_helper<T:Ord + Eq + PartialOrd + PartialEq>(arr: &mut [T], left: in break } arr.swap(i as uint, j as uint); - if arr[i as uint] == (*v) { + if compare(&arr[i as uint], &*v) == Equal { p += 1; arr.swap(p as uint, i as uint) } - if (*v) == arr[j as uint] { + if compare(&*v, &arr[j as uint]) == Equal { q -= 1; arr.swap(j as uint, q as uint) } @@ -60,21 +60,21 @@ fn quicksort_helper<T:Ord + Eq + PartialOrd + PartialEq>(arr: &mut [T], left: in assert!(k != 0); } - quicksort_helper(arr, left, j); - quicksort_helper(arr, i, right); + quicksort_helper(arr, left, j, compare); + quicksort_helper(arr, i, right, compare); } /// An in-place quicksort. /// /// The algorithm is from Sedgewick and Bentley, "Quicksort is Optimal": /// http://www.cs.princeton.edu/~rs/talks/QuicksortIsOptimal.pdf -pub fn quicksort<T:Ord + Eq + PartialOrd + PartialEq>(arr: &mut [T]) { +pub fn quicksort_by<T>(arr: &mut [T], compare: fn(&T, &T) -> Ordering) { if arr.len() <= 1 { return } let len = arr.len(); - quicksort_helper(arr, 0, (len - 1) as int); + quicksort_helper(arr, 0, (len - 1) as int, compare); } #[cfg(test)] @@ -90,7 +90,8 @@ pub mod test { for _ in range(0u32, 50000u32) { let len: uint = rng.gen(); let mut v: Vec<int> = rng.gen_iter::<int>().take((len % 32) + 1).collect(); - sort::quicksort(v.as_mut_slice()); + fn compare_ints(a: &int, b: &int) -> Ordering { a.cmp(b) } + sort::quicksort_by(v.as_mut_slice(), compare_ints); for i in range(0, v.len() - 1) { assert!(v.get(i) <= v.get(i + 1)) } diff --git a/src/support/css/rust-cssparser b/src/support/css/rust-cssparser -Subproject 918bae60d4703e3db7f306b6c4a09de0670b947 +Subproject 48e517b40c8aad55b5f2b7072d092102b48797b |