Store selectors and combinators inline in a single sequence.

This improves cache locality and reduces allocations during parsing.

Note that this reverses the iteration order within a sequence of simple selectors,
but that shouldn't matter.
This commit is contained in:
Bobby Holley 2017-04-18 23:43:39 -07:00
parent 93fa0ae1e3
commit 6d66ec5e11
10 changed files with 703 additions and 330 deletions

View file

@ -0,0 +1,326 @@
/* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T> {
data: *const [T],
counts: Arc<Box<[T]>>,
}
unsafe impl<T: Send + Sync> Send for ArcSlice<T> {}
unsafe impl<T: Send + Sync> Sync for ArcSlice<T> {}
/// 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<T> {
data: *const [T],
counts: Weak<Box<[T]>>,
}
unsafe impl<T: Send + Sync> Send for WeakSlice<T> {}
unsafe impl<T: Send + Sync> Sync for WeakSlice<T> {}
impl<T> ArcSlice<T> {
/// Construct a new `ArcSlice` containing the elements of `slice`.
///
/// This reuses the allocation of `slice`.
pub fn new(slice: Box<[T]>) -> ArcSlice<T> {
ArcSlice {
data: &*slice,
counts: Arc::new(slice),
}
}
/// Downgrade self into a weak slice.
pub fn downgrade(&self) -> WeakSlice<T> {
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<T> {
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<T> {
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<T> {
let hi = self.len();
self.slice(lo, hi)
}
}
impl<T> Clone for ArcSlice<T> {
fn clone(&self) -> ArcSlice<T> {
ArcSlice {
data: self.data,
counts: self.counts.clone()
}
}
}
impl<T> ops::Deref for ArcSlice<T> {
type Target = [T];
fn deref<'a>(&'a self) -> &'a [T] {
unsafe { &*self.data }
}
}
impl<T> AsRef<[T]> for ArcSlice<T> {
fn as_ref(&self) -> &[T] { &**self }
}
impl<T: PartialEq> PartialEq for ArcSlice<T> {
fn eq(&self, other: &ArcSlice<T>) -> bool { **self == **other }
fn ne(&self, other: &ArcSlice<T>) -> bool { **self != **other }
}
impl<T: Eq> Eq for ArcSlice<T> {}
impl<T: PartialOrd> PartialOrd for ArcSlice<T> {
fn partial_cmp(&self, other: &ArcSlice<T>) -> Option<cmp::Ordering> {
(**self).partial_cmp(&**other)
}
fn lt(&self, other: &ArcSlice<T>) -> bool { **self < **other }
fn le(&self, other: &ArcSlice<T>) -> bool { **self <= **other }
fn gt(&self, other: &ArcSlice<T>) -> bool { **self > **other }
fn ge(&self, other: &ArcSlice<T>) -> bool { **self >= **other }
}
impl<T: Ord> Ord for ArcSlice<T> {
fn cmp(&self, other: &ArcSlice<T>) -> cmp::Ordering { (**self).cmp(&**other) }
}
impl<T: Hash> Hash for ArcSlice<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
Hash::hash(&**self, state)
}
}
impl<T: fmt::Debug> fmt::Debug for ArcSlice<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T> WeakSlice<T> {
/// 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<ArcSlice<T>> {
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<i32> = 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<T: Send>() {}
fn assert_sync<T: Send>() {}
assert_send::<ArcSlice<u8>>();
assert_sync::<ArcSlice<u8>>();
assert_send::<WeakSlice<u8>>();
assert_sync::<WeakSlice<u8>>();
}
#[test]
fn test_drop() {
let drop_flag = Arc::new(Mutex::new(0));
struct Foo(Arc<Mutex<i32>>);
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);
}
}

View file

@ -9,6 +9,7 @@ extern crate fnv;
extern crate precomputed_hash; extern crate precomputed_hash;
extern crate smallvec; extern crate smallvec;
pub mod arcslice;
pub mod bloom; pub mod bloom;
pub mod matching; pub mod matching;
pub mod parser; pub mod parser;

View file

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use bloom::BloomFilter; use bloom::BloomFilter;
use parser::{CaseSensitivity, Combinator, ComplexSelector, LocalName}; use parser::{CaseSensitivity, Combinator, ComplexSelector, LocalName};
use parser::{SimpleSelector, Selector, SelectorInner}; use parser::{SimpleSelector, Selector, SelectorInner, SelectorIter};
use std::borrow::Borrow; use std::borrow::Borrow;
use tree::Element; use tree::Element;
@ -207,7 +207,7 @@ pub fn matches_complex_selector<E, F>(selector: &ComplexSelector<E::Impl>,
where E: Element, where E: Element,
F: FnMut(&E, ElementSelectorFlags), F: FnMut(&E, ElementSelectorFlags),
{ {
match matches_complex_selector_internal(selector, match matches_complex_selector_internal(selector.iter(),
element, element,
relations, relations,
flags_setter) { flags_setter) {
@ -216,7 +216,7 @@ pub fn matches_complex_selector<E, F>(selector: &ComplexSelector<E::Impl>,
} }
} }
fn matches_complex_selector_internal<E, F>(selector: &ComplexSelector<E::Impl>, fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Impl>,
element: &E, element: &E,
relations: &mut StyleRelations, relations: &mut StyleRelations,
flags_setter: &mut F) flags_setter: &mut F)
@ -224,14 +224,12 @@ fn matches_complex_selector_internal<E, F>(selector: &ComplexSelector<E::Impl>,
where E: Element, where E: Element,
F: FnMut(&E, ElementSelectorFlags), 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) matches_simple_selector(simple, element, relations, flags_setter)
}); });
let siblings = selector.next.as_ref().map_or(false, |&(_, combinator)| { let combinator = selector_iter.next_sequence();
matches!(combinator, Combinator::NextSibling | Combinator::LaterSibling) let siblings = combinator.map_or(false, |c| c.is_sibling());
});
if siblings { if siblings {
flags_setter(element, HAS_SLOW_SELECTOR_LATER_SIBLINGS); flags_setter(element, HAS_SLOW_SELECTOR_LATER_SIBLINGS);
} }
@ -240,9 +238,9 @@ fn matches_complex_selector_internal<E, F>(selector: &ComplexSelector<E::Impl>,
return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling; return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling;
} }
match selector.next { match combinator {
None => SelectorMatchingResult::Matched, None => SelectorMatchingResult::Matched,
Some((ref next_selector, combinator)) => { Some(c) => {
let (mut next_element, candidate_not_found) = if siblings { let (mut next_element, candidate_not_found) = if siblings {
(element.prev_sibling_element(), (element.prev_sibling_element(),
SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant) SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant)
@ -256,11 +254,11 @@ fn matches_complex_selector_internal<E, F>(selector: &ComplexSelector<E::Impl>,
None => return candidate_not_found, None => return candidate_not_found,
Some(next_element) => next_element, Some(next_element) => next_element,
}; };
let result = matches_complex_selector_internal(&**next_selector, let result = matches_complex_selector_internal(selector_iter.clone(),
&element, &element,
relations, relations,
flags_setter); flags_setter);
match (result, combinator) { match (result, c) {
// Return the status immediately. // Return the status immediately.
(SelectorMatchingResult::Matched, _) => return result, (SelectorMatchingResult::Matched, _) => return result,
(SelectorMatchingResult::NotMatchedGlobally, _) => return result, (SelectorMatchingResult::NotMatchedGlobally, _) => return result,
@ -319,6 +317,7 @@ fn matches_simple_selector<E, F>(
} }
match *selector { match *selector {
SimpleSelector::Combinator(_) => unreachable!(),
SimpleSelector::LocalName(LocalName { ref name, ref lower_name }) => { SimpleSelector::LocalName(LocalName { ref name, ref lower_name }) => {
let name = if element.is_html_element_in_html_document() { lower_name } else { name }; let name = if element.is_html_element_in_html_document() { lower_name } else { name };
element.get_local_name() == name.borrow() element.get_local_name() == name.borrow()
@ -421,7 +420,7 @@ fn matches_simple_selector<E, F>(
} }
SimpleSelector::Negation(ref negated) => { SimpleSelector::Negation(ref negated) => {
!negated.iter().all(|s| { !negated.iter().all(|s| {
match matches_complex_selector_internal(s, match matches_complex_selector_internal(s.iter(),
element, element,
relations, relations,
flags_setter) { flags_setter) {

View file

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * 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 cssparser::{Token, Parser as CssParser, parse_nth, ToCss, serialize_identifier, CssStringWriter};
use precomputed_hash::PrecomputedHash; use precomputed_hash::PrecomputedHash;
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
@ -9,8 +10,9 @@ use std::borrow::{Borrow, Cow};
use std::cmp; use std::cmp;
use std::fmt::{self, Display, Debug, Write}; use std::fmt::{self, Display, Debug, Write};
use std::hash::Hash; use std::hash::Hash;
use std::iter::Rev;
use std::ops::Add; use std::ops::Add;
use std::sync::Arc; use std::slice;
use tree::SELECTOR_WHITESPACE; use tree::SELECTOR_WHITESPACE;
use visitor::SelectorVisitor; use visitor::SelectorVisitor;
@ -132,7 +134,7 @@ const NUM_ANCESTOR_HASHES: usize = 4;
#[derive(PartialEq, Eq, Hash, Clone)] #[derive(PartialEq, Eq, Hash, Clone)]
pub struct SelectorInner<Impl: SelectorImpl> { pub struct SelectorInner<Impl: SelectorImpl> {
/// The selector data. /// The selector data.
pub complex: Arc<ComplexSelector<Impl>>, pub complex: ComplexSelector<Impl>,
/// Ancestor hashes for the bloom filter. We precompute these and store /// Ancestor hashes for the bloom filter. We precompute these and store
/// them inline to optimize cache performance during selector matching. /// them inline to optimize cache performance during selector matching.
/// This matters a lot. /// This matters a lot.
@ -140,12 +142,11 @@ pub struct SelectorInner<Impl: SelectorImpl> {
} }
impl<Impl: SelectorImpl> SelectorInner<Impl> { impl<Impl: SelectorImpl> SelectorInner<Impl> {
pub fn new(c: Arc<ComplexSelector<Impl>>) -> Self { pub fn new(c: ComplexSelector<Impl>) -> Self {
let mut hashes = [0; NUM_ANCESTOR_HASHES]; let mut hashes = [0; NUM_ANCESTOR_HASHES];
{ {
// Compute ancestor hashes for the bloom filter. // Compute ancestor hashes for the bloom filter.
let mut hash_iter = let mut hash_iter = c.iter_ancestors()
iter_ancestors(&c).flat_map(|x| x.compound_selector.iter())
.map(|x| x.ancestor_hash()) .map(|x| x.ancestor_hash())
.filter(|x| x.is_some()) .filter(|x| x.is_some())
.map(|x| x.unwrap()); .map(|x| x.unwrap());
@ -162,6 +163,12 @@ impl<Impl: SelectorImpl> SelectorInner<Impl> {
ancestor_hashes: hashes, ancestor_hashes: hashes,
} }
} }
/// Creates a SelectorInner from a Vec of SimpleSelectors. Used in tests.
pub fn from_vec(vec: Vec<SimpleSelector<Impl>>) -> Self {
let complex = ComplexSelector::from_vec(vec);
Self::new(complex)
}
} }
#[derive(PartialEq, Eq, Hash, Clone)] #[derive(PartialEq, Eq, Hash, Clone)]
@ -194,25 +201,22 @@ impl<Impl: SelectorImpl> SelectorMethods for ComplexSelector<Impl> {
fn visit<V>(&self, visitor: &mut V) -> bool fn visit<V>(&self, visitor: &mut V) -> bool
where V: SelectorVisitor<Impl = Impl>, where V: SelectorVisitor<Impl = Impl>,
{ {
let mut current = self; let mut current = self.iter();
let mut combinator = None; let mut combinator = None;
loop { loop {
if !visitor.visit_complex_selector(current, combinator) { if !visitor.visit_complex_selector(current.clone(), combinator) {
return false; return false;
} }
for selector in &current.compound_selector { for selector in &mut current {
if !selector.visit(visitor) { if !selector.visit(visitor) {
return false; return false;
} }
} }
match current.next { combinator = current.next_sequence();
Some((ref next, next_combinator)) => { if combinator.is_none() {
current = next; break;
combinator = Some(next_combinator);
}
None => break,
} }
} }
@ -262,38 +266,139 @@ impl<Impl: SelectorImpl> SelectorMethods for SimpleSelector<Impl> {
} }
} }
/// 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)] #[derive(Clone, Eq, Hash, PartialEq)]
pub struct ComplexSelector<Impl: SelectorImpl> { pub struct ComplexSelector<Impl: SelectorImpl>(ArcSlice<SimpleSelector<Impl>>);
pub compound_selector: Vec<SimpleSelector<Impl>>,
pub next: Option<(Arc<ComplexSelector<Impl>>, Combinator)>, // c.next is left of c impl<Impl: SelectorImpl> ComplexSelector<Impl> {
/// 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<Impl> {
SelectorIter {
iter: self.iter_raw(),
next_combinator: None,
}
} }
struct AncestorIterator<'a, Impl: 'a + SelectorImpl> { /// Returns an iterator over the entire sequence of simple selectors and combinators,
curr: Option<&'a Arc<ComplexSelector<Impl>>>, /// from right to left.
pub fn iter_raw(&self) -> Rev<slice::Iter<SimpleSelector<Impl>>> {
self.iter_raw_rev().rev()
} }
impl<'a, Impl: SelectorImpl> Iterator for AncestorIterator<'a, Impl> { /// Returns an iterator over the entire sequence of simple selectors and combinators,
type Item = &'a Arc<ComplexSelector<Impl>>; /// from left to right.
pub fn iter_raw_rev(&self) -> slice::Iter<SimpleSelector<Impl>> {
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<Impl> {
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 SimpleSelectors. Used in tests.
pub fn from_vec(vec: Vec<SimpleSelector<Impl>>) -> Self {
ComplexSelector(ArcSlice::new(vec.into_boxed_slice()))
}
}
pub struct SelectorIter<'a, Impl: 'a + SelectorImpl> {
iter: Rev<slice::Iter<'a, SimpleSelector<Impl>>>,
next_combinator: Option<Combinator>,
}
// 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<Combinator> {
self.next_combinator.take()
}
}
impl<'a, Impl: SelectorImpl> Iterator for SelectorIter<'a, Impl> {
type Item = &'a SimpleSelector<Impl>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
while let Some(sel) = self.curr.take() { debug_assert!(self.next_combinator.is_none(), "Should call take_combinator!");
let (next_sel, is_ancestor) = match sel.next { match self.iter.next() {
None => (None, true), None => None,
Some((ref sel, comb)) => Some(&SimpleSelector::Combinator(c)) => {
(Some(sel), matches!(comb, Combinator::Child | Combinator::Descendant)), self.next_combinator = Some(c);
}; None
self.curr = next_sel; },
if is_ancestor { 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; break;
} }
} }
self.curr
} }
} }
fn iter_ancestors<Impl: SelectorImpl>(sel: &Arc<ComplexSelector<Impl>>) -> AncestorIterator<Impl> { impl<'a, Impl: SelectorImpl> Iterator for AncestorIter<'a, Impl> {
AncestorIterator { type Item = &'a SimpleSelector<Impl>;
curr: Some(sel) fn next(&mut self) -> Option<Self::Item> {
// 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 +410,29 @@ pub enum Combinator {
LaterSibling, // ~ 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].
///
/// We could rename this SimpleSelectorOrCombinator, but that's annoying to
/// type everywhere, and the combinators are generally filtered out and
/// handled separately by the iterator classes anyway.
///
/// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1357973
#[derive(Eq, PartialEq, Clone, Hash)] #[derive(Eq, PartialEq, Clone, Hash)]
pub enum SimpleSelector<Impl: SelectorImpl> { pub enum SimpleSelector<Impl: SelectorImpl> {
Combinator(Combinator),
ID(Impl::Identifier), ID(Impl::Identifier),
Class(Impl::ClassName), Class(Impl::ClassName),
LocalName(LocalName<Impl>), LocalName(LocalName<Impl>),
@ -327,7 +453,7 @@ pub enum SimpleSelector<Impl: SelectorImpl> {
AttrSuffixNeverMatch(AttrSelector<Impl>, Impl::AttrValue), // empty value AttrSuffixNeverMatch(AttrSelector<Impl>, Impl::AttrValue), // empty value
// Pseudo-classes // Pseudo-classes
Negation(Vec<Arc<ComplexSelector<Impl>>>), Negation(Vec<ComplexSelector<Impl>>),
FirstChild, LastChild, OnlyChild, FirstChild, LastChild, OnlyChild,
Root, Root,
Empty, Empty,
@ -368,6 +494,11 @@ impl<Impl: SelectorImpl> SimpleSelector<Impl> {
_ => None, _ => None,
} }
} }
/// Returns true if this is a combinator.
pub fn is_combinator(&self) -> bool {
matches!(*self, SimpleSelector::Combinator(_))
}
} }
#[derive(Eq, PartialEq, Clone, Hash, Copy, Debug)] #[derive(Eq, PartialEq, Clone, Hash, Copy, Debug)]
@ -466,30 +597,10 @@ impl<Impl: SelectorImpl> ToCss for Selector<Impl> {
impl<Impl: SelectorImpl> ToCss for ComplexSelector<Impl> { impl<Impl: SelectorImpl> ToCss for ComplexSelector<Impl> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
use smallvec::SmallVec; for item in self.iter_raw_rev() {
let mut current = self; item.to_css(dest)?;
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 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(())
} }
} }
@ -509,6 +620,9 @@ impl<Impl: SelectorImpl> ToCss for SimpleSelector<Impl> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
use self::SimpleSelector::*; use self::SimpleSelector::*;
match *self { match *self {
Combinator(ref c) => {
c.to_css(dest)
}
ID(ref s) => { ID(ref s) => {
dest.write_char('#')?; dest.write_char('#')?;
display_to_css_identifier(s, dest) display_to_css_identifier(s, dest)
@ -699,14 +813,15 @@ fn specificity<Impl>(complex_selector: &ComplexSelector<Impl>,
specificity.into() specificity.into()
} }
fn complex_selector_specificity<Impl>(mut selector: &ComplexSelector<Impl>) fn complex_selector_specificity<Impl>(selector: &ComplexSelector<Impl>)
-> Specificity -> Specificity
where Impl: SelectorImpl { where Impl: SelectorImpl {
fn compound_selector_specificity<Impl>(compound_selector: &[SimpleSelector<Impl>], fn compound_selector_specificity<Impl>(selector_iter: &mut SelectorIter<Impl>,
specificity: &mut Specificity) specificity: &mut Specificity)
where Impl: SelectorImpl { where Impl: SelectorImpl {
for simple_selector in compound_selector.iter() { for simple_selector in selector_iter {
match *simple_selector { match *simple_selector {
SimpleSelector::Combinator(..) => unreachable!(),
SimpleSelector::LocalName(..) => SimpleSelector::LocalName(..) =>
specificity.element_selectors += 1, specificity.element_selectors += 1,
SimpleSelector::ID(..) => SimpleSelector::ID(..) =>
@ -749,16 +864,11 @@ fn complex_selector_specificity<Impl>(mut selector: &ComplexSelector<Impl>)
} }
let mut specificity = Default::default(); let mut specificity = Default::default();
compound_selector_specificity(&selector.compound_selector, let mut iter = selector.iter();
&mut specificity);
loop { loop {
match selector.next { compound_selector_specificity(&mut iter, &mut specificity);
None => break, if iter.next_sequence().is_none() {
Some((ref next_selector, _)) => { break;
selector = &**next_selector;
compound_selector_specificity(&selector.compound_selector,
&mut specificity)
}
} }
} }
specificity specificity
@ -775,7 +885,7 @@ fn parse_selector<P, Impl>(parser: &P, input: &mut CssParser) -> Result<Selector
parse_complex_selector_and_pseudo_element(parser, input)?; parse_complex_selector_and_pseudo_element(parser, input)?;
Ok(Selector { Ok(Selector {
specificity: specificity(&complex, pseudo_element.as_ref()), specificity: specificity(&complex, pseudo_element.as_ref()),
inner: SelectorInner::new(Arc::new(complex)), inner: SelectorInner::new(complex),
pseudo_element: pseudo_element, pseudo_element: pseudo_element,
}) })
} }
@ -786,10 +896,16 @@ fn parse_complex_selector_and_pseudo_element<P, Impl>(
-> Result<(ComplexSelector<Impl>, Option<Impl::PseudoElement>), ()> -> Result<(ComplexSelector<Impl>, Option<Impl::PseudoElement>), ()>
where P: Parser<Impl=Impl>, Impl: SelectorImpl where P: Parser<Impl=Impl>, Impl: SelectorImpl
{ {
let (first, mut pseudo_element) = parse_compound_selector(parser, input)?; let mut sequence = Vec::new();
let mut complex = ComplexSelector { compound_selector: first, next: None }; 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 combinator;
let mut any_whitespace = false; let mut any_whitespace = false;
loop { loop {
@ -820,14 +936,10 @@ fn parse_complex_selector_and_pseudo_element<P, Impl>(
} }
} }
} }
let (compound_selector, pseudo) = parse_compound_selector(parser, input)?; sequence.push(SimpleSelector::Combinator(combinator));
complex = ComplexSelector {
compound_selector: compound_selector,
next: Some((Arc::new(complex), combinator))
};
pseudo_element = pseudo;
} }
let complex = ComplexSelector(ArcSlice::new(sequence.into_boxed_slice()));
Ok((complex, pseudo_element)) Ok((complex, pseudo_element))
} }
@ -848,30 +960,29 @@ impl<Impl: SelectorImpl> ComplexSelector<Impl> {
/// * `Err(())`: Invalid selector, abort /// * `Err(())`: Invalid selector, abort
/// * `Ok(None)`: Not a type selector, could be something else. `input` was not consumed. /// * `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`) /// * `Ok(Some(vec))`: Length 0 (`*|*`), 1 (`*|E` or `ns|*`) or 2 (`|E` or `ns|E`)
fn parse_type_selector<P, Impl>(parser: &P, input: &mut CssParser) fn parse_type_selector<P, Impl>(parser: &P, input: &mut CssParser, sequence: &mut Vec<SimpleSelector<Impl>>)
-> Result<Option<Vec<SimpleSelector<Impl>>>, ()> -> Result<bool, ()>
where P: Parser<Impl=Impl>, Impl: SelectorImpl where P: Parser<Impl=Impl>, Impl: SelectorImpl
{ {
match parse_qualified_name(parser, input, /* in_attr_selector = */ false)? { match parse_qualified_name(parser, input, /* in_attr_selector = */ false)? {
None => Ok(None), None => Ok(false),
Some((namespace, local_name)) => { Some((namespace, local_name)) => {
let mut compound_selector = vec!();
match namespace { match namespace {
NamespaceConstraint::Specific(ns) => { NamespaceConstraint::Specific(ns) => {
compound_selector.push(SimpleSelector::Namespace(ns)) sequence.push(SimpleSelector::Namespace(ns))
}, },
NamespaceConstraint::Any => (), NamespaceConstraint::Any => (),
} }
match local_name { match local_name {
Some(name) => { Some(name) => {
compound_selector.push(SimpleSelector::LocalName(LocalName { sequence.push(SimpleSelector::LocalName(LocalName {
lower_name: from_ascii_lowercase(&name), lower_name: from_ascii_lowercase(&name),
name: from_cow_str(name), name: from_cow_str(name),
})) }))
} }
None => (), None => (),
} }
Ok(Some(compound_selector)) Ok(true)
} }
} }
} }
@ -1051,7 +1162,7 @@ fn parse_negation<P, Impl>(parser: &P,
-> Result<SimpleSelector<Impl>, ()> -> Result<SimpleSelector<Impl>, ()>
where P: Parser<Impl=Impl>, Impl: SelectorImpl where P: Parser<Impl=Impl>, Impl: SelectorImpl
{ {
input.parse_comma_separated(|input| ComplexSelector::parse(parser, input).map(Arc::new)) input.parse_comma_separated(|input| ComplexSelector::parse(parser, input))
.map(SimpleSelector::Negation) .map(SimpleSelector::Negation)
} }
@ -1062,8 +1173,9 @@ fn parse_negation<P, Impl>(parser: &P,
/// `Err(())` means invalid selector /// `Err(())` means invalid selector
fn parse_compound_selector<P, Impl>( fn parse_compound_selector<P, Impl>(
parser: &P, parser: &P,
input: &mut CssParser) input: &mut CssParser,
-> Result<(Vec<SimpleSelector<Impl>>, Option<Impl::PseudoElement>), ()> mut sequence: &mut Vec<SimpleSelector<Impl>>)
-> Result<Option<Impl::PseudoElement>, ()>
where P: Parser<Impl=Impl>, Impl: SelectorImpl where P: Parser<Impl=Impl>, Impl: SelectorImpl
{ {
// Consume any leading whitespace. // Consume any leading whitespace.
@ -1075,28 +1187,26 @@ fn parse_compound_selector<P, Impl>(
} }
} }
let mut empty = true; let mut empty = true;
let mut compound_selector = match parse_type_selector(parser, input)? { if !parse_type_selector(parser, input, &mut sequence)? {
None => { if let Some(url) = parser.default_namespace() {
match parser.default_namespace() {
// If there was no explicit type selector, but there is a // If there was no explicit type selector, but there is a
// default namespace, there is an implicit "<defaultns>|*" type // default namespace, there is an implicit "<defaultns>|*" type
// selector. // selector.
Some(url) => vec![SimpleSelector::Namespace(Namespace { sequence.push(SimpleSelector::Namespace(Namespace {
prefix: None, prefix: None,
url: url url: url
})], }));
None => vec![],
} }
} else {
empty = false;
} }
Some(s) => { empty = false; s }
};
let mut pseudo_element = None; let mut pseudo_element = None;
loop { loop {
match parse_one_simple_selector(parser, input, /* inside_negation = */ false)? { match parse_one_simple_selector(parser, input, /* inside_negation = */ false)? {
None => break, None => break,
Some(SimpleSelectorParseResult::SimpleSelector(s)) => { Some(SimpleSelectorParseResult::SimpleSelector(s)) => {
compound_selector.push(s); sequence.push(s);
empty = false empty = false
} }
Some(SimpleSelectorParseResult::PseudoElement(p)) => { Some(SimpleSelectorParseResult::PseudoElement(p)) => {
@ -1110,7 +1220,7 @@ fn parse_compound_selector<P, Impl>(
// An empty selector is invalid. // An empty selector is invalid.
Err(()) Err(())
} else { } else {
Ok((compound_selector, pseudo_element)) Ok(pseudo_element)
} }
} }
@ -1244,7 +1354,6 @@ pub mod tests {
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::sync::Arc;
use super::*; use super::*;
#[derive(PartialEq, Clone, Debug, Hash, Eq)] #[derive(PartialEq, Clone, Debug, Hash, Eq)]
@ -1408,57 +1517,45 @@ pub mod tests {
assert_eq!(parse(":lang(4)"), Err(())) ; assert_eq!(parse(":lang(4)"), Err(())) ;
assert_eq!(parse(":lang(en US)"), Err(())) ; assert_eq!(parse(":lang(en US)"), Err(())) ;
assert_eq!(parse("EeÉ"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("EeÉ"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(vec!(SimpleSelector::LocalName(LocalName {
compound_selector: vec!(SimpleSelector::LocalName(LocalName {
name: DummyAtom::from("EeÉ"), name: DummyAtom::from("EeÉ"),
lower_name: DummyAtom::from("eeÉ") })), lower_name: DummyAtom::from("eeÉ") })),
next: None, ),
})),
pseudo_element: None, pseudo_element: None,
specificity: specificity(0, 0, 1), specificity: specificity(0, 0, 1),
})))); }))));
assert_eq!(parse(".foo:lang(en-US)"), Ok(SelectorList(vec!(Selector { assert_eq!(parse(".foo:lang(en-US)"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(vec![
compound_selector: vec![
SimpleSelector::Class(DummyAtom::from("foo")), SimpleSelector::Class(DummyAtom::from("foo")),
SimpleSelector::NonTSPseudoClass(PseudoClass::Lang("en-US".to_owned())) SimpleSelector::NonTSPseudoClass(PseudoClass::Lang("en-US".to_owned()))
], ]),
next: None,
})),
pseudo_element: None, pseudo_element: None,
specificity: specificity(0, 2, 0), specificity: specificity(0, 2, 0),
})))); }))));
assert_eq!(parse("#bar"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("#bar"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(vec!(SimpleSelector::ID(DummyAtom::from("bar")))),
compound_selector: vec!(SimpleSelector::ID(DummyAtom::from("bar"))),
next: None,
})),
pseudo_element: None, pseudo_element: None,
specificity: specificity(1, 0, 0), specificity: specificity(1, 0, 0),
})))); }))));
assert_eq!(parse("e.foo#bar"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("e.foo#bar"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(vec!(SimpleSelector::LocalName(LocalName {
compound_selector: vec!(SimpleSelector::LocalName(LocalName {
name: DummyAtom::from("e"), name: DummyAtom::from("e"),
lower_name: DummyAtom::from("e") }), lower_name: DummyAtom::from("e") }),
SimpleSelector::Class(DummyAtom::from("foo")), SimpleSelector::Class(DummyAtom::from("foo")),
SimpleSelector::ID(DummyAtom::from("bar"))), SimpleSelector::ID(DummyAtom::from("bar")))),
next: None,
})),
pseudo_element: None, pseudo_element: None,
specificity: specificity(1, 1, 1), specificity: specificity(1, 1, 1),
})))); }))));
assert_eq!(parse("e.foo #bar"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("e.foo #bar"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(vec!(
compound_selector: vec!(SimpleSelector::ID(DummyAtom::from("bar"))), SimpleSelector::LocalName(LocalName {
next: Some((Arc::new(ComplexSelector {
compound_selector: vec!(SimpleSelector::LocalName(LocalName {
name: DummyAtom::from("e"), name: DummyAtom::from("e"),
lower_name: DummyAtom::from("e") }), lower_name: DummyAtom::from("e")
SimpleSelector::Class(DummyAtom::from("foo"))), }),
next: None, SimpleSelector::Class(DummyAtom::from("foo")),
}), Combinator::Descendant)), SimpleSelector::Combinator(Combinator::Descendant),
})), SimpleSelector::ID(DummyAtom::from("bar")),
)),
pseudo_element: None, pseudo_element: None,
specificity: specificity(1, 1, 1), specificity: specificity(1, 1, 1),
})))); }))));
@ -1466,25 +1563,22 @@ pub mod tests {
// https://github.com/mozilla/servo/pull/1652 // https://github.com/mozilla/servo/pull/1652
let mut parser = DummyParser::default(); let mut parser = DummyParser::default();
assert_eq!(parse_ns("[Foo]", &parser), Ok(SelectorList(vec!(Selector { assert_eq!(parse_ns("[Foo]", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(vec!(
compound_selector: vec!(SimpleSelector::AttrExists(AttrSelector { SimpleSelector::AttrExists(AttrSelector {
name: DummyAtom::from("Foo"), name: DummyAtom::from("Foo"),
lower_name: DummyAtom::from("foo"), lower_name: DummyAtom::from("foo"),
namespace: NamespaceConstraint::Specific(Namespace { namespace: NamespaceConstraint::Specific(Namespace {
prefix: None, prefix: None,
url: "".into(), url: "".into(),
}), }) }))),
})),
next: None,
})),
pseudo_element: None, pseudo_element: None,
specificity: specificity(0, 1, 0), specificity: specificity(0, 1, 0),
})))); }))));
assert_eq!(parse_ns("svg|circle", &parser), Err(())); assert_eq!(parse_ns("svg|circle", &parser), Err(()));
parser.ns_prefixes.insert(DummyAtom("svg".into()), DummyAtom(SVG.into())); parser.ns_prefixes.insert(DummyAtom("svg".into()), DummyAtom(SVG.into()));
assert_eq!(parse_ns("svg|circle", &parser), Ok(SelectorList(vec![Selector { assert_eq!(parse_ns("svg|circle", &parser), Ok(SelectorList(vec![Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(
compound_selector: vec![ vec![
SimpleSelector::Namespace(Namespace { SimpleSelector::Namespace(Namespace {
prefix: Some(DummyAtom("svg".into())), prefix: Some(DummyAtom("svg".into())),
url: SVG.into(), url: SVG.into(),
@ -1493,9 +1587,7 @@ pub mod tests {
name: DummyAtom::from("circle"), name: DummyAtom::from("circle"),
lower_name: DummyAtom::from("circle"), lower_name: DummyAtom::from("circle"),
}) })
], ]),
next: None,
})),
pseudo_element: None, pseudo_element: None,
specificity: specificity(0, 0, 1), specificity: specificity(0, 0, 1),
}]))); }])));
@ -1505,8 +1597,8 @@ pub mod tests {
// https://github.com/servo/rust-selectors/pull/82 // https://github.com/servo/rust-selectors/pull/82
parser.default_ns = Some(MATHML.into()); parser.default_ns = Some(MATHML.into());
assert_eq!(parse_ns("[Foo]", &parser), Ok(SelectorList(vec!(Selector { assert_eq!(parse_ns("[Foo]", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(
compound_selector: vec![ vec![
SimpleSelector::Namespace(Namespace { SimpleSelector::Namespace(Namespace {
prefix: None, prefix: None,
url: MATHML.into(), url: MATHML.into(),
@ -1519,16 +1611,14 @@ pub mod tests {
url: "".into(), url: "".into(),
}), }),
}), }),
], ]),
next: None,
})),
pseudo_element: None, pseudo_element: None,
specificity: specificity(0, 1, 0), specificity: specificity(0, 1, 0),
})))); }))));
// Default namespace does apply to type selectors // Default namespace does apply to type selectors
assert_eq!(parse_ns("e", &parser), Ok(SelectorList(vec!(Selector { assert_eq!(parse_ns("e", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(
compound_selector: vec!( vec!(
SimpleSelector::Namespace(Namespace { SimpleSelector::Namespace(Namespace {
prefix: None, prefix: None,
url: MATHML.into(), url: MATHML.into(),
@ -1536,15 +1626,13 @@ pub mod tests {
SimpleSelector::LocalName(LocalName { SimpleSelector::LocalName(LocalName {
name: DummyAtom::from("e"), name: DummyAtom::from("e"),
lower_name: DummyAtom::from("e") }), lower_name: DummyAtom::from("e") }),
), )),
next: None,
})),
pseudo_element: None, pseudo_element: None,
specificity: specificity(0, 0, 1), specificity: specificity(0, 0, 1),
})))); }))));
assert_eq!(parse("[attr |= \"foo\"]"), Ok(SelectorList(vec![Selector { assert_eq!(parse("[attr |= \"foo\"]"), Ok(SelectorList(vec![Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(
compound_selector: vec![ vec![
SimpleSelector::AttrDashMatch(AttrSelector { SimpleSelector::AttrDashMatch(AttrSelector {
name: DummyAtom::from("attr"), name: DummyAtom::from("attr"),
lower_name: DummyAtom::from("attr"), lower_name: DummyAtom::from("attr"),
@ -1553,70 +1641,48 @@ pub mod tests {
url: "".into(), url: "".into(),
}), }),
}, DummyAtom::from("foo")) }, DummyAtom::from("foo"))
], ]),
next: None,
})),
pseudo_element: None, pseudo_element: None,
specificity: specificity(0, 1, 0), specificity: specificity(0, 1, 0),
}]))); }])));
// https://github.com/mozilla/servo/issues/1723 // https://github.com/mozilla/servo/issues/1723
assert_eq!(parse("::before"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("::before"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(vec![]),
compound_selector: vec!(),
next: None,
})),
pseudo_element: Some(PseudoElement::Before), pseudo_element: Some(PseudoElement::Before),
specificity: specificity(0, 0, 1), specificity: specificity(0, 0, 1),
})))); }))));
// https://github.com/servo/servo/issues/15335 // https://github.com/servo/servo/issues/15335
assert_eq!(parse(":: before"), Err(())); assert_eq!(parse(":: before"), Err(()));
assert_eq!(parse("div ::after"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("div ::after"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(
compound_selector: vec!(), vec![
next: Some((Arc::new(ComplexSelector { SimpleSelector::LocalName(LocalName {
compound_selector: vec!(SimpleSelector::LocalName(LocalName {
name: DummyAtom::from("div"), name: DummyAtom::from("div"),
lower_name: DummyAtom::from("div") })), lower_name: DummyAtom::from("div") }),
next: None, SimpleSelector::Combinator(Combinator::Descendant),
}), Combinator::Descendant)), ]),
})),
pseudo_element: Some(PseudoElement::After), pseudo_element: Some(PseudoElement::After),
specificity: specificity(0, 0, 2), specificity: specificity(0, 0, 2),
})))); }))));
assert_eq!(parse("#d1 > .ok"), Ok(SelectorList(vec![Selector { assert_eq!(parse("#d1 > .ok"), Ok(SelectorList(vec![Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(
compound_selector: vec![ vec![
SimpleSelector::Class(DummyAtom::from("ok")),
],
next: Some((Arc::new(ComplexSelector {
compound_selector: vec![
SimpleSelector::ID(DummyAtom::from("d1")), SimpleSelector::ID(DummyAtom::from("d1")),
], SimpleSelector::Combinator(Combinator::Child),
next: None, SimpleSelector::Class(DummyAtom::from("ok")),
}), Combinator::Child)), ]),
})),
pseudo_element: None, pseudo_element: None,
specificity: (1 << 20) + (1 << 10) + (0 << 0), specificity: (1 << 20) + (1 << 10) + (0 << 0),
}]))); }])));
assert_eq!(parse(":not(.babybel, #provel.old)"), Ok(SelectorList(vec!(Selector { assert_eq!(parse(":not(.babybel, #provel.old)"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(vec!(SimpleSelector::Negation(
compound_selector: vec!(SimpleSelector::Negation(
vec!( vec!(
Arc::new(ComplexSelector { ComplexSelector::from_vec(vec!(SimpleSelector::Class(DummyAtom::from("babybel")))),
compound_selector: vec!(SimpleSelector::Class(DummyAtom::from("babybel"))), ComplexSelector::from_vec(vec!(
next: None
}),
Arc::new(ComplexSelector {
compound_selector: vec!(
SimpleSelector::ID(DummyAtom::from("provel")), SimpleSelector::ID(DummyAtom::from("provel")),
SimpleSelector::Class(DummyAtom::from("old")), SimpleSelector::Class(DummyAtom::from("old")),
), )))
next: None ))),
}),
)
)),
next: None,
})),
pseudo_element: None, pseudo_element: None,
specificity: specificity(1, 1, 0), specificity: specificity(1, 1, 0),
})))); }))));

View file

@ -6,7 +6,8 @@
#![deny(missing_docs)] #![deny(missing_docs)]
use parser::{AttrSelector, Combinator, ComplexSelector, SelectorImpl, SimpleSelector}; use parser::{AttrSelector, Combinator, SelectorImpl};
use parser::{SelectorIter, SimpleSelector};
/// A trait to visit selector properties. /// A trait to visit selector properties.
/// ///
@ -33,7 +34,7 @@ pub trait SelectorVisitor {
/// Gets the combinator to the right of the selector, or `None` if the /// Gets the combinator to the right of the selector, or `None` if the
/// selector is the leftmost one. /// selector is the leftmost one.
fn visit_complex_selector(&mut self, fn visit_complex_selector(&mut self,
_: &ComplexSelector<Self::Impl>, _: SelectorIter<Self::Impl>,
_combinator_to_right: Option<Combinator>) _combinator_to_right: Option<Combinator>)
-> bool { -> bool {
true true

View file

@ -14,7 +14,6 @@ use selectors::visitor::SelectorVisitor;
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::ptr; use std::ptr;
use std::sync::Arc;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
/// A representation of a CSS pseudo-element. /// 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 /// TODO(emilio): We disallow combinators and pseudos here, so we
/// should use SimpleSelector instead /// should use SimpleSelector instead
MozAny(Vec<Arc<ComplexSelector<SelectorImpl>>>), MozAny(Vec<ComplexSelector<SelectorImpl>>),
} }
} }
} }
@ -396,10 +395,10 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
}, )* }, )*
"-moz-any" => { "-moz-any" => {
let selectors = parser.parse_comma_separated(|input| { 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. // 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(()) return Err(())
} }
NonTSPseudoClass::MozAny(selectors) NonTSPseudoClass::MozAny(selectors)

View file

@ -17,10 +17,10 @@ use selector_parser::{AttrValue, NonTSPseudoClass, Snapshot, SelectorImpl};
use selectors::{Element, MatchAttr}; use selectors::{Element, MatchAttr};
use selectors::matching::{ElementSelectorFlags, StyleRelations}; use selectors::matching::{ElementSelectorFlags, StyleRelations};
use selectors::matching::matches_selector; use selectors::matching::matches_selector;
use selectors::parser::{AttrSelector, Combinator, ComplexSelector, SelectorInner, SelectorMethods, SimpleSelector}; use selectors::parser::{AttrSelector, Combinator, ComplexSelector, SelectorInner, SelectorIter};
use selectors::parser::{SelectorMethods, SimpleSelector};
use selectors::visitor::SelectorVisitor; use selectors::visitor::SelectorVisitor;
use std::clone::Clone; use std::clone::Clone;
use std::sync::Arc;
bitflags! { bitflags! {
/// When the ElementState of an element (like IN_HOVER_STATE) changes, /// When the ElementState of an element (like IN_HOVER_STATE) changes,
@ -509,7 +509,7 @@ impl SelectorVisitor for SensitivitiesVisitor {
type Impl = SelectorImpl; type Impl = SelectorImpl;
fn visit_complex_selector(&mut self, fn visit_complex_selector(&mut self,
_: &ComplexSelector<SelectorImpl>, _: SelectorIter<SelectorImpl>,
combinator: Option<Combinator>) -> bool { combinator: Option<Combinator>) -> bool {
self.hint |= combinator_to_restyle_hint(combinator); self.hint |= combinator_to_restyle_hint(combinator);
self.needs_revalidation |= self.hint.contains(RESTYLE_LATER_SIBLINGS); self.needs_revalidation |= self.hint.contains(RESTYLE_LATER_SIBLINGS);
@ -567,50 +567,47 @@ impl DependencySet {
/// cache revalidation, that is, whether two siblings of the same "shape" /// cache revalidation, that is, whether two siblings of the same "shape"
/// may have different style due to this selector. /// may have different style due to this selector.
pub fn note_selector(&mut self, pub fn note_selector(&mut self,
selector: &Arc<ComplexSelector<SelectorImpl>>) base: &ComplexSelector<SelectorImpl>)
-> bool -> bool
{ {
let mut next = Some(base.clone());
let mut combinator = None; let mut combinator = None;
let mut current = selector;
let mut needs_revalidation = false; let mut needs_revalidation = false;
loop { while let Some(current) = next.take() {
let mut sensitivities_visitor = SensitivitiesVisitor { // Set up our visitor.
let mut visitor = SensitivitiesVisitor {
sensitivities: Sensitivities::new(), sensitivities: Sensitivities::new(),
hint: RestyleHint::empty(), hint: combinator_to_restyle_hint(combinator),
needs_revalidation: false, needs_revalidation: false,
}; };
for ss in &current.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;
} }
needs_revalidation |= sensitivities_visitor.needs_revalidation; // 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);
}
}
let SensitivitiesVisitor { // Note what we found.
sensitivities, needs_revalidation |= visitor.needs_revalidation;
mut hint, if !visitor.sensitivities.is_empty() {
..
} = sensitivities_visitor;
hint |= combinator_to_restyle_hint(combinator);
if !sensitivities.is_empty() {
self.add_dependency(Dependency { self.add_dependency(Dependency {
sensitivities: sensitivities, sensitivities: visitor.sensitivities,
hint: hint, hint: visitor.hint,
selector: SelectorInner::new(current.clone()), selector: SelectorInner::new(current),
}) })
} }
match current.next {
Some((ref next, next_combinator)) => {
current = next;
combinator = Some(next_combinator);
}
None => break,
}
} }
needs_revalidation needs_revalidation
@ -729,7 +726,7 @@ fn smoke_restyle_hints() {
let mut dependencies = DependencySet::new(); let mut dependencies = DependencySet::new();
let mut p = Parser::new(":not(:active) ~ label"); 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); dependencies.note_selector(&selector);
assert_eq!(dependencies.len(), 1); assert_eq!(dependencies.len(), 1);
assert_eq!(dependencies.state_deps.len(), 1); assert_eq!(dependencies.state_deps.len(), 1);

View file

@ -1070,8 +1070,7 @@ impl SelectorMap {
// correct, and also to not trigger rule tree assertions. // correct, and also to not trigger rule tree assertions.
let mut important = vec![]; let mut important = vec![];
for rule in self.other_rules.iter() { for rule in self.other_rules.iter() {
if rule.selector.complex.compound_selector.is_empty() && if rule.selector.complex.iter_raw().next().is_none() {
rule.selector.complex.next.is_none() {
let style_rule = rule.style_rule.read_with(guard); let style_rule = rule.style_rule.read_with(guard);
let block = style_rule.block.read_with(guard); let block = style_rule.block.read_with(guard);
if block.any_normal() { if block.any_normal() {
@ -1180,7 +1179,7 @@ impl SelectorMap {
/// Retrieve the first ID name in Rule, or None otherwise. /// Retrieve the first ID name in Rule, or None otherwise.
pub fn get_id_name(rule: &Rule) -> Option<Atom> { pub fn get_id_name(rule: &Rule) -> Option<Atom> {
for ss in &rule.selector.complex.compound_selector { for ss in rule.selector.complex.iter() {
// TODO(pradeep): Implement case-sensitivity based on the // TODO(pradeep): Implement case-sensitivity based on the
// document type and quirks mode. // document type and quirks mode.
if let SimpleSelector::ID(ref id) = *ss { if let SimpleSelector::ID(ref id) = *ss {
@ -1193,7 +1192,7 @@ impl SelectorMap {
/// Retrieve the FIRST class name in Rule, or None otherwise. /// Retrieve the FIRST class name in Rule, or None otherwise.
pub fn get_class_name(rule: &Rule) -> Option<Atom> { pub fn get_class_name(rule: &Rule) -> Option<Atom> {
for ss in &rule.selector.complex.compound_selector { for ss in rule.selector.complex.iter() {
// TODO(pradeep): Implement case-sensitivity based on the // TODO(pradeep): Implement case-sensitivity based on the
// document type and quirks mode. // document type and quirks mode.
if let SimpleSelector::Class(ref class) = *ss { if let SimpleSelector::Class(ref class) = *ss {
@ -1206,7 +1205,7 @@ impl SelectorMap {
/// Retrieve the name if it is a type selector, or None otherwise. /// Retrieve the name if it is a type selector, or None otherwise.
pub fn get_local_name(rule: &Rule) -> Option<LocalNameSelector<SelectorImpl>> { pub fn get_local_name(rule: &Rule) -> Option<LocalNameSelector<SelectorImpl>> {
for ss in &rule.selector.complex.compound_selector { for ss in rule.selector.complex.iter() {
if let SimpleSelector::LocalName(ref n) = *ss { if let SimpleSelector::LocalName(ref n) = *ss {
return Some(LocalNameSelector { return Some(LocalNameSelector {
name: n.name.clone(), name: n.name.clone(),

View file

@ -84,8 +84,7 @@ fn test_parse_stylesheet() {
CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule { CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
selectors: SelectorList(vec![ selectors: SelectorList(vec![
Selector { Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(vec![
compound_selector: vec![
SimpleSelector::Namespace(Namespace { SimpleSelector::Namespace(Namespace {
prefix: None, prefix: None,
url: NsAtom::from("http://www.w3.org/1999/xhtml") url: NsAtom::from("http://www.w3.org/1999/xhtml")
@ -102,9 +101,7 @@ fn test_parse_stylesheet() {
url: ns!() url: ns!()
}), }),
}, "hidden".to_owned(), CaseSensitivity::CaseInsensitive) }, "hidden".to_owned(), CaseSensitivity::CaseInsensitive)
], ]),
next: None,
})),
pseudo_element: None, pseudo_element: None,
specificity: (0 << 20) + (1 << 10) + (1 << 0), specificity: (0 << 20) + (1 << 10) + (1 << 0),
}, },
@ -120,8 +117,7 @@ fn test_parse_stylesheet() {
CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule { CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
selectors: SelectorList(vec![ selectors: SelectorList(vec![
Selector { Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(vec![
compound_selector: vec![
SimpleSelector::Namespace(Namespace { SimpleSelector::Namespace(Namespace {
prefix: None, prefix: None,
url: NsAtom::from("http://www.w3.org/1999/xhtml") url: NsAtom::from("http://www.w3.org/1999/xhtml")
@ -130,15 +126,12 @@ fn test_parse_stylesheet() {
name: local_name!("html"), name: local_name!("html"),
lower_name: local_name!("html"), lower_name: local_name!("html"),
}), }),
], ]),
next: None,
})),
pseudo_element: None, pseudo_element: None,
specificity: (0 << 20) + (0 << 10) + (1 << 0), specificity: (0 << 20) + (0 << 10) + (1 << 0),
}, },
Selector { Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(vec![
compound_selector: vec![
SimpleSelector::Namespace(Namespace { SimpleSelector::Namespace(Namespace {
prefix: None, prefix: None,
url: NsAtom::from("http://www.w3.org/1999/xhtml") url: NsAtom::from("http://www.w3.org/1999/xhtml")
@ -147,9 +140,7 @@ fn test_parse_stylesheet() {
name: local_name!("body"), name: local_name!("body"),
lower_name: local_name!("body"), lower_name: local_name!("body"),
}), }),
], ]),
next: None,
})),
pseudo_element: None, pseudo_element: None,
specificity: (0 << 20) + (0 << 10) + (1 << 0), specificity: (0 << 20) + (0 << 10) + (1 << 0),
}, },
@ -162,25 +153,19 @@ fn test_parse_stylesheet() {
CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule { CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
selectors: SelectorList(vec![ selectors: SelectorList(vec![
Selector { Selector {
inner: SelectorInner::new(Arc::new(ComplexSelector { inner: SelectorInner::from_vec(vec![
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 { SimpleSelector::Namespace(Namespace {
prefix: None, prefix: None,
url: NsAtom::from("http://www.w3.org/1999/xhtml") url: NsAtom::from("http://www.w3.org/1999/xhtml")
}), }),
SimpleSelector::ID(Atom::from("d1")), SimpleSelector::ID(Atom::from("d1")),
], SimpleSelector::Combinator(Combinator::Child),
next: None, SimpleSelector::Namespace(Namespace {
}), Combinator::Child)), prefix: None,
})), url: NsAtom::from("http://www.w3.org/1999/xhtml")
}),
SimpleSelector::Class(Atom::from("ok")),
]),
pseudo_element: None, pseudo_element: None,
specificity: (1 << 20) + (1 << 10) + (0 << 0), specificity: (1 << 20) + (1 << 10) + (0 << 0),
}, },

View file

@ -77,7 +77,7 @@ fn test_get_id_name() {
#[test] #[test]
fn test_get_class_name() { fn test_get_class_name() {
let (rules_list, _) = get_mock_rules(&[".intro.foo", "#top"]); 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); 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()); selector_map.insert(rules_list[1][0].clone());
assert_eq!(1, selector_map.id_hash.get(&Atom::from("top")).unwrap()[0].source_order); assert_eq!(1, selector_map.id_hash.get(&Atom::from("top")).unwrap()[0].source_order);
selector_map.insert(rules_list[0][0].clone()); selector_map.insert(rules_list[0][0].clone());
assert_eq!(0, selector_map.class_hash.get(&Atom::from("intro")).unwrap()[0].source_order); assert_eq!(0, selector_map.class_hash.get(&Atom::from("foo")).unwrap()[0].source_order);
assert!(selector_map.class_hash.get(&Atom::from("foo")).is_none()); assert!(selector_map.class_hash.get(&Atom::from("intro")).is_none());
} }
#[test] #[test]