servo: Merge #16077 - Bug 1345950: stylo: Fix slow selector flags. r=bholley (from emilio:slow-flags); r=bholley
authorEmilio Cobos Álvarez <emilio@crisal.io>
Wed, 22 Mar 2017 03:23:51 -0700
changeset 399328 adc2959fab79cd2c85ec730e7db6217e2a450bfd
parent 399327 20318a73395e89d3b2023735ec2bd9782e62d075
child 399329 8db97122e90e0d1af59da402c814ea1fdcf4dc4e
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley, bholley
bugs1345950
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 #16077 - Bug 1345950: stylo: Fix slow selector flags. r=bholley (from emilio:slow-flags); r=bholley Source-Repo: https://github.com/servo/servo Source-Revision: 545f8744126157d273158c5404d07fb09621d3ea
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
@@ -2411,20 +2411,23 @@ impl<'a> ::selectors::Element for Root<E
     fn get_local_name(&self) -> &LocalName {
         self.local_name()
     }
 
     fn get_namespace(&self) -> &Namespace {
         self.namespace()
     }
 
-    fn match_non_ts_pseudo_class(&self,
-                                 pseudo_class: &NonTSPseudoClass,
-                                 _: &mut StyleRelations,
-                                 _: &mut ElementSelectorFlags) -> bool {
+    fn match_non_ts_pseudo_class<F>(&self,
+                                    pseudo_class: &NonTSPseudoClass,
+                                    _: &mut StyleRelations,
+                                    _: &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(),
             NonTSPseudoClass::Visited => false,
 
             NonTSPseudoClass::ServoNonZeroBorder => {
                 match self.downcast::<HTMLTableElement>() {
--- a/servo/components/script/layout_wrapper.rs
+++ b/servo/components/script/layout_wrapper.rs
@@ -610,20 +610,23 @@ impl<'le> ::selectors::Element for Servo
         self.element.local_name()
     }
 
     #[inline]
     fn get_namespace(&self) -> &Namespace {
         self.element.namespace()
     }
 
-    fn match_non_ts_pseudo_class(&self,
-                                 pseudo_class: &NonTSPseudoClass,
-                                 _: &mut StyleRelations,
-                                 _: &mut ElementSelectorFlags) -> bool {
+    fn match_non_ts_pseudo_class<F>(&self,
+                                    pseudo_class: &NonTSPseudoClass,
+                                    _: &mut StyleRelations,
+                                    _: &mut F)
+                                    -> bool
+        where F: FnMut(&Self, ElementSelectorFlags),
+    {
         match *pseudo_class {
             // https://github.com/servo/servo/issues/8718
             NonTSPseudoClass::Link |
             NonTSPseudoClass::AnyLink => unsafe {
                 match self.as_node().script_type_id() {
                     // https://html.spec.whatwg.org/multipage/#selector-link
                     NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) |
                     NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
@@ -1109,20 +1112,23 @@ impl<'le> ::selectors::Element for Servo
         self.element.get_local_name()
     }
 
     #[inline]
     fn get_namespace(&self) -> &Namespace {
         self.element.get_namespace()
     }
 
-    fn match_non_ts_pseudo_class(&self,
-                                 _: &NonTSPseudoClass,
-                                 _: &mut StyleRelations,
-                                 _: &mut ElementSelectorFlags) -> bool {
+    fn match_non_ts_pseudo_class<F>(&self,
+                                    _: &NonTSPseudoClass,
+                                    _: &mut StyleRelations,
+                                    _: &mut F)
+                                    -> bool
+        where F: FnMut(&Self, ElementSelectorFlags),
+    {
         // NB: This could maybe be implemented
         warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called");
         false
     }
 
     fn get_id(&self) -> Option<Atom> {
         debug!("ServoThreadSafeLayoutElement::get_id called");
         None
--- a/servo/components/selectors/matching.rs
+++ b/servo/components/selectors/matching.rs
@@ -102,18 +102,21 @@ impl ElementSelectorFlags {
 pub fn matches<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_complex_selector(&*selector.complex_selector, element, parent_bf,
-                                 &mut StyleRelations::empty(), &mut ElementSelectorFlags::empty())
+        matches_complex_selector(&*selector.complex_selector,
+                                 element,
+                                 parent_bf,
+                                 &mut StyleRelations::empty(),
+                                 &mut |_, _| {})
     })
 }
 
 fn may_match<E>(mut selector: &ComplexSelector<E::Impl>,
                 bf: &BloomFilter)
                 -> bool
     where E: Element,
 {
@@ -157,31 +160,35 @@ fn may_match<E>(mut selector: &ComplexSe
         }
     }
 
     // If we haven't proven otherwise, it may match.
     true
 }
 
 /// Determines whether the given element matches the given complex selector.
-pub fn matches_complex_selector<E>(selector: &ComplexSelector<E::Impl>,
-                                   element: &E,
-                                   parent_bf: Option<&BloomFilter>,
-                                   relations: &mut StyleRelations,
-                                   flags: &mut ElementSelectorFlags)
-                                   -> bool
-    where E: Element
+pub fn matches_complex_selector<E, F>(selector: &ComplexSelector<E::Impl>,
+                                      element: &E,
+                                      parent_bf: Option<&BloomFilter>,
+                                      relations: &mut StyleRelations,
+                                      flags_setter: &mut F)
+                                      -> bool
+    where E: Element,
+          F: FnMut(&E, ElementSelectorFlags),
 {
     if let Some(filter) = parent_bf {
         if !may_match::<E>(selector, filter) {
             return false;
         }
     }
 
-    match matches_complex_selector_internal(selector, element, relations, flags) {
+    match matches_complex_selector_internal(selector,
+                                            element,
+                                            relations,
+                                            flags_setter) {
         SelectorMatchingResult::Matched => {
             match selector.next {
                 Some((_, Combinator::NextSibling)) |
                 Some((_, Combinator::LaterSibling)) => *relations |= AFFECTED_BY_SIBLINGS,
                 _ => {}
             }
 
             true
@@ -235,54 +242,60 @@ pub fn matches_complex_selector<E>(selec
 #[derive(PartialEq, Eq, Copy, Clone)]
 enum SelectorMatchingResult {
     Matched,
     NotMatchedAndRestartFromClosestLaterSibling,
     NotMatchedAndRestartFromClosestDescendant,
     NotMatchedGlobally,
 }
 
-fn matches_complex_selector_internal<E>(selector: &ComplexSelector<E::Impl>,
-                                        element: &E,
-                                        relations: &mut StyleRelations,
-                                        flags: &mut ElementSelectorFlags)
-                                        -> SelectorMatchingResult
-     where E: Element
+fn matches_complex_selector_internal<E, F>(selector: &ComplexSelector<E::Impl>,
+                                           element: &E,
+                                           relations: &mut StyleRelations,
+                                           flags_setter: &mut F)
+                                           -> SelectorMatchingResult
+     where E: Element,
+           F: FnMut(&E, ElementSelectorFlags),
 {
     let matches_all_simple_selectors = selector.compound_selector.iter().all(|simple| {
-        matches_simple_selector(simple, element, relations, flags)
+        matches_simple_selector(simple, element, relations, flags_setter)
     });
 
+    let siblings = selector.next.as_ref().map_or(false, |&(_, combinator)| {
+        matches!(combinator, Combinator::NextSibling | Combinator::LaterSibling)
+    });
+
+    if siblings {
+        flags_setter(element, HAS_SLOW_SELECTOR_LATER_SIBLINGS);
+    }
+
     if !matches_all_simple_selectors {
         return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling;
     }
 
     match selector.next {
         None => SelectorMatchingResult::Matched,
         Some((ref next_selector, combinator)) => {
-            let (siblings, candidate_not_found) = match combinator {
-                Combinator::Child => (false, SelectorMatchingResult::NotMatchedGlobally),
-                Combinator::Descendant => (false, SelectorMatchingResult::NotMatchedGlobally),
-                Combinator::NextSibling => (true, SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant),
-                Combinator::LaterSibling => (true, SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant),
+            let (mut next_element, candidate_not_found) = if siblings {
+                (element.prev_sibling_element(),
+                 SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant)
+            } else {
+                (element.parent_element(),
+                 SelectorMatchingResult::NotMatchedGlobally)
             };
-            let mut next_element = if siblings {
-                element.prev_sibling_element()
-            } else {
-                element.parent_element()
-            };
+
             loop {
                 let element = match next_element {
                     None => return candidate_not_found,
                     Some(next_element) => next_element,
                 };
                 let result = matches_complex_selector_internal(&**next_selector,
                                                                &element,
                                                                relations,
-                                                               flags);
+                                                               flags_setter);
                 match (result, combinator) {
                     // Return the status immediately.
                     (SelectorMatchingResult::Matched, _) => return result,
                     (SelectorMatchingResult::NotMatchedGlobally, _) => return result,
 
                     // Upgrade the failure status to
                     // NotMatchedAndRestartFromClosestDescendant.
                     (_, Combinator::Child) => return SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant,
@@ -311,23 +324,24 @@ fn matches_complex_selector_internal<E>(
                 };
             }
         }
     }
 }
 
 /// Determines whether the given element matches the given single selector.
 #[inline]
-fn matches_simple_selector<E>(
+fn matches_simple_selector<E, F>(
         selector: &SimpleSelector<E::Impl>,
         element: &E,
         relations: &mut StyleRelations,
-        flags: &mut ElementSelectorFlags)
+        flags_setter: &mut F)
         -> bool
-    where E: Element
+    where E: Element,
+          F: FnMut(&E, ElementSelectorFlags),
 {
     macro_rules! relation_if {
         ($ex:expr, $flag:ident) => {
             if $ex {
                 *relations |= $flag;
                 true
             } else {
                 false
@@ -394,89 +408,96 @@ fn matches_simple_selector<E>(
         }
         SimpleSelector::AttrIncludesNeverMatch(..) |
         SimpleSelector::AttrPrefixNeverMatch(..) |
         SimpleSelector::AttrSubstringNeverMatch(..) |
         SimpleSelector::AttrSuffixNeverMatch(..) => {
             false
         }
         SimpleSelector::NonTSPseudoClass(ref pc) => {
-            relation_if!(element.match_non_ts_pseudo_class(pc, relations, flags),
+            relation_if!(element.match_non_ts_pseudo_class(pc, relations, flags_setter),
                          AFFECTED_BY_STATE)
         }
         SimpleSelector::FirstChild => {
-            relation_if!(matches_first_child(element, flags), AFFECTED_BY_CHILD_INDEX)
+            relation_if!(matches_first_child(element, flags_setter),
+                         AFFECTED_BY_CHILD_INDEX)
         }
         SimpleSelector::LastChild => {
-            relation_if!(matches_last_child(element, flags), AFFECTED_BY_CHILD_INDEX)
+            relation_if!(matches_last_child(element, flags_setter),
+                         AFFECTED_BY_CHILD_INDEX)
         }
         SimpleSelector::OnlyChild => {
-            relation_if!(matches_first_child(element, flags) &&
-                         matches_last_child(element, flags), AFFECTED_BY_CHILD_INDEX)
+            relation_if!(matches_first_child(element, flags_setter) &&
+                         matches_last_child(element, flags_setter),
+                         AFFECTED_BY_CHILD_INDEX)
         }
         SimpleSelector::Root => {
             // We never share styles with an element with no parent, so no point
             // in creating a new StyleRelation.
             element.is_root()
         }
         SimpleSelector::Empty => {
-            flags.insert(HAS_EMPTY_SELECTOR);
+            flags_setter(element, HAS_EMPTY_SELECTOR);
             relation_if!(element.is_empty(), AFFECTED_BY_EMPTY)
         }
         SimpleSelector::NthChild(a, b) => {
-            relation_if!(matches_generic_nth_child(element, a, b, false, false, flags),
+            relation_if!(matches_generic_nth_child(element, a, b, false, false, flags_setter),
                          AFFECTED_BY_CHILD_INDEX)
         }
         SimpleSelector::NthLastChild(a, b) => {
-            relation_if!(matches_generic_nth_child(element, a, b, false, true, flags),
+            relation_if!(matches_generic_nth_child(element, a, b, false, true, flags_setter),
                          AFFECTED_BY_CHILD_INDEX)
         }
         SimpleSelector::NthOfType(a, b) => {
-            relation_if!(matches_generic_nth_child(element, a, b, true, false, flags),
+            relation_if!(matches_generic_nth_child(element, a, b, true, false, flags_setter),
                          AFFECTED_BY_CHILD_INDEX)
         }
         SimpleSelector::NthLastOfType(a, b) => {
-            relation_if!(matches_generic_nth_child(element, a, b, true, true, flags),
+            relation_if!(matches_generic_nth_child(element, a, b, true, true, flags_setter),
                          AFFECTED_BY_CHILD_INDEX)
         }
         SimpleSelector::FirstOfType => {
-            relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags),
+            relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags_setter),
                          AFFECTED_BY_CHILD_INDEX)
         }
         SimpleSelector::LastOfType => {
-            relation_if!(matches_generic_nth_child(element, 0, 1, true, true, flags),
+            relation_if!(matches_generic_nth_child(element, 0, 1, true, true, flags_setter),
                          AFFECTED_BY_CHILD_INDEX)
         }
         SimpleSelector::OnlyOfType => {
-            relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags) &&
-                         matches_generic_nth_child(element, 0, 1, true, true, flags),
+            relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags_setter) &&
+                         matches_generic_nth_child(element, 0, 1, true, true, flags_setter),
                          AFFECTED_BY_CHILD_INDEX)
         }
         SimpleSelector::Negation(ref negated) => {
             !negated.iter().all(|s| {
-                match matches_complex_selector_internal(s, element, relations, flags) {
+                match matches_complex_selector_internal(s,
+                                                        element,
+                                                        relations,
+                                                        flags_setter) {
                     SelectorMatchingResult::Matched => true,
                     _ => false,
                 }
             })
         }
     }
 }
 
 #[inline]
-fn matches_generic_nth_child<E>(element: &E,
-                                a: i32,
-                                b: i32,
-                                is_of_type: bool,
-                                is_from_end: bool,
-                                flags: &mut ElementSelectorFlags)
-                                -> bool
-    where E: Element
+fn matches_generic_nth_child<E, F>(element: &E,
+                                   a: i32,
+                                   b: i32,
+                                   is_of_type: bool,
+                                   is_from_end: bool,
+                                   flags_setter: &mut F)
+                                   -> bool
+    where E: Element,
+          F: FnMut(&E, ElementSelectorFlags),
 {
-    flags.insert(if is_from_end {
+    flags_setter(element, if is_from_end {
         HAS_SLOW_SELECTOR
     } else {
         HAS_SLOW_SELECTOR_LATER_SIBLINGS
     });
 
     let mut index = 1;
     let mut next_sibling = if is_from_end {
         element.next_sibling_element()
@@ -509,20 +530,24 @@ fn matches_generic_nth_child<E>(element:
         b == index
     } else {
         (index - b) / a >= 0 &&
         (index - b) % a == 0
     }
 }
 
 #[inline]
-fn matches_first_child<E>(element: &E, flags: &mut ElementSelectorFlags)
-                          -> bool where E: Element {
-    flags.insert(HAS_EDGE_CHILD_SELECTOR);
+fn matches_first_child<E, F>(element: &E, flags_setter: &mut F) -> bool
+    where E: Element,
+          F: FnMut(&E, ElementSelectorFlags),
+{
+    flags_setter(element, HAS_EDGE_CHILD_SELECTOR);
     element.prev_sibling_element().is_none()
 }
 
 #[inline]
-fn matches_last_child<E>(element: &E, flags: &mut ElementSelectorFlags)
-                         -> bool where E: Element {
-    flags.insert(HAS_EDGE_CHILD_SELECTOR);
+fn matches_last_child<E, F>(element: &E, flags_setter: &mut F) -> bool
+    where E: Element,
+          F: FnMut(&E, ElementSelectorFlags),
+{
+    flags_setter(element, HAS_EDGE_CHILD_SELECTOR);
     element.next_sibling_element().is_none()
 }
--- a/servo/components/selectors/tree.rs
+++ b/servo/components/selectors/tree.rs
@@ -134,20 +134,21 @@ pub trait Element: MatchAttr + Sized {
 
     // Skips non-element nodes
     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(&self,
-                                 pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
-                                 relations: &mut StyleRelations,
-                                 flags: &mut ElementSelectorFlags) -> bool;
+    fn match_non_ts_pseudo_class<F>(&self,
+                                    pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
+                                    relations: &mut StyleRelations,
+                                    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`.
     ///
     /// That is, whether it does not contain any child element or any non-zero-length text node.
     /// See http://dev.w3.org/csswg/selectors-3/#empty-pseudo
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -658,20 +658,23 @@ impl<'le> ::selectors::Element for Gecko
     }
 
     fn get_namespace(&self) -> &WeakNamespace {
         unsafe {
             WeakNamespace::new(Gecko_Namespace(self.0))
         }
     }
 
-    fn match_non_ts_pseudo_class(&self,
-                                 pseudo_class: &NonTSPseudoClass,
-                                 relations: &mut StyleRelations,
-                                 flags: &mut ElementSelectorFlags) -> bool {
+    fn match_non_ts_pseudo_class<F>(&self,
+                                    pseudo_class: &NonTSPseudoClass,
+                                    relations: &mut StyleRelations,
+                                    flags_setter: &mut F)
+                                    -> bool
+        where F: FnMut(&Self, ElementSelectorFlags),
+    {
         match *pseudo_class {
             // https://github.com/servo/servo/issues/8718
             NonTSPseudoClass::AnyLink => unsafe { Gecko_IsLink(self.0) },
             NonTSPseudoClass::Link => unsafe { Gecko_IsUnvisitedLink(self.0) },
             NonTSPseudoClass::Visited => unsafe { Gecko_IsVisitedLink(self.0) },
             NonTSPseudoClass::Active |
             NonTSPseudoClass::Focus |
             NonTSPseudoClass::Hover |
@@ -699,31 +702,37 @@ impl<'le> ::selectors::Element for Gecko
                 !self.get_state().contains(pseudo_class.state_flag())
             }
 
             NonTSPseudoClass::MozTableBorderNonzero |
             NonTSPseudoClass::MozBrowserFrame => unsafe {
                 Gecko_MatchesElement(pseudo_class.to_gecko_pseudoclasstype().unwrap(), self.0)
             },
             NonTSPseudoClass::MozAny(ref sels) => {
-                sels.iter().any(|s| matches_complex_selector(s, self, None, relations, flags))
+                sels.iter().any(|s| {
+                    matches_complex_selector(s,
+                                             self,
+                                             None,
+                                             relations,
+                                             flags_setter)
+                })
             }
             NonTSPseudoClass::MozSystemMetric(ref s) |
             NonTSPseudoClass::MozLocaleDir(ref s) |
             NonTSPseudoClass::MozEmptyExceptChildrenWithLocalname(ref s) |
             NonTSPseudoClass::Dir(ref s) |
             NonTSPseudoClass::Lang(ref s) => {
                 use selectors::matching::HAS_SLOW_SELECTOR;
                 unsafe {
                     let mut set_slow_selector = false;
                     let matches = Gecko_MatchStringArgPseudo(self.0,
                                        pseudo_class.to_gecko_pseudoclasstype().unwrap(),
                                        s.as_ptr(), &mut set_slow_selector);
                     if set_slow_selector {
-                        *flags |= HAS_SLOW_SELECTOR;
+                        flags_setter(self, HAS_SLOW_SELECTOR);
                     }
                     matches
                 }
             }
         }
     }
 
     fn get_id(&self) -> Option<Atom> {
@@ -857,16 +866,16 @@ 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 ElementSelectorFlags::empty())
+                                       &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
@@ -13,17 +13,17 @@ use atomic_refcell::AtomicRefMut;
 use cache::{LRUCache, LRUCacheMutIterator};
 use cascade_info::CascadeInfo;
 use context::{SequentialTask, SharedStyleContext, StyleContext};
 use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
 use dom::{AnimationRules, SendElement, TElement, TNode};
 use properties::{CascadeFlags, ComputedValues, SHAREABLE, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
 use properties::longhands::display::computed_value as display;
 use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RestyleHint};
-use rule_tree::{CascadeLevel, StrongRuleNode};
+use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode};
 use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
 use selectors::MatchAttr;
 use selectors::bloom::BloomFilter;
 use selectors::matching::{ElementSelectorFlags, StyleRelations};
 use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS;
 use servo_config::opts;
 use sink::ForgetfulSink;
 use std::collections::hash_map::Entry;
@@ -745,22 +745,22 @@ trait PrivateMatchMethods: TElement {
                                               shared_context: &SharedStyleContext,
                                               candidate: &mut StyleSharingCandidate<Self>)
                                               -> Result<ComputedStyle, CacheMiss> {
         let candidate_element = *candidate.element;
         element_matches_candidate(self, candidate, &candidate_element, shared_context)
     }
 }
 
-fn compute_rule_node<E: TElement>(context: &StyleContext<E>,
+fn compute_rule_node<E: TElement>(rule_tree: &RuleTree,
                                   applicable_declarations: &mut Vec<ApplicableDeclarationBlock>)
                                   -> StrongRuleNode
 {
     let rules = applicable_declarations.drain(..).map(|d| (d.source, d.level));
-    let rule_node = context.shared.stylist.rule_tree.insert_ordered_rules(rules);
+    let rule_node = rule_tree.insert_ordered_rules(rules);
     rule_node
 }
 
 impl<E: TElement> PrivateMatchMethods for E {}
 
 /// The public API that elements expose for selector matching.
 pub trait MatchMethods : TElement {
     /// Runs selector matching to (re)compute rule nodes for this element.
@@ -770,30 +770,85 @@ pub trait MatchMethods : TElement {
                      -> MatchResults
     {
         let mut applicable_declarations =
             Vec::<ApplicableDeclarationBlock>::with_capacity(16);
 
         let stylist = &context.shared.stylist;
         let style_attribute = self.style_attribute();
         let animation_rules = self.get_animation_rules(None);
-        let mut flags = ElementSelectorFlags::empty();
         let mut rule_nodes_changed = false;
 
+        // TODO(emilio): This is somewhat inefficient, because of a variety of
+        // reasons:
+        //
+        //  * It doesn't coalesce flags.
+        //  * It doesn't look at flags already sent in a task for the main
+        //    thread to process.
+        //  * It doesn't take advantage of us knowing that the traversal is
+        //    sequential.
+        //
+        // I suspect (need to measure!) that we don't use to set flags on
+        // a lot of different elements, but we could end up posting the same
+        // flag over and over with this approach.
+        //
+        // If the number of elements is low, perhaps a small cache with the
+        // flags already sent would be appropriate.
+        //
+        // The sequential task business for this is kind of sad :(.
+        //
+        // Anyway, let's do the obvious thing for now.
+        let tasks = &mut context.thread_local.tasks;
+        let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
+            // Apply the selector flags.
+            let self_flags = flags.for_self();
+            if !self_flags.is_empty() {
+                if element == self {
+                    unsafe { element.set_selector_flags(self_flags); }
+                } else {
+                    if !element.has_selector_flags(self_flags) {
+                        let task =
+                            SequentialTask::set_selector_flags(element.clone(),
+                                                               self_flags);
+                        tasks.push(task);
+                    }
+                }
+            }
+            let parent_flags = flags.for_parent();
+            if !parent_flags.is_empty() {
+                if let Some(p) = element.parent_element() {
+                    // Avoid the overhead of the SequentialTask if the flags are
+                    // already set.
+                    if !p.has_selector_flags(parent_flags) {
+                        let task = SequentialTask::set_selector_flags(p, parent_flags);
+                        tasks.push(task);
+                    }
+                }
+            }
+        };
+
+        // Borrow the stuff we need here so the borrow checker doesn't get mad
+        // at us later in the closure.
+        let guards = &context.shared.guards;
+        let rule_tree = &context.shared.stylist.rule_tree;
+        let bloom_filter = context.thread_local.bloom_filter.filter();
+
         // Compute the primary rule node.
         let mut primary_relations =
             stylist.push_applicable_declarations(self,
-                                                 Some(context.thread_local.bloom_filter.filter()),
+                                                 Some(bloom_filter),
                                                  style_attribute,
                                                  animation_rules,
                                                  None,
-                                                 &context.shared.guards,
+                                                 guards,
                                                  &mut applicable_declarations,
-                                                 &mut flags);
-        let primary_rule_node = compute_rule_node(context, &mut applicable_declarations);
+                                                 &mut set_selector_flags);
+
+        let primary_rule_node =
+            compute_rule_node::<Self>(rule_tree, &mut applicable_declarations);
         if !data.has_styles() {
             data.set_styles(ElementStyles::new(ComputedStyle::new_partial(primary_rule_node)));
             rule_nodes_changed = true;
         } else if data.styles().primary.rules != primary_rule_node {
             data.styles_mut().primary.rules = primary_rule_node;
             rule_nodes_changed = true;
         }
 
@@ -803,25 +858,26 @@ pub trait MatchMethods : TElement {
             let mut per_pseudo = &mut data.styles_mut().pseudos;
             debug_assert!(applicable_declarations.is_empty());
             let pseudo_animation_rules = if <Self as MatchAttr>::Impl::pseudo_is_before_or_after(&pseudo) {
                 self.get_animation_rules(Some(&pseudo))
             } else {
                 AnimationRules(None, None)
             };
             stylist.push_applicable_declarations(self,
-                                                 Some(context.thread_local.bloom_filter.filter()),
+                                                 Some(bloom_filter),
                                                  None, pseudo_animation_rules,
                                                  Some(&pseudo),
-                                                 &context.shared.guards,
+                                                 &guards,
                                                  &mut applicable_declarations,
-                                                 &mut flags);
+                                                 &mut set_selector_flags);
 
             if !applicable_declarations.is_empty() {
-                let new_rules = compute_rule_node(context, &mut applicable_declarations);
+                let new_rules =
+                    compute_rule_node::<Self>(rule_tree, &mut applicable_declarations);
                 match per_pseudo.entry(pseudo) {
                     Entry::Occupied(mut e) => {
                         if e.get().rules != new_rules {
                             e.get_mut().rules = new_rules;
                             rule_nodes_changed = true;
                         }
                     },
                     Entry::Vacant(e) => {
@@ -843,32 +899,16 @@ pub trait MatchMethods : TElement {
             }
         }
 
         // If we have any pseudo elements, indicate so in the primary StyleRelations.
         if !data.styles().pseudos.is_empty() {
             primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
         }
 
-        // Apply the selector flags.
-        let self_flags = flags.for_self();
-        if !self_flags.is_empty() {
-            unsafe { self.set_selector_flags(self_flags); }
-        }
-        let parent_flags = flags.for_parent();
-        if !parent_flags.is_empty() {
-            if let Some(p) = self.parent_element() {
-                // Avoid the overhead of the SequentialTask if the flags are already set.
-                if !p.has_selector_flags(parent_flags) {
-                    let task = SequentialTask::set_selector_flags(p, parent_flags);
-                    context.thread_local.tasks.push(task);
-                }
-            }
-        }
-
         MatchResults {
             primary_relations: Some(primary_relations),
             rule_nodes_changed: rule_nodes_changed,
         }
     }
 
     /// Updates the rule nodes without re-running selector matching, using just
     /// the rule tree. Returns true if the rule nodes changed.
--- a/servo/components/style/restyle_hints.rs
+++ b/servo/components/style/restyle_hints.rs
@@ -264,27 +264,35 @@ impl<'a, E> MatchAttr for ElementWrapper
             _   => self.element.match_attr_suffix(attr, value)
         }
     }
 }
 
 impl<'a, E> Element for ElementWrapper<'a, E>
     where E: TElement,
 {
-    fn match_non_ts_pseudo_class(&self,
-                                 pseudo_class: &NonTSPseudoClass,
-                                 relations: &mut StyleRelations,
-                                 flags: &mut ElementSelectorFlags) -> bool {
+    fn match_non_ts_pseudo_class<F>(&self,
+                                    pseudo_class: &NonTSPseudoClass,
+                                    relations: &mut StyleRelations,
+                                    _: &mut F)
+                                    -> bool
+        where F: FnMut(&Self, ElementSelectorFlags),
+    {
         let flag = SelectorImpl::pseudo_class_state_flag(pseudo_class);
-        if flag == ElementState::empty() {
-            self.element.match_non_ts_pseudo_class(pseudo_class, relations, flags)
-        } else {
-            match self.snapshot.and_then(|s| s.state()) {
-                Some(snapshot_state) => snapshot_state.contains(flag),
-                _   => self.element.match_non_ts_pseudo_class(pseudo_class, relations, flags)
+        if flag.is_empty() {
+            return self.element.match_non_ts_pseudo_class(pseudo_class,
+                                                          relations,
+                                                          &mut |_, _| {})
+        }
+        match self.snapshot.and_then(|s| s.state()) {
+            Some(snapshot_state) => snapshot_state.contains(flag),
+            None => {
+                self.element.match_non_ts_pseudo_class(pseudo_class,
+                                                       relations,
+                                                       &mut |_, _| {})
             }
         }
     }
 
     fn parent_element(&self) -> Option<Self> {
         self.element.parent_element().map(ElementWrapper::new)
     }
 
@@ -576,21 +584,21 @@ impl DependencySet {
                           (attrs_changed && dep.sensitivities.attrs),
                           "Testing a known ineffective dependency?");
             if (attrs_changed || state_changes.intersects(dep.sensitivities.states)) && !hint.intersects(dep.hint) {
                 // 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_complex_selector(&dep.selector, snapshot, None,
                                              &mut StyleRelations::empty(),
-                                             &mut ElementSelectorFlags::empty());
+                                             &mut |_, _| {});
                 let matches_now =
                     matches_complex_selector(&dep.selector, element, None,
                                              &mut StyleRelations::empty(),
-                                             &mut ElementSelectorFlags::empty());
+                                             &mut |_, _| {});
                 if matched_then != matches_now {
                     hint.insert(dep.hint);
                 }
                 if hint.is_all() {
                     break;
                 }
             }
         }
--- a/servo/components/style/servo/selector_parser.rs
+++ b/servo/components/style/servo/selector_parser.rs
@@ -9,17 +9,17 @@
 use {Atom, Prefix, Namespace, LocalName};
 use attr::{AttrIdentifier, AttrValue};
 use cssparser::{Parser as CssParser, ToCss, serialize_identifier};
 use element_state::ElementState;
 use restyle_hints::ElementSnapshot;
 use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
 use selector_parser::{attr_equals_selector_is_shareable, attr_exists_selector_is_shareable};
 use selectors::{Element, MatchAttrGeneric};
-use selectors::matching::{ElementSelectorFlags, StyleRelations};
+use selectors::matching::StyleRelations;
 use selectors::parser::{AttrSelector, SelectorMethods};
 use std::borrow::Cow;
 use std::fmt;
 use std::fmt::Debug;
 
 /// A pseudo-element, both public and private.
 ///
 /// NB: If you add to this list, be sure to update `each_pseudo_element` too.
@@ -454,16 +454,16 @@ impl MatchAttrGeneric for ServoElementSn
         }.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 ElementSelectorFlags::empty())
+                                       &mut |_, _| {})
     }
 
     #[inline]
     fn matches_user_and_author_rules(&self) -> bool {
         true
     }
 }
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -393,25 +393,49 @@ impl Stylist {
     {
         debug_assert!(SelectorImpl::pseudo_element_cascade_type(pseudo).is_lazy());
         if self.pseudos_map.get(pseudo).is_none() {
             return None;
         }
 
         let mut declarations = vec![];
 
-        let mut flags = ElementSelectorFlags::empty();
+        // Apply the selector flags. We should be in sequential mode
+        // already, so we can directly apply the parent flags.
+        let mut set_selector_flags = |element: &E, flags: ElementSelectorFlags| {
+            if cfg!(feature = "servo") {
+                // Servo calls this function from the worker, but only for internal
+                // pseudos, so we should never generate selector flags here.
+                unreachable!("internal pseudo generated slow selector flags?");
+            }
+
+            // Gecko calls this from sequential mode, so we can directly apply
+            // the flags.
+            debug_assert!(thread_state::get() == thread_state::LAYOUT);
+            let self_flags = flags.for_self();
+            if !self_flags.is_empty() {
+                unsafe { element.set_selector_flags(self_flags); }
+            }
+            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); }
+                }
+            }
+        };
+
+
         self.push_applicable_declarations(element,
                                           None,
                                           None,
                                           AnimationRules(None, None),
                                           Some(pseudo),
                                           guards,
                                           &mut declarations,
-                                          &mut flags);
+                                          &mut set_selector_flags);
 
         let rule_node =
             self.rule_tree.insert_ordered_rules(
                 declarations.into_iter().map(|a| (a.source, a.level)));
 
         // Read the comment on `precomputed_values_for_pseudo` to see why it's
         // difficult to assert that display: contents nodes never arrive here
         // (tl;dr: It doesn't apply for replaced elements and such, but the
@@ -421,38 +445,16 @@ impl Stylist {
                                 &rule_node,
                                 guards,
                                 Some(&**parent),
                                 Some(&**parent),
                                 None,
                                 &StdoutErrorReporter,
                                 CascadeFlags::empty());
 
-        // Apply the selector flags. We should be in sequential mode already,
-        // so we can directly apply the parent flags.
-        if cfg!(feature = "servo") {
-            // Servo calls this function from the worker, but only for internal
-            // pseudos, so we should never generate selector flags here.
-            debug_assert!(flags.is_empty());
-        } else {
-            // Gecko calls this from sequential mode, so we can directly apply
-            // the flags.
-            debug_assert!(thread_state::get() == thread_state::LAYOUT);
-            let self_flags = flags.for_self();
-            if !self_flags.is_empty() {
-                unsafe { element.set_selector_flags(self_flags); }
-            }
-            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); }
-                }
-            }
-        }
-
         Some(ComputedStyle::new(rule_node, Arc::new(computed)))
     }
 
     /// Set a given device, which may change the styles that apply to the
     /// document.
     ///
     /// This means that we may need to rebuild style data even if the
     /// stylesheets haven't changed.
@@ -532,30 +534,32 @@ impl Stylist {
     }
 
     /// 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.
-    pub fn push_applicable_declarations<E, V>(
+    pub fn push_applicable_declarations<E, V, F>(
                                         &self,
                                         element: &E,
                                         parent_bf: Option<&BloomFilter>,
                                         style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
                                         animation_rules: AnimationRules,
                                         pseudo_element: Option<&PseudoElement>,
                                         guards: &StylesheetGuards,
                                         applicable_declarations: &mut V,
-                                        flags: &mut ElementSelectorFlags) -> StyleRelations
+                                        flags_setter: &mut F)
+                                        -> StyleRelations
         where E: TElement +
                  fmt::Debug +
                  PresentationalHintsSynthetizer,
-              V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>
+              V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>,
+              F: FnMut(&E, ElementSelectorFlags),
     {
         debug_assert!(!self.is_device_dirty);
         debug_assert!(style_attribute.is_none() || pseudo_element.is_none(),
                       "Style attributes do not apply to pseudo-elements");
         debug_assert!(pseudo_element.is_none() ||
                       !SelectorImpl::pseudo_element_cascade_type(pseudo_element.as_ref().unwrap())
                         .is_precomputed());
 
@@ -568,17 +572,17 @@ impl Stylist {
 
         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,
                                               parent_bf,
                                               guards.ua_or_user,
                                               applicable_declarations,
                                               &mut relations,
-                                              flags,
+                                              flags_setter,
                                               CascadeLevel::UANormal);
         debug!("UA normal: {:?}", relations);
 
         // 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) {
@@ -593,25 +597,25 @@ impl Stylist {
 
         if element.matches_user_and_author_rules() {
             // Step 3: User and author normal rules.
             map.user.get_all_matching_rules(element,
                                             parent_bf,
                                             guards.ua_or_user,
                                             applicable_declarations,
                                             &mut relations,
-                                            flags,
+                                            flags_setter,
                                             CascadeLevel::UserNormal);
             debug!("user normal: {:?}", relations);
             map.author.get_all_matching_rules(element,
                                               parent_bf,
                                               guards.author,
                                               applicable_declarations,
                                               &mut relations,
-                                              flags,
+                                              flags_setter,
                                               CascadeLevel::AuthorNormal);
             debug!("author normal: {:?}", relations);
 
             // Step 4: Normal style attributes.
             if let Some(sa) = style_attribute {
                 if sa.read_with(guards.author).any_normal() {
                     relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
                     Push::push(
@@ -636,17 +640,17 @@ impl Stylist {
             debug!("animation: {:?}", relations);
 
             // Step 6: Author-supplied `!important` rules.
             map.author.get_all_matching_rules(element,
                                               parent_bf,
                                               guards.author,
                                               applicable_declarations,
                                               &mut relations,
-                                              flags,
+                                              flags_setter,
                                               CascadeLevel::AuthorImportant);
 
             debug!("author important: {:?}", relations);
 
             // Step 7: `!important` style attributes.
             if let Some(sa) = style_attribute {
                 if sa.read_with(guards.author).any_important() {
                     relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
@@ -660,31 +664,31 @@ impl Stylist {
             debug!("style attr important: {:?}", relations);
 
             // Step 8: User `!important` rules.
             map.user.get_all_matching_rules(element,
                                             parent_bf,
                                             guards.ua_or_user,
                                             applicable_declarations,
                                             &mut relations,
-                                            flags,
+                                            flags_setter,
                                             CascadeLevel::UserImportant);
 
             debug!("user important: {:?}", relations);
         } else {
             debug!("skipping non-agent rules");
         }
 
         // Step 9: UA `!important` rules.
         map.user_agent.get_all_matching_rules(element,
                                               parent_bf,
                                               guards.ua_or_user,
                                               applicable_declarations,
                                               &mut relations,
-                                              flags,
+                                              flags_setter,
                                               CascadeLevel::UAImportant);
 
         debug!("UA important: {:?}", relations);
 
         // Step 10: Transitions.
         // The transitions sheet (CSS transitions that are tied to CSS markup)
         if let Some(anim) = animation_rules.1 {
             relations |= AFFECTED_BY_TRANSITIONS;
@@ -730,21 +734,21 @@ impl Stylist {
         // the same.
         //
         // TODO(emilio): Use the bloom filter, since they contain the element's
         // ancestor chain and it's correct for the candidate too.
         for ref selector in self.non_common_style_affecting_attributes_selectors.iter() {
             let element_matches =
                 matches_complex_selector(&selector.complex_selector, element,
                                          None, &mut StyleRelations::empty(),
-                                         &mut ElementSelectorFlags::empty());
+                                         &mut |_, _| {});
             let candidate_matches =
                 matches_complex_selector(&selector.complex_selector, candidate,
                                          None, &mut StyleRelations::empty(),
-                                         &mut ElementSelectorFlags::empty());
+                                         &mut |_, _| {});
 
             if element_matches != candidate_matches {
                 return false;
             }
         }
 
         true
     }
@@ -769,22 +773,22 @@ impl Stylist {
         // know what rules it matches.
         //
         // TODO(emilio): Use the bloom filter, since they contain the element's
         // ancestor chain and it's correct for the candidate too.
         for ref selector in self.sibling_affecting_selectors.iter() {
             let element_matches =
                 matches_complex_selector(&selector.complex_selector, element,
                                          None, &mut StyleRelations::empty(),
-                                         &mut ElementSelectorFlags::empty());
+                                         &mut |_, _| {});
 
             let candidate_matches =
                 matches_complex_selector(&selector.complex_selector, candidate,
                                          None, &mut StyleRelations::empty(),
-                                         &mut ElementSelectorFlags::empty());
+                                         &mut |_, _| {});
 
             if element_matches != candidate_matches {
                 debug!("match_same_sibling_affecting_rules: Failure due to {:?}",
                        selector.complex_selector);
                 return false;
             }
         }
 
@@ -907,79 +911,80 @@ impl SelectorMap {
             empty: true,
         }
     }
 
     /// Append to `rule_list` all Rules in `self` that match element.
     ///
     /// 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>(&self,
-                                        element: &E,
-                                        parent_bf: Option<&BloomFilter>,
-                                        guard: &SharedRwLockReadGuard,
-                                        matching_rules_list: &mut V,
-                                        relations: &mut StyleRelations,
-                                        flags: &mut ElementSelectorFlags,
-                                        cascade_level: CascadeLevel)
+    pub fn get_all_matching_rules<E, V, F>(&self,
+                                           element: &E,
+                                           parent_bf: Option<&BloomFilter>,
+                                           guard: &SharedRwLockReadGuard,
+                                           matching_rules_list: &mut V,
+                                           relations: &mut StyleRelations,
+                                           flags_setter: &mut F,
+                                           cascade_level: CascadeLevel)
         where E: Element<Impl=SelectorImpl>,
-              V: VecLike<ApplicableDeclarationBlock>
+              V: VecLike<ApplicableDeclarationBlock>,
+              F: FnMut(&E, ElementSelectorFlags),
     {
         if self.empty {
             return
         }
 
         // At the end, we're going to sort the rules that we added, so remember where we began.
         let init_len = matching_rules_list.len();
         if let Some(id) = element.get_id() {
             SelectorMap::get_matching_rules_from_hash(element,
                                                       parent_bf,
                                                       &self.id_hash,
                                                       &id,
                                                       guard,
                                                       matching_rules_list,
                                                       relations,
-                                                      flags,
+                                                      flags_setter,
                                                       cascade_level)
         }
 
         element.each_class(|class| {
             SelectorMap::get_matching_rules_from_hash(element,
                                                       parent_bf,
                                                       &self.class_hash,
                                                       class,
                                                       guard,
                                                       matching_rules_list,
                                                       relations,
-                                                      flags,
+                                                      flags_setter,
                                                       cascade_level);
         });
 
         let local_name_hash = if element.is_html_element_in_html_document() {
             &self.lower_local_name_hash
         } else {
             &self.local_name_hash
         };
         SelectorMap::get_matching_rules_from_hash(element,
                                                   parent_bf,
                                                   local_name_hash,
                                                   element.get_local_name(),
                                                   guard,
                                                   matching_rules_list,
                                                   relations,
-                                                  flags,
+                                                  flags_setter,
                                                   cascade_level);
 
         SelectorMap::get_matching_rules(element,
                                         parent_bf,
                                         &self.other_rules,
                                         guard,
                                         matching_rules_list,
                                         relations,
-                                        flags,
+                                        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));
     }
 
     /// Append to `rule_list` all universal Rules (rules with selector `*|*`) in
@@ -1022,66 +1027,68 @@ impl SelectorMap {
         sort_by_key(&mut matching_rules_list[0..normal_len],
                     |block| (block.specificity, block.source_order));
         sort_by_key(&mut matching_rules_list[normal_len..],
                     |block| (block.specificity, block.source_order));
 
         matching_rules_list
     }
 
-    fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector>(
+    fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>(
         element: &E,
         parent_bf: Option<&BloomFilter>,
         hash: &FnvHashMap<Str, Vec<Rule>>,
         key: &BorrowedStr,
         guard: &SharedRwLockReadGuard,
         matching_rules: &mut Vector,
         relations: &mut StyleRelations,
-        flags: &mut ElementSelectorFlags,
+        flags_setter: &mut F,
         cascade_level: CascadeLevel)
         where E: Element<Impl=SelectorImpl>,
               Str: Borrow<BorrowedStr> + Eq + Hash,
               BorrowedStr: Eq + Hash,
-              Vector: VecLike<ApplicableDeclarationBlock>
+              Vector: VecLike<ApplicableDeclarationBlock>,
+              F: FnMut(&E, ElementSelectorFlags),
     {
         if let Some(rules) = hash.get(key) {
             SelectorMap::get_matching_rules(element,
                                             parent_bf,
                                             rules,
                                             guard,
                                             matching_rules,
                                             relations,
-                                            flags,
+                                            flags_setter,
                                             cascade_level)
         }
     }
 
     /// Adds rules in `rules` that match `element` to the `matching_rules` list.
-    fn get_matching_rules<E, V>(element: &E,
-                                parent_bf: Option<&BloomFilter>,
-                                rules: &[Rule],
-                                guard: &SharedRwLockReadGuard,
-                                matching_rules: &mut V,
-                                relations: &mut StyleRelations,
-                                flags: &mut ElementSelectorFlags,
-                                cascade_level: CascadeLevel)
+    fn get_matching_rules<E, V, F>(element: &E,
+                                   parent_bf: Option<&BloomFilter>,
+                                   rules: &[Rule],
+                                   guard: &SharedRwLockReadGuard,
+                                   matching_rules: &mut V,
+                                   relations: &mut StyleRelations,
+                                   flags_setter: &mut F,
+                                   cascade_level: CascadeLevel)
         where E: Element<Impl=SelectorImpl>,
-              V: VecLike<ApplicableDeclarationBlock>
+              V: VecLike<ApplicableDeclarationBlock>,
+              F: FnMut(&E, ElementSelectorFlags),
     {
         for rule in rules.iter() {
             let style_rule = rule.style_rule.read_with(guard);
             let block = style_rule.block.read_with(guard);
             let any_declaration_for_importance = if cascade_level.is_important() {
                 block.any_important()
             } else {
                 block.any_normal()
             };
             if any_declaration_for_importance &&
                matches_complex_selector(&*rule.selector, element, parent_bf,
-                                        relations, flags) {
+                                        relations, flags_setter) {
                 matching_rules.push(
                     rule.to_applicable_declaration_block(cascade_level));
             }
         }
     }
 
     /// Insert rule into the correct hash.
     /// Order in which to try: id_hash, class_hash, local_name_hash, other_rules.