mirror of
https://github.com/servo/servo.git
synced 2025-06-24 09:04:33 +01:00
Auto merge of #17179 - bholley:one_selector_allocation, r=emilio
shrink Rule and store all heap-allocated selector data inline https://bugzilla.mozilla.org/show_bug.cgi?id=1370107 <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17179) <!-- Reviewable:end -->
This commit is contained in:
commit
74ea8ce3ed
22 changed files with 840 additions and 901 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -2523,6 +2523,7 @@ dependencies = [
|
||||||
"phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
"phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
"phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"servo_arc 0.0.1",
|
||||||
"size_of_test 0.0.1",
|
"size_of_test 0.0.1",
|
||||||
"smallvec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"smallvec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
@ -2688,6 +2689,15 @@ dependencies = [
|
||||||
"url 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "servo_arc"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"nodrop 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "servo_atoms"
|
name = "servo_atoms"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
@ -2913,6 +2923,7 @@ dependencies = [
|
||||||
"selectors 0.19.0",
|
"selectors 0.19.0",
|
||||||
"serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"servo_arc 0.0.1",
|
||||||
"servo_atoms 0.0.1",
|
"servo_atoms 0.0.1",
|
||||||
"servo_config 0.0.1",
|
"servo_config 0.0.1",
|
||||||
"servo_url 0.0.1",
|
"servo_url 0.0.1",
|
||||||
|
|
|
@ -2061,7 +2061,7 @@ impl ElementMethods for Element {
|
||||||
Err(()) => Err(Error::Syntax),
|
Err(()) => Err(Error::Syntax),
|
||||||
Ok(selectors) => {
|
Ok(selectors) => {
|
||||||
let mut ctx = MatchingContext::new(MatchingMode::Normal, None);
|
let mut ctx = MatchingContext::new(MatchingMode::Normal, None);
|
||||||
Ok(matches_selector_list(&selectors.0, &Root::from_ref(self), &mut ctx))
|
Ok(matches_selector_list(&selectors, &Root::from_ref(self), &mut ctx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2080,7 +2080,7 @@ impl ElementMethods for Element {
|
||||||
for element in root.inclusive_ancestors() {
|
for element in root.inclusive_ancestors() {
|
||||||
if let Some(element) = Root::downcast::<Element>(element) {
|
if let Some(element) = Root::downcast::<Element>(element) {
|
||||||
let mut ctx = MatchingContext::new(MatchingMode::Normal, None);
|
let mut ctx = MatchingContext::new(MatchingMode::Normal, None);
|
||||||
if matches_selector_list(&selectors.0, &element, &mut ctx) {
|
if matches_selector_list(&selectors, &element, &mut ctx) {
|
||||||
return Ok(Some(element));
|
return Ok(Some(element));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -345,7 +345,7 @@ impl<'a> Iterator for QuerySelectorIterator {
|
||||||
type Item = Root<Node>;
|
type Item = Root<Node>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Root<Node>> {
|
fn next(&mut self) -> Option<Root<Node>> {
|
||||||
let selectors = &self.selectors.0;
|
let selectors = &self.selectors;
|
||||||
|
|
||||||
// TODO(cgaebel): Is it worth it to build a bloom filter here
|
// TODO(cgaebel): Is it worth it to build a bloom filter here
|
||||||
// (instead of passing `None`)? Probably.
|
// (instead of passing `None`)? Probably.
|
||||||
|
@ -722,7 +722,7 @@ impl Node {
|
||||||
Ok(selectors) => {
|
Ok(selectors) => {
|
||||||
let mut ctx = MatchingContext::new(MatchingMode::Normal, None);
|
let mut ctx = MatchingContext::new(MatchingMode::Normal, None);
|
||||||
Ok(self.traverse_preorder().filter_map(Root::downcast).find(|element| {
|
Ok(self.traverse_preorder().filter_map(Root::downcast).find(|element| {
|
||||||
matches_selector_list(&selectors.0, element, &mut ctx)
|
matches_selector_list(&selectors, element, &mut ctx)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ cssparser = "0.13.7"
|
||||||
fnv = "1.0"
|
fnv = "1.0"
|
||||||
phf = "0.7.18"
|
phf = "0.7.18"
|
||||||
precomputed-hash = "0.1"
|
precomputed-hash = "0.1"
|
||||||
|
servo_arc = { path = "../servo_arc" }
|
||||||
smallvec = "0.4"
|
smallvec = "0.4"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -1,326 +0,0 @@
|
||||||
/* 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,9 +9,9 @@ extern crate fnv;
|
||||||
extern crate phf;
|
extern crate phf;
|
||||||
extern crate precomputed_hash;
|
extern crate precomputed_hash;
|
||||||
#[cfg(test)] #[macro_use] extern crate size_of_test;
|
#[cfg(test)] #[macro_use] extern crate size_of_test;
|
||||||
|
extern crate servo_arc;
|
||||||
extern crate smallvec;
|
extern crate smallvec;
|
||||||
|
|
||||||
pub mod arcslice;
|
|
||||||
pub mod attr;
|
pub mod attr;
|
||||||
pub mod bloom;
|
pub mod bloom;
|
||||||
pub mod matching;
|
pub mod matching;
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
use attr::{ParsedAttrSelectorOperation, AttrSelectorOperation, NamespaceConstraint};
|
use attr::{ParsedAttrSelectorOperation, AttrSelectorOperation, NamespaceConstraint};
|
||||||
use bloom::BloomFilter;
|
use bloom::BloomFilter;
|
||||||
use parser::{Combinator, ComplexSelector, Component, LocalName};
|
use parser::{AncestorHashes, Combinator, Component, LocalName};
|
||||||
use parser::{Selector, SelectorInner, SelectorIter};
|
use parser::{Selector, SelectorIter, SelectorList};
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use tree::Element;
|
use tree::Element;
|
||||||
|
|
||||||
|
@ -152,27 +152,30 @@ impl<'a> MatchingContext<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn matches_selector_list<E>(selector_list: &[Selector<E::Impl>],
|
pub fn matches_selector_list<E>(selector_list: &SelectorList<E::Impl>,
|
||||||
element: &E,
|
element: &E,
|
||||||
context: &mut MatchingContext)
|
context: &mut MatchingContext)
|
||||||
-> bool
|
-> bool
|
||||||
where E: Element
|
where E: Element
|
||||||
{
|
{
|
||||||
selector_list.iter().any(|selector| {
|
selector_list.0.iter().any(|selector_and_hashes| {
|
||||||
matches_selector(&selector.inner,
|
matches_selector(&selector_and_hashes.selector,
|
||||||
|
0,
|
||||||
|
&selector_and_hashes.hashes,
|
||||||
element,
|
element,
|
||||||
context,
|
context,
|
||||||
&mut |_, _| {})
|
&mut |_, _| {})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn may_match<E>(sel: &SelectorInner<E::Impl>,
|
#[inline(always)]
|
||||||
|
fn may_match<E>(hashes: &AncestorHashes,
|
||||||
bf: &BloomFilter)
|
bf: &BloomFilter)
|
||||||
-> bool
|
-> bool
|
||||||
where E: Element,
|
where E: Element,
|
||||||
{
|
{
|
||||||
// Check against the list of precomputed hashes.
|
// Check against the list of precomputed hashes.
|
||||||
for hash in sel.ancestor_hashes.iter() {
|
for hash in hashes.0.iter() {
|
||||||
// If we hit the 0 sentinel hash, that means the rest are zero as well.
|
// If we hit the 0 sentinel hash, that means the rest are zero as well.
|
||||||
if *hash == 0 {
|
if *hash == 0 {
|
||||||
break;
|
break;
|
||||||
|
@ -330,8 +333,18 @@ enum SelectorMatchingResult {
|
||||||
NotMatchedGlobally,
|
NotMatchedGlobally,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Matches an inner selector.
|
/// Matches a selector, fast-rejecting against a bloom filter.
|
||||||
pub fn matches_selector<E, F>(selector: &SelectorInner<E::Impl>,
|
///
|
||||||
|
/// We accept an offset to allow consumers to represent and match against partial
|
||||||
|
/// selectors (indexed from the right). We use this API design, rather than
|
||||||
|
/// having the callers pass a SelectorIter, because creating a SelectorIter
|
||||||
|
/// requires dereferencing the selector to get the length, which adds an
|
||||||
|
/// unncessary cache miss for cases when we can fast-reject with AncestorHashes
|
||||||
|
/// (which the caller can store inline with the selector pointer).
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn matches_selector<E, F>(selector: &Selector<E::Impl>,
|
||||||
|
offset: usize,
|
||||||
|
hashes: &AncestorHashes,
|
||||||
element: &E,
|
element: &E,
|
||||||
context: &mut MatchingContext,
|
context: &mut MatchingContext,
|
||||||
flags_setter: &mut F)
|
flags_setter: &mut F)
|
||||||
|
@ -341,18 +354,17 @@ pub fn matches_selector<E, F>(selector: &SelectorInner<E::Impl>,
|
||||||
{
|
{
|
||||||
// Use the bloom filter to fast-reject.
|
// Use the bloom filter to fast-reject.
|
||||||
if let Some(filter) = context.bloom_filter {
|
if let Some(filter) = context.bloom_filter {
|
||||||
if !may_match::<E>(&selector, filter) {
|
if !may_match::<E>(hashes, filter) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
matches_complex_selector(&selector.complex, element, context, flags_setter)
|
matches_complex_selector(selector, offset, element, context, flags_setter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Matches a complex selector.
|
/// Matches a complex selector.
|
||||||
///
|
pub fn matches_complex_selector<E, F>(complex_selector: &Selector<E::Impl>,
|
||||||
/// Use `matches_selector` if you need to skip pseudos.
|
offset: usize,
|
||||||
pub fn matches_complex_selector<E, F>(complex_selector: &ComplexSelector<E::Impl>,
|
|
||||||
element: &E,
|
element: &E,
|
||||||
context: &mut MatchingContext,
|
context: &mut MatchingContext,
|
||||||
flags_setter: &mut F)
|
flags_setter: &mut F)
|
||||||
|
@ -360,11 +372,15 @@ pub fn matches_complex_selector<E, F>(complex_selector: &ComplexSelector<E::Impl
|
||||||
where E: Element,
|
where E: Element,
|
||||||
F: FnMut(&E, ElementSelectorFlags),
|
F: FnMut(&E, ElementSelectorFlags),
|
||||||
{
|
{
|
||||||
let mut iter = complex_selector.iter();
|
let mut iter = if offset == 0 {
|
||||||
|
complex_selector.iter()
|
||||||
|
} else {
|
||||||
|
complex_selector.iter_from(offset)
|
||||||
|
};
|
||||||
|
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
if context.matching_mode == MatchingMode::ForStatelessPseudoElement {
|
if context.matching_mode == MatchingMode::ForStatelessPseudoElement {
|
||||||
assert!(complex_selector.iter().any(|c| {
|
assert!(iter.clone().any(|c| {
|
||||||
matches!(*c, Component::PseudoElement(..))
|
matches!(*c, Component::PseudoElement(..))
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -11,10 +11,8 @@ use precomputed_hash::PrecomputedHash;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use visitor::SelectorVisitor;
|
use visitor::SelectorVisitor;
|
||||||
|
|
||||||
size_of_test!(size_of_selector, Selector<Impl>, 48);
|
size_of_test!(size_of_selector, Selector<Impl>, 8);
|
||||||
size_of_test!(size_of_pseudo_element, gecko_like_types::PseudoElement, 1);
|
size_of_test!(size_of_pseudo_element, gecko_like_types::PseudoElement, 1);
|
||||||
size_of_test!(size_of_selector_inner, SelectorInner<Impl>, 40);
|
|
||||||
size_of_test!(size_of_complex_selector, ComplexSelector<Impl>, 24);
|
|
||||||
|
|
||||||
size_of_test!(size_of_component, Component<Impl>, 32);
|
size_of_test!(size_of_component, Component<Impl>, 32);
|
||||||
size_of_test!(size_of_pseudo_class, PseudoClass, 24);
|
size_of_test!(size_of_pseudo_class, PseudoClass, 24);
|
||||||
|
|
18
components/servo_arc/Cargo.toml
Normal file
18
components/servo_arc/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "servo_arc"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = ["The Servo Project Developers"]
|
||||||
|
license = "MPL-2.0"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "servo_arc"
|
||||||
|
path = "lib.rs"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
servo = ["serde", "heapsize"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
heapsize = {version = "0.4.0", optional = true}
|
||||||
|
serde = {version = "0.9", optional = true}
|
||||||
|
nodrop = {version = "0.1.8"}
|
|
@ -8,11 +8,13 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
//! Fork of Arc for the style system. This has the following advantages over std::Arc:
|
//! Fork of Arc for Servo. This has the following advantages over std::Arc:
|
||||||
//! * We don't waste storage on the weak reference count.
|
//! * We don't waste storage on the weak reference count.
|
||||||
//! * We don't do extra RMU operations to handle the possibility of weak references.
|
//! * We don't do extra RMU operations to handle the possibility of weak references.
|
||||||
//! * We can experiment with arena allocation (todo).
|
//! * We can experiment with arena allocation (todo).
|
||||||
//! * We can add methods to support our custom use cases [1].
|
//! * We can add methods to support our custom use cases [1].
|
||||||
|
//! * We have support for dynamically-sized types (see from_header_and_iter).
|
||||||
|
//! * We have support for thin arcs to unsized types (see ThinArc).
|
||||||
//!
|
//!
|
||||||
//! [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1360883
|
//! [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1360883
|
||||||
|
|
||||||
|
@ -20,8 +22,12 @@
|
||||||
// duplicate those here.
|
// duplicate those here.
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
#[cfg(feature = "servo")] extern crate serde;
|
||||||
|
extern crate nodrop;
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
use heapsize::HeapSizeOf;
|
use heapsize::HeapSizeOf;
|
||||||
|
use nodrop::NoDrop;
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{isize, usize};
|
use std::{isize, usize};
|
||||||
|
@ -30,8 +36,11 @@ use std::cmp::Ordering;
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::iter::{ExactSizeIterator, Iterator};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::ptr;
|
||||||
|
use std::slice;
|
||||||
use std::sync::atomic;
|
use std::sync::atomic;
|
||||||
use std::sync::atomic::Ordering::{Acquire, Relaxed, Release};
|
use std::sync::atomic::Ordering::{Acquire, Relaxed, Release};
|
||||||
|
|
||||||
|
@ -412,3 +421,257 @@ impl<T: Serialize> Serialize for Arc<T>
|
||||||
(**self).serialize(serializer)
|
(**self).serialize(serializer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Structure to allow Arc-managing some fixed-sized data and a variably-sized
|
||||||
|
/// slice in a single allocation.
|
||||||
|
#[derive(Debug, Eq, PartialEq, PartialOrd)]
|
||||||
|
pub struct HeaderSlice<H, T: ?Sized> {
|
||||||
|
/// The fixed-sized data.
|
||||||
|
pub header: H,
|
||||||
|
|
||||||
|
/// The dynamically-sized data.
|
||||||
|
pub slice: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn divide_rounding_up(dividend: usize, divisor: usize) -> usize {
|
||||||
|
(dividend + divisor - 1) / divisor
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, T> Arc<HeaderSlice<H, [T]>> {
|
||||||
|
/// Creates an Arc for a HeaderSlice using the given header struct and
|
||||||
|
/// iterator to generate the slice. The resulting Arc will be fat.
|
||||||
|
#[inline]
|
||||||
|
pub fn from_header_and_iter<I>(header: H, mut items: I) -> Self
|
||||||
|
where I: Iterator<Item=T> + ExactSizeIterator
|
||||||
|
{
|
||||||
|
use ::std::mem::size_of;
|
||||||
|
assert!(size_of::<T>() != 0, "Need to think about ZST");
|
||||||
|
|
||||||
|
// Compute the required size for the allocation.
|
||||||
|
let num_items = items.len();
|
||||||
|
let size = {
|
||||||
|
// First, determine the alignment of a hypothetical pointer to a
|
||||||
|
// HeaderSlice.
|
||||||
|
let fake_slice_ptr_align: usize = mem::align_of::<ArcInner<HeaderSlice<H, [T; 1]>>>();
|
||||||
|
|
||||||
|
// Next, synthesize a totally garbage (but properly aligned) pointer
|
||||||
|
// to a sequence of T.
|
||||||
|
let fake_slice_ptr = fake_slice_ptr_align as *const T;
|
||||||
|
|
||||||
|
// Convert that sequence to a fat pointer. The address component of
|
||||||
|
// the fat pointer will be garbage, but the length will be correct.
|
||||||
|
let fake_slice = unsafe { slice::from_raw_parts(fake_slice_ptr, num_items) };
|
||||||
|
|
||||||
|
// Pretend the garbage address points to our allocation target (with
|
||||||
|
// a trailing sequence of T), rather than just a sequence of T.
|
||||||
|
let fake_ptr = fake_slice as *const [T] as *const ArcInner<HeaderSlice<H, [T]>>;
|
||||||
|
let fake_ref: &ArcInner<HeaderSlice<H, [T]>> = unsafe { &*fake_ptr };
|
||||||
|
|
||||||
|
// Use size_of_val, which will combine static information about the
|
||||||
|
// type with the length from the fat pointer. The garbage address
|
||||||
|
// will not be used.
|
||||||
|
mem::size_of_val(fake_ref)
|
||||||
|
};
|
||||||
|
|
||||||
|
let ptr: *mut ArcInner<HeaderSlice<H, [T]>>;
|
||||||
|
unsafe {
|
||||||
|
// Allocate the buffer. We use Vec because the underlying allocation
|
||||||
|
// machinery isn't available in stable Rust.
|
||||||
|
//
|
||||||
|
// To avoid alignment issues, we allocate words rather than bytes,
|
||||||
|
// rounding up to the nearest word size.
|
||||||
|
let words_to_allocate = divide_rounding_up(size, size_of::<usize>());
|
||||||
|
let mut vec = Vec::<usize>::with_capacity(words_to_allocate);
|
||||||
|
vec.set_len(words_to_allocate);
|
||||||
|
let buffer = Box::into_raw(vec.into_boxed_slice()) as *mut usize as *mut u8;
|
||||||
|
|
||||||
|
// Synthesize the fat pointer. We do this by claiming we have a direct
|
||||||
|
// pointer to a [T], and then changing the type of the borrow. The key
|
||||||
|
// point here is that the length portion of the fat pointer applies
|
||||||
|
// only to the number of elements in the dynamically-sized portion of
|
||||||
|
// the type, so the value will be the same whether it points to a [T]
|
||||||
|
// or something else with a [T] as its last member.
|
||||||
|
let fake_slice: &mut [T] = slice::from_raw_parts_mut(buffer as *mut T, num_items);
|
||||||
|
ptr = fake_slice as *mut [T] as *mut ArcInner<HeaderSlice<H, [T]>>;
|
||||||
|
|
||||||
|
// Write the data.
|
||||||
|
ptr::write(&mut ((*ptr).count), atomic::AtomicUsize::new(1));
|
||||||
|
ptr::write(&mut ((*ptr).data.header), header);
|
||||||
|
let mut current: *mut T = &mut (*ptr).data.slice[0];
|
||||||
|
for _ in 0..num_items {
|
||||||
|
ptr::write(current, items.next().expect("ExactSizeIterator over-reported length"));
|
||||||
|
current = current.offset(1);
|
||||||
|
}
|
||||||
|
assert!(items.next().is_none(), "ExactSizeIterator under-reported length");
|
||||||
|
|
||||||
|
// We should have consumed the buffer exactly.
|
||||||
|
debug_assert!(current as *mut u8 == buffer.offset(size as isize));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the fat Arc.
|
||||||
|
assert_eq!(size_of::<Self>(), size_of::<usize>() * 2, "The Arc will be fat");
|
||||||
|
Arc { ptr: ptr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Header data with an inline length. Consumers that use HeaderWithLength as the
|
||||||
|
/// Header type in HeaderSlice can take advantage of ThinArc.
|
||||||
|
#[derive(Debug, Eq, PartialEq, PartialOrd)]
|
||||||
|
pub struct HeaderWithLength<H> {
|
||||||
|
/// The fixed-sized data.
|
||||||
|
pub header: H,
|
||||||
|
|
||||||
|
/// The slice length.
|
||||||
|
length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H> HeaderWithLength<H> {
|
||||||
|
/// Creates a new HeaderWithLength.
|
||||||
|
pub fn new(header: H, length: usize) -> Self {
|
||||||
|
HeaderWithLength {
|
||||||
|
header: header,
|
||||||
|
length: length,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ThinArc<H, T> {
|
||||||
|
ptr: *mut ArcInner<HeaderSlice<HeaderWithLength<H>, [T; 1]>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<H: Sync + Send, T: Sync + Send> Send for ThinArc<H, T> {}
|
||||||
|
unsafe impl<H: Sync + Send, T: Sync + Send> Sync for ThinArc<H, T> {}
|
||||||
|
|
||||||
|
// Synthesize a fat pointer from a thin pointer.
|
||||||
|
//
|
||||||
|
// See the comment around the analogous operation in from_header_and_iter.
|
||||||
|
fn thin_to_thick<H, T>(thin: *mut ArcInner<HeaderSlice<HeaderWithLength<H>, [T; 1]>>)
|
||||||
|
-> *mut ArcInner<HeaderSlice<HeaderWithLength<H>, [T]>>
|
||||||
|
{
|
||||||
|
let len = unsafe { (*thin).data.header.length };
|
||||||
|
let fake_slice: *mut [T] = unsafe {
|
||||||
|
slice::from_raw_parts_mut(thin as *mut T, len)
|
||||||
|
};
|
||||||
|
|
||||||
|
fake_slice as *mut ArcInner<HeaderSlice<HeaderWithLength<H>, [T]>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, T> ThinArc<H, T> {
|
||||||
|
/// Temporarily converts |self| into a bonafide Arc and exposes it to the
|
||||||
|
/// provided callback. The refcount is not modified.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn with_arc<F, U>(&self, f: F) -> U
|
||||||
|
where F: FnOnce(&Arc<HeaderSlice<HeaderWithLength<H>, [T]>>) -> U
|
||||||
|
{
|
||||||
|
// Synthesize transient Arc, which never touches the refcount of the ArcInner.
|
||||||
|
let transient = NoDrop::new(Arc {
|
||||||
|
ptr: thin_to_thick(self.ptr)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Expose the transient Arc to the callback, which may clone it if it wants.
|
||||||
|
let result = f(&transient);
|
||||||
|
|
||||||
|
// Forget the transient Arc to leave the refcount untouched.
|
||||||
|
mem::forget(transient);
|
||||||
|
|
||||||
|
// Forward the result.
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, T> Deref for ThinArc<H, T> {
|
||||||
|
type Target = HeaderSlice<HeaderWithLength<H>, [T]>;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
unsafe { &(*thin_to_thick(self.ptr)).data }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, T> Clone for ThinArc<H, T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
ThinArc::with_arc(self, |a| Arc::into_thin(a.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, T> Drop for ThinArc<H, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = Arc::from_thin(ThinArc { ptr: self.ptr });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, T> Arc<HeaderSlice<HeaderWithLength<H>, [T]>> {
|
||||||
|
/// Converts an Arc into a ThinArc. This consumes the Arc, so the refcount
|
||||||
|
/// is not modified.
|
||||||
|
pub fn into_thin(a: Self) -> ThinArc<H, T> {
|
||||||
|
assert!(a.header.length == a.slice.len(),
|
||||||
|
"Length needs to be correct for ThinArc to work");
|
||||||
|
let fat_ptr: *mut ArcInner<HeaderSlice<HeaderWithLength<H>, [T]>> = a.ptr;
|
||||||
|
mem::forget(a);
|
||||||
|
let thin_ptr = fat_ptr as *mut [usize] as *mut usize;
|
||||||
|
ThinArc {
|
||||||
|
ptr: thin_ptr as *mut ArcInner<HeaderSlice<HeaderWithLength<H>, [T; 1]>>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a ThinArc into an Arc. This consumes the ThinArc, so the refcount
|
||||||
|
/// is not modified.
|
||||||
|
pub fn from_thin(a: ThinArc<H, T>) -> Self {
|
||||||
|
let ptr = thin_to_thick(a.ptr);
|
||||||
|
mem::forget(a);
|
||||||
|
Arc {
|
||||||
|
ptr: ptr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: PartialEq, T: PartialEq> PartialEq for ThinArc<H, T> {
|
||||||
|
fn eq(&self, other: &ThinArc<H, T>) -> bool {
|
||||||
|
ThinArc::with_arc(self, |a| {
|
||||||
|
ThinArc::with_arc(other, |b| {
|
||||||
|
*a == *b
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: Eq, T: Eq> Eq for ThinArc<H, T> {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::clone::Clone;
|
||||||
|
use std::ops::Drop;
|
||||||
|
use std::sync::atomic;
|
||||||
|
use std::sync::atomic::Ordering::{Acquire, SeqCst};
|
||||||
|
use super::{Arc, HeaderWithLength, ThinArc};
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
struct Canary(*mut atomic::AtomicUsize);
|
||||||
|
|
||||||
|
impl Drop for Canary {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
match *self {
|
||||||
|
Canary(c) => {
|
||||||
|
(*c).fetch_add(1, SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn slices_and_thin() {
|
||||||
|
let mut canary = atomic::AtomicUsize::new(0);
|
||||||
|
let c = Canary(&mut canary as *mut atomic::AtomicUsize);
|
||||||
|
let v = vec![5, 6];
|
||||||
|
let header = HeaderWithLength::new(c, v.len());
|
||||||
|
{
|
||||||
|
let x = Arc::into_thin(Arc::from_header_and_iter(header, v.into_iter()));
|
||||||
|
let y = ThinArc::with_arc(&x, |q| q.clone());
|
||||||
|
let _ = y.clone();
|
||||||
|
let _ = x == x;
|
||||||
|
Arc::from_thin(x.clone());
|
||||||
|
}
|
||||||
|
assert!(canary.load(Acquire) == 1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,6 +61,7 @@ rayon = "0.7.1"
|
||||||
selectors = { path = "../selectors" }
|
selectors = { path = "../selectors" }
|
||||||
serde = {version = "0.9", optional = true}
|
serde = {version = "0.9", optional = true}
|
||||||
serde_derive = {version = "0.9", optional = true}
|
serde_derive = {version = "0.9", optional = true}
|
||||||
|
servo_arc = { path = "../servo_arc" }
|
||||||
servo_atoms = {path = "../atoms", optional = true}
|
servo_atoms = {path = "../atoms", optional = true}
|
||||||
servo_config = {path = "../config", optional = true}
|
servo_config = {path = "../config", optional = true}
|
||||||
smallvec = "0.4"
|
smallvec = "0.4"
|
||||||
|
|
|
@ -8,7 +8,7 @@ use cssparser::{Parser, ToCss};
|
||||||
use element_state::ElementState;
|
use element_state::ElementState;
|
||||||
use gecko_bindings::structs::CSSPseudoClassType;
|
use gecko_bindings::structs::CSSPseudoClassType;
|
||||||
use selector_parser::{SelectorParser, PseudoElementCascadeType};
|
use selector_parser::{SelectorParser, PseudoElementCascadeType};
|
||||||
use selectors::parser::{ComplexSelector, SelectorMethods};
|
use selectors::parser::{Selector, SelectorMethods};
|
||||||
use selectors::visitor::SelectorVisitor;
|
use selectors::visitor::SelectorVisitor;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -47,7 +47,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(Box<[ComplexSelector<SelectorImpl>]>),
|
MozAny(Box<[Selector<SelectorImpl>]>),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,7 +273,7 @@ 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)
|
Selector::parse(self, input)
|
||||||
})?;
|
})?;
|
||||||
// Selectors inside `:-moz-any` may not include combinators.
|
// Selectors inside `:-moz-any` may not include combinators.
|
||||||
if selectors.iter().flat_map(|x| x.iter_raw()).any(|s| s.is_combinator()) {
|
if selectors.iter().flat_map(|x| x.iter_raw()).any(|s| s.is_combinator()) {
|
||||||
|
|
|
@ -1405,7 +1405,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
||||||
NonTSPseudoClass::MozPlaceholder => false,
|
NonTSPseudoClass::MozPlaceholder => false,
|
||||||
NonTSPseudoClass::MozAny(ref sels) => {
|
NonTSPseudoClass::MozAny(ref sels) => {
|
||||||
sels.iter().any(|s| {
|
sels.iter().any(|s| {
|
||||||
matches_complex_selector(s, self, context, flags_setter)
|
matches_complex_selector(s, 0, self, context, flags_setter)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
NonTSPseudoClass::MozSystemMetric(ref s) |
|
NonTSPseudoClass::MozSystemMetric(ref s) |
|
||||||
|
|
|
@ -218,7 +218,7 @@ impl StylesheetInvalidationSet {
|
||||||
let mut scope: Option<InvalidationScope> = None;
|
let mut scope: Option<InvalidationScope> = None;
|
||||||
|
|
||||||
let mut scan = true;
|
let mut scan = true;
|
||||||
let mut iter = selector.inner.complex.iter();
|
let mut iter = selector.iter();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
for component in &mut iter {
|
for component in &mut iter {
|
||||||
|
@ -262,8 +262,8 @@ impl StylesheetInvalidationSet {
|
||||||
match *rule {
|
match *rule {
|
||||||
Style(ref lock) => {
|
Style(ref lock) => {
|
||||||
let style_rule = lock.read_with(guard);
|
let style_rule = lock.read_with(guard);
|
||||||
for selector in &style_rule.selectors.0 {
|
for selector_and_hashes in &style_rule.selectors.0 {
|
||||||
self.collect_scopes(selector);
|
self.collect_scopes(&selector_and_hashes.selector);
|
||||||
if self.fully_invalid {
|
if self.fully_invalid {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,8 +72,8 @@ extern crate pdqsort;
|
||||||
#[cfg(feature = "gecko")] extern crate precomputed_hash;
|
#[cfg(feature = "gecko")] extern crate precomputed_hash;
|
||||||
extern crate rayon;
|
extern crate rayon;
|
||||||
extern crate selectors;
|
extern crate selectors;
|
||||||
#[cfg(feature = "servo")] extern crate serde;
|
|
||||||
#[cfg(feature = "servo")] #[macro_use] extern crate serde_derive;
|
#[cfg(feature = "servo")] #[macro_use] extern crate serde_derive;
|
||||||
|
pub extern crate servo_arc;
|
||||||
#[cfg(feature = "servo")] #[macro_use] extern crate servo_atoms;
|
#[cfg(feature = "servo")] #[macro_use] extern crate servo_atoms;
|
||||||
#[cfg(feature = "servo")] extern crate servo_config;
|
#[cfg(feature = "servo")] extern crate servo_config;
|
||||||
#[cfg(feature = "servo")] extern crate servo_url;
|
#[cfg(feature = "servo")] extern crate servo_url;
|
||||||
|
@ -129,7 +129,6 @@ pub mod sequential;
|
||||||
pub mod sink;
|
pub mod sink;
|
||||||
pub mod str;
|
pub mod str;
|
||||||
pub mod style_adjuster;
|
pub mod style_adjuster;
|
||||||
pub mod stylearc;
|
|
||||||
pub mod stylesheet_set;
|
pub mod stylesheet_set;
|
||||||
pub mod stylesheets;
|
pub mod stylesheets;
|
||||||
pub mod thread_state;
|
pub mod thread_state;
|
||||||
|
@ -139,6 +138,10 @@ pub mod traversal;
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub mod values;
|
pub mod values;
|
||||||
|
|
||||||
|
// Compat shim for the old name when it lived in the style crate.
|
||||||
|
// FIXME(bholley) Remove this.
|
||||||
|
pub use servo_arc as stylearc;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,8 @@ use selectors::Element;
|
||||||
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
|
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
|
||||||
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
|
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
|
||||||
use selectors::matching::{RelevantLinkStatus, VisitedHandlingMode, matches_selector};
|
use selectors::matching::{RelevantLinkStatus, VisitedHandlingMode, matches_selector};
|
||||||
use selectors::parser::{Combinator, Component, Selector, SelectorInner, SelectorMethods};
|
use selectors::parser::{AncestorHashes, Combinator, Component};
|
||||||
|
use selectors::parser::{Selector, SelectorAndHashes, SelectorIter, SelectorMethods};
|
||||||
use selectors::visitor::SelectorVisitor;
|
use selectors::visitor::SelectorVisitor;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
@ -642,7 +643,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
||||||
use selectors::matching::matches_complex_selector;
|
use selectors::matching::matches_complex_selector;
|
||||||
if let NonTSPseudoClass::MozAny(ref selectors) = *pseudo_class {
|
if let NonTSPseudoClass::MozAny(ref selectors) = *pseudo_class {
|
||||||
return selectors.iter().any(|s| {
|
return selectors.iter().any(|s| {
|
||||||
matches_complex_selector(s, self, context, _setter)
|
matches_complex_selector(s, 0, self, context, _setter)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -866,8 +867,15 @@ impl Sensitivities {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct Dependency {
|
pub struct Dependency {
|
||||||
|
/// The dependency selector.
|
||||||
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
||||||
selector: SelectorInner<SelectorImpl>,
|
selector: Selector<SelectorImpl>,
|
||||||
|
/// The offset into the selector that we should match on.
|
||||||
|
selector_offset: usize,
|
||||||
|
/// The ancestor hashes associated with the above selector at the given
|
||||||
|
/// offset.
|
||||||
|
#[cfg_attr(feature = "servo", ignore_heap_size_of = "No heap data")]
|
||||||
|
hashes: AncestorHashes,
|
||||||
/// The hint associated with this dependency.
|
/// The hint associated with this dependency.
|
||||||
pub hint: RestyleHint,
|
pub hint: RestyleHint,
|
||||||
/// The sensitivities associated with this dependency.
|
/// The sensitivities associated with this dependency.
|
||||||
|
@ -875,8 +883,12 @@ pub struct Dependency {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectorMapEntry for Dependency {
|
impl SelectorMapEntry for Dependency {
|
||||||
fn selector(&self) -> &SelectorInner<SelectorImpl> {
|
fn selector(&self) -> SelectorIter<SelectorImpl> {
|
||||||
&self.selector
|
self.selector.iter_from(self.selector_offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hashes(&self) -> &AncestorHashes {
|
||||||
|
&self.hashes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -940,9 +952,9 @@ pub enum HintComputationContext<'a, E: 'a>
|
||||||
|
|
||||||
impl DependencySet {
|
impl DependencySet {
|
||||||
/// Adds a selector to this `DependencySet`.
|
/// Adds a selector to this `DependencySet`.
|
||||||
pub fn note_selector(&mut self, selector: &Selector<SelectorImpl>) {
|
pub fn note_selector(&mut self, selector_and_hashes: &SelectorAndHashes<SelectorImpl>) {
|
||||||
let mut combinator = None;
|
let mut combinator = None;
|
||||||
let mut iter = selector.inner.complex.iter();
|
let mut iter = selector_and_hashes.selector.iter();
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
let mut child_combinators_seen = 0;
|
let mut child_combinators_seen = 0;
|
||||||
let mut saw_descendant_combinator = false;
|
let mut saw_descendant_combinator = false;
|
||||||
|
@ -992,17 +1004,21 @@ impl DependencySet {
|
||||||
None => RestyleHint::for_self(),
|
None => RestyleHint::for_self(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let dep_selector = if sequence_start == 0 {
|
// Reuse the bloom hashes if this is the base selector. Otherwise,
|
||||||
// Reuse the bloom hashes if this is the base selector.
|
// rebuild them.
|
||||||
selector.inner.clone()
|
let hashes = if sequence_start == 0 {
|
||||||
|
selector_and_hashes.hashes.clone()
|
||||||
} else {
|
} else {
|
||||||
SelectorInner::new(selector.inner.complex.slice_from(sequence_start))
|
let seq_iter = selector_and_hashes.selector.iter_from(sequence_start);
|
||||||
|
AncestorHashes::from_iter(seq_iter)
|
||||||
};
|
};
|
||||||
|
|
||||||
self.dependencies.insert(Dependency {
|
self.dependencies.insert(Dependency {
|
||||||
sensitivities: visitor.sensitivities,
|
sensitivities: visitor.sensitivities,
|
||||||
hint: hint,
|
hint: hint,
|
||||||
selector: dep_selector,
|
selector: selector_and_hashes.selector.clone(),
|
||||||
|
selector_offset: sequence_start,
|
||||||
|
hashes: hashes,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1130,14 +1146,20 @@ impl DependencySet {
|
||||||
MatchingContext::new_for_visited(MatchingMode::Normal, None,
|
MatchingContext::new_for_visited(MatchingMode::Normal, None,
|
||||||
VisitedHandlingMode::AllLinksUnvisited);
|
VisitedHandlingMode::AllLinksUnvisited);
|
||||||
let matched_then =
|
let matched_then =
|
||||||
matches_selector(&dep.selector, &snapshot_el,
|
matches_selector(&dep.selector,
|
||||||
|
dep.selector_offset,
|
||||||
|
&dep.hashes,
|
||||||
|
&snapshot_el,
|
||||||
&mut then_context,
|
&mut then_context,
|
||||||
&mut |_, _| {});
|
&mut |_, _| {});
|
||||||
let mut now_context =
|
let mut now_context =
|
||||||
MatchingContext::new_for_visited(MatchingMode::Normal, bloom_filter,
|
MatchingContext::new_for_visited(MatchingMode::Normal, bloom_filter,
|
||||||
VisitedHandlingMode::AllLinksUnvisited);
|
VisitedHandlingMode::AllLinksUnvisited);
|
||||||
let matches_now =
|
let matches_now =
|
||||||
matches_selector(&dep.selector, el,
|
matches_selector(&dep.selector,
|
||||||
|
dep.selector_offset,
|
||||||
|
&dep.hashes,
|
||||||
|
el,
|
||||||
&mut now_context,
|
&mut now_context,
|
||||||
&mut |_, _| {});
|
&mut |_, _| {});
|
||||||
|
|
||||||
|
@ -1162,12 +1184,18 @@ impl DependencySet {
|
||||||
dep.sensitivities.states.intersects(IN_VISITED_OR_UNVISITED_STATE) {
|
dep.sensitivities.states.intersects(IN_VISITED_OR_UNVISITED_STATE) {
|
||||||
then_context.visited_handling = VisitedHandlingMode::RelevantLinkVisited;
|
then_context.visited_handling = VisitedHandlingMode::RelevantLinkVisited;
|
||||||
let matched_then =
|
let matched_then =
|
||||||
matches_selector(&dep.selector, &snapshot_el,
|
matches_selector(&dep.selector,
|
||||||
|
dep.selector_offset,
|
||||||
|
&dep.hashes,
|
||||||
|
&snapshot_el,
|
||||||
&mut then_context,
|
&mut then_context,
|
||||||
&mut |_, _| {});
|
&mut |_, _| {});
|
||||||
now_context.visited_handling = VisitedHandlingMode::RelevantLinkVisited;
|
now_context.visited_handling = VisitedHandlingMode::RelevantLinkVisited;
|
||||||
let matches_now =
|
let matches_now =
|
||||||
matches_selector(&dep.selector, el,
|
matches_selector(&dep.selector,
|
||||||
|
dep.selector_offset,
|
||||||
|
&dep.hashes,
|
||||||
|
el,
|
||||||
&mut now_context,
|
&mut now_context,
|
||||||
&mut |_, _| {});
|
&mut |_, _| {});
|
||||||
if matched_then != matches_now {
|
if matched_then != matches_now {
|
||||||
|
|
|
@ -12,7 +12,7 @@ use pdqsort::sort_by;
|
||||||
use rule_tree::CascadeLevel;
|
use rule_tree::CascadeLevel;
|
||||||
use selector_parser::SelectorImpl;
|
use selector_parser::SelectorImpl;
|
||||||
use selectors::matching::{matches_selector, MatchingContext, ElementSelectorFlags};
|
use selectors::matching::{matches_selector, MatchingContext, ElementSelectorFlags};
|
||||||
use selectors::parser::{Component, Combinator, SelectorInner};
|
use selectors::parser::{AncestorHashes, Component, Combinator, SelectorAndHashes, SelectorIter};
|
||||||
use selectors::parser::LocalName as LocalNameSelector;
|
use selectors::parser::LocalName as LocalNameSelector;
|
||||||
use smallvec::VecLike;
|
use smallvec::VecLike;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
|
@ -22,13 +22,20 @@ use stylist::{ApplicableDeclarationBlock, Rule};
|
||||||
|
|
||||||
/// A trait to abstract over a given selector map entry.
|
/// A trait to abstract over a given selector map entry.
|
||||||
pub trait SelectorMapEntry : Sized + Clone {
|
pub trait SelectorMapEntry : Sized + Clone {
|
||||||
/// Get the selector we should use to index in the selector map.
|
/// Gets the selector we should use to index in the selector map.
|
||||||
fn selector(&self) -> &SelectorInner<SelectorImpl>;
|
fn selector(&self) -> SelectorIter<SelectorImpl>;
|
||||||
|
|
||||||
|
/// Gets the ancestor hashes associated with the selector.
|
||||||
|
fn hashes(&self) -> &AncestorHashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectorMapEntry for SelectorInner<SelectorImpl> {
|
impl SelectorMapEntry for SelectorAndHashes<SelectorImpl> {
|
||||||
fn selector(&self) -> &SelectorInner<SelectorImpl> {
|
fn selector(&self) -> SelectorIter<SelectorImpl> {
|
||||||
self
|
self.selector.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hashes(&self) -> &AncestorHashes {
|
||||||
|
&self.hashes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +231,9 @@ impl SelectorMap<Rule> {
|
||||||
F: FnMut(&E, ElementSelectorFlags),
|
F: FnMut(&E, ElementSelectorFlags),
|
||||||
{
|
{
|
||||||
for rule in rules {
|
for rule in rules {
|
||||||
if matches_selector(&rule.selector.inner,
|
if matches_selector(&rule.selector,
|
||||||
|
0,
|
||||||
|
&rule.hashes,
|
||||||
element,
|
element,
|
||||||
context,
|
context,
|
||||||
flags_setter) {
|
flags_setter) {
|
||||||
|
@ -390,12 +399,12 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
///
|
///
|
||||||
/// Effectively, pseudo-elements are ignored, given only state pseudo-classes
|
/// Effectively, pseudo-elements are ignored, given only state pseudo-classes
|
||||||
/// may appear before them.
|
/// may appear before them.
|
||||||
fn find_from_right<F, R>(selector: &SelectorInner<SelectorImpl>,
|
#[inline(always)]
|
||||||
|
fn find_from_right<F, R>(mut iter: SelectorIter<SelectorImpl>,
|
||||||
mut f: F)
|
mut f: F)
|
||||||
-> Option<R>
|
-> Option<R>
|
||||||
where F: FnMut(&Component<SelectorImpl>) -> Option<R>,
|
where F: FnMut(&Component<SelectorImpl>) -> Option<R>,
|
||||||
{
|
{
|
||||||
let mut iter = selector.complex.iter();
|
|
||||||
for ss in &mut iter {
|
for ss in &mut iter {
|
||||||
if let Some(r) = f(ss) {
|
if let Some(r) = f(ss) {
|
||||||
return Some(r)
|
return Some(r)
|
||||||
|
@ -414,9 +423,10 @@ fn find_from_right<F, R>(selector: &SelectorInner<SelectorImpl>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the first ID name in the selector, or None otherwise.
|
/// Retrieve the first ID name in the selector, or None otherwise.
|
||||||
pub fn get_id_name(selector: &SelectorInner<SelectorImpl>)
|
#[inline(always)]
|
||||||
-> Option<Atom> {
|
pub fn get_id_name(iter: SelectorIter<SelectorImpl>)
|
||||||
find_from_right(selector, |ss| {
|
-> Option<Atom> {
|
||||||
|
find_from_right(iter, |ss| {
|
||||||
// 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 Component::ID(ref id) = *ss {
|
if let Component::ID(ref id) = *ss {
|
||||||
|
@ -427,9 +437,10 @@ pub fn get_id_name(selector: &SelectorInner<SelectorImpl>)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the FIRST class name in the selector, or None otherwise.
|
/// Retrieve the FIRST class name in the selector, or None otherwise.
|
||||||
pub fn get_class_name(selector: &SelectorInner<SelectorImpl>)
|
#[inline(always)]
|
||||||
-> Option<Atom> {
|
pub fn get_class_name(iter: SelectorIter<SelectorImpl>)
|
||||||
find_from_right(selector, |ss| {
|
-> Option<Atom> {
|
||||||
|
find_from_right(iter, |ss| {
|
||||||
// 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 Component::Class(ref class) = *ss {
|
if let Component::Class(ref class) = *ss {
|
||||||
|
@ -440,9 +451,10 @@ pub fn get_class_name(selector: &SelectorInner<SelectorImpl>)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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(selector: &SelectorInner<SelectorImpl>)
|
#[inline(always)]
|
||||||
-> Option<LocalNameSelector<SelectorImpl>> {
|
pub fn get_local_name(iter: SelectorIter<SelectorImpl>)
|
||||||
find_from_right(selector, |ss| {
|
-> Option<LocalNameSelector<SelectorImpl>> {
|
||||||
|
find_from_right(iter, |ss| {
|
||||||
if let Component::LocalName(ref n) = *ss {
|
if let Component::LocalName(ref n) = *ss {
|
||||||
return Some(LocalNameSelector {
|
return Some(LocalNameSelector {
|
||||||
name: n.name.clone(),
|
name: n.name.clone(),
|
||||||
|
|
|
@ -28,7 +28,8 @@ use selectors::attr::NamespaceConstraint;
|
||||||
use selectors::bloom::BloomFilter;
|
use selectors::bloom::BloomFilter;
|
||||||
use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext, MatchingMode};
|
use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext, MatchingMode};
|
||||||
use selectors::matching::AFFECTED_BY_PRESENTATIONAL_HINTS;
|
use selectors::matching::AFFECTED_BY_PRESENTATIONAL_HINTS;
|
||||||
use selectors::parser::{Combinator, Component, Selector, SelectorInner, SelectorIter, SelectorMethods};
|
use selectors::parser::{AncestorHashes, Combinator, Component, Selector, SelectorAndHashes};
|
||||||
|
use selectors::parser::{SelectorIter, SelectorMethods};
|
||||||
use selectors::visitor::SelectorVisitor;
|
use selectors::visitor::SelectorVisitor;
|
||||||
use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
|
use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
|
||||||
use sink::Push;
|
use sink::Push;
|
||||||
|
@ -159,7 +160,7 @@ pub struct Stylist {
|
||||||
/// on state that is not otherwise visible to the cache, like attributes or
|
/// on state that is not otherwise visible to the cache, like attributes or
|
||||||
/// tree-structural state like child index and pseudos).
|
/// tree-structural state like child index and pseudos).
|
||||||
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
||||||
selectors_for_cache_revalidation: SelectorMap<SelectorInner<SelectorImpl>>,
|
selectors_for_cache_revalidation: SelectorMap<SelectorAndHashes<SelectorImpl>>,
|
||||||
|
|
||||||
/// The total number of selectors.
|
/// The total number of selectors.
|
||||||
num_selectors: usize,
|
num_selectors: usize,
|
||||||
|
@ -454,10 +455,10 @@ impl Stylist {
|
||||||
CssRule::Style(ref locked) => {
|
CssRule::Style(ref locked) => {
|
||||||
let style_rule = locked.read_with(&guard);
|
let style_rule = locked.read_with(&guard);
|
||||||
self.num_declarations += style_rule.block.read_with(&guard).len();
|
self.num_declarations += style_rule.block.read_with(&guard).len();
|
||||||
for selector in &style_rule.selectors.0 {
|
for selector_and_hashes in &style_rule.selectors.0 {
|
||||||
self.num_selectors += 1;
|
self.num_selectors += 1;
|
||||||
|
|
||||||
let map = if let Some(pseudo) = selector.pseudo_element() {
|
let map = if let Some(pseudo) = selector_and_hashes.selector.pseudo_element() {
|
||||||
self.pseudos_map
|
self.pseudos_map
|
||||||
.entry(pseudo.canonical())
|
.entry(pseudo.canonical())
|
||||||
.or_insert_with(PerPseudoElementSelectorMap::new)
|
.or_insert_with(PerPseudoElementSelectorMap::new)
|
||||||
|
@ -466,20 +467,21 @@ impl Stylist {
|
||||||
self.element_map.borrow_for_origin(&stylesheet.origin)
|
self.element_map.borrow_for_origin(&stylesheet.origin)
|
||||||
};
|
};
|
||||||
|
|
||||||
map.insert(Rule::new(selector.clone(),
|
map.insert(Rule::new(selector_and_hashes.selector.clone(),
|
||||||
|
selector_and_hashes.hashes.clone(),
|
||||||
locked.clone(),
|
locked.clone(),
|
||||||
self.rules_source_order));
|
self.rules_source_order));
|
||||||
|
|
||||||
self.dependencies.note_selector(selector);
|
self.dependencies.note_selector(selector_and_hashes);
|
||||||
if needs_revalidation(selector) {
|
if needs_revalidation(&selector_and_hashes.selector) {
|
||||||
self.selectors_for_cache_revalidation.insert(selector.inner.clone());
|
self.selectors_for_cache_revalidation.insert(selector_and_hashes.clone());
|
||||||
}
|
}
|
||||||
selector.visit(&mut AttributeAndStateDependencyVisitor {
|
selector_and_hashes.selector.visit(&mut AttributeAndStateDependencyVisitor {
|
||||||
attribute_dependencies: &mut self.attribute_dependencies,
|
attribute_dependencies: &mut self.attribute_dependencies,
|
||||||
style_attribute_dependency: &mut self.style_attribute_dependency,
|
style_attribute_dependency: &mut self.style_attribute_dependency,
|
||||||
state_dependencies: &mut self.state_dependencies,
|
state_dependencies: &mut self.state_dependencies,
|
||||||
});
|
});
|
||||||
selector.visit(&mut MappedIdVisitor {
|
selector_and_hashes.selector.visit(&mut MappedIdVisitor {
|
||||||
mapped_ids: &mut self.mapped_ids,
|
mapped_ids: &mut self.mapped_ids,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1130,8 +1132,10 @@ impl Stylist {
|
||||||
// the lookups, which means that the bitvecs are comparable. We verify
|
// the lookups, which means that the bitvecs are comparable. We verify
|
||||||
// this in the caller by asserting that the bitvecs are same-length.
|
// this in the caller by asserting that the bitvecs are same-length.
|
||||||
let mut results = BitVec::new();
|
let mut results = BitVec::new();
|
||||||
self.selectors_for_cache_revalidation.lookup(*element, &mut |selector| {
|
self.selectors_for_cache_revalidation.lookup(*element, &mut |selector_and_hashes| {
|
||||||
results.push(matches_selector(selector,
|
results.push(matches_selector(&selector_and_hashes.selector,
|
||||||
|
0,
|
||||||
|
&selector_and_hashes.hashes,
|
||||||
element,
|
element,
|
||||||
&mut matching_context,
|
&mut matching_context,
|
||||||
flags_setter));
|
flags_setter));
|
||||||
|
@ -1410,6 +1414,9 @@ pub struct Rule {
|
||||||
/// can ruin performance when there are a lot of rules.
|
/// can ruin performance when there are a lot of rules.
|
||||||
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
||||||
pub selector: Selector<SelectorImpl>,
|
pub selector: Selector<SelectorImpl>,
|
||||||
|
/// The ancestor hashes associated with the selector.
|
||||||
|
#[cfg_attr(feature = "servo", ignore_heap_size_of = "No heap data")]
|
||||||
|
pub hashes: AncestorHashes,
|
||||||
/// The actual style rule.
|
/// The actual style rule.
|
||||||
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
||||||
pub style_rule: Arc<Locked<StyleRule>>,
|
pub style_rule: Arc<Locked<StyleRule>>,
|
||||||
|
@ -1418,8 +1425,12 @@ pub struct Rule {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectorMapEntry for Rule {
|
impl SelectorMapEntry for Rule {
|
||||||
fn selector(&self) -> &SelectorInner<SelectorImpl> {
|
fn selector(&self) -> SelectorIter<SelectorImpl> {
|
||||||
&self.selector.inner
|
self.selector.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hashes(&self) -> &AncestorHashes {
|
||||||
|
&self.hashes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1444,12 +1455,14 @@ impl Rule {
|
||||||
|
|
||||||
/// Creates a new Rule.
|
/// Creates a new Rule.
|
||||||
pub fn new(selector: Selector<SelectorImpl>,
|
pub fn new(selector: Selector<SelectorImpl>,
|
||||||
|
hashes: AncestorHashes,
|
||||||
style_rule: Arc<Locked<StyleRule>>,
|
style_rule: Arc<Locked<StyleRule>>,
|
||||||
source_order: usize)
|
source_order: usize)
|
||||||
-> Self
|
-> Self
|
||||||
{
|
{
|
||||||
Rule {
|
Rule {
|
||||||
selector: selector,
|
selector: selector,
|
||||||
|
hashes: hashes,
|
||||||
style_rule: style_rule,
|
style_rule: style_rule,
|
||||||
source_order: source_order,
|
source_order: source_order,
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,26 +90,23 @@ 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::from_vec(vec!(
|
||||||
Selector::new_for_unit_testing(
|
Selector::from_vec(vec!(
|
||||||
SelectorInner::from_vec(vec![
|
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
|
||||||
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
|
Component::LocalName(LocalName {
|
||||||
Component::LocalName(LocalName {
|
name: local_name!("input"),
|
||||||
name: local_name!("input"),
|
lower_name: local_name!("input"),
|
||||||
lower_name: local_name!("input"),
|
}),
|
||||||
}),
|
Component::AttributeInNoNamespace {
|
||||||
Component::AttributeInNoNamespace {
|
local_name: local_name!("type"),
|
||||||
local_name: local_name!("type"),
|
local_name_lower: local_name!("type"),
|
||||||
local_name_lower: local_name!("type"),
|
operator: AttrSelectorOperator::Equal,
|
||||||
operator: AttrSelectorOperator::Equal,
|
value: "hidden".to_owned(),
|
||||||
value: "hidden".to_owned(),
|
case_sensitivity: ParsedCaseSensitivity::AsciiCaseInsensitive,
|
||||||
case_sensitivity: ParsedCaseSensitivity::AsciiCaseInsensitive,
|
never_matches: false,
|
||||||
never_matches: false,
|
}
|
||||||
}
|
), (0 << 20) + (1 << 10) + (1 << 0))
|
||||||
]),
|
)),
|
||||||
(0 << 20) + (1 << 10) + (1 << 0)
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
|
block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
|
||||||
(PropertyDeclaration::Display(longhands::display::SpecifiedValue::none),
|
(PropertyDeclaration::Display(longhands::display::SpecifiedValue::none),
|
||||||
Importance::Important),
|
Importance::Important),
|
||||||
|
@ -123,28 +120,23 @@ 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::from_vec(vec!(
|
||||||
Selector::new_for_unit_testing(
|
Selector::from_vec(vec!(
|
||||||
SelectorInner::from_vec(vec![
|
|
||||||
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
|
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
|
||||||
Component::LocalName(LocalName {
|
Component::LocalName(LocalName {
|
||||||
name: local_name!("html"),
|
name: local_name!("html"),
|
||||||
lower_name: local_name!("html"),
|
lower_name: local_name!("html"),
|
||||||
}),
|
}),
|
||||||
]),
|
), (0 << 20) + (0 << 10) + (1 << 0)),
|
||||||
(0 << 20) + (0 << 10) + (1 << 0)
|
Selector::from_vec(vec!(
|
||||||
|
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
|
||||||
|
Component::LocalName(LocalName {
|
||||||
|
name: local_name!("body"),
|
||||||
|
lower_name: local_name!("body"),
|
||||||
|
})
|
||||||
|
), (0 << 20) + (0 << 10) + (1 << 0)
|
||||||
),
|
),
|
||||||
Selector::new_for_unit_testing(
|
)),
|
||||||
SelectorInner::from_vec(vec![
|
|
||||||
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
|
|
||||||
Component::LocalName(LocalName {
|
|
||||||
name: local_name!("body"),
|
|
||||||
lower_name: local_name!("body"),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
(0 << 20) + (0 << 10) + (1 << 0)
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
|
block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
|
||||||
(PropertyDeclaration::Display(longhands::display::SpecifiedValue::block),
|
(PropertyDeclaration::Display(longhands::display::SpecifiedValue::block),
|
||||||
Importance::Normal),
|
Importance::Normal),
|
||||||
|
@ -155,18 +147,15 @@ 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::from_vec(vec!(
|
||||||
Selector::new_for_unit_testing(
|
Selector::from_vec(vec!(
|
||||||
SelectorInner::from_vec(vec![
|
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
|
||||||
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
|
Component::ID(Atom::from("d1")),
|
||||||
Component::ID(Atom::from("d1")),
|
Component::Combinator(Combinator::Child),
|
||||||
Component::Combinator(Combinator::Child),
|
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
|
||||||
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
|
Component::Class(Atom::from("ok"))
|
||||||
Component::Class(Atom::from("ok")),
|
), (1 << 20) + (1 << 10) + (0 << 0))
|
||||||
]),
|
)),
|
||||||
(1 << 20) + (1 << 10) + (0 << 0)
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
|
block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
|
||||||
(PropertyDeclaration::BackgroundColor(
|
(PropertyDeclaration::BackgroundColor(
|
||||||
longhands::background_color::SpecifiedValue {
|
longhands::background_color::SpecifiedValue {
|
||||||
|
|
|
@ -45,7 +45,7 @@ fn get_mock_rules(css_selectors: &[&str]) -> (Vec<Vec<Rule>>, SharedRwLock) {
|
||||||
let guard = shared_lock.read();
|
let guard = shared_lock.read();
|
||||||
let rule = locked.read_with(&guard);
|
let rule = locked.read_with(&guard);
|
||||||
rule.selectors.0.iter().map(|s| {
|
rule.selectors.0.iter().map(|s| {
|
||||||
Rule::new(s.clone(), locked.clone(), i)
|
Rule::new(s.selector.clone(), s.hashes.clone(), locked.clone(), i)
|
||||||
}).collect()
|
}).collect()
|
||||||
}).collect(), shared_lock)
|
}).collect(), shared_lock)
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ fn parse_selectors(selectors: &[&str]) -> Vec<Selector<SelectorImpl>> {
|
||||||
.map(|x| SelectorParser::parse_author_origin_no_namespace(x).unwrap().0
|
.map(|x| SelectorParser::parse_author_origin_no_namespace(x).unwrap().0
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.nth(0)
|
.nth(0)
|
||||||
.unwrap())
|
.unwrap().selector)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +126,6 @@ fn test_revalidation_selectors() {
|
||||||
"p:first-child span",
|
"p:first-child span",
|
||||||
]).into_iter()
|
]).into_iter()
|
||||||
.filter(|s| needs_revalidation(&s))
|
.filter(|s| needs_revalidation(&s))
|
||||||
.map(|s| s.inner.complex)
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let reference = parse_selectors(&[
|
let reference = parse_selectors(&[
|
||||||
|
@ -167,7 +166,6 @@ fn test_revalidation_selectors() {
|
||||||
// Selectors in the ancestor chain (needed for cousin sharing).
|
// Selectors in the ancestor chain (needed for cousin sharing).
|
||||||
"p:first-child span",
|
"p:first-child span",
|
||||||
]).into_iter()
|
]).into_iter()
|
||||||
.map(|s| s.inner.complex)
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(test.len(), reference.len());
|
assert_eq!(test.len(), reference.len());
|
||||||
|
@ -189,22 +187,22 @@ fn test_rule_ordering_same_specificity() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_id_name() {
|
fn test_get_id_name() {
|
||||||
let (rules_list, _) = get_mock_rules(&[".intro", "#top"]);
|
let (rules_list, _) = get_mock_rules(&[".intro", "#top"]);
|
||||||
assert_eq!(selector_map::get_id_name(&rules_list[0][0].selector.inner), None);
|
assert_eq!(selector_map::get_id_name(rules_list[0][0].selector.iter()), None);
|
||||||
assert_eq!(selector_map::get_id_name(&rules_list[1][0].selector.inner), Some(Atom::from("top")));
|
assert_eq!(selector_map::get_id_name(rules_list[1][0].selector.iter()), Some(Atom::from("top")));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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!(selector_map::get_class_name(&rules_list[0][0].selector.inner), Some(Atom::from("foo")));
|
assert_eq!(selector_map::get_class_name(rules_list[0][0].selector.iter()), Some(Atom::from("foo")));
|
||||||
assert_eq!(selector_map::get_class_name(&rules_list[1][0].selector.inner), None);
|
assert_eq!(selector_map::get_class_name(rules_list[1][0].selector.iter()), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_local_name() {
|
fn test_get_local_name() {
|
||||||
let (rules_list, _) = get_mock_rules(&["img.foo", "#top", "IMG", "ImG"]);
|
let (rules_list, _) = get_mock_rules(&["img.foo", "#top", "IMG", "ImG"]);
|
||||||
let check = |i: usize, names: Option<(&str, &str)>| {
|
let check = |i: usize, names: Option<(&str, &str)>| {
|
||||||
assert!(selector_map::get_local_name(&rules_list[i][0].selector.inner)
|
assert!(selector_map::get_local_name(rules_list[i][0].selector.iter())
|
||||||
== names.map(|(name, lower_name)| LocalNameSelector {
|
== names.map(|(name, lower_name)| LocalNameSelector {
|
||||||
name: LocalName::from(name),
|
name: LocalName::from(name),
|
||||||
lower_name: LocalName::from(lower_name) }))
|
lower_name: LocalName::from(lower_name) }))
|
||||||
|
|
|
@ -19,6 +19,11 @@ fn size_of_selectors_dummy_types() {
|
||||||
assert_eq!(align_of::<dummies::Atom>(), align_of::<style::Atom>());
|
assert_eq!(align_of::<dummies::Atom>(), align_of::<style::Atom>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The size of this is critical to performance on the bloom-basic microbenchmark.
|
||||||
|
// When iterating over a large Rule array, we want to be able to fast-reject
|
||||||
|
// selectors (with the inline hashes) with as few cache misses as possible.
|
||||||
|
size_of_test!(test_size_of_rule, style::stylist::Rule, 40);
|
||||||
|
|
||||||
size_of_test!(test_size_of_property_declaration, style::properties::PropertyDeclaration, 32);
|
size_of_test!(test_size_of_property_declaration, style::properties::PropertyDeclaration, 32);
|
||||||
|
|
||||||
// This is huge, but we allocate it on the stack and then never move it,
|
// This is huge, but we allocate it on the stack and then never move it,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue