From 1d8d1cb9d90e163cfc10328ceed0a2347279eb78 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 24 Jun 2016 15:01:37 -0700 Subject: [PATCH] Upgrade rust-selectors. --- components/layout/Cargo.toml | 2 +- components/layout/traversal.rs | 2 +- components/script/Cargo.toml | 2 +- components/script/dom/element.rs | 53 ++++---- components/script/layout_wrapper.rs | 74 +++++------ components/script_layout_interface/Cargo.toml | 2 +- .../script_layout_interface/wrapper_traits.rs | 4 +- components/servo/Cargo.lock | 12 +- components/style/Cargo.toml | 4 +- components/style/matching.rs | 10 +- components/style/restyle_hints.rs | 117 +++++++++++------- components/style/selector_impl.rs | 3 +- components/style/selector_matching.rs | 34 ++--- ports/cef/Cargo.lock | 10 +- ports/geckolib/Cargo.lock | 6 +- ports/geckolib/Cargo.toml | 2 +- ports/geckolib/selector_impl.rs | 2 + ports/geckolib/wrapper.rs | 113 ++++++++++++++--- tests/unit/style/Cargo.toml | 2 +- 19 files changed, 280 insertions(+), 174 deletions(-) diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml index 188b39e9207..05f9fcda82c 100644 --- a/components/layout/Cargo.toml +++ b/components/layout/Cargo.toml @@ -31,7 +31,7 @@ range = {path = "../range"} rustc-serialize = "0.3" script_layout_interface = {path = "../script_layout_interface"} script_traits = {path = "../script_traits"} -selectors = {version = "0.6", features = ["heap_size"]} +selectors = {version = "0.7", features = ["heap_size"]} serde_macros = "0.7.11" smallvec = "0.1" string_cache = {version = "0.2.20", features = ["heap_size"]} diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index 5db766f68b5..581fee5e03d 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -28,7 +28,7 @@ pub struct RecalcStyleAndConstructFlows<'lc> { impl<'lc, N> DomTraversalContext for RecalcStyleAndConstructFlows<'lc> where N: LayoutNode + TNode, - N::ConcreteElement: ::selectors::Element + N::ConcreteElement: ::selectors::Element { type SharedContext = SharedLayoutContext; diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index d9bdeac843b..fa2d75c633d 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -57,7 +57,7 @@ regex = "0.1.43" rustc-serialize = "0.3" script_layout_interface = {path = "../script_layout_interface"} script_traits = {path = "../script_traits"} -selectors = {version = "0.6", features = ["heap_size"]} +selectors = {version = "0.7", features = ["heap_size"]} serde = "0.7.11" smallvec = "0.1" string_cache = {version = "0.2.20", features = ["heap_size", "unstable"]} diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index d9535663d46..e53f7dbb77b 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -2217,6 +2217,34 @@ impl VirtualMethods for Element { } } +impl<'a> ::selectors::MatchAttrGeneric for Root { + fn match_attr(&self, attr: &AttrSelector, test: F) -> bool + where F: Fn(&str) -> bool + { + use ::selectors::Element; + let local_name = { + if self.is_html_element_in_html_document() { + &attr.lower_name + } else { + &attr.name + } + }; + match attr.namespace { + NamespaceConstraint::Specific(ref ns) => { + self.get_attribute(ns, local_name) + .map_or(false, |attr| { + test(&attr.value()) + }) + }, + NamespaceConstraint::Any => { + self.attrs.borrow().iter().any(|attr| { + attr.local_name() == local_name && test(&attr.value()) + }) + } + } + } +} + impl<'a> ::selectors::Element for Root { type Impl = ServoSelectorImpl; @@ -2317,31 +2345,6 @@ impl<'a> ::selectors::Element for Root { } } - fn match_attr(&self, attr: &AttrSelector, test: F) -> bool - where F: Fn(&str) -> bool - { - let local_name = { - if self.is_html_element_in_html_document() { - &attr.lower_name - } else { - &attr.name - } - }; - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => { - self.get_attribute(ns, local_name) - .map_or(false, |attr| { - test(&attr.value()) - }) - }, - NamespaceConstraint::Any => { - self.attrs.borrow().iter().any(|attr| { - attr.local_name() == local_name && test(&attr.value()) - }) - } - } - } - fn is_html_element_in_html_document(&self) -> bool { self.html_element_in_html_document() } diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index a916ee73512..7f3aaf3f242 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -418,6 +418,28 @@ fn as_element<'le>(node: LayoutJS) -> Option> { node.downcast().map(ServoLayoutElement::from_layout_js) } +impl<'le> ::selectors::MatchAttrGeneric for ServoLayoutElement<'le> { + fn match_attr(&self, attr: &AttrSelector, test: F) -> bool where F: Fn(&str) -> bool { + use ::selectors::Element; + let name = if self.is_html_element_in_html_document() { + &attr.lower_name + } else { + &attr.name + }; + match attr.namespace { + NamespaceConstraint::Specific(ref ns) => { + self.get_attr(ns, name).map_or(false, |attr| test(attr)) + }, + NamespaceConstraint::Any => { + let attrs = unsafe { + (*self.element.unsafe_get()).get_attr_vals_for_layout(name) + }; + attrs.iter().any(|attr| test(*attr)) + } + } + } +} + impl<'le> ::selectors::Element for ServoLayoutElement<'le> { type Impl = ServoSelectorImpl; @@ -553,25 +575,6 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { } } - fn match_attr(&self, attr: &AttrSelector, test: F) -> bool where F: Fn(&str) -> bool { - let name = if self.is_html_element_in_html_document() { - &attr.lower_name - } else { - &attr.name - }; - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => { - self.get_attr(ns, name).map_or(false, |attr| test(attr)) - }, - NamespaceConstraint::Any => { - let attrs = unsafe { - (*self.element.unsafe_get()).get_attr_vals_for_layout(name) - }; - attrs.iter().any(|attr| test(*attr)) - } - } - } - fn is_html_element_in_html_document(&self) -> bool { unsafe { self.element.html_element_in_html_document_for_layout() @@ -906,7 +909,23 @@ impl<'le> ThreadSafeLayoutElement for ServoThreadSafeLayoutElement<'le> { /// /// Note that the element implementation is needed only for selector matching, /// not for inheritance (styles are inherited appropiately). -impl <'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { +impl<'le> ::selectors::MatchAttrGeneric for ServoThreadSafeLayoutElement<'le> { + fn match_attr(&self, attr: &AttrSelector, test: F) -> bool + where F: Fn(&str) -> bool { + match attr.namespace { + NamespaceConstraint::Specific(ref ns) => { + self.get_attr(ns, &attr.name).map_or(false, |attr| test(attr)) + }, + NamespaceConstraint::Any => { + unsafe { + self.element.get_attr_vals_for_layout(&attr.name).iter() + .any(|attr| test(*attr)) + } + } + } + } +} +impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { type Impl = ServoSelectorImpl; fn parent_element(&self) -> Option { @@ -968,21 +987,6 @@ impl <'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { false } - fn match_attr(&self, attr: &AttrSelector, test: F) -> bool - where F: Fn(&str) -> bool { - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => { - self.get_attr(ns, &attr.name).map_or(false, |attr| test(attr)) - }, - NamespaceConstraint::Any => { - unsafe { - self.element.get_attr_vals_for_layout(&attr.name).iter() - .any(|attr| test(*attr)) - } - } - } - } - fn is_empty(&self) -> bool { warn!("ServoThreadSafeLayoutElement::is_empty called"); false diff --git a/components/script_layout_interface/Cargo.toml b/components/script_layout_interface/Cargo.toml index 51b767c753f..41c1f6e2598 100644 --- a/components/script_layout_interface/Cargo.toml +++ b/components/script_layout_interface/Cargo.toml @@ -26,7 +26,7 @@ plugins = {path = "../plugins"} profile_traits = {path = "../profile_traits"} range = {path = "../range"} script_traits = {path = "../script_traits"} -selectors = {version = "0.6", features = ["heap_size"]} +selectors = {version = "0.7", features = ["heap_size"]} string_cache = {version = "0.2.20", features = ["heap_size"]} style = {path = "../style"} url = {version = "1.0.0", features = ["heap_size"]} diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs index a816321ec28..bdbf31c37c8 100644 --- a/components/script_layout_interface/wrapper_traits.rs +++ b/components/script_layout_interface/wrapper_traits.rs @@ -86,7 +86,7 @@ pub trait LayoutNode: TNode { pub trait ThreadSafeLayoutNode: Clone + Copy + Sized + PartialEq { type ConcreteThreadSafeLayoutElement: ThreadSafeLayoutElement - + ::selectors::Element; + + ::selectors::Element; type ChildrenIterator: Iterator + Sized; /// Creates a new `ThreadSafeLayoutNode` for the same `LayoutNode` @@ -351,7 +351,7 @@ pub trait DangerousThreadSafeLayoutNode: ThreadSafeLayoutNode { } pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + - ::selectors::Element + + ::selectors::Element + PresentationalHintsSynthetizer { type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode; diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 3424416cead..997fc8ab250 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -1159,7 +1159,7 @@ dependencies = [ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "script_layout_interface 0.0.1", "script_traits 0.0.1", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_macros 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1942,7 +1942,7 @@ dependencies = [ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "script_layout_interface 0.0.1", "script_traits 0.0.1", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1978,7 +1978,7 @@ dependencies = [ "profile_traits 0.0.1", "range 0.0.1", "script_traits 0.0.1", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "style 0.0.1", "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2026,7 +2026,7 @@ dependencies = [ [[package]] name = "selectors" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2243,7 +2243,7 @@ dependencies = [ "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "serde_macros 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2263,7 +2263,7 @@ dependencies = [ "cssparser 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "style 0.0.1", "style_traits 0.0.1", diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index a90673b9e40..049dd560eeb 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -11,7 +11,7 @@ name = "style" path = "lib.rs" [features] -gecko = ["gecko_bindings"] +gecko = ["gecko_bindings", "selectors/gecko"] servo = ["serde", "serde/nightly", "serde_macros", "heapsize", "heapsize_plugin", "style_traits/servo", "app_units/plugins", "euclid/plugins", "cssparser/heap_size", "cssparser/serde-serialization", @@ -36,7 +36,7 @@ matches = "0.1" num-traits = "0.1.32" rand = "0.3" rustc-serialize = "0.3" -selectors = "0.6" +selectors = "0.7" serde = {version = "0.7.11", optional = true} serde_macros = {version = "0.7.11", optional = true} smallvec = "0.1" diff --git a/components/style/matching.rs b/components/style/matching.rs index f97ee2aab2c..5d4fa82b8ce 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -38,9 +38,8 @@ fn create_common_style_affecting_attributes_from_element(element: & flags.insert(flag) } } - CommonStyleAffectingAttributeMode::IsEqual(target_value, flag) => { - let atom = Atom::from(target_value); // FIXME(bholley): This goes away in the next patch. - if element.attr_equals(&ns!(), &attribute_info.atom, &atom) { + CommonStyleAffectingAttributeMode::IsEqual(ref target_value, flag) => { + if element.attr_equals(&ns!(), &attribute_info.atom, target_value) { flags.insert(flag) } } @@ -298,11 +297,10 @@ impl StyleSharingCandidate { return false } } - CommonStyleAffectingAttributeMode::IsEqual(target_value, flag) => { - let atom = Atom::from(target_value); // FIXME(bholley): This goes away in the next patch. + CommonStyleAffectingAttributeMode::IsEqual(ref target_value, flag) => { let contains = self.common_style_affecting_attributes.contains(flag); if element.has_attr(&ns!(), &attribute_info.atom) { - if !contains || !element.attr_equals(&ns!(), &attribute_info.atom, &atom) { + if !contains || !element.attr_equals(&ns!(), &attribute_info.atom, target_value) { return false } } else if contains { diff --git a/components/style/restyle_hints.rs b/components/style/restyle_hints.rs index c517a6f1a7b..cd9957322e4 100644 --- a/components/style/restyle_hints.rs +++ b/components/style/restyle_hints.rs @@ -7,9 +7,9 @@ use attr::{AttrIdentifier, AttrValue}; use element_state::*; use selector_impl::SelectorImplExt; -use selectors::Element; use selectors::matching::matches_compound_selector; use selectors::parser::{AttrSelector, Combinator, CompoundSelector, SelectorImpl, SimpleSelector}; +use selectors::{Element, MatchAttrGeneric}; use std::clone::Clone; use std::sync::Arc; use string_cache::{Atom, BorrowedAtom, BorrowedNamespace, Namespace}; @@ -81,15 +81,21 @@ impl ElementSnapshot { static EMPTY_SNAPSHOT: ElementSnapshot = ElementSnapshot { state: None, attrs: None }; +// FIXME(bholley): This implementation isn't going to work for geckolib, because +// it's fundamentally based on get_attr/match_attr, which we don't want to support +// that configuration due to the overhead of converting between UTF-16 and UTF-8. +// We'll need to figure something out when we start using restyle hints with +// geckolib, but in the mean time we can just use the trait parameters to +// specialize it to the Servo configuration. struct ElementWrapper<'a, E> - where E: Element, + where E: Element, E::Impl: SelectorImplExt { element: E, snapshot: &'a ElementSnapshot, } impl<'a, E> ElementWrapper<'a, E> - where E: Element, + where E: Element, E::Impl: SelectorImplExt { pub fn new(el: E) -> ElementWrapper<'a, E> { ElementWrapper { element: el, snapshot: &EMPTY_SNAPSHOT } @@ -100,8 +106,42 @@ impl<'a, E> ElementWrapper<'a, E> } } +#[cfg(not(feature = "gecko"))] +impl<'a, E> MatchAttrGeneric for ElementWrapper<'a, E> + where E: Element, + E: MatchAttrGeneric, + E::Impl: SelectorImplExt { + fn match_attr(&self, attr: &AttrSelector, test: F) -> bool + where F: Fn(&str) -> bool { + use selectors::parser::NamespaceConstraint; + match self.snapshot.attrs { + Some(_) => { + let html = self.is_html_element_in_html_document(); + let local_name = if html { &attr.lower_name } else { &attr.name }; + match attr.namespace { + NamespaceConstraint::Specific(ref ns) => self.snapshot.get_attr(ns, local_name), + NamespaceConstraint::Any => self.snapshot.get_attr_ignore_ns(local_name), + }.map_or(false, |v| test(v)) + }, + None => self.element.match_attr(attr, test) + } + } +} + +#[cfg(feature = "gecko")] +impl<'a, E> MatchAttrGeneric for ElementWrapper<'a, E> + where E: Element, + E: MatchAttrGeneric, + E::Impl: SelectorImplExt { + fn match_attr(&self, _: &AttrSelector, _: F) -> bool + where F: Fn(&str) -> bool { + panic!("Not implemented for Gecko - this system will need to be redesigned"); + } +} + impl<'a, E> Element for ElementWrapper<'a, E> - where E: Element, + where E: Element, + E: MatchAttrGeneric, E::Impl: SelectorImplExt { type Impl = E::Impl; @@ -155,27 +195,6 @@ impl<'a, E> Element for ElementWrapper<'a, E> None => self.element.has_class(name), } } - #[cfg(feature = "gecko")] - fn match_attr(&self, _: &AttrSelector, _: F) -> bool - where F: Fn(&str) -> bool { - panic!("Gecko can't borrow atoms as UTF-8."); - } - #[cfg(not(feature = "gecko"))] - fn match_attr(&self, attr: &AttrSelector, test: F) -> bool - where F: Fn(&str) -> bool { - use selectors::parser::NamespaceConstraint; - match self.snapshot.attrs { - Some(_) => { - let html = self.is_html_element_in_html_document(); - let local_name = if html { &attr.lower_name } else { &attr.name }; - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => self.snapshot.get_attr(ns, local_name), - NamespaceConstraint::Any => self.snapshot.get_attr_ignore_ns(local_name), - }.map_or(false, |v| test(v)) - }, - None => self.element.match_attr(attr, test) - } - } fn is_empty(&self) -> bool { self.element.is_empty() } @@ -208,7 +227,7 @@ fn is_attr_selector(sel: &SimpleSelector) -> bool { SimpleSelector::AttrExists(_) | SimpleSelector::AttrEqual(_, _, _) | SimpleSelector::AttrIncludes(_, _) | - SimpleSelector::AttrDashMatch(_, _, _) | + SimpleSelector::AttrDashMatch(_, _) | SimpleSelector::AttrPrefixMatch(_, _) | SimpleSelector::AttrSubstringMatch(_, _) | SimpleSelector::AttrSuffixMatch(_, _) => true, @@ -285,28 +304,6 @@ impl DependencySet { DependencySet { deps: Vec::new() } } - pub fn compute_hint(&self, el: &E, snapshot: &ElementSnapshot, current_state: ElementState) - -> RestyleHint - where E: Element + Clone { - let state_changes = snapshot.state.map_or(ElementState::empty(), |old_state| current_state ^ old_state); - let attrs_changed = snapshot.attrs.is_some(); - let mut hint = RestyleHint::empty(); - for dep in &self.deps { - if state_changes.intersects(dep.sensitivities.states) || (attrs_changed && dep.sensitivities.attrs) { - let old_el: ElementWrapper = ElementWrapper::new_with_snapshot(el.clone(), snapshot); - let matched_then = matches_compound_selector(&*dep.selector, &old_el, None, &mut false); - let matches_now = matches_compound_selector(&*dep.selector, el, None, &mut false); - if matched_then != matches_now { - hint.insert(combinator_to_restyle_hint(dep.combinator)); - if hint.is_all() { - break - } - } - } - } - hint - } - pub fn note_selector(&mut self, selector: Arc>) { let mut cur = selector; let mut combinator: Option = None; @@ -340,3 +337,27 @@ impl DependencySet { self.deps.clear(); } } + +impl> DependencySet { + pub fn compute_hint(&self, el: &E, snapshot: &ElementSnapshot, current_state: ElementState) + -> RestyleHint + where E: Element + Clone + MatchAttrGeneric { + let state_changes = snapshot.state.map_or(ElementState::empty(), |old_state| current_state ^ old_state); + let attrs_changed = snapshot.attrs.is_some(); + let mut hint = RestyleHint::empty(); + for dep in &self.deps { + if state_changes.intersects(dep.sensitivities.states) || (attrs_changed && dep.sensitivities.attrs) { + let old_el: ElementWrapper = ElementWrapper::new_with_snapshot(el.clone(), snapshot); + let matched_then = matches_compound_selector(&*dep.selector, &old_el, None, &mut false); + let matches_now = matches_compound_selector(&*dep.selector, el, None, &mut false); + if matched_then != matches_now { + hint.insert(combinator_to_restyle_hint(dep.combinator)); + if hint.is_all() { + break + } + } + } + } + hint + } +} diff --git a/components/style/selector_impl.rs b/components/style/selector_impl.rs index cf95f4a9048..cd47d0dc7f5 100644 --- a/components/style/selector_impl.rs +++ b/components/style/selector_impl.rs @@ -181,6 +181,7 @@ impl NonTSPseudoClass { pub struct ServoSelectorImpl; impl SelectorImpl for ServoSelectorImpl { + type AttrString = String; type PseudoElement = PseudoElement; type NonTSPseudoClass = NonTSPseudoClass; @@ -278,7 +279,7 @@ impl SelectorImplExt for ServoSelectorImpl { } } -impl> ElementExt for E { +impl> ElementExt for E { fn is_link(&self) -> bool { self.match_non_ts_pseudo_class(NonTSPseudoClass::AnyLink) } diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs index 74718d4b69b..b632d783e1e 100644 --- a/components/style/selector_matching.rs +++ b/components/style/selector_matching.rs @@ -13,11 +13,11 @@ use parser::ParserContextExtraData; use properties::{self, PropertyDeclaration, PropertyDeclarationBlock}; use restyle_hints::{ElementSnapshot, RestyleHint, DependencySet}; use selector_impl::{SelectorImplExt, ServoSelectorImpl}; -use selectors::Element; use selectors::bloom::BloomFilter; use selectors::matching::DeclarationBlock as GenericDeclarationBlock; use selectors::matching::{Rule, SelectorMap}; use selectors::parser::SelectorImpl; +use selectors::{Element, MatchAttrGeneric}; use sink::Push; use smallvec::VecLike; use std::collections::HashMap; @@ -311,7 +311,7 @@ impl Stylist { pseudo: &Impl::PseudoElement, parent: &Arc) -> Option> - where E: Element + + where E: Element + PresentationalHintsSynthetizer { debug_assert!(Impl::pseudo_element_cascade_type(pseudo).is_lazy()); if self.pseudos_map.get(pseudo).is_none() { @@ -336,18 +336,6 @@ impl Stylist { Some(Arc::new(computed)) } - pub fn compute_restyle_hint(&self, element: &E, - snapshot: &ElementSnapshot, - // NB: We need to pass current_state as an argument because - // selectors::Element doesn't provide access to ElementState - // directly, and computing it from the ElementState would be - // more expensive than getting it directly from the caller. - current_state: ElementState) - -> RestyleHint - where E: Element + Clone { - self.state_deps.compute_hint(element, snapshot, current_state) - } - pub fn set_device(&mut self, mut device: Device, stylesheets: &[Arc>]) { let cascaded_rule = stylesheets.iter() .flat_map(|s| s.effective_rules(&self.device).viewport()) @@ -389,7 +377,8 @@ impl Stylist { pseudo_element: Option<&Impl::PseudoElement>, applicable_declarations: &mut V) -> bool - where E: Element + PresentationalHintsSynthetizer, + where E: Element + + PresentationalHintsSynthetizer, V: Push + VecLike { assert!(!self.is_device_dirty); assert!(style_attribute.is_none() || pseudo_element.is_none(), @@ -474,6 +463,21 @@ impl Stylist { } } +impl> Stylist { + pub fn compute_restyle_hint(&self, element: &E, + snapshot: &ElementSnapshot, + // NB: We need to pass current_state as an argument because + // selectors::Element doesn't provide access to ElementState + // directly, and computing it from the ElementState would be + // more expensive than getting it directly from the caller. + current_state: ElementState) + -> RestyleHint + where E: Element + Clone + MatchAttrGeneric { + self.state_deps.compute_hint(element, snapshot, current_state) + } +} + + /// Map that contains the CSS rules for a given origin. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] struct PerOriginSelectorMap { diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index d2acbc92d13..4907cc20629 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -1068,7 +1068,7 @@ dependencies = [ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "script_layout_interface 0.0.1", "script_traits 0.0.1", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_macros 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1796,7 +1796,7 @@ dependencies = [ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "script_layout_interface 0.0.1", "script_traits 0.0.1", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1832,7 +1832,7 @@ dependencies = [ "profile_traits 0.0.1", "range 0.0.1", "script_traits 0.0.1", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "style 0.0.1", "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1870,7 +1870,7 @@ dependencies = [ [[package]] name = "selectors" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2128,7 +2128,7 @@ dependencies = [ "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "serde_macros 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/geckolib/Cargo.lock b/ports/geckolib/Cargo.lock index 3fe70abd1d9..27b841fb790 100644 --- a/ports/geckolib/Cargo.lock +++ b/ports/geckolib/Cargo.lock @@ -12,7 +12,7 @@ dependencies = [ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "style 0.0.1", @@ -379,7 +379,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "selectors" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -465,7 +465,7 @@ dependencies = [ "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "serde_macros 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/geckolib/Cargo.toml b/ports/geckolib/Cargo.toml index 6ca0d6d4e2e..8f1112ccddc 100644 --- a/ports/geckolib/Cargo.toml +++ b/ports/geckolib/Cargo.toml @@ -37,7 +37,7 @@ lazy_static = "0.2" libc = "0.2" log = {version = "0.3.5", features = ["release_max_level_info"]} num_cpus = "0.2.2" -selectors = "0.6" +selectors = "0.7" smallvec = "0.1" string_cache = {version = "0.2.20", features = ["unstable"]} style = {path = "../../components/style", features = ["gecko"]} diff --git a/ports/geckolib/selector_impl.rs b/ports/geckolib/selector_impl.rs index c1284015414..a2f0d1eba92 100644 --- a/ports/geckolib/selector_impl.rs +++ b/ports/geckolib/selector_impl.rs @@ -4,6 +4,7 @@ use properties::GeckoComputedValues; use selectors::parser::{ParserContext, SelectorImpl}; +use string_cache::Atom; use style; use style::element_state::ElementState; use style::selector_impl::{PseudoElementCascadeType, SelectorImplExt}; @@ -157,6 +158,7 @@ impl NonTSPseudoClass { } impl SelectorImpl for GeckoSelectorImpl { + type AttrString = Atom; type PseudoElement = PseudoElement; type NonTSPseudoClass = NonTSPseudoClass; fn parse_non_ts_pseudo_class(_context: &ParserContext, diff --git a/ports/geckolib/wrapper.rs b/ports/geckolib/wrapper.rs index 247d3b9216c..7e06c533ef1 100644 --- a/ports/geckolib/wrapper.rs +++ b/ports/geckolib/wrapper.rs @@ -4,6 +4,7 @@ #![allow(unsafe_code)] +use gecko_bindings::bindings; use gecko_bindings::bindings::Gecko_ChildrenCount; use gecko_bindings::bindings::Gecko_ClassOrClassList; use gecko_bindings::bindings::Gecko_GetElementId; @@ -521,26 +522,6 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { } } - fn match_attr(&self, attr: &AttrSelector, test: F) -> bool where F: Fn(&str) -> bool { - // FIXME(bholley): This is copy-pasted from the servo wrapper's version. - // We should find a way to share it. - let name = if self.is_html_element_in_html_document() { - &attr.lower_name - } else { - &attr.name - }; - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => { - self.get_attr(ns, name).map_or(false, |attr| test(attr)) - }, - NamespaceConstraint::Any => { - // We should probably pass the atom into gecko and let it match - // against attributes across namespaces. - unimplemented!() - } - } - } - fn is_html_element_in_html_document(&self) -> bool { unsafe { Gecko_IsHTMLElementInHTMLDocument(self.element) @@ -548,6 +529,98 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { } } +trait AttrSelectorHelpers { + fn ns_or_null(&self) -> *mut nsIAtom; + fn select_name<'le>(&self, el: &GeckoElement<'le>) -> *mut nsIAtom; +} + +impl AttrSelectorHelpers for AttrSelector { + fn ns_or_null(&self) -> *mut nsIAtom { + match self.namespace { + NamespaceConstraint::Any => ptr::null_mut(), + NamespaceConstraint::Specific(ref ns) => ns.0.as_ptr(), + } + } + + fn select_name<'le>(&self, el: &GeckoElement<'le>) -> *mut nsIAtom { + if el.is_html_element_in_html_document() { + self.lower_name.as_ptr() + } else { + self.name.as_ptr() + } + + } +} + +impl<'le> ::selectors::MatchAttr for GeckoElement<'le> { + type AttrString = Atom; + fn match_attr_has(&self, attr: &AttrSelector) -> bool { + unsafe { + bindings::Gecko_HasAttr(self.element, + attr.ns_or_null(), + attr.select_name(self)) + } + } + fn match_attr_equals(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool { + unsafe { + bindings::Gecko_AttrEquals(self.element, + attr.ns_or_null(), + attr.select_name(self), + value.as_ptr(), + /* ignoreCase = */ false) + } + } + fn match_attr_equals_ignore_ascii_case(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool { + unsafe { + bindings::Gecko_AttrEquals(self.element, + attr.ns_or_null(), + attr.select_name(self), + value.as_ptr(), + /* ignoreCase = */ false) + } + } + fn match_attr_includes(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool { + unsafe { + bindings::Gecko_AttrIncludes(self.element, + attr.ns_or_null(), + attr.select_name(self), + value.as_ptr()) + } + } + fn match_attr_dash(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool { + unsafe { + bindings::Gecko_AttrDashEquals(self.element, + attr.ns_or_null(), + attr.select_name(self), + value.as_ptr()) + } + } + fn match_attr_prefix(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool { + unsafe { + bindings::Gecko_AttrHasPrefix(self.element, + attr.ns_or_null(), + attr.select_name(self), + value.as_ptr()) + } + } + fn match_attr_substring(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool { + unsafe { + bindings::Gecko_AttrHasSubstring(self.element, + attr.ns_or_null(), + attr.select_name(self), + value.as_ptr()) + } + } + fn match_attr_suffix(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool { + unsafe { + bindings::Gecko_AttrHasSuffix(self.element, + attr.ns_or_null(), + attr.select_name(self), + value.as_ptr()) + } + } +} + impl<'le> ElementExt for GeckoElement<'le> { fn is_link(&self) -> bool { self.match_non_ts_pseudo_class(NonTSPseudoClass::AnyLink) diff --git a/tests/unit/style/Cargo.toml b/tests/unit/style/Cargo.toml index 1e8e120651f..8063e1a5b8d 100644 --- a/tests/unit/style/Cargo.toml +++ b/tests/unit/style/Cargo.toml @@ -13,7 +13,7 @@ app_units = "0.2.5" cssparser = {version = "0.5.4", features = ["heap_size"]} euclid = "0.7.1" rustc-serialize = "0.3" -selectors = {version = "0.6", features = ["heap_size"]} +selectors = {version = "0.7", features = ["heap_size"]} string_cache = {version = "0.2", features = ["heap_size"]} style = {path = "../../../components/style"} style_traits = {path = "../../../components/style_traits"}