mirror of
https://github.com/servo/servo.git
synced 2025-06-20 15:18:58 +01:00
style: Add a low-priority selector list for pseudo-classes that have global rules
This avoids trying to match those global rules for most elements that can't match them anyways. Differential Revision: https://phabricator.services.mozilla.com/D147640
This commit is contained in:
parent
1162204bad
commit
c283b32991
2 changed files with 86 additions and 15 deletions
|
@ -407,24 +407,24 @@ where
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let state_changes = self.state_changes;
|
self.collect_state_dependencies(&map.state_affecting_selectors)
|
||||||
if !state_changes.is_empty() {
|
|
||||||
self.collect_state_dependencies(&map.state_affecting_selectors, state_changes)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_state_dependencies(
|
fn collect_state_dependencies(
|
||||||
&mut self,
|
&mut self,
|
||||||
map: &'selectors SelectorMap<StateDependency>,
|
map: &'selectors SelectorMap<StateDependency>,
|
||||||
state_changes: ElementState,
|
|
||||||
) {
|
) {
|
||||||
|
if self.state_changes.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
map.lookup_with_additional(
|
map.lookup_with_additional(
|
||||||
self.lookup_element,
|
self.lookup_element,
|
||||||
self.matching_context.quirks_mode(),
|
self.matching_context.quirks_mode(),
|
||||||
self.removed_id,
|
self.removed_id,
|
||||||
self.classes_removed,
|
self.classes_removed,
|
||||||
|
self.state_changes,
|
||||||
|dependency| {
|
|dependency| {
|
||||||
if !dependency.state.intersects(state_changes) {
|
if !dependency.state.intersects(self.state_changes) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
self.scan_dependency(&dependency.dep);
|
self.scan_dependency(&dependency.dep);
|
||||||
|
|
|
@ -10,9 +10,10 @@ use crate::context::QuirksMode;
|
||||||
use crate::dom::TElement;
|
use crate::dom::TElement;
|
||||||
use crate::rule_tree::CascadeLevel;
|
use crate::rule_tree::CascadeLevel;
|
||||||
use crate::selector_parser::SelectorImpl;
|
use crate::selector_parser::SelectorImpl;
|
||||||
use crate::stylist::{Stylist, CascadeData, Rule, ContainerConditionId};
|
use crate::stylist::{CascadeData, ContainerConditionId, Rule, Stylist};
|
||||||
use crate::AllocErr;
|
use crate::AllocErr;
|
||||||
use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom};
|
use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom};
|
||||||
|
use dom::ElementState;
|
||||||
use precomputed_hash::PrecomputedHash;
|
use precomputed_hash::PrecomputedHash;
|
||||||
use selectors::matching::{matches_selector, MatchingContext};
|
use selectors::matching::{matches_selector, MatchingContext};
|
||||||
use selectors::parser::{Combinator, Component, SelectorIter};
|
use selectors::parser::{Combinator, Component, SelectorIter};
|
||||||
|
@ -33,6 +34,22 @@ impl Default for PrecomputedHasher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This is a set of pseudo-classes that are both relatively-rare (they don't
|
||||||
|
/// affect most elements by default) and likely or known to have global rules
|
||||||
|
/// (in e.g., the UA sheets).
|
||||||
|
///
|
||||||
|
/// We can avoid selector-matching those global rules for all elements without
|
||||||
|
/// these pseudo-class states.
|
||||||
|
const RARE_PSEUDO_CLASS_STATES: ElementState = ElementState::from_bits_truncate(
|
||||||
|
ElementState::FULLSCREEN.bits() |
|
||||||
|
ElementState::VISITED_OR_UNVISITED.bits() |
|
||||||
|
ElementState::URLTARGET.bits() |
|
||||||
|
ElementState::INERT.bits() |
|
||||||
|
ElementState::FOCUS.bits() |
|
||||||
|
ElementState::FOCUSRING.bits() |
|
||||||
|
ElementState::TOPMOST_MODAL.bits()
|
||||||
|
);
|
||||||
|
|
||||||
/// A simple alias for a hashmap using PrecomputedHasher.
|
/// A simple alias for a hashmap using PrecomputedHasher.
|
||||||
pub type PrecomputedHashMap<K, V> = HashMap<K, V, BuildHasherDefault<PrecomputedHasher>>;
|
pub type PrecomputedHashMap<K, V> = HashMap<K, V, BuildHasherDefault<PrecomputedHasher>>;
|
||||||
|
|
||||||
|
@ -107,6 +124,8 @@ pub struct SelectorMap<T: 'static> {
|
||||||
pub attribute_hash: PrecomputedHashMap<LocalName, SmallVec<[T; 1]>>,
|
pub attribute_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 for pseudo-states that are rare but have global selectors.
|
||||||
|
pub rare_pseudo_classes: SmallVec<[T; 1]>,
|
||||||
/// All other rules.
|
/// All other rules.
|
||||||
pub other: SmallVec<[T; 1]>,
|
pub other: SmallVec<[T; 1]>,
|
||||||
/// Whether we should bucket by attribute names.
|
/// Whether we should bucket by attribute names.
|
||||||
|
@ -132,6 +151,7 @@ impl<T> SelectorMap<T> {
|
||||||
attribute_hash: HashMap::default(),
|
attribute_hash: HashMap::default(),
|
||||||
local_name_hash: HashMap::default(),
|
local_name_hash: HashMap::default(),
|
||||||
namespace_hash: HashMap::default(),
|
namespace_hash: HashMap::default(),
|
||||||
|
rare_pseudo_classes: SmallVec::new(),
|
||||||
other: SmallVec::new(),
|
other: SmallVec::new(),
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
bucket_attributes: static_prefs::pref!("layout.css.bucket-attribute-names.enabled"),
|
bucket_attributes: static_prefs::pref!("layout.css.bucket-attribute-names.enabled"),
|
||||||
|
@ -166,6 +186,7 @@ impl<T> SelectorMap<T> {
|
||||||
self.attribute_hash.clear();
|
self.attribute_hash.clear();
|
||||||
self.local_name_hash.clear();
|
self.local_name_hash.clear();
|
||||||
self.namespace_hash.clear();
|
self.namespace_hash.clear();
|
||||||
|
self.rare_pseudo_classes.clear();
|
||||||
self.other.clear();
|
self.other.clear();
|
||||||
self.count = 0;
|
self.count = 0;
|
||||||
}
|
}
|
||||||
|
@ -272,6 +293,18 @@ impl SelectorMap<Rule> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rule_hash_target.state().intersects(RARE_PSEUDO_CLASS_STATES) {
|
||||||
|
SelectorMap::get_matching_rules(
|
||||||
|
element,
|
||||||
|
&self.rare_pseudo_classes,
|
||||||
|
matching_rules_list,
|
||||||
|
matching_context,
|
||||||
|
cascade_level,
|
||||||
|
cascade_data,
|
||||||
|
stylist,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(rules) = self.namespace_hash.get(rule_hash_target.namespace()) {
|
if let Some(rules) = self.namespace_hash.get(rule_hash_target.namespace()) {
|
||||||
SelectorMap::get_matching_rules(
|
SelectorMap::get_matching_rules(
|
||||||
element,
|
element,
|
||||||
|
@ -385,6 +418,7 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
self.namespace_hash.try_reserve(1)?;
|
self.namespace_hash.try_reserve(1)?;
|
||||||
self.namespace_hash.entry(url.clone()).or_default()
|
self.namespace_hash.entry(url.clone()).or_default()
|
||||||
},
|
},
|
||||||
|
Bucket::RarePseudoClasses => &mut self.rare_pseudo_classes,
|
||||||
Bucket::Universal => &mut self.other,
|
Bucket::Universal => &mut self.other,
|
||||||
};
|
};
|
||||||
vec.try_reserve(1)?;
|
vec.try_reserve(1)?;
|
||||||
|
@ -443,8 +477,22 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
///
|
///
|
||||||
/// FIXME(bholley) This overlaps with SelectorMap<Rule>::get_all_matching_rules,
|
/// FIXME(bholley) This overlaps with SelectorMap<Rule>::get_all_matching_rules,
|
||||||
/// but that function is extremely hot and I'd rather not rearrange it.
|
/// but that function is extremely hot and I'd rather not rearrange it.
|
||||||
|
pub fn lookup<'a, E, F>(&'a self, element: E, quirks_mode: QuirksMode, f: F) -> bool
|
||||||
|
where
|
||||||
|
E: TElement,
|
||||||
|
F: FnMut(&'a T) -> bool,
|
||||||
|
{
|
||||||
|
self.lookup_with_state(element, element.state(), quirks_mode, f)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn lookup<'a, E, F>(&'a self, element: E, quirks_mode: QuirksMode, mut f: F) -> bool
|
fn lookup_with_state<'a, E, F>(
|
||||||
|
&'a self,
|
||||||
|
element: E,
|
||||||
|
element_state: ElementState,
|
||||||
|
quirks_mode: QuirksMode,
|
||||||
|
mut f: F,
|
||||||
|
) -> bool
|
||||||
where
|
where
|
||||||
E: TElement,
|
E: TElement,
|
||||||
F: FnMut(&'a T) -> bool,
|
F: FnMut(&'a T) -> bool,
|
||||||
|
@ -522,6 +570,14 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if element_state.intersects(RARE_PSEUDO_CLASS_STATES) {
|
||||||
|
for entry in self.rare_pseudo_classes.iter() {
|
||||||
|
if !f(&entry) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for entry in self.other.iter() {
|
for entry in self.other.iter() {
|
||||||
if !f(&entry) {
|
if !f(&entry) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -545,6 +601,7 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
additional_id: Option<&WeakAtom>,
|
additional_id: Option<&WeakAtom>,
|
||||||
additional_classes: &[Atom],
|
additional_classes: &[Atom],
|
||||||
|
additional_states: ElementState,
|
||||||
mut f: F,
|
mut f: F,
|
||||||
) -> bool
|
) -> bool
|
||||||
where
|
where
|
||||||
|
@ -552,7 +609,12 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
F: FnMut(&'a T) -> bool,
|
F: FnMut(&'a T) -> bool,
|
||||||
{
|
{
|
||||||
// Do the normal lookup.
|
// Do the normal lookup.
|
||||||
if !self.lookup(element, quirks_mode, |entry| f(entry)) {
|
if !self.lookup_with_state(
|
||||||
|
element,
|
||||||
|
element.state() | additional_states,
|
||||||
|
quirks_mode,
|
||||||
|
|entry| f(entry),
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,6 +647,7 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
enum Bucket<'a> {
|
enum Bucket<'a> {
|
||||||
Universal,
|
Universal,
|
||||||
Namespace(&'a Namespace),
|
Namespace(&'a Namespace),
|
||||||
|
RarePseudoClasses,
|
||||||
LocalName {
|
LocalName {
|
||||||
name: &'a LocalName,
|
name: &'a LocalName,
|
||||||
lower_name: &'a LocalName,
|
lower_name: &'a LocalName,
|
||||||
|
@ -599,17 +662,18 @@ enum Bucket<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Bucket<'a> {
|
impl<'a> Bucket<'a> {
|
||||||
/// root > id > class > local name > namespace > universal.
|
/// root > id > class > local name > namespace > pseudo-classes > universal.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn specificity(&self) -> usize {
|
fn specificity(&self) -> usize {
|
||||||
match *self {
|
match *self {
|
||||||
Bucket::Universal => 0,
|
Bucket::Universal => 0,
|
||||||
Bucket::Namespace(..) => 1,
|
Bucket::Namespace(..) => 1,
|
||||||
Bucket::LocalName { .. } => 2,
|
Bucket::RarePseudoClasses => 2,
|
||||||
Bucket::Attribute { .. } => 3,
|
Bucket::LocalName { .. } => 3,
|
||||||
Bucket::Class(..) => 4,
|
Bucket::Attribute { .. } => 4,
|
||||||
Bucket::ID(..) => 5,
|
Bucket::Class(..) => 5,
|
||||||
Bucket::Root => 6,
|
Bucket::ID(..) => 6,
|
||||||
|
Bucket::Root => 7,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -689,6 +753,13 @@ fn specific_bucket_for<'a>(
|
||||||
Bucket::Universal
|
Bucket::Universal
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Component::NonTSPseudoClass(ref pseudo_class)
|
||||||
|
if pseudo_class
|
||||||
|
.state_flag()
|
||||||
|
.intersects(RARE_PSEUDO_CLASS_STATES) =>
|
||||||
|
{
|
||||||
|
Bucket::RarePseudoClasses
|
||||||
|
},
|
||||||
_ => Bucket::Universal,
|
_ => Bucket::Universal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue