Bug 1345950: stylo: Fix slow selector flags. r?heycam draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 21 Mar 2017 12:58:28 +0100
changeset 502247 67134a214f71b1a71fad30b2983c5e57dac6c9f2
parent 502245 bd4f3810b402147f8656390555b29502ce5e2644
child 550108 28bebfdc732a0ef93a9fbaa50eb3407179c00871
push id50227
push userbmo:emilio+bugs@crisal.io
push dateTue, 21 Mar 2017 14:08:51 +0000
reviewersheycam
bugs1345950
milestone55.0a1
Bug 1345950: stylo: Fix slow selector flags. r?heycam MozReview-Commit-ID: QwuSxulNG0
layout/style/test/stylo-failures.md
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/stylist.rs
servo/ports/geckolib/Cargo.toml
servo/ports/geckolib/lib.rs
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/rust/Cargo.lock
--- a/layout/style/test/stylo-failures.md
+++ b/layout/style/test/stylo-failures.md
@@ -68,17 +68,16 @@ to mochitest command.
     * test_bug221428.html [1]
     * test_css_eof_handling.html: also relies on \@-moz-document [1]
   * @keyframes
     * test_keyframes_rules.html [1]
     * test_rules_out_of_sheets.html [1]
   * @support
     * test_supports_rules.html [1]
 * test_box_size_keywords.html: moz-prefixed intrinsic size keyword value [64]
-* test_bug229915.html: sibling selector with dynamic change bug 1330885 [5]
 * test_bug357614.html: case-insensitivity for old attrs in attr selector servo/servo#15006 [2]
 * mapped attribute not supported
   * test_bug363146.html [2]
   * test_bug389464.html: also font-size computation [1]
   * test_html_attribute_computed_values.html: also list-style-type [8]
 * test_bug387615.html: servo/servo#15006 [1]
 * test_bug397427.html: @import issue bug 1331291 and CSSOM support of @import [3]
 * console support:
--- 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
@@ -657,20 +657,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,17 +702,23 @@ impl<'le> ::selectors::Element for Gecko
             }
 
             NonTSPseudoClass::MozTableBorderNonzero |
             NonTSPseudoClass::MozBrowserFrame => unsafe {
                 Gecko_MatchesElement(pseudo_class.to_gecko_pseudoclasstype().unwrap(), self.0)
             },
             NonTSPseudoClass::MozSystemMetric(_) => false,
             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)
+                })
             }
         }
     }
 
     fn get_id(&self) -> Option<Atom> {
         let ptr = unsafe {
             bindings::Gecko_AtomAttrValue(self.0,
                                           atom!("id").as_ptr())
@@ -840,16 +849,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/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.
--- a/servo/ports/geckolib/Cargo.toml
+++ b/servo/ports/geckolib/Cargo.toml
@@ -12,17 +12,16 @@ crate-type = ["staticlib", "rlib"]
 [features]
 bindgen = ["style/use_bindgen"]
 testing = ["style/testing"]
 
 [dependencies]
 atomic_refcell = "0.1"
 cssparser = "0.12"
 env_logger = {version = "0.4", default-features = false} # disable `regex` to reduce code size
-lazy_static = "0.2"
 libc = "0.2"
 log = {version = "0.3.5", features = ["release_max_level_info"]}
 parking_lot = "0.3"
 selectors = {path = "../../components/selectors"}
 servo_url = {path = "../../components/url"}
 style = {path = "../../components/style", features = ["gecko"]}
 style_traits = {path = "../../components/style_traits"}
 
--- a/servo/ports/geckolib/lib.rs
+++ b/servo/ports/geckolib/lib.rs
@@ -2,17 +2,16 @@
  * 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/. */
 
 #![deny(warnings)]
 
 extern crate atomic_refcell;
 extern crate cssparser;
 extern crate env_logger;
-#[macro_use] extern crate lazy_static;
 extern crate libc;
 #[macro_use] extern crate log;
 extern crate parking_lot;
 extern crate selectors;
 extern crate servo_url;
 #[macro_use] extern crate style;
 extern crate style_traits;
 
--- a/toolkit/library/gtest/rust/Cargo.lock
+++ b/toolkit/library/gtest/rust/Cargo.lock
@@ -297,17 +297,16 @@ dependencies = [
 
 [[package]]
 name = "geckoservo"
 version = "0.0.1"
 dependencies = [
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.18.0",
  "servo_url 0.0.1",
  "style 0.0.1",
  "style_traits 0.0.1",
 ]
--- a/toolkit/library/rust/Cargo.lock
+++ b/toolkit/library/rust/Cargo.lock
@@ -295,17 +295,16 @@ dependencies = [
 
 [[package]]
 name = "geckoservo"
 version = "0.0.1"
 dependencies = [
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.18.0",
  "servo_url 0.0.1",
  "style 0.0.1",
  "style_traits 0.0.1",
 ]