diff --git a/components/selectors/arcslice.rs b/components/selectors/arcslice.rs new file mode 100644 index 00000000000..e5722ba6505 --- /dev/null +++ b/components/selectors/arcslice.rs @@ -0,0 +1,326 @@ +/* Licensed under the Apache License, Version 2.0 or the MIT license + * , at your + * option. This file may not be copied, modified, or distributed + * except according to those terms. + * + * See the COPYRIGHT file at the top-level directory of this distribution */ +//! A thread-safe reference-counted slice type. +//! +//! Forked from https://github.com/huonw/shared_slice , which doesn't work on +//! rust stable. + +use std::{cmp, fmt, ops}; +use std::hash::{Hash, Hasher}; +use std::sync::{Arc, Weak}; + + +/// A reference-counted slice type. +pub struct ArcSlice { + data: *const [T], + counts: Arc>, +} + +unsafe impl Send for ArcSlice {} +unsafe impl Sync for ArcSlice {} + +/// A non-owning reference-counted slice type. +/// +/// This is to `ArcSlice` as `std::sync::Weak` is to `std::sync::Arc`, and +/// allows one to have cyclic references without stopping memory from +/// being deallocated. +pub struct WeakSlice { + data: *const [T], + counts: Weak>, +} +unsafe impl Send for WeakSlice {} +unsafe impl Sync for WeakSlice {} + +impl ArcSlice { + /// Construct a new `ArcSlice` containing the elements of `slice`. + /// + /// This reuses the allocation of `slice`. + pub fn new(slice: Box<[T]>) -> ArcSlice { + ArcSlice { + data: &*slice, + counts: Arc::new(slice), + } + } + + /// Downgrade self into a weak slice. + pub fn downgrade(&self) -> WeakSlice { + WeakSlice { + data: self.data, + counts: Arc::downgrade(&self.counts) + } + } + + /// Construct a new `ArcSlice` that only points to elements at + /// indices `lo` (inclusive) through `hi` (exclusive). + /// + /// This consumes `self` to avoid unnecessary reference-count + /// modifications. Use `.clone()` if it is necessary to refer to + /// `self` after calling this. + /// + /// # Panics + /// + /// Panics if `lo > hi` or if either are strictly greater than + /// `self.len()`. + pub fn slice(mut self, lo: usize, hi: usize) -> ArcSlice { + self.data = &self[lo..hi]; + self + } + /// Construct a new `ArcSlice` that only points to elements at + /// indices up to `hi` (exclusive). + /// + /// This consumes `self` to avoid unnecessary reference-count + /// modifications. Use `.clone()` if it is necessary to refer to + /// `self` after calling this. + /// + /// # Panics + /// + /// Panics if `hi > self.len()`. + pub fn slice_to(self, hi: usize) -> ArcSlice { + self.slice(0, hi) + } + /// Construct a new `ArcSlice` that only points to elements at + /// indices starting at `lo` (inclusive). + /// + /// This consumes `self` to avoid unnecessary reference-count + /// modifications. Use `.clone()` if it is necessary to refer to + /// `self` after calling this. + /// + /// # Panics + /// + /// Panics if `lo > self.len()`. + pub fn slice_from(self, lo: usize) -> ArcSlice { + let hi = self.len(); + self.slice(lo, hi) + } +} + +impl Clone for ArcSlice { + fn clone(&self) -> ArcSlice { + ArcSlice { + data: self.data, + counts: self.counts.clone() + } + } +} + +impl ops::Deref for ArcSlice { + type Target = [T]; + fn deref<'a>(&'a self) -> &'a [T] { + unsafe { &*self.data } + } +} + +impl AsRef<[T]> for ArcSlice { + fn as_ref(&self) -> &[T] { &**self } +} + +impl PartialEq for ArcSlice { + fn eq(&self, other: &ArcSlice) -> bool { **self == **other } + fn ne(&self, other: &ArcSlice) -> bool { **self != **other } +} +impl Eq for ArcSlice {} + +impl PartialOrd for ArcSlice { + fn partial_cmp(&self, other: &ArcSlice) -> Option { + (**self).partial_cmp(&**other) + } + fn lt(&self, other: &ArcSlice) -> bool { **self < **other } + fn le(&self, other: &ArcSlice) -> bool { **self <= **other } + fn gt(&self, other: &ArcSlice) -> bool { **self > **other } + fn ge(&self, other: &ArcSlice) -> bool { **self >= **other } +} +impl Ord for ArcSlice { + fn cmp(&self, other: &ArcSlice) -> cmp::Ordering { (**self).cmp(&**other) } +} + +impl Hash for ArcSlice { + fn hash(&self, state: &mut H) { + Hash::hash(&**self, state) + } +} + +impl fmt::Debug for ArcSlice { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl WeakSlice { + /// Attempt to upgrade `self` to a strongly-counted `ArcSlice`. + /// + /// Returns `None` if this is not possible (the data has already + /// been freed). + pub fn upgrade(&self) -> Option> { + self.counts.upgrade().map(|counts| { + ArcSlice { + data: self.data, + counts: counts + } + }) + } +} + +#[cfg(test)] +mod tests { + use std::cell::Cell; + use std::cmp::Ordering; + use std::sync::{Arc, Mutex}; + use super::{ArcSlice, WeakSlice}; + #[test] + fn clone() { + let x = ArcSlice::new(Box::new([Cell::new(false)])); + let y = x.clone(); + + assert_eq!(x[0].get(), false); + assert_eq!(y[0].get(), false); + + x[0].set(true); + assert_eq!(x[0].get(), true); + assert_eq!(y[0].get(), true); + } + + #[test] + fn test_upgrade_downgrade() { + let x = ArcSlice::new(Box::new([1])); + let y: WeakSlice<_> = x.downgrade(); + + assert_eq!(y.upgrade(), Some(x.clone())); + + drop(x); + + assert!(y.upgrade().is_none()) + } + + #[test] + fn test_total_cmp() { + let x = ArcSlice::new(Box::new([1, 2, 3])); + let y = ArcSlice::new(Box::new([1, 2, 3])); + let z = ArcSlice::new(Box::new([1, 2, 4])); + + assert_eq!(x, x); + assert_eq!(x, y); + assert!(x != z); + assert!(y != z); + + assert!(x < z); + assert!(x <= z); + assert!(!(x > z)); + assert!(!(x >= z)); + + assert!(!(z < x)); + assert!(!(z <= x)); + assert!(z > x); + assert!(z >= x); + + assert_eq!(x.partial_cmp(&x), Some(Ordering::Equal)); + assert_eq!(x.partial_cmp(&y), Some(Ordering::Equal)); + assert_eq!(x.partial_cmp(&z), Some(Ordering::Less)); + assert_eq!(z.partial_cmp(&y), Some(Ordering::Greater)); + + assert_eq!(x.cmp(&x), Ordering::Equal); + assert_eq!(x.cmp(&y), Ordering::Equal); + assert_eq!(x.cmp(&z), Ordering::Less); + assert_eq!(z.cmp(&y), Ordering::Greater); + } + + #[test] + fn test_partial_cmp() { + use std::f64; + let x = ArcSlice::new(Box::new([1.0, f64::NAN])); + let y = ArcSlice::new(Box::new([1.0, f64::NAN])); + let z = ArcSlice::new(Box::new([2.0, f64::NAN])); + let w = ArcSlice::new(Box::new([f64::NAN, 1.0])); + assert!(!(x == y)); + assert!(x != y); + + assert!(!(x < y)); + assert!(!(x <= y)); + assert!(!(x > y)); + assert!(!(x >= y)); + + assert!(x < z); + assert!(x <= z); + assert!(!(x > z)); + assert!(!(x >= z)); + + assert!(!(z < w)); + assert!(!(z <= w)); + assert!(!(z > w)); + assert!(!(z >= w)); + + assert_eq!(x.partial_cmp(&x), None); + assert_eq!(x.partial_cmp(&y), None); + assert_eq!(x.partial_cmp(&z), Some(Ordering::Less)); + assert_eq!(z.partial_cmp(&x), Some(Ordering::Greater)); + + assert_eq!(x.partial_cmp(&w), None); + assert_eq!(y.partial_cmp(&w), None); + assert_eq!(z.partial_cmp(&w), None); + assert_eq!(w.partial_cmp(&w), None); + } + + #[test] + fn test_show() { + let x = ArcSlice::new(Box::new([1, 2])); + assert_eq!(format!("{:?}", x), "[1, 2]"); + + let y: ArcSlice = ArcSlice::new(Box::new([])); + assert_eq!(format!("{:?}", y), "[]"); + } + + #[test] + fn test_slice() { + let x = ArcSlice::new(Box::new([1, 2, 3])); + let real = [1, 2, 3]; + for i in 0..3 + 1 { + for j in i..3 + 1 { + let slice: ArcSlice<_> = x.clone().slice(i, j); + assert_eq!(&*slice, &real[i..j]); + } + assert_eq!(&*x.clone().slice_to(i), &real[..i]); + assert_eq!(&*x.clone().slice_from(i), &real[i..]); + } + } + + + #[test] + fn test_send_sync() { + fn assert_send() {} + fn assert_sync() {} + + assert_send::>(); + assert_sync::>(); + assert_send::>(); + assert_sync::>(); + } + + #[test] + fn test_drop() { + let drop_flag = Arc::new(Mutex::new(0)); + struct Foo(Arc>); + + impl Drop for Foo { + fn drop(&mut self) { + let mut n = self.0.lock().unwrap(); + *n += 1; + } + } + + let whole = ArcSlice::new(Box::new([Foo(drop_flag.clone()), Foo(drop_flag.clone())])); + + drop(whole); + assert_eq!(*drop_flag.lock().unwrap(), 2); + + *drop_flag.lock().unwrap() = 0; + + let whole = ArcSlice::new(Box::new([Foo(drop_flag.clone()), Foo(drop_flag.clone())])); + let part = whole.slice(1, 2); + drop(part); + assert_eq!(*drop_flag.lock().unwrap(), 2); + } +} diff --git a/components/selectors/lib.rs b/components/selectors/lib.rs index 49fce51a4fe..452e47963c7 100644 --- a/components/selectors/lib.rs +++ b/components/selectors/lib.rs @@ -9,6 +9,7 @@ extern crate fnv; extern crate precomputed_hash; extern crate smallvec; +pub mod arcslice; pub mod bloom; pub mod matching; pub mod parser; diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index dcb904fdb08..83a094b637a 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -2,8 +2,8 @@ * 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, SelectorInner}; +use parser::{CaseSensitivity, Combinator, ComplexSelector, Component, LocalName}; +use parser::{Selector, SelectorInner, SelectorIter}; use std::borrow::Borrow; use tree::Element; @@ -207,7 +207,7 @@ pub fn matches_complex_selector(selector: &ComplexSelector, where E: Element, F: FnMut(&E, ElementSelectorFlags), { - match matches_complex_selector_internal(selector, + match matches_complex_selector_internal(selector.iter(), element, relations, flags_setter) { @@ -216,7 +216,7 @@ pub fn matches_complex_selector(selector: &ComplexSelector, } } -fn matches_complex_selector_internal(selector: &ComplexSelector, +fn matches_complex_selector_internal(mut selector_iter: SelectorIter, element: &E, relations: &mut StyleRelations, flags_setter: &mut F) @@ -224,14 +224,12 @@ fn matches_complex_selector_internal(selector: &ComplexSelector, where E: Element, F: FnMut(&E, ElementSelectorFlags), { - let matches_all_simple_selectors = selector.compound_selector.iter().all(|simple| { + let matches_all_simple_selectors = selector_iter.all(|simple| { matches_simple_selector(simple, element, relations, flags_setter) }); - let siblings = selector.next.as_ref().map_or(false, |&(_, combinator)| { - matches!(combinator, Combinator::NextSibling | Combinator::LaterSibling) - }); - + let combinator = selector_iter.next_sequence(); + let siblings = combinator.map_or(false, |c| c.is_sibling()); if siblings { flags_setter(element, HAS_SLOW_SELECTOR_LATER_SIBLINGS); } @@ -240,9 +238,9 @@ fn matches_complex_selector_internal(selector: &ComplexSelector, return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling; } - match selector.next { + match combinator { None => SelectorMatchingResult::Matched, - Some((ref next_selector, combinator)) => { + Some(c) => { let (mut next_element, candidate_not_found) = if siblings { (element.prev_sibling_element(), SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant) @@ -256,11 +254,11 @@ fn matches_complex_selector_internal(selector: &ComplexSelector, None => return candidate_not_found, Some(next_element) => next_element, }; - let result = matches_complex_selector_internal(&**next_selector, + let result = matches_complex_selector_internal(selector_iter.clone(), &element, relations, flags_setter); - match (result, combinator) { + match (result, c) { // Return the status immediately. (SelectorMatchingResult::Matched, _) => return result, (SelectorMatchingResult::NotMatchedGlobally, _) => return result, @@ -299,7 +297,7 @@ fn matches_complex_selector_internal(selector: &ComplexSelector, /// Determines whether the given element matches the given single selector. #[inline] fn matches_simple_selector( - selector: &SimpleSelector, + selector: &Component, element: &E, relations: &mut StyleRelations, flags_setter: &mut F) @@ -319,109 +317,110 @@ fn matches_simple_selector( } match *selector { - SimpleSelector::LocalName(LocalName { ref name, ref lower_name }) => { + Component::Combinator(_) => unreachable!(), + Component::LocalName(LocalName { ref name, ref lower_name }) => { let name = if element.is_html_element_in_html_document() { lower_name } else { name }; element.get_local_name() == name.borrow() } - SimpleSelector::Namespace(ref namespace) => { + Component::Namespace(ref namespace) => { element.get_namespace() == namespace.url.borrow() } // TODO: case-sensitivity depends on the document type and quirks mode - SimpleSelector::ID(ref id) => { + Component::ID(ref id) => { relation_if!(element.get_id().map_or(false, |attr| attr == *id), AFFECTED_BY_ID_SELECTOR) } - SimpleSelector::Class(ref class) => { + Component::Class(ref class) => { element.has_class(class) } - SimpleSelector::AttrExists(ref attr) => { + Component::AttrExists(ref attr) => { element.match_attr_has(attr) } - SimpleSelector::AttrEqual(ref attr, ref value, case_sensitivity) => { + Component::AttrEqual(ref attr, ref value, case_sensitivity) => { match case_sensitivity { CaseSensitivity::CaseSensitive => element.match_attr_equals(attr, value), CaseSensitivity::CaseInsensitive => element.match_attr_equals_ignore_ascii_case(attr, value), } } - SimpleSelector::AttrIncludes(ref attr, ref value) => { + Component::AttrIncludes(ref attr, ref value) => { element.match_attr_includes(attr, value) } - SimpleSelector::AttrDashMatch(ref attr, ref value) => { + Component::AttrDashMatch(ref attr, ref value) => { element.match_attr_dash(attr, value) } - SimpleSelector::AttrPrefixMatch(ref attr, ref value) => { + Component::AttrPrefixMatch(ref attr, ref value) => { element.match_attr_prefix(attr, value) } - SimpleSelector::AttrSubstringMatch(ref attr, ref value) => { + Component::AttrSubstringMatch(ref attr, ref value) => { element.match_attr_substring(attr, value) } - SimpleSelector::AttrSuffixMatch(ref attr, ref value) => { + Component::AttrSuffixMatch(ref attr, ref value) => { element.match_attr_suffix(attr, value) } - SimpleSelector::AttrIncludesNeverMatch(..) | - SimpleSelector::AttrPrefixNeverMatch(..) | - SimpleSelector::AttrSubstringNeverMatch(..) | - SimpleSelector::AttrSuffixNeverMatch(..) => { + Component::AttrIncludesNeverMatch(..) | + Component::AttrPrefixNeverMatch(..) | + Component::AttrSubstringNeverMatch(..) | + Component::AttrSuffixNeverMatch(..) => { false } - SimpleSelector::NonTSPseudoClass(ref pc) => { + Component::NonTSPseudoClass(ref pc) => { relation_if!(element.match_non_ts_pseudo_class(pc, relations, flags_setter), AFFECTED_BY_STATE) } - SimpleSelector::FirstChild => { + Component::FirstChild => { relation_if!(matches_first_child(element, flags_setter), AFFECTED_BY_CHILD_INDEX) } - SimpleSelector::LastChild => { + Component::LastChild => { relation_if!(matches_last_child(element, flags_setter), AFFECTED_BY_CHILD_INDEX) } - SimpleSelector::OnlyChild => { + Component::OnlyChild => { relation_if!(matches_first_child(element, flags_setter) && matches_last_child(element, flags_setter), AFFECTED_BY_CHILD_INDEX) } - SimpleSelector::Root => { + Component::Root => { // We never share styles with an element with no parent, so no point // in creating a new StyleRelation. element.is_root() } - SimpleSelector::Empty => { + Component::Empty => { flags_setter(element, HAS_EMPTY_SELECTOR); relation_if!(element.is_empty(), AFFECTED_BY_EMPTY) } - SimpleSelector::NthChild(a, b) => { + Component::NthChild(a, b) => { relation_if!(matches_generic_nth_child(element, a, b, false, false, flags_setter), AFFECTED_BY_CHILD_INDEX) } - SimpleSelector::NthLastChild(a, b) => { + Component::NthLastChild(a, b) => { relation_if!(matches_generic_nth_child(element, a, b, false, true, flags_setter), AFFECTED_BY_CHILD_INDEX) } - SimpleSelector::NthOfType(a, b) => { + Component::NthOfType(a, b) => { relation_if!(matches_generic_nth_child(element, a, b, true, false, flags_setter), AFFECTED_BY_CHILD_INDEX) } - SimpleSelector::NthLastOfType(a, b) => { + Component::NthLastOfType(a, b) => { relation_if!(matches_generic_nth_child(element, a, b, true, true, flags_setter), AFFECTED_BY_CHILD_INDEX) } - SimpleSelector::FirstOfType => { + Component::FirstOfType => { relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags_setter), AFFECTED_BY_CHILD_INDEX) } - SimpleSelector::LastOfType => { + Component::LastOfType => { relation_if!(matches_generic_nth_child(element, 0, 1, true, true, flags_setter), AFFECTED_BY_CHILD_INDEX) } - SimpleSelector::OnlyOfType => { + Component::OnlyOfType => { 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) => { + Component::Negation(ref negated) => { !negated.iter().all(|s| { - match matches_complex_selector_internal(s, + match matches_complex_selector_internal(s.iter(), element, relations, flags_setter) { diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index 8f0ee4c3aa8..8ee4378cdc7 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -2,15 +2,18 @@ * 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 arcslice::ArcSlice; use cssparser::{Token, Parser as CssParser, parse_nth, ToCss, serialize_identifier, CssStringWriter}; use precomputed_hash::PrecomputedHash; +use smallvec::SmallVec; use std::ascii::AsciiExt; use std::borrow::{Borrow, Cow}; use std::cmp; use std::fmt::{self, Display, Debug, Write}; use std::hash::Hash; +use std::iter::Rev; use std::ops::Add; -use std::sync::Arc; +use std::slice; use tree::SELECTOR_WHITESPACE; use visitor::SelectorVisitor; @@ -132,7 +135,7 @@ const NUM_ANCESTOR_HASHES: usize = 4; #[derive(PartialEq, Eq, Hash, Clone)] pub struct SelectorInner { /// The selector data. - pub complex: Arc>, + pub complex: ComplexSelector, /// Ancestor hashes for the bloom filter. We precompute these and store /// them inline to optimize cache performance during selector matching. /// This matters a lot. @@ -140,15 +143,14 @@ pub struct SelectorInner { } impl SelectorInner { - pub fn new(c: Arc>) -> Self { + pub fn new(c: ComplexSelector) -> 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()); + let mut hash_iter = c.iter_ancestors() + .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, @@ -162,6 +164,12 @@ impl SelectorInner { ancestor_hashes: hashes, } } + + /// Creates a SelectorInner from a Vec of Components. Used in tests. + pub fn from_vec(vec: Vec>) -> Self { + let complex = ComplexSelector::from_vec(vec); + Self::new(complex) + } } #[derive(PartialEq, Eq, Hash, Clone)] @@ -194,25 +202,22 @@ impl SelectorMethods for ComplexSelector { fn visit(&self, visitor: &mut V) -> bool where V: SelectorVisitor, { - let mut current = self; + let mut current = self.iter(); let mut combinator = None; loop { - if !visitor.visit_complex_selector(current, combinator) { + if !visitor.visit_complex_selector(current.clone(), combinator) { return false; } - for selector in ¤t.compound_selector { + for selector in &mut current { if !selector.visit(visitor) { return false; } } - match current.next { - Some((ref next, next_combinator)) => { - current = next; - combinator = Some(next_combinator); - } - None => break, + combinator = current.next_sequence(); + if combinator.is_none() { + break; } } @@ -220,20 +225,20 @@ impl SelectorMethods for ComplexSelector { } } -impl SelectorMethods for SimpleSelector { +impl SelectorMethods for Component { type Impl = Impl; fn visit(&self, visitor: &mut V) -> bool where V: SelectorVisitor, { - use self::SimpleSelector::*; + use self::Component::*; if !visitor.visit_simple_selector(self) { return false; } match *self { Negation(ref negated) => { - for selector in negated { + for selector in negated.iter() { if !selector.visit(visitor) { return false; } @@ -262,38 +267,139 @@ impl SelectorMethods for SimpleSelector { } } +/// A ComplexSelectors stores a sequence of simple selectors and combinators. The +/// iterator classes allow callers to iterate at either the raw sequence level or +/// at the level of sequences of simple selectors separated by combinators. Most +/// callers want the higher-level iterator. +/// +/// We store selectors internally left-to-right (in parsing order), but the +/// canonical iteration order is right-to-left (selector matching order). The +/// iterators abstract over these details. #[derive(Clone, Eq, Hash, PartialEq)] -pub struct ComplexSelector { - pub compound_selector: Vec>, - pub next: Option<(Arc>, Combinator)>, // c.next is left of c -} +pub struct ComplexSelector(ArcSlice>); -struct AncestorIterator<'a, Impl: 'a + SelectorImpl> { - curr: Option<&'a Arc>>, -} - -impl<'a, Impl: SelectorImpl> Iterator for AncestorIterator<'a, Impl> { - type Item = &'a Arc>; - fn next(&mut self) -> Option { - 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; - } +impl ComplexSelector { + /// Returns an iterator over the next sequence of simple selectors. When + /// a combinator is reached, the iterator will return None, and + /// next_sequence() may be called to continue to the next sequence. + pub fn iter(&self) -> SelectorIter { + SelectorIter { + iter: self.iter_raw(), + next_combinator: None, } + } - self.curr + /// Returns an iterator over the entire sequence of simple selectors and combinators, + /// from right to left. + pub fn iter_raw(&self) -> Rev>> { + self.iter_raw_rev().rev() + } + + /// Returns an iterator over the entire sequence of simple selectors and combinators, + /// from left to right. + pub fn iter_raw_rev(&self) -> slice::Iter> { + self.0.iter() + } + + /// Returns an iterator over ancestor simple selectors. All combinators and + /// non-ancestor simple selectors will be skipped. + pub fn iter_ancestors(&self) -> AncestorIter { + AncestorIter::new(self.iter()) + } + + /// Returns a ComplexSelector identical to |self| but with the rightmost |index| + /// entries removed. + pub fn slice_from(&self, index: usize) -> Self { + // Note that we convert the slice_from to slice_to because selectors are + // stored left-to-right but logical order is right-to-left. + ComplexSelector(self.0.clone().slice_to(self.0.len() - index)) + } + + /// Creates a ComplexSelector from a vec of Components. Used in tests. + pub fn from_vec(vec: Vec>) -> Self { + ComplexSelector(ArcSlice::new(vec.into_boxed_slice())) } } -fn iter_ancestors(sel: &Arc>) -> AncestorIterator { - AncestorIterator { - curr: Some(sel) +pub struct SelectorIter<'a, Impl: 'a + SelectorImpl> { + iter: Rev>>, + next_combinator: Option, +} + +// NB: Deriving this doesn't work for some reason, because it expects Impl to +// implement Clone. +impl<'a, Impl: 'a + SelectorImpl> Clone for SelectorIter<'a, Impl> { + fn clone(&self) -> Self { + SelectorIter { + iter: self.iter.clone(), + next_combinator: self.next_combinator.clone(), + } + } +} + +impl<'a, Impl: 'a + SelectorImpl> SelectorIter<'a, Impl> { + /// Prepares this iterator to point to the next sequence to the left, + /// returning the combinator if the sequence was found. + pub fn next_sequence(&mut self) -> Option { + self.next_combinator.take() + } +} + +impl<'a, Impl: SelectorImpl> Iterator for SelectorIter<'a, Impl> { + type Item = &'a Component; + fn next(&mut self) -> Option { + debug_assert!(self.next_combinator.is_none(), "Should call take_combinator!"); + match self.iter.next() { + None => None, + Some(&Component::Combinator(c)) => { + self.next_combinator = Some(c); + None + }, + Some(x) => Some(x), + } + } +} + +/// An iterator over all simple selectors belonging to ancestors. +pub struct AncestorIter<'a, Impl: 'a + SelectorImpl>(SelectorIter<'a, Impl>); +impl<'a, Impl: 'a + SelectorImpl> AncestorIter<'a, Impl> { + /// Creates an AncestorIter. The passed-in iterator is assumed to point to + /// the beginning of the child sequence, which will be skipped. + fn new(inner: SelectorIter<'a, Impl>) -> Self { + let mut result = AncestorIter(inner); + result.skip_until_ancestor(); + result + } + + /// Skips a sequence of simple selectors and all subsequent sequences until an + /// ancestor combinator is reached. + fn skip_until_ancestor(&mut self) { + loop { + while let Some(_) = self.0.next() {} + if self.0.next_sequence().map_or(true, |x| x.is_ancestor()) { + break; + } + } + } +} + +impl<'a, Impl: SelectorImpl> Iterator for AncestorIter<'a, Impl> { + type Item = &'a Component; + fn next(&mut self) -> Option { + // Grab the next simple selector in the sequence if available. + let next = self.0.next(); + if next.is_some() { + return next; + } + + // See if there are more sequences. If so, skip any non-ancestor sequences. + if let Some(combinator) = self.0.next_sequence() { + if !combinator.is_ancestor() { + self.skip_until_ancestor(); + } + } + + self.0.next() } } @@ -305,8 +411,25 @@ pub enum Combinator { LaterSibling, // ~ } +impl Combinator { + /// Returns true if this combinator is a child or descendant combinator. + pub fn is_ancestor(&self) -> bool { + matches!(*self, Combinator::Child | Combinator::Descendant) + } + + /// Returns true if this combinator is a next- or later-sibling combinator. + pub fn is_sibling(&self) -> bool { + matches!(*self, Combinator::NextSibling | Combinator::LaterSibling) + } +} + +/// A CSS simple selector or combinator. We store both in the same enum for +/// optimal packing and cache performance, see [1]. +/// +/// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1357973 #[derive(Eq, PartialEq, Clone, Hash)] -pub enum SimpleSelector { +pub enum Component { + Combinator(Combinator), ID(Impl::Identifier), Class(Impl::ClassName), LocalName(LocalName), @@ -327,7 +450,7 @@ pub enum SimpleSelector { AttrSuffixNeverMatch(AttrSelector, Impl::AttrValue), // empty value // Pseudo-classes - Negation(Vec>>), + Negation(Box<[ComplexSelector]>), FirstChild, LastChild, OnlyChild, Root, Empty, @@ -342,11 +465,11 @@ pub enum SimpleSelector { // ... } -impl SimpleSelector { +impl Component { /// Compute the ancestor hash to check against the bloom filter. fn ancestor_hash(&self) -> Option { match *self { - SimpleSelector::LocalName(LocalName { ref name, ref lower_name }) => { + Component::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. @@ -356,18 +479,23 @@ impl SimpleSelector { None } }, - SimpleSelector::Namespace(ref namespace) => { + Component::Namespace(ref namespace) => { Some(namespace.url.precomputed_hash()) }, - SimpleSelector::ID(ref id) => { + Component::ID(ref id) => { Some(id.precomputed_hash()) }, - SimpleSelector::Class(ref class) => { + Component::Class(ref class) => { Some(class.precomputed_hash()) }, _ => None, } } + + /// Returns true if this is a combinator. + pub fn is_combinator(&self) -> bool { + matches!(*self, Component::Combinator(_)) + } } #[derive(Eq, PartialEq, Clone, Hash, Copy, Debug)] @@ -427,7 +555,7 @@ impl Debug for SelectorInner { impl Debug for ComplexSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_css(f) } } -impl Debug for SimpleSelector { +impl Debug for Component { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_css(f) } } impl Debug for AttrSelector { @@ -466,31 +594,11 @@ impl ToCss for Selector { impl ToCss for ComplexSelector { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - use smallvec::SmallVec; - let mut current = self; - let mut nodes = SmallVec::<[&Self;8]>::new(); - nodes.push(current); - - loop { - match current.next { - None => break, - Some((ref next, _)) => { - current = &**next; - nodes.push(next); - } - } + for item in self.iter_raw_rev() { + item.to_css(dest)?; } - for selector in nodes.iter().rev() { - if let Some((_, ref combinator)) = selector.next { - combinator.to_css(dest)?; - } - - for simple in &selector.compound_selector { - simple.to_css(dest)?; - } - } - Ok(()) + Ok(()) } } @@ -505,10 +613,13 @@ impl ToCss for Combinator { } } -impl ToCss for SimpleSelector { +impl ToCss for Component { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - use self::SimpleSelector::*; + use self::Component::*; match *self { + Combinator(ref c) => { + c.to_css(dest) + } ID(ref s) => { dest.write_char('#')?; display_to_css_identifier(s, dest) @@ -699,46 +810,47 @@ fn specificity(complex_selector: &ComplexSelector, specificity.into() } -fn complex_selector_specificity(mut selector: &ComplexSelector) +fn complex_selector_specificity(selector: &ComplexSelector) -> Specificity where Impl: SelectorImpl { - fn compound_selector_specificity(compound_selector: &[SimpleSelector], + fn compound_selector_specificity(selector_iter: &mut SelectorIter, specificity: &mut Specificity) where Impl: SelectorImpl { - for simple_selector in compound_selector.iter() { + for simple_selector in selector_iter { match *simple_selector { - SimpleSelector::LocalName(..) => + Component::Combinator(..) => unreachable!(), + Component::LocalName(..) => specificity.element_selectors += 1, - SimpleSelector::ID(..) => + Component::ID(..) => specificity.id_selectors += 1, - SimpleSelector::Class(..) | - SimpleSelector::AttrExists(..) | - SimpleSelector::AttrEqual(..) | - SimpleSelector::AttrIncludes(..) | - SimpleSelector::AttrDashMatch(..) | - SimpleSelector::AttrPrefixMatch(..) | - SimpleSelector::AttrSubstringMatch(..) | - SimpleSelector::AttrSuffixMatch(..) | + Component::Class(..) | + Component::AttrExists(..) | + Component::AttrEqual(..) | + Component::AttrIncludes(..) | + Component::AttrDashMatch(..) | + Component::AttrPrefixMatch(..) | + Component::AttrSubstringMatch(..) | + Component::AttrSuffixMatch(..) | - SimpleSelector::AttrIncludesNeverMatch(..) | - SimpleSelector::AttrPrefixNeverMatch(..) | - SimpleSelector::AttrSubstringNeverMatch(..) | - SimpleSelector::AttrSuffixNeverMatch(..) | + Component::AttrIncludesNeverMatch(..) | + Component::AttrPrefixNeverMatch(..) | + Component::AttrSubstringNeverMatch(..) | + Component::AttrSuffixNeverMatch(..) | - SimpleSelector::FirstChild | SimpleSelector::LastChild | - SimpleSelector::OnlyChild | SimpleSelector::Root | - SimpleSelector::Empty | - SimpleSelector::NthChild(..) | - SimpleSelector::NthLastChild(..) | - SimpleSelector::NthOfType(..) | - SimpleSelector::NthLastOfType(..) | - SimpleSelector::FirstOfType | SimpleSelector::LastOfType | - SimpleSelector::OnlyOfType | - SimpleSelector::NonTSPseudoClass(..) => + Component::FirstChild | Component::LastChild | + Component::OnlyChild | Component::Root | + Component::Empty | + Component::NthChild(..) | + Component::NthLastChild(..) | + Component::NthOfType(..) | + Component::NthLastOfType(..) | + Component::FirstOfType | Component::LastOfType | + Component::OnlyOfType | + Component::NonTSPseudoClass(..) => specificity.class_like_selectors += 1, - SimpleSelector::Namespace(..) => (), - SimpleSelector::Negation(ref negated) => { + Component::Namespace(..) => (), + Component::Negation(ref negated) => { let max = negated.iter().map(|s| complex_selector_specificity(&s)) .max().unwrap(); @@ -749,16 +861,11 @@ fn complex_selector_specificity(mut selector: &ComplexSelector) } let mut specificity = Default::default(); - compound_selector_specificity(&selector.compound_selector, - &mut specificity); + let mut iter = selector.iter(); loop { - match selector.next { - None => break, - Some((ref next_selector, _)) => { - selector = &**next_selector; - compound_selector_specificity(&selector.compound_selector, - &mut specificity) - } + compound_selector_specificity(&mut iter, &mut specificity); + if iter.next_sequence().is_none() { + break; } } specificity @@ -775,21 +882,37 @@ fn parse_selector(parser: &P, input: &mut CssParser) -> Result 8 entries, we save two reallocations. +type ParseVec = SmallVec<[Component; 8]>; + fn parse_complex_selector_and_pseudo_element( parser: &P, input: &mut CssParser) -> Result<(ComplexSelector, Option), ()> where P: Parser, Impl: SelectorImpl { - let (first, mut pseudo_element) = parse_compound_selector(parser, input)?; - let mut complex = ComplexSelector { compound_selector: first, next: None }; + let mut sequence = ParseVec::new(); + let mut pseudo_element; + 'outer_loop: loop { + // Parse a sequence of simple selectors. + pseudo_element = parse_compound_selector(parser, input, &mut sequence)?; + if pseudo_element.is_some() { + break; + } - 'outer_loop: while pseudo_element.is_none() { + // Parse a combinator. let combinator; let mut any_whitespace = false; loop { @@ -820,14 +943,10 @@ fn parse_complex_selector_and_pseudo_element( } } } - let (compound_selector, pseudo) = parse_compound_selector(parser, input)?; - complex = ComplexSelector { - compound_selector: compound_selector, - next: Some((Arc::new(complex), combinator)) - }; - pseudo_element = pseudo; + sequence.push(Component::Combinator(combinator)); } + let complex = ComplexSelector(ArcSlice::new(sequence.into_vec().into_boxed_slice())); Ok((complex, pseudo_element)) } @@ -848,37 +967,36 @@ impl ComplexSelector { /// * `Err(())`: Invalid selector, abort /// * `Ok(None)`: Not a type selector, could be something else. `input` was not consumed. /// * `Ok(Some(vec))`: Length 0 (`*|*`), 1 (`*|E` or `ns|*`) or 2 (`|E` or `ns|E`) -fn parse_type_selector(parser: &P, input: &mut CssParser) - -> Result>>, ()> +fn parse_type_selector(parser: &P, input: &mut CssParser, sequence: &mut ParseVec) + -> Result where P: Parser, Impl: SelectorImpl { match parse_qualified_name(parser, input, /* in_attr_selector = */ false)? { - None => Ok(None), + None => Ok(false), Some((namespace, local_name)) => { - let mut compound_selector = vec!(); match namespace { NamespaceConstraint::Specific(ns) => { - compound_selector.push(SimpleSelector::Namespace(ns)) + sequence.push(Component::Namespace(ns)) }, NamespaceConstraint::Any => (), } match local_name { Some(name) => { - compound_selector.push(SimpleSelector::LocalName(LocalName { + sequence.push(Component::LocalName(LocalName { lower_name: from_ascii_lowercase(&name), name: from_cow_str(name), })) } None => (), } - Ok(Some(compound_selector)) + Ok(true) } } } #[derive(Debug)] enum SimpleSelectorParseResult { - SimpleSelector(SimpleSelector), + SimpleSelector(Component), PseudoElement(Impl::PseudoElement), } @@ -964,7 +1082,7 @@ fn parse_qualified_name<'i, 't, P, Impl> fn parse_attribute_selector(parser: &P, input: &mut CssParser) - -> Result, ()> + -> Result, ()> where P: Parser, Impl: SelectorImpl { let attr = match parse_qualified_name(parser, input, /* in_attr_selector = */ true)? { @@ -979,53 +1097,53 @@ fn parse_attribute_selector(parser: &P, input: &mut CssParser) match input.next() { // [foo] - Err(()) => Ok(SimpleSelector::AttrExists(attr)), + Err(()) => Ok(Component::AttrExists(attr)), // [foo=bar] Ok(Token::Delim('=')) => { let value = input.expect_ident_or_string()?; let flags = parse_attribute_flags(input)?; - Ok(SimpleSelector::AttrEqual(attr, from_cow_str(value), flags)) + Ok(Component::AttrEqual(attr, from_cow_str(value), flags)) } // [foo~=bar] Ok(Token::IncludeMatch) => { let value = input.expect_ident_or_string()?; if value.is_empty() || value.contains(SELECTOR_WHITESPACE) { - Ok(SimpleSelector::AttrIncludesNeverMatch(attr, from_cow_str(value))) + Ok(Component::AttrIncludesNeverMatch(attr, from_cow_str(value))) } else { - Ok(SimpleSelector::AttrIncludes(attr, from_cow_str(value))) + Ok(Component::AttrIncludes(attr, from_cow_str(value))) } } // [foo|=bar] Ok(Token::DashMatch) => { let value = input.expect_ident_or_string()?; - Ok(SimpleSelector::AttrDashMatch(attr, from_cow_str(value))) + Ok(Component::AttrDashMatch(attr, from_cow_str(value))) } // [foo^=bar] Ok(Token::PrefixMatch) => { let value = input.expect_ident_or_string()?; if value.is_empty() { - Ok(SimpleSelector::AttrPrefixNeverMatch(attr, from_cow_str(value))) + Ok(Component::AttrPrefixNeverMatch(attr, from_cow_str(value))) } else { - Ok(SimpleSelector::AttrPrefixMatch(attr, from_cow_str(value))) + Ok(Component::AttrPrefixMatch(attr, from_cow_str(value))) } } // [foo*=bar] Ok(Token::SubstringMatch) => { let value = input.expect_ident_or_string()?; if value.is_empty() { - Ok(SimpleSelector::AttrSubstringNeverMatch(attr, from_cow_str(value))) + Ok(Component::AttrSubstringNeverMatch(attr, from_cow_str(value))) } else { - Ok(SimpleSelector::AttrSubstringMatch(attr, from_cow_str(value))) + Ok(Component::AttrSubstringMatch(attr, from_cow_str(value))) } } // [foo$=bar] Ok(Token::SuffixMatch) => { let value = input.expect_ident_or_string()?; if value.is_empty() { - Ok(SimpleSelector::AttrSuffixNeverMatch(attr, from_cow_str(value))) + Ok(Component::AttrSuffixNeverMatch(attr, from_cow_str(value))) } else { - Ok(SimpleSelector::AttrSuffixMatch(attr, from_cow_str(value))) + Ok(Component::AttrSuffixMatch(attr, from_cow_str(value))) } } _ => Err(()) @@ -1048,11 +1166,11 @@ fn parse_attribute_flags(input: &mut CssParser) -> Result { /// implied "|*" type selector.) fn parse_negation(parser: &P, input: &mut CssParser) - -> Result, ()> + -> Result, ()> where P: Parser, Impl: SelectorImpl { - input.parse_comma_separated(|input| ComplexSelector::parse(parser, input).map(Arc::new)) - .map(SimpleSelector::Negation) + input.parse_comma_separated(|input| ComplexSelector::parse(parser, input)) + .map(|v| Component::Negation(v.into_boxed_slice())) } /// simple_selector_sequence @@ -1062,8 +1180,9 @@ fn parse_negation(parser: &P, /// `Err(())` means invalid selector fn parse_compound_selector( parser: &P, - input: &mut CssParser) - -> Result<(Vec>, Option), ()> + input: &mut CssParser, + mut sequence: &mut ParseVec) + -> Result, ()> where P: Parser, Impl: SelectorImpl { // Consume any leading whitespace. @@ -1075,28 +1194,26 @@ fn parse_compound_selector( } } let mut empty = true; - let mut compound_selector = match parse_type_selector(parser, input)? { - None => { - match parser.default_namespace() { - // If there was no explicit type selector, but there is a - // default namespace, there is an implicit "|*" type - // selector. - Some(url) => vec![SimpleSelector::Namespace(Namespace { - prefix: None, - url: url - })], - None => vec![], - } + if !parse_type_selector(parser, input, &mut sequence)? { + if let Some(url) = parser.default_namespace() { + // If there was no explicit type selector, but there is a + // default namespace, there is an implicit "|*" type + // selector. + sequence.push(Component::Namespace(Namespace { + prefix: None, + url: url + })); } - Some(s) => { empty = false; s } - }; + } else { + empty = false; + } let mut pseudo_element = None; loop { match parse_one_simple_selector(parser, input, /* inside_negation = */ false)? { None => break, Some(SimpleSelectorParseResult::SimpleSelector(s)) => { - compound_selector.push(s); + sequence.push(s); empty = false } Some(SimpleSelectorParseResult::PseudoElement(p)) => { @@ -1110,7 +1227,7 @@ fn parse_compound_selector( // An empty selector is invalid. Err(()) } else { - Ok((compound_selector, pseudo_element)) + Ok(pseudo_element) } } @@ -1118,14 +1235,14 @@ fn parse_functional_pseudo_class(parser: &P, input: &mut CssParser, name: Cow, inside_negation: bool) - -> Result, ()> + -> Result, ()> where P: Parser, Impl: SelectorImpl { match_ignore_ascii_case! { &name, - "nth-child" => return parse_nth_pseudo_class(input, SimpleSelector::NthChild), - "nth-of-type" => return parse_nth_pseudo_class(input, SimpleSelector::NthOfType), - "nth-last-child" => return parse_nth_pseudo_class(input, SimpleSelector::NthLastChild), - "nth-last-of-type" => return parse_nth_pseudo_class(input, SimpleSelector::NthLastOfType), + "nth-child" => return parse_nth_pseudo_class(input, Component::NthChild), + "nth-of-type" => return parse_nth_pseudo_class(input, Component::NthOfType), + "nth-last-child" => return parse_nth_pseudo_class(input, Component::NthLastChild), + "nth-last-of-type" => return parse_nth_pseudo_class(input, Component::NthLastOfType), "not" => { if inside_negation { return Err(()) @@ -1135,13 +1252,13 @@ fn parse_functional_pseudo_class(parser: &P, _ => {} } P::parse_non_ts_functional_pseudo_class(parser, name, input) - .map(SimpleSelector::NonTSPseudoClass) + .map(Component::NonTSPseudoClass) } fn parse_nth_pseudo_class(input: &mut CssParser, selector: F) - -> Result, ()> -where Impl: SelectorImpl, F: FnOnce(i32, i32) -> SimpleSelector { + -> Result, ()> +where Impl: SelectorImpl, F: FnOnce(i32, i32) -> Component { let (a, b) = parse_nth(input)?; Ok(selector(a, b)) } @@ -1161,13 +1278,13 @@ fn parse_one_simple_selector(parser: &P, let start_position = input.position(); match input.next_including_whitespace() { Ok(Token::IDHash(id)) => { - let id = SimpleSelector::ID(from_cow_str(id)); + let id = Component::ID(from_cow_str(id)); Ok(Some(SimpleSelectorParseResult::SimpleSelector(id))) } Ok(Token::Delim('.')) => { match input.next_including_whitespace() { Ok(Token::Ident(class)) => { - let class = SimpleSelector::Class(from_cow_str(class)); + let class = Component::Class(from_cow_str(class)); Ok(Some(SimpleSelectorParseResult::SimpleSelector(class))) } _ => Err(()), @@ -1218,22 +1335,22 @@ fn parse_one_simple_selector(parser: &P, } } -fn parse_simple_pseudo_class(parser: &P, name: Cow) -> Result, ()> +fn parse_simple_pseudo_class(parser: &P, name: Cow) -> Result, ()> where P: Parser, Impl: SelectorImpl { (match_ignore_ascii_case! { &name, - "first-child" => Ok(SimpleSelector::FirstChild), - "last-child" => Ok(SimpleSelector::LastChild), - "only-child" => Ok(SimpleSelector::OnlyChild), - "root" => Ok(SimpleSelector::Root), - "empty" => Ok(SimpleSelector::Empty), - "first-of-type" => Ok(SimpleSelector::FirstOfType), - "last-of-type" => Ok(SimpleSelector::LastOfType), - "only-of-type" => Ok(SimpleSelector::OnlyOfType), + "first-child" => Ok(Component::FirstChild), + "last-child" => Ok(Component::LastChild), + "only-child" => Ok(Component::OnlyChild), + "root" => Ok(Component::Root), + "empty" => Ok(Component::Empty), + "first-of-type" => Ok(Component::FirstOfType), + "last-of-type" => Ok(Component::LastOfType), + "only-of-type" => Ok(Component::OnlyOfType), _ => Err(()) }).or_else(|()| { P::parse_non_ts_pseudo_class(parser, name) - .map(SimpleSelector::NonTSPseudoClass) + .map(Component::NonTSPseudoClass) }) } @@ -1244,7 +1361,6 @@ pub mod tests { use std::borrow::Cow; use std::collections::HashMap; use std::fmt; - use std::sync::Arc; use super::*; #[derive(PartialEq, Clone, Debug, Hash, Eq)] @@ -1408,57 +1524,45 @@ pub mod tests { assert_eq!(parse(":lang(4)"), Err(())) ; assert_eq!(parse(":lang(en US)"), Err(())) ; assert_eq!(parse("EeÉ"), Ok(SelectorList(vec!(Selector { - inner: SelectorInner::new(Arc::new(ComplexSelector { - compound_selector: vec!(SimpleSelector::LocalName(LocalName { + inner: SelectorInner::from_vec(vec!(Component::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 { - inner: SelectorInner::new(Arc::new(ComplexSelector { - compound_selector: vec![ - SimpleSelector::Class(DummyAtom::from("foo")), - SimpleSelector::NonTSPseudoClass(PseudoClass::Lang("en-US".to_owned())) - ], - next: None, - })), + inner: SelectorInner::from_vec(vec![ + Component::Class(DummyAtom::from("foo")), + Component::NonTSPseudoClass(PseudoClass::Lang("en-US".to_owned())) + ]), pseudo_element: None, specificity: specificity(0, 2, 0), })))); assert_eq!(parse("#bar"), Ok(SelectorList(vec!(Selector { - inner: SelectorInner::new(Arc::new(ComplexSelector { - compound_selector: vec!(SimpleSelector::ID(DummyAtom::from("bar"))), - next: None, - })), + inner: SelectorInner::from_vec(vec!(Component::ID(DummyAtom::from("bar")))), pseudo_element: None, specificity: specificity(1, 0, 0), })))); assert_eq!(parse("e.foo#bar"), Ok(SelectorList(vec!(Selector { - inner: SelectorInner::new(Arc::new(ComplexSelector { - compound_selector: vec!(SimpleSelector::LocalName(LocalName { + inner: SelectorInner::from_vec(vec!(Component::LocalName(LocalName { name: DummyAtom::from("e"), lower_name: DummyAtom::from("e") }), - SimpleSelector::Class(DummyAtom::from("foo")), - SimpleSelector::ID(DummyAtom::from("bar"))), - next: None, - })), + Component::Class(DummyAtom::from("foo")), + Component::ID(DummyAtom::from("bar")))), pseudo_element: None, specificity: specificity(1, 1, 1), })))); assert_eq!(parse("e.foo #bar"), Ok(SelectorList(vec!(Selector { - 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)), - })), + inner: SelectorInner::from_vec(vec!( + Component::LocalName(LocalName { + name: DummyAtom::from("e"), + lower_name: DummyAtom::from("e") + }), + Component::Class(DummyAtom::from("foo")), + Component::Combinator(Combinator::Descendant), + Component::ID(DummyAtom::from("bar")), + )), pseudo_element: None, specificity: specificity(1, 1, 1), })))); @@ -1466,36 +1570,31 @@ pub mod tests { // https://github.com/mozilla/servo/pull/1652 let mut parser = DummyParser::default(); assert_eq!(parse_ns("[Foo]", &parser), Ok(SelectorList(vec!(Selector { - inner: SelectorInner::new(Arc::new(ComplexSelector { - compound_selector: vec!(SimpleSelector::AttrExists(AttrSelector { + inner: SelectorInner::from_vec(vec!( + Component::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 { - inner: SelectorInner::new(Arc::new(ComplexSelector { - compound_selector: vec![ - SimpleSelector::Namespace(Namespace { + inner: SelectorInner::from_vec( + vec![ + Component::Namespace(Namespace { prefix: Some(DummyAtom("svg".into())), url: SVG.into(), }), - SimpleSelector::LocalName(LocalName { + Component::LocalName(LocalName { name: DummyAtom::from("circle"), lower_name: DummyAtom::from("circle"), }) - ], - next: None, - })), + ]), pseudo_element: None, specificity: specificity(0, 0, 1), }]))); @@ -1505,13 +1604,13 @@ pub mod tests { // https://github.com/servo/rust-selectors/pull/82 parser.default_ns = Some(MATHML.into()); assert_eq!(parse_ns("[Foo]", &parser), Ok(SelectorList(vec!(Selector { - inner: SelectorInner::new(Arc::new(ComplexSelector { - compound_selector: vec![ - SimpleSelector::Namespace(Namespace { + inner: SelectorInner::from_vec( + vec![ + Component::Namespace(Namespace { prefix: None, url: MATHML.into(), }), - SimpleSelector::AttrExists(AttrSelector { + Component::AttrExists(AttrSelector { name: DummyAtom::from("Foo"), lower_name: DummyAtom::from("foo"), namespace: NamespaceConstraint::Specific(Namespace { @@ -1519,33 +1618,29 @@ pub mod tests { 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 { - inner: SelectorInner::new(Arc::new(ComplexSelector { - compound_selector: vec!( - SimpleSelector::Namespace(Namespace { + inner: SelectorInner::from_vec( + vec!( + Component::Namespace(Namespace { prefix: None, url: MATHML.into(), }), - SimpleSelector::LocalName(LocalName { + Component::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 { - inner: SelectorInner::new(Arc::new(ComplexSelector { - compound_selector: vec![ - SimpleSelector::AttrDashMatch(AttrSelector { + inner: SelectorInner::from_vec( + vec![ + Component::AttrDashMatch(AttrSelector { name: DummyAtom::from("attr"), lower_name: DummyAtom::from("attr"), namespace: NamespaceConstraint::Specific(Namespace { @@ -1553,70 +1648,48 @@ pub mod tests { 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 { - inner: SelectorInner::new(Arc::new(ComplexSelector { - compound_selector: vec!(), - next: None, - })), + inner: SelectorInner::from_vec(vec![]), 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 { - inner: SelectorInner::new(Arc::new(ComplexSelector { - compound_selector: vec!(), - next: Some((Arc::new(ComplexSelector { - compound_selector: vec!(SimpleSelector::LocalName(LocalName { + inner: SelectorInner::from_vec( + vec![ + Component::LocalName(LocalName { name: DummyAtom::from("div"), - lower_name: DummyAtom::from("div") })), - next: None, - }), Combinator::Descendant)), - })), + lower_name: DummyAtom::from("div") }), + Component::Combinator(Combinator::Descendant), + ]), pseudo_element: Some(PseudoElement::After), specificity: specificity(0, 0, 2), })))); assert_eq!(parse("#d1 > .ok"), Ok(SelectorList(vec![Selector { - 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)), - })), + inner: SelectorInner::from_vec( + vec![ + Component::ID(DummyAtom::from("d1")), + Component::Combinator(Combinator::Child), + Component::Class(DummyAtom::from("ok")), + ]), pseudo_element: None, specificity: (1 << 20) + (1 << 10) + (0 << 0), }]))); assert_eq!(parse(":not(.babybel, #provel.old)"), Ok(SelectorList(vec!(Selector { - inner: SelectorInner::new(Arc::new(ComplexSelector { - compound_selector: vec!(SimpleSelector::Negation( + inner: SelectorInner::from_vec(vec!(Component::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, - })), + ComplexSelector::from_vec(vec!(Component::Class(DummyAtom::from("babybel")))), + ComplexSelector::from_vec(vec!( + Component::ID(DummyAtom::from("provel")), + Component::Class(DummyAtom::from("old")), + ))).into_boxed_slice() + ))), pseudo_element: None, specificity: specificity(1, 1, 0), })))); @@ -1629,7 +1702,7 @@ pub mod tests { impl SelectorVisitor for TestVisitor { type Impl = DummySelectorImpl; - fn visit_simple_selector(&mut self, s: &SimpleSelector) -> bool { + fn visit_simple_selector(&mut self, s: &Component) -> bool { let mut dest = String::new(); s.to_css(&mut dest).unwrap(); self.seen.push(dest); diff --git a/components/selectors/visitor.rs b/components/selectors/visitor.rs index 2e0b52987f8..be335aed87b 100644 --- a/components/selectors/visitor.rs +++ b/components/selectors/visitor.rs @@ -6,7 +6,8 @@ #![deny(missing_docs)] -use parser::{AttrSelector, Combinator, ComplexSelector, SelectorImpl, SimpleSelector}; +use parser::{AttrSelector, Combinator, Component}; +use parser::{SelectorImpl, SelectorIter}; /// A trait to visit selector properties. /// @@ -24,7 +25,7 @@ pub trait SelectorVisitor { } /// Visit a simple selector. - fn visit_simple_selector(&mut self, _: &SimpleSelector) -> bool { + fn visit_simple_selector(&mut self, _: &Component) -> bool { true } @@ -33,7 +34,7 @@ pub trait SelectorVisitor { /// Gets the combinator to the right of the selector, or `None` if the /// selector is the leftmost one. fn visit_complex_selector(&mut self, - _: &ComplexSelector, + _: SelectorIter, _combinator_to_right: Option) -> bool { true diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index adbd3a041bd..e87336669d5 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -14,7 +14,6 @@ use selectors::visitor::SelectorVisitor; use std::borrow::Cow; use std::fmt; use std::ptr; -use std::sync::Arc; use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; /// A representation of a CSS pseudo-element. @@ -218,7 +217,7 @@ macro_rules! pseudo_class_name { /// /// TODO(emilio): We disallow combinators and pseudos here, so we /// should use SimpleSelector instead - MozAny(Vec>>), + MozAny(Box<[ComplexSelector]>), } } } @@ -267,7 +266,7 @@ impl SelectorMethods for NonTSPseudoClass { where V: SelectorVisitor, { if let NonTSPseudoClass::MozAny(ref selectors) = *self { - for selector in selectors { + for selector in selectors.iter() { if !selector.visit(visitor) { return false; } @@ -396,13 +395,13 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> { }, )* "-moz-any" => { let selectors = parser.parse_comma_separated(|input| { - ComplexSelector::parse(self, input).map(Arc::new) + ComplexSelector::parse(self, input) })?; // Selectors inside `:-moz-any` may not include combinators. - if selectors.iter().any(|s| s.next.is_some()) { + if selectors.iter().flat_map(|x| x.iter_raw()).any(|s| s.is_combinator()) { return Err(()) } - NonTSPseudoClass::MozAny(selectors) + NonTSPseudoClass::MozAny(selectors.into_boxed_slice()) } _ => return Err(()) } diff --git a/components/style/matching.rs b/components/style/matching.rs index 90a92a4ea5e..0cc0677599f 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -1198,11 +1198,11 @@ pub trait MatchMethods : TElement { // > fn with_really_simple_selectors(&self, f: |&H|); - // In terms of `SimpleSelector`s, these two functions will insert and remove: - // - `SimpleSelector::LocalName` - // - `SimpleSelector::Namepace` - // - `SimpleSelector::ID` - // - `SimpleSelector::Class` + // In terms of `Component`s, these two functions will insert and remove: + // - `Component::LocalName` + // - `Component::Namepace` + // - `Component::ID` + // - `Component::Class` /// Inserts and removes the matching `Descendant` selectors from a bloom /// filter. This is used to speed up CSS selector matching to remove diff --git a/components/style/restyle_hints.rs b/components/style/restyle_hints.rs index 0ac298442ba..d28bbee910f 100644 --- a/components/style/restyle_hints.rs +++ b/components/style/restyle_hints.rs @@ -17,10 +17,10 @@ use selector_parser::{AttrValue, NonTSPseudoClass, Snapshot, SelectorImpl}; use selectors::{Element, MatchAttr}; use selectors::matching::{ElementSelectorFlags, StyleRelations}; use selectors::matching::matches_selector; -use selectors::parser::{AttrSelector, Combinator, ComplexSelector, SelectorInner, SelectorMethods, SimpleSelector}; +use selectors::parser::{AttrSelector, Combinator, ComplexSelector, Component}; +use selectors::parser::{SelectorInner, SelectorIter, SelectorMethods}; use selectors::visitor::SelectorVisitor; use std::clone::Clone; -use std::sync::Arc; bitflags! { /// When the ElementState of an element (like IN_HOVER_STATE) changes, @@ -388,24 +388,24 @@ impl<'a, E> Element for ElementWrapper<'a, E> } } -fn selector_to_state(sel: &SimpleSelector) -> ElementState { +fn selector_to_state(sel: &Component) -> ElementState { match *sel { - SimpleSelector::NonTSPseudoClass(ref pc) => pc.state_flag(), + Component::NonTSPseudoClass(ref pc) => pc.state_flag(), _ => ElementState::empty(), } } -fn is_attr_selector(sel: &SimpleSelector) -> bool { +fn is_attr_selector(sel: &Component) -> bool { match *sel { - SimpleSelector::ID(_) | - SimpleSelector::Class(_) | - SimpleSelector::AttrExists(_) | - SimpleSelector::AttrEqual(_, _, _) | - SimpleSelector::AttrIncludes(_, _) | - SimpleSelector::AttrDashMatch(_, _) | - SimpleSelector::AttrPrefixMatch(_, _) | - SimpleSelector::AttrSubstringMatch(_, _) | - SimpleSelector::AttrSuffixMatch(_, _) => true, + Component::ID(_) | + Component::Class(_) | + Component::AttrExists(_) | + Component::AttrEqual(_, _, _) | + Component::AttrIncludes(_, _) | + Component::AttrDashMatch(_, _) | + Component::AttrPrefixMatch(_, _) | + Component::AttrSubstringMatch(_, _) | + Component::AttrSuffixMatch(_, _) => true, _ => false, } } @@ -416,22 +416,22 @@ fn is_attr_selector(sel: &SimpleSelector) -> bool { /// /// We use this for selectors that can have different matching behavior between /// siblings that are otherwise identical as far as the cache is concerned. -fn needs_cache_revalidation(sel: &SimpleSelector) -> bool { +fn needs_cache_revalidation(sel: &Component) -> bool { match *sel { - SimpleSelector::Empty | - SimpleSelector::FirstChild | - SimpleSelector::LastChild | - SimpleSelector::OnlyChild | - SimpleSelector::NthChild(..) | - SimpleSelector::NthLastChild(..) | - SimpleSelector::NthOfType(..) | - SimpleSelector::NthLastOfType(..) | - SimpleSelector::FirstOfType | - SimpleSelector::LastOfType | - SimpleSelector::OnlyOfType => true, + Component::Empty | + Component::FirstChild | + Component::LastChild | + Component::OnlyChild | + Component::NthChild(..) | + Component::NthLastChild(..) | + Component::NthOfType(..) | + Component::NthLastOfType(..) | + Component::FirstOfType | + Component::LastOfType | + Component::OnlyOfType => true, // FIXME(emilio): This sets the "revalidation" flag for :any, which is // probably expensive given we use it a lot in UA sheets. - SimpleSelector::NonTSPseudoClass(ref p) => p.state_flag().is_empty(), + Component::NonTSPseudoClass(ref p) => p.state_flag().is_empty(), _ => false, } } @@ -509,7 +509,7 @@ impl SelectorVisitor for SensitivitiesVisitor { type Impl = SelectorImpl; fn visit_complex_selector(&mut self, - _: &ComplexSelector, + _: SelectorIter, combinator: Option) -> bool { self.hint |= combinator_to_restyle_hint(combinator); self.needs_revalidation |= self.hint.contains(RESTYLE_LATER_SIBLINGS); @@ -517,7 +517,7 @@ impl SelectorVisitor for SensitivitiesVisitor { true } - fn visit_simple_selector(&mut self, s: &SimpleSelector) -> bool { + fn visit_simple_selector(&mut self, s: &Component) -> bool { self.sensitivities.states.insert(selector_to_state(s)); if !self.sensitivities.attrs { @@ -567,50 +567,47 @@ impl DependencySet { /// cache revalidation, that is, whether two siblings of the same "shape" /// may have different style due to this selector. pub fn note_selector(&mut self, - selector: &Arc>) + base: &ComplexSelector) -> bool { + let mut next = Some(base.clone()); let mut combinator = None; - let mut current = selector; - let mut needs_revalidation = false; - loop { - let mut sensitivities_visitor = SensitivitiesVisitor { + while let Some(current) = next.take() { + // Set up our visitor. + let mut visitor = SensitivitiesVisitor { sensitivities: Sensitivities::new(), - hint: RestyleHint::empty(), + hint: combinator_to_restyle_hint(combinator), needs_revalidation: false, }; - for ss in ¤t.compound_selector { - ss.visit(&mut sensitivities_visitor); + { + // Visit all the simple selectors. + let mut iter = current.iter(); + let mut index = 0usize; + for ss in &mut iter { + ss.visit(&mut visitor); + index += 1; + } + + // Prepare the next sequence of simple selectors. + if let Some(next_combinator) = iter.next_sequence() { + next = Some(current.slice_from(index + 1)); + combinator = Some(next_combinator); + } } - needs_revalidation |= sensitivities_visitor.needs_revalidation; - - let SensitivitiesVisitor { - sensitivities, - mut hint, - .. - } = sensitivities_visitor; - - hint |= combinator_to_restyle_hint(combinator); - - if !sensitivities.is_empty() { + // Note what we found. + needs_revalidation |= visitor.needs_revalidation; + if !visitor.sensitivities.is_empty() { self.add_dependency(Dependency { - sensitivities: sensitivities, - hint: hint, - selector: SelectorInner::new(current.clone()), + sensitivities: visitor.sensitivities, + hint: visitor.hint, + selector: SelectorInner::new(current), }) } - match current.next { - Some((ref next, next_combinator)) => { - current = next; - combinator = Some(next_combinator); - } - None => break, - } } needs_revalidation @@ -729,7 +726,7 @@ fn smoke_restyle_hints() { let mut dependencies = DependencySet::new(); let mut p = Parser::new(":not(:active) ~ label"); - let selector = Arc::new(ComplexSelector::parse(&parser, &mut p).unwrap()); + let selector = ComplexSelector::parse(&parser, &mut p).unwrap(); dependencies.note_selector(&selector); assert_eq!(dependencies.len(), 1); assert_eq!(dependencies.state_deps.len(), 1); diff --git a/components/style/stylist.rs b/components/style/stylist.rs index e3c500b8b37..2781f50c287 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -27,7 +27,7 @@ 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_selector}; -use selectors::parser::{Selector, SelectorInner, SimpleSelector, LocalName as LocalNameSelector}; +use selectors::parser::{Component, Selector, SelectorInner, LocalName as LocalNameSelector}; use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; use sink::Push; use smallvec::VecLike; @@ -1070,8 +1070,7 @@ impl SelectorMap { // correct, and also to not trigger rule tree assertions. let mut important = vec![]; for rule in self.other_rules.iter() { - if rule.selector.complex.compound_selector.is_empty() && - rule.selector.complex.next.is_none() { + if rule.selector.complex.iter_raw().next().is_none() { let style_rule = rule.style_rule.read_with(guard); let block = style_rule.block.read_with(guard); if block.any_normal() { @@ -1180,10 +1179,10 @@ impl SelectorMap { /// Retrieve the first ID name in Rule, or None otherwise. pub fn get_id_name(rule: &Rule) -> Option { - for ss in &rule.selector.complex.compound_selector { + for ss in rule.selector.complex.iter() { // TODO(pradeep): Implement case-sensitivity based on the // document type and quirks mode. - if let SimpleSelector::ID(ref id) = *ss { + if let Component::ID(ref id) = *ss { return Some(id.clone()); } } @@ -1193,10 +1192,10 @@ impl SelectorMap { /// Retrieve the FIRST class name in Rule, or None otherwise. pub fn get_class_name(rule: &Rule) -> Option { - for ss in &rule.selector.complex.compound_selector { + for ss in rule.selector.complex.iter() { // TODO(pradeep): Implement case-sensitivity based on the // document type and quirks mode. - if let SimpleSelector::Class(ref class) = *ss { + if let Component::Class(ref class) = *ss { return Some(class.clone()); } } @@ -1206,8 +1205,8 @@ impl SelectorMap { /// Retrieve the name if it is a type selector, or None otherwise. pub fn get_local_name(rule: &Rule) -> Option> { - for ss in &rule.selector.complex.compound_selector { - if let SimpleSelector::LocalName(ref n) = *ss { + for ss in rule.selector.complex.iter() { + if let Component::LocalName(ref n) = *ss { return Some(LocalNameSelector { name: n.name.clone(), lower_name: n.lower_name.clone(), diff --git a/tests/unit/style/stylesheets.rs b/tests/unit/style/stylesheets.rs index 0d2bfe5f387..ed5696003b9 100644 --- a/tests/unit/style/stylesheets.rs +++ b/tests/unit/style/stylesheets.rs @@ -84,27 +84,24 @@ fn test_parse_stylesheet() { CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule { selectors: SelectorList(vec![ Selector { - inner: SelectorInner::new(Arc::new(ComplexSelector { - compound_selector: vec![ - SimpleSelector::Namespace(Namespace { + inner: SelectorInner::from_vec(vec![ + Component::Namespace(Namespace { + prefix: None, + url: NsAtom::from("http://www.w3.org/1999/xhtml") + }), + Component::LocalName(LocalName { + name: local_name!("input"), + lower_name: local_name!("input"), + }), + Component::AttrEqual(AttrSelector { + name: local_name!("type"), + lower_name: local_name!("type"), + namespace: NamespaceConstraint::Specific(Namespace { prefix: None, - url: NsAtom::from("http://www.w3.org/1999/xhtml") + url: ns!() }), - SimpleSelector::LocalName(LocalName { - name: local_name!("input"), - lower_name: local_name!("input"), - }), - SimpleSelector::AttrEqual(AttrSelector { - name: local_name!("type"), - lower_name: local_name!("type"), - namespace: NamespaceConstraint::Specific(Namespace { - prefix: None, - url: ns!() - }), - }, "hidden".to_owned(), CaseSensitivity::CaseInsensitive) - ], - next: None, - })), + }, "hidden".to_owned(), CaseSensitivity::CaseInsensitive) + ]), pseudo_element: None, specificity: (0 << 20) + (1 << 10) + (1 << 0), }, @@ -120,36 +117,30 @@ fn test_parse_stylesheet() { CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule { selectors: SelectorList(vec![ Selector { - 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, - })), + inner: SelectorInner::from_vec(vec![ + Component::Namespace(Namespace { + prefix: None, + url: NsAtom::from("http://www.w3.org/1999/xhtml") + }), + Component::LocalName(LocalName { + name: local_name!("html"), + lower_name: local_name!("html"), + }), + ]), pseudo_element: None, specificity: (0 << 20) + (0 << 10) + (1 << 0), }, Selector { - 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, - })), + inner: SelectorInner::from_vec(vec![ + Component::Namespace(Namespace { + prefix: None, + url: NsAtom::from("http://www.w3.org/1999/xhtml") + }), + Component::LocalName(LocalName { + name: local_name!("body"), + lower_name: local_name!("body"), + }), + ]), pseudo_element: None, specificity: (0 << 20) + (0 << 10) + (1 << 0), }, @@ -162,25 +153,19 @@ fn test_parse_stylesheet() { CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule { selectors: SelectorList(vec![ Selector { - 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 { - compound_selector: vec![ - SimpleSelector::Namespace(Namespace { - prefix: None, - url: NsAtom::from("http://www.w3.org/1999/xhtml") - }), - SimpleSelector::ID(Atom::from("d1")), - ], - next: None, - }), Combinator::Child)), - })), + inner: SelectorInner::from_vec(vec![ + Component::Namespace(Namespace { + prefix: None, + url: NsAtom::from("http://www.w3.org/1999/xhtml") + }), + Component::ID(Atom::from("d1")), + Component::Combinator(Combinator::Child), + Component::Namespace(Namespace { + prefix: None, + url: NsAtom::from("http://www.w3.org/1999/xhtml") + }), + Component::Class(Atom::from("ok")), + ]), pseudo_element: None, specificity: (1 << 20) + (1 << 10) + (0 << 0), }, diff --git a/tests/unit/style/stylist.rs b/tests/unit/style/stylist.rs index 77791ebeedc..414a569ac37 100644 --- a/tests/unit/style/stylist.rs +++ b/tests/unit/style/stylist.rs @@ -77,7 +77,7 @@ fn test_get_id_name() { #[test] fn test_get_class_name() { let (rules_list, _) = get_mock_rules(&[".intro.foo", "#top"]); - assert_eq!(SelectorMap::get_class_name(&rules_list[0][0]), Some(Atom::from("intro"))); + assert_eq!(SelectorMap::get_class_name(&rules_list[0][0]), Some(Atom::from("foo"))); assert_eq!(SelectorMap::get_class_name(&rules_list[1][0]), None); } @@ -103,8 +103,8 @@ fn test_insert() { selector_map.insert(rules_list[1][0].clone()); assert_eq!(1, selector_map.id_hash.get(&Atom::from("top")).unwrap()[0].source_order); selector_map.insert(rules_list[0][0].clone()); - assert_eq!(0, selector_map.class_hash.get(&Atom::from("intro")).unwrap()[0].source_order); - assert!(selector_map.class_hash.get(&Atom::from("foo")).is_none()); + assert_eq!(0, selector_map.class_hash.get(&Atom::from("foo")).unwrap()[0].source_order); + assert!(selector_map.class_hash.get(&Atom::from("intro")).is_none()); } #[test]