style/invalidation/element/
invalidator.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! The struct that takes care of encapsulating all the logic on where and how
6//! element styles need to be invalidated.
7
8use crate::context::StackLimitChecker;
9use crate::dom::{TElement, TNode, TShadowRoot};
10use crate::invalidation::element::invalidation_map::{
11    Dependency, DependencyInvalidationKind, NormalDependencyInvalidationKind,
12    RelativeDependencyInvalidationKind, ScopeDependencyInvalidationKind,
13};
14use selectors::matching::matches_compound_selector_from;
15use selectors::matching::{CompoundSelectorMatchingResult, MatchingContext};
16use selectors::parser::{Combinator, Component, Selector, SelectorVisitor};
17use selectors::{OpaqueElement, SelectorImpl};
18use smallvec::{smallvec, SmallVec};
19use std::fmt;
20use std::fmt::Write;
21
22struct SiblingInfo<E>
23where
24    E: TElement,
25{
26    affected: E,
27    prev_sibling: Option<E>,
28    next_sibling: Option<E>,
29}
30
31/// Traversal mapping for elements under consideration. It acts like a snapshot map,
32/// though this only "maps" one element at most.
33/// For general invalidations, this has no effect, especially since when
34/// DOM mutates, the mutation's effect should not escape the subtree being mutated.
35/// This is not the case for relative selectors, unfortunately, so we may end up
36/// traversing a portion of the DOM tree that mutated. In case the mutation is removal,
37/// its sibling relation is severed by the time the invalidation happens. This structure
38/// recovers that relation. Note - it assumes that there is only one element under this
39/// effect.
40pub struct SiblingTraversalMap<E>
41where
42    E: TElement,
43{
44    info: Option<SiblingInfo<E>>,
45}
46
47impl<E> Default for SiblingTraversalMap<E>
48where
49    E: TElement,
50{
51    fn default() -> Self {
52        Self { info: None }
53    }
54}
55
56impl<E> SiblingTraversalMap<E>
57where
58    E: TElement,
59{
60    /// Create a new traversal map with the affected element.
61    pub fn new(affected: E, prev_sibling: Option<E>, next_sibling: Option<E>) -> Self {
62        Self {
63            info: Some(SiblingInfo {
64                affected,
65                prev_sibling,
66                next_sibling,
67            }),
68        }
69    }
70
71    /// Get the element's previous sibling element.
72    pub fn next_sibling_for(&self, element: &E) -> Option<E> {
73        if let Some(ref info) = self.info {
74            if *element == info.affected {
75                return info.next_sibling;
76            }
77        }
78        element.next_sibling_element()
79    }
80
81    /// Get the element's previous sibling element.
82    pub fn prev_sibling_for(&self, element: &E) -> Option<E> {
83        if let Some(ref info) = self.info {
84            if *element == info.affected {
85                return info.prev_sibling;
86            }
87        }
88        element.prev_sibling_element()
89    }
90}
91
92/// A trait to abstract the collection of invalidations for a given pass.
93pub trait InvalidationProcessor<'a, 'b, E>
94where
95    E: TElement,
96{
97    /// Whether an invalidation that contains only a pseudo-element selector
98    /// like ::before or ::after triggers invalidation of the element that would
99    /// originate it.
100    fn invalidates_on_pseudo_element(&self) -> bool {
101        false
102    }
103
104    /// Whether the invalidation processor only cares about light-tree
105    /// descendants of a given element, that is, doesn't invalidate
106    /// pseudo-elements, NAC, shadow dom...
107    fn light_tree_only(&self) -> bool {
108        false
109    }
110
111    /// When a dependency from a :where or :is selector matches, it may still be
112    /// the case that we don't need to invalidate the full style. Consider the
113    /// case of:
114    ///
115    ///   div .foo:where(.bar *, .baz) .qux
116    ///
117    /// We can get to the `*` part after a .bar class change, but you only need
118    /// to restyle the element if it also matches .foo.
119    ///
120    /// Similarly, you only need to restyle .baz if the whole result of matching
121    /// the selector changes.
122    ///
123    /// This function is called to check the result of matching the "outer"
124    /// dependency that we generate for the parent of the `:where` selector,
125    /// that is, in the case above it should match
126    /// `div .foo:where(.bar *, .baz)`.
127    ///
128    /// `scope` is set to `Some()` if this dependency follows a scope invalidation
129    /// Matching context should be adjusted accordingly with `nest_for_scope`.
130    ///
131    /// Returning true unconditionally here is over-optimistic and may
132    /// over-invalidate.
133    fn check_outer_dependency(
134        &mut self,
135        dependency: &Dependency,
136        element: E,
137        scope: Option<OpaqueElement>,
138    ) -> bool;
139
140    /// The matching context that should be used to process invalidations.
141    fn matching_context(&mut self) -> &mut MatchingContext<'b, E::Impl>;
142
143    /// The traversal map that should be used to process invalidations.
144    fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E>;
145
146    /// Collect invalidations for a given element's descendants and siblings.
147    ///
148    /// Returns whether the element itself was invalidated.
149    fn collect_invalidations(
150        &mut self,
151        element: E,
152        self_invalidations: &mut InvalidationVector<'a>,
153        descendant_invalidations: &mut DescendantInvalidationLists<'a>,
154        sibling_invalidations: &mut InvalidationVector<'a>,
155    ) -> bool;
156
157    /// Returns whether the invalidation process should process the descendants
158    /// of the given element.
159    fn should_process_descendants(&mut self, element: E) -> bool;
160
161    /// Executes an arbitrary action when the recursion limit is exceded (if
162    /// any).
163    fn recursion_limit_exceeded(&mut self, element: E);
164
165    /// Executes an action when `Self` is invalidated.
166    fn invalidated_self(&mut self, element: E);
167
168    /// Executes an action when `sibling` is invalidated as a sibling of
169    /// `of`.
170    fn invalidated_sibling(&mut self, sibling: E, of: E);
171
172    /// Executes an action when any descendant of `Self` is invalidated.
173    fn invalidated_descendants(&mut self, element: E, child: E);
174
175    /// Executes an action when an element in a relative selector is reached.
176    /// Lets the dependency to be borrowed for further processing out of the
177    /// invalidation traversal.
178    fn found_relative_selector_invalidation(
179        &mut self,
180        _element: E,
181        _kind: RelativeDependencyInvalidationKind,
182        _relative_dependency: &'a Dependency,
183    ) {
184        debug_assert!(false, "Reached relative selector dependency");
185    }
186}
187
188/// Different invalidation lists for descendants.
189#[derive(Debug, Default)]
190pub struct DescendantInvalidationLists<'a> {
191    /// Invalidations for normal DOM children and pseudo-elements.
192    ///
193    /// TODO(emilio): Having a list of invalidations just for pseudo-elements
194    /// may save some work here and there.
195    pub dom_descendants: InvalidationVector<'a>,
196    /// Invalidations for slotted children of an element.
197    pub slotted_descendants: InvalidationVector<'a>,
198    /// Invalidations for ::part()s of an element.
199    pub parts: InvalidationVector<'a>,
200}
201
202impl<'a> DescendantInvalidationLists<'a> {
203    fn is_empty(&self) -> bool {
204        self.dom_descendants.is_empty()
205            && self.slotted_descendants.is_empty()
206            && self.parts.is_empty()
207    }
208}
209
210/// The struct that takes care of encapsulating all the logic on where and how
211/// element styles need to be invalidated.
212pub struct TreeStyleInvalidator<'a, 'b, 'c, E, P: 'a>
213where
214    'b: 'a,
215    E: TElement,
216    P: InvalidationProcessor<'b, 'c, E>,
217{
218    element: E,
219    stack_limit_checker: Option<&'a StackLimitChecker>,
220    processor: &'a mut P,
221    _marker: std::marker::PhantomData<(&'b (), &'c ())>,
222}
223
224/// A vector of invalidations, optimized for small invalidation sets.
225pub type InvalidationVector<'a> = SmallVec<[Invalidation<'a>; 10]>;
226
227/// The kind of descendant invalidation we're processing.
228#[derive(Clone, Copy, Debug, Eq, PartialEq)]
229enum DescendantInvalidationKind {
230    /// A DOM descendant invalidation.
231    Dom,
232    /// A ::slotted() descendant invalidation.
233    Slotted,
234    /// A ::part() descendant invalidation.
235    Part,
236}
237
238/// The kind of invalidation we're processing.
239///
240/// We can use this to avoid pushing invalidations of the same kind to our
241/// descendants or siblings.
242#[derive(Clone, Copy, Debug, Eq, PartialEq)]
243enum InvalidationKind {
244    Descendant(DescendantInvalidationKind),
245    Sibling,
246}
247
248/// The kind of traversal an invalidation requires.
249pub enum InvalidationAddOverride {
250    /// This invalidation should be added to descendant invalidation
251    Descendant,
252    /// This invalidation should be added to sibling invalidations
253    Sibling,
254}
255
256/// An `Invalidation` is a complex selector that describes which elements,
257/// relative to a current element we are processing, must be restyled.
258#[derive(Clone)]
259pub struct Invalidation<'a> {
260    /// The dependency that generated this invalidation.
261    ///
262    /// Note that the offset inside the dependency is not really useful after
263    /// construction.
264    dependency: &'a Dependency,
265    /// The right shadow host from where the rule came from, if any.
266    ///
267    /// This is needed to ensure that we match the selector with the right
268    /// state, as whether some selectors like :host and ::part() match depends
269    /// on it.
270    host: Option<OpaqueElement>,
271    /// The scope element from which this rule comes from, if any.
272    scope: Option<OpaqueElement>,
273    /// The offset of the selector pointing to a compound selector.
274    ///
275    /// This order is a "parse order" offset, that is, zero is the leftmost part
276    /// of the selector written as parsed / serialized.
277    ///
278    /// It is initialized from the offset from `dependency`.
279    offset: usize,
280    /// Whether the invalidation was already matched by any previous sibling or
281    /// ancestor.
282    ///
283    /// If this is the case, we can avoid pushing invalidations generated by
284    /// this one if the generated invalidation is effective for all the siblings
285    /// or descendants after us.
286    matched_by_any_previous: bool,
287    /// Whether this incalidation should always be pushed to next invalidations.
288    ///
289    /// This is useful for overriding invalidations we would otherwise skip.
290    ///  e.g @scope(.a){:not(:scope)} where we would need the :not(:scope)
291    /// invalidation to traverse down for all children of the scope root
292    always_effective_for_next_descendant: bool,
293}
294
295impl<'a> Invalidation<'a> {
296    /// Create a new invalidation for matching a dependency.
297    pub fn new(
298        dependency: &'a Dependency,
299        host: Option<OpaqueElement>,
300        scope: Option<OpaqueElement>,
301    ) -> Self {
302        debug_assert!(
303            dependency.selector_offset == dependency.selector.len() + 1
304                || dependency.invalidation_kind()
305                    != DependencyInvalidationKind::Normal(
306                        NormalDependencyInvalidationKind::Element
307                    ),
308            "No point to this, if the dependency matched the element we should just invalidate it"
309        );
310        Self {
311            dependency,
312            host,
313            scope,
314            // + 1 to go past the combinator.
315            offset: dependency.selector.len() + 1 - dependency.selector_offset,
316            matched_by_any_previous: false,
317            always_effective_for_next_descendant: false,
318        }
319    }
320
321    /// Create a new invalidation for matching a dependency from the selector's subject.
322    /// Using this should be avoided whenever possible as it overinvalidates.
323    /// Only use it when it's not possible to match the selector in order due to
324    /// invalidations that don't necessarily start at the pointed compound, such as
325    /// what happens in note_scope_dependency_force_at_subject.
326    pub fn new_subject_invalidation(
327        dependency: &'a Dependency,
328        host: Option<OpaqueElement>,
329        scope: Option<OpaqueElement>,
330    ) -> Self {
331        let mut compound_offset = 0;
332        for s in dependency.selector.iter_raw_match_order() {
333            if s.is_combinator() {
334                break;
335            }
336            compound_offset += 1;
337        }
338
339        Self {
340            dependency,
341            host,
342            scope,
343            offset: dependency.selector.len() - compound_offset,
344            matched_by_any_previous: false,
345            always_effective_for_next_descendant: true,
346        }
347    }
348
349    /// Create a new invalidation for matching a dependency that should always check
350    /// its next descendants. It tends to overinvalidate less than new_subject_invalidation
351    /// but it should also be avoided whenever possible. Specifically used when crossing
352    /// into implicit scope invalidation.
353    pub fn new_always_effective_for_next_descendant(
354        dependency: &'a Dependency,
355        host: Option<OpaqueElement>,
356        scope: Option<OpaqueElement>,
357    ) -> Self {
358        if dependency.selector.is_rightmost(dependency.selector_offset) {
359            return Self::new_subject_invalidation(dependency, host, scope);
360        }
361
362        Self {
363            dependency,
364            host,
365            scope,
366            // + 1 to go past the combinator.
367            offset: dependency.selector.len() + 1 - dependency.selector_offset,
368            matched_by_any_previous: false,
369            always_effective_for_next_descendant: true,
370        }
371    }
372
373    /// Return the combinator to the right of the currently invalidating compound
374    /// Useful for determining whether this invalidation should be pushed to
375    /// sibling or descendant invalidations.
376    pub fn combinator_to_right(&self) -> Combinator {
377        debug_assert_ne!(self.dependency.selector_offset, 0);
378        self.dependency
379            .selector
380            .combinator_at_match_order(self.dependency.selector.len() - self.offset)
381    }
382
383    /// Whether this invalidation is effective for the next sibling or
384    /// descendant after us.
385    fn effective_for_next(&self) -> bool {
386        if self.offset == 0 || self.always_effective_for_next_descendant {
387            return true;
388        }
389
390        // TODO(emilio): For pseudo-elements this should be mostly false, except
391        // for the weird pseudos in <input type="number">.
392        //
393        // We should be able to do better here!
394        match self
395            .dependency
396            .selector
397            .combinator_at_parse_order(self.offset - 1)
398        {
399            Combinator::Descendant | Combinator::LaterSibling | Combinator::PseudoElement => true,
400            Combinator::Part
401            | Combinator::SlotAssignment
402            | Combinator::NextSibling
403            | Combinator::Child => false,
404        }
405    }
406
407    fn kind(&self) -> InvalidationKind {
408        if self.offset == 0 {
409            return InvalidationKind::Descendant(DescendantInvalidationKind::Dom);
410        }
411
412        match self
413            .dependency
414            .selector
415            .combinator_at_parse_order(self.offset - 1)
416        {
417            Combinator::Child | Combinator::Descendant | Combinator::PseudoElement => {
418                InvalidationKind::Descendant(DescendantInvalidationKind::Dom)
419            },
420            Combinator::Part => InvalidationKind::Descendant(DescendantInvalidationKind::Part),
421            Combinator::SlotAssignment => {
422                InvalidationKind::Descendant(DescendantInvalidationKind::Slotted)
423            },
424            Combinator::NextSibling | Combinator::LaterSibling => InvalidationKind::Sibling,
425        }
426    }
427}
428
429/// A struct that visits a selector and determines if there is a `:scope`
430/// component nested withing a negation. eg. :not(:scope)
431struct NegationScopeVisitor {
432    /// Have we found a negation list yet
433    in_negation: bool,
434    /// Have we found a :scope inside a negation yet
435    found_scope_in_negation: bool,
436}
437
438impl NegationScopeVisitor {
439    /// Create a new NegationScopeVisitor
440    fn new() -> Self {
441        Self {
442            in_negation: false,
443            found_scope_in_negation: false,
444        }
445    }
446
447    fn traverse_selector(
448        mut self,
449        selector: &Selector<<NegationScopeVisitor as SelectorVisitor>::Impl>,
450    ) -> bool {
451        selector.visit(&mut self);
452        self.found_scope_in_negation
453    }
454
455    /// Traverse all the next dependencies in an outer dependency until we reach
456    /// 1. :not(* :scope *)
457    /// 2. a scope or relative dependency
458    /// 3. the end of the chain of dependencies
459    /// Return whether or not we encountered :not(* :scope *)
460    fn traverse_dependency(mut self, dependency: &Dependency) -> bool {
461        if dependency.next.is_none()
462            || !matches!(
463                dependency.invalidation_kind(),
464                DependencyInvalidationKind::Normal(..)
465            )
466        {
467            dependency.selector.visit(&mut self);
468            return self.found_scope_in_negation;
469        }
470
471        let nested_visitor = Self {
472            in_negation: self.in_negation,
473            found_scope_in_negation: false,
474        };
475        dependency.selector.visit(&mut self);
476        // Has to be normal dependency and next.is_some()
477        nested_visitor.traverse_dependency(&dependency.next.as_ref().unwrap().slice()[0])
478    }
479}
480
481impl SelectorVisitor for NegationScopeVisitor {
482    type Impl = crate::selector_parser::SelectorImpl;
483
484    fn visit_attribute_selector(
485        &mut self,
486        _namespace: &selectors::attr::NamespaceConstraint<
487            &<Self::Impl as SelectorImpl>::NamespaceUrl,
488        >,
489        _local_name: &<Self::Impl as SelectorImpl>::LocalName,
490        _local_name_lower: &<Self::Impl as SelectorImpl>::LocalName,
491    ) -> bool {
492        true
493    }
494
495    fn visit_simple_selector(&mut self, component: &Component<Self::Impl>) -> bool {
496        if self.in_negation {
497            match component {
498                Component::Scope => {
499                    self.found_scope_in_negation = true;
500                },
501                _ => {},
502            }
503        }
504        true
505    }
506
507    fn visit_relative_selector_list(
508        &mut self,
509        _list: &[selectors::parser::RelativeSelector<Self::Impl>],
510    ) -> bool {
511        true
512    }
513
514    fn visit_selector_list(
515        &mut self,
516        list_kind: selectors::visitor::SelectorListKind,
517        list: &[selectors::parser::Selector<Self::Impl>],
518    ) -> bool {
519        for nested in list {
520            let nested_visitor = Self {
521                in_negation: list_kind.in_negation(),
522                found_scope_in_negation: false,
523            };
524
525            self.found_scope_in_negation |= nested_visitor.traverse_selector(nested);
526        }
527        true
528    }
529
530    fn visit_complex_selector(&mut self, _combinator_to_right: Option<Combinator>) -> bool {
531        true
532    }
533}
534
535/// Determines if we can find a selector in the form of :not(:scope)
536/// anywhere down the chain of dependencies.
537pub fn any_next_has_scope_in_negation(dependency: &Dependency) -> bool {
538    let next = match dependency.next.as_ref() {
539        None => return false,
540        Some(l) => l,
541    };
542
543    next.slice().iter().any(|dep| {
544        let visitor = NegationScopeVisitor::new();
545        visitor.traverse_dependency(dep)
546    })
547}
548
549impl<'a> fmt::Debug for Invalidation<'a> {
550    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
551        use cssparser::ToCss;
552
553        f.write_str("Invalidation(")?;
554        for component in self
555            .dependency
556            .selector
557            .iter_raw_parse_order_from(self.offset)
558        {
559            if matches!(*component, Component::Combinator(..)) {
560                break;
561            }
562            component.to_css(f)?;
563        }
564        f.write_char(')')
565    }
566}
567
568/// The result of processing a single invalidation for a given element.
569struct ProcessInvalidationResult {
570    /// Whether the element itself was invalidated.
571    invalidated_self: bool,
572    /// Whether the invalidation matched, either invalidating the element or
573    /// generating another invalidation.
574    matched: bool,
575}
576
577/// The result of a whole invalidation process for a given element.
578pub struct InvalidationResult {
579    /// Whether the element itself was invalidated.
580    invalidated_self: bool,
581    /// Whether the element's descendants were invalidated.
582    invalidated_descendants: bool,
583    /// Whether the element's siblings were invalidated.
584    invalidated_siblings: bool,
585}
586
587impl InvalidationResult {
588    /// Create an emtpy result.
589    pub fn empty() -> Self {
590        Self {
591            invalidated_self: false,
592            invalidated_descendants: false,
593            invalidated_siblings: false,
594        }
595    }
596
597    /// Whether the invalidation has invalidate the element itself.
598    pub fn has_invalidated_self(&self) -> bool {
599        self.invalidated_self
600    }
601
602    /// Whether the invalidation has invalidate desendants.
603    pub fn has_invalidated_descendants(&self) -> bool {
604        self.invalidated_descendants
605    }
606
607    /// Whether the invalidation has invalidate siblings.
608    pub fn has_invalidated_siblings(&self) -> bool {
609        self.invalidated_siblings
610    }
611}
612
613impl<'a, 'b, 'c, E, P: 'a> TreeStyleInvalidator<'a, 'b, 'c, E, P>
614where
615    'b: 'a,
616    E: TElement,
617    P: InvalidationProcessor<'b, 'c, E>,
618{
619    /// Trivially constructs a new `TreeStyleInvalidator`.
620    pub fn new(
621        element: E,
622        stack_limit_checker: Option<&'a StackLimitChecker>,
623        processor: &'a mut P,
624    ) -> Self {
625        Self {
626            element,
627            stack_limit_checker,
628            processor,
629            _marker: std::marker::PhantomData,
630        }
631    }
632
633    /// Perform the invalidation pass.
634    pub fn invalidate(mut self) -> InvalidationResult {
635        debug!("StyleTreeInvalidator::invalidate({:?})", self.element);
636
637        let mut self_invalidations = InvalidationVector::new();
638        let mut descendant_invalidations = DescendantInvalidationLists::default();
639        let mut sibling_invalidations = InvalidationVector::new();
640
641        let mut invalidated_self = self.processor.collect_invalidations(
642            self.element,
643            &mut self_invalidations,
644            &mut descendant_invalidations,
645            &mut sibling_invalidations,
646        );
647
648        debug!("Collected invalidations (self: {}): ", invalidated_self);
649        debug!(
650            " > self: {}, {:?}",
651            self_invalidations.len(),
652            self_invalidations
653        );
654        debug!(" > descendants: {:?}", descendant_invalidations);
655        debug!(
656            " > siblings: {}, {:?}",
657            sibling_invalidations.len(),
658            sibling_invalidations
659        );
660
661        let invalidated_self_from_collection = invalidated_self;
662
663        invalidated_self |= self.process_descendant_invalidations(
664            &self_invalidations,
665            &mut descendant_invalidations,
666            &mut sibling_invalidations,
667            DescendantInvalidationKind::Dom,
668        );
669
670        if invalidated_self && !invalidated_self_from_collection {
671            self.processor.invalidated_self(self.element);
672        }
673
674        let invalidated_descendants = self.invalidate_descendants(&descendant_invalidations);
675        let invalidated_siblings = self.invalidate_siblings(&mut sibling_invalidations);
676
677        InvalidationResult {
678            invalidated_self,
679            invalidated_descendants,
680            invalidated_siblings,
681        }
682    }
683
684    /// Go through later DOM siblings, invalidating style as needed using the
685    /// `sibling_invalidations` list.
686    ///
687    /// Returns whether any sibling's style or any sibling descendant's style
688    /// was invalidated.
689    fn invalidate_siblings(&mut self, sibling_invalidations: &mut InvalidationVector<'b>) -> bool {
690        if sibling_invalidations.is_empty() {
691            return false;
692        }
693
694        let mut current = self
695            .processor
696            .sibling_traversal_map()
697            .next_sibling_for(&self.element);
698        let mut any_invalidated = false;
699
700        while let Some(sibling) = current {
701            let mut sibling_invalidator =
702                TreeStyleInvalidator::new(sibling, self.stack_limit_checker, self.processor);
703
704            let mut invalidations_for_descendants = DescendantInvalidationLists::default();
705            let invalidated_sibling = sibling_invalidator.process_sibling_invalidations(
706                &mut invalidations_for_descendants,
707                sibling_invalidations,
708            );
709
710            if invalidated_sibling {
711                sibling_invalidator
712                    .processor
713                    .invalidated_sibling(sibling, self.element);
714            }
715
716            any_invalidated |= invalidated_sibling;
717
718            any_invalidated |=
719                sibling_invalidator.invalidate_descendants(&invalidations_for_descendants);
720
721            if sibling_invalidations.is_empty() {
722                break;
723            }
724
725            current = self
726                .processor
727                .sibling_traversal_map()
728                .next_sibling_for(&sibling);
729        }
730
731        any_invalidated
732    }
733
734    fn invalidate_pseudo_element_or_nac(
735        &mut self,
736        child: E,
737        invalidations: &[Invalidation<'b>],
738    ) -> bool {
739        let mut sibling_invalidations = InvalidationVector::new();
740
741        let result = self.invalidate_child(
742            child,
743            invalidations,
744            &mut sibling_invalidations,
745            DescendantInvalidationKind::Dom,
746        );
747
748        // Roots of NAC subtrees can indeed generate sibling invalidations, but
749        // they can be just ignored, since they have no siblings.
750        //
751        // Note that we can end up testing selectors that wouldn't end up
752        // matching due to this being NAC, like those coming from document
753        // rules, but we overinvalidate instead of checking this.
754
755        result
756    }
757
758    /// Invalidate a child and recurse down invalidating its descendants if
759    /// needed.
760    fn invalidate_child(
761        &mut self,
762        child: E,
763        invalidations: &[Invalidation<'b>],
764        sibling_invalidations: &mut InvalidationVector<'b>,
765        descendant_invalidation_kind: DescendantInvalidationKind,
766    ) -> bool {
767        let mut invalidations_for_descendants = DescendantInvalidationLists::default();
768
769        let mut invalidated_child = false;
770        let invalidated_descendants = {
771            let mut child_invalidator =
772                TreeStyleInvalidator::new(child, self.stack_limit_checker, self.processor);
773
774            invalidated_child |= child_invalidator.process_sibling_invalidations(
775                &mut invalidations_for_descendants,
776                sibling_invalidations,
777            );
778
779            invalidated_child |= child_invalidator.process_descendant_invalidations(
780                invalidations,
781                &mut invalidations_for_descendants,
782                sibling_invalidations,
783                descendant_invalidation_kind,
784            );
785
786            if invalidated_child {
787                child_invalidator.processor.invalidated_self(child);
788            }
789
790            child_invalidator.invalidate_descendants(&invalidations_for_descendants)
791        };
792
793        // The child may not be a flattened tree child of the current element,
794        // but may be arbitrarily deep.
795        //
796        // Since we keep the traversal flags in terms of the flattened tree,
797        // we need to propagate it as appropriate.
798        if invalidated_child || invalidated_descendants {
799            self.processor.invalidated_descendants(self.element, child);
800        }
801
802        invalidated_child || invalidated_descendants
803    }
804
805    fn invalidate_nac(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
806        let mut any_nac_root = false;
807
808        let element = self.element;
809        element.each_anonymous_content_child(|nac| {
810            any_nac_root |= self.invalidate_pseudo_element_or_nac(nac, invalidations);
811        });
812
813        any_nac_root
814    }
815
816    // NB: It's important that this operates on DOM children, which is what
817    // selector-matching operates on.
818    fn invalidate_dom_descendants_of(
819        &mut self,
820        parent: E::ConcreteNode,
821        invalidations: &[Invalidation<'b>],
822    ) -> bool {
823        let mut any_descendant = false;
824
825        let mut sibling_invalidations = InvalidationVector::new();
826        for child in parent.dom_children() {
827            let child = match child.as_element() {
828                Some(e) => e,
829                None => continue,
830            };
831
832            any_descendant |= self.invalidate_child(
833                child,
834                invalidations,
835                &mut sibling_invalidations,
836                DescendantInvalidationKind::Dom,
837            );
838        }
839
840        any_descendant
841    }
842
843    fn invalidate_parts_in_shadow_tree(
844        &mut self,
845        shadow: <E::ConcreteNode as TNode>::ConcreteShadowRoot,
846        invalidations: &[Invalidation<'b>],
847    ) -> bool {
848        debug_assert!(!invalidations.is_empty());
849
850        let mut any = false;
851        let mut sibling_invalidations = InvalidationVector::new();
852
853        for node in shadow.as_node().dom_descendants() {
854            let element = match node.as_element() {
855                Some(e) => e,
856                None => continue,
857            };
858
859            if element.has_part_attr() {
860                any |= self.invalidate_child(
861                    element,
862                    invalidations,
863                    &mut sibling_invalidations,
864                    DescendantInvalidationKind::Part,
865                );
866                debug_assert!(
867                    sibling_invalidations.is_empty(),
868                    "::part() shouldn't have sibling combinators to the right, \
869                     this makes no sense! {:?}",
870                    sibling_invalidations
871                );
872            }
873
874            if let Some(shadow) = element.shadow_root() {
875                if element.exports_any_part() {
876                    any |= self.invalidate_parts_in_shadow_tree(shadow, invalidations)
877                }
878            }
879        }
880
881        any
882    }
883
884    fn invalidate_parts(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
885        if invalidations.is_empty() {
886            return false;
887        }
888
889        let shadow = match self.element.shadow_root() {
890            Some(s) => s,
891            None => return false,
892        };
893
894        self.invalidate_parts_in_shadow_tree(shadow, invalidations)
895    }
896
897    fn invalidate_slotted_elements(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
898        if invalidations.is_empty() {
899            return false;
900        }
901
902        let slot = self.element;
903        self.invalidate_slotted_elements_in_slot(slot, invalidations)
904    }
905
906    fn invalidate_slotted_elements_in_slot(
907        &mut self,
908        slot: E,
909        invalidations: &[Invalidation<'b>],
910    ) -> bool {
911        let mut any = false;
912
913        let mut sibling_invalidations = InvalidationVector::new();
914        for node in slot.slotted_nodes() {
915            let element = match node.as_element() {
916                Some(e) => e,
917                None => continue,
918            };
919
920            if element.is_html_slot_element() {
921                any |= self.invalidate_slotted_elements_in_slot(element, invalidations);
922            } else {
923                any |= self.invalidate_child(
924                    element,
925                    invalidations,
926                    &mut sibling_invalidations,
927                    DescendantInvalidationKind::Slotted,
928                );
929            }
930
931            debug_assert!(
932                sibling_invalidations.is_empty(),
933                "::slotted() shouldn't have sibling combinators to the right, \
934                 this makes no sense! {:?}",
935                sibling_invalidations
936            );
937        }
938
939        any
940    }
941
942    fn invalidate_non_slotted_descendants(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
943        if invalidations.is_empty() {
944            return false;
945        }
946
947        if self.processor.light_tree_only() {
948            let node = self.element.as_node();
949            return self.invalidate_dom_descendants_of(node, invalidations);
950        }
951
952        let mut any_descendant = false;
953
954        // NOTE(emilio): This is only needed for Shadow DOM to invalidate
955        // correctly on :host(..) changes. Instead of doing this, we could add
956        // a third kind of invalidation list that walks shadow root children,
957        // but it's not clear it's worth it.
958        //
959        // Also, it's needed as of right now for document state invalidation,
960        // where we rely on iterating every element that ends up in the composed
961        // doc, but we could fix that invalidating per subtree.
962        if let Some(root) = self.element.shadow_root() {
963            any_descendant |= self.invalidate_dom_descendants_of(root.as_node(), invalidations);
964        }
965
966        any_descendant |= self.invalidate_dom_descendants_of(self.element.as_node(), invalidations);
967
968        any_descendant |= self.invalidate_nac(invalidations);
969
970        any_descendant
971    }
972
973    /// Given the descendant invalidation lists, go through the current
974    /// element's descendants, and invalidate style on them.
975    fn invalidate_descendants(&mut self, invalidations: &DescendantInvalidationLists<'b>) -> bool {
976        if invalidations.is_empty() {
977            return false;
978        }
979
980        debug!(
981            "StyleTreeInvalidator::invalidate_descendants({:?})",
982            self.element
983        );
984        debug!(" > {:?}", invalidations);
985
986        let should_process = self.processor.should_process_descendants(self.element);
987
988        if !should_process {
989            return false;
990        }
991
992        if let Some(checker) = self.stack_limit_checker {
993            if checker.limit_exceeded() {
994                self.processor.recursion_limit_exceeded(self.element);
995                return true;
996            }
997        }
998
999        let mut any_descendant = false;
1000
1001        any_descendant |= self.invalidate_non_slotted_descendants(&invalidations.dom_descendants);
1002        any_descendant |= self.invalidate_slotted_elements(&invalidations.slotted_descendants);
1003        any_descendant |= self.invalidate_parts(&invalidations.parts);
1004
1005        any_descendant
1006    }
1007
1008    /// Process the given sibling invalidations coming from our previous
1009    /// sibling.
1010    ///
1011    /// The sibling invalidations are somewhat special because they can be
1012    /// modified on the fly. New invalidations may be added and removed.
1013    ///
1014    /// In particular, all descendants get the same set of invalidations from
1015    /// the parent, but the invalidations from a given sibling depend on the
1016    /// ones we got from the previous one.
1017    ///
1018    /// Returns whether invalidated the current element's style.
1019    fn process_sibling_invalidations(
1020        &mut self,
1021        descendant_invalidations: &mut DescendantInvalidationLists<'b>,
1022        sibling_invalidations: &mut InvalidationVector<'b>,
1023    ) -> bool {
1024        let mut i = 0;
1025        let mut new_sibling_invalidations = InvalidationVector::new();
1026        let mut invalidated_self = false;
1027
1028        while i < sibling_invalidations.len() {
1029            let result = self.process_invalidation(
1030                &sibling_invalidations[i],
1031                descendant_invalidations,
1032                &mut new_sibling_invalidations,
1033                InvalidationKind::Sibling,
1034            );
1035
1036            invalidated_self |= result.invalidated_self;
1037            sibling_invalidations[i].matched_by_any_previous |= result.matched;
1038            if sibling_invalidations[i].effective_for_next() {
1039                i += 1;
1040            } else {
1041                sibling_invalidations.remove(i);
1042            }
1043        }
1044
1045        sibling_invalidations.extend(new_sibling_invalidations.drain(..));
1046        invalidated_self
1047    }
1048
1049    /// Process a given invalidation list coming from our parent,
1050    /// adding to `descendant_invalidations` and `sibling_invalidations` as
1051    /// needed.
1052    ///
1053    /// Returns whether our style was invalidated as a result.
1054    fn process_descendant_invalidations(
1055        &mut self,
1056        invalidations: &[Invalidation<'b>],
1057        descendant_invalidations: &mut DescendantInvalidationLists<'b>,
1058        sibling_invalidations: &mut InvalidationVector<'b>,
1059        descendant_invalidation_kind: DescendantInvalidationKind,
1060    ) -> bool {
1061        let mut invalidated = false;
1062
1063        for invalidation in invalidations {
1064            let result = self.process_invalidation(
1065                invalidation,
1066                descendant_invalidations,
1067                sibling_invalidations,
1068                InvalidationKind::Descendant(descendant_invalidation_kind),
1069            );
1070
1071            invalidated |= result.invalidated_self;
1072            if invalidation.effective_for_next() {
1073                let mut invalidation = invalidation.clone();
1074                invalidation.matched_by_any_previous |= result.matched;
1075                debug_assert_eq!(
1076                    descendant_invalidation_kind,
1077                    DescendantInvalidationKind::Dom,
1078                    "Slotted or part invalidations don't propagate."
1079                );
1080                descendant_invalidations.dom_descendants.push(invalidation);
1081            }
1082        }
1083
1084        invalidated
1085    }
1086
1087    #[inline(always)]
1088    fn handle_fully_matched(
1089        &mut self,
1090        invalidation: &Invalidation<'b>,
1091    ) -> (ProcessInvalidationResult, SmallVec<[Invalidation<'b>; 1]>) {
1092        debug!(" > Invalidation matched completely");
1093        // We matched completely. If we're an inner selector now we need
1094        // to go outside our selector and carry on invalidating.
1095        let mut to_process: SmallVec<[&Dependency; 1]> = SmallVec::from([invalidation.dependency]);
1096        let mut next_invalidations: SmallVec<[Invalidation; 1]> = SmallVec::new();
1097        let mut result = ProcessInvalidationResult {
1098            invalidated_self: false,
1099            matched: false,
1100        };
1101
1102        while !to_process.is_empty() {
1103            let mut next_dependencies: SmallVec<[&Dependency; 1]> = SmallVec::new();
1104
1105            while let Some(dependency) = to_process.pop() {
1106                if let DependencyInvalidationKind::Scope(scope_kind) =
1107                    dependency.invalidation_kind()
1108                {
1109                    if scope_kind == ScopeDependencyInvalidationKind::ImplicitScope {
1110                        if let Some(ref deps) = dependency.next {
1111                            for dep in deps.as_ref().slice() {
1112                                let invalidation =
1113                                    Invalidation::new_always_effective_for_next_descendant(
1114                                        dep,
1115                                        invalidation.host,
1116                                        invalidation.scope,
1117                                    );
1118                                next_invalidations.push(invalidation);
1119                            }
1120                        }
1121                        continue;
1122                    }
1123
1124                    let force_add = any_next_has_scope_in_negation(dependency);
1125                    if scope_kind == ScopeDependencyInvalidationKind::ScopeEnd || force_add {
1126                        let invalidations = note_scope_dependency_force_at_subject(
1127                            dependency,
1128                            invalidation.host,
1129                            invalidation.scope,
1130                            force_add,
1131                        );
1132
1133                        next_invalidations.extend(invalidations);
1134
1135                        continue;
1136                    }
1137                }
1138
1139                match dependency.next {
1140                    None => {
1141                        result.invalidated_self = true;
1142                        result.matched = true;
1143                    },
1144                    Some(ref deps) => {
1145                        for n in deps.as_ref().slice() {
1146                            let invalidation_kind = n.invalidation_kind();
1147                            match invalidation_kind {
1148                                DependencyInvalidationKind::FullSelector => unreachable!(),
1149                                DependencyInvalidationKind::Normal(_) => next_dependencies.push(n),
1150                                //TODO(descalente, bug 1934061): Add specific handling for implicit scopes.
1151                                DependencyInvalidationKind::Scope(_) => {
1152                                    next_dependencies.push(n);
1153                                },
1154                                DependencyInvalidationKind::Relative(kind) => {
1155                                    self.processor.found_relative_selector_invalidation(
1156                                        self.element,
1157                                        kind,
1158                                        n,
1159                                    );
1160                                    result.matched = true;
1161                                },
1162                            }
1163                        }
1164                    },
1165                };
1166            }
1167
1168            for cur_dependency in next_dependencies.as_ref() {
1169                let scope = matches!(
1170                    invalidation.dependency.invalidation_kind(),
1171                    DependencyInvalidationKind::Scope(_)
1172                )
1173                .then(|| self.element.opaque());
1174                debug!(" > Checking outer dependency {:?}", cur_dependency);
1175
1176                // The inner selector changed, now check if the full
1177                // previous part of the selector did, before keeping
1178                // checking for descendants.
1179                if !self
1180                    .processor
1181                    .check_outer_dependency(cur_dependency, self.element, scope)
1182                {
1183                    // Dependency is not relevant, do not note it down
1184                    continue;
1185                }
1186
1187                let invalidation_kind = cur_dependency.invalidation_kind();
1188                if matches!(
1189                    invalidation_kind,
1190                    DependencyInvalidationKind::Normal(NormalDependencyInvalidationKind::Element)
1191                ) || (matches!(invalidation_kind, DependencyInvalidationKind::Scope(_))
1192                    && cur_dependency.selector.is_rightmost(cur_dependency.selector_offset))
1193                {
1194                    // Add to dependency stack to process its next dependencies.
1195                    to_process.push(cur_dependency);
1196                    continue;
1197                }
1198
1199                debug!(" > Generating invalidation");
1200                next_invalidations.push(Invalidation::new(
1201                    cur_dependency,
1202                    invalidation.host,
1203                    scope,
1204                ));
1205            }
1206        }
1207        return (result, next_invalidations);
1208    }
1209
1210    /// Processes a given invalidation, potentially invalidating the style of
1211    /// the current element.
1212    ///
1213    /// Returns whether invalidated the style of the element, and whether the
1214    /// invalidation should be effective to subsequent siblings or descendants
1215    /// down in the tree.
1216    fn process_invalidation(
1217        &mut self,
1218        invalidation: &Invalidation<'b>,
1219        descendant_invalidations: &mut DescendantInvalidationLists<'b>,
1220        sibling_invalidations: &mut InvalidationVector<'b>,
1221        invalidation_kind: InvalidationKind,
1222    ) -> ProcessInvalidationResult {
1223        debug!(
1224            "TreeStyleInvalidator::process_invalidation({:?}, {:?}, {:?})",
1225            self.element, invalidation, invalidation_kind
1226        );
1227
1228        let matching_result = {
1229            let context = self.processor.matching_context();
1230            context.current_host = invalidation.host;
1231
1232            context.nest_for_scope_condition(invalidation.scope, |ctx| {
1233                matches_compound_selector_from(
1234                    &invalidation.dependency.selector,
1235                    invalidation.offset,
1236                    ctx,
1237                    &self.element,
1238                )
1239            })
1240        };
1241
1242        let (mut result, next_invalidations) = match matching_result {
1243            CompoundSelectorMatchingResult::NotMatched => {
1244                return ProcessInvalidationResult {
1245                    invalidated_self: false,
1246                    matched: false,
1247                }
1248            },
1249            CompoundSelectorMatchingResult::FullyMatched => self.handle_fully_matched(invalidation),
1250            CompoundSelectorMatchingResult::Matched {
1251                next_combinator_offset,
1252            } => (
1253                ProcessInvalidationResult {
1254                    invalidated_self: false,
1255                    matched: true,
1256                },
1257                smallvec![Invalidation {
1258                    dependency: invalidation.dependency,
1259                    host: invalidation.host,
1260                    scope: invalidation.scope,
1261                    offset: next_combinator_offset + 1,
1262                    matched_by_any_previous: false,
1263                    always_effective_for_next_descendant: invalidation
1264                        .always_effective_for_next_descendant,
1265                }],
1266            ),
1267        };
1268
1269        for next_invalidation in next_invalidations {
1270            let next_invalidation_kind = if next_invalidation.always_effective_for_next_descendant {
1271                InvalidationKind::Descendant(DescendantInvalidationKind::Dom)
1272            } else {
1273                debug_assert_ne!(
1274                    next_invalidation.offset, 0,
1275                    "Rightmost selectors shouldn't generate more invalidations",
1276                );
1277
1278                let next_combinator = next_invalidation
1279                    .dependency
1280                    .selector
1281                    .combinator_at_parse_order(next_invalidation.offset - 1);
1282
1283                if matches!(next_combinator, Combinator::PseudoElement)
1284                    && self.processor.invalidates_on_pseudo_element()
1285                {
1286                    // We need to invalidate the element whenever pseudos change, for
1287                    // two reasons:
1288                    //
1289                    //  * Eager pseudo styles are stored as part of the originating
1290                    //    element's computed style.
1291                    //
1292                    //  * Lazy pseudo-styles might be cached on the originating
1293                    //    element's pseudo-style cache.
1294                    //
1295                    // This could be more fine-grained (perhaps with a RESTYLE_PSEUDOS
1296                    // hint?).
1297                    //
1298                    // Note that we'll also restyle the pseudo-element because it would
1299                    // match this invalidation.
1300                    //
1301                    // FIXME: For non-element-backed pseudos this is still not quite
1302                    // correct. For example for ::selection even though we invalidate
1303                    // the style properly there's nothing that triggers a repaint
1304                    // necessarily. Though this matches old Gecko behavior, and the
1305                    // ::selection implementation needs to change significantly anyway
1306                    // to implement https://github.com/w3c/csswg-drafts/issues/2474 for
1307                    // example.
1308                    result.invalidated_self = true;
1309                }
1310
1311                debug!(
1312                    " > Invalidation matched, next: {:?}, ({:?})",
1313                    next_invalidation, next_combinator
1314                );
1315
1316                next_invalidation.kind()
1317            };
1318
1319            // We can skip pushing under some circumstances, and we should
1320            // because otherwise the invalidation list could grow
1321            // exponentially.
1322            //
1323            //  * First of all, both invalidations need to be of the same
1324            //    kind. This is because of how we propagate them going to
1325            //    the right of the tree for sibling invalidations and going
1326            //    down the tree for children invalidations. A sibling
1327            //    invalidation that ends up generating a children
1328            //    invalidation ends up (correctly) in five different lists,
1329            //    not in the same list five different times.
1330            //
1331            //  * Then, the invalidation needs to be matched by a previous
1332            //    ancestor/sibling, in order to know that this invalidation
1333            //    has been generated already.
1334            //
1335            //  * Finally, the new invalidation needs to be
1336            //    `effective_for_next()`, in order for us to know that it is
1337            //    still in the list, since we remove the dependencies that
1338            //    aren't from the lists for our children / siblings.
1339            //
1340            // To go through an example, let's imagine we are processing a
1341            // dom subtree like:
1342            //
1343            //   <div><address><div><div/></div></address></div>
1344            //
1345            // And an invalidation list with a single invalidation like:
1346            //
1347            //   [div div div]
1348            //
1349            // When we process the invalidation list for the outer div, we
1350            // match it, and generate a `div div` invalidation, so for the
1351            // <address> child we have:
1352            //
1353            //   [div div div, div div]
1354            //
1355            // With the first of them marked as `matched`.
1356            //
1357            // When we process the <address> child, we don't match any of
1358            // them, so both invalidations go untouched to our children.
1359            //
1360            // When we process the second <div>, we match _both_
1361            // invalidations.
1362            //
1363            // However, when matching the first, we can tell it's been
1364            // matched, and not push the corresponding `div div`
1365            // invalidation, since we know it's necessarily already on the
1366            // list.
1367            //
1368            // Thus, without skipping the push, we'll arrive to the
1369            // innermost <div> with:
1370            //
1371            //   [div div div, div div, div div, div]
1372            //
1373            // While skipping it, we won't arrive here with duplicating
1374            // dependencies:
1375            //
1376            //   [div div div, div div, div]
1377            //
1378            let can_skip_pushing = next_invalidation_kind == invalidation_kind
1379                && invalidation.matched_by_any_previous
1380                && next_invalidation.effective_for_next();
1381
1382            if can_skip_pushing {
1383                debug!(
1384                    " > Can avoid push, since the invalidation had \
1385                    already been matched before"
1386                );
1387            } else {
1388                match next_invalidation_kind {
1389                    InvalidationKind::Descendant(DescendantInvalidationKind::Dom) => {
1390                        descendant_invalidations
1391                            .dom_descendants
1392                            .push(next_invalidation);
1393                    },
1394                    InvalidationKind::Descendant(DescendantInvalidationKind::Part) => {
1395                        descendant_invalidations.parts.push(next_invalidation);
1396                    },
1397                    InvalidationKind::Descendant(DescendantInvalidationKind::Slotted) => {
1398                        descendant_invalidations
1399                            .slotted_descendants
1400                            .push(next_invalidation);
1401                    },
1402                    InvalidationKind::Sibling => {
1403                        sibling_invalidations.push(next_invalidation);
1404                    },
1405                }
1406            }
1407        }
1408
1409        result
1410    }
1411}
1412
1413/// Note the child dependencies of a scope end selector
1414/// This is necessary because the scope end selector is not bound to :scope
1415///
1416/// e.g @scope to (.b) {:scope .a .c {...}}
1417/// in the case of the following:
1418/// <div class=a><div id=x class=b><div class=c></div></div></div>
1419///
1420/// If we toggle class "b" in x, we would have to go up to find .a
1421/// if we wanted to invalidate correctly. However, this is costly.
1422/// Instead we just invalidate to the subject of the selector .c
1423pub fn note_scope_dependency_force_at_subject<'selectors>(
1424    dependency: &'selectors Dependency,
1425    current_host: Option<OpaqueElement>,
1426    scope: Option<OpaqueElement>,
1427    traversed_non_subject: bool,
1428) -> Vec<Invalidation<'selectors>> {
1429    let mut invalidations: Vec<Invalidation> = Vec::new();
1430    if let Some(next) = dependency.next.as_ref() {
1431        for dep in next.slice() {
1432            if dep.selector.is_rightmost(dep.selector_offset) && !traversed_non_subject {
1433                continue;
1434            }
1435
1436            // Follow the normal dependencies as far as we can, leaving
1437            // other kinds to their own invalidation mechanisms elsewhere
1438            if dep.next.is_some()
1439                && matches!(
1440                    dep.invalidation_kind(),
1441                    DependencyInvalidationKind::Normal(_)
1442                )
1443            {
1444                invalidations.extend(note_scope_dependency_force_at_subject(
1445                    dep,
1446                    current_host,
1447                    scope,
1448                    // Force add from now on because we
1449                    // passed through a non-subject compound
1450                    true,
1451                ));
1452            } else {
1453                let invalidation = Invalidation::new_subject_invalidation(dep, current_host, scope);
1454
1455                invalidations.push(invalidation);
1456            }
1457        }
1458    }
1459    invalidations
1460}