diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index fe0b5079988..9d8e6b051df 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -184,6 +184,9 @@ impl SelectorMethods for SimpleSelector { where V: SelectorVisitor, { use self::SimpleSelector::*; + if !visitor.visit_simple_selector(self) { + return false; + } match *self { Negation(ref negated) => { diff --git a/components/selectors/visitor.rs b/components/selectors/visitor.rs index 429c70c1cc8..8a27c1a3ef9 100644 --- a/components/selectors/visitor.rs +++ b/components/selectors/visitor.rs @@ -6,7 +6,7 @@ #![deny(missing_docs)] -use parser::{AttrSelector, Combinator, ComplexSelector, SelectorImpl}; +use parser::{AttrSelector, Combinator, ComplexSelector, SelectorImpl, SimpleSelector}; use std::sync::Arc; /// A trait to visit selector properties. @@ -24,6 +24,11 @@ pub trait SelectorVisitor { true } + /// Visit a simple selector. + fn visit_simple_selector(&mut self, _: &SimpleSelector) -> bool { + true + } + /// Visits a complex selector. /// /// Gets the combinator to the right of the selector, or `None` if the diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index 18f56ed1228..adbd3a041bd 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -8,7 +8,6 @@ use cssparser::{Parser, ToCss}; use element_state::ElementState; use gecko_bindings::structs::CSSPseudoClassType; use gecko_bindings::structs::nsIAtom; -use restyle_hints::complex_selector_to_state; use selector_parser::{SelectorParser, PseudoElementCascadeType}; use selectors::parser::{ComplexSelector, SelectorMethods}; use selectors::visitor::SelectorVisitor; @@ -313,11 +312,7 @@ impl NonTSPseudoClass { match *self { $(NonTSPseudoClass::$name => flag!($state),)* $(NonTSPseudoClass::$s_name(..) => flag!($s_state),)* - NonTSPseudoClass::MozAny(ref selectors) => { - selectors.iter().fold(ElementState::empty(), |state, s| { - state | complex_selector_to_state(s) - }) - } + NonTSPseudoClass::MozAny(..) => ElementState::empty(), } } } diff --git a/components/style/restyle_hints.rs b/components/style/restyle_hints.rs index 594246f4838..2a24c9bb1c8 100644 --- a/components/style/restyle_hints.rs +++ b/components/style/restyle_hints.rs @@ -17,7 +17,7 @@ use selector_parser::{AttrValue, NonTSPseudoClass, Snapshot, SelectorImpl}; use selectors::{Element, MatchAttr}; use selectors::matching::{ElementSelectorFlags, StyleRelations}; use selectors::matching::matches_complex_selector; -use selectors::parser::{AttrSelector, Combinator, ComplexSelector, SimpleSelector}; +use selectors::parser::{AttrSelector, Combinator, ComplexSelector, SelectorMethods, SimpleSelector}; use selectors::visitor::SelectorVisitor; use std::clone::Clone; use std::sync::Arc; @@ -283,6 +283,21 @@ impl<'a, E> Element for ElementWrapper<'a, E> -> bool where F: FnMut(&Self, ElementSelectorFlags), { + // :moz-any is quite special, because we need to keep matching as a + // snapshot. + #[cfg(feature = "gecko")] + { + if let NonTSPseudoClass::MozAny(ref selectors) = *pseudo_class { + return selectors.iter().any(|s| { + matches_complex_selector(s, + self, + None, + relations, + setter) + }) + } + } + let flag = SelectorImpl::pseudo_class_state_flag(pseudo_class); if flag.is_empty() { return self.element.match_non_ts_pseudo_class(pseudo_class, @@ -365,22 +380,9 @@ impl<'a, E> Element for ElementWrapper<'a, E> } } -/// Returns the union of any `ElementState` flags for components of a -/// `ComplexSelector`. -pub fn complex_selector_to_state(sel: &ComplexSelector) -> ElementState { - sel.compound_selector.iter().fold(ElementState::empty(), |state, s| { - state | selector_to_state(s) - }) -} - fn selector_to_state(sel: &SimpleSelector) -> ElementState { match *sel { SimpleSelector::NonTSPseudoClass(ref pc) => SelectorImpl::pseudo_class_state_flag(pc), - SimpleSelector::Negation(ref negated) => { - negated.iter().fold(ElementState::empty(), |state, s| { - state | complex_selector_to_state(s) - }) - } _ => ElementState::empty(), } } @@ -483,10 +485,35 @@ struct Dependency { sensitivities: Sensitivities, } + +/// The following visitor visits all the simple selectors for a given complex +/// selector, taking care of :not and :any combinators, collecting whether any +/// of them is sensitive to attribute or state changes. +struct SensitivitiesVisitor { + sensitivities: Sensitivities, +} + +impl SelectorVisitor for SensitivitiesVisitor { + type Impl = SelectorImpl; + + fn visit_simple_selector(&mut self, s: &SimpleSelector) -> bool { + self.sensitivities.states.insert(selector_to_state(s)); + + if !self.sensitivities.attrs { + self.sensitivities.attrs = is_attr_selector(s); + } + + true + } +} + /// A visitor struct that collects information for a given selector. /// /// This is the struct responsible of adding dependencies for a given complex -/// selector. +/// selector and all the selectors to its left. +/// +/// This uses a `SensitivitiesVisitor` internally to collect all the +/// dependencies inside the given complex selector. pub struct SelectorDependencyVisitor<'a> { dependency_set: &'a mut DependencySet, needs_cache_revalidation: bool, @@ -511,32 +538,37 @@ impl<'a> SelectorDependencyVisitor<'a> { impl<'a> SelectorVisitor for SelectorDependencyVisitor<'a> { type Impl = SelectorImpl; + fn visit_simple_selector(&mut self, s: &SimpleSelector) -> bool { + if !self.needs_cache_revalidation { + self.needs_cache_revalidation = needs_cache_revalidation(s); + } + + true + } + fn visit_complex_selector(&mut self, selector: &Arc>, combinator: Option) -> bool { - let mut sensitivities = Sensitivities::new(); + let mut sensitivity_visitor = SensitivitiesVisitor { + sensitivities: Sensitivities::new(), + }; + for s in &selector.compound_selector { - sensitivities.states.insert(selector_to_state(s)); - if !self.needs_cache_revalidation { - self.needs_cache_revalidation = needs_cache_revalidation(s); - } - if !sensitivities.attrs { - sensitivities.attrs = is_attr_selector(s); - } + s.visit(&mut sensitivity_visitor); } let hint = combinator_to_restyle_hint(combinator); - self.needs_cache_revalidation |= sensitivities.attrs; + self.needs_cache_revalidation |= sensitivity_visitor.sensitivities.attrs; self.needs_cache_revalidation |= hint.intersects(RESTYLE_LATER_SIBLINGS); - if !sensitivities.is_empty() { + if !sensitivity_visitor.sensitivities.is_empty() { self.dependency_set.add_dependency(Dependency { selector: selector.clone(), hint: hint, - sensitivities: sensitivities, + sensitivities: sensitivity_visitor.sensitivities, }); } diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index c8d994cd4db..c531e5e0762 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -3743,6 +3743,18 @@ {} ] ], + "css/negation-attr-dependence.html": [ + [ + "/_mozilla/css/negation-attr-dependence.html", + [ + [ + "/_mozilla/css/negation-attr-dependence-ref.html", + "==" + ] + ], + {} + ] + ], "css/negative-calc-cv.html": [ [ "/_mozilla/css/negative-calc-cv.html", @@ -8299,6 +8311,11 @@ {} ] ], + "css/negation-attr-dependence-ref.html": [ + [ + {} + ] + ], "css/negative-calc-cv-ref.html": [ [ {} @@ -22501,6 +22518,14 @@ "a82c94a9a4a18cda6009e467993cace8babe7591", "support" ], + "css/negation-attr-dependence-ref.html": [ + "560c5d8b523f6d4cad6e15e5604f51646647d487", + "support" + ], + "css/negation-attr-dependence.html": [ + "27bec4af590a2e6d5e88b54ed37fd254ef0f0a99", + "reftest" + ], "css/negative-calc-cv-ref.html": [ "a42f34470ead45d5865562618f6538fde263a359", "support" diff --git a/tests/wpt/mozilla/tests/css/negation-attr-dependence-ref.html b/tests/wpt/mozilla/tests/css/negation-attr-dependence-ref.html new file mode 100644 index 00000000000..3297637824d --- /dev/null +++ b/tests/wpt/mozilla/tests/css/negation-attr-dependence-ref.html @@ -0,0 +1,8 @@ + + +CSS Test Reference + +
+
Should be green
diff --git a/tests/wpt/mozilla/tests/css/negation-attr-dependence.html b/tests/wpt/mozilla/tests/css/negation-attr-dependence.html new file mode 100644 index 00000000000..99fb54b802f --- /dev/null +++ b/tests/wpt/mozilla/tests/css/negation-attr-dependence.html @@ -0,0 +1,15 @@ + + +CSS Test: Negation with attribute dependence correctly restyle siblings. + + + +
+
Should be green
+