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
#[derive(Debug, MallocSizeOf)]
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.
pub id_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>,
/// 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]>>,
/// A hash from namespace to rules which contain that namespace selector.
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]>,
/// The number of entries in this map.
pub count: usize,
@ -124,6 +126,7 @@ impl<T: 'static> SelectorMap<T> {
/// Trivially constructs an empty `SelectorMap`.
pub fn new() -> Self {
SelectorMap {
root: SmallVec::new(),
id_hash: MaybeCaseInsensitiveHashMap::new(),
class_hash: MaybeCaseInsensitiveHashMap::new(),
local_name_hash: HashMap::default(),
@ -135,6 +138,7 @@ impl<T: 'static> SelectorMap<T> {
/// Clears the hashmap retaining storage.
pub fn clear(&mut self) {
self.root.clear();
self.id_hash.clear();
self.class_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
// where we began.
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(rules) = self.id_hash.get(id, quirks_mode) {
SelectorMap::get_matching_rules(
@ -287,6 +304,7 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
self.count += 1;
let vector = match find_bucket(entry.selector()) {
Bucket::Root => &mut self.root,
Bucket::ID(id) => self.id_hash
.try_entry(id.clone(), quirks_mode)?
.or_insert_with(SmallVec::new),
@ -340,6 +358,14 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
E: TElement,
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(v) = self.id_hash.get(id, quirks_mode) {
for entry in v.iter() {
@ -444,6 +470,7 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
}
enum Bucket<'a> {
Root,
ID(&'a Atom),
Class(&'a Atom),
LocalName {
@ -456,6 +483,7 @@ enum Bucket<'a> {
fn specific_bucket_for<'a>(component: &'a Component<SelectorImpl>) -> Bucket<'a> {
match *component {
Component::Root => Bucket::Root,
Component::ID(ref id) => Bucket::ID(id),
Component::Class(ref class) => Bucket::Class(class),
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,
// where:
//
// id > class > local name > namespace > universal.
// root > id > class > local name > namespace > universal.
//
for ss in &mut iter {
let new_bucket = specific_bucket_for(ss);
match new_bucket {
Bucket::ID(..) => return new_bucket,
Bucket::Class(..) => {
Bucket::Root => return new_bucket,
Bucket::ID(..) => {
current_bucket = new_bucket;
}
Bucket::Class(..) => {
if !matches!(current_bucket, Bucket::ID(..)) {
current_bucket = new_bucket;
}
},
Bucket::LocalName { .. } => {
if matches!(current_bucket, Bucket::Universal | Bucket::Namespace(..)) {