style: Add a root bucket to the selector map.

:root can't change without getting unbound from the tree so no fancy stuff other
than that needed.

This removes a lot of revalidation and attribute invalidation matching from the
Chrome, and looks like it should be a good idea in general.

Differential Revision: https://phabricator.services.mozilla.com/D2462
This commit is contained in:
Emilio Cobos Álvarez 2018-07-30 12:53:18 +02:00
parent 0b520c9558
commit 2e3aacdf80
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C

View file

@ -96,6 +96,8 @@ pub trait SelectorMapEntry: Sized + Clone {
/// TODO: Tune the initial capacity of the HashMap /// TODO: Tune the initial capacity of the HashMap
#[derive(Debug, MallocSizeOf)] #[derive(Debug, MallocSizeOf)]
pub struct SelectorMap<T: 'static> { pub struct SelectorMap<T: 'static> {
/// Rules that have `:root` selectors.
pub root: SmallVec<[T; 1]>,
/// A hash from an ID to rules which contain that ID selector. /// A hash from an ID to rules which contain that ID selector.
pub id_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>, pub id_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>,
/// A hash from a class name to rules which contain that class selector. /// A hash from a class name to rules which contain that class selector.
@ -104,7 +106,7 @@ pub struct SelectorMap<T: 'static> {
pub local_name_hash: PrecomputedHashMap<LocalName, SmallVec<[T; 1]>>, pub local_name_hash: PrecomputedHashMap<LocalName, SmallVec<[T; 1]>>,
/// A hash from namespace to rules which contain that namespace selector. /// A hash from namespace to rules which contain that namespace selector.
pub namespace_hash: PrecomputedHashMap<Namespace, SmallVec<[T; 1]>>, pub namespace_hash: PrecomputedHashMap<Namespace, SmallVec<[T; 1]>>,
/// Rules that don't have ID, class, or element selectors. /// All other rules.
pub other: SmallVec<[T; 1]>, pub other: SmallVec<[T; 1]>,
/// The number of entries in this map. /// The number of entries in this map.
pub count: usize, pub count: usize,
@ -124,6 +126,7 @@ impl<T: 'static> SelectorMap<T> {
/// Trivially constructs an empty `SelectorMap`. /// Trivially constructs an empty `SelectorMap`.
pub fn new() -> Self { pub fn new() -> Self {
SelectorMap { SelectorMap {
root: SmallVec::new(),
id_hash: MaybeCaseInsensitiveHashMap::new(), id_hash: MaybeCaseInsensitiveHashMap::new(),
class_hash: MaybeCaseInsensitiveHashMap::new(), class_hash: MaybeCaseInsensitiveHashMap::new(),
local_name_hash: HashMap::default(), local_name_hash: HashMap::default(),
@ -135,6 +138,7 @@ impl<T: 'static> SelectorMap<T> {
/// Clears the hashmap retaining storage. /// Clears the hashmap retaining storage.
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.root.clear();
self.id_hash.clear(); self.id_hash.clear();
self.class_hash.clear(); self.class_hash.clear();
self.local_name_hash.clear(); self.local_name_hash.clear();
@ -181,6 +185,19 @@ impl SelectorMap<Rule> {
// At the end, we're going to sort the rules that we added, so remember // At the end, we're going to sort the rules that we added, so remember
// where we began. // where we began.
let init_len = matching_rules_list.len(); let init_len = matching_rules_list.len();
if rule_hash_target.is_root() {
SelectorMap::get_matching_rules(
element,
&self.root,
matching_rules_list,
context,
flags_setter,
cascade_level,
shadow_cascade_order,
);
}
if let Some(id) = rule_hash_target.id() { if let Some(id) = rule_hash_target.id() {
if let Some(rules) = self.id_hash.get(id, quirks_mode) { if let Some(rules) = self.id_hash.get(id, quirks_mode) {
SelectorMap::get_matching_rules( SelectorMap::get_matching_rules(
@ -287,6 +304,7 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
self.count += 1; self.count += 1;
let vector = match find_bucket(entry.selector()) { let vector = match find_bucket(entry.selector()) {
Bucket::Root => &mut self.root,
Bucket::ID(id) => self.id_hash Bucket::ID(id) => self.id_hash
.try_entry(id.clone(), quirks_mode)? .try_entry(id.clone(), quirks_mode)?
.or_insert_with(SmallVec::new), .or_insert_with(SmallVec::new),
@ -340,6 +358,14 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
E: TElement, E: TElement,
F: FnMut(&'a T) -> bool, F: FnMut(&'a T) -> bool,
{ {
if element.is_root() {
for entry in self.root.iter() {
if !f(&entry) {
return false;
}
}
}
if let Some(id) = element.id() { if let Some(id) = element.id() {
if let Some(v) = self.id_hash.get(id, quirks_mode) { if let Some(v) = self.id_hash.get(id, quirks_mode) {
for entry in v.iter() { for entry in v.iter() {
@ -444,6 +470,7 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
} }
enum Bucket<'a> { enum Bucket<'a> {
Root,
ID(&'a Atom), ID(&'a Atom),
Class(&'a Atom), Class(&'a Atom),
LocalName { LocalName {
@ -456,6 +483,7 @@ enum Bucket<'a> {
fn specific_bucket_for<'a>(component: &'a Component<SelectorImpl>) -> Bucket<'a> { fn specific_bucket_for<'a>(component: &'a Component<SelectorImpl>) -> Bucket<'a> {
match *component { match *component {
Component::Root => Bucket::Root,
Component::ID(ref id) => Bucket::ID(id), Component::ID(ref id) => Bucket::ID(id),
Component::Class(ref class) => Bucket::Class(class), Component::Class(ref class) => Bucket::Class(class),
Component::LocalName(ref selector) => Bucket::LocalName { Component::LocalName(ref selector) => Bucket::LocalName {
@ -498,14 +526,19 @@ fn find_bucket<'a>(mut iter: SelectorIter<'a, SelectorImpl>) -> Bucket<'a> {
// We basically want to find the most specific bucket, // We basically want to find the most specific bucket,
// where: // where:
// //
// id > class > local name > namespace > universal. // root > id > class > local name > namespace > universal.
// //
for ss in &mut iter { for ss in &mut iter {
let new_bucket = specific_bucket_for(ss); let new_bucket = specific_bucket_for(ss);
match new_bucket { match new_bucket {
Bucket::ID(..) => return new_bucket, Bucket::Root => return new_bucket,
Bucket::Class(..) => { Bucket::ID(..) => {
current_bucket = new_bucket; current_bucket = new_bucket;
}
Bucket::Class(..) => {
if !matches!(current_bucket, Bucket::ID(..)) {
current_bucket = new_bucket;
}
}, },
Bucket::LocalName { .. } => { Bucket::LocalName { .. } => {
if matches!(current_bucket, Bucket::Universal | Bucket::Namespace(..)) { if matches!(current_bucket, Bucket::Universal | Bucket::Namespace(..)) {