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