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