Coverage Report

Created: 2026-10-28 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}