/rust/registry/src/index.crates.io-1949cf8c6b5b557f/parcel_selectors-0.28.3/context.rs
Line | Count | Source |
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 | | use crate::attr::CaseSensitivity; |
6 | | use crate::bloom::BloomFilter; |
7 | | use crate::nth_index_cache::NthIndexCache; |
8 | | use crate::parser::SelectorImpl; |
9 | | use crate::tree::{Element, OpaqueElement}; |
10 | | |
11 | | /// What kind of selector matching mode we should use. |
12 | | /// |
13 | | /// There are two modes of selector matching. The difference is only noticeable |
14 | | /// in presence of pseudo-elements. |
15 | | #[derive(Clone, Copy, Debug, PartialEq)] |
16 | | pub enum MatchingMode { |
17 | | /// Don't ignore any pseudo-element selectors. |
18 | | Normal, |
19 | | |
20 | | /// Ignores any stateless pseudo-element selectors in the rightmost sequence |
21 | | /// of simple selectors. |
22 | | /// |
23 | | /// This is useful, for example, to match against ::before when you aren't a |
24 | | /// pseudo-element yourself. |
25 | | /// |
26 | | /// For example, in presence of `::before:hover`, it would never match, but |
27 | | /// `::before` would be ignored as in "matching". |
28 | | /// |
29 | | /// It's required for all the selectors you match using this mode to have a |
30 | | /// pseudo-element. |
31 | | ForStatelessPseudoElement, |
32 | | } |
33 | | |
34 | | /// The mode to use when matching unvisited and visited links. |
35 | | #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
36 | | pub enum VisitedHandlingMode { |
37 | | /// All links are matched as if they are unvisted. |
38 | | AllLinksUnvisited, |
39 | | /// All links are matched as if they are visited and unvisited (both :link |
40 | | /// and :visited match). |
41 | | /// |
42 | | /// This is intended to be used from invalidation code, to be conservative |
43 | | /// about whether we need to restyle a link. |
44 | | AllLinksVisitedAndUnvisited, |
45 | | /// A element's "relevant link" is the element being matched if it is a link |
46 | | /// or the nearest ancestor link. The relevant link is matched as though it |
47 | | /// is visited, and all other links are matched as if they are unvisited. |
48 | | RelevantLinkVisited, |
49 | | } |
50 | | |
51 | | impl VisitedHandlingMode { |
52 | | #[inline] |
53 | 0 | pub fn matches_visited(&self) -> bool { |
54 | 0 | matches!( |
55 | 0 | *self, |
56 | | VisitedHandlingMode::RelevantLinkVisited | VisitedHandlingMode::AllLinksVisitedAndUnvisited |
57 | | ) |
58 | 0 | } |
59 | | |
60 | | #[inline] |
61 | 0 | pub fn matches_unvisited(&self) -> bool { |
62 | 0 | matches!( |
63 | 0 | *self, |
64 | | VisitedHandlingMode::AllLinksUnvisited | VisitedHandlingMode::AllLinksVisitedAndUnvisited |
65 | | ) |
66 | 0 | } |
67 | | } |
68 | | |
69 | | /// Which quirks mode is this document in. |
70 | | /// |
71 | | /// See: https://quirks.spec.whatwg.org/ |
72 | | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
73 | | pub enum QuirksMode { |
74 | | /// Quirks mode. |
75 | | Quirks, |
76 | | /// Limited quirks mode. |
77 | | LimitedQuirks, |
78 | | /// No quirks mode. |
79 | | NoQuirks, |
80 | | } |
81 | | |
82 | | impl QuirksMode { |
83 | | #[inline] |
84 | 0 | pub fn classes_and_ids_case_sensitivity(self) -> CaseSensitivity { |
85 | 0 | match self { |
86 | 0 | QuirksMode::NoQuirks | QuirksMode::LimitedQuirks => CaseSensitivity::CaseSensitive, |
87 | 0 | QuirksMode::Quirks => CaseSensitivity::AsciiCaseInsensitive, |
88 | | } |
89 | 0 | } |
90 | | } |
91 | | |
92 | | /// Data associated with the matching process for a element. This context is |
93 | | /// used across many selectors for an element, so it's not appropriate for |
94 | | /// transient data that applies to only a single selector. |
95 | | pub struct MatchingContext<'a, 'i, Impl> |
96 | | where |
97 | | Impl: SelectorImpl<'i>, |
98 | | { |
99 | | /// Input with the matching mode we should use when matching selectors. |
100 | | matching_mode: MatchingMode, |
101 | | /// Input with the bloom filter used to fast-reject selectors. |
102 | | pub bloom_filter: Option<&'a BloomFilter>, |
103 | | /// An optional cache to speed up nth-index-like selectors. |
104 | | pub nth_index_cache: Option<&'a mut NthIndexCache>, |
105 | | /// The element which is going to match :scope pseudo-class. It can be |
106 | | /// either one :scope element, or the scoping element. |
107 | | /// |
108 | | /// Note that, although in theory there can be multiple :scope elements, |
109 | | /// in current specs, at most one is specified, and when there is one, |
110 | | /// scoping element is not relevant anymore, so we use a single field for |
111 | | /// them. |
112 | | /// |
113 | | /// When this is None, :scope will match the root element. |
114 | | /// |
115 | | /// See https://drafts.csswg.org/selectors-4/#scope-pseudo |
116 | | pub scope_element: Option<OpaqueElement>, |
117 | | |
118 | | /// The current shadow host we're collecting :host rules for. |
119 | | pub current_host: Option<OpaqueElement>, |
120 | | |
121 | | /// Controls how matching for links is handled. |
122 | | visited_handling: VisitedHandlingMode, |
123 | | |
124 | | /// The current nesting level of selectors that we're matching. |
125 | | /// |
126 | | /// FIXME(emilio): Consider putting the mutable stuff in a Cell, then make |
127 | | /// MatchingContext immutable again. |
128 | | nesting_level: usize, |
129 | | |
130 | | /// Whether we're inside a negation or not. |
131 | | in_negation: bool, |
132 | | |
133 | | /// An optional hook function for checking whether a pseudo-element |
134 | | /// should match when matching_mode is ForStatelessPseudoElement. |
135 | | pub pseudo_element_matching_fn: Option<&'a dyn Fn(&Impl::PseudoElement) -> bool>, |
136 | | |
137 | | /// Extra implementation-dependent matching data. |
138 | | pub extra_data: Impl::ExtraMatchingData, |
139 | | |
140 | | quirks_mode: QuirksMode, |
141 | | classes_and_ids_case_sensitivity: CaseSensitivity, |
142 | | _impl: ::std::marker::PhantomData<Impl>, |
143 | | } |
144 | | |
145 | | impl<'a, 'i, Impl> MatchingContext<'a, 'i, Impl> |
146 | | where |
147 | | Impl: SelectorImpl<'i>, |
148 | | { |
149 | | /// Constructs a new `MatchingContext`. |
150 | 0 | pub fn new( |
151 | 0 | matching_mode: MatchingMode, |
152 | 0 | bloom_filter: Option<&'a BloomFilter>, |
153 | 0 | nth_index_cache: Option<&'a mut NthIndexCache>, |
154 | 0 | quirks_mode: QuirksMode, |
155 | 0 | ) -> Self { |
156 | 0 | Self::new_for_visited( |
157 | 0 | matching_mode, |
158 | 0 | bloom_filter, |
159 | 0 | nth_index_cache, |
160 | 0 | VisitedHandlingMode::AllLinksUnvisited, |
161 | 0 | quirks_mode, |
162 | | ) |
163 | 0 | } |
164 | | |
165 | | /// Constructs a new `MatchingContext` for use in visited matching. |
166 | 0 | pub fn new_for_visited( |
167 | 0 | matching_mode: MatchingMode, |
168 | 0 | bloom_filter: Option<&'a BloomFilter>, |
169 | 0 | nth_index_cache: Option<&'a mut NthIndexCache>, |
170 | 0 | visited_handling: VisitedHandlingMode, |
171 | 0 | quirks_mode: QuirksMode, |
172 | 0 | ) -> Self { |
173 | 0 | Self { |
174 | 0 | matching_mode, |
175 | 0 | bloom_filter, |
176 | 0 | visited_handling, |
177 | 0 | nth_index_cache, |
178 | 0 | quirks_mode, |
179 | 0 | classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(), |
180 | 0 | scope_element: None, |
181 | 0 | current_host: None, |
182 | 0 | nesting_level: 0, |
183 | 0 | in_negation: false, |
184 | 0 | pseudo_element_matching_fn: None, |
185 | 0 | extra_data: Default::default(), |
186 | 0 | _impl: ::std::marker::PhantomData, |
187 | 0 | } |
188 | 0 | } |
189 | | |
190 | | /// Whether we're matching a nested selector. |
191 | | #[inline] |
192 | 0 | pub fn is_nested(&self) -> bool { |
193 | 0 | self.nesting_level != 0 |
194 | 0 | } |
195 | | |
196 | | /// Whether we're matching inside a :not(..) selector. |
197 | | #[inline] |
198 | 0 | pub fn in_negation(&self) -> bool { |
199 | 0 | self.in_negation |
200 | 0 | } |
201 | | |
202 | | /// The quirks mode of the document. |
203 | | #[inline] |
204 | 0 | pub fn quirks_mode(&self) -> QuirksMode { |
205 | 0 | self.quirks_mode |
206 | 0 | } |
207 | | |
208 | | /// The matching-mode for this selector-matching operation. |
209 | | #[inline] |
210 | 0 | pub fn matching_mode(&self) -> MatchingMode { |
211 | 0 | self.matching_mode |
212 | 0 | } |
213 | | |
214 | | /// The case-sensitivity for class and ID selectors |
215 | | #[inline] |
216 | 0 | pub fn classes_and_ids_case_sensitivity(&self) -> CaseSensitivity { |
217 | 0 | self.classes_and_ids_case_sensitivity |
218 | 0 | } |
219 | | |
220 | | /// Runs F with a deeper nesting level. |
221 | | #[inline] |
222 | 0 | pub fn nest<F, R>(&mut self, f: F) -> R |
223 | 0 | where |
224 | 0 | F: FnOnce(&mut Self) -> R, |
225 | | { |
226 | 0 | self.nesting_level += 1; |
227 | 0 | let result = f(self); |
228 | 0 | self.nesting_level -= 1; |
229 | 0 | result |
230 | 0 | } |
231 | | |
232 | | /// Runs F with a deeper nesting level, and marking ourselves in a negation, |
233 | | /// for a :not(..) selector, for example. |
234 | | #[inline] |
235 | 0 | pub fn nest_for_negation<F, R>(&mut self, f: F) -> R |
236 | 0 | where |
237 | 0 | F: FnOnce(&mut Self) -> R, |
238 | | { |
239 | 0 | let old_in_negation = self.in_negation; |
240 | 0 | self.in_negation = true; |
241 | 0 | let result = self.nest(f); |
242 | 0 | self.in_negation = old_in_negation; |
243 | 0 | result |
244 | 0 | } |
245 | | |
246 | | #[inline] |
247 | 0 | pub fn visited_handling(&self) -> VisitedHandlingMode { |
248 | 0 | self.visited_handling |
249 | 0 | } |
250 | | |
251 | | /// Runs F with a different VisitedHandlingMode. |
252 | | #[inline] |
253 | 0 | pub fn with_visited_handling_mode<F, R>(&mut self, handling_mode: VisitedHandlingMode, f: F) -> R |
254 | 0 | where |
255 | 0 | F: FnOnce(&mut Self) -> R, |
256 | | { |
257 | 0 | let original_handling_mode = self.visited_handling; |
258 | 0 | self.visited_handling = handling_mode; |
259 | 0 | let result = f(self); |
260 | 0 | self.visited_handling = original_handling_mode; |
261 | 0 | result |
262 | 0 | } |
263 | | |
264 | | /// Runs F with a given shadow host which is the root of the tree whose |
265 | | /// rules we're matching. |
266 | | #[inline] |
267 | 0 | pub fn with_shadow_host<F, E, R>(&mut self, host: Option<E>, f: F) -> R |
268 | 0 | where |
269 | 0 | E: Element<'i>, |
270 | 0 | F: FnOnce(&mut Self) -> R, |
271 | | { |
272 | 0 | let original_host = self.current_host.take(); |
273 | 0 | self.current_host = host.map(|h| h.opaque()); |
274 | 0 | let result = f(self); |
275 | 0 | self.current_host = original_host; |
276 | 0 | result |
277 | 0 | } |
278 | | |
279 | | /// Returns the current shadow host whose shadow root we're matching rules |
280 | | /// against. |
281 | | #[inline] |
282 | 0 | pub fn shadow_host(&self) -> Option<OpaqueElement> { |
283 | 0 | self.current_host |
284 | 0 | } |
285 | | } |