Bug 1889109: Part 5 - Add scope subject map and use it to early-reject elements that can't be scopes. r=firefox-style-system-reviewers,emilio

Differential Revision: https://phabricator.services.mozilla.com/D212729
This commit is contained in:
David Shin 2024-07-15 18:41:14 +00:00
Родитель a565996e37
Коммит f3304f0ef8
2 изменённых файлов: 126 добавлений и 7 удалений

Просмотреть файл

@ -15,14 +15,15 @@ use crate::shared_lock::{
};
use crate::str::CssStringWriter;
use crate::stylesheets::CssRules;
use crate::simple_buckets_map::SimpleBucketsMap;
use cssparser::{Parser, SourceLocation, ToCss};
#[cfg(feature = "gecko")]
use malloc_size_of::{
MallocSizeOfOps, MallocUnconditionalShallowSizeOf, MallocUnconditionalSizeOf,
};
use selectors::context::MatchingContext;
use selectors::context::{MatchingContext, QuirksMode};
use selectors::matching::matches_selector;
use selectors::parser::{ParseRelative, Selector, SelectorList};
use selectors::parser::{Component, ParseRelative, Selector, SelectorList};
use selectors::OpaqueElement;
use servo_arc::Arc;
use std::fmt::{self, Write};
@ -206,15 +207,19 @@ pub enum ScopeTarget<'a> {
impl<'a> ScopeTarget<'a> {
/// Check if the given element is the scope.
pub fn check<E: TElement>(
fn check<E: TElement>(
&self,
element: E,
scope: Option<OpaqueElement>,
scope_subject_map: &ScopeSubjectMap,
context: &mut MatchingContext<E::Impl>,
) -> bool {
match self {
Self::Selector(list) => {
context.nest_for_scope_condition(scope, |context| {
if scope_subject_map.early_reject(element, context.quirks_mode()) {
return false;
}
for selector in list.slice().iter() {
if matches_selector(selector, 0, None, &element, context) {
return true;
@ -245,6 +250,7 @@ pub fn collect_scope_roots<E>(
context: &mut MatchingContext<E::Impl>,
target: &ScopeTarget,
matches_shadow_host: bool,
scope_subject_map: &ScopeSubjectMap,
) -> Vec<ScopeRootCandidate>
where
E: TElement,
@ -256,7 +262,7 @@ where
if ceiling == Some(p.opaque()) {
break;
}
if target.check(p, ceiling, context) {
if target.check(p, ceiling, scope_subject_map, context) {
result.push(ScopeRootCandidate {
root: p.opaque(),
proximity: ScopeProximity::new(proximity),
@ -308,3 +314,106 @@ where
return false;
})
}
/// A map containing simple selectors in subjects of scope selectors.
/// This allows fast-rejecting scopes before running the full match.
#[derive(Clone, Debug, Default, MallocSizeOf)]
pub struct ScopeSubjectMap {
buckets: SimpleBucketsMap<()>,
any: bool,
}
impl ScopeSubjectMap {
/// Add the `<scope-start>` of a scope.
pub fn add_bound_start(&mut self, selectors: &SelectorList<SelectorImpl>, quirks_mode: QuirksMode) {
if self.add_selector_list(selectors, quirks_mode) {
self.any = true;
}
}
fn add_selector_list(&mut self, selectors: &SelectorList<SelectorImpl>, quirks_mode: QuirksMode) -> bool {
let mut is_any = false;
for selector in selectors.slice().iter() {
is_any = is_any || self.add_selector(selector, quirks_mode);
}
is_any
}
fn add_selector(&mut self, selector: &Selector<SelectorImpl>, quirks_mode: QuirksMode) -> bool {
let mut is_any = true;
let mut iter = selector.iter();
while let Some(c) = iter.next() {
let component_any = match c {
Component::Class(cls) => {
match self.buckets.classes.try_entry(cls.0.clone(), quirks_mode) {
Ok(e) => {
e.or_insert(());
false
},
Err(_) => true,
}
},
Component::ID(id) => {
match self.buckets.ids.try_entry(id.0.clone(), quirks_mode) {
Ok(e) => {
e.or_insert(());
false
},
Err(_) => true,
}
},
Component::LocalName(local_name) => {
self.buckets.local_names.insert(local_name.lower_name.clone(), ());
false
},
Component::Is(ref list) | Component::Where(ref list) => {
self.add_selector_list(list, quirks_mode)
},
_ => true,
};
is_any = is_any && component_any;
}
is_any
}
/// Shrink the map as much as possible.
pub fn shrink_if_needed(&mut self) {
self.buckets.shrink_if_needed();
}
/// Clear the map.
pub fn clear(&mut self) {
self.buckets.clear();
self.any = false;
}
/// Could a given element possibly be a scope root?
fn early_reject<E: TElement>(&self, element: E, quirks_mode: QuirksMode) -> bool {
if self.any {
return false;
}
if let Some(id) = element.id() {
if self.buckets.ids.get(id, quirks_mode).is_some() {
return false;
}
}
let mut found = false;
element.each_class(|cls| {
if self.buckets.classes.get(cls, quirks_mode).is_some() {
found = true;
}
});
if found {
return false;
}
if self.buckets.local_names.get(element.local_name()).is_some() {
return false;
}
true
}
}

Просмотреть файл

@ -42,8 +42,7 @@ use crate::stylesheets::import_rule::ImportLayer;
use crate::stylesheets::keyframes_rule::KeyframesAnimation;
use crate::stylesheets::layer_rule::{LayerName, LayerOrder};
use crate::stylesheets::scope_rule::{
collect_scope_roots, element_is_outside_of_scope, ImplicitScopeRoot, ScopeRootCandidate,
ScopeTarget,
collect_scope_roots, element_is_outside_of_scope, ImplicitScopeRoot, ScopeRootCandidate, ScopeSubjectMap, ScopeTarget
};
#[cfg(feature = "gecko")]
use crate::stylesheets::{
@ -2640,6 +2639,9 @@ pub struct CascadeData {
/// The list of scope conditions, indexed by their id.
scope_conditions: SmallVec<[ScopeConditionReference; 1]>,
/// Map of unique selectors on scope start selectors' subjects.
scope_subject_map: ScopeSubjectMap,
/// Effective media query results cached from the last rebuild.
effective_media_query_results: EffectiveMediaQueryResults,
@ -2699,6 +2701,7 @@ impl CascadeData {
layers: smallvec::smallvec![CascadeLayer::root()],
container_conditions: smallvec::smallvec![ContainerConditionReference::none()],
scope_conditions: smallvec::smallvec![ScopeConditionReference::none()],
scope_subject_map: Default::default(),
extra_data: ExtraStyleData::default(),
effective_media_query_results: EffectiveMediaQueryResults::new(),
rules_source_order: 0,
@ -2996,7 +2999,7 @@ impl CascadeData {
};
let potential_scope_roots = if is_outermost_scope {
collect_scope_roots(element, None, context, &root_target, matches_shadow_host)
collect_scope_roots(element, None, context, &root_target, matches_shadow_host, &self.scope_subject_map)
} else {
let mut result = vec![];
for activation in &outer_scope_roots {
@ -3006,6 +3009,7 @@ impl CascadeData {
context,
&root_target,
matches_shadow_host,
&self.scope_subject_map,
);
result.append(&mut this_result);
}
@ -3071,6 +3075,7 @@ impl CascadeData {
self.mapped_ids.shrink_if_needed();
self.layer_id.shrink_if_needed();
self.selectors_for_cache_revalidation.shrink_if_needed();
self.scope_subject_map.shrink_if_needed();
}
fn compute_layer_order(&mut self) {
@ -3599,6 +3604,10 @@ impl CascadeData {
ScopeBoundsWithHashes::new(quirks_mode, start, end)
};
if let Some(selectors) = replaced.start.as_ref() {
self.scope_subject_map.add_bound_start(&selectors.selectors, quirks_mode);
}
self.scope_conditions.push(ScopeConditionReference {
parent: containing_rule_state.scope_condition_id,
condition: Some(replaced),
@ -3850,6 +3859,7 @@ impl CascadeData {
self.nth_of_mapped_ids.clear();
self.selectors_for_cache_revalidation.clear();
self.effective_media_query_results.clear();
self.scope_subject_map.clear();
}
}