servo: Merge #16872 - Create a MatchingContext to group related matching args (from jryans:matching-context); r=emilio
authorJ. Ryan Stinnett <jryans@gmail.com>
Tue, 16 May 2017 09:45:36 -0500
changeset 358613 62ff1262c0906f716b8db002b94644479d4719bf
parent 358612 638cd9bc82f45405386e56989af6b783f887c3cd
child 358614 81a6855ed0939635bdfdce471697b0d72e88fd5e
push id31833
push usercbook@mozilla.com
push dateWed, 17 May 2017 09:14:24 +0000
treeherdermozilla-central@95990be385ca [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
milestone55.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 #16872 - Create a MatchingContext to group related matching args (from jryans:matching-context); r=emilio https://bugzilla.mozilla.org/show_bug.cgi?id=1364914 Upcoming changes for visited support and others as well need to pass more args through matching, so this creates a MatchingContext to group them together. Source-Repo: https://github.com/servo/servo Source-Revision: 7f6b28e241e58944f1e11826094776446965ca64
servo/components/script/dom/element.rs
servo/components/script/layout_wrapper.rs
servo/components/selectors/matching.rs
servo/components/selectors/tree.rs
servo/components/style/gecko/wrapper.rs
servo/components/style/matching.rs
servo/components/style/restyle_hints.rs
servo/components/style/servo/selector_parser.rs
servo/components/style/stylist.rs
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -80,17 +80,17 @@ use html5ever::serialize;
 use html5ever::serialize::SerializeOpts;
 use html5ever::serialize::TraversalScope;
 use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
 use js::jsapi::{HandleValue, JSAutoCompartment};
 use net_traits::request::CorsSettings;
 use ref_filter_map::ref_filter_map;
 use script_layout_interface::message::ReflowQueryType;
 use script_thread::Runnable;
-use selectors::matching::{ElementSelectorFlags, StyleRelations, matches_selector_list};
+use selectors::matching::{ElementSelectorFlags, MatchingContext, matches_selector_list};
 use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS};
 use selectors::parser::{AttrSelector, NamespaceConstraint};
 use servo_atoms::Atom;
 use std::ascii::AsciiExt;
 use std::borrow::Cow;
 use std::cell::{Cell, Ref};
 use std::convert::TryFrom;
 use std::default::Default;
@@ -2409,17 +2409,17 @@ impl<'a> ::selectors::Element for Root<E
     }
 
     fn get_namespace(&self) -> &Namespace {
         self.namespace()
     }
 
     fn match_non_ts_pseudo_class<F>(&self,
                                     pseudo_class: &NonTSPseudoClass,
-                                    _: &mut StyleRelations,
+                                    _: &mut MatchingContext,
                                     _: &mut F)
                                     -> bool
         where F: FnMut(&Self, ElementSelectorFlags),
     {
         match *pseudo_class {
             // https://github.com/servo/servo/issues/8718
             NonTSPseudoClass::Link |
             NonTSPseudoClass::AnyLink => self.is_link(),
--- a/servo/components/script/layout_wrapper.rs
+++ b/servo/components/script/layout_wrapper.rs
@@ -45,17 +45,17 @@ use dom::text::Text;
 use gfx_traits::ByteIndex;
 use html5ever::{LocalName, Namespace};
 use msg::constellation_msg::{FrameId, PipelineId};
 use range::Range;
 use script_layout_interface::{HTMLCanvasData, LayoutNodeType, SVGSVGData, TrustedNodeAddress};
 use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutData};
 use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode};
 use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
-use selectors::matching::{ElementSelectorFlags, StyleRelations};
+use selectors::matching::{ElementSelectorFlags, MatchingContext};
 use selectors::parser::{AttrSelector, NamespaceConstraint};
 use servo_atoms::Atom;
 use servo_url::ServoUrl;
 use std::fmt;
 use std::fmt::Debug;
 use std::hash::{Hash, Hasher};
 use std::marker::PhantomData;
 use std::mem::transmute;
@@ -649,17 +649,17 @@ impl<'le> ::selectors::Element for Servo
 
     #[inline]
     fn get_namespace(&self) -> &Namespace {
         self.element.namespace()
     }
 
     fn match_non_ts_pseudo_class<F>(&self,
                                     pseudo_class: &NonTSPseudoClass,
-                                    _: &mut StyleRelations,
+                                    _: &mut MatchingContext,
                                     _: &mut F)
                                     -> bool
         where F: FnMut(&Self, ElementSelectorFlags),
     {
         match *pseudo_class {
             // https://github.com/servo/servo/issues/8718
             NonTSPseudoClass::Link |
             NonTSPseudoClass::AnyLink => unsafe {
@@ -1147,17 +1147,17 @@ impl<'le> ::selectors::Element for Servo
 
     #[inline]
     fn get_namespace(&self) -> &Namespace {
         self.element.get_namespace()
     }
 
     fn match_non_ts_pseudo_class<F>(&self,
                                     _: &NonTSPseudoClass,
-                                    _: &mut StyleRelations,
+                                    _: &mut MatchingContext,
                                     _: &mut F)
                                     -> bool
         where F: FnMut(&Self, ElementSelectorFlags),
     {
         // NB: This could maybe be implemented
         warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called");
         false
     }
--- a/servo/components/selectors/matching.rs
+++ b/servo/components/selectors/matching.rs
@@ -12,16 +12,17 @@ use tree::Element;
 // rapidly increase.
 pub static RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE: usize = 4096;
 
 bitflags! {
     /// Set of flags that determine the different kind of elements affected by
     /// the selector matching process.
     ///
     /// This is used to implement efficient sharing.
+    #[derive(Default)]
     pub flags StyleRelations: usize {
         /// Whether this element is affected by an ID selector.
         const AFFECTED_BY_ID_SELECTOR = 1 << 0,
         /// Whether this element has a style attribute. Computed
         /// externally.
         const AFFECTED_BY_STYLE_ATTRIBUTE = 1 << 1,
         /// Whether this element is affected by presentational hints. This is
         /// computed externally (that is, in Servo).
@@ -63,28 +64,38 @@ impl ElementSelectorFlags {
     }
 
     /// Returns the subset of flags that apply to the parent.
     pub fn for_parent(self) -> ElementSelectorFlags {
         self & (HAS_SLOW_SELECTOR | HAS_SLOW_SELECTOR_LATER_SIBLINGS | HAS_EDGE_CHILD_SELECTOR)
     }
 }
 
+/// Data associated with the matching process for a element.  This context is
+/// used across many selectors for an element, so it's not appropriate for
+/// transient data that applies to only a single selector.
+#[derive(Default)]
+pub struct MatchingContext {
+    /// Output that records certains relations between elements noticed during
+    /// matching (and also extended after matching).
+    pub relations: StyleRelations,
+}
+
 pub fn matches_selector_list<E>(selector_list: &[Selector<E::Impl>],
                                 element: &E,
                                 parent_bf: Option<&BloomFilter>)
                                 -> bool
     where E: Element
 {
     selector_list.iter().any(|selector| {
         selector.pseudo_element.is_none() &&
         matches_selector(&selector.inner,
                          element,
                          parent_bf,
-                         &mut StyleRelations::empty(),
+                         &mut MatchingContext::default(),
                          &mut |_, _| {})
     })
 }
 
 fn may_match<E>(sel: &SelectorInner<E::Impl>,
                 bf: &BloomFilter)
                 -> bool
     where E: Element,
@@ -103,31 +114,31 @@ fn may_match<E>(sel: &SelectorInner<E::I
 
     true
 }
 
 /// Determines whether the given element matches the given complex selector.
 pub fn matches_selector<E, F>(selector: &SelectorInner<E::Impl>,
                               element: &E,
                               parent_bf: Option<&BloomFilter>,
-                              relations: &mut StyleRelations,
+                              context: &mut MatchingContext,
                               flags_setter: &mut F)
                               -> bool
     where E: Element,
           F: FnMut(&E, ElementSelectorFlags),
 {
     // Use the bloom filter to fast-reject.
     if let Some(filter) = parent_bf {
         if !may_match::<E>(selector, filter) {
             return false;
         }
     }
 
     // Match the selector.
-    matches_complex_selector(&selector.complex, element, relations, flags_setter)
+    matches_complex_selector(&selector.complex, element, context, flags_setter)
 }
 
 /// A result of selector matching, includes 3 failure types,
 ///
 ///   NotMatchedAndRestartFromClosestLaterSibling
 ///   NotMatchedAndRestartFromClosestDescendant
 ///   NotMatchedGlobally
 ///
@@ -173,41 +184,41 @@ enum SelectorMatchingResult {
     NotMatchedAndRestartFromClosestLaterSibling,
     NotMatchedAndRestartFromClosestDescendant,
     NotMatchedGlobally,
 }
 
 /// Matches a complex selector.
 pub fn matches_complex_selector<E, F>(selector: &ComplexSelector<E::Impl>,
                                       element: &E,
-                                      relations: &mut StyleRelations,
+                                      context: &mut MatchingContext,
                                       flags_setter: &mut F)
                                       -> bool
      where E: Element,
            F: FnMut(&E, ElementSelectorFlags),
 {
     match matches_complex_selector_internal(selector.iter(),
                                             element,
-                                            relations,
+                                            context,
                                             flags_setter) {
         SelectorMatchingResult::Matched => true,
         _ => false
     }
 }
 
 fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Impl>,
                                            element: &E,
-                                           relations: &mut StyleRelations,
+                                           context: &mut MatchingContext,
                                            flags_setter: &mut F)
                                            -> SelectorMatchingResult
      where E: Element,
            F: FnMut(&E, ElementSelectorFlags),
 {
     let matches_all_simple_selectors = selector_iter.all(|simple| {
-        matches_simple_selector(simple, element, relations, flags_setter)
+        matches_simple_selector(simple, element, context, flags_setter)
     });
 
     let combinator = selector_iter.next_sequence();
     let siblings = combinator.map_or(false, |c| c.is_sibling());
     if siblings {
         flags_setter(element, HAS_SLOW_SELECTOR_LATER_SIBLINGS);
     }
 
@@ -228,17 +239,17 @@ fn matches_complex_selector_internal<E, 
 
             loop {
                 let element = match next_element {
                     None => return candidate_not_found,
                     Some(next_element) => next_element,
                 };
                 let result = matches_complex_selector_internal(selector_iter.clone(),
                                                                &element,
-                                                               relations,
+                                                               context,
                                                                flags_setter);
                 match (result, c) {
                     // Return the status immediately.
                     (SelectorMatchingResult::Matched, _) => return result,
                     (SelectorMatchingResult::NotMatchedGlobally, _) => return result,
 
                     // Upgrade the failure status to
                     // NotMatchedAndRestartFromClosestDescendant.
@@ -271,26 +282,26 @@ fn matches_complex_selector_internal<E, 
     }
 }
 
 /// Determines whether the given element matches the given single selector.
 #[inline]
 fn matches_simple_selector<E, F>(
         selector: &Component<E::Impl>,
         element: &E,
-        relations: &mut StyleRelations,
+        context: &mut MatchingContext,
         flags_setter: &mut F)
         -> bool
     where E: Element,
           F: FnMut(&E, ElementSelectorFlags),
 {
     macro_rules! relation_if {
         ($ex:expr, $flag:ident) => {
             if $ex {
-                *relations |= $flag;
+                context.relations |= $flag;
                 true
             } else {
                 false
             }
         }
     }
 
     match *selector {
@@ -336,17 +347,17 @@ fn matches_simple_selector<E, F>(
         }
         Component::AttrIncludesNeverMatch(..) |
         Component::AttrPrefixNeverMatch(..) |
         Component::AttrSubstringNeverMatch(..) |
         Component::AttrSuffixNeverMatch(..) => {
             false
         }
         Component::NonTSPseudoClass(ref pc) => {
-            element.match_non_ts_pseudo_class(pc, relations, flags_setter)
+            element.match_non_ts_pseudo_class(pc, context, flags_setter)
         }
         Component::FirstChild => {
             matches_first_child(element, flags_setter)
         }
         Component::LastChild => {
             matches_last_child(element, flags_setter)
         }
         Component::OnlyChild => {
@@ -380,17 +391,17 @@ fn matches_simple_selector<E, F>(
         Component::LastOfType => {
             matches_generic_nth_child(element, 0, 1, true, true, flags_setter)
         }
         Component::OnlyOfType => {
             matches_generic_nth_child(element, 0, 1, true, false, flags_setter) &&
             matches_generic_nth_child(element, 0, 1, true, true, flags_setter)
         }
         Component::Negation(ref negated) => {
-            !negated.iter().all(|ss| matches_simple_selector(ss, element, relations, flags_setter))
+            !negated.iter().all(|ss| matches_simple_selector(ss, element, context, flags_setter))
         }
     }
 }
 
 #[inline]
 fn matches_generic_nth_child<E, F>(element: &E,
                                    a: i32,
                                    b: i32,
--- a/servo/components/selectors/tree.rs
+++ b/servo/components/selectors/tree.rs
@@ -1,16 +1,16 @@
 /* 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/. */
 
 //! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and
 //! style.
 
-use matching::{ElementSelectorFlags, StyleRelations};
+use matching::{ElementSelectorFlags, MatchingContext};
 use parser::{AttrSelector, SelectorImpl};
 use std::ascii::AsciiExt;
 
 /// The definition of whitespace per CSS Selectors Level 3 ยง 4.
 pub static SELECTOR_WHITESPACE: &'static [char] = &[' ', '\t', '\n', '\r', '\x0C'];
 
 // Attribute matching routines. Consumers with simple implementations can implement
 // MatchAttrGeneric instead.
@@ -136,17 +136,17 @@ pub trait Element: MatchAttr + Sized {
     fn next_sibling_element(&self) -> Option<Self>;
 
     fn is_html_element_in_html_document(&self) -> bool;
     fn get_local_name(&self) -> &<Self::Impl as SelectorImpl>::BorrowedLocalName;
     fn get_namespace(&self) -> &<Self::Impl as SelectorImpl>::BorrowedNamespaceUrl;
 
     fn match_non_ts_pseudo_class<F>(&self,
                                     pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
-                                    relations: &mut StyleRelations,
+                                    context: &mut MatchingContext,
                                     flags_setter: &mut F) -> bool
         where F: FnMut(&Self, ElementSelectorFlags);
 
     fn get_id(&self) -> Option<<Self::Impl as SelectorImpl>::Identifier>;
     fn has_class(&self, name: &<Self::Impl as SelectorImpl>::ClassName) -> bool;
 
     /// Returns whether this element matches `:empty`.
     ///
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -59,17 +59,17 @@ use logical_geometry::WritingMode;
 use media_queries::Device;
 use properties::{ComputedValues, parse_style_attribute};
 use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
 use properties::animated_properties::{AnimationValue, AnimationValueMap, TransitionProperty};
 use properties::style_structs::Font;
 use rule_tree::CascadeLevel as ServoCascadeLevel;
 use selector_parser::ElementExt;
 use selectors::Element;
-use selectors::matching::{ElementSelectorFlags, StyleRelations};
+use selectors::matching::{ElementSelectorFlags, MatchingContext};
 use selectors::parser::{AttrSelector, NamespaceConstraint};
 use shared_lock::Locked;
 use sink::Push;
 use std::cell::RefCell;
 use std::collections::HashMap;
 use std::fmt;
 use std::hash::{Hash, Hasher};
 use std::ptr;
@@ -1120,17 +1120,17 @@ impl<'le> ::selectors::Element for Gecko
     fn get_namespace(&self) -> &WeakNamespace {
         unsafe {
             WeakNamespace::new(Gecko_Namespace(self.0))
         }
     }
 
     fn match_non_ts_pseudo_class<F>(&self,
                                     pseudo_class: &NonTSPseudoClass,
-                                    relations: &mut StyleRelations,
+                                    context: &mut MatchingContext,
                                     flags_setter: &mut F)
                                     -> bool
         where F: FnMut(&Self, ElementSelectorFlags),
     {
         use selectors::matching::*;
         match *pseudo_class {
             NonTSPseudoClass::AnyLink |
             NonTSPseudoClass::Link |
@@ -1214,17 +1214,17 @@ impl<'le> ::selectors::Element for Gecko
             }
             NonTSPseudoClass::MozTableBorderNonzero |
             NonTSPseudoClass::MozBrowserFrame |
             NonTSPseudoClass::MozNativeAnonymous => unsafe {
                 Gecko_MatchesElement(pseudo_class.to_gecko_pseudoclasstype().unwrap(), self.0)
             },
             NonTSPseudoClass::MozAny(ref sels) => {
                 sels.iter().any(|s| {
-                    matches_complex_selector(s, self, relations, flags_setter)
+                    matches_complex_selector(s, self, context, flags_setter)
                 })
             }
             NonTSPseudoClass::MozSystemMetric(ref s) |
             NonTSPseudoClass::MozLocaleDir(ref s) |
             NonTSPseudoClass::MozEmptyExceptChildrenWithLocalname(ref s) |
             NonTSPseudoClass::Dir(ref s) |
             NonTSPseudoClass::Lang(ref s) => {
                 unsafe {
@@ -1371,17 +1371,17 @@ impl<'le> ::selectors::MatchAttr for Gec
         }
     }
 }
 
 impl<'le> ElementExt for GeckoElement<'le> {
     #[inline]
     fn is_link(&self) -> bool {
         self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
-                                       &mut StyleRelations::empty(),
+                                       &mut MatchingContext::default(),
                                        &mut |_, _| {})
     }
 
     #[inline]
     fn matches_user_and_author_rules(&self) -> bool {
         self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) == 0
     }
 }
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -19,17 +19,17 @@ use element_state::ElementState;
 use font_metrics::FontMetricsProvider;
 use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
 use properties::longhands::display::computed_value as display;
 use restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS, RestyleHint};
 use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_SMIL};
 use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode};
 use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
 use selectors::bloom::BloomFilter;
-use selectors::matching::{ElementSelectorFlags, StyleRelations};
+use selectors::matching::{ElementSelectorFlags, MatchingContext, StyleRelations};
 use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS;
 use shared_lock::StylesheetGuards;
 use sink::ForgetfulSink;
 use stylearc::Arc;
 use stylist::ApplicableDeclarationList;
 
 /// The way a style should be inherited.
 enum InheritMode {
@@ -890,32 +890,34 @@ pub trait MatchMethods : TElement {
     /// Performs selector matching and property cascading on an element and its
     /// eager pseudos.
     fn match_and_cascade(&self,
                          context: &mut StyleContext<Self>,
                          data: &mut ElementData,
                          sharing: StyleSharingBehavior)
     {
         // Perform selector matching for the primary style.
-        let mut primary_relations = StyleRelations::empty();
-        let _rule_node_changed = self.match_primary(context, data, &mut primary_relations);
+        let mut primary_matching_context = MatchingContext::default();
+        let _rule_node_changed = self.match_primary(context,
+                                                    data,
+                                                    &mut primary_matching_context);
 
         // Cascade properties and compute primary values.
         self.cascade_primary(context, data);
 
         // Match and cascade eager pseudo-elements.
         if !data.styles().is_display_none() {
             let _pseudo_rule_nodes_changed =
                 self.match_pseudos(context, data);
             self.cascade_pseudos(context, data);
         }
 
         // If we have any pseudo elements, indicate so in the primary StyleRelations.
         if !data.styles().pseudos.is_empty() {
-            primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
+            primary_matching_context.relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
         }
 
         // If the style is shareable, add it to the LRU cache.
         if sharing == StyleSharingBehavior::Allow {
             // If we previously tried to match this element against the cache,
             // the revalidation match results will already be cached. Otherwise
             // we'll have None, and compute them later on-demand.
             //
@@ -925,17 +927,17 @@ pub trait MatchMethods : TElement {
                                                     .current_element_info
                                                     .as_mut().unwrap()
                                                     .revalidation_match_results
                                                     .take();
             context.thread_local
                    .style_sharing_candidate_cache
                    .insert_if_possible(self,
                                        data.styles().primary.values(),
-                                       primary_relations,
+                                       primary_matching_context.relations,
                                        revalidation_match_results);
         }
     }
 
     /// Performs the cascade, without matching.
     fn cascade_primary_and_pseudos(&self,
                                    context: &mut StyleContext<Self>,
                                    mut data: &mut ElementData)
@@ -945,17 +947,17 @@ pub trait MatchMethods : TElement {
     }
 
     /// Runs selector matching to (re)compute the primary rule node for this element.
     ///
     /// Returns whether the primary rule node changed.
     fn match_primary(&self,
                      context: &mut StyleContext<Self>,
                      data: &mut ElementData,
-                     relations: &mut StyleRelations)
+                     matching_context: &mut MatchingContext)
                      -> bool
     {
         let implemented_pseudo = self.implemented_pseudo_element();
         if let Some(ref pseudo) = implemented_pseudo {
             if pseudo.is_eager() {
                 // If it's an eager element-backed pseudo, just grab the matched
                 // rules from the parent, and update animations.
                 let parent = self.parent_element().unwrap();
@@ -1016,24 +1018,25 @@ pub trait MatchMethods : TElement {
         };
 
         let pseudo_and_state = match implemented_pseudo {
             Some(ref pseudo) => Some((pseudo, self.get_state())),
             None => None,
         };
 
         // Compute the primary rule node.
-        *relations = stylist.push_applicable_declarations(&selector_matching_target,
-                                                          Some(bloom),
-                                                          style_attribute,
-                                                          smil_override,
-                                                          animation_rules,
-                                                          pseudo_and_state,
-                                                          &mut applicable_declarations,
-                                                          &mut set_selector_flags);
+        stylist.push_applicable_declarations(&selector_matching_target,
+                                             Some(bloom),
+                                             style_attribute,
+                                             smil_override,
+                                             animation_rules,
+                                             pseudo_and_state,
+                                             &mut applicable_declarations,
+                                             matching_context,
+                                             &mut set_selector_flags);
 
         let primary_rule_node =
             compute_rule_node::<Self>(&stylist.rule_tree,
                                       &mut applicable_declarations,
                                       &context.shared.guards);
 
         return data.set_primary_rules(primary_rule_node);
     }
@@ -1077,16 +1080,17 @@ pub trait MatchMethods : TElement {
             // traversing them.
             stylist.push_applicable_declarations(self,
                                                  Some(bloom_filter),
                                                  None,
                                                  None,
                                                  AnimationRules(None, None),
                                                  Some((&pseudo, ElementState::empty())),
                                                  &mut applicable_declarations,
+                                                 &mut MatchingContext::default(),
                                                  &mut set_selector_flags);
 
             if !applicable_declarations.is_empty() {
                 let new_rules =
                     compute_rule_node::<Self>(rule_tree,
                                               &mut applicable_declarations,
                                               &guards);
                 if pseudos.has(&pseudo) {
--- a/servo/components/style/restyle_hints.rs
+++ b/servo/components/style/restyle_hints.rs
@@ -11,17 +11,17 @@ use dom::TElement;
 use element_state::*;
 use fnv::FnvHashMap;
 #[cfg(feature = "gecko")]
 use gecko_bindings::structs::nsRestyleHint;
 #[cfg(feature = "servo")]
 use heapsize::HeapSizeOf;
 use selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap};
 use selectors::{Element, MatchAttr};
-use selectors::matching::{ElementSelectorFlags, StyleRelations};
+use selectors::matching::{ElementSelectorFlags, MatchingContext};
 use selectors::matching::matches_selector;
 use selectors::parser::{AttrSelector, Combinator, Component, Selector};
 use selectors::parser::{SelectorInner, SelectorMethods};
 use selectors::visitor::SelectorVisitor;
 use smallvec::SmallVec;
 use std::borrow::Borrow;
 use std::cell::Cell;
 use std::clone::Clone;
@@ -343,29 +343,29 @@ fn dir_selector_to_state(s: &[u16]) -> E
     }
 }
 
 impl<'a, E> Element for ElementWrapper<'a, E>
     where E: TElement,
 {
     fn match_non_ts_pseudo_class<F>(&self,
                                     pseudo_class: &NonTSPseudoClass,
-                                    relations: &mut StyleRelations,
+                                    context: &mut MatchingContext,
                                     _setter: &mut F)
                                     -> bool
         where F: FnMut(&Self, ElementSelectorFlags),
     {
         // :moz-any is quite special, because we need to keep matching as a
         // snapshot.
         #[cfg(feature = "gecko")]
         {
             use selectors::matching::matches_complex_selector;
             if let NonTSPseudoClass::MozAny(ref selectors) = *pseudo_class {
                 return selectors.iter().any(|s| {
-                    matches_complex_selector(s, self, relations, _setter)
+                    matches_complex_selector(s, self, context, _setter)
                 })
             }
         }
 
         // :dir needs special handling.  It's implemented in terms of state
         // flags, but which state flag it maps to depends on the argument to
         // :dir.  That means we can't just add its state flags to the
         // NonTSPseudoClass, because if we added all of them there, and tested
@@ -388,24 +388,24 @@ impl<'a, E> Element for ElementWrapper<'
                 };
                 return state.contains(selector_flag);
             }
         }
 
         let flag = pseudo_class.state_flag();
         if flag.is_empty() {
             return self.element.match_non_ts_pseudo_class(pseudo_class,
-                                                          relations,
+                                                          context,
                                                           &mut |_, _| {})
         }
         match self.snapshot().and_then(|s| s.state()) {
             Some(snapshot_state) => snapshot_state.intersects(flag),
             None => {
                 self.element.match_non_ts_pseudo_class(pseudo_class,
-                                                       relations,
+                                                       context,
                                                        &mut |_, _| {})
             }
         }
     }
 
     fn parent_element(&self) -> Option<Self> {
         self.element.parent_element()
             .map(|e| ElementWrapper::new(e, self.snapshot_map))
@@ -775,17 +775,17 @@ impl DependencySet {
         // If that ever changes, we'd need to share more code with
         // `compute_element_hint`.
         let mut hint = RestyleHint::empty();
         map.lookup(selector_matching_target, &mut |dep| {
             // If the selector didn't match before, it either doesn't match now
             // either (or it doesn't matter because our parent posted a restyle
             // for us above).
             if !matches_selector(&dep.selector.inner, &selector_matching_target,
-                                 None, &mut StyleRelations::empty(),
+                                 None, &mut MatchingContext::default(),
                                  &mut |_, _| {}) {
                 return true;
             }
 
             let pseudo_selector = dep.selector.pseudo_element.as_ref().unwrap();
             debug_assert!(!pseudo_selector.state().is_empty());
 
             if pseudo_selector.state().intersects(pseudo_state_changes) {
@@ -852,21 +852,21 @@ impl DependencySet {
                 return true;
             }
 
             // We can ignore the selector flags, since they would have already
             // been set during original matching for any element that might
             // change its matching behavior here.
             let matched_then =
                 matches_selector(&dep.selector, &snapshot_el, None,
-                                 &mut StyleRelations::empty(),
+                                 &mut MatchingContext::default(),
                                  &mut |_, _| {});
             let matches_now =
                 matches_selector(&dep.selector, el, None,
-                                 &mut StyleRelations::empty(),
+                                 &mut MatchingContext::default(),
                                  &mut |_, _| {});
             if matched_then != matches_now {
                 hint.insert(dep.hint);
             }
 
             !hint.is_all()
         });
 
--- a/servo/components/style/servo/selector_parser.rs
+++ b/servo/components/style/servo/selector_parser.rs
@@ -10,17 +10,17 @@ use {Atom, Prefix, Namespace, LocalName}
 use attr::{AttrIdentifier, AttrValue};
 use cssparser::{Parser as CssParser, ToCss, serialize_identifier};
 use dom::{OpaqueNode, TElement, TNode};
 use element_state::ElementState;
 use fnv::FnvHashMap;
 use restyle_hints::ElementSnapshot;
 use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
 use selectors::{Element, MatchAttrGeneric};
-use selectors::matching::StyleRelations;
+use selectors::matching::MatchingContext;
 use selectors::parser::{AttrSelector, SelectorMethods};
 use selectors::visitor::SelectorVisitor;
 use std::borrow::Cow;
 use std::fmt;
 use std::fmt::Debug;
 use std::mem;
 use std::ops::{Deref, DerefMut};
 
@@ -575,17 +575,17 @@ impl MatchAttrGeneric for ServoElementSn
             NamespaceConstraint::Any => self.get_attr_ignore_ns(local_name),
         }.map_or(false, |v| test(v))
     }
 }
 
 impl<E: Element<Impl=SelectorImpl> + Debug> ElementExt for E {
     fn is_link(&self) -> bool {
         self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
-                                       &mut StyleRelations::empty(),
+                                       &mut MatchingContext::default(),
                                        &mut |_, _| {})
     }
 
     #[inline]
     fn matches_user_and_author_rules(&self) -> bool {
         true
     }
 }
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -24,17 +24,17 @@ use properties::{self, CascadeFlags, Com
 use properties::INHERIT_ALL;
 use properties::PropertyDeclarationBlock;
 use restyle_hints::{RestyleHint, DependencySet};
 use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
 use selector_parser::{SelectorImpl, PseudoElement, SnapshotMap};
 use selectors::Element;
 use selectors::bloom::BloomFilter;
 use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS};
-use selectors::matching::{ElementSelectorFlags, StyleRelations, matches_selector};
+use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext};
 use selectors::parser::{AttrSelector, Combinator, Component, Selector, SelectorInner, SelectorIter};
 use selectors::parser::{SelectorMethods, LocalName as LocalNameSelector};
 use selectors::visitor::SelectorVisitor;
 use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
 use sink::Push;
 use smallvec::{SmallVec, VecLike};
 use std::borrow::Borrow;
 use std::collections::HashMap;
@@ -702,25 +702,25 @@ impl Stylist {
             let parent_flags = flags.for_parent();
             if !parent_flags.is_empty() {
                 if let Some(p) = element.parent_element() {
                     unsafe { p.set_selector_flags(parent_flags); }
                 }
             }
         };
 
-
         let mut declarations = ApplicableDeclarationList::new();
         self.push_applicable_declarations(element,
                                           None,
                                           None,
                                           None,
                                           AnimationRules(None, None),
                                           Some((pseudo, pseudo_state)),
                                           &mut declarations,
+                                          &mut MatchingContext::default(),
                                           &mut set_selector_flags);
         if declarations.is_empty() {
             return None
         }
 
         let rule_node =
             self.rule_tree.insert_ordered_rules_with_important(
                 declarations.into_iter().map(|a| (a.source, a.level)),
@@ -829,29 +829,29 @@ impl Stylist {
         // mode info in the `SharedLayoutContext`.
         self.quirks_mode = quirks_mode;
     }
 
     /// Returns the applicable CSS declarations for the given element.
     ///
     /// This corresponds to `ElementRuleCollector` in WebKit.
     ///
-    /// The returned `StyleRelations` indicate hints about which kind of rules
-    /// have matched.
+    /// The `StyleRelations` recorded in `MatchingContext` indicate hints about
+    /// which kind of rules have matched.
     pub fn push_applicable_declarations<E, V, F>(
                                         &self,
                                         element: &E,
                                         parent_bf: Option<&BloomFilter>,
                                         style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
                                         smil_override: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
                                         animation_rules: AnimationRules,
                                         pseudo_element: Option<(&PseudoElement, ElementState)>,
                                         applicable_declarations: &mut V,
+                                        context: &mut MatchingContext,
                                         flags_setter: &mut F)
-                                        -> StyleRelations
         where E: TElement,
               V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>,
               F: FnMut(&E, ElementSelectorFlags),
     {
         debug_assert!(!self.is_device_dirty);
         // Gecko definitely has pseudo-elements with style attributes, like
         // ::-moz-color-swatch.
         debug_assert!(cfg!(feature = "gecko") ||
@@ -859,118 +859,114 @@ impl Stylist {
                       "Style attributes do not apply to pseudo-elements");
         debug_assert!(pseudo_element.as_ref().map_or(true, |p| !p.0.is_precomputed()));
 
         let map = match pseudo_element {
             Some((ref pseudo, _)) => self.pseudos_map.get(pseudo).unwrap(),
             None => &self.element_map,
         };
 
-        let mut relations = StyleRelations::empty();
-
         debug!("Determining if style is shareable: pseudo: {}",
                pseudo_element.is_some());
 
         // Step 1: Normal user-agent rules.
         map.user_agent.get_all_matching_rules(element,
                                               pseudo_element,
                                               parent_bf,
                                               applicable_declarations,
-                                              &mut relations,
+                                              context,
                                               flags_setter,
                                               CascadeLevel::UANormal);
-        debug!("UA normal: {:?}", relations);
+        debug!("UA normal: {:?}", context.relations);
 
         if pseudo_element.is_none() {
             // Step 2: Presentational hints.
             let length_before_preshints = applicable_declarations.len();
             element.synthesize_presentational_hints_for_legacy_attributes(applicable_declarations);
             if applicable_declarations.len() != length_before_preshints {
                 if cfg!(debug_assertions) {
                     for declaration in &applicable_declarations[length_before_preshints..] {
                         assert_eq!(declaration.level, CascadeLevel::PresHints);
                     }
                 }
                 // Never share style for elements with preshints
-                relations |= AFFECTED_BY_PRESENTATIONAL_HINTS;
+                context.relations |= AFFECTED_BY_PRESENTATIONAL_HINTS;
             }
-            debug!("preshints: {:?}", relations);
+            debug!("preshints: {:?}", context.relations);
         }
 
         if element.matches_user_and_author_rules() {
             // Step 3: User and author normal rules.
             map.user.get_all_matching_rules(element,
                                             pseudo_element,
                                             parent_bf,
                                             applicable_declarations,
-                                            &mut relations,
+                                            context,
                                             flags_setter,
                                             CascadeLevel::UserNormal);
-            debug!("user normal: {:?}", relations);
+            debug!("user normal: {:?}", context.relations);
             map.author.get_all_matching_rules(element,
                                               pseudo_element,
                                               parent_bf,
                                               applicable_declarations,
-                                              &mut relations,
+                                              context,
                                               flags_setter,
                                               CascadeLevel::AuthorNormal);
-            debug!("author normal: {:?}", relations);
+            debug!("author normal: {:?}", context.relations);
 
             // Step 4: Normal style attributes.
             if let Some(sa) = style_attribute {
-                relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
+                context.relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
                 Push::push(
                     applicable_declarations,
                     ApplicableDeclarationBlock::from_declarations(sa.clone(),
                                                                   CascadeLevel::StyleAttributeNormal));
             }
 
-            debug!("style attr: {:?}", relations);
+            debug!("style attr: {:?}", context.relations);
 
             // Step 5: SMIL override.
             // Declarations from SVG SMIL animation elements.
             if let Some(so) = smil_override {
                 Push::push(
                     applicable_declarations,
                     ApplicableDeclarationBlock::from_declarations(so.clone(),
                                                                   CascadeLevel::SMILOverride));
             }
-            debug!("SMIL: {:?}", relations);
+            debug!("SMIL: {:?}", context.relations);
 
             // Step 6: Animations.
             // The animations sheet (CSS animations, script-generated animations,
             // and CSS transitions that are no longer tied to CSS markup)
             if let Some(anim) = animation_rules.0 {
                 Push::push(
                     applicable_declarations,
                     ApplicableDeclarationBlock::from_declarations(anim,
                                                                   CascadeLevel::Animations));
             }
-            debug!("animation: {:?}", relations);
+            debug!("animation: {:?}", context.relations);
         } else {
             debug!("skipping non-agent rules");
         }
 
         //
         // Steps 7-10 correspond to !important rules, and are handled during
         // rule tree insertion.
         //
 
         // Step 11: Transitions.
         // The transitions sheet (CSS transitions that are tied to CSS markup)
         if let Some(anim) = animation_rules.1 {
             Push::push(
                 applicable_declarations,
                 ApplicableDeclarationBlock::from_declarations(anim, CascadeLevel::Transitions));
         }
-        debug!("transition: {:?}", relations);
+        debug!("transition: {:?}", context.relations);
 
-        debug!("push_applicable_declarations: shareable: {:?}", relations);
-
-        relations
+        debug!("push_applicable_declarations: shareable: {:?}", context.relations);
     }
 
     /// Return whether the device is dirty, that is, whether the screen size or
     /// media type have changed (for now).
     #[inline]
     pub fn is_device_dirty(&self) -> bool {
         self.is_device_dirty
     }
@@ -992,30 +988,27 @@ impl Stylist {
     pub fn match_revalidation_selectors<E, F>(&self,
                                               element: &E,
                                               bloom: &BloomFilter,
                                               flags_setter: &mut F)
                                               -> BitVec
         where E: TElement,
               F: FnMut(&E, ElementSelectorFlags),
     {
-        use selectors::matching::StyleRelations;
-        use selectors::matching::matches_selector;
-
         // Note that, by the time we're revalidating, we're guaranteed that the
         // candidate and the entry have the same id, classes, and local name.
         // This means we're guaranteed to get the same rulehash buckets for all
         // the lookups, which means that the bitvecs are comparable. We verify
         // this in the caller by asserting that the bitvecs are same-length.
         let mut results = BitVec::new();
         self.selectors_for_cache_revalidation.lookup(*element, &mut |selector| {
             results.push(matches_selector(selector,
                                           element,
                                           Some(bloom),
-                                          &mut StyleRelations::empty(),
+                                          &mut MatchingContext::default(),
                                           flags_setter));
             true
         });
 
         results
     }
 
     /// Given an element, and a snapshot table that represents a previous state
@@ -1288,17 +1281,17 @@ impl SelectorMap<Rule> {
     ///
     /// Extract matching rules as per element's ID, classes, tag name, etc..
     /// Sort the Rules at the end to maintain cascading order.
     pub fn get_all_matching_rules<E, V, F>(&self,
                                            element: &E,
                                            pseudo_element: Option<(&PseudoElement, ElementState)>,
                                            parent_bf: Option<&BloomFilter>,
                                            matching_rules_list: &mut V,
-                                           relations: &mut StyleRelations,
+                                           context: &mut MatchingContext,
                                            flags_setter: &mut F,
                                            cascade_level: CascadeLevel)
         where E: Element<Impl=SelectorImpl>,
               V: VecLike<ApplicableDeclarationBlock>,
               F: FnMut(&E, ElementSelectorFlags),
     {
         if self.is_empty() {
             return
@@ -1308,49 +1301,49 @@ impl SelectorMap<Rule> {
         let init_len = matching_rules_list.len();
         if let Some(id) = element.get_id() {
             SelectorMap::get_matching_rules_from_hash(element,
                                                       pseudo_element,
                                                       parent_bf,
                                                       &self.id_hash,
                                                       &id,
                                                       matching_rules_list,
-                                                      relations,
+                                                      context,
                                                       flags_setter,
                                                       cascade_level)
         }
 
         element.each_class(|class| {
             SelectorMap::get_matching_rules_from_hash(element,
                                                       pseudo_element,
                                                       parent_bf,
                                                       &self.class_hash,
                                                       class,
                                                       matching_rules_list,
-                                                      relations,
+                                                      context,
                                                       flags_setter,
                                                       cascade_level);
         });
 
         SelectorMap::get_matching_rules_from_hash(element,
                                                   pseudo_element,
                                                   parent_bf,
                                                   &self.local_name_hash,
                                                   element.get_local_name(),
                                                   matching_rules_list,
-                                                  relations,
+                                                  context,
                                                   flags_setter,
                                                   cascade_level);
 
         SelectorMap::get_matching_rules(element,
                                         pseudo_element,
                                         parent_bf,
                                         &self.other,
                                         matching_rules_list,
-                                        relations,
+                                        context,
                                         flags_setter,
                                         cascade_level);
 
         // Sort only the rules we just added.
         sort_by_key(&mut matching_rules_list[init_len..],
                     |block| (block.specificity, block.source_order));
     }
 
@@ -1379,44 +1372,44 @@ impl SelectorMap<Rule> {
 
     fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>(
         element: &E,
         pseudo_element: Option<(&PseudoElement, ElementState)>,
         parent_bf: Option<&BloomFilter>,
         hash: &FnvHashMap<Str, Vec<Rule>>,
         key: &BorrowedStr,
         matching_rules: &mut Vector,
-        relations: &mut StyleRelations,
+        context: &mut MatchingContext,
         flags_setter: &mut F,
         cascade_level: CascadeLevel)
         where E: Element<Impl=SelectorImpl>,
               Str: Borrow<BorrowedStr> + Eq + Hash,
               BorrowedStr: Eq + Hash,
               Vector: VecLike<ApplicableDeclarationBlock>,
               F: FnMut(&E, ElementSelectorFlags),
     {
         if let Some(rules) = hash.get(key) {
             SelectorMap::get_matching_rules(element,
                                             pseudo_element,
                                             parent_bf,
                                             rules,
                                             matching_rules,
-                                            relations,
+                                            context,
                                             flags_setter,
                                             cascade_level)
         }
     }
 
     /// Adds rules in `rules` that match `element` to the `matching_rules` list.
     fn get_matching_rules<E, V, F>(element: &E,
                                    pseudo_element: Option<(&PseudoElement, ElementState)>,
                                    parent_bf: Option<&BloomFilter>,
                                    rules: &[Rule],
                                    matching_rules: &mut V,
-                                   relations: &mut StyleRelations,
+                                   context: &mut MatchingContext,
                                    flags_setter: &mut F,
                                    cascade_level: CascadeLevel)
         where E: Element<Impl=SelectorImpl>,
               V: VecLike<ApplicableDeclarationBlock>,
               F: FnMut(&E, ElementSelectorFlags),
     {
         for rule in rules.iter() {
             debug_assert_eq!(rule.selector.pseudo_element.is_some(),
@@ -1438,17 +1431,17 @@ impl SelectorMap<Rule> {
                 if !state.is_empty() && !pseudo_state.contains(state) {
                     continue;
                 }
             }
 
             if matches_selector(&rule.selector.inner,
                                 element,
                                 parent_bf,
-                                relations,
+                                context,
                                 flags_setter) {
                 matching_rules.push(
                     rule.to_applicable_declaration_block(cascade_level));
             }
         }
     }
 }