style/invalidation/element/
document_state.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//! An invalidation processor for style changes due to document state changes.
6
7use crate::dom::TElement;
8use crate::invalidation::element::invalidation_map::Dependency;
9use crate::invalidation::element::invalidator::{
10    DescendantInvalidationLists, InvalidationVector, SiblingTraversalMap,
11};
12use crate::invalidation::element::invalidator::{Invalidation, InvalidationProcessor};
13use crate::invalidation::element::state_and_attributes;
14use crate::stylist::CascadeData;
15use dom::DocumentState;
16use selectors::matching::{
17    IncludeStartingStyle, MatchingContext, MatchingForInvalidation, MatchingMode,
18    NeedsSelectorFlags, QuirksMode, SelectorCaches, VisitedHandlingMode,
19};
20
21/// A struct holding the members necessary to invalidate document state
22/// selectors.
23#[derive(Debug)]
24pub struct InvalidationMatchingData {
25    /// The document state that has changed, which makes it always match.
26    pub document_state: DocumentState,
27}
28
29impl Default for InvalidationMatchingData {
30    #[inline(always)]
31    fn default() -> Self {
32        Self {
33            document_state: DocumentState::empty(),
34        }
35    }
36}
37
38/// An invalidation processor for style changes due to state and attribute
39/// changes.
40pub struct DocumentStateInvalidationProcessor<'a, 'b, E: TElement, I> {
41    rules: I,
42    matching_context: MatchingContext<'a, E::Impl>,
43    traversal_map: SiblingTraversalMap<E>,
44    document_states_changed: DocumentState,
45    _marker: std::marker::PhantomData<&'b ()>,
46}
47
48impl<'a, 'b, E: TElement, I> DocumentStateInvalidationProcessor<'a, 'b, E, I> {
49    /// Creates a new DocumentStateInvalidationProcessor.
50    #[inline]
51    pub fn new(
52        rules: I,
53        document_states_changed: DocumentState,
54        selector_caches: &'a mut SelectorCaches,
55        quirks_mode: QuirksMode,
56    ) -> Self {
57        let mut matching_context = MatchingContext::<'a, E::Impl>::new_for_visited(
58            MatchingMode::Normal,
59            None,
60            selector_caches,
61            VisitedHandlingMode::AllLinksVisitedAndUnvisited,
62            IncludeStartingStyle::No,
63            quirks_mode,
64            NeedsSelectorFlags::No,
65            MatchingForInvalidation::No,
66        );
67
68        matching_context.extra_data.invalidation_data.document_state = document_states_changed;
69
70        Self {
71            rules,
72            document_states_changed,
73            matching_context,
74            traversal_map: SiblingTraversalMap::default(),
75            _marker: std::marker::PhantomData,
76        }
77    }
78}
79
80impl<'a, 'b, E, I> InvalidationProcessor<'b, 'a, E>
81    for DocumentStateInvalidationProcessor<'a, 'b, E, I>
82where
83    E: TElement,
84    I: Iterator<Item = &'b CascadeData>,
85{
86    fn check_outer_dependency(&mut self, _: &Dependency, _: E) -> bool {
87        debug_assert!(
88            false,
89            "how, we should only have parent-less dependencies here!"
90        );
91        true
92    }
93
94    fn collect_invalidations(
95        &mut self,
96        _element: E,
97        self_invalidations: &mut InvalidationVector<'b>,
98        _descendant_invalidations: &mut DescendantInvalidationLists<'b>,
99        _sibling_invalidations: &mut InvalidationVector<'b>,
100    ) -> bool {
101        for cascade_data in &mut self.rules {
102            let map = cascade_data.invalidation_map();
103            for dependency in &map.document_state_selectors {
104                if !dependency.state.intersects(self.document_states_changed) {
105                    continue;
106                }
107
108                // We pass `None` as a scope, as document state selectors aren't
109                // affected by the current scope.
110                //
111                // FIXME(emilio): We should really pass the relevant host for
112                // self.rules, so that we invalidate correctly if the selector
113                // happens to have something like :host(:-moz-window-inactive)
114                // for example.
115                self_invalidations.push(Invalidation::new(
116                    &dependency.dependency,
117                    /* scope = */ None,
118                ));
119            }
120        }
121
122        false
123    }
124
125    fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {
126        &mut self.matching_context
127    }
128
129    fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> {
130        &self.traversal_map
131    }
132
133    fn recursion_limit_exceeded(&mut self, _: E) {
134        unreachable!("We don't run document state invalidation with stack limits")
135    }
136
137    fn should_process_descendants(&mut self, element: E) -> bool {
138        match element.borrow_data() {
139            Some(d) => state_and_attributes::should_process_descendants(&d),
140            None => false,
141        }
142    }
143
144    fn invalidated_descendants(&mut self, element: E, child: E) {
145        state_and_attributes::invalidated_descendants(element, child)
146    }
147
148    fn invalidated_self(&mut self, element: E) {
149        state_and_attributes::invalidated_self(element);
150    }
151
152    fn invalidated_sibling(&mut self, sibling: E, of: E) {
153        state_and_attributes::invalidated_sibling(sibling, of);
154    }
155}