tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

state_and_attributes.rs (26585B)


      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 //! An invalidation processor for style changes due to state and attribute
      6 //! changes.
      7 
      8 use crate::context::SharedStyleContext;
      9 use crate::data::ElementData;
     10 use crate::dom::{TElement, TNode};
     11 use crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
     12 use crate::invalidation::element::invalidation_map::*;
     13 use crate::invalidation::element::invalidator::{
     14    any_next_has_scope_in_negation, note_scope_dependency_force_at_subject,
     15    DescendantInvalidationLists, InvalidationVector, SiblingTraversalMap,
     16 };
     17 use crate::invalidation::element::invalidator::{Invalidation, InvalidationProcessor};
     18 use crate::invalidation::element::restyle_hints::RestyleHint;
     19 use crate::selector_map::SelectorMap;
     20 use crate::selector_parser::Snapshot;
     21 use crate::stylesheets::origin::OriginSet;
     22 use crate::values::AtomIdent;
     23 use crate::{Atom, WeakAtom};
     24 use dom::ElementState;
     25 use selectors::attr::CaseSensitivity;
     26 use selectors::kleene_value::KleeneValue;
     27 use selectors::matching::{
     28    matches_selector_kleene, IncludeStartingStyle, MatchingContext, MatchingForInvalidation,
     29    MatchingMode, NeedsSelectorFlags, SelectorCaches, VisitedHandlingMode,
     30 };
     31 use selectors::OpaqueElement;
     32 use smallvec::SmallVec;
     33 
     34 /// The collector implementation.
     35 struct Collector<'a, 'b: 'a, 'selectors: 'a, E>
     36 where
     37    E: TElement,
     38 {
     39    element: E,
     40    wrapper: ElementWrapper<'b, E>,
     41    snapshot: &'a Snapshot,
     42    matching_context: &'a mut MatchingContext<'b, E::Impl>,
     43    lookup_element: E,
     44    removed_id: Option<&'a WeakAtom>,
     45    added_id: Option<&'a WeakAtom>,
     46    classes_removed: &'a SmallVec<[Atom; 8]>,
     47    classes_added: &'a SmallVec<[Atom; 8]>,
     48    custom_states_removed: &'a SmallVec<[AtomIdent; 8]>,
     49    custom_states_added: &'a SmallVec<[AtomIdent; 8]>,
     50    state_changes: ElementState,
     51    descendant_invalidations: &'a mut DescendantInvalidationLists<'selectors>,
     52    sibling_invalidations: &'a mut InvalidationVector<'selectors>,
     53    invalidates_self: bool,
     54 }
     55 
     56 /// An invalidation processor for style changes due to state and attribute
     57 /// changes.
     58 pub struct StateAndAttrInvalidationProcessor<'a, 'b: 'a, E: TElement> {
     59    shared_context: &'a SharedStyleContext<'b>,
     60    element: E,
     61    data: &'a mut ElementData,
     62    matching_context: MatchingContext<'a, E::Impl>,
     63    traversal_map: SiblingTraversalMap<E>,
     64 }
     65 
     66 impl<'a, 'b: 'a, E: TElement + 'b> StateAndAttrInvalidationProcessor<'a, 'b, E> {
     67    /// Creates a new StateAndAttrInvalidationProcessor.
     68    pub fn new(
     69        shared_context: &'a SharedStyleContext<'b>,
     70        element: E,
     71        data: &'a mut ElementData,
     72        selector_caches: &'a mut SelectorCaches,
     73    ) -> Self {
     74        let matching_context = MatchingContext::new_for_visited(
     75            MatchingMode::Normal,
     76            None,
     77            selector_caches,
     78            VisitedHandlingMode::AllLinksVisitedAndUnvisited,
     79            IncludeStartingStyle::No,
     80            shared_context.quirks_mode(),
     81            NeedsSelectorFlags::No,
     82            MatchingForInvalidation::Yes,
     83        );
     84 
     85        Self {
     86            shared_context,
     87            element,
     88            data,
     89            matching_context,
     90            traversal_map: SiblingTraversalMap::default(),
     91        }
     92    }
     93 }
     94 
     95 /// Checks a dependency against a given element and wrapper, to see if something
     96 /// changed.
     97 pub fn check_dependency<E, W>(
     98    dependency: &Dependency,
     99    element: &E,
    100    wrapper: &W,
    101    context: &mut MatchingContext<'_, E::Impl>,
    102    scope: Option<OpaqueElement>,
    103 ) -> bool
    104 where
    105    E: TElement,
    106    W: selectors::Element<Impl = E::Impl>,
    107 {
    108    context.for_invalidation_comparison(|context| {
    109        context.nest_for_scope_condition(scope, |context| {
    110            let matches_now = matches_selector_kleene(
    111                &dependency.selector,
    112                dependency.selector_offset,
    113                None,
    114                element,
    115                context,
    116            );
    117 
    118            // If the previous dependency was a scope dependency (i.e. by `scope` is set),
    119            // possible change in scope element is encapsulated in `:scope`, whose
    120            // matching value will not change. We instead check that the change in scope
    121            // element can propagate (i.e. This selector matches).
    122            if scope.is_some() && matches_now != KleeneValue::False {
    123                return true;
    124            }
    125 
    126            let matched_then = matches_selector_kleene(
    127                &dependency.selector,
    128                dependency.selector_offset,
    129                None,
    130                wrapper,
    131                context,
    132            );
    133 
    134            matched_then != matches_now || matches_now == KleeneValue::Unknown
    135        })
    136    })
    137 }
    138 
    139 /// Whether we should process the descendants of a given element for style
    140 /// invalidation.
    141 pub fn should_process_descendants(data: &ElementData) -> bool {
    142    !data.styles.is_display_none() && !data.hint.contains(RestyleHint::RESTYLE_DESCENDANTS)
    143 }
    144 
    145 /// Propagates the bits after invalidating a descendant child.
    146 pub fn propagate_dirty_bit_up_to<E>(ancestor: E, child: E)
    147 where
    148    E: TElement,
    149 {
    150    // The child may not be a flattened tree child of the current element,
    151    // but may be arbitrarily deep.
    152    //
    153    // Since we keep the traversal flags in terms of the flattened tree,
    154    // we need to propagate it as appropriate.
    155    let mut current = child.traversal_parent();
    156    while let Some(parent) = current.take() {
    157        unsafe { parent.set_dirty_descendants() };
    158        current = parent.traversal_parent();
    159 
    160        if parent == ancestor {
    161            return;
    162        }
    163    }
    164    debug_assert!(
    165        false,
    166        "Should've found {:?} as an ancestor of {:?}",
    167        ancestor, child
    168    );
    169 }
    170 
    171 /// Propagates the bits after invalidating a descendant child, if needed.
    172 pub fn invalidated_descendants<E>(element: E, child: E)
    173 where
    174    E: TElement,
    175 {
    176    if !child.has_data() {
    177        return;
    178    }
    179    propagate_dirty_bit_up_to(element, child)
    180 }
    181 
    182 /// Sets the appropriate restyle hint after invalidating the style of a given
    183 /// element.
    184 pub fn invalidated_self<E>(element: E) -> bool
    185 where
    186    E: TElement,
    187 {
    188    let mut data = match element.mutate_data() {
    189        Some(data) => data,
    190        None => return false,
    191    };
    192    data.hint.insert(RestyleHint::RESTYLE_SELF);
    193    true
    194 }
    195 
    196 /// Sets the appropriate hint after invalidating the style of a sibling.
    197 pub fn invalidated_sibling<E>(element: E, of: E)
    198 where
    199    E: TElement,
    200 {
    201    debug_assert_eq!(
    202        element.as_node().parent_node(),
    203        of.as_node().parent_node(),
    204        "Should be siblings"
    205    );
    206    if !invalidated_self(element) {
    207        return;
    208    }
    209    if element.traversal_parent() != of.traversal_parent() {
    210        let parent = element.as_node().parent_element_or_host();
    211        debug_assert!(
    212            parent.is_some(),
    213            "How can we have siblings without parent nodes?"
    214        );
    215        if let Some(e) = parent {
    216            propagate_dirty_bit_up_to(e, element)
    217        }
    218    }
    219 }
    220 
    221 impl<'a, 'b: 'a, E: 'a> InvalidationProcessor<'a, 'a, E>
    222    for StateAndAttrInvalidationProcessor<'a, 'b, E>
    223 where
    224    E: TElement,
    225 {
    226    /// We need to invalidate style on pseudo-elements, in order to process
    227    /// changes that could otherwise end up in ::before or ::after content being
    228    /// generated, and invalidate lazy pseudo caches.
    229    fn invalidates_on_pseudo_element(&self) -> bool {
    230        true
    231    }
    232 
    233    fn check_outer_dependency(
    234        &mut self,
    235        dependency: &Dependency,
    236        element: E,
    237        scope: Option<OpaqueElement>,
    238    ) -> bool {
    239        // We cannot assert about `element` having a snapshot here (in fact it
    240        // most likely won't), because it may be an arbitrary descendant or
    241        // later-sibling of the element we started invalidating with.
    242        let wrapper = ElementWrapper::new(element, &*self.shared_context.snapshot_map);
    243        check_dependency(
    244            dependency,
    245            &element,
    246            &wrapper,
    247            &mut self.matching_context,
    248            scope,
    249        )
    250    }
    251 
    252    fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {
    253        &mut self.matching_context
    254    }
    255 
    256    fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> {
    257        &self.traversal_map
    258    }
    259 
    260    fn collect_invalidations(
    261        &mut self,
    262        element: E,
    263        _self_invalidations: &mut InvalidationVector<'a>,
    264        descendant_invalidations: &mut DescendantInvalidationLists<'a>,
    265        sibling_invalidations: &mut InvalidationVector<'a>,
    266    ) -> bool {
    267        debug_assert_eq!(element, self.element);
    268        debug_assert!(element.has_snapshot(), "Why bothering?");
    269 
    270        let wrapper = ElementWrapper::new(element, &*self.shared_context.snapshot_map);
    271 
    272        let state_changes = wrapper.state_changes();
    273        let Some(snapshot) = wrapper.snapshot() else {
    274            return false;
    275        };
    276 
    277        if !snapshot.has_attrs() && !snapshot.has_custom_states() && state_changes.is_empty() {
    278            return false;
    279        }
    280 
    281        let mut classes_removed = SmallVec::<[Atom; 8]>::new();
    282        let mut classes_added = SmallVec::<[Atom; 8]>::new();
    283        if snapshot.class_changed() {
    284            // TODO(emilio): Do this more efficiently!
    285            snapshot.each_class(|c| {
    286                if !element.has_class(c, CaseSensitivity::CaseSensitive) {
    287                    classes_removed.push(c.0.clone())
    288                }
    289            });
    290 
    291            element.each_class(|c| {
    292                if !snapshot.has_class(c, CaseSensitivity::CaseSensitive) {
    293                    classes_added.push(c.0.clone())
    294                }
    295            })
    296        }
    297 
    298        let mut custom_states_removed = SmallVec::<[AtomIdent; 8]>::new();
    299        let mut custom_states_added = SmallVec::<[AtomIdent; 8]>::new();
    300        if snapshot.has_custom_states() {
    301            snapshot.each_custom_state(|s| {
    302                if !element.has_custom_state(s) {
    303                    custom_states_removed.push(s.clone())
    304                }
    305            });
    306            element.each_custom_state(|s| {
    307                if !snapshot.has_custom_state(s) {
    308                    custom_states_added.push(s.clone())
    309                }
    310            })
    311        }
    312 
    313        let mut id_removed = None;
    314        let mut id_added = None;
    315        if snapshot.id_changed() {
    316            let old_id = snapshot.id_attr();
    317            let current_id = element.id();
    318 
    319            if old_id != current_id {
    320                id_removed = old_id;
    321                id_added = current_id;
    322            }
    323        }
    324 
    325        if log_enabled!(::log::Level::Debug) {
    326            debug!("Collecting changes for: {:?}", element);
    327            if !state_changes.is_empty() {
    328                debug!(" > state: {:?}", state_changes);
    329            }
    330            if snapshot.id_changed() {
    331                debug!(" > id changed: +{:?} -{:?}", id_added, id_removed);
    332            }
    333            if snapshot.class_changed() {
    334                debug!(
    335                    " > class changed: +{:?} -{:?}",
    336                    classes_added, classes_removed
    337                );
    338            }
    339            let mut attributes_changed = false;
    340            snapshot.each_attr_changed(|_| {
    341                attributes_changed = true;
    342            });
    343            if attributes_changed {
    344                debug!(
    345                    " > attributes changed, old: {}",
    346                    snapshot.debug_list_attributes()
    347                )
    348            }
    349        }
    350 
    351        let lookup_element = if element.implemented_pseudo_element().is_some() {
    352            element.pseudo_element_originating_element().unwrap()
    353        } else {
    354            element
    355        };
    356 
    357        let mut shadow_rule_datas = SmallVec::<[_; 3]>::new();
    358        let matches_document_author_rules =
    359            element.each_applicable_non_document_style_rule_data(|data, host| {
    360                shadow_rule_datas.push((data, host.opaque()))
    361            });
    362 
    363        let invalidated_self = {
    364            let mut collector = Collector {
    365                wrapper,
    366                lookup_element,
    367                state_changes,
    368                element,
    369                snapshot: &snapshot,
    370                matching_context: &mut self.matching_context,
    371                removed_id: id_removed,
    372                added_id: id_added,
    373                classes_removed: &classes_removed,
    374                classes_added: &classes_added,
    375                custom_states_removed: &custom_states_removed,
    376                custom_states_added: &custom_states_added,
    377                descendant_invalidations,
    378                sibling_invalidations,
    379                invalidates_self: false,
    380            };
    381 
    382            let document_origins = if !matches_document_author_rules {
    383                OriginSet::ORIGIN_USER_AGENT | OriginSet::ORIGIN_USER
    384            } else {
    385                OriginSet::all()
    386            };
    387 
    388            for (cascade_data, origin) in self.shared_context.stylist.iter_origins() {
    389                if document_origins.contains(origin.into()) {
    390                    collector
    391                        .collect_dependencies_in_invalidation_map(cascade_data.invalidation_map());
    392                }
    393            }
    394 
    395            for &(ref data, ref host) in &shadow_rule_datas {
    396                collector.matching_context.current_host = Some(host.clone());
    397                collector.collect_dependencies_in_invalidation_map(data.invalidation_map());
    398            }
    399 
    400            collector.invalidates_self
    401        };
    402 
    403        // If we generated a ton of descendant invalidations, it's probably not
    404        // worth to go ahead and try to process them.
    405        //
    406        // Just restyle the descendants directly.
    407        //
    408        // This number is completely made-up, but the page that made us add this
    409        // code generated 1960+ invalidations (bug 1420741).
    410        //
    411        // We don't look at slotted_descendants because those don't propagate
    412        // down more than one level anyway.
    413        if descendant_invalidations.dom_descendants.len() > 150 {
    414            self.data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS);
    415        }
    416 
    417        if invalidated_self {
    418            self.data.hint.insert(RestyleHint::RESTYLE_SELF);
    419        }
    420 
    421        invalidated_self
    422    }
    423 
    424    fn should_process_descendants(&mut self, element: E) -> bool {
    425        if element == self.element {
    426            return should_process_descendants(&self.data);
    427        }
    428 
    429        match element.borrow_data() {
    430            Some(d) => should_process_descendants(&d),
    431            None => return false,
    432        }
    433    }
    434 
    435    fn recursion_limit_exceeded(&mut self, element: E) {
    436        if element == self.element {
    437            self.data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS);
    438            return;
    439        }
    440 
    441        if let Some(mut data) = element.mutate_data() {
    442            data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS);
    443        }
    444    }
    445 
    446    fn invalidated_descendants(&mut self, element: E, child: E) {
    447        invalidated_descendants(element, child)
    448    }
    449 
    450    fn invalidated_self(&mut self, element: E) {
    451        debug_assert_ne!(element, self.element);
    452        invalidated_self(element);
    453    }
    454 
    455    fn invalidated_sibling(&mut self, element: E, of: E) {
    456        debug_assert_ne!(element, self.element);
    457        invalidated_sibling(element, of);
    458    }
    459 }
    460 
    461 impl<'a, 'b, 'selectors, E> Collector<'a, 'b, 'selectors, E>
    462 where
    463    E: TElement,
    464    'selectors: 'a,
    465 {
    466    fn collect_dependencies_in_invalidation_map(&mut self, map: &'selectors InvalidationMap) {
    467        let quirks_mode = self.matching_context.quirks_mode();
    468        let removed_id = self.removed_id;
    469        if let Some(ref id) = removed_id {
    470            if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {
    471                for dep in deps {
    472                    self.scan_dependency(dep, false);
    473                }
    474            }
    475        }
    476 
    477        let added_id = self.added_id;
    478        if let Some(ref id) = added_id {
    479            if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {
    480                for dep in deps {
    481                    self.scan_dependency(dep, false);
    482                }
    483            }
    484        }
    485 
    486        for class in self.classes_added.iter().chain(self.classes_removed.iter()) {
    487            if let Some(deps) = map.class_to_selector.get(class, quirks_mode) {
    488                for dep in deps {
    489                    self.scan_dependency(dep, false);
    490                }
    491            }
    492        }
    493 
    494        for state in self
    495            .custom_states_added
    496            .iter()
    497            .chain(self.custom_states_removed.iter())
    498        {
    499            if let Some(deps) = map.custom_state_affecting_selectors.get(state) {
    500                for dep in deps {
    501                    self.scan_dependency(dep, false);
    502                }
    503            }
    504        }
    505 
    506        self.snapshot.each_attr_changed(|attribute| {
    507            if let Some(deps) = map.other_attribute_affecting_selectors.get(attribute) {
    508                for dep in deps {
    509                    self.scan_dependency(dep, false);
    510                }
    511            }
    512        });
    513 
    514        self.collect_state_dependencies(&map.state_affecting_selectors)
    515    }
    516 
    517    fn collect_state_dependencies(&mut self, map: &'selectors SelectorMap<StateDependency>) {
    518        if self.state_changes.is_empty() {
    519            return;
    520        }
    521        map.lookup_with_additional(
    522            self.lookup_element,
    523            self.matching_context.quirks_mode(),
    524            self.removed_id,
    525            self.classes_removed,
    526            self.state_changes,
    527            |dependency| {
    528                if !dependency.state.intersects(self.state_changes) {
    529                    return true;
    530                }
    531                self.scan_dependency(&dependency.dep, false);
    532                true
    533            },
    534        );
    535    }
    536 
    537    /// Check whether a dependency should be taken into account.
    538    #[inline]
    539    fn check_dependency(&mut self, dependency: &Dependency, set_scope: bool) -> bool {
    540        check_dependency(
    541            dependency,
    542            &self.element,
    543            &self.wrapper,
    544            &mut self.matching_context,
    545            set_scope.then(|| self.element.opaque()),
    546        )
    547    }
    548 
    549    fn scan_dependency(&mut self, dependency: &'selectors Dependency, set_scope: bool) {
    550        debug_assert!(
    551            matches!(
    552                dependency.invalidation_kind(),
    553                DependencyInvalidationKind::Normal(_) | DependencyInvalidationKind::Scope(_)
    554            ),
    555            "Found unexpected dependency invalidation kind"
    556        );
    557        debug!(
    558            "TreeStyleInvalidator::scan_dependency({:?}, {:?})",
    559            self.element, dependency
    560        );
    561 
    562        if !self.dependency_may_be_relevant(dependency) {
    563            return;
    564        }
    565 
    566        if self.check_dependency(dependency, set_scope) {
    567            return self.note_dependency(dependency, set_scope);
    568        }
    569    }
    570 
    571    fn note_dependency(&mut self, dependency: &'selectors Dependency, set_scope: bool) {
    572        debug_assert!(self.dependency_may_be_relevant(dependency));
    573        let invalidation_kind = dependency.invalidation_kind();
    574        if matches!(
    575            invalidation_kind,
    576            DependencyInvalidationKind::Normal(NormalDependencyInvalidationKind::Element)
    577        ) {
    578            if let Some(ref next) = dependency.next {
    579                // We know something changed in the inner selector, go outwards
    580                // now.
    581                self.scan_dependency(&next.as_ref().slice()[0], set_scope);
    582            } else {
    583                self.invalidates_self = true;
    584            }
    585            return;
    586        }
    587 
    588        if let DependencyInvalidationKind::Scope(scope_kind) = invalidation_kind {
    589            if scope_kind == ScopeDependencyInvalidationKind::ImplicitScope {
    590                if let Some(ref next) = dependency.next {
    591                    // When we reach an implicit scope dependency, we know there's an
    592                    // element matching that implicit scope somewhere in the descendant.
    593                    // We need to go find it so that we can continue the invalidation from
    594                    // its next dependencies.
    595                    for dep in next.as_ref().slice() {
    596                        let invalidation = Invalidation::new_always_effective_for_next_descendant(
    597                            dep,
    598                            self.matching_context.current_host.clone(),
    599                            self.matching_context.scope_element,
    600                        );
    601 
    602                        self.descendant_invalidations
    603                            .dom_descendants
    604                            .push(invalidation);
    605                    }
    606                    return;
    607                }
    608            }
    609 
    610            if dependency.selector.is_rightmost(dependency.selector_offset) {
    611                let force_add = any_next_has_scope_in_negation(dependency);
    612                if scope_kind == ScopeDependencyInvalidationKind::ScopeEnd || force_add {
    613                    let invalidations = note_scope_dependency_force_at_subject(
    614                        dependency,
    615                        self.matching_context.current_host.clone(),
    616                        self.matching_context.scope_element,
    617                        force_add,
    618                    );
    619                    for invalidation in invalidations {
    620                        self.descendant_invalidations
    621                            .dom_descendants
    622                            .push(invalidation);
    623                    }
    624                    self.invalidates_self = true;
    625                } else if let Some(ref next) = dependency.next {
    626                    for dep in next.as_ref().slice() {
    627                        self.scan_dependency(dep, true);
    628                    }
    629                }
    630                return;
    631            }
    632        }
    633 
    634        debug_assert_ne!(dependency.selector_offset, 0);
    635        debug_assert_ne!(dependency.selector_offset, dependency.selector.len());
    636 
    637        let invalidation = Invalidation::new(
    638            &dependency,
    639            self.matching_context.current_host.clone(),
    640            self.matching_context.scope_element.clone(),
    641        );
    642 
    643        self.invalidates_self |= push_invalidation(
    644            invalidation,
    645            invalidation_kind,
    646            self.descendant_invalidations,
    647            self.sibling_invalidations,
    648        );
    649    }
    650 
    651    /// Returns whether `dependency` may cause us to invalidate the style of
    652    /// more elements than what we've already invalidated.
    653    fn dependency_may_be_relevant(&self, dependency: &Dependency) -> bool {
    654        match dependency.invalidation_kind() {
    655            DependencyInvalidationKind::FullSelector | DependencyInvalidationKind::Relative(_) => {
    656                unreachable!()
    657            },
    658            DependencyInvalidationKind::Scope(_) => true,
    659            DependencyInvalidationKind::Normal(kind) => match kind {
    660                NormalDependencyInvalidationKind::Element => !self.invalidates_self,
    661                NormalDependencyInvalidationKind::SlottedElements => {
    662                    self.element.is_html_slot_element()
    663                },
    664                NormalDependencyInvalidationKind::Parts => self.element.shadow_root().is_some(),
    665                NormalDependencyInvalidationKind::ElementAndDescendants
    666                | NormalDependencyInvalidationKind::Siblings
    667                | NormalDependencyInvalidationKind::Descendants => true,
    668            },
    669        }
    670    }
    671 }
    672 
    673 pub(crate) fn push_invalidation<'a>(
    674    invalidation: Invalidation<'a>,
    675    invalidation_kind: DependencyInvalidationKind,
    676    descendant_invalidations: &mut DescendantInvalidationLists<'a>,
    677    sibling_invalidations: &mut InvalidationVector<'a>,
    678 ) -> bool {
    679    match invalidation_kind {
    680        DependencyInvalidationKind::FullSelector => unreachable!(),
    681        DependencyInvalidationKind::Relative(_) => unreachable!(),
    682        DependencyInvalidationKind::Scope(_) => {
    683            // Scope invalidation kind matters only upon reaching the subject.
    684            // Examine the combinator to the right of the compound.
    685            let combinator = invalidation.combinator_to_right();
    686            if combinator.is_sibling() {
    687                sibling_invalidations.push(invalidation);
    688            } else {
    689                descendant_invalidations.dom_descendants.push(invalidation);
    690            }
    691            true
    692        },
    693        DependencyInvalidationKind::Normal(kind) => match kind {
    694            NormalDependencyInvalidationKind::Element => unreachable!(),
    695            NormalDependencyInvalidationKind::ElementAndDescendants => {
    696                descendant_invalidations.dom_descendants.push(invalidation);
    697                true
    698            },
    699            NormalDependencyInvalidationKind::Descendants => {
    700                descendant_invalidations.dom_descendants.push(invalidation);
    701                false
    702            },
    703            NormalDependencyInvalidationKind::Siblings => {
    704                sibling_invalidations.push(invalidation);
    705                false
    706            },
    707            NormalDependencyInvalidationKind::Parts => {
    708                descendant_invalidations.parts.push(invalidation);
    709                false
    710            },
    711            NormalDependencyInvalidationKind::SlottedElements => {
    712                descendant_invalidations
    713                    .slotted_descendants
    714                    .push(invalidation);
    715                false
    716            },
    717        },
    718    }
    719 }
    720 
    721 pub(crate) fn dependency_may_be_relevant<E: TElement>(
    722    dependency: &Dependency,
    723    element: &E,
    724    already_invalidated_self: bool,
    725 ) -> bool {
    726    match dependency.invalidation_kind() {
    727        DependencyInvalidationKind::FullSelector => unreachable!(),
    728        DependencyInvalidationKind::Relative(_) => unreachable!(),
    729        DependencyInvalidationKind::Scope(_) => true,
    730        DependencyInvalidationKind::Normal(kind) => match kind {
    731            NormalDependencyInvalidationKind::Element => !already_invalidated_self,
    732            NormalDependencyInvalidationKind::SlottedElements => element.is_html_slot_element(),
    733            NormalDependencyInvalidationKind::Parts => element.shadow_root().is_some(),
    734            NormalDependencyInvalidationKind::ElementAndDescendants
    735            | NormalDependencyInvalidationKind::Siblings
    736            | NormalDependencyInvalidationKind::Descendants => true,
    737        },
    738    }
    739 }