servo: Merge #16521 - Store bloom filter hashes inline in the selector (from bholley:precompute_hashes); r=emilio
authorBobby Holley <bobbyholley@gmail.com>
Wed, 19 Apr 2017 02:32:18 -0500
changeset 353823 ba082e450401b845a0c9e877592fde67287d3ae4
parent 353822 96e7a9cb5588327eba8dfa772b9675d350aea546
child 353824 1eb86cbfac794754da6ed1a8f6adbc4150900415
push id31680
push userkwierso@gmail.com
push dateWed, 19 Apr 2017 23:57:00 +0000
treeherdermozilla-central@e9a5d4f62461 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #16521 - Store bloom filter hashes inline in the selector (from bholley:precompute_hashes); r=emilio https://bugzilla.mozilla.org/show_bug.cgi?id=1357304 Source-Repo: https://github.com/servo/servo Source-Revision: b4bea83312abdceaf7fe142ba82a62c12ce9bd02
servo/components/script/dom/element.rs
servo/components/script/dom/node.rs
servo/components/selectors/matching.rs
servo/components/selectors/parser.rs
servo/components/style/gecko/wrapper.rs
servo/components/style/restyle_hints.rs
servo/components/style/stylist.rs
servo/tests/unit/style/stylesheets.rs
servo/tests/unit/style/stylist.rs
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -81,17 +81,17 @@ use html5ever::serialize::SerializeOpts;
 use html5ever::serialize::TraversalScope;
 use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
 use html5ever_atoms::{Prefix, LocalName, Namespace, QualName};
 use js::jsapi::{HandleValue, JSAutoCompartment};
 use net_traits::request::CorsSettings;
 use ref_filter_map::ref_filter_map;
 use script_layout_interface::message::ReflowQueryType;
 use script_thread::Runnable;
-use selectors::matching::{ElementSelectorFlags, StyleRelations, matches};
+use selectors::matching::{ElementSelectorFlags, StyleRelations, matches_selector_list};
 use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS};
 use selectors::parser::{AttrSelector, NamespaceConstraint};
 use servo_atoms::Atom;
 use std::ascii::AsciiExt;
 use std::borrow::Cow;
 use std::cell::{Cell, Ref};
 use std::convert::TryFrom;
 use std::default::Default;
@@ -2045,17 +2045,17 @@ impl ElementMethods for Element {
         self.upcast::<Node>().remove_self();
     }
 
     // https://dom.spec.whatwg.org/#dom-element-matches
     fn Matches(&self, selectors: DOMString) -> Fallible<bool> {
         match SelectorParser::parse_author_origin_no_namespace(&selectors) {
             Err(()) => Err(Error::Syntax),
             Ok(selectors) => {
-                Ok(matches(&selectors.0, &Root::from_ref(self), None))
+                Ok(matches_selector_list(&selectors.0, &Root::from_ref(self), None))
             }
         }
     }
 
     // https://dom.spec.whatwg.org/#dom-element-webkitmatchesselector
     fn WebkitMatchesSelector(&self, selectors: DOMString) -> Fallible<bool> {
         self.Matches(selectors)
     }
@@ -2063,17 +2063,17 @@ impl ElementMethods for Element {
     // https://dom.spec.whatwg.org/#dom-element-closest
     fn Closest(&self, selectors: DOMString) -> Fallible<Option<Root<Element>>> {
         match SelectorParser::parse_author_origin_no_namespace(&selectors) {
             Err(()) => Err(Error::Syntax),
             Ok(selectors) => {
                 let root = self.upcast::<Node>();
                 for element in root.inclusive_ancestors() {
                     if let Some(element) = Root::downcast::<Element>(element) {
-                        if matches(&selectors.0, &element, None)
+                        if matches_selector_list(&selectors.0, &element, None)
                         {
                             return Ok(Some(element));
                         }
                     }
                 }
                 Ok(None)
             }
         }
--- a/servo/components/script/dom/node.rs
+++ b/servo/components/script/dom/node.rs
@@ -63,17 +63,17 @@ use js::jsapi::{JSContext, JSObject, JSR
 use libc::{self, c_void, uintptr_t};
 use msg::constellation_msg::PipelineId;
 use ref_slice::ref_slice;
 use script_layout_interface::{HTMLCanvasData, OpaqueStyleAndLayoutData, SVGSVGData};
 use script_layout_interface::{LayoutElementType, LayoutNodeType, TrustedNodeAddress};
 use script_layout_interface::message::Msg;
 use script_traits::DocumentActivity;
 use script_traits::UntrustedNodeAddress;
-use selectors::matching::matches;
+use selectors::matching::matches_selector_list;
 use selectors::parser::SelectorList;
 use servo_url::ServoUrl;
 use std::borrow::ToOwned;
 use std::cell::{Cell, UnsafeCell};
 use std::cmp::max;
 use std::default::Default;
 use std::iter;
 use std::mem;
@@ -327,17 +327,17 @@ impl<'a> Iterator for QuerySelectorItera
     type Item = Root<Node>;
 
     fn next(&mut self) -> Option<Root<Node>> {
         let selectors = &self.selectors.0;
         // TODO(cgaebel): Is it worth it to build a bloom filter here
         // (instead of passing `None`)? Probably.
         self.iterator.by_ref().filter_map(|node| {
             if let Some(element) = Root::downcast(node) {
-                if matches(selectors, &element, None) {
+                if matches_selector_list(selectors, &element, None) {
                     return Some(Root::upcast(element));
                 }
             }
             None
         }).next()
     }
 }
 
@@ -690,17 +690,17 @@ impl Node {
     pub fn query_selector(&self, selectors: DOMString) -> Fallible<Option<Root<Element>>> {
         // Step 1.
         match SelectorParser::parse_author_origin_no_namespace(&selectors) {
             // Step 2.
             Err(()) => Err(Error::Syntax),
             // Step 3.
             Ok(selectors) => {
                 Ok(self.traverse_preorder().filter_map(Root::downcast).find(|element| {
-                    matches(&selectors.0, element, None)
+                    matches_selector_list(&selectors.0, element, None)
                 }))
             }
         }
     }
 
     /// https://dom.spec.whatwg.org/#scope-match-a-selectors-string
     /// Get an iterator over all nodes which match a set of selectors
     /// Be careful not to do anything which may manipulate the DOM tree
--- a/servo/components/selectors/matching.rs
+++ b/servo/components/selectors/matching.rs
@@ -1,33 +1,28 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 use bloom::BloomFilter;
 use parser::{CaseSensitivity, Combinator, ComplexSelector, LocalName};
-use parser::{SimpleSelector, Selector};
-use precomputed_hash::PrecomputedHash;
+use parser::{SimpleSelector, Selector, SelectorInner};
 use std::borrow::Borrow;
 use tree::Element;
 
 // The bloom filter for descendant CSS selectors will have a <1% false
 // positive rate until it has this many selectors in it, then it will
 // rapidly increase.
 pub static RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE: usize = 4096;
 
 bitflags! {
     /// Set of flags that determine the different kind of elements affected by
     /// the selector matching process.
     ///
     /// This is used to implement efficient sharing.
     pub flags StyleRelations: u16 {
-        /// Whether this element has matched any rule that is determined by a
-        /// sibling (when using the `+` or `~` combinators).
-        const AFFECTED_BY_SIBLINGS = 1 << 0,
-
         /// Whether this element has matched any rule whose matching is
         /// determined by its position in the tree (i.e., first-child,
         /// nth-child, etc.).
         const AFFECTED_BY_CHILD_INDEX = 1 << 1,
 
         /// Whether this flag is affected by any state (i.e., non
         /// tree-structural pseudo-class).
         const AFFECTED_BY_STATE = 1 << 2,
@@ -91,113 +86,71 @@ impl ElementSelectorFlags {
     }
 
     /// Returns the subset of flags that apply to the parent.
     pub fn for_parent(self) -> ElementSelectorFlags {
         self & (HAS_SLOW_SELECTOR | HAS_SLOW_SELECTOR_LATER_SIBLINGS | HAS_EDGE_CHILD_SELECTOR)
     }
 }
 
-pub fn matches<E>(selector_list: &[Selector<E::Impl>],
-                  element: &E,
-                  parent_bf: Option<&BloomFilter>)
-                  -> bool
+pub fn matches_selector_list<E>(selector_list: &[Selector<E::Impl>],
+                                element: &E,
+                                parent_bf: Option<&BloomFilter>)
+                                -> bool
     where E: Element
 {
     selector_list.iter().any(|selector| {
         selector.pseudo_element.is_none() &&
-        matches_complex_selector(&*selector.complex_selector,
-                                 element,
-                                 parent_bf,
-                                 &mut StyleRelations::empty(),
-                                 &mut |_, _| {})
+        matches_selector(&selector.inner,
+                         element,
+                         parent_bf,
+                         &mut StyleRelations::empty(),
+                         &mut |_, _| {})
     })
 }
 
-fn may_match<E>(mut selector: &ComplexSelector<E::Impl>,
+fn may_match<E>(sel: &SelectorInner<E::Impl>,
                 bf: &BloomFilter)
                 -> bool
     where E: Element,
 {
-    // See if the bloom filter can exclude any of the descendant selectors, and
-    // reject if we can.
-    loop {
-         match selector.next {
-             None => break,
-             Some((ref cs, Combinator::Child)) |
-             Some((ref cs, Combinator::Descendant)) => selector = &**cs,
-             Some((ref cs, _)) => {
-                 selector = &**cs;
-                 continue;
-             }
-         };
+    // Check against the list of precomputed hashes.
+    for hash in sel.ancestor_hashes.iter() {
+        // If we hit the 0 sentinel hash, that means the rest are zero as well.
+        if *hash == 0 {
+            break;
+        }
 
-        for ss in selector.compound_selector.iter() {
-            match *ss {
-                SimpleSelector::LocalName(LocalName { ref name, ref lower_name })  => {
-                    if !bf.might_contain_hash(name.precomputed_hash()) &&
-                       !bf.might_contain_hash(lower_name.precomputed_hash()) {
-                       return false
-                    }
-                },
-                SimpleSelector::Namespace(ref namespace) => {
-                    if !bf.might_contain_hash(namespace.url.precomputed_hash()) {
-                        return false
-                    }
-                },
-                SimpleSelector::ID(ref id) => {
-                    if !bf.might_contain_hash(id.precomputed_hash()) {
-                        return false
-                    }
-                },
-                SimpleSelector::Class(ref class) => {
-                    if !bf.might_contain_hash(class.precomputed_hash()) {
-                        return false
-                    }
-                },
-                _ => {},
-            }
+        if !bf.might_contain_hash(*hash) {
+            return false;
         }
     }
 
-    // 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, F>(selector: &ComplexSelector<E::Impl>,
-                                      element: &E,
-                                      parent_bf: Option<&BloomFilter>,
-                                      relations: &mut StyleRelations,
-                                      flags_setter: &mut F)
-                                      -> bool
+pub fn matches_selector<E, F>(selector: &SelectorInner<E::Impl>,
+                              element: &E,
+                              parent_bf: Option<&BloomFilter>,
+                              relations: &mut StyleRelations,
+                              flags_setter: &mut F)
+                              -> bool
     where E: Element,
           F: FnMut(&E, ElementSelectorFlags),
 {
+    // Use the bloom filter to fast-reject.
     if let Some(filter) = parent_bf {
         if !may_match::<E>(selector, filter) {
             return false;
         }
     }
 
-    match 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
-        }
-        _ => false
-    }
+    // Match the selector.
+    matches_complex_selector(&selector.complex, element, relations, flags_setter)
 }
 
 /// A result of selector matching, includes 3 failure types,
 ///
 ///   NotMatchedAndRestartFromClosestLaterSibling
 ///   NotMatchedAndRestartFromClosestDescendant
 ///   NotMatchedGlobally
 ///
@@ -240,16 +193,34 @@ pub fn matches_complex_selector<E, F>(se
 #[derive(PartialEq, Eq, Copy, Clone)]
 enum SelectorMatchingResult {
     Matched,
     NotMatchedAndRestartFromClosestLaterSibling,
     NotMatchedAndRestartFromClosestDescendant,
     NotMatchedGlobally,
 }
 
+/// Matches a complex selector.
+pub fn matches_complex_selector<E, F>(selector: &ComplexSelector<E::Impl>,
+                                      element: &E,
+                                      relations: &mut StyleRelations,
+                                      flags_setter: &mut F)
+                                      -> bool
+     where E: Element,
+           F: FnMut(&E, ElementSelectorFlags),
+{
+    match matches_complex_selector_internal(selector,
+                                            element,
+                                            relations,
+                                            flags_setter) {
+        SelectorMatchingResult::Matched => true,
+        _ => false
+    }
+}
+
 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),
 {
--- a/servo/components/selectors/parser.rs
+++ b/servo/components/selectors/parser.rs
@@ -116,19 +116,62 @@ impl<Impl: SelectorImpl> SelectorList<Im
     /// Return the Selectors or Err if there is an invalid selector.
     pub fn parse<P>(parser: &P, input: &mut CssParser) -> Result<Self, ()>
     where P: Parser<Impl=Impl> {
         input.parse_comma_separated(|input| parse_selector(parser, input))
              .map(SelectorList)
     }
 }
 
+/// Copied from Gecko, where it was noted to be unmeasured.
+const NUM_ANCESTOR_HASHES: usize = 4;
+
+/// The cores parts of a selector used for matching. This exists to make that
+/// information accessibly separately from the specificity and pseudo-element
+/// information that lives on |Selector| proper. We may want to refactor things
+/// and move that information elsewhere, at which point we could rename this
+/// to |Selector|.
+#[derive(PartialEq, Eq, Hash, Clone)]
+pub struct SelectorInner<Impl: SelectorImpl> {
+    /// The selector data.
+    pub complex: Arc<ComplexSelector<Impl>>,
+    /// Ancestor hashes for the bloom filter. We precompute these and store
+    /// them inline to optimize cache performance during selector matching.
+    /// This matters a lot.
+    pub ancestor_hashes: [u32; NUM_ANCESTOR_HASHES],
+}
+
+impl<Impl: SelectorImpl> SelectorInner<Impl> {
+    pub fn new(c: Arc<ComplexSelector<Impl>>) -> Self {
+        let mut hashes = [0; NUM_ANCESTOR_HASHES];
+        {
+            // Compute ancestor hashes for the bloom filter.
+            let mut hash_iter =
+                iter_ancestors(&c).flat_map(|x| x.compound_selector.iter())
+                                  .map(|x| x.ancestor_hash())
+                                  .filter(|x| x.is_some())
+                                  .map(|x| x.unwrap());
+            for i in 0..NUM_ANCESTOR_HASHES {
+                hashes[i] = match hash_iter.next() {
+                    Some(x) => x,
+                    None => break,
+                }
+            }
+        }
+
+        SelectorInner {
+            complex: c,
+            ancestor_hashes: hashes,
+        }
+    }
+}
+
 #[derive(PartialEq, Eq, Hash, Clone)]
 pub struct Selector<Impl: SelectorImpl> {
-    pub complex_selector: Arc<ComplexSelector<Impl>>,
+    pub inner: SelectorInner<Impl>,
     pub pseudo_element: Option<Impl::PseudoElement>,
     pub specificity: u32,
 }
 
 pub trait SelectorMethods {
     type Impl: SelectorImpl;
 
     fn visit<V>(&self, visitor: &mut V) -> bool
@@ -136,17 +179,17 @@ pub trait SelectorMethods {
 }
 
 impl<Impl: SelectorImpl> SelectorMethods for Selector<Impl> {
     type Impl = Impl;
 
     fn visit<V>(&self, visitor: &mut V) -> bool
         where V: SelectorVisitor<Impl = Impl>,
     {
-        self.complex_selector.visit(visitor)
+        self.inner.complex.visit(visitor)
     }
 }
 
 impl<Impl: SelectorImpl> SelectorMethods for ComplexSelector<Impl> {
     type Impl = Impl;
 
     fn visit<V>(&self, visitor: &mut V) -> bool
         where V: SelectorVisitor<Impl = Impl>,
@@ -220,16 +263,45 @@ impl<Impl: SelectorImpl> SelectorMethods
 }
 
 #[derive(Clone, Eq, Hash, PartialEq)]
 pub struct ComplexSelector<Impl: SelectorImpl> {
     pub compound_selector: Vec<SimpleSelector<Impl>>,
     pub next: Option<(Arc<ComplexSelector<Impl>>, Combinator)>,  // c.next is left of c
 }
 
+struct AncestorIterator<'a, Impl: 'a + SelectorImpl> {
+    curr: Option<&'a Arc<ComplexSelector<Impl>>>,
+}
+
+impl<'a, Impl: SelectorImpl> Iterator for AncestorIterator<'a, Impl> {
+    type Item = &'a Arc<ComplexSelector<Impl>>;
+    fn next(&mut self) -> Option<Self::Item> {
+        while let Some(sel) = self.curr.take() {
+            let (next_sel, is_ancestor) = match sel.next {
+                None => (None, true),
+                Some((ref sel, comb)) =>
+                    (Some(sel), matches!(comb, Combinator::Child | Combinator::Descendant)),
+            };
+            self.curr = next_sel;
+            if is_ancestor {
+                break;
+            }
+        }
+
+        self.curr
+    }
+}
+
+fn iter_ancestors<Impl: SelectorImpl>(sel: &Arc<ComplexSelector<Impl>>) -> AncestorIterator<Impl> {
+    AncestorIterator {
+        curr: Some(sel)
+    }
+}
+
 #[derive(Eq, PartialEq, Clone, Copy, Debug, Hash)]
 pub enum Combinator {
     Child,  //  >
     Descendant,  // space
     NextSibling,  // +
     LaterSibling,  // ~
 }
 
@@ -265,16 +337,44 @@ pub enum SimpleSelector<Impl: SelectorIm
     NthLastOfType(i32, i32),
     FirstOfType,
     LastOfType,
     OnlyOfType,
     NonTSPseudoClass(Impl::NonTSPseudoClass),
     // ...
 }
 
+impl<Impl: SelectorImpl> SimpleSelector<Impl> {
+    /// Compute the ancestor hash to check against the bloom filter.
+    fn ancestor_hash(&self) -> Option<u32> {
+        match *self {
+            SimpleSelector::LocalName(LocalName { ref name, ref lower_name }) => {
+                // Only insert the local-name into the filter if it's all lowercase.
+                // Otherwise we would need to test both hashes, and our data structures
+                // aren't really set up for that.
+                if name == lower_name {
+                    Some(name.precomputed_hash())
+                } else {
+                    None
+                }
+            },
+            SimpleSelector::Namespace(ref namespace) => {
+                Some(namespace.url.precomputed_hash())
+            },
+            SimpleSelector::ID(ref id) => {
+                Some(id.precomputed_hash())
+            },
+            SimpleSelector::Class(ref class) => {
+                Some(class.precomputed_hash())
+            },
+            _ => None,
+        }
+    }
+}
+
 #[derive(Eq, PartialEq, Clone, Hash, Copy, Debug)]
 pub enum CaseSensitivity {
     CaseSensitive,  // Selectors spec says language-defined, but HTML says sensitive.
     CaseInsensitive,
 }
 
 
 #[derive(Eq, PartialEq, Clone, Hash)]
@@ -316,16 +416,19 @@ impl<Impl: SelectorImpl> Default for Nam
 impl<Impl: SelectorImpl> Debug for Selector<Impl> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.write_str("Selector(")?;
         self.to_css(f)?;
         write!(f, ", specificity = 0x{:x})", self.specificity)
     }
 }
 
+impl<Impl: SelectorImpl> Debug for SelectorInner<Impl> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.complex.to_css(f) }
+}
 impl<Impl: SelectorImpl> Debug for ComplexSelector<Impl> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_css(f) }
 }
 impl<Impl: SelectorImpl> Debug for SimpleSelector<Impl> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_css(f) }
 }
 impl<Impl: SelectorImpl> Debug for AttrSelector<Impl> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_css(f) }
@@ -348,17 +451,17 @@ impl<Impl: SelectorImpl> ToCss for Selec
             selector.to_css(dest)?;
         }
         Ok(())
     }
 }
 
 impl<Impl: SelectorImpl> ToCss for Selector<Impl> {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        self.complex_selector.to_css(dest)?;
+        self.inner.complex.to_css(dest)?;
         if let Some(ref pseudo) = self.pseudo_element {
             pseudo.to_css(dest)?;
         }
         Ok(())
     }
 }
 
 impl<Impl: SelectorImpl> ToCss for ComplexSelector<Impl> {
@@ -667,17 +770,17 @@ fn complex_selector_specificity<Impl>(mu
 /// `Err` means invalid selector.
 fn parse_selector<P, Impl>(parser: &P, input: &mut CssParser) -> Result<Selector<Impl>, ()>
     where P: Parser<Impl=Impl>, Impl: SelectorImpl
 {
     let (complex, pseudo_element) =
         parse_complex_selector_and_pseudo_element(parser, input)?;
     Ok(Selector {
         specificity: specificity(&complex, pseudo_element.as_ref()),
-        complex_selector: Arc::new(complex),
+        inner: SelectorInner::new(Arc::new(complex)),
         pseudo_element: pseudo_element,
     })
 }
 
 fn parse_complex_selector_and_pseudo_element<P, Impl>(
         parser: &P,
         input: &mut CssParser)
         -> Result<(ComplexSelector<Impl>, Option<Impl::PseudoElement>), ()>
@@ -1300,225 +1403,225 @@ pub mod tests {
     const SVG: &'static str = "http://www.w3.org/2000/svg";
 
     #[test]
     fn test_parsing() {
         assert_eq!(parse(""), Err(())) ;
         assert_eq!(parse(":lang(4)"), Err(())) ;
         assert_eq!(parse(":lang(en US)"), Err(())) ;
         assert_eq!(parse("EeÉ"), Ok(SelectorList(vec!(Selector {
-            complex_selector: Arc::new(ComplexSelector {
+            inner: SelectorInner::new(Arc::new(ComplexSelector {
                 compound_selector: vec!(SimpleSelector::LocalName(LocalName {
                     name: DummyAtom::from("EeÉ"),
                     lower_name: DummyAtom::from("eeÉ") })),
                 next: None,
-            }),
+            })),
             pseudo_element: None,
             specificity: specificity(0, 0, 1),
         }))));
         assert_eq!(parse(".foo:lang(en-US)"), Ok(SelectorList(vec!(Selector {
-            complex_selector: Arc::new(ComplexSelector {
+            inner: SelectorInner::new(Arc::new(ComplexSelector {
                 compound_selector: vec![
                     SimpleSelector::Class(DummyAtom::from("foo")),
                     SimpleSelector::NonTSPseudoClass(PseudoClass::Lang("en-US".to_owned()))
                 ],
                 next: None,
-            }),
+            })),
             pseudo_element: None,
             specificity: specificity(0, 2, 0),
         }))));
         assert_eq!(parse("#bar"), Ok(SelectorList(vec!(Selector {
-            complex_selector: Arc::new(ComplexSelector {
+            inner: SelectorInner::new(Arc::new(ComplexSelector {
                 compound_selector: vec!(SimpleSelector::ID(DummyAtom::from("bar"))),
                 next: None,
-            }),
+            })),
             pseudo_element: None,
             specificity: specificity(1, 0, 0),
         }))));
         assert_eq!(parse("e.foo#bar"), Ok(SelectorList(vec!(Selector {
-            complex_selector: Arc::new(ComplexSelector {
+            inner: SelectorInner::new(Arc::new(ComplexSelector {
                 compound_selector: vec!(SimpleSelector::LocalName(LocalName {
                                             name: DummyAtom::from("e"),
                                             lower_name: DummyAtom::from("e") }),
                                        SimpleSelector::Class(DummyAtom::from("foo")),
                                        SimpleSelector::ID(DummyAtom::from("bar"))),
                 next: None,
-            }),
+            })),
             pseudo_element: None,
             specificity: specificity(1, 1, 1),
         }))));
         assert_eq!(parse("e.foo #bar"), Ok(SelectorList(vec!(Selector {
-            complex_selector: Arc::new(ComplexSelector {
+            inner: SelectorInner::new(Arc::new(ComplexSelector {
                 compound_selector: vec!(SimpleSelector::ID(DummyAtom::from("bar"))),
                 next: Some((Arc::new(ComplexSelector {
                     compound_selector: vec!(SimpleSelector::LocalName(LocalName {
                                                 name: DummyAtom::from("e"),
                                                 lower_name: DummyAtom::from("e") }),
                                            SimpleSelector::Class(DummyAtom::from("foo"))),
                     next: None,
                 }), Combinator::Descendant)),
-            }),
+            })),
             pseudo_element: None,
             specificity: specificity(1, 1, 1),
         }))));
         // Default namespace does not apply to attribute selectors
         // https://github.com/mozilla/servo/pull/1652
         let mut parser = DummyParser::default();
         assert_eq!(parse_ns("[Foo]", &parser), Ok(SelectorList(vec!(Selector {
-            complex_selector: Arc::new(ComplexSelector {
+            inner: SelectorInner::new(Arc::new(ComplexSelector {
                 compound_selector: vec!(SimpleSelector::AttrExists(AttrSelector {
                     name: DummyAtom::from("Foo"),
                     lower_name: DummyAtom::from("foo"),
                     namespace: NamespaceConstraint::Specific(Namespace {
                         prefix: None,
                         url: "".into(),
                     }),
                 })),
                 next: None,
-            }),
+            })),
             pseudo_element: None,
             specificity: specificity(0, 1, 0),
         }))));
         assert_eq!(parse_ns("svg|circle", &parser), Err(()));
         parser.ns_prefixes.insert(DummyAtom("svg".into()), DummyAtom(SVG.into()));
         assert_eq!(parse_ns("svg|circle", &parser), Ok(SelectorList(vec![Selector {
-            complex_selector: Arc::new(ComplexSelector {
+            inner: SelectorInner::new(Arc::new(ComplexSelector {
                 compound_selector: vec![
                     SimpleSelector::Namespace(Namespace {
                         prefix: Some(DummyAtom("svg".into())),
                         url: SVG.into(),
                     }),
                     SimpleSelector::LocalName(LocalName {
                         name: DummyAtom::from("circle"),
                         lower_name: DummyAtom::from("circle"),
                     })
                 ],
                 next: None,
-            }),
+            })),
             pseudo_element: None,
             specificity: specificity(0, 0, 1),
         }])));
         // Default namespace does not apply to attribute selectors
         // https://github.com/mozilla/servo/pull/1652
         // but it does apply to implicit type selectors
         // https://github.com/servo/rust-selectors/pull/82
         parser.default_ns = Some(MATHML.into());
         assert_eq!(parse_ns("[Foo]", &parser), Ok(SelectorList(vec!(Selector {
-            complex_selector: Arc::new(ComplexSelector {
+            inner: SelectorInner::new(Arc::new(ComplexSelector {
                 compound_selector: vec![
                     SimpleSelector::Namespace(Namespace {
                         prefix: None,
                         url: MATHML.into(),
                     }),
                     SimpleSelector::AttrExists(AttrSelector {
                         name: DummyAtom::from("Foo"),
                         lower_name: DummyAtom::from("foo"),
                         namespace: NamespaceConstraint::Specific(Namespace {
                             prefix: None,
                             url: "".into(),
                         }),
                     }),
                 ],
                 next: None,
-            }),
+            })),
             pseudo_element: None,
             specificity: specificity(0, 1, 0),
         }))));
         // Default namespace does apply to type selectors
         assert_eq!(parse_ns("e", &parser), Ok(SelectorList(vec!(Selector {
-            complex_selector: Arc::new(ComplexSelector {
+            inner: SelectorInner::new(Arc::new(ComplexSelector {
                 compound_selector: vec!(
                     SimpleSelector::Namespace(Namespace {
                         prefix: None,
                         url: MATHML.into(),
                     }),
                     SimpleSelector::LocalName(LocalName {
                         name: DummyAtom::from("e"),
                         lower_name: DummyAtom::from("e") }),
                 ),
                 next: None,
-            }),
+            })),
             pseudo_element: None,
             specificity: specificity(0, 0, 1),
         }))));
         assert_eq!(parse("[attr |= \"foo\"]"), Ok(SelectorList(vec![Selector {
-            complex_selector: Arc::new(ComplexSelector {
+            inner: SelectorInner::new(Arc::new(ComplexSelector {
                 compound_selector: vec![
                     SimpleSelector::AttrDashMatch(AttrSelector {
                         name: DummyAtom::from("attr"),
                         lower_name: DummyAtom::from("attr"),
                         namespace: NamespaceConstraint::Specific(Namespace {
                             prefix: None,
                             url: "".into(),
                         }),
                     }, DummyAtom::from("foo"))
                 ],
                 next: None,
-            }),
+            })),
             pseudo_element: None,
             specificity: specificity(0, 1, 0),
         }])));
         // https://github.com/mozilla/servo/issues/1723
         assert_eq!(parse("::before"), Ok(SelectorList(vec!(Selector {
-            complex_selector: Arc::new(ComplexSelector {
+            inner: SelectorInner::new(Arc::new(ComplexSelector {
                 compound_selector: vec!(),
                 next: None,
-            }),
+            })),
             pseudo_element: Some(PseudoElement::Before),
             specificity: specificity(0, 0, 1),
         }))));
         // https://github.com/servo/servo/issues/15335
         assert_eq!(parse(":: before"), Err(()));
         assert_eq!(parse("div ::after"), Ok(SelectorList(vec!(Selector {
-            complex_selector: Arc::new(ComplexSelector {
+            inner: SelectorInner::new(Arc::new(ComplexSelector {
                 compound_selector: vec!(),
                 next: Some((Arc::new(ComplexSelector {
                     compound_selector: vec!(SimpleSelector::LocalName(LocalName {
                         name: DummyAtom::from("div"),
                         lower_name: DummyAtom::from("div") })),
                     next: None,
                 }), Combinator::Descendant)),
-            }),
+            })),
             pseudo_element: Some(PseudoElement::After),
             specificity: specificity(0, 0, 2),
         }))));
         assert_eq!(parse("#d1 > .ok"), Ok(SelectorList(vec![Selector {
-            complex_selector: Arc::new(ComplexSelector {
+            inner: SelectorInner::new(Arc::new(ComplexSelector {
                 compound_selector: vec![
                     SimpleSelector::Class(DummyAtom::from("ok")),
                 ],
                 next: Some((Arc::new(ComplexSelector {
                     compound_selector: vec![
                         SimpleSelector::ID(DummyAtom::from("d1")),
                     ],
                     next: None,
                 }), Combinator::Child)),
-            }),
+            })),
             pseudo_element: None,
             specificity: (1 << 20) + (1 << 10) + (0 << 0),
         }])));
         assert_eq!(parse(":not(.babybel, #provel.old)"), Ok(SelectorList(vec!(Selector {
-            complex_selector: Arc::new(ComplexSelector {
+            inner: SelectorInner::new(Arc::new(ComplexSelector {
                 compound_selector: vec!(SimpleSelector::Negation(
                     vec!(
                         Arc::new(ComplexSelector {
                             compound_selector: vec!(SimpleSelector::Class(DummyAtom::from("babybel"))),
                             next: None
                         }),
                         Arc::new(ComplexSelector {
                             compound_selector: vec!(
                                 SimpleSelector::ID(DummyAtom::from("provel")),
                                 SimpleSelector::Class(DummyAtom::from("old")),
                             ),
                             next: None
                         }),
                     )
                 )),
                 next: None,
-            }),
+            })),
             pseudo_element: None,
             specificity: specificity(1, 1, 0),
         }))));
     }
 
     struct TestVisitor {
         seen: Vec<String>,
     }
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -1145,21 +1145,17 @@ impl<'le> ::selectors::Element for Gecko
                 true
             }
             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_setter)
+                    matches_complex_selector(s, self, relations, flags_setter)
                 })
             }
             NonTSPseudoClass::MozSystemMetric(ref s) |
             NonTSPseudoClass::MozLocaleDir(ref s) |
             NonTSPseudoClass::MozEmptyExceptChildrenWithLocalname(ref s) |
             NonTSPseudoClass::Dir(ref s) |
             NonTSPseudoClass::Lang(ref s) => {
                 unsafe {
--- a/servo/components/style/restyle_hints.rs
+++ b/servo/components/style/restyle_hints.rs
@@ -11,18 +11,18 @@ use dom::TElement;
 use element_state::*;
 #[cfg(feature = "gecko")]
 use gecko_bindings::structs::nsRestyleHint;
 #[cfg(feature = "servo")]
 use heapsize::HeapSizeOf;
 use selector_parser::{AttrValue, NonTSPseudoClass, Snapshot, SelectorImpl};
 use selectors::{Element, MatchAttr};
 use selectors::matching::{ElementSelectorFlags, StyleRelations};
-use selectors::matching::matches_complex_selector;
-use selectors::parser::{AttrSelector, Combinator, ComplexSelector, SelectorMethods, SimpleSelector};
+use selectors::matching::matches_selector;
+use selectors::parser::{AttrSelector, Combinator, ComplexSelector, SelectorInner, SelectorMethods, SimpleSelector};
 use selectors::visitor::SelectorVisitor;
 use std::clone::Clone;
 use std::sync::Arc;
 
 bitflags! {
     /// When the ElementState of an element (like IN_HOVER_STATE) changes,
     /// certain pseudo-classes (like :hover) may require us to restyle that
     /// element, its siblings, and/or its descendants. Similarly, when various
@@ -293,23 +293,20 @@ impl<'a, E> Element for ElementWrapper<'
                                     _setter: &mut F)
                                     -> bool
         where F: FnMut(&Self, ElementSelectorFlags),
     {
         // :moz-any is quite special, because we need to keep matching as a
         // snapshot.
         #[cfg(feature = "gecko")]
         {
+            use selectors::matching::matches_complex_selector;
             if let NonTSPseudoClass::MozAny(ref selectors) = *pseudo_class {
                 return selectors.iter().any(|s| {
-                    matches_complex_selector(s,
-                                             self,
-                                             None,
-                                             relations,
-                                             _setter)
+                    matches_complex_selector(s, self, relations, _setter)
                 })
             }
         }
 
         let flag = pseudo_class.state_flag();
         if flag.is_empty() {
             return self.element.match_non_ts_pseudo_class(pseudo_class,
                                                           relations,
@@ -488,17 +485,17 @@ impl Sensitivities {
 /// even though those selectors may not appear on their own in any stylesheet.
 /// This allows us to quickly scan through the dependency sites of all style
 /// rules and determine the maximum effect that a given state or attribute
 /// change may have on the style of elements in the document.
 #[derive(Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 struct Dependency {
     #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
-    selector: Arc<ComplexSelector<SelectorImpl>>,
+    selector: SelectorInner<SelectorImpl>,
     hint: RestyleHint,
     sensitivities: Sensitivities,
 }
 
 
 /// The following visitor visits all the simple selectors for a given complex
 /// selector, taking care of :not and :any combinators, collecting whether any
 /// of them is sensitive to attribute or state changes.
@@ -598,17 +595,17 @@ impl DependencySet {
             } = sensitivities_visitor;
 
             hint |= combinator_to_restyle_hint(combinator);
 
             if !sensitivities.is_empty() {
                 self.add_dependency(Dependency {
                     sensitivities: sensitivities,
                     hint: hint,
-                    selector: current.clone(),
+                    selector: SelectorInner::new(current.clone()),
                 })
             }
 
             match current.next {
                 Some((ref next, next_combinator)) => {
                     current = next;
                     combinator = Some(next_combinator);
                 }
@@ -694,23 +691,23 @@ impl DependencySet {
         for dep in deps {
             debug_assert!((!state_changes.is_empty() && !dep.sensitivities.states.is_empty()) ||
                           (attrs_changed && dep.sensitivities.attrs),
                           "Testing a known ineffective dependency?");
             if (attrs_changed || state_changes.intersects(dep.sensitivities.states)) && !hint.contains(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 |_, _| {});
+                    matches_selector(&dep.selector, snapshot, None,
+                                     &mut StyleRelations::empty(),
+                                     &mut |_, _| {});
                 let matches_now =
-                    matches_complex_selector(&dep.selector, element, None,
-                                             &mut StyleRelations::empty(),
-                                             &mut |_, _| {});
+                    matches_selector(&dep.selector, element, None,
+                                     &mut StyleRelations::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
@@ -21,18 +21,18 @@ use properties::INHERIT_ALL;
 use properties::PropertyDeclarationBlock;
 use restyle_hints::{RestyleHint, DependencySet};
 use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
 use selector_parser::{SelectorImpl, PseudoElement, Snapshot};
 use selectors::Element;
 use selectors::bloom::BloomFilter;
 use selectors::matching::{AFFECTED_BY_ANIMATIONS, AFFECTED_BY_TRANSITIONS};
 use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS};
-use selectors::matching::{ElementSelectorFlags, StyleRelations, matches_complex_selector};
-use selectors::parser::{Selector, SimpleSelector, LocalName as LocalNameSelector, ComplexSelector};
+use selectors::matching::{ElementSelectorFlags, StyleRelations, matches_selector};
+use selectors::parser::{Selector, SelectorInner, SimpleSelector, LocalName as LocalNameSelector};
 use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
 use sink::Push;
 use smallvec::VecLike;
 use std::borrow::Borrow;
 use std::collections::HashMap;
 use std::fmt;
 use std::hash::Hash;
 #[cfg(feature = "servo")]
@@ -331,27 +331,27 @@ impl Stylist {
                                 .entry(pseudo.clone())
                                 .or_insert_with(PerPseudoElementSelectorMap::new)
                                 .borrow_for_origin(&stylesheet.origin)
                         } else {
                             self.element_map.borrow_for_origin(&stylesheet.origin)
                         };
 
                         map.insert(Rule {
-                            selector: selector.complex_selector.clone(),
+                            selector: selector.inner.clone(),
                             style_rule: locked.clone(),
                             specificity: selector.specificity,
                             source_order: self.rules_source_order,
                         });
                     }
                     self.rules_source_order += 1;
 
                     for selector in &style_rule.selectors.0 {
                         let needs_cache_revalidation =
-                            self.dependencies.note_selector(&selector.complex_selector);
+                            self.dependencies.note_selector(&selector.inner.complex);
                         if needs_cache_revalidation {
                             self.selectors_for_cache_revalidation.push(selector.clone());
                         }
                     }
                 }
                 CssRule::Import(ref import) => {
                     let import = import.read_with(guard);
                     self.add_stylesheet(&import.stylesheet, guard, extra_data)
@@ -821,28 +821,28 @@ impl Stylist {
                                               element: &E,
                                               bloom: &BloomFilter,
                                               flags_setter: &mut F)
                                               -> BitVec
         where E: TElement,
               F: FnMut(&E, ElementSelectorFlags)
     {
         use selectors::matching::StyleRelations;
-        use selectors::matching::matches_complex_selector;
+        use selectors::matching::matches_selector;
 
         let len = self.selectors_for_cache_revalidation.len();
         let mut results = BitVec::from_elem(len, false);
 
         for (i, ref selector) in self.selectors_for_cache_revalidation
                                      .iter().enumerate() {
-            results.set(i, matches_complex_selector(&selector.complex_selector,
-                                                    element,
-                                                    Some(bloom),
-                                                    &mut StyleRelations::empty(),
-                                                    flags_setter));
+            results.set(i, matches_selector(&selector.inner,
+                                            element,
+                                            Some(bloom),
+                                            &mut StyleRelations::empty(),
+                                            flags_setter));
         }
 
         results
     }
 
     /// Given an element, and a snapshot that represents a previous state of the
     /// element, compute the appropriate restyle hint, that is, the kind of
     /// restyle we need to do.
@@ -1076,18 +1076,18 @@ impl SelectorMap {
         }
 
         let mut matching_rules_list = vec![];
 
         // We need to insert important rules _after_ normal rules for this to be
         // correct, and also to not trigger rule tree assertions.
         let mut important = vec![];
         for rule in self.other_rules.iter() {
-            if rule.selector.compound_selector.is_empty() &&
-               rule.selector.next.is_none() {
+            if rule.selector.complex.compound_selector.is_empty() &&
+               rule.selector.complex.next.is_none() {
                 let style_rule = rule.style_rule.read_with(guard);
                 let block = style_rule.block.read_with(guard);
                 if block.any_normal() {
                     matching_rules_list.push(
                         rule.to_applicable_declaration_block(cascade_level));
                 }
                 if block.any_important() {
                     important.push(
@@ -1152,18 +1152,18 @@ impl SelectorMap {
             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_setter) {
+               matches_selector(&rule.selector, element, parent_bf,
+                                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.
@@ -1186,43 +1186,43 @@ impl SelectorMap {
             return;
         }
 
         self.other_rules.push(rule);
     }
 
     /// Retrieve the first ID name in Rule, or None otherwise.
     pub fn get_id_name(rule: &Rule) -> Option<Atom> {
-        for ss in &rule.selector.compound_selector {
+        for ss in &rule.selector.complex.compound_selector {
             // TODO(pradeep): Implement case-sensitivity based on the
             // document type and quirks mode.
             if let SimpleSelector::ID(ref id) = *ss {
                 return Some(id.clone());
             }
         }
 
         None
     }
 
     /// Retrieve the FIRST class name in Rule, or None otherwise.
     pub fn get_class_name(rule: &Rule) -> Option<Atom> {
-        for ss in &rule.selector.compound_selector {
+        for ss in &rule.selector.complex.compound_selector {
             // TODO(pradeep): Implement case-sensitivity based on the
             // document type and quirks mode.
             if let SimpleSelector::Class(ref class) = *ss {
                 return Some(class.clone());
             }
         }
 
         None
     }
 
     /// Retrieve the name if it is a type selector, or None otherwise.
     pub fn get_local_name(rule: &Rule) -> Option<LocalNameSelector<SelectorImpl>> {
-        for ss in &rule.selector.compound_selector {
+        for ss in &rule.selector.complex.compound_selector {
             if let SimpleSelector::LocalName(ref n) = *ss {
                 return Some(LocalNameSelector {
                     name: n.name.clone(),
                     lower_name: n.lower_name.clone(),
                 })
             }
         }
 
@@ -1240,17 +1240,17 @@ pub struct Rule {
     /// that it matches. Selector contains an owned vector (through
     /// ComplexSelector) and we want to avoid the allocation.
     ///
     /// FIXME(emilio): We should be able to get rid of it and just use the style
     /// rule? This predates the time where the rule was in `selectors`, and the
     /// style rule was a generic parameter to it. It's not trivial though, due
     /// to the specificity.
     #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
-    pub selector: Arc<ComplexSelector<SelectorImpl>>,
+    pub selector: SelectorInner<SelectorImpl>,
     /// The actual style rule.
     #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
     pub style_rule: Arc<Locked<StyleRule>>,
     /// The source order this style rule appears in.
     pub source_order: usize,
     /// The specificity of the rule this selector represents.
     pub specificity: u32,
 }
--- a/servo/tests/unit/style/stylesheets.rs
+++ b/servo/tests/unit/style/stylesheets.rs
@@ -79,17 +79,17 @@ fn test_parse_stylesheet() {
         rules: CssRules::new(vec![
             CssRule::Namespace(Arc::new(stylesheet.shared_lock.wrap(NamespaceRule {
                 prefix: None,
                 url: NsAtom::from("http://www.w3.org/1999/xhtml")
             }))),
             CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
                 selectors: SelectorList(vec![
                     Selector {
-                        complex_selector: Arc::new(ComplexSelector {
+                        inner: SelectorInner::new(Arc::new(ComplexSelector {
                             compound_selector: vec![
                                 SimpleSelector::Namespace(Namespace {
                                     prefix: None,
                                     url: NsAtom::from("http://www.w3.org/1999/xhtml")
                                 }),
                                 SimpleSelector::LocalName(LocalName {
                                     name: local_name!("input"),
                                     lower_name: local_name!("input"),
@@ -99,75 +99,75 @@ fn test_parse_stylesheet() {
                                     lower_name: local_name!("type"),
                                     namespace: NamespaceConstraint::Specific(Namespace {
                                         prefix: None,
                                         url: ns!()
                                     }),
                                 }, "hidden".to_owned(), CaseSensitivity::CaseInsensitive)
                             ],
                             next: None,
-                        }),
+                        })),
                         pseudo_element: None,
                         specificity: (0 << 20) + (1 << 10) + (1 << 0),
                     },
                 ]),
                 block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
                     (PropertyDeclaration::Display(longhands::display::SpecifiedValue::none),
                      Importance::Important),
                     (PropertyDeclaration::Custom(Atom::from("a"),
                      DeclaredValueOwned::CSSWideKeyword(CSSWideKeyword::Inherit)),
                      Importance::Important),
                 ]))),
             }))),
             CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
                 selectors: SelectorList(vec![
                     Selector {
-                        complex_selector: Arc::new(ComplexSelector {
+                        inner: SelectorInner::new(Arc::new(ComplexSelector {
                             compound_selector: vec![
                                 SimpleSelector::Namespace(Namespace {
                                     prefix: None,
                                     url: NsAtom::from("http://www.w3.org/1999/xhtml")
                                 }),
                                 SimpleSelector::LocalName(LocalName {
                                     name: local_name!("html"),
                                     lower_name: local_name!("html"),
                                 }),
                             ],
                             next: None,
-                        }),
+                        })),
                         pseudo_element: None,
                         specificity: (0 << 20) + (0 << 10) + (1 << 0),
                     },
                     Selector {
-                        complex_selector: Arc::new(ComplexSelector {
+                        inner: SelectorInner::new(Arc::new(ComplexSelector {
                             compound_selector: vec![
                                 SimpleSelector::Namespace(Namespace {
                                     prefix: None,
                                     url: NsAtom::from("http://www.w3.org/1999/xhtml")
                                 }),
                                 SimpleSelector::LocalName(LocalName {
                                     name: local_name!("body"),
                                     lower_name: local_name!("body"),
                                 }),
                             ],
                             next: None,
-                        }),
+                        })),
                         pseudo_element: None,
                         specificity: (0 << 20) + (0 << 10) + (1 << 0),
                     },
                 ]),
                 block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
                     (PropertyDeclaration::Display(longhands::display::SpecifiedValue::block),
                      Importance::Normal),
                 ]))),
             }))),
             CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
                 selectors: SelectorList(vec![
                     Selector {
-                        complex_selector: Arc::new(ComplexSelector {
+                        inner: SelectorInner::new(Arc::new(ComplexSelector {
                             compound_selector: vec![
                                 SimpleSelector::Namespace(Namespace {
                                     prefix: None,
                                     url: NsAtom::from("http://www.w3.org/1999/xhtml")
                                 }),
                                 SimpleSelector::Class(Atom::from("ok")),
                             ],
                             next: Some((Arc::new(ComplexSelector {
@@ -175,17 +175,17 @@ fn test_parse_stylesheet() {
                                     SimpleSelector::Namespace(Namespace {
                                         prefix: None,
                                         url: NsAtom::from("http://www.w3.org/1999/xhtml")
                                     }),
                                     SimpleSelector::ID(Atom::from("d1")),
                                 ],
                                 next: None,
                             }), Combinator::Child)),
-                        }),
+                        })),
                         pseudo_element: None,
                         specificity: (1 << 20) + (1 << 10) + (0 << 0),
                     },
                 ]),
                 block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
                     (PropertyDeclaration::BackgroundColor(
                         longhands::background_color::SpecifiedValue {
                             authored: Some("blue".to_owned().into_boxed_str()),
--- a/servo/tests/unit/style/stylist.rs
+++ b/servo/tests/unit/style/stylist.rs
@@ -30,17 +30,17 @@ fn get_mock_rules(css_selectors: &[&str]
                 Importance::Normal
             ))),
         }));
 
         let guard = shared_lock.read();
         let rule = locked.read_with(&guard);
         rule.selectors.0.iter().map(|s| {
             Rule {
-                selector: s.complex_selector.clone(),
+                selector: s.inner.clone(),
                 style_rule: locked.clone(),
                 specificity: s.specificity,
                 source_order: i,
             }
         }).collect()
     }).collect(), shared_lock)
 }