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};
17use selectors::OpaqueElement;
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}
288
289impl<'a> Invalidation<'a> {
290    /// Create a new invalidation for matching a dependency.
291    pub fn new(
292        dependency: &'a Dependency,
293        host: Option<OpaqueElement>,
294        scope: Option<OpaqueElement>,
295    ) -> Self {
296        debug_assert!(
297            dependency.selector_offset == dependency.selector.len() + 1
298                || dependency.invalidation_kind()
299                    != DependencyInvalidationKind::Normal(
300                        NormalDependencyInvalidationKind::Element
301                    ),
302            "No point to this, if the dependency matched the element we should just invalidate it"
303        );
304        Self {
305            dependency,
306            host,
307            scope,
308            // + 1 to go past the combinator.
309            offset: dependency.selector.len() + 1 - dependency.selector_offset,
310            matched_by_any_previous: false,
311        }
312    }
313
314    /// Create a new invalidation for matching a dependency from the selector's subject.
315    /// Using this should be avoided whenever possible as it overinvalidates.
316    /// Only use it when it's not possible to match the selector in order due to
317    /// invalidations that don't necessarily start at the pointed compound, such as
318    /// what happens in note_scope_dependency_force_at_subject.
319    pub fn new_subject_invalidation(
320        dependency: &'a Dependency,
321        host: Option<OpaqueElement>,
322        scope: Option<OpaqueElement>,
323    ) -> Self {
324        let mut compound_offset = 0;
325        for s in dependency.selector.iter_raw_match_order() {
326            if s.is_combinator() {
327                break;
328            }
329            compound_offset += 1;
330        }
331
332        Self {
333            dependency,
334            host,
335            scope,
336            offset: dependency.selector.len() - compound_offset,
337            matched_by_any_previous: false,
338        }
339    }
340
341    /// Whether this invalidation is effective for the next sibling or
342    /// descendant after us.
343    fn effective_for_next(&self) -> bool {
344        if self.offset == 0 {
345            return true;
346        }
347
348        // TODO(emilio): For pseudo-elements this should be mostly false, except
349        // for the weird pseudos in <input type="number">.
350        //
351        // We should be able to do better here!
352        match self
353            .dependency
354            .selector
355            .combinator_at_parse_order(self.offset - 1)
356        {
357            Combinator::Descendant | Combinator::LaterSibling | Combinator::PseudoElement => true,
358            Combinator::Part
359            | Combinator::SlotAssignment
360            | Combinator::NextSibling
361            | Combinator::Child => false,
362        }
363    }
364
365    fn kind(&self) -> InvalidationKind {
366        if self.offset == 0 {
367            return InvalidationKind::Descendant(DescendantInvalidationKind::Dom);
368        }
369
370        match self
371            .dependency
372            .selector
373            .combinator_at_parse_order(self.offset - 1)
374        {
375            Combinator::Child | Combinator::Descendant | Combinator::PseudoElement => {
376                InvalidationKind::Descendant(DescendantInvalidationKind::Dom)
377            },
378            Combinator::Part => InvalidationKind::Descendant(DescendantInvalidationKind::Part),
379            Combinator::SlotAssignment => {
380                InvalidationKind::Descendant(DescendantInvalidationKind::Slotted)
381            },
382            Combinator::NextSibling | Combinator::LaterSibling => InvalidationKind::Sibling,
383        }
384    }
385}
386
387impl<'a> fmt::Debug for Invalidation<'a> {
388    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
389        use cssparser::ToCss;
390
391        f.write_str("Invalidation(")?;
392        for component in self
393            .dependency
394            .selector
395            .iter_raw_parse_order_from(self.offset)
396        {
397            if matches!(*component, Component::Combinator(..)) {
398                break;
399            }
400            component.to_css(f)?;
401        }
402        f.write_char(')')
403    }
404}
405
406/// The result of processing a single invalidation for a given element.
407struct ProcessInvalidationResult {
408    /// Whether the element itself was invalidated.
409    invalidated_self: bool,
410    /// Whether the invalidation matched, either invalidating the element or
411    /// generating another invalidation.
412    matched: bool,
413}
414
415/// The result of a whole invalidation process for a given element.
416pub struct InvalidationResult {
417    /// Whether the element itself was invalidated.
418    invalidated_self: bool,
419    /// Whether the element's descendants were invalidated.
420    invalidated_descendants: bool,
421    /// Whether the element's siblings were invalidated.
422    invalidated_siblings: bool,
423}
424
425impl InvalidationResult {
426    /// Create an emtpy result.
427    pub fn empty() -> Self {
428        Self {
429            invalidated_self: false,
430            invalidated_descendants: false,
431            invalidated_siblings: false,
432        }
433    }
434
435    /// Whether the invalidation has invalidate the element itself.
436    pub fn has_invalidated_self(&self) -> bool {
437        self.invalidated_self
438    }
439
440    /// Whether the invalidation has invalidate desendants.
441    pub fn has_invalidated_descendants(&self) -> bool {
442        self.invalidated_descendants
443    }
444
445    /// Whether the invalidation has invalidate siblings.
446    pub fn has_invalidated_siblings(&self) -> bool {
447        self.invalidated_siblings
448    }
449}
450
451impl<'a, 'b, 'c, E, P: 'a> TreeStyleInvalidator<'a, 'b, 'c, E, P>
452where
453    'b: 'a,
454    E: TElement,
455    P: InvalidationProcessor<'b, 'c, E>,
456{
457    /// Trivially constructs a new `TreeStyleInvalidator`.
458    pub fn new(
459        element: E,
460        stack_limit_checker: Option<&'a StackLimitChecker>,
461        processor: &'a mut P,
462    ) -> Self {
463        Self {
464            element,
465            stack_limit_checker,
466            processor,
467            _marker: std::marker::PhantomData,
468        }
469    }
470
471    /// Perform the invalidation pass.
472    pub fn invalidate(mut self) -> InvalidationResult {
473        debug!("StyleTreeInvalidator::invalidate({:?})", self.element);
474
475        let mut self_invalidations = InvalidationVector::new();
476        let mut descendant_invalidations = DescendantInvalidationLists::default();
477        let mut sibling_invalidations = InvalidationVector::new();
478
479        let mut invalidated_self = self.processor.collect_invalidations(
480            self.element,
481            &mut self_invalidations,
482            &mut descendant_invalidations,
483            &mut sibling_invalidations,
484        );
485
486        debug!("Collected invalidations (self: {}): ", invalidated_self);
487        debug!(
488            " > self: {}, {:?}",
489            self_invalidations.len(),
490            self_invalidations
491        );
492        debug!(" > descendants: {:?}", descendant_invalidations);
493        debug!(
494            " > siblings: {}, {:?}",
495            sibling_invalidations.len(),
496            sibling_invalidations
497        );
498
499        let invalidated_self_from_collection = invalidated_self;
500
501        invalidated_self |= self.process_descendant_invalidations(
502            &self_invalidations,
503            &mut descendant_invalidations,
504            &mut sibling_invalidations,
505            DescendantInvalidationKind::Dom,
506        );
507
508        if invalidated_self && !invalidated_self_from_collection {
509            self.processor.invalidated_self(self.element);
510        }
511
512        let invalidated_descendants = self.invalidate_descendants(&descendant_invalidations);
513        let invalidated_siblings = self.invalidate_siblings(&mut sibling_invalidations);
514
515        InvalidationResult {
516            invalidated_self,
517            invalidated_descendants,
518            invalidated_siblings,
519        }
520    }
521
522    /// Go through later DOM siblings, invalidating style as needed using the
523    /// `sibling_invalidations` list.
524    ///
525    /// Returns whether any sibling's style or any sibling descendant's style
526    /// was invalidated.
527    fn invalidate_siblings(&mut self, sibling_invalidations: &mut InvalidationVector<'b>) -> bool {
528        if sibling_invalidations.is_empty() {
529            return false;
530        }
531
532        let mut current = self
533            .processor
534            .sibling_traversal_map()
535            .next_sibling_for(&self.element);
536        let mut any_invalidated = false;
537
538        while let Some(sibling) = current {
539            let mut sibling_invalidator =
540                TreeStyleInvalidator::new(sibling, self.stack_limit_checker, self.processor);
541
542            let mut invalidations_for_descendants = DescendantInvalidationLists::default();
543            let invalidated_sibling = sibling_invalidator.process_sibling_invalidations(
544                &mut invalidations_for_descendants,
545                sibling_invalidations,
546            );
547
548            if invalidated_sibling {
549                sibling_invalidator
550                    .processor
551                    .invalidated_sibling(sibling, self.element);
552            }
553
554            any_invalidated |= invalidated_sibling;
555
556            any_invalidated |=
557                sibling_invalidator.invalidate_descendants(&invalidations_for_descendants);
558
559            if sibling_invalidations.is_empty() {
560                break;
561            }
562
563            current = self
564                .processor
565                .sibling_traversal_map()
566                .next_sibling_for(&sibling);
567        }
568
569        any_invalidated
570    }
571
572    fn invalidate_pseudo_element_or_nac(
573        &mut self,
574        child: E,
575        invalidations: &[Invalidation<'b>],
576    ) -> bool {
577        let mut sibling_invalidations = InvalidationVector::new();
578
579        let result = self.invalidate_child(
580            child,
581            invalidations,
582            &mut sibling_invalidations,
583            DescendantInvalidationKind::Dom,
584        );
585
586        // Roots of NAC subtrees can indeed generate sibling invalidations, but
587        // they can be just ignored, since they have no siblings.
588        //
589        // Note that we can end up testing selectors that wouldn't end up
590        // matching due to this being NAC, like those coming from document
591        // rules, but we overinvalidate instead of checking this.
592
593        result
594    }
595
596    /// Invalidate a child and recurse down invalidating its descendants if
597    /// needed.
598    fn invalidate_child(
599        &mut self,
600        child: E,
601        invalidations: &[Invalidation<'b>],
602        sibling_invalidations: &mut InvalidationVector<'b>,
603        descendant_invalidation_kind: DescendantInvalidationKind,
604    ) -> bool {
605        let mut invalidations_for_descendants = DescendantInvalidationLists::default();
606
607        let mut invalidated_child = false;
608        let invalidated_descendants = {
609            let mut child_invalidator =
610                TreeStyleInvalidator::new(child, self.stack_limit_checker, self.processor);
611
612            invalidated_child |= child_invalidator.process_sibling_invalidations(
613                &mut invalidations_for_descendants,
614                sibling_invalidations,
615            );
616
617            invalidated_child |= child_invalidator.process_descendant_invalidations(
618                invalidations,
619                &mut invalidations_for_descendants,
620                sibling_invalidations,
621                descendant_invalidation_kind,
622            );
623
624            if invalidated_child {
625                child_invalidator.processor.invalidated_self(child);
626            }
627
628            child_invalidator.invalidate_descendants(&invalidations_for_descendants)
629        };
630
631        // The child may not be a flattened tree child of the current element,
632        // but may be arbitrarily deep.
633        //
634        // Since we keep the traversal flags in terms of the flattened tree,
635        // we need to propagate it as appropriate.
636        if invalidated_child || invalidated_descendants {
637            self.processor.invalidated_descendants(self.element, child);
638        }
639
640        invalidated_child || invalidated_descendants
641    }
642
643    fn invalidate_nac(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
644        let mut any_nac_root = false;
645
646        let element = self.element;
647        element.each_anonymous_content_child(|nac| {
648            any_nac_root |= self.invalidate_pseudo_element_or_nac(nac, invalidations);
649        });
650
651        any_nac_root
652    }
653
654    // NB: It's important that this operates on DOM children, which is what
655    // selector-matching operates on.
656    fn invalidate_dom_descendants_of(
657        &mut self,
658        parent: E::ConcreteNode,
659        invalidations: &[Invalidation<'b>],
660    ) -> bool {
661        let mut any_descendant = false;
662
663        let mut sibling_invalidations = InvalidationVector::new();
664        for child in parent.dom_children() {
665            let child = match child.as_element() {
666                Some(e) => e,
667                None => continue,
668            };
669
670            any_descendant |= self.invalidate_child(
671                child,
672                invalidations,
673                &mut sibling_invalidations,
674                DescendantInvalidationKind::Dom,
675            );
676        }
677
678        any_descendant
679    }
680
681    fn invalidate_parts_in_shadow_tree(
682        &mut self,
683        shadow: <E::ConcreteNode as TNode>::ConcreteShadowRoot,
684        invalidations: &[Invalidation<'b>],
685    ) -> bool {
686        debug_assert!(!invalidations.is_empty());
687
688        let mut any = false;
689        let mut sibling_invalidations = InvalidationVector::new();
690
691        for node in shadow.as_node().dom_descendants() {
692            let element = match node.as_element() {
693                Some(e) => e,
694                None => continue,
695            };
696
697            if element.has_part_attr() {
698                any |= self.invalidate_child(
699                    element,
700                    invalidations,
701                    &mut sibling_invalidations,
702                    DescendantInvalidationKind::Part,
703                );
704                debug_assert!(
705                    sibling_invalidations.is_empty(),
706                    "::part() shouldn't have sibling combinators to the right, \
707                     this makes no sense! {:?}",
708                    sibling_invalidations
709                );
710            }
711
712            if let Some(shadow) = element.shadow_root() {
713                if element.exports_any_part() {
714                    any |= self.invalidate_parts_in_shadow_tree(shadow, invalidations)
715                }
716            }
717        }
718
719        any
720    }
721
722    fn invalidate_parts(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
723        if invalidations.is_empty() {
724            return false;
725        }
726
727        let shadow = match self.element.shadow_root() {
728            Some(s) => s,
729            None => return false,
730        };
731
732        self.invalidate_parts_in_shadow_tree(shadow, invalidations)
733    }
734
735    fn invalidate_slotted_elements(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
736        if invalidations.is_empty() {
737            return false;
738        }
739
740        let slot = self.element;
741        self.invalidate_slotted_elements_in_slot(slot, invalidations)
742    }
743
744    fn invalidate_slotted_elements_in_slot(
745        &mut self,
746        slot: E,
747        invalidations: &[Invalidation<'b>],
748    ) -> bool {
749        let mut any = false;
750
751        let mut sibling_invalidations = InvalidationVector::new();
752        for node in slot.slotted_nodes() {
753            let element = match node.as_element() {
754                Some(e) => e,
755                None => continue,
756            };
757
758            if element.is_html_slot_element() {
759                any |= self.invalidate_slotted_elements_in_slot(element, invalidations);
760            } else {
761                any |= self.invalidate_child(
762                    element,
763                    invalidations,
764                    &mut sibling_invalidations,
765                    DescendantInvalidationKind::Slotted,
766                );
767            }
768
769            debug_assert!(
770                sibling_invalidations.is_empty(),
771                "::slotted() shouldn't have sibling combinators to the right, \
772                 this makes no sense! {:?}",
773                sibling_invalidations
774            );
775        }
776
777        any
778    }
779
780    fn invalidate_non_slotted_descendants(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
781        if invalidations.is_empty() {
782            return false;
783        }
784
785        if self.processor.light_tree_only() {
786            let node = self.element.as_node();
787            return self.invalidate_dom_descendants_of(node, invalidations);
788        }
789
790        let mut any_descendant = false;
791
792        // NOTE(emilio): This is only needed for Shadow DOM to invalidate
793        // correctly on :host(..) changes. Instead of doing this, we could add
794        // a third kind of invalidation list that walks shadow root children,
795        // but it's not clear it's worth it.
796        //
797        // Also, it's needed as of right now for document state invalidation,
798        // where we rely on iterating every element that ends up in the composed
799        // doc, but we could fix that invalidating per subtree.
800        if let Some(root) = self.element.shadow_root() {
801            any_descendant |= self.invalidate_dom_descendants_of(root.as_node(), invalidations);
802        }
803
804        any_descendant |= self.invalidate_dom_descendants_of(self.element.as_node(), invalidations);
805
806        any_descendant |= self.invalidate_nac(invalidations);
807
808        any_descendant
809    }
810
811    /// Given the descendant invalidation lists, go through the current
812    /// element's descendants, and invalidate style on them.
813    fn invalidate_descendants(&mut self, invalidations: &DescendantInvalidationLists<'b>) -> bool {
814        if invalidations.is_empty() {
815            return false;
816        }
817
818        debug!(
819            "StyleTreeInvalidator::invalidate_descendants({:?})",
820            self.element
821        );
822        debug!(" > {:?}", invalidations);
823
824        let should_process = self.processor.should_process_descendants(self.element);
825
826        if !should_process {
827            return false;
828        }
829
830        if let Some(checker) = self.stack_limit_checker {
831            if checker.limit_exceeded() {
832                self.processor.recursion_limit_exceeded(self.element);
833                return true;
834            }
835        }
836
837        let mut any_descendant = false;
838
839        any_descendant |= self.invalidate_non_slotted_descendants(&invalidations.dom_descendants);
840        any_descendant |= self.invalidate_slotted_elements(&invalidations.slotted_descendants);
841        any_descendant |= self.invalidate_parts(&invalidations.parts);
842
843        any_descendant
844    }
845
846    /// Process the given sibling invalidations coming from our previous
847    /// sibling.
848    ///
849    /// The sibling invalidations are somewhat special because they can be
850    /// modified on the fly. New invalidations may be added and removed.
851    ///
852    /// In particular, all descendants get the same set of invalidations from
853    /// the parent, but the invalidations from a given sibling depend on the
854    /// ones we got from the previous one.
855    ///
856    /// Returns whether invalidated the current element's style.
857    fn process_sibling_invalidations(
858        &mut self,
859        descendant_invalidations: &mut DescendantInvalidationLists<'b>,
860        sibling_invalidations: &mut InvalidationVector<'b>,
861    ) -> bool {
862        let mut i = 0;
863        let mut new_sibling_invalidations = InvalidationVector::new();
864        let mut invalidated_self = false;
865
866        while i < sibling_invalidations.len() {
867            let result = self.process_invalidation(
868                &sibling_invalidations[i],
869                descendant_invalidations,
870                &mut new_sibling_invalidations,
871                InvalidationKind::Sibling,
872            );
873
874            invalidated_self |= result.invalidated_self;
875            sibling_invalidations[i].matched_by_any_previous |= result.matched;
876            if sibling_invalidations[i].effective_for_next() {
877                i += 1;
878            } else {
879                sibling_invalidations.remove(i);
880            }
881        }
882
883        sibling_invalidations.extend(new_sibling_invalidations.drain(..));
884        invalidated_self
885    }
886
887    /// Process a given invalidation list coming from our parent,
888    /// adding to `descendant_invalidations` and `sibling_invalidations` as
889    /// needed.
890    ///
891    /// Returns whether our style was invalidated as a result.
892    fn process_descendant_invalidations(
893        &mut self,
894        invalidations: &[Invalidation<'b>],
895        descendant_invalidations: &mut DescendantInvalidationLists<'b>,
896        sibling_invalidations: &mut InvalidationVector<'b>,
897        descendant_invalidation_kind: DescendantInvalidationKind,
898    ) -> bool {
899        let mut invalidated = false;
900
901        for invalidation in invalidations {
902            let result = self.process_invalidation(
903                invalidation,
904                descendant_invalidations,
905                sibling_invalidations,
906                InvalidationKind::Descendant(descendant_invalidation_kind),
907            );
908
909            invalidated |= result.invalidated_self;
910            if invalidation.effective_for_next() {
911                let mut invalidation = invalidation.clone();
912                invalidation.matched_by_any_previous |= result.matched;
913                debug_assert_eq!(
914                    descendant_invalidation_kind,
915                    DescendantInvalidationKind::Dom,
916                    "Slotted or part invalidations don't propagate."
917                );
918                descendant_invalidations.dom_descendants.push(invalidation);
919            }
920        }
921
922        invalidated
923    }
924
925    #[inline(always)]
926    fn handle_fully_matched(
927        &mut self,
928        invalidation: &Invalidation<'b>,
929        descendant_invalidations: &mut DescendantInvalidationLists<'b>,
930        sibling_invalidations: &mut InvalidationVector<'b>,
931    ) -> (ProcessInvalidationResult, SmallVec<[Invalidation<'b>; 1]>) {
932        debug!(" > Invalidation matched completely");
933        // We matched completely. If we're an inner selector now we need
934        // to go outside our selector and carry on invalidating.
935        let mut to_process: SmallVec<[&Dependency; 1]> = SmallVec::from([invalidation.dependency]);
936        let mut next_invalidations: SmallVec<[Invalidation; 1]> = SmallVec::new();
937        let mut result = ProcessInvalidationResult {
938            invalidated_self: false,
939            matched: false,
940        };
941
942        while !to_process.is_empty() {
943            let mut next_dependencies: SmallVec<[&Dependency; 1]> = SmallVec::new();
944
945            while let Some(dependency) = to_process.pop() {
946                if dependency.invalidation_kind()
947                    == DependencyInvalidationKind::Scope(ScopeDependencyInvalidationKind::ScopeEnd)
948                {
949                    let invalidations =
950                        note_scope_dependency_force_at_subject(dependency, invalidation.host);
951                    for (invalidation, override_type) in invalidations {
952                        match override_type {
953                            InvalidationAddOverride::Descendant => {
954                                descendant_invalidations.dom_descendants.push(invalidation)
955                            },
956                            InvalidationAddOverride::Sibling => {
957                                sibling_invalidations.push(invalidation)
958                            },
959                        }
960                    }
961                    continue;
962                }
963                match dependency.next {
964                    None => {
965                        result.invalidated_self = true;
966                        result.matched = true;
967                    },
968                    Some(ref deps) => {
969                        for n in deps.as_ref().slice() {
970                            let invalidation_kind = n.invalidation_kind();
971                            match invalidation_kind {
972                                DependencyInvalidationKind::FullSelector => unreachable!(),
973                                DependencyInvalidationKind::Normal(_) => next_dependencies.push(n),
974                                //TODO(descalente, bug 1934061): Add specific handling for implicit scopes.
975                                DependencyInvalidationKind::Scope(_) => {
976                                    next_dependencies.push(n);
977                                },
978                                DependencyInvalidationKind::Relative(kind) => {
979                                    self.processor.found_relative_selector_invalidation(
980                                        self.element,
981                                        kind,
982                                        n,
983                                    );
984                                    result.matched = true;
985                                },
986                            }
987                        }
988                    },
989                };
990            }
991
992            for cur_dependency in next_dependencies.as_ref() {
993                let scope = matches!(
994                    invalidation.dependency.invalidation_kind(),
995                    DependencyInvalidationKind::Scope(_)
996                )
997                .then(|| self.element.opaque());
998                debug!(" > Checking outer dependency {:?}", cur_dependency);
999
1000                // The inner selector changed, now check if the full
1001                // previous part of the selector did, before keeping
1002                // checking for descendants.
1003                if !self
1004                    .processor
1005                    .check_outer_dependency(cur_dependency, self.element, scope)
1006                {
1007                    // Dependency is not relevant, do not note it down
1008                    continue;
1009                }
1010
1011                let invalidation_kind = cur_dependency.invalidation_kind();
1012                if matches!(
1013                    invalidation_kind,
1014                    DependencyInvalidationKind::Normal(NormalDependencyInvalidationKind::Element)
1015                ) || (matches!(invalidation_kind, DependencyInvalidationKind::Scope(_))
1016                    && cur_dependency.selector_offset == 0)
1017                {
1018                    // Add to dependency stack to process its next dependencies.
1019                    to_process.push(cur_dependency);
1020                    continue;
1021                }
1022
1023                debug!(" > Generating invalidation");
1024                next_invalidations.push(Invalidation::new(
1025                    cur_dependency,
1026                    invalidation.host,
1027                    scope,
1028                ));
1029            }
1030        }
1031        return (result, next_invalidations);
1032    }
1033
1034    /// Processes a given invalidation, potentially invalidating the style of
1035    /// the current element.
1036    ///
1037    /// Returns whether invalidated the style of the element, and whether the
1038    /// invalidation should be effective to subsequent siblings or descendants
1039    /// down in the tree.
1040    fn process_invalidation(
1041        &mut self,
1042        invalidation: &Invalidation<'b>,
1043        descendant_invalidations: &mut DescendantInvalidationLists<'b>,
1044        sibling_invalidations: &mut InvalidationVector<'b>,
1045        invalidation_kind: InvalidationKind,
1046    ) -> ProcessInvalidationResult {
1047        debug!(
1048            "TreeStyleInvalidator::process_invalidation({:?}, {:?}, {:?})",
1049            self.element, invalidation, invalidation_kind
1050        );
1051
1052        let matching_result = {
1053            let context = self.processor.matching_context();
1054            context.current_host = invalidation.host;
1055
1056            context.nest_for_scope_condition(invalidation.scope, |ctx| {
1057                matches_compound_selector_from(
1058                    &invalidation.dependency.selector,
1059                    invalidation.offset,
1060                    ctx,
1061                    &self.element,
1062                )
1063            })
1064        };
1065
1066        let (result, next_invalidations) = match matching_result {
1067            CompoundSelectorMatchingResult::NotMatched => {
1068                return ProcessInvalidationResult {
1069                    invalidated_self: false,
1070                    matched: false,
1071                }
1072            },
1073            CompoundSelectorMatchingResult::FullyMatched => self.handle_fully_matched(
1074                invalidation,
1075                descendant_invalidations,
1076                sibling_invalidations,
1077            ),
1078            CompoundSelectorMatchingResult::Matched {
1079                next_combinator_offset,
1080            } => (
1081                ProcessInvalidationResult {
1082                    invalidated_self: false,
1083                    matched: true,
1084                },
1085                smallvec![Invalidation {
1086                    dependency: invalidation.dependency,
1087                    host: invalidation.host,
1088                    scope: invalidation.scope,
1089                    offset: next_combinator_offset + 1,
1090                    matched_by_any_previous: false,
1091                }],
1092            ),
1093        };
1094
1095        let mut invalidated_self = result.invalidated_self;
1096        for next_invalidation in next_invalidations {
1097            debug_assert_ne!(
1098                next_invalidation.offset, 0,
1099                "Rightmost selectors shouldn't generate more invalidations",
1100            );
1101
1102            let next_combinator = next_invalidation
1103                .dependency
1104                .selector
1105                .combinator_at_parse_order(next_invalidation.offset - 1);
1106
1107            if matches!(next_combinator, Combinator::PseudoElement)
1108                && self.processor.invalidates_on_pseudo_element()
1109            {
1110                // We need to invalidate the element whenever pseudos change, for
1111                // two reasons:
1112                //
1113                //  * Eager pseudo styles are stored as part of the originating
1114                //    element's computed style.
1115                //
1116                //  * Lazy pseudo-styles might be cached on the originating
1117                //    element's pseudo-style cache.
1118                //
1119                // This could be more fine-grained (perhaps with a RESTYLE_PSEUDOS
1120                // hint?).
1121                //
1122                // Note that we'll also restyle the pseudo-element because it would
1123                // match this invalidation.
1124                //
1125                // FIXME: For non-element-backed pseudos this is still not quite
1126                // correct. For example for ::selection even though we invalidate
1127                // the style properly there's nothing that triggers a repaint
1128                // necessarily. Though this matches old Gecko behavior, and the
1129                // ::selection implementation needs to change significantly anyway
1130                // to implement https://github.com/w3c/csswg-drafts/issues/2474 for
1131                // example.
1132                invalidated_self = true;
1133            }
1134
1135            debug!(
1136                " > Invalidation matched, next: {:?}, ({:?})",
1137                next_invalidation, next_combinator
1138            );
1139
1140            let next_invalidation_kind = next_invalidation.kind();
1141
1142            // We can skip pushing under some circumstances, and we should
1143            // because otherwise the invalidation list could grow
1144            // exponentially.
1145            //
1146            //  * First of all, both invalidations need to be of the same
1147            //    kind. This is because of how we propagate them going to
1148            //    the right of the tree for sibling invalidations and going
1149            //    down the tree for children invalidations. A sibling
1150            //    invalidation that ends up generating a children
1151            //    invalidation ends up (correctly) in five different lists,
1152            //    not in the same list five different times.
1153            //
1154            //  * Then, the invalidation needs to be matched by a previous
1155            //    ancestor/sibling, in order to know that this invalidation
1156            //    has been generated already.
1157            //
1158            //  * Finally, the new invalidation needs to be
1159            //    `effective_for_next()`, in order for us to know that it is
1160            //    still in the list, since we remove the dependencies that
1161            //    aren't from the lists for our children / siblings.
1162            //
1163            // To go through an example, let's imagine we are processing a
1164            // dom subtree like:
1165            //
1166            //   <div><address><div><div/></div></address></div>
1167            //
1168            // And an invalidation list with a single invalidation like:
1169            //
1170            //   [div div div]
1171            //
1172            // When we process the invalidation list for the outer div, we
1173            // match it, and generate a `div div` invalidation, so for the
1174            // <address> child we have:
1175            //
1176            //   [div div div, div div]
1177            //
1178            // With the first of them marked as `matched`.
1179            //
1180            // When we process the <address> child, we don't match any of
1181            // them, so both invalidations go untouched to our children.
1182            //
1183            // When we process the second <div>, we match _both_
1184            // invalidations.
1185            //
1186            // However, when matching the first, we can tell it's been
1187            // matched, and not push the corresponding `div div`
1188            // invalidation, since we know it's necessarily already on the
1189            // list.
1190            //
1191            // Thus, without skipping the push, we'll arrive to the
1192            // innermost <div> with:
1193            //
1194            //   [div div div, div div, div div, div]
1195            //
1196            // While skipping it, we won't arrive here with duplicating
1197            // dependencies:
1198            //
1199            //   [div div div, div div, div]
1200            //
1201            let can_skip_pushing = next_invalidation_kind == invalidation_kind
1202                && invalidation.matched_by_any_previous
1203                && next_invalidation.effective_for_next();
1204
1205            if can_skip_pushing {
1206                debug!(
1207                    " > Can avoid push, since the invalidation had \
1208                    already been matched before"
1209                );
1210            } else {
1211                match next_invalidation_kind {
1212                    InvalidationKind::Descendant(DescendantInvalidationKind::Dom) => {
1213                        descendant_invalidations
1214                            .dom_descendants
1215                            .push(next_invalidation);
1216                    },
1217                    InvalidationKind::Descendant(DescendantInvalidationKind::Part) => {
1218                        descendant_invalidations.parts.push(next_invalidation);
1219                    },
1220                    InvalidationKind::Descendant(DescendantInvalidationKind::Slotted) => {
1221                        descendant_invalidations
1222                            .slotted_descendants
1223                            .push(next_invalidation);
1224                    },
1225                    InvalidationKind::Sibling => {
1226                        sibling_invalidations.push(next_invalidation);
1227                    },
1228                }
1229            }
1230        }
1231
1232        ProcessInvalidationResult {
1233            invalidated_self,
1234            matched: true,
1235        }
1236    }
1237}
1238
1239/// Note the child dependencies of a scope end selector
1240/// This is necessary because the scope end selector is not bound to :scope
1241///
1242/// e.g @scope to (.b) {:scope .a .c {...}}
1243/// in the case of the following:
1244/// <div class=a><div id=x class=b><div class=c></div></div></div>
1245///
1246/// If we toggle class "b" in x, we would have to go up to find .a
1247/// if we wanted to invalidate correctly. However, this is costly.
1248/// Instead we just invalidate to the subject of the selector .c
1249pub fn note_scope_dependency_force_at_subject<'selectors>(
1250    dependency: &'selectors Dependency,
1251    current_host: Option<OpaqueElement>,
1252) -> Vec<(Invalidation<'selectors>, InvalidationAddOverride)> {
1253    let mut invalidations_and_override_types: Vec<(Invalidation, InvalidationAddOverride)> =
1254        Vec::new();
1255    if let Some(next) = dependency.next.as_ref() {
1256        for dep in next.slice() {
1257            if dep.selector_offset == 0 {
1258                continue;
1259            }
1260
1261            let invalidation = Invalidation::new_subject_invalidation(dep, current_host, None);
1262
1263            let combinator = dep
1264                .selector
1265                .combinator_at_match_order(dep.selector_offset - 1);
1266
1267            let invalidation_override = match combinator.is_sibling() {
1268                true => InvalidationAddOverride::Sibling,
1269                false => InvalidationAddOverride::Descendant,
1270            };
1271
1272            invalidations_and_override_types.push((invalidation, invalidation_override));
1273            invalidations_and_override_types
1274                .extend(note_scope_dependency_force_at_subject(dep, current_host));
1275        }
1276    }
1277    invalidations_and_override_types
1278}