servo: Merge #19747 - style: Add a document state invalidator (from emilio:doc-state-invalidator); r=upsuper
authorEmilio Cobos Álvarez <emilio@crisal.io>
Fri, 12 Jan 2018 05:09:04 -0600
changeset 450801 d80a7ef3d32b3ca1e9dfaf0803c61e752261a4d7
parent 450800 822e3896efed8822e5c2776e7a5873bce63f3e2e
child 450802 cd2dd542895fa0332959c7a877a2d71a4c6c0ef2
push id8543
push userryanvm@gmail.com
push dateTue, 16 Jan 2018 14:33:22 +0000
treeherdermozilla-beta@a6525ed16a32 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersupsuper
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #19747 - style: Add a document state invalidator (from emilio:doc-state-invalidator); r=upsuper Don't use it yet (since I was working from a Servo tree). Will hook it up and improve in the Gecko bug. Right now it takes a `StyleRuleCascadeData`, which means that if all the origins in the document have state selectors we could do just one walk over the tree and not multiple, that will be improved. Other than that, this is completely untested of course, but I prefer to land it, given I don't think it's complex, and work on the Gecko integration separately. The reason for this is that I also plan to fix the `<slot>` bugs, which will change `StyleRuleCascadeData` and such, and I want the two bugs to conflict as little as possible. Source-Repo: https://github.com/servo/servo Source-Revision: 50e4171958d4ff7f1c76d133a8f89e7d5995376f
servo/components/selectors/context.rs
servo/components/selectors/parser.rs
servo/components/selectors/size_of_tests.rs
servo/components/style/data.rs
servo/components/style/gecko/selector_parser.rs
servo/components/style/gecko/wrapper.rs
servo/components/style/invalidation/element/document_state.rs
servo/components/style/invalidation/element/invalidation_map.rs
servo/components/style/invalidation/element/invalidator.rs
servo/components/style/invalidation/element/mod.rs
servo/components/style/invalidation/element/state_and_attributes.rs
servo/components/style/servo/selector_parser.rs
--- a/servo/components/selectors/context.rs
+++ b/servo/components/selectors/context.rs
@@ -125,16 +125,19 @@ where
     /// FIXME(emilio): Move this somewhere else and make MatchingContext
     /// immutable again.
     pub nesting_level: usize,
 
     /// An optional hook function for checking whether a pseudo-element
     /// should match when matching_mode is ForStatelessPseudoElement.
     pub pseudo_element_matching_fn: Option<&'a Fn(&Impl::PseudoElement) -> bool>,
 
+    /// Extra implementation-dependent matching data.
+    pub extra_data: Impl::ExtraMatchingData,
+
     quirks_mode: QuirksMode,
     classes_and_ids_case_sensitivity: CaseSensitivity,
     _impl: ::std::marker::PhantomData<Impl>,
 }
 
 impl<'a, Impl> MatchingContext<'a, Impl>
 where
     Impl: SelectorImpl,
@@ -168,16 +171,17 @@ where
             bloom_filter,
             visited_handling,
             nth_index_cache,
             quirks_mode,
             classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(),
             scope_element: None,
             nesting_level: 0,
             pseudo_element_matching_fn: None,
+            extra_data: Default::default(),
             _impl: ::std::marker::PhantomData,
         }
     }
 
     /// The quirks mode of the document.
     #[inline]
     pub fn quirks_mode(&self) -> QuirksMode {
         self.quirks_mode
--- a/servo/components/selectors/parser.rs
+++ b/servo/components/selectors/parser.rs
@@ -79,16 +79,17 @@ macro_rules! with_all_bounds {
     ) => {
         /// This trait allows to define the parser implementation in regards
         /// of pseudo-classes/elements
         ///
         /// NB: We need Clone so that we can derive(Clone) on struct with that
         /// are parameterized on SelectorImpl. See
         /// <https://github.com/rust-lang/rust/issues/26925>
         pub trait SelectorImpl: Clone + Sized + 'static {
+            type ExtraMatchingData: Sized + Default + 'static;
             type AttrValue: $($InSelector)*;
             type Identifier: $($InSelector)* + PrecomputedHash;
             type ClassName: $($InSelector)* + PrecomputedHash;
             type LocalName: $($InSelector)* + Borrow<Self::BorrowedLocalName> + PrecomputedHash;
             type NamespaceUrl: $($CommonBounds)* + Default + Borrow<Self::BorrowedNamespaceUrl> + PrecomputedHash;
             type NamespacePrefix: $($InSelector)* + Default;
             type BorrowedNamespaceUrl: ?Sized + Eq;
             type BorrowedLocalName: ?Sized + Eq;
@@ -2022,16 +2023,17 @@ pub mod tests {
             DummyParser {
                 default_ns: Some(default_ns),
                 ns_prefixes: Default::default(),
             }
         }
     }
 
     impl SelectorImpl for DummySelectorImpl {
+        type ExtraMatchingData = ();
         type AttrValue = DummyAtom;
         type Identifier = DummyAtom;
         type ClassName = DummyAtom;
         type LocalName = DummyAtom;
         type NamespaceUrl = DummyAtom;
         type NamespacePrefix = DummyAtom;
         type BorrowedLocalName = DummyAtom;
         type BorrowedNamespaceUrl = DummyAtom;
--- a/servo/components/selectors/size_of_tests.rs
+++ b/servo/components/selectors/size_of_tests.rs
@@ -19,16 +19,17 @@ size_of_test!(size_of_pseudo_class, Pseu
 
 impl parser::PseudoElement for gecko_like_types::PseudoElement {
     type Impl = Impl;
 }
 
 // Boilerplate
 
 impl SelectorImpl for Impl {
+    type ExtraMatchingData = u64;
     type AttrValue = Atom;
     type Identifier = Atom;
     type ClassName = Atom;
     type LocalName = Atom;
     type NamespaceUrl = Atom;
     type NamespacePrefix = Atom;
     type BorrowedLocalName = Atom;
     type BorrowedNamespaceUrl = Atom;
--- a/servo/components/style/data.rs
+++ b/servo/components/style/data.rs
@@ -257,16 +257,18 @@ impl ElementData {
                 element.handled_snapshot(),
                 element.implemented_pseudo_element());
 
         if !element.has_snapshot() || element.handled_snapshot() {
             return InvalidationResult::empty();
         }
 
         let mut xbl_stylists = SmallVec::<[_; 3]>::new();
+        // FIXME(emilio): This is wrong, needs to account for ::slotted rules
+        // that may apply to elements down the tree.
         let cut_off_inheritance =
             element.each_applicable_non_document_style_rule_data(|data, quirks_mode| {
                 xbl_stylists.push((data, quirks_mode))
             });
 
         let mut processor = StateAndAttrInvalidationProcessor::new(
             shared_context,
             &xbl_stylists,
--- a/servo/components/style/gecko/selector_parser.rs
+++ b/servo/components/style/gecko/selector_parser.rs
@@ -4,16 +4,17 @@
 
 //! Gecko-specific bits for selector-parsing.
 
 use cssparser::{BasicParseError, BasicParseErrorKind, Parser, ToCss, Token, CowRcStr, SourceLocation};
 use element_state::{DocumentState, ElementState};
 use gecko_bindings::structs::{self, CSSPseudoClassType};
 use gecko_bindings::structs::RawServoSelectorList;
 use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
+use invalidation::element::document_state::InvalidationMatchingData;
 use selector_parser::{Direction, SelectorParser};
 use selectors::SelectorList;
 use selectors::parser::{self as selector_parser, Selector, SelectorMethods, SelectorParseErrorKind};
 use selectors::visitor::SelectorVisitor;
 use std::fmt;
 use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
 use style_traits::{ParseError, StyleParseErrorKind, ToCss as ToCss_};
 
@@ -273,16 +274,17 @@ impl NonTSPseudoClass {
     }
 }
 
 /// The dummy struct we use to implement our selector parsing.
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct SelectorImpl;
 
 impl ::selectors::SelectorImpl for SelectorImpl {
+    type ExtraMatchingData = InvalidationMatchingData;
     type AttrValue = Atom;
     type Identifier = Atom;
     type ClassName = Atom;
     type LocalName = Atom;
     type NamespacePrefix = Atom;
     type NamespaceUrl = Namespace;
     type BorrowedNamespaceUrl = WeakNamespace;
     type BorrowedLocalName = WeakAtom;
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -2105,35 +2105,44 @@ impl<'le> ::selectors::Element for Gecko
             }
             NonTSPseudoClass::MozLWThemeBrightText => {
                 self.get_document_theme() == DocumentTheme::Doc_Theme_Bright
             }
             NonTSPseudoClass::MozLWThemeDarkText => {
                 self.get_document_theme() == DocumentTheme::Doc_Theme_Dark
             }
             NonTSPseudoClass::MozWindowInactive => {
-                self.document_state()
-                    .contains(DocumentState::NS_DOCUMENT_STATE_WINDOW_INACTIVE)
+                let state_bit = DocumentState::NS_DOCUMENT_STATE_WINDOW_INACTIVE;
+                if context.extra_data.document_state.intersects(state_bit) {
+                    return true;
+                }
+
+                self.document_state().contains(state_bit)
             }
             NonTSPseudoClass::MozPlaceholder => false,
             NonTSPseudoClass::MozAny(ref sels) => {
                 context.nesting_level += 1;
                 let result = sels.iter().any(|s| {
                     matches_complex_selector(s.iter(), self, context, flags_setter)
                 });
                 context.nesting_level -= 1;
                 result
             }
             NonTSPseudoClass::Lang(ref lang_arg) => {
                 self.match_element_lang(None, lang_arg)
             }
             NonTSPseudoClass::MozLocaleDir(ref dir) => {
-                let doc_is_rtl =
-                    self.document_state()
-                        .contains(DocumentState::NS_DOCUMENT_STATE_RTL_LOCALE);
+                let state_bit = DocumentState::NS_DOCUMENT_STATE_RTL_LOCALE;
+                if context.extra_data.document_state.intersects(state_bit) {
+                    // NOTE(emilio): We could still return false for
+                    // Direction::Other(..), but we don't bother.
+                    return true;
+                }
+
+                let doc_is_rtl = self.document_state().contains(state_bit);
 
                 match **dir {
                     Direction::Ltr => !doc_is_rtl,
                     Direction::Rtl => doc_is_rtl,
                     Direction::Other(..) => false,
                 }
             }
             NonTSPseudoClass::Dir(ref dir) => {
new file mode 100644
--- /dev/null
+++ b/servo/components/style/invalidation/element/document_state.rs
@@ -0,0 +1,109 @@
+/* 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/. */
+
+//! An invalidation processor for style changes due to document state changes.
+
+use dom::TElement;
+use element_state::DocumentState;
+use invalidation::element::invalidator::{DescendantInvalidationLists, InvalidationVector};
+use invalidation::element::invalidator::{Invalidation, InvalidationProcessor};
+use invalidation::element::state_and_attributes;
+use selectors::matching::{MatchingContext, MatchingMode, QuirksMode, VisitedHandlingMode};
+use stylist::StyleRuleCascadeData;
+
+/// A struct holding the members necessary to invalidate document state
+/// selectors.
+pub struct InvalidationMatchingData {
+    /// The document state that has changed, which makes it always match.
+    pub document_state: DocumentState,
+}
+
+impl Default for InvalidationMatchingData {
+    #[inline(always)]
+    fn default() -> Self {
+        Self {
+            document_state: DocumentState::empty(),
+        }
+    }
+}
+
+/// An invalidation processor for style changes due to state and attribute
+/// changes.
+pub struct DocumentStateInvalidationProcessor<'a, E: TElement> {
+    // TODO(emilio): We might want to just run everything for every possible
+    // binding along with the document data, or just apply the XBL stuff to the
+    // bound subtrees.
+    rules: &'a StyleRuleCascadeData,
+    matching_context: MatchingContext<'a, E::Impl>,
+    document_states_changed: DocumentState,
+}
+
+impl<'a, E: TElement> DocumentStateInvalidationProcessor<'a, E> {
+    /// Creates a new DocumentStateInvalidationProcessor.
+    #[inline]
+    pub fn new(
+        rules: &'a StyleRuleCascadeData,
+        document_states_changed: DocumentState,
+        quirks_mode: QuirksMode,
+    ) -> Self {
+        let mut matching_context = MatchingContext::new_for_visited(
+            MatchingMode::Normal,
+            None,
+            None,
+            VisitedHandlingMode::AllLinksVisitedAndUnvisited,
+            quirks_mode,
+        );
+
+        matching_context.extra_data = InvalidationMatchingData {
+            document_state: document_states_changed,
+        };
+
+        Self { rules, document_states_changed, matching_context }
+    }
+}
+
+impl<'a, E: TElement> InvalidationProcessor<'a, E> for DocumentStateInvalidationProcessor<'a, E> {
+    fn collect_invalidations(
+        &mut self,
+        _element: E,
+        self_invalidations: &mut InvalidationVector<'a>,
+        _descendant_invalidations: &mut DescendantInvalidationLists<'a>,
+        _sibling_invalidations: &mut InvalidationVector<'a>,
+    ) -> bool {
+        let map = self.rules.invalidation_map();
+
+        for dependency in &map.document_state_selectors {
+            if !dependency.state.intersects(self.document_states_changed) {
+                continue;
+            }
+
+            self_invalidations.push(Invalidation::new(&dependency.selector, 0));
+        }
+
+        false
+    }
+
+    fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {
+        &mut self.matching_context
+    }
+
+    fn recursion_limit_exceeded(&mut self, _: E) {
+        unreachable!("We don't run document state invalidation with stack limits")
+    }
+
+    fn should_process_descendants(&mut self, element: E) -> bool {
+        match element.borrow_data() {
+            Some(d) => state_and_attributes::should_process_descendants(&d),
+            None => false,
+        }
+    }
+
+    fn invalidated_descendants(&mut self, element: E, child: E) {
+        state_and_attributes::invalidated_descendants(element, child)
+    }
+
+    fn invalidated_self(&mut self, element: E) {
+        state_and_attributes::invalidated_self(element);
+    }
+}
--- a/servo/components/style/invalidation/element/invalidation_map.rs
+++ b/servo/components/style/invalidation/element/invalidation_map.rs
@@ -1,17 +1,17 @@
 /* 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/. */
 
 //! Code for invalidations due to state or attribute changes.
 
 use {Atom, LocalName, Namespace};
 use context::QuirksMode;
-use element_state::ElementState;
+use element_state::{DocumentState, ElementState};
 use fallible::FallibleVec;
 use hashglobe::FailedAllocationError;
 use selector_map::{MaybeCaseInsensitiveHashMap, SelectorMap, SelectorMapEntry};
 #[cfg(feature = "gecko")]
 use selector_parser::Direction;
 use selector_parser::SelectorImpl;
 use selectors::attr::NamespaceConstraint;
 use selectors::parser::{Combinator, Component};
@@ -128,16 +128,29 @@ pub struct StateDependency {
 }
 
 impl SelectorMapEntry for StateDependency {
     fn selector(&self) -> SelectorIter<SelectorImpl> {
         self.dep.selector()
     }
 }
 
+/// The same, but for document state selectors.
+#[derive(Clone, Debug, MallocSizeOf)]
+pub struct DocumentStateDependency {
+    /// The selector that is affected. We don't need to track an offset, since
+    /// when it changes it changes for the whole document anyway.
+    #[cfg_attr(feature = "gecko",
+               ignore_malloc_size_of = "CssRules have primary refs, we measure there")]
+    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
+    pub selector: Selector<SelectorImpl>,
+    /// The state this dependency is affected by.
+    pub state: DocumentState,
+}
+
 /// A map where we store invalidations.
 ///
 /// This is slightly different to a SelectorMap, in the sense of that the same
 /// selector may appear multiple times.
 ///
 /// In particular, we want to lookup as few things as possible to get the fewer
 /// selectors the better, so this looks up by id, class, or looks at the list of
 /// state/other attribute affecting selectors.
@@ -146,16 +159,18 @@ pub struct InvalidationMap {
     /// A map from a given class name to all the selectors with that class
     /// selector.
     pub class_to_selector: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[Dependency; 1]>>,
     /// A map from a given id to all the selectors with that ID in the
     /// stylesheets currently applying to the document.
     pub id_to_selector: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[Dependency; 1]>>,
     /// A map of all the state dependencies.
     pub state_affecting_selectors: SelectorMap<StateDependency>,
+    /// A list of document state dependencies in the rules we represent.
+    pub document_state_selectors: Vec<DocumentStateDependency>,
     /// A map of other attribute affecting selectors.
     pub other_attribute_affecting_selectors: SelectorMap<Dependency>,
     /// Whether there are attribute rules of the form `[class~="foo"]` that may
     /// match. In that case, we need to look at
     /// `other_attribute_affecting_selectors` too even if only the `class` has
     /// changed.
     pub has_class_attribute_selectors: bool,
     /// Whether there are attribute rules of the form `[id|="foo"]` that may
@@ -167,49 +182,52 @@ pub struct InvalidationMap {
 
 impl InvalidationMap {
     /// Creates an empty `InvalidationMap`.
     pub fn new() -> Self {
         Self {
             class_to_selector: MaybeCaseInsensitiveHashMap::new(),
             id_to_selector: MaybeCaseInsensitiveHashMap::new(),
             state_affecting_selectors: SelectorMap::new(),
+            document_state_selectors: Vec::new(),
             other_attribute_affecting_selectors: SelectorMap::new(),
             has_class_attribute_selectors: false,
             has_id_attribute_selectors: false,
         }
     }
 
     /// Returns the number of dependencies stored in the invalidation map.
     pub fn len(&self) -> usize {
         self.state_affecting_selectors.len() +
+        self.document_state_selectors.len() +
         self.other_attribute_affecting_selectors.len() +
         self.id_to_selector.iter().fold(0, |accum, (_, ref v)| {
             accum + v.len()
         }) +
         self.class_to_selector.iter().fold(0, |accum, (_, ref v)| {
             accum + v.len()
         })
     }
 
     /// Adds a selector to this `InvalidationMap`.  Returns Err(..) to
     /// signify OOM.
     pub fn note_selector(
         &mut self,
         selector: &Selector<SelectorImpl>,
-        quirks_mode: QuirksMode
+        quirks_mode: QuirksMode,
     ) -> Result<(), FailedAllocationError> {
         self.collect_invalidations_for(selector, quirks_mode)
     }
 
     /// Clears this map, leaving it empty.
     pub fn clear(&mut self) {
         self.class_to_selector.clear();
         self.id_to_selector.clear();
         self.state_affecting_selectors.clear();
+        self.document_state_selectors.clear();
         self.other_attribute_affecting_selectors.clear();
         self.has_id_attribute_selectors = false;
         self.has_class_attribute_selectors = false;
     }
 
     // Returns Err(..) to signify OOM.
     fn collect_invalidations_for(
         &mut self,
@@ -217,23 +235,26 @@ impl InvalidationMap {
         quirks_mode: QuirksMode
     ) -> Result<(), FailedAllocationError> {
         debug!("InvalidationMap::collect_invalidations_for({:?})", selector);
 
         let mut iter = selector.iter();
         let mut combinator;
         let mut index = 0;
 
+        let mut document_state = DocumentState::empty();
+
         loop {
             let sequence_start = index;
 
             let mut compound_visitor = CompoundSelectorDependencyCollector {
                 classes: SmallVec::new(),
                 ids: SmallVec::new(),
                 state: ElementState::empty(),
+                document_state: &mut document_state,
                 other_attributes: false,
                 has_id_attribute_selectors: false,
                 has_class_attribute_selectors: false,
             };
 
             // Visit all the simple selectors in this sequence.
             //
             // Note that this works because we can't have combinators nested
@@ -292,25 +313,38 @@ impl InvalidationMap {
             combinator = iter.next_sequence();
             if combinator.is_none() {
                 break;
             }
 
             index += 1; // Account for the combinator.
         }
 
+        if !document_state.is_empty() {
+            self.document_state_selectors.try_push(DocumentStateDependency {
+                state: document_state,
+                selector: selector.clone(),
+            })?;
+        }
+
         Ok(())
     }
 }
 
 /// A struct that collects invalidations for a given compound selector.
-struct CompoundSelectorDependencyCollector {
+struct CompoundSelectorDependencyCollector<'a> {
     /// The state this compound selector is affected by.
     state: ElementState,
 
+    /// The document this _complex_ selector is affected by.
+    ///
+    /// We don't need to track state per compound selector, since it's global
+    /// state and it changes for everything.
+    document_state: &'a mut DocumentState,
+
     /// The classes this compound selector is affected by.
     ///
     /// NB: This will be often a single class, but could be multiple in
     /// presence of :not, :-moz-any, .foo.bar.baz, etc.
     classes: SmallVec<[Atom; 5]>,
 
     /// The IDs this compound selector is affected by.
     ///
@@ -325,17 +359,17 @@ struct CompoundSelectorDependencyCollect
 
     /// Whether there were attribute selectors with the id attribute.
     has_id_attribute_selectors: bool,
 
     /// Whether there were attribute selectors with the class attribute.
     has_class_attribute_selectors: bool,
 }
 
-impl SelectorVisitor for CompoundSelectorDependencyCollector {
+impl<'a> SelectorVisitor for CompoundSelectorDependencyCollector<'a> {
     type Impl = SelectorImpl;
 
     fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {
         #[cfg(feature = "gecko")]
         use selector_parser::NonTSPseudoClass;
 
         match *s {
             Component::ID(ref id) => {
@@ -348,16 +382,17 @@ impl SelectorVisitor for CompoundSelecto
                 self.other_attributes |= pc.is_attr_based();
                 self.state |= match *pc {
                     #[cfg(feature = "gecko")]
                     NonTSPseudoClass::Dir(ref dir) => {
                         dir_selector_to_state(dir)
                     }
                     _ => pc.state_flag(),
                 };
+                *self.document_state |= pc.document_state_flag();
             }
             _ => {}
         }
 
         true
     }
 
     fn visit_attribute_selector(
--- a/servo/components/style/invalidation/element/invalidator.rs
+++ b/servo/components/style/invalidation/element/invalidator.rs
@@ -500,16 +500,19 @@ where
 
             any |= self.invalidate_child(
                 element,
                 invalidations,
                 &mut sibling_invalidations,
                 DescendantInvalidationKind::Slotted,
             );
 
+            // FIXME(emilio): Need to handle nested slotted nodes if `element`
+            // is itself a <slot>.
+
             debug_assert!(
                 sibling_invalidations.is_empty(),
                 "::slotted() shouldn't have sibling combinators to the right, \
                  this makes no sense! {:?}",
                 sibling_invalidations
             );
         }
 
--- a/servo/components/style/invalidation/element/mod.rs
+++ b/servo/components/style/invalidation/element/mod.rs
@@ -1,11 +1,12 @@
 /* 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/. */
 
 //! Invalidation of element styles due to attribute or style changes.
 
+pub mod document_state;
 pub mod element_wrapper;
 pub mod invalidation_map;
 pub mod invalidator;
 pub mod restyle_hints;
 pub mod state_and_attributes;
--- a/servo/components/style/invalidation/element/state_and_attributes.rs
+++ b/servo/components/style/invalidation/element/state_and_attributes.rs
@@ -293,22 +293,20 @@ where
         invalidated_self
     }
 
     fn should_process_descendants(&mut self, element: E) -> bool {
         if element == self.element {
             return should_process_descendants(&self.data)
         }
 
-        let data = match element.borrow_data() {
-            Some(d) => d,
+        match element.borrow_data() {
+            Some(d) => should_process_descendants(&d),
             None => return false,
-        };
-
-        should_process_descendants(&data)
+        }
     }
 
     fn recursion_limit_exceeded(&mut self, element: E) {
         if element == self.element {
             self.data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS);
             return;
         }
 
--- a/servo/components/style/servo/selector_parser.rs
+++ b/servo/components/style/servo/selector_parser.rs
@@ -7,16 +7,17 @@
 //! Servo's selector parser.
 
 use {Atom, Prefix, Namespace, LocalName, CaseSensitivityExt};
 use attr::{AttrIdentifier, AttrValue};
 use cssparser::{Parser as CssParser, ToCss, serialize_identifier, CowRcStr, SourceLocation};
 use dom::{OpaqueNode, TElement, TNode};
 use element_state::{DocumentState, ElementState};
 use fnv::FnvHashMap;
+use invalidation::element::document_state::InvalidationMatchingData;
 use invalidation::element::element_wrapper::ElementSnapshot;
 use properties::ComputedValues;
 use properties::PropertyFlags;
 use properties::longhands::display::computed_value::T as Display;
 use selector_parser::{AttrValue as SelectorAttrValue, PseudoElementCascadeType, SelectorParser};
 use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
 use selectors::parser::{SelectorMethods, SelectorParseErrorKind};
 use selectors::visitor::SelectorVisitor;
@@ -372,16 +373,17 @@ impl NonTSPseudoClass {
 #[derive(Clone, Debug, PartialEq)]
 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
 pub struct SelectorImpl;
 
 impl ::selectors::SelectorImpl for SelectorImpl {
     type PseudoElement = PseudoElement;
     type NonTSPseudoClass = NonTSPseudoClass;
 
+    type ExtraMatchingData = InvalidationMatchingData;
     type AttrValue = String;
     type Identifier = Atom;
     type ClassName = Atom;
     type LocalName = LocalName;
     type NamespacePrefix = Prefix;
     type NamespaceUrl = Namespace;
     type BorrowedLocalName = LocalName;
     type BorrowedNamespaceUrl = Namespace;