Bug 1792501: Part 2 - Infrastructure for relative selector invalidation. r=emilio

Invalidating a relative selector requires traversal in the opposite direction of
the usual invalidation, i.e. In the directions of ancestor and/or earlier sibling.
However, when there are complex selectors within the relative selector, e.g.
`:has(:is(..) ..)`, we first need to perform invalidation in the usual direction to
reach the relative selector's search space, then perform the relative selector
invalidation.

There are two major changes to this effect:

1. `InvalidationProcessor` has an additional lifetime that separates matching context from
invalidations. This enables storing encountered dependencies (Since we may be in a deep recursion
during the invalidation) to be relative selector invalidated, without requiring that the
matching context live that long.

2. There now exists a separate category for relative selector invalidation depenedencies,
which triggers relative selector invalidation. Dependencies now can be either normal or
relative, since any complex selector inside a relative selector would have normal
dependencies, but with its outer dependency being a relative dependency.

Differential Revision: https://phabricator.services.mozilla.com/D185675
This commit is contained in:
David Shin 2023-09-14 22:21:25 +00:00
Родитель 612ec06c74
Коммит 0dfd1143fd
6 изменённых файлов: 299 добавлений и 113 удалений

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

@ -8,7 +8,9 @@
use crate::context::QuirksMode;
use crate::dom::{TDocument, TElement, TNode, TShadowRoot};
use crate::invalidation::element::invalidation_map::Dependency;
use crate::invalidation::element::invalidator::{DescendantInvalidationLists, Invalidation};
use crate::invalidation::element::invalidator::{
DescendantInvalidationLists, Invalidation, SiblingTraversalMap,
};
use crate::invalidation::element::invalidator::{InvalidationProcessor, InvalidationVector};
use crate::selector_parser::SelectorImpl;
use crate::values::AtomIdent;
@ -138,18 +140,19 @@ impl<E: TElement> SelectorQuery<E> for QueryFirst {
}
}
struct QuerySelectorProcessor<'a, E, Q>
struct QuerySelectorProcessor<'a, 'b, E, Q>
where
E: TElement + 'a,
Q: SelectorQuery<E>,
Q::Output: 'a,
{
results: &'a mut Q::Output,
matching_context: MatchingContext<'a, E::Impl>,
matching_context: MatchingContext<'b, E::Impl>,
traversal_map: SiblingTraversalMap<E>,
dependencies: &'a [Dependency],
}
impl<'a, E, Q> InvalidationProcessor<'a, E> for QuerySelectorProcessor<'a, E, Q>
impl<'a, 'b, E, Q> InvalidationProcessor<'a, 'b, E> for QuerySelectorProcessor<'a, 'b, E, Q>
where
E: TElement + 'a,
Q: SelectorQuery<E>,
@ -205,10 +208,14 @@ where
false
}
fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {
fn matching_context(&mut self) -> &mut MatchingContext<'b, E::Impl> {
&mut self.matching_context
}
fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> {
&self.traversal_map
}
fn should_process_descendants(&mut self, _: E) -> bool {
if Q::should_stop_after_first_match() {
return Q::is_empty(&self.results);
@ -788,6 +795,7 @@ pub fn query_selector<E, Q>(
let mut processor = QuerySelectorProcessor::<E, Q> {
results,
matching_context,
traversal_map: SiblingTraversalMap::default(),
dependencies: &dependencies,
};

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

@ -6,7 +6,9 @@
use crate::dom::TElement;
use crate::invalidation::element::invalidation_map::Dependency;
use crate::invalidation::element::invalidator::{DescendantInvalidationLists, InvalidationVector};
use crate::invalidation::element::invalidator::{
DescendantInvalidationLists, InvalidationVector, SiblingTraversalMap,
};
use crate::invalidation::element::invalidator::{Invalidation, InvalidationProcessor};
use crate::invalidation::element::state_and_attributes;
use crate::stylist::CascadeData;
@ -35,13 +37,15 @@ impl Default for InvalidationMatchingData {
/// An invalidation processor for style changes due to state and attribute
/// changes.
pub struct DocumentStateInvalidationProcessor<'a, E: TElement, I> {
pub struct DocumentStateInvalidationProcessor<'a, 'b, E: TElement, I> {
rules: I,
matching_context: MatchingContext<'a, E::Impl>,
traversal_map: SiblingTraversalMap<E>,
document_states_changed: DocumentState,
_marker: std::marker::PhantomData<&'b ()>,
}
impl<'a, E: TElement, I> DocumentStateInvalidationProcessor<'a, E, I> {
impl<'a, 'b, E: TElement, I> DocumentStateInvalidationProcessor<'a, 'b, E, I> {
/// Creates a new DocumentStateInvalidationProcessor.
#[inline]
pub fn new(
@ -66,14 +70,17 @@ impl<'a, E: TElement, I> DocumentStateInvalidationProcessor<'a, E, I> {
rules,
document_states_changed,
matching_context,
traversal_map: SiblingTraversalMap::default(),
_marker: std::marker::PhantomData,
}
}
}
impl<'a, E, I> InvalidationProcessor<'a, E> for DocumentStateInvalidationProcessor<'a, E, I>
impl<'a, 'b, E, I> InvalidationProcessor<'b, 'a, E>
for DocumentStateInvalidationProcessor<'a, 'b, E, I>
where
E: TElement,
I: Iterator<Item = &'a CascadeData>,
I: Iterator<Item = &'b CascadeData>,
{
fn check_outer_dependency(&mut self, _: &Dependency, _: E) -> bool {
debug_assert!(
@ -86,9 +93,9 @@ where
fn collect_invalidations(
&mut self,
_element: E,
self_invalidations: &mut InvalidationVector<'a>,
_descendant_invalidations: &mut DescendantInvalidationLists<'a>,
_sibling_invalidations: &mut InvalidationVector<'a>,
self_invalidations: &mut InvalidationVector<'b>,
_descendant_invalidations: &mut DescendantInvalidationLists<'b>,
_sibling_invalidations: &mut InvalidationVector<'b>,
) -> bool {
for cascade_data in &mut self.rules {
let map = cascade_data.invalidation_map();
@ -118,6 +125,10 @@ where
&mut self.matching_context
}
fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> {
&self.traversal_map
}
fn recursion_limit_exceeded(&mut self, _: E) {
unreachable!("We don't run document state invalidation with stack limits")
}

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

@ -63,11 +63,21 @@ pub struct Dependency {
///
#[ignore_malloc_size_of = "Arc"]
pub parent: Option<Arc<Dependency>>,
/// What kind of relative selector invalidation this generates.
/// None if this dependency is not within a relative selector.
relative_kind: Option<RelativeDependencyInvalidationKind>,
}
impl SelectorMapEntry for Dependency {
fn selector(&self) -> SelectorIter<SelectorImpl> {
self.selector.iter_from(self.selector_offset)
}
}
/// The kind of elements down the tree this dependency may affect.
#[derive(Debug, Eq, PartialEq)]
pub enum DependencyInvalidationKind {
#[derive(Clone, Copy, Debug, Eq, PartialEq, MallocSizeOf)]
pub enum NormalDependencyInvalidationKind {
/// This dependency may affect the element that changed itself.
Element,
/// This dependency affects the style of the element itself, and also the
@ -86,6 +96,34 @@ pub enum DependencyInvalidationKind {
Parts,
}
/// The kind of elements up the tree this relative selector dependency may
/// affect. Because this travels upwards, it's not viable for parallel subtree
/// traversal, and is handled separately.
#[derive(Clone, Copy, Debug, Eq, PartialEq, MallocSizeOf)]
pub enum RelativeDependencyInvalidationKind {
/// This dependency may affect relative selector anchors for ancestors.
Ancestors,
/// This dependency may affect a relative selector anchor for the parent.
Parent,
/// This dependency may affect a relative selector anchor for the previous sibling.
PrevSibling,
/// This dependency may affect relative selector anchors for ancestors' previous siblings.
AncestorPrevSibling,
/// This dependency may affect relative selector anchors for earlier siblings.
EarlierSibling,
/// This dependency may affect relative selector anchors for ancestors' earlier siblings.
AncestorEarlierSibling,
}
/// Invalidation kind merging normal and relative dependencies.
#[derive(Clone, Copy, Debug, Eq, PartialEq, MallocSizeOf)]
pub enum DependencyInvalidationKind {
/// This dependency is a normal dependency.
Normal(NormalDependencyInvalidationKind),
/// This dependency is a relative dependency.
Relative(RelativeDependencyInvalidationKind),
}
impl Dependency {
/// Creates a dummy dependency to invalidate the whole selector.
///
@ -100,6 +138,7 @@ impl Dependency {
selector_offset: selector.len() + 1,
selector,
parent: None,
relative_kind: None,
}
}
@ -107,7 +146,7 @@ impl Dependency {
/// dependency represents.
///
/// TODO(emilio): Consider storing inline if it helps cache locality?
pub fn combinator(&self) -> Option<Combinator> {
fn combinator(&self) -> Option<Combinator> {
if self.selector_offset == 0 {
return None;
}
@ -118,28 +157,32 @@ impl Dependency {
)
}
/// The kind of invalidation that this would generate.
pub fn invalidation_kind(&self) -> DependencyInvalidationKind {
/// The kind of normal invalidation that this would generate. The dependency
/// in question must be a normal dependency.
pub fn normal_invalidation_kind(&self) -> NormalDependencyInvalidationKind {
debug_assert!(self.relative_kind.is_none(), "Querying normal invalidation kind on relative dependency.");
match self.combinator() {
None => DependencyInvalidationKind::Element,
None => NormalDependencyInvalidationKind::Element,
Some(Combinator::Child) | Some(Combinator::Descendant) => {
DependencyInvalidationKind::Descendants
NormalDependencyInvalidationKind::Descendants
},
Some(Combinator::LaterSibling) | Some(Combinator::NextSibling) => {
DependencyInvalidationKind::Siblings
NormalDependencyInvalidationKind::Siblings
},
// TODO(emilio): We could look at the selector itself to see if it's
// an eager pseudo, and return only Descendants here if not.
Some(Combinator::PseudoElement) => DependencyInvalidationKind::ElementAndDescendants,
Some(Combinator::SlotAssignment) => DependencyInvalidationKind::SlottedElements,
Some(Combinator::Part) => DependencyInvalidationKind::Parts,
Some(Combinator::PseudoElement) => NormalDependencyInvalidationKind::ElementAndDescendants,
Some(Combinator::SlotAssignment) => NormalDependencyInvalidationKind::SlottedElements,
Some(Combinator::Part) => NormalDependencyInvalidationKind::Parts,
}
}
}
impl SelectorMapEntry for Dependency {
fn selector(&self) -> SelectorIter<SelectorImpl> {
self.selector.iter_from(self.selector_offset)
/// The kind of invalidation that this would generate.
pub fn invalidation_kind(&self) -> DependencyInvalidationKind {
if let Some(kind) = self.relative_kind {
return DependencyInvalidationKind::Relative(kind);
}
DependencyInvalidationKind::Normal(self.normal_invalidation_kind())
}
}
@ -242,51 +285,47 @@ impl InvalidationMap {
self.state_affecting_selectors.shrink_if_needed();
self.other_attribute_affecting_selectors.shrink_if_needed();
}
/// Adds a selector to this `InvalidationMap`. Returns Err(..) to
/// signify OOM.
pub fn note_selector(
&mut self,
selector: &Selector<SelectorImpl>,
quirks_mode: QuirksMode,
) -> Result<(), AllocErr> {
debug!("InvalidationMap::note_selector({:?})", selector);
let mut document_state = DocumentState::empty();
{
let mut parent_stack = SmallVec::new();
let mut alloc_error = None;
let mut collector = SelectorDependencyCollector {
map: self,
document_state: &mut document_state,
selector,
parent_selectors: &mut parent_stack,
quirks_mode,
compound_state: PerCompoundState::new(0),
alloc_error: &mut alloc_error,
};
let visit_result = collector.visit_whole_selector();
debug_assert_eq!(!visit_result, alloc_error.is_some());
if let Some(alloc_error) = alloc_error {
return Err(alloc_error);
}
}
if !document_state.is_empty() {
let dep = DocumentStateDependency {
state: document_state,
dependency: Dependency::for_full_selector_invalidation(selector.clone()),
};
self.document_state_selectors.try_reserve(1)?;
self.document_state_selectors.push(dep);
}
Ok(())
}
}
/// Adds a selector to the given `InvalidationMap`. Returns Err(..) to signify OOM.
pub fn note_selector_for_invalidation(
selector: &Selector<SelectorImpl>,
quirks_mode: QuirksMode,
map: &mut InvalidationMap,
) -> Result<(), AllocErr> {
debug!("note_selector_for_invalidation({:?})", selector);
let mut document_state = DocumentState::empty();
{
let mut parent_stack = SmallVec::new();
let mut alloc_error = None;
let mut collector = SelectorDependencyCollector {
map,
document_state: &mut document_state,
selector,
parent_selectors: &mut parent_stack,
quirks_mode,
compound_state: PerCompoundState::new(0),
alloc_error: &mut alloc_error,
};
let visit_result = collector.visit_whole_selector();
debug_assert_eq!(!visit_result, alloc_error.is_some());
if let Some(alloc_error) = alloc_error {
return Err(alloc_error);
}
}
if !document_state.is_empty() {
let dep = DocumentStateDependency {
state: document_state,
dependency: Dependency::for_full_selector_invalidation(selector.clone()),
};
map.document_state_selectors.try_reserve(1)?;
map.document_state_selectors.push(dep);
}
Ok(())
}
struct PerCompoundState {
/// The offset at which our compound starts.
offset: usize,
@ -410,7 +449,7 @@ impl<'a> SelectorDependencyCollector<'a> {
fn dependencies_from(entries: &mut [ParentDependencyEntry]) -> Option<Arc<Dependency>> {
if entries.is_empty() {
return None
return None;
}
let last_index = entries.len() - 1;
@ -418,13 +457,18 @@ impl<'a> SelectorDependencyCollector<'a> {
let last = &mut last[0];
let selector = &last.selector;
let selector_offset = last.offset;
Some(last.cached_dependency.get_or_insert_with(|| {
Arc::new(Dependency {
selector: selector.clone(),
selector_offset,
parent: dependencies_from(previous),
})
}).clone())
Some(
last.cached_dependency
.get_or_insert_with(|| {
Arc::new(Dependency {
selector: selector.clone(),
selector_offset,
parent: dependencies_from(previous),
relative_kind: None,
})
})
.clone(),
)
}
dependencies_from(&mut self.parent_selectors)
@ -436,6 +480,7 @@ impl<'a> SelectorDependencyCollector<'a> {
selector: self.selector.clone(),
selector_offset: self.compound_state.offset,
parent,
relative_kind: None,
}
}
}

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

@ -7,7 +7,9 @@
use crate::context::StackLimitChecker;
use crate::dom::{TElement, TNode, TShadowRoot};
use crate::invalidation::element::invalidation_map::{Dependency, DependencyInvalidationKind};
use crate::invalidation::element::invalidation_map::{
Dependency, NormalDependencyInvalidationKind, DependencyInvalidationKind, RelativeDependencyInvalidationKind
};
use selectors::matching::matches_compound_selector_from;
use selectors::matching::{CompoundSelectorMatchingResult, MatchingContext};
use selectors::parser::{Combinator, Component};
@ -16,8 +18,78 @@ use smallvec::SmallVec;
use std::fmt;
use std::fmt::Write;
struct SiblingInfo<E>
where
E: TElement,
{
affected: E,
prev_sibling: Option<E>,
next_sibling: Option<E>,
}
/// Traversal mapping for elements under consideration. It acts like a snapshot map,
/// though this only "maps" one element at most.
/// For general invalidations, this has no effect, especially since when
/// DOM mutates, the mutation's effect should not escape the subtree being mutated.
/// This is not the case for relative selectors, unfortunately, so we may end up
/// traversing a portion of the DOM tree that mutated. In case the mutation is removal,
/// its sibling relation is severed by the time the invalidation happens. This structure
/// recovers that relation. Note - it assumes that there is only one element under this
/// effect.
pub struct SiblingTraversalMap<E>
where
E: TElement,
{
info: Option<SiblingInfo<E>>,
}
impl<E> Default for SiblingTraversalMap<E>
where
E: TElement,
{
fn default() -> Self {
Self { info: None }
}
}
impl<E> SiblingTraversalMap<E>
where
E: TElement,
{
/// Create a new traversal map with the affected element.
pub fn new(affected: E, prev_sibling: Option<E>, next_sibling: Option<E>) -> Self {
Self {
info: Some(SiblingInfo {
affected,
prev_sibling,
next_sibling,
}),
}
}
/// Get the element's previous sibling element.
pub fn next_sibling_for(&self, element: &E) -> Option<E> {
if let Some(ref info) = self.info {
if *element == info.affected {
return info.next_sibling;
}
}
element.next_sibling_element()
}
/// Get the element's previous sibling element.
pub fn prev_sibling_for(&self, element: &E) -> Option<E> {
if let Some(ref info) = self.info {
if *element == info.affected {
return info.prev_sibling;
}
}
element.prev_sibling_element()
}
}
/// A trait to abstract the collection of invalidations for a given pass.
pub trait InvalidationProcessor<'a, E>
pub trait InvalidationProcessor<'a, 'b, E>
where
E: TElement,
{
@ -57,7 +129,10 @@ where
fn check_outer_dependency(&mut self, dependency: &Dependency, element: E) -> bool;
/// The matching context that should be used to process invalidations.
fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl>;
fn matching_context(&mut self) -> &mut MatchingContext<'b, E::Impl>;
/// The traversal map that should be used to process invalidations.
fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E>;
/// Collect invalidations for a given element's descendants and siblings.
///
@ -87,6 +162,18 @@ where
/// Executes an action when any descendant of `Self` is invalidated.
fn invalidated_descendants(&mut self, element: E, child: E);
/// Executes an action when an element in a relative selector is reached.
/// Lets the dependency to be borrowed for further processing out of the
/// invalidation traversal.
fn found_relative_selector_invalidation(
&mut self,
_element: E,
_kind: RelativeDependencyInvalidationKind,
_relative_dependency: &'a Dependency,
) {
debug_assert!(false, "Reached relative selector dependency");
}
}
/// Different invalidation lists for descendants.
@ -113,16 +200,16 @@ impl<'a> DescendantInvalidationLists<'a> {
/// The struct that takes care of encapsulating all the logic on where and how
/// element styles need to be invalidated.
pub struct TreeStyleInvalidator<'a, 'b, E, P: 'a>
pub struct TreeStyleInvalidator<'a, 'b, 'c, E, P: 'a>
where
'b: 'a,
E: TElement,
P: InvalidationProcessor<'b, E>,
P: InvalidationProcessor<'b, 'c, E>,
{
element: E,
stack_limit_checker: Option<&'a StackLimitChecker>,
processor: &'a mut P,
_marker: ::std::marker::PhantomData<&'b ()>,
_marker: std::marker::PhantomData<(&'b (), &'c ())>,
}
/// A vector of invalidations, optimized for small invalidation sets.
@ -185,7 +272,7 @@ impl<'a> Invalidation<'a> {
pub fn new(dependency: &'a Dependency, scope: Option<OpaqueElement>) -> Self {
debug_assert!(
dependency.selector_offset == dependency.selector.len() + 1 ||
dependency.invalidation_kind() != DependencyInvalidationKind::Element,
dependency.normal_invalidation_kind() != NormalDependencyInvalidationKind::Element,
"No point to this, if the dependency matched the element we should just invalidate it"
);
Self {
@ -307,11 +394,11 @@ impl InvalidationResult {
}
}
impl<'a, 'b, E, P: 'a> TreeStyleInvalidator<'a, 'b, E, P>
impl<'a, 'b, 'c, E, P: 'a> TreeStyleInvalidator<'a, 'b, 'c, E, P>
where
'b: 'a,
E: TElement,
P: InvalidationProcessor<'b, E>,
P: InvalidationProcessor<'b, 'c, E>,
{
/// Trivially constructs a new `TreeStyleInvalidator`.
pub fn new(
@ -323,7 +410,7 @@ where
element,
stack_limit_checker,
processor,
_marker: ::std::marker::PhantomData,
_marker: std::marker::PhantomData,
}
}
@ -388,7 +475,10 @@ where
return false;
}
let mut current = self.element.next_sibling_element();
let mut current = self
.processor
.sibling_traversal_map()
.next_sibling_for(&self.element);
let mut any_invalidated = false;
while let Some(sibling) = current {
@ -416,7 +506,10 @@ where
break;
}
current = sibling.next_sibling_element();
current = self
.processor
.sibling_traversal_map()
.next_sibling_for(&sibling);
}
any_invalidated
@ -838,7 +931,20 @@ where
matched: true,
}
},
Some(ref p) => &**p,
Some(ref p) => {
let invalidation_kind = p.invalidation_kind();
match invalidation_kind {
DependencyInvalidationKind::Normal(_) => &**p,
DependencyInvalidationKind::Relative(kind) => {
self.processor
.found_relative_selector_invalidation(self.element, kind, &**p);
return SingleInvalidationResult {
invalidated_self: false,
matched: true,
};
},
}
},
};
debug!(" > Checking outer dependency {:?}", cur_dependency);
@ -856,7 +962,7 @@ where
};
}
if cur_dependency.invalidation_kind() == DependencyInvalidationKind::Element {
if cur_dependency.normal_invalidation_kind() == NormalDependencyInvalidationKind::Element {
continue;
}

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

@ -10,7 +10,9 @@ use crate::data::ElementData;
use crate::dom::{TElement, TNode};
use crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
use crate::invalidation::element::invalidation_map::*;
use crate::invalidation::element::invalidator::{DescendantInvalidationLists, InvalidationVector};
use crate::invalidation::element::invalidator::{
DescendantInvalidationLists, InvalidationVector, SiblingTraversalMap,
};
use crate::invalidation::element::invalidator::{Invalidation, InvalidationProcessor};
use crate::invalidation::element::restyle_hints::RestyleHint;
use crate::selector_map::SelectorMap;
@ -52,6 +54,7 @@ pub struct StateAndAttrInvalidationProcessor<'a, 'b: 'a, E: TElement> {
element: E,
data: &'a mut ElementData,
matching_context: MatchingContext<'a, E::Impl>,
traversal_map: SiblingTraversalMap<E>,
}
impl<'a, 'b: 'a, E: TElement + 'b> StateAndAttrInvalidationProcessor<'a, 'b, E> {
@ -77,6 +80,7 @@ impl<'a, 'b: 'a, E: TElement + 'b> StateAndAttrInvalidationProcessor<'a, 'b, E>
element,
data,
matching_context,
traversal_map: SiblingTraversalMap::default(),
}
}
}
@ -194,7 +198,7 @@ where
}
}
impl<'a, 'b: 'a, E: 'a> InvalidationProcessor<'a, E>
impl<'a, 'b: 'a, E: 'a> InvalidationProcessor<'a, 'a, E>
for StateAndAttrInvalidationProcessor<'a, 'b, E>
where
E: TElement,
@ -218,6 +222,10 @@ where
&mut self.matching_context
}
fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> {
&self.traversal_map
}
fn collect_invalidations(
&mut self,
element: E,
@ -476,6 +484,9 @@ where
}
fn scan_dependency(&mut self, dependency: &'selectors Dependency) {
debug_assert!(
matches!(dependency.invalidation_kind(), DependencyInvalidationKind::Normal(_)),
"Found relative selector dependency");
debug!(
"TreeStyleInvalidator::scan_dependency({:?}, {:?})",
self.element, dependency
@ -493,8 +504,8 @@ where
fn note_dependency(&mut self, dependency: &'selectors Dependency) {
debug_assert!(self.dependency_may_be_relevant(dependency));
let invalidation_kind = dependency.invalidation_kind();
if matches!(invalidation_kind, DependencyInvalidationKind::Element) {
let invalidation_kind = dependency.normal_invalidation_kind();
if matches!(invalidation_kind, NormalDependencyInvalidationKind::Element) {
if let Some(ref parent) = dependency.parent {
// We know something changed in the inner selector, go outwards
// now.
@ -512,25 +523,25 @@ where
Invalidation::new(&dependency, self.matching_context.current_host.clone());
match invalidation_kind {
DependencyInvalidationKind::Element => unreachable!(),
DependencyInvalidationKind::ElementAndDescendants => {
NormalDependencyInvalidationKind::Element => unreachable!(),
NormalDependencyInvalidationKind::ElementAndDescendants => {
self.invalidates_self = true;
self.descendant_invalidations
.dom_descendants
.push(invalidation);
},
DependencyInvalidationKind::Descendants => {
NormalDependencyInvalidationKind::Descendants => {
self.descendant_invalidations
.dom_descendants
.push(invalidation);
},
DependencyInvalidationKind::Siblings => {
NormalDependencyInvalidationKind::Siblings => {
self.sibling_invalidations.push(invalidation);
},
DependencyInvalidationKind::Parts => {
NormalDependencyInvalidationKind::Parts => {
self.descendant_invalidations.parts.push(invalidation);
},
DependencyInvalidationKind::SlottedElements => {
NormalDependencyInvalidationKind::SlottedElements => {
self.descendant_invalidations
.slotted_descendants
.push(invalidation);
@ -541,13 +552,13 @@ where
/// Returns whether `dependency` may cause us to invalidate the style of
/// more elements than what we've already invalidated.
fn dependency_may_be_relevant(&self, dependency: &Dependency) -> bool {
match dependency.invalidation_kind() {
DependencyInvalidationKind::Element => !self.invalidates_self,
DependencyInvalidationKind::SlottedElements => self.element.is_html_slot_element(),
DependencyInvalidationKind::Parts => self.element.shadow_root().is_some(),
DependencyInvalidationKind::ElementAndDescendants |
DependencyInvalidationKind::Siblings |
DependencyInvalidationKind::Descendants => true,
match dependency.normal_invalidation_kind() {
NormalDependencyInvalidationKind::Element => !self.invalidates_self,
NormalDependencyInvalidationKind::SlottedElements => self.element.is_html_slot_element(),
NormalDependencyInvalidationKind::Parts => self.element.shadow_root().is_some(),
NormalDependencyInvalidationKind::ElementAndDescendants |
NormalDependencyInvalidationKind::Siblings |
NormalDependencyInvalidationKind::Descendants => true,
}
}
}

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

@ -11,7 +11,9 @@ use crate::context::{CascadeInputs, QuirksMode};
use crate::dom::{TElement, TShadowRoot};
#[cfg(feature = "gecko")]
use crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion};
use crate::invalidation::element::invalidation_map::InvalidationMap;
use crate::invalidation::element::invalidation_map::{
note_selector_for_invalidation, InvalidationMap,
};
use crate::invalidation::media_queries::{
EffectiveMediaQueryResults, MediaListKey, ToMediaListKey,
};
@ -2768,8 +2770,11 @@ impl CascadeData {
}
if rebuild_kind.should_rebuild_invalidation() {
self.invalidation_map
.note_selector(&rule.selector, quirks_mode)?;
note_selector_for_invalidation(
&rule.selector,
quirks_mode,
&mut self.invalidation_map,
)?;
let mut needs_revalidation = false;
let mut visitor = StylistSelectorVisitor {
needs_revalidation: &mut needs_revalidation,