mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
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:
parent
93fa0ae1e3
commit
6d66ec5e11
10 changed files with 703 additions and 330 deletions
326
components/selectors/arcslice.rs
Normal file
326
components/selectors/arcslice.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* 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::{SimpleSelector, Selector, SelectorInner, SelectorIter};
|
||||
use std::borrow::Borrow;
|
||||
use tree::Element;
|
||||
|
||||
|
@ -207,7 +207,7 @@ pub fn matches_complex_selector<E, F>(selector: &ComplexSelector<E::Impl>,
|
|||
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<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,
|
||||
relations: &mut StyleRelations,
|
||||
flags_setter: &mut F)
|
||||
|
@ -224,14 +224,12 @@ fn matches_complex_selector_internal<E, F>(selector: &ComplexSelector<E::Impl>,
|
|||
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<E, F>(selector: &ComplexSelector<E::Impl>,
|
|||
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<E, F>(selector: &ComplexSelector<E::Impl>,
|
|||
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,
|
||||
|
@ -319,6 +317,7 @@ fn matches_simple_selector<E, F>(
|
|||
}
|
||||
|
||||
match *selector {
|
||||
SimpleSelector::Combinator(_) => unreachable!(),
|
||||
SimpleSelector::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()
|
||||
|
@ -421,7 +420,7 @@ fn matches_simple_selector<E, F>(
|
|||
}
|
||||
SimpleSelector::Negation(ref negated) => {
|
||||
!negated.iter().all(|s| {
|
||||
match matches_complex_selector_internal(s,
|
||||
match matches_complex_selector_internal(s.iter(),
|
||||
element,
|
||||
relations,
|
||||
flags_setter) {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* 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 std::ascii::AsciiExt;
|
||||
|
@ -9,8 +10,9 @@ 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 +134,7 @@ const NUM_ANCESTOR_HASHES: usize = 4;
|
|||
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||
pub struct SelectorInner<Impl: SelectorImpl> {
|
||||
/// The selector data.
|
||||
pub complex: Arc<ComplexSelector<Impl>>,
|
||||
pub complex: ComplexSelector<Impl>,
|
||||
/// Ancestor hashes for the bloom filter. We precompute these and store
|
||||
/// them inline to optimize cache performance during selector matching.
|
||||
/// This matters a lot.
|
||||
|
@ -140,15 +142,14 @@ pub struct SelectorInner<Impl: SelectorImpl> {
|
|||
}
|
||||
|
||||
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];
|
||||
{
|
||||
// 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 +163,12 @@ impl<Impl: SelectorImpl> SelectorInner<Impl> {
|
|||
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)]
|
||||
|
@ -194,25 +201,22 @@ impl<Impl: SelectorImpl> SelectorMethods for ComplexSelector<Impl> {
|
|||
fn visit<V>(&self, visitor: &mut V) -> bool
|
||||
where V: SelectorVisitor<Impl = Impl>,
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)]
|
||||
pub struct ComplexSelector<Impl: SelectorImpl> {
|
||||
pub compound_selector: Vec<SimpleSelector<Impl>>,
|
||||
pub next: Option<(Arc<ComplexSelector<Impl>>, Combinator)>, // c.next is left of c
|
||||
}
|
||||
pub struct ComplexSelector<Impl: SelectorImpl>(ArcSlice<SimpleSelector<Impl>>);
|
||||
|
||||
struct AncestorIterator<'a, Impl: 'a + SelectorImpl> {
|
||||
curr: Option<&'a Arc<ComplexSelector<Impl>>>,
|
||||
}
|
||||
|
||||
impl<'a, Impl: SelectorImpl> Iterator for AncestorIterator<'a, Impl> {
|
||||
type Item = &'a Arc<ComplexSelector<Impl>>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(sel) = self.curr.take() {
|
||||
let (next_sel, is_ancestor) = match sel.next {
|
||||
None => (None, true),
|
||||
Some((ref sel, comb)) =>
|
||||
(Some(sel), matches!(comb, Combinator::Child | Combinator::Descendant)),
|
||||
};
|
||||
self.curr = next_sel;
|
||||
if is_ancestor {
|
||||
break;
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
self.curr
|
||||
/// Returns an iterator over the entire sequence of simple selectors and combinators,
|
||||
/// from right to left.
|
||||
pub fn iter_raw(&self) -> Rev<slice::Iter<SimpleSelector<Impl>>> {
|
||||
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<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()))
|
||||
}
|
||||
}
|
||||
|
||||
fn iter_ancestors<Impl: SelectorImpl>(sel: &Arc<ComplexSelector<Impl>>) -> AncestorIterator<Impl> {
|
||||
AncestorIterator {
|
||||
curr: Some(sel)
|
||||
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> {
|
||||
debug_assert!(self.next_combinator.is_none(), "Should call take_combinator!");
|
||||
match self.iter.next() {
|
||||
None => None,
|
||||
Some(&SimpleSelector::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 SimpleSelector<Impl>;
|
||||
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, // ~
|
||||
}
|
||||
|
||||
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)]
|
||||
pub enum SimpleSelector<Impl: SelectorImpl> {
|
||||
Combinator(Combinator),
|
||||
ID(Impl::Identifier),
|
||||
Class(Impl::ClassName),
|
||||
LocalName(LocalName<Impl>),
|
||||
|
@ -327,7 +453,7 @@ pub enum SimpleSelector<Impl: SelectorImpl> {
|
|||
AttrSuffixNeverMatch(AttrSelector<Impl>, Impl::AttrValue), // empty value
|
||||
|
||||
// Pseudo-classes
|
||||
Negation(Vec<Arc<ComplexSelector<Impl>>>),
|
||||
Negation(Vec<ComplexSelector<Impl>>),
|
||||
FirstChild, LastChild, OnlyChild,
|
||||
Root,
|
||||
Empty,
|
||||
|
@ -368,6 +494,11 @@ impl<Impl: SelectorImpl> SimpleSelector<Impl> {
|
|||
_ => 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)]
|
||||
|
@ -466,31 +597,11 @@ impl<Impl: SelectorImpl> ToCss for Selector<Impl> {
|
|||
|
||||
impl<Impl: SelectorImpl> ToCss for ComplexSelector<Impl> {
|
||||
fn to_css<W>(&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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
use self::SimpleSelector::*;
|
||||
match *self {
|
||||
Combinator(ref c) => {
|
||||
c.to_css(dest)
|
||||
}
|
||||
ID(ref s) => {
|
||||
dest.write_char('#')?;
|
||||
display_to_css_identifier(s, dest)
|
||||
|
@ -699,14 +813,15 @@ fn specificity<Impl>(complex_selector: &ComplexSelector<Impl>,
|
|||
specificity.into()
|
||||
}
|
||||
|
||||
fn complex_selector_specificity<Impl>(mut selector: &ComplexSelector<Impl>)
|
||||
fn complex_selector_specificity<Impl>(selector: &ComplexSelector<Impl>)
|
||||
-> Specificity
|
||||
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)
|
||||
where Impl: SelectorImpl {
|
||||
for simple_selector in compound_selector.iter() {
|
||||
for simple_selector in selector_iter {
|
||||
match *simple_selector {
|
||||
SimpleSelector::Combinator(..) => unreachable!(),
|
||||
SimpleSelector::LocalName(..) =>
|
||||
specificity.element_selectors += 1,
|
||||
SimpleSelector::ID(..) =>
|
||||
|
@ -749,16 +864,11 @@ fn complex_selector_specificity<Impl>(mut selector: &ComplexSelector<Impl>)
|
|||
}
|
||||
|
||||
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,7 +885,7 @@ fn parse_selector<P, Impl>(parser: &P, input: &mut CssParser) -> Result<Selector
|
|||
parse_complex_selector_and_pseudo_element(parser, input)?;
|
||||
Ok(Selector {
|
||||
specificity: specificity(&complex, pseudo_element.as_ref()),
|
||||
inner: SelectorInner::new(Arc::new(complex)),
|
||||
inner: SelectorInner::new(complex),
|
||||
pseudo_element: pseudo_element,
|
||||
})
|
||||
}
|
||||
|
@ -786,10 +896,16 @@ fn parse_complex_selector_and_pseudo_element<P, Impl>(
|
|||
-> Result<(ComplexSelector<Impl>, Option<Impl::PseudoElement>), ()>
|
||||
where P: Parser<Impl=Impl>, Impl: SelectorImpl
|
||||
{
|
||||
let (first, mut pseudo_element) = parse_compound_selector(parser, input)?;
|
||||
let mut complex = ComplexSelector { compound_selector: first, next: None };
|
||||
let mut sequence = Vec::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 +936,10 @@ fn parse_complex_selector_and_pseudo_element<P, Impl>(
|
|||
}
|
||||
}
|
||||
}
|
||||
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(SimpleSelector::Combinator(combinator));
|
||||
}
|
||||
|
||||
let complex = ComplexSelector(ArcSlice::new(sequence.into_boxed_slice()));
|
||||
Ok((complex, pseudo_element))
|
||||
}
|
||||
|
||||
|
@ -848,30 +960,29 @@ impl<Impl: SelectorImpl> ComplexSelector<Impl> {
|
|||
/// * `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<P, Impl>(parser: &P, input: &mut CssParser)
|
||||
-> Result<Option<Vec<SimpleSelector<Impl>>>, ()>
|
||||
fn parse_type_selector<P, Impl>(parser: &P, input: &mut CssParser, sequence: &mut Vec<SimpleSelector<Impl>>)
|
||||
-> Result<bool, ()>
|
||||
where P: Parser<Impl=Impl>, 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(SimpleSelector::Namespace(ns))
|
||||
},
|
||||
NamespaceConstraint::Any => (),
|
||||
}
|
||||
match local_name {
|
||||
Some(name) => {
|
||||
compound_selector.push(SimpleSelector::LocalName(LocalName {
|
||||
sequence.push(SimpleSelector::LocalName(LocalName {
|
||||
lower_name: from_ascii_lowercase(&name),
|
||||
name: from_cow_str(name),
|
||||
}))
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
Ok(Some(compound_selector))
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1051,7 +1162,7 @@ fn parse_negation<P, Impl>(parser: &P,
|
|||
-> Result<SimpleSelector<Impl>, ()>
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -1062,8 +1173,9 @@ fn parse_negation<P, Impl>(parser: &P,
|
|||
/// `Err(())` means invalid selector
|
||||
fn parse_compound_selector<P, Impl>(
|
||||
parser: &P,
|
||||
input: &mut CssParser)
|
||||
-> Result<(Vec<SimpleSelector<Impl>>, Option<Impl::PseudoElement>), ()>
|
||||
input: &mut CssParser,
|
||||
mut sequence: &mut Vec<SimpleSelector<Impl>>)
|
||||
-> Result<Option<Impl::PseudoElement>, ()>
|
||||
where P: Parser<Impl=Impl>, Impl: SelectorImpl
|
||||
{
|
||||
// Consume any leading whitespace.
|
||||
|
@ -1075,28 +1187,26 @@ fn parse_compound_selector<P, Impl>(
|
|||
}
|
||||
}
|
||||
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 "<defaultns>|*" 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 "<defaultns>|*" type
|
||||
// selector.
|
||||
sequence.push(SimpleSelector::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 +1220,7 @@ fn parse_compound_selector<P, Impl>(
|
|||
// An empty selector is invalid.
|
||||
Err(())
|
||||
} else {
|
||||
Ok((compound_selector, pseudo_element))
|
||||
Ok(pseudo_element)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1244,7 +1354,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 +1517,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!(SimpleSelector::LocalName(LocalName {
|
||||
name: DummyAtom::from("EeÉ"),
|
||||
lower_name: DummyAtom::from("eeÉ") })),
|
||||
next: None,
|
||||
})),
|
||||
),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 1),
|
||||
}))));
|
||||
assert_eq!(parse(".foo:lang(en-US)"), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::new(Arc::new(ComplexSelector {
|
||||
compound_selector: vec![
|
||||
inner: SelectorInner::from_vec(vec![
|
||||
SimpleSelector::Class(DummyAtom::from("foo")),
|
||||
SimpleSelector::NonTSPseudoClass(PseudoClass::Lang("en-US".to_owned()))
|
||||
],
|
||||
next: None,
|
||||
})),
|
||||
]),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 2, 0),
|
||||
}))));
|
||||
assert_eq!(parse("#bar"), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::new(Arc::new(ComplexSelector {
|
||||
compound_selector: vec!(SimpleSelector::ID(DummyAtom::from("bar"))),
|
||||
next: None,
|
||||
})),
|
||||
inner: SelectorInner::from_vec(vec!(SimpleSelector::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!(SimpleSelector::LocalName(LocalName {
|
||||
name: DummyAtom::from("e"),
|
||||
lower_name: DummyAtom::from("e") }),
|
||||
SimpleSelector::Class(DummyAtom::from("foo")),
|
||||
SimpleSelector::ID(DummyAtom::from("bar"))),
|
||||
next: None,
|
||||
})),
|
||||
SimpleSelector::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!(
|
||||
SimpleSelector::LocalName(LocalName {
|
||||
name: DummyAtom::from("e"),
|
||||
lower_name: DummyAtom::from("e")
|
||||
}),
|
||||
SimpleSelector::Class(DummyAtom::from("foo")),
|
||||
SimpleSelector::Combinator(Combinator::Descendant),
|
||||
SimpleSelector::ID(DummyAtom::from("bar")),
|
||||
)),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(1, 1, 1),
|
||||
}))));
|
||||
|
@ -1466,25 +1563,22 @@ 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!(
|
||||
SimpleSelector::AttrExists(AttrSelector {
|
||||
name: DummyAtom::from("Foo"),
|
||||
lower_name: DummyAtom::from("foo"),
|
||||
namespace: NamespaceConstraint::Specific(Namespace {
|
||||
prefix: None,
|
||||
url: "".into(),
|
||||
}),
|
||||
})),
|
||||
next: None,
|
||||
})),
|
||||
}) }))),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 1, 0),
|
||||
}))));
|
||||
assert_eq!(parse_ns("svg|circle", &parser), Err(()));
|
||||
parser.ns_prefixes.insert(DummyAtom("svg".into()), DummyAtom(SVG.into()));
|
||||
assert_eq!(parse_ns("svg|circle", &parser), Ok(SelectorList(vec![Selector {
|
||||
inner: SelectorInner::new(Arc::new(ComplexSelector {
|
||||
compound_selector: vec![
|
||||
inner: SelectorInner::from_vec(
|
||||
vec![
|
||||
SimpleSelector::Namespace(Namespace {
|
||||
prefix: Some(DummyAtom("svg".into())),
|
||||
url: SVG.into(),
|
||||
|
@ -1493,9 +1587,7 @@ pub mod tests {
|
|||
name: DummyAtom::from("circle"),
|
||||
lower_name: DummyAtom::from("circle"),
|
||||
})
|
||||
],
|
||||
next: None,
|
||||
})),
|
||||
]),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 1),
|
||||
}])));
|
||||
|
@ -1505,8 +1597,8 @@ 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![
|
||||
inner: SelectorInner::from_vec(
|
||||
vec![
|
||||
SimpleSelector::Namespace(Namespace {
|
||||
prefix: None,
|
||||
url: MATHML.into(),
|
||||
|
@ -1519,16 +1611,14 @@ 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!(
|
||||
inner: SelectorInner::from_vec(
|
||||
vec!(
|
||||
SimpleSelector::Namespace(Namespace {
|
||||
prefix: None,
|
||||
url: MATHML.into(),
|
||||
|
@ -1536,15 +1626,13 @@ pub mod tests {
|
|||
SimpleSelector::LocalName(LocalName {
|
||||
name: DummyAtom::from("e"),
|
||||
lower_name: DummyAtom::from("e") }),
|
||||
),
|
||||
next: None,
|
||||
})),
|
||||
)),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 1),
|
||||
}))));
|
||||
assert_eq!(parse("[attr |= \"foo\"]"), Ok(SelectorList(vec![Selector {
|
||||
inner: SelectorInner::new(Arc::new(ComplexSelector {
|
||||
compound_selector: vec![
|
||||
inner: SelectorInner::from_vec(
|
||||
vec![
|
||||
SimpleSelector::AttrDashMatch(AttrSelector {
|
||||
name: DummyAtom::from("attr"),
|
||||
lower_name: DummyAtom::from("attr"),
|
||||
|
@ -1553,70 +1641,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![
|
||||
SimpleSelector::LocalName(LocalName {
|
||||
name: DummyAtom::from("div"),
|
||||
lower_name: DummyAtom::from("div") })),
|
||||
next: None,
|
||||
}), Combinator::Descendant)),
|
||||
})),
|
||||
lower_name: DummyAtom::from("div") }),
|
||||
SimpleSelector::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![
|
||||
inner: SelectorInner::from_vec(
|
||||
vec![
|
||||
SimpleSelector::ID(DummyAtom::from("d1")),
|
||||
SimpleSelector::Combinator(Combinator::Child),
|
||||
SimpleSelector::Class(DummyAtom::from("ok")),
|
||||
],
|
||||
next: Some((Arc::new(ComplexSelector {
|
||||
compound_selector: vec![
|
||||
SimpleSelector::ID(DummyAtom::from("d1")),
|
||||
],
|
||||
next: None,
|
||||
}), Combinator::Child)),
|
||||
})),
|
||||
]),
|
||||
pseudo_element: None,
|
||||
specificity: (1 << 20) + (1 << 10) + (0 << 0),
|
||||
}])));
|
||||
assert_eq!(parse(":not(.babybel, #provel.old)"), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::new(Arc::new(ComplexSelector {
|
||||
compound_selector: vec!(SimpleSelector::Negation(
|
||||
inner: SelectorInner::from_vec(vec!(SimpleSelector::Negation(
|
||||
vec!(
|
||||
Arc::new(ComplexSelector {
|
||||
compound_selector: vec!(SimpleSelector::Class(DummyAtom::from("babybel"))),
|
||||
next: None
|
||||
}),
|
||||
Arc::new(ComplexSelector {
|
||||
compound_selector: vec!(
|
||||
ComplexSelector::from_vec(vec!(SimpleSelector::Class(DummyAtom::from("babybel")))),
|
||||
ComplexSelector::from_vec(vec!(
|
||||
SimpleSelector::ID(DummyAtom::from("provel")),
|
||||
SimpleSelector::Class(DummyAtom::from("old")),
|
||||
),
|
||||
next: None
|
||||
}),
|
||||
)
|
||||
)),
|
||||
next: None,
|
||||
})),
|
||||
)))
|
||||
))),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(1, 1, 0),
|
||||
}))));
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
#![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.
|
||||
///
|
||||
|
@ -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<Self::Impl>,
|
||||
_: SelectorIter<Self::Impl>,
|
||||
_combinator_to_right: Option<Combinator>)
|
||||
-> bool {
|
||||
true
|
||||
|
|
|
@ -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<Arc<ComplexSelector<SelectorImpl>>>),
|
||||
MozAny(Vec<ComplexSelector<SelectorImpl>>),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -396,10 +395,10 @@ 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)
|
||||
|
|
|
@ -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, SelectorInner, SelectorIter};
|
||||
use selectors::parser::{SelectorMethods, SimpleSelector};
|
||||
use selectors::visitor::SelectorVisitor;
|
||||
use std::clone::Clone;
|
||||
use std::sync::Arc;
|
||||
|
||||
bitflags! {
|
||||
/// When the ElementState of an element (like IN_HOVER_STATE) changes,
|
||||
|
@ -509,7 +509,7 @@ impl SelectorVisitor for SensitivitiesVisitor {
|
|||
type Impl = SelectorImpl;
|
||||
|
||||
fn visit_complex_selector(&mut self,
|
||||
_: &ComplexSelector<SelectorImpl>,
|
||||
_: SelectorIter<SelectorImpl>,
|
||||
combinator: Option<Combinator>) -> bool {
|
||||
self.hint |= combinator_to_restyle_hint(combinator);
|
||||
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"
|
||||
/// may have different style due to this selector.
|
||||
pub fn note_selector(&mut self,
|
||||
selector: &Arc<ComplexSelector<SelectorImpl>>)
|
||||
base: &ComplexSelector<SelectorImpl>)
|
||||
-> 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);
|
||||
|
|
|
@ -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,7 +1179,7 @@ impl SelectorMap {
|
|||
|
||||
/// Retrieve the first ID name in Rule, or None otherwise.
|
||||
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
|
||||
// document type and quirks mode.
|
||||
if let SimpleSelector::ID(ref id) = *ss {
|
||||
|
@ -1193,7 +1192,7 @@ impl SelectorMap {
|
|||
|
||||
/// Retrieve the FIRST class name in Rule, or None otherwise.
|
||||
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
|
||||
// document type and quirks mode.
|
||||
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.
|
||||
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 {
|
||||
return Some(LocalNameSelector {
|
||||
name: n.name.clone(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue