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