зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
a565996e37
Коммит
f3304f0ef8
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче