Source code

Revision control

Other Tools

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
pub fn matches_visited(&self) -> bool {
54
matches!(
55
*self,
56
VisitedHandlingMode::RelevantLinkVisited |
57
VisitedHandlingMode::AllLinksVisitedAndUnvisited
58
)
59
}
60
61
#[inline]
62
pub fn matches_unvisited(&self) -> bool {
63
matches!(
64
*self,
65
VisitedHandlingMode::AllLinksUnvisited |
66
VisitedHandlingMode::AllLinksVisitedAndUnvisited
67
)
68
}
69
}
70
71
/// Which quirks mode is this document in.
72
///
74
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
75
pub enum QuirksMode {
76
/// Quirks mode.
77
Quirks,
78
/// Limited quirks mode.
79
LimitedQuirks,
80
/// No quirks mode.
81
NoQuirks,
82
}
83
84
impl QuirksMode {
85
#[inline]
86
pub fn classes_and_ids_case_sensitivity(self) -> CaseSensitivity {
87
match self {
88
QuirksMode::NoQuirks | QuirksMode::LimitedQuirks => CaseSensitivity::CaseSensitive,
89
QuirksMode::Quirks => CaseSensitivity::AsciiCaseInsensitive,
90
}
91
}
92
}
93
94
/// Data associated with the matching process for a element. This context is
95
/// used across many selectors for an element, so it's not appropriate for
96
/// transient data that applies to only a single selector.
97
pub struct MatchingContext<'a, Impl>
98
where
99
Impl: SelectorImpl,
100
{
101
/// Input with the matching mode we should use when matching selectors.
102
matching_mode: MatchingMode,
103
/// Input with the bloom filter used to fast-reject selectors.
104
pub bloom_filter: Option<&'a BloomFilter>,
105
/// An optional cache to speed up nth-index-like selectors.
106
pub nth_index_cache: Option<&'a mut NthIndexCache>,
107
/// The element which is going to match :scope pseudo-class. It can be
108
/// either one :scope element, or the scoping element.
109
///
110
/// Note that, although in theory there can be multiple :scope elements,
111
/// in current specs, at most one is specified, and when there is one,
112
/// scoping element is not relevant anymore, so we use a single field for
113
/// them.
114
///
115
/// When this is None, :scope will match the root element.
116
///
118
pub scope_element: Option<OpaqueElement>,
119
120
/// The current shadow host we're collecting :host rules for.
121
pub current_host: Option<OpaqueElement>,
122
123
/// Controls how matching for links is handled.
124
visited_handling: VisitedHandlingMode,
125
126
/// The current nesting level of selectors that we're matching.
127
///
128
/// FIXME(emilio): Consider putting the mutable stuff in a Cell, then make
129
/// MatchingContext immutable again.
130
nesting_level: usize,
131
132
/// Whether we're inside a negation or not.
133
in_negation: bool,
134
135
/// An optional hook function for checking whether a pseudo-element
136
/// should match when matching_mode is ForStatelessPseudoElement.
137
pub pseudo_element_matching_fn: Option<&'a dyn Fn(&Impl::PseudoElement) -> bool>,
138
139
/// Extra implementation-dependent matching data.
140
pub extra_data: Impl::ExtraMatchingData,
141
142
quirks_mode: QuirksMode,
143
classes_and_ids_case_sensitivity: CaseSensitivity,
144
_impl: ::std::marker::PhantomData<Impl>,
145
}
146
147
impl<'a, Impl> MatchingContext<'a, Impl>
148
where
149
Impl: SelectorImpl,
150
{
151
/// Constructs a new `MatchingContext`.
152
pub fn new(
153
matching_mode: MatchingMode,
154
bloom_filter: Option<&'a BloomFilter>,
155
nth_index_cache: Option<&'a mut NthIndexCache>,
156
quirks_mode: QuirksMode,
157
) -> Self {
158
Self::new_for_visited(
159
matching_mode,
160
bloom_filter,
161
nth_index_cache,
162
VisitedHandlingMode::AllLinksUnvisited,
163
quirks_mode,
164
)
165
}
166
167
/// Constructs a new `MatchingContext` for use in visited matching.
168
pub fn new_for_visited(
169
matching_mode: MatchingMode,
170
bloom_filter: Option<&'a BloomFilter>,
171
nth_index_cache: Option<&'a mut NthIndexCache>,
172
visited_handling: VisitedHandlingMode,
173
quirks_mode: QuirksMode,
174
) -> Self {
175
Self {
176
matching_mode,
177
bloom_filter,
178
visited_handling,
179
nth_index_cache,
180
quirks_mode,
181
classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(),
182
scope_element: None,
183
current_host: None,
184
nesting_level: 0,
185
in_negation: false,
186
pseudo_element_matching_fn: None,
187
extra_data: Default::default(),
188
_impl: ::std::marker::PhantomData,
189
}
190
}
191
192
/// Whether we're matching a nested selector.
193
#[inline]
194
pub fn is_nested(&self) -> bool {
195
self.nesting_level != 0
196
}
197
198
/// Whether we're matching inside a :not(..) selector.
199
#[inline]
200
pub fn in_negation(&self) -> bool {
201
self.in_negation
202
}
203
204
/// The quirks mode of the document.
205
#[inline]
206
pub fn quirks_mode(&self) -> QuirksMode {
207
self.quirks_mode
208
}
209
210
/// The matching-mode for this selector-matching operation.
211
#[inline]
212
pub fn matching_mode(&self) -> MatchingMode {
213
self.matching_mode
214
}
215
216
/// The case-sensitivity for class and ID selectors
217
#[inline]
218
pub fn classes_and_ids_case_sensitivity(&self) -> CaseSensitivity {
219
self.classes_and_ids_case_sensitivity
220
}
221
222
/// Runs F with a deeper nesting level.
223
#[inline]
224
pub fn nest<F, R>(&mut self, f: F) -> R
225
where
226
F: FnOnce(&mut Self) -> R,
227
{
228
self.nesting_level += 1;
229
let result = f(self);
230
self.nesting_level -= 1;
231
result
232
}
233
234
/// Runs F with a deeper nesting level, and marking ourselves in a negation,
235
/// for a :not(..) selector, for example.
236
#[inline]
237
pub fn nest_for_negation<F, R>(&mut self, f: F) -> R
238
where
239
F: FnOnce(&mut Self) -> R,
240
{
241
debug_assert!(!self.in_negation, "Someone messed up parsing?");
242
self.in_negation = true;
243
let result = self.nest(f);
244
self.in_negation = false;
245
result
246
}
247
248
#[inline]
249
pub fn visited_handling(&self) -> VisitedHandlingMode {
250
self.visited_handling
251
}
252
253
/// Runs F with a different VisitedHandlingMode.
254
#[inline]
255
pub fn with_visited_handling_mode<F, R>(
256
&mut self,
257
handling_mode: VisitedHandlingMode,
258
f: F,
259
) -> R
260
where
261
F: FnOnce(&mut Self) -> R,
262
{
263
let original_handling_mode = self.visited_handling;
264
self.visited_handling = handling_mode;
265
let result = f(self);
266
self.visited_handling = original_handling_mode;
267
result
268
}
269
270
/// Runs F with a given shadow host which is the root of the tree whose
271
/// rules we're matching.
272
#[inline]
273
pub fn with_shadow_host<F, E, R>(&mut self, host: Option<E>, f: F) -> R
274
where
275
E: Element,
276
F: FnOnce(&mut Self) -> R,
277
{
278
let original_host = self.current_host.take();
279
self.current_host = host.map(|h| h.opaque());
280
let result = f(self);
281
self.current_host = original_host;
282
result
283
}
284
285
/// Returns the current shadow host whose shadow root we're matching rules
286
/// against.
287
#[inline]
288
pub fn shadow_host(&self) -> Option<OpaqueElement> {
289
self.current_host.clone()
290
}
291
}