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> for LayoutNode<'ln> { } fn match_attr(&self, attr: &AttrSelector, test: |&str| -> bool) -> bool { - let name = unsafe { - let element: JS = 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> for LayoutNode<'ln> { AnyNamespace => false, } } + + fn is_html_element_in_html_document(&self) -> bool { + unsafe { + self.is_element() && { + let element: JS = self.node.transmute_copy(); + element.html_element_in_html_document_for_layout() + } + } + } } pub struct LayoutNodeChildrenIterator<'a> { diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index d6076ab7b71..1b0869eb3f2 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -1997,29 +1997,32 @@ impl<'a> style::TNode> for JSRef<'a, Node> { fn parent_node(&self) -> Option> { (self as &NodeHelpers).parent_node().map(|node| *node.root()) } + fn prev_sibling(&self) -> Option> { (self as &NodeHelpers).prev_sibling().map(|node| *node.root()) } + fn next_sibling(&self) -> Option> { (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 +2037,13 @@ impl<'a> style::TNode> 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/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 : 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/selector_matching.rs b/src/components/style/selector_matching.rs index abe04b21e83..f36c7bb1f73 100644 --- a/src/components/style/selector_matching.rs +++ b/src/components/style/selector_matching.rs @@ -3,7 +3,7 @@ * 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; @@ -52,7 +52,10 @@ struct SelectorMap { // TODO: Tune the initial capacity of the HashMap id_hash: HashMap>, class_hash: HashMap>, - element_hash: HashMap>, + local_name_hash: HashMap>, + /// Same as local_name_hash, but keys are lower-cased. + /// For HTML elements in HTML documents. + lower_local_name_hash: HashMap>, // For Rules that don't have ID, class, or element selectors. universal_rules: Vec, /// Whether this hash is empty. @@ -64,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, } @@ -113,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(), @@ -150,23 +157,6 @@ impl SelectorMap { } } - fn get_matching_rules_from_hash_ignoring_case, - V:VecLike>( - node: &N, - hash: &HashMap>, - 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, @@ -183,49 +173,29 @@ impl SelectorMap { } /// 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 => {} @@ -263,14 +233,12 @@ impl SelectorMap { } /// Retrieve the name if it is a type selector, or None otherwise. - fn get_element_name(rule: &Rule) -> Option { + fn get_local_name(rule: &Rule) -> Option { 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()) } _ => {} } @@ -629,11 +597,10 @@ fn matches_simple_selector 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) => { @@ -918,11 +885,30 @@ fn matches_last_child>(element: &N) -> bool { } +trait FindPush { + fn find_push(&mut self, key: K, value: V); +} + +impl FindPush for HashMap> { + 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::{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. @@ -971,12 +957,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] diff --git a/src/components/style/selectors.rs b/src/components/style/selectors.rs index 86ed980aff5..c174d49c314 100644 --- a/src/components/style/selectors.rs +++ b/src/components/style/selectors.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::{cmp, iter}; -use std::ascii::StrAsciiExt; +use std::ascii::{StrAsciiExt, OwnedStrAsciiExt}; use std::vec; use sync::Arc; @@ -59,7 +59,7 @@ pub enum Combinator { pub enum SimpleSelector { IDSelector(Atom), ClassSelector(Atom), - LocalNameSelector(Atom), + LocalNameSelector(LocalNameSelector), NamespaceSelector(Namespace), // Attribute selectors @@ -93,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, @@ -268,8 +274,10 @@ 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 => (), } @@ -574,9 +582,11 @@ mod tests { #[test] fn test_parsing() { assert!(parse("") == Err(())) - assert!(parse("e") == Ok(vec!(Selector { + 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, @@ -600,7 +610,9 @@ mod tests { }))) 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, @@ -612,7 +624,9 @@ mod tests { 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)), @@ -655,7 +669,9 @@ mod tests { 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, }), @@ -675,7 +691,9 @@ mod tests { 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 5c03f9393da..36576c1cae9 100644 --- a/src/components/style/style.rs +++ b/src/components/style/style.rs @@ -41,7 +41,7 @@ pub use properties::longhands; 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 selectors::{LocalNameSelector, 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};