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/parser.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::{AttrSelectorOperator, AttrSelectorWithOptionalNamespace};
6
use crate::attr::{NamespaceConstraint, ParsedAttrSelectorOperation};
7
use crate::attr::{ParsedCaseSensitivity, SELECTOR_WHITESPACE};
8
use crate::bloom::BLOOM_HASH_MASK;
9
use crate::builder::{SelectorBuilder, SelectorFlags, SpecificityAndFlags};
10
use crate::context::QuirksMode;
11
use crate::sink::Push;
12
pub use crate::visitor::SelectorVisitor;
13
use cssparser::parse_nth;
14
use cssparser::{BasicParseError, BasicParseErrorKind, ParseError, ParseErrorKind};
15
use cssparser::{CowRcStr, Delimiter, SourceLocation};
16
use cssparser::{Parser as CssParser, ToCss, Token};
17
use precomputed_hash::PrecomputedHash;
18
use smallvec::{smallvec, SmallVec};
19
use std::borrow::Borrow;
20
use std::fmt::{self, Debug};
21
use std::iter::Rev;
22
use std::slice;
23
24
/// A trait that represents a pseudo-element.
25
pub trait PseudoElement<'i>: Sized + ToCss {
26
  /// The `SelectorImpl` this pseudo-element is used for.
27
  type Impl: SelectorImpl<'i>;
28
29
  /// Whether the pseudo-element supports a given state selector to the right
30
  /// of it.
31
0
  fn accepts_state_pseudo_classes(&self) -> bool {
32
0
    false
33
0
  }
34
35
  /// Whether this pseudo-element is valid after a ::slotted(..) pseudo.
36
0
  fn valid_after_slotted(&self) -> bool {
37
0
    false
38
0
  }
39
40
0
  fn is_webkit_scrollbar(&self) -> bool {
41
0
    false
42
0
  }
43
44
0
  fn is_view_transition(&self) -> bool {
45
0
    false
46
0
  }
47
48
0
  fn is_unknown(&self) -> bool {
49
0
    false
50
0
  }
51
}
52
53
/// A trait that represents a pseudo-class.
54
pub trait NonTSPseudoClass<'i>: Sized + ToCss {
55
  /// The `SelectorImpl` this pseudo-element is used for.
56
  type Impl: SelectorImpl<'i>;
57
58
  /// Whether this pseudo-class is :active or :hover.
59
  fn is_active_or_hover(&self) -> bool;
60
61
  /// Whether this pseudo-class belongs to:
62
  ///
63
  /// https://drafts.csswg.org/selectors-4/#useraction-pseudos
64
  fn is_user_action_state(&self) -> bool;
65
66
0
  fn is_valid_before_webkit_scrollbar(&self) -> bool {
67
0
    true
68
0
  }
69
70
0
  fn is_valid_after_webkit_scrollbar(&self) -> bool {
71
0
    false
72
0
  }
73
74
0
  fn visit<V>(&self, _visitor: &mut V) -> bool
75
0
  where
76
0
    V: SelectorVisitor<'i, Impl = Self::Impl>,
77
  {
78
0
    true
79
0
  }
80
}
81
82
/// Returns a Cow::Borrowed if `s` is already ASCII lowercase, and a
83
/// Cow::Owned if `s` had to be converted into ASCII lowercase.
84
2.01M
fn to_ascii_lowercase<'i>(s: CowRcStr<'i>) -> CowRcStr<'i> {
85
27.8M
  if let Some(first_uppercase) = s.bytes().position(|byte| byte >= b'A' && byte <= b'Z') {
86
455k
    let mut string = s.to_string();
87
455k
    string[first_uppercase..].make_ascii_lowercase();
88
455k
    string.into()
89
  } else {
90
1.56M
    s
91
  }
92
2.01M
}
93
94
bitflags! {
95
    /// Flags that indicate at which point of parsing a selector are we.
96
    #[derive(PartialEq, Eq, Clone, Copy)]
97
    struct SelectorParsingState: u16 {
98
        /// Whether we should avoid adding default namespaces to selectors that
99
        /// aren't type or universal selectors.
100
        const SKIP_DEFAULT_NAMESPACE = 1 << 0;
101
102
        /// Whether we've parsed a ::slotted() pseudo-element already.
103
        ///
104
        /// If so, then we can only parse a subset of pseudo-elements, and
105
        /// whatever comes after them if so.
106
        const AFTER_SLOTTED = 1 << 1;
107
        /// Whether we've parsed a ::part() pseudo-element already.
108
        ///
109
        /// If so, then we can only parse a subset of pseudo-elements, and
110
        /// whatever comes after them if so.
111
        const AFTER_PART = 1 << 2;
112
        /// Whether we've parsed a pseudo-element (as in, an
113
        /// `Impl::PseudoElement` thus not accounting for `::slotted` or
114
        /// `::part`) already.
115
        ///
116
        /// If so, then other pseudo-elements and most other selectors are
117
        /// disallowed.
118
        const AFTER_PSEUDO_ELEMENT = 1 << 3;
119
        /// Whether we've parsed a non-stateful pseudo-element (again, as-in
120
        /// `Impl::PseudoElement`) already. If so, then other pseudo-classes are
121
        /// disallowed. If this flag is set, `AFTER_PSEUDO_ELEMENT` must be set
122
        /// as well.
123
        const AFTER_NON_STATEFUL_PSEUDO_ELEMENT = 1 << 4;
124
125
        /// Whether we are after any of the pseudo-like things.
126
        const AFTER_PSEUDO = Self::AFTER_PART.bits() | Self::AFTER_SLOTTED.bits() | Self::AFTER_PSEUDO_ELEMENT.bits();
127
128
        /// Whether we explicitly disallow combinators.
129
        const DISALLOW_COMBINATORS = 1 << 5;
130
131
        /// Whether we explicitly disallow pseudo-element-like things.
132
        const DISALLOW_PSEUDOS = 1 << 6;
133
134
        /// Whether we have seen a nesting selector.
135
        const AFTER_NESTING = 1 << 7;
136
137
        const AFTER_WEBKIT_SCROLLBAR = 1 << 8;
138
        const AFTER_VIEW_TRANSITION = 1 << 9;
139
        const AFTER_UNKNOWN_PSEUDO_ELEMENT = 1 << 10;
140
    }
141
}
142
143
impl SelectorParsingState {
144
  #[inline]
145
65.8k
  fn allows_pseudos(self) -> bool {
146
    // NOTE(emilio): We allow pseudos after ::part and such.
147
65.8k
    !self.intersects(Self::AFTER_PSEUDO_ELEMENT | Self::DISALLOW_PSEUDOS)
148
65.8k
  }
<parcel_selectors::parser::SelectorParsingState>::allows_pseudos
Line
Count
Source
145
65.8k
  fn allows_pseudos(self) -> bool {
146
    // NOTE(emilio): We allow pseudos after ::part and such.
147
65.8k
    !self.intersects(Self::AFTER_PSEUDO_ELEMENT | Self::DISALLOW_PSEUDOS)
148
65.8k
  }
Unexecuted instantiation: <parcel_selectors::parser::SelectorParsingState>::allows_pseudos
149
150
  #[inline]
151
0
  fn allows_slotted(self) -> bool {
152
0
    !self.intersects(Self::AFTER_PSEUDO | Self::DISALLOW_PSEUDOS)
153
0
  }
Unexecuted instantiation: <parcel_selectors::parser::SelectorParsingState>::allows_slotted
Unexecuted instantiation: <parcel_selectors::parser::SelectorParsingState>::allows_slotted
154
155
  #[inline]
156
0
  fn allows_part(self) -> bool {
157
0
    !self.intersects(Self::AFTER_PSEUDO | Self::DISALLOW_PSEUDOS)
158
0
  }
Unexecuted instantiation: <parcel_selectors::parser::SelectorParsingState>::allows_part
Unexecuted instantiation: <parcel_selectors::parser::SelectorParsingState>::allows_part
159
160
  // TODO(emilio): Maybe some of these should be allowed, but this gets us on
161
  // the safe side for now, matching previous behavior. Gotta be careful with
162
  // the ones like :-moz-any, which allow nested selectors but don't carry the
163
  // state, and so on.
164
  #[inline]
165
3.85k
  fn allows_custom_functional_pseudo_classes(self) -> bool {
166
3.85k
    !self.intersects(Self::AFTER_PSEUDO)
167
3.85k
  }
<parcel_selectors::parser::SelectorParsingState>::allows_custom_functional_pseudo_classes
Line
Count
Source
165
3.85k
  fn allows_custom_functional_pseudo_classes(self) -> bool {
166
3.85k
    !self.intersects(Self::AFTER_PSEUDO)
167
3.85k
  }
Unexecuted instantiation: <parcel_selectors::parser::SelectorParsingState>::allows_custom_functional_pseudo_classes
168
169
  #[inline]
170
289k
  fn allows_non_functional_pseudo_classes(self) -> bool {
171
289k
    !self.intersects(Self::AFTER_SLOTTED | Self::AFTER_NON_STATEFUL_PSEUDO_ELEMENT)
172
289k
  }
<parcel_selectors::parser::SelectorParsingState>::allows_non_functional_pseudo_classes
Line
Count
Source
170
289k
  fn allows_non_functional_pseudo_classes(self) -> bool {
171
289k
    !self.intersects(Self::AFTER_SLOTTED | Self::AFTER_NON_STATEFUL_PSEUDO_ELEMENT)
172
289k
  }
Unexecuted instantiation: <parcel_selectors::parser::SelectorParsingState>::allows_non_functional_pseudo_classes
173
174
  #[inline]
175
289k
  fn allows_tree_structural_pseudo_classes(self) -> bool {
176
289k
    !self.intersects(Self::AFTER_PSEUDO)
177
289k
  }
<parcel_selectors::parser::SelectorParsingState>::allows_tree_structural_pseudo_classes
Line
Count
Source
175
289k
  fn allows_tree_structural_pseudo_classes(self) -> bool {
176
289k
    !self.intersects(Self::AFTER_PSEUDO)
177
289k
  }
Unexecuted instantiation: <parcel_selectors::parser::SelectorParsingState>::allows_tree_structural_pseudo_classes
178
179
  #[inline]
180
581k
  fn allows_combinators(self) -> bool {
181
581k
    !self.intersects(Self::DISALLOW_COMBINATORS)
182
581k
  }
<parcel_selectors::parser::SelectorParsingState>::allows_combinators
Line
Count
Source
180
581k
  fn allows_combinators(self) -> bool {
181
581k
    !self.intersects(Self::DISALLOW_COMBINATORS)
182
581k
  }
Unexecuted instantiation: <parcel_selectors::parser::SelectorParsingState>::allows_combinators
183
}
184
185
pub type SelectorParseError<'i> = ParseError<'i, SelectorParseErrorKind<'i>>;
186
187
#[derive(Clone, Debug, PartialEq)]
188
pub enum SelectorParseErrorKind<'i> {
189
  NoQualifiedNameInAttributeSelector(Token<'i>),
190
  EmptySelector,
191
  DanglingCombinator,
192
  InvalidPseudoClassBeforeWebKitScrollbar,
193
  InvalidPseudoClassAfterWebKitScrollbar,
194
  InvalidPseudoClassAfterPseudoElement,
195
  InvalidState,
196
  MissingNestingSelector,
197
  MissingNestingPrefix,
198
  UnexpectedTokenInAttributeSelector(Token<'i>),
199
  PseudoElementExpectedIdent(Token<'i>),
200
  UnsupportedPseudoElement(CowRcStr<'i>),
201
  UnsupportedPseudoClass(CowRcStr<'i>),
202
  AmbiguousCssModuleClass(CowRcStr<'i>),
203
  UnexpectedIdent(CowRcStr<'i>),
204
  ExpectedNamespace(CowRcStr<'i>),
205
  ExpectedBarInAttr(Token<'i>),
206
  BadValueInAttr(Token<'i>),
207
  InvalidQualNameInAttr(Token<'i>),
208
  ExplicitNamespaceUnexpectedToken(Token<'i>),
209
  ClassNeedsIdent(Token<'i>),
210
  UnexpectedSelectorAfterPseudoElement(Token<'i>),
211
}
212
213
macro_rules! with_all_bounds {
214
    (
215
        [ $( $InSelector: tt )* ]
216
        [ $( $CommonBounds: tt )* ]
217
        [ $( $FromStr: tt )* ]
218
    ) => {
219
        /// This trait allows to define the parser implementation in regards
220
        /// of pseudo-classes/elements
221
        ///
222
        /// NB: We need Clone so that we can derive(Clone) on struct with that
223
        /// are parameterized on SelectorImpl. See
224
        /// <https://github.com/rust-lang/rust/issues/26925>
225
        pub trait SelectorImpl<'i>: Clone + Debug + Sized + 'static {
226
            type ExtraMatchingData: Sized + Default + 'static;
227
            type AttrValue: $($InSelector)*;
228
            type Identifier: $($InSelector)*;
229
            type LocalName: $($InSelector)* + Borrow<Self::BorrowedLocalName>;
230
            type NamespaceUrl: $($CommonBounds)* + $($FromStr)* + Default + Borrow<Self::BorrowedNamespaceUrl>;
231
            type NamespacePrefix: $($InSelector)* + Default;
232
            type BorrowedNamespaceUrl: ?Sized + Eq;
233
            type BorrowedLocalName: ?Sized + Eq;
234
235
            /// non tree-structural pseudo-classes
236
            /// (see: https://drafts.csswg.org/selectors/#structural-pseudos)
237
            type NonTSPseudoClass: $($CommonBounds)* + NonTSPseudoClass<'i, Impl = Self>;
238
            type VendorPrefix: Sized + Eq + $($CommonBounds)* + ToCss;
239
240
            /// pseudo-elements
241
            type PseudoElement: $($CommonBounds)* + PseudoElement<'i, Impl = Self>;
242
243
0
            fn to_css<W: fmt::Write>(selectors: &SelectorList<'i, Self>, dest: &mut W) -> fmt::Result {
244
0
                serialize_selector_list(selectors.0.iter(), dest)
245
0
            }
246
        }
247
    }
248
}
249
250
macro_rules! with_bounds {
251
    ( [ $( $CommonBounds: tt )* ] [ $( $FromStr: tt )* ]) => {
252
        with_all_bounds! {
253
            [$($CommonBounds)* + $($FromStr)* + ToCss]
254
            [$($CommonBounds)*]
255
            [$($FromStr)*]
256
        }
257
    }
258
}
259
260
#[cfg(feature = "serde")]
261
with_bounds! {
262
    [Clone + PartialEq + Eq + std::hash::Hash]
263
    [From<CowRcStr<'i>> + From<std::borrow::Cow<'i, str>> + AsRef<str>]
264
}
265
266
#[cfg(not(feature = "serde"))]
267
with_bounds! {
268
    [Clone + PartialEq + Eq + std::hash::Hash]
269
    [From<CowRcStr<'i>>]
270
}
271
272
pub trait Parser<'i> {
273
  type Impl: SelectorImpl<'i>;
274
  type Error: 'i + From<SelectorParseErrorKind<'i>>;
275
276
  /// Whether to parse the `::slotted()` pseudo-element.
277
0
  fn parse_slotted(&self) -> bool {
278
0
    false
279
0
  }
280
281
  /// Whether to parse the `::part()` pseudo-element.
282
0
  fn parse_part(&self) -> bool {
283
0
    false
284
0
  }
285
286
  /// Whether to parse the `:where` pseudo-class.
287
0
  fn parse_is_and_where(&self) -> bool {
288
0
    false
289
0
  }
290
291
  /// The error recovery that selector lists inside :is() and :where() have.
292
3.62k
  fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
293
3.62k
    ParseErrorRecovery::IgnoreInvalidSelector
294
3.62k
  }
<lightningcss::selector::SelectorParser as parcel_selectors::parser::Parser>::is_and_where_error_recovery
Line
Count
Source
292
3.62k
  fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
293
3.62k
    ParseErrorRecovery::IgnoreInvalidSelector
294
3.62k
  }
Unexecuted instantiation: <_ as parcel_selectors::parser::Parser>::is_and_where_error_recovery
295
296
  /// Whether the given function name is an alias for the `:is()` function.
297
0
  fn parse_any_prefix(&self, _name: &str) -> Option<<Self::Impl as SelectorImpl<'i>>::VendorPrefix> {
298
0
    None
299
0
  }
300
301
  /// Whether to parse the `:host` pseudo-class.
302
0
  fn parse_host(&self) -> bool {
303
0
    false
304
0
  }
305
306
  /// Parses non-tree-structural pseudo-classes. Tree structural pseudo-classes,
307
  /// like `:first-child`, are built into this library.
308
  ///
309
  /// This function can return an "Err" pseudo-element in order to support CSS2.1
310
  /// pseudo-elements.
311
0
  fn parse_non_ts_pseudo_class(
312
0
    &self,
313
0
    location: SourceLocation,
314
0
    name: CowRcStr<'i>,
315
0
  ) -> Result<<Self::Impl as SelectorImpl<'i>>::NonTSPseudoClass, ParseError<'i, Self::Error>> {
316
0
    Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClass(name)))
317
0
  }
318
319
0
  fn parse_non_ts_functional_pseudo_class<'t>(
320
0
    &self,
321
0
    name: CowRcStr<'i>,
322
0
    arguments: &mut CssParser<'i, 't>,
323
0
  ) -> Result<<Self::Impl as SelectorImpl<'i>>::NonTSPseudoClass, ParseError<'i, Self::Error>> {
324
0
    Err(arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClass(name)))
325
0
  }
326
327
0
  fn parse_pseudo_element(
328
0
    &self,
329
0
    location: SourceLocation,
330
0
    name: CowRcStr<'i>,
331
0
  ) -> Result<<Self::Impl as SelectorImpl<'i>>::PseudoElement, ParseError<'i, Self::Error>> {
332
0
    Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoElement(name)))
333
0
  }
334
335
0
  fn parse_functional_pseudo_element<'t>(
336
0
    &self,
337
0
    name: CowRcStr<'i>,
338
0
    arguments: &mut CssParser<'i, 't>,
339
0
  ) -> Result<<Self::Impl as SelectorImpl<'i>>::PseudoElement, ParseError<'i, Self::Error>> {
340
0
    Err(arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoElement(name)))
341
0
  }
342
343
0
  fn default_namespace(&self) -> Option<<Self::Impl as SelectorImpl<'i>>::NamespaceUrl> {
344
0
    None
345
0
  }
346
347
0
  fn namespace_for_prefix(
348
0
    &self,
349
0
    _prefix: &<Self::Impl as SelectorImpl<'i>>::NamespacePrefix,
350
0
  ) -> Option<<Self::Impl as SelectorImpl<'i>>::NamespaceUrl> {
351
0
    None
352
0
  }
353
354
0
  fn is_nesting_allowed(&self) -> bool {
355
0
    false
356
0
  }
357
358
0
  fn deep_combinator_enabled(&self) -> bool {
359
0
    false
360
0
  }
361
}
362
363
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
364
#[cfg_attr(
365
  feature = "serde",
366
  derive(serde::Serialize, serde::Deserialize),
367
  serde(bound(
368
    serialize = "Impl::NonTSPseudoClass: serde::Serialize, Impl::PseudoElement: serde::Serialize, Impl::VendorPrefix: serde::Serialize",
369
    deserialize = "Impl::NonTSPseudoClass: serde::Deserialize<'de>, Impl::PseudoElement: serde::Deserialize<'de>, Impl::VendorPrefix: serde::Deserialize<'de>"
370
  ))
371
)]
372
#[cfg_attr(
373
  feature = "jsonschema",
374
  derive(schemars::JsonSchema),
375
  schemars(
376
    rename = "SelectorList",
377
    bound = "Impl: schemars::JsonSchema, Impl::NonTSPseudoClass: schemars::JsonSchema, Impl::PseudoElement: schemars::JsonSchema, Impl::VendorPrefix: schemars::JsonSchema"
378
  )
379
)]
380
pub struct SelectorList<'i, Impl: SelectorImpl<'i>>(
381
  #[cfg_attr(feature = "serde", serde(borrow))] pub SmallVec<[Selector<'i, Impl>; 1]>,
382
);
383
384
#[cfg(feature = "into_owned")]
385
impl<'any, 'i, Impl: SelectorImpl<'i>, NewSel> static_self::IntoOwned<'any> for SelectorList<'i, Impl>
386
where
387
  Impl: static_self::IntoOwned<'any, Owned = NewSel>,
388
  NewSel: SelectorImpl<'any>,
389
  Component<'i, Impl>: static_self::IntoOwned<'any, Owned = Component<'any, NewSel>>,
390
{
391
  type Owned = SelectorList<'any, NewSel>;
392
393
  fn into_owned(self) -> Self::Owned {
394
    SelectorList(self.0.into_owned())
395
  }
396
}
397
398
/// How to treat invalid selectors in a selector list.
399
pub enum ParseErrorRecovery {
400
  /// Discard the entire selector list, this is the default behavior for
401
  /// almost all of CSS.
402
  DiscardList,
403
  /// Ignore invalid selectors, potentially creating an empty selector list.
404
  ///
405
  /// This is the error recovery mode of :is() and :where()
406
  IgnoreInvalidSelector,
407
}
408
409
#[derive(Eq, PartialEq, Clone, Copy)]
410
pub enum NestingRequirement {
411
  None,
412
  Prefixed,
413
  Contained,
414
  Implicit,
415
}
416
417
impl<'i, Impl: SelectorImpl<'i>> SelectorList<'i, Impl> {
418
  /// Parse a comma-separated list of Selectors.
419
  /// <https://drafts.csswg.org/selectors/#grouping>
420
  ///
421
  /// Return the Selectors or Err if there is an invalid selector.
422
307k
  pub fn parse<'t, P>(
423
307k
    parser: &P,
424
307k
    input: &mut CssParser<'i, 't>,
425
307k
    error_recovery: ParseErrorRecovery,
426
307k
    nesting_requirement: NestingRequirement,
427
307k
  ) -> Result<Self, ParseError<'i, P::Error>>
428
307k
  where
429
307k
    P: Parser<'i, Impl = Impl>,
430
  {
431
307k
    Self::parse_with_state(
432
307k
      parser,
433
307k
      input,
434
307k
      &mut SelectorParsingState::empty(),
435
307k
      error_recovery,
436
307k
      nesting_requirement,
437
    )
438
307k
  }
<parcel_selectors::parser::SelectorList<lightningcss::selector::private::Selectors>>::parse::<lightningcss::selector::SelectorParser>
Line
Count
Source
422
307k
  pub fn parse<'t, P>(
423
307k
    parser: &P,
424
307k
    input: &mut CssParser<'i, 't>,
425
307k
    error_recovery: ParseErrorRecovery,
426
307k
    nesting_requirement: NestingRequirement,
427
307k
  ) -> Result<Self, ParseError<'i, P::Error>>
428
307k
  where
429
307k
    P: Parser<'i, Impl = Impl>,
430
  {
431
307k
    Self::parse_with_state(
432
307k
      parser,
433
307k
      input,
434
307k
      &mut SelectorParsingState::empty(),
435
307k
      error_recovery,
436
307k
      nesting_requirement,
437
    )
438
307k
  }
Unexecuted instantiation: <parcel_selectors::parser::SelectorList<_>>::parse::<_>
439
440
  #[inline]
441
311k
  fn parse_with_state<'t, P>(
442
311k
    parser: &P,
443
311k
    input: &mut CssParser<'i, 't>,
444
311k
    state: &mut SelectorParsingState,
445
311k
    recovery: ParseErrorRecovery,
446
311k
    nesting_requirement: NestingRequirement,
447
311k
  ) -> Result<Self, ParseError<'i, P::Error>>
448
311k
  where
449
311k
    P: Parser<'i, Impl = Impl>,
450
  {
451
311k
    let original_state = *state;
452
311k
    let mut values = SmallVec::new();
453
    loop {
454
361k
      let selector = input.parse_until_before(Delimiter::Comma, |input| {
455
361k
        let mut selector_state = original_state;
456
361k
        let result = parse_selector(parser, input, &mut selector_state, nesting_requirement);
457
361k
        if selector_state.contains(SelectorParsingState::AFTER_NESTING) {
458
17.9k
          state.insert(SelectorParsingState::AFTER_NESTING)
459
343k
        }
460
361k
        result
461
361k
      });
<parcel_selectors::parser::SelectorList<lightningcss::selector::private::Selectors>>::parse_with_state::<lightningcss::selector::SelectorParser>::{closure#0}
Line
Count
Source
454
361k
      let selector = input.parse_until_before(Delimiter::Comma, |input| {
455
361k
        let mut selector_state = original_state;
456
361k
        let result = parse_selector(parser, input, &mut selector_state, nesting_requirement);
457
361k
        if selector_state.contains(SelectorParsingState::AFTER_NESTING) {
458
17.9k
          state.insert(SelectorParsingState::AFTER_NESTING)
459
343k
        }
460
361k
        result
461
361k
      });
Unexecuted instantiation: <parcel_selectors::parser::SelectorList<_>>::parse_with_state::<_>::{closure#0}
462
463
361k
      let was_ok = selector.is_ok();
464
361k
      match selector {
465
156k
        Ok(selector) => values.push(selector),
466
204k
        Err(err) => match recovery {
467
194k
          ParseErrorRecovery::DiscardList => return Err(err),
468
9.55k
          ParseErrorRecovery::IgnoreInvalidSelector => {}
469
        },
470
      }
471
472
      loop {
473
166k
        match input.next() {
474
116k
          Err(_) => return Ok(SelectorList(values)),
475
49.7k
          Ok(&Token::Comma) => break,
476
          Ok(_) => {
477
0
            debug_assert!(!was_ok, "Shouldn't have got a selector if getting here");
478
          }
479
        }
480
      }
481
    }
482
311k
  }
<parcel_selectors::parser::SelectorList<lightningcss::selector::private::Selectors>>::parse_with_state::<lightningcss::selector::SelectorParser>
Line
Count
Source
441
311k
  fn parse_with_state<'t, P>(
442
311k
    parser: &P,
443
311k
    input: &mut CssParser<'i, 't>,
444
311k
    state: &mut SelectorParsingState,
445
311k
    recovery: ParseErrorRecovery,
446
311k
    nesting_requirement: NestingRequirement,
447
311k
  ) -> Result<Self, ParseError<'i, P::Error>>
448
311k
  where
449
311k
    P: Parser<'i, Impl = Impl>,
450
  {
451
311k
    let original_state = *state;
452
311k
    let mut values = SmallVec::new();
453
    loop {
454
361k
      let selector = input.parse_until_before(Delimiter::Comma, |input| {
455
        let mut selector_state = original_state;
456
        let result = parse_selector(parser, input, &mut selector_state, nesting_requirement);
457
        if selector_state.contains(SelectorParsingState::AFTER_NESTING) {
458
          state.insert(SelectorParsingState::AFTER_NESTING)
459
        }
460
        result
461
      });
462
463
361k
      let was_ok = selector.is_ok();
464
361k
      match selector {
465
156k
        Ok(selector) => values.push(selector),
466
204k
        Err(err) => match recovery {
467
194k
          ParseErrorRecovery::DiscardList => return Err(err),
468
9.55k
          ParseErrorRecovery::IgnoreInvalidSelector => {}
469
        },
470
      }
471
472
      loop {
473
166k
        match input.next() {
474
116k
          Err(_) => return Ok(SelectorList(values)),
475
49.7k
          Ok(&Token::Comma) => break,
476
          Ok(_) => {
477
0
            debug_assert!(!was_ok, "Shouldn't have got a selector if getting here");
478
          }
479
        }
480
      }
481
    }
482
311k
  }
Unexecuted instantiation: <parcel_selectors::parser::SelectorList<_>>::parse_with_state::<_>
483
484
1.40M
  pub fn parse_relative<'t, P>(
485
1.40M
    parser: &P,
486
1.40M
    input: &mut CssParser<'i, 't>,
487
1.40M
    error_recovery: ParseErrorRecovery,
488
1.40M
    nesting_requirement: NestingRequirement,
489
1.40M
  ) -> Result<Self, ParseError<'i, P::Error>>
490
1.40M
  where
491
1.40M
    P: Parser<'i, Impl = Impl>,
492
  {
493
1.40M
    Self::parse_relative_with_state(
494
1.40M
      parser,
495
1.40M
      input,
496
1.40M
      &mut SelectorParsingState::empty(),
497
1.40M
      error_recovery,
498
1.40M
      nesting_requirement,
499
    )
500
1.40M
  }
<parcel_selectors::parser::SelectorList<lightningcss::selector::private::Selectors>>::parse_relative::<lightningcss::selector::SelectorParser>
Line
Count
Source
484
1.40M
  pub fn parse_relative<'t, P>(
485
1.40M
    parser: &P,
486
1.40M
    input: &mut CssParser<'i, 't>,
487
1.40M
    error_recovery: ParseErrorRecovery,
488
1.40M
    nesting_requirement: NestingRequirement,
489
1.40M
  ) -> Result<Self, ParseError<'i, P::Error>>
490
1.40M
  where
491
1.40M
    P: Parser<'i, Impl = Impl>,
492
  {
493
1.40M
    Self::parse_relative_with_state(
494
1.40M
      parser,
495
1.40M
      input,
496
1.40M
      &mut SelectorParsingState::empty(),
497
1.40M
      error_recovery,
498
1.40M
      nesting_requirement,
499
    )
500
1.40M
  }
Unexecuted instantiation: <parcel_selectors::parser::SelectorList<_>>::parse_relative::<_>
501
502
  #[inline]
503
1.40M
  fn parse_relative_with_state<'t, P>(
504
1.40M
    parser: &P,
505
1.40M
    input: &mut CssParser<'i, 't>,
506
1.40M
    state: &mut SelectorParsingState,
507
1.40M
    recovery: ParseErrorRecovery,
508
1.40M
    nesting_requirement: NestingRequirement,
509
1.40M
  ) -> Result<Self, ParseError<'i, P::Error>>
510
1.40M
  where
511
1.40M
    P: Parser<'i, Impl = Impl>,
512
  {
513
1.40M
    let original_state = *state;
514
1.40M
    let mut values = SmallVec::new();
515
    loop {
516
1.56M
      let selector = input.parse_until_before(Delimiter::Comma, |input| {
517
1.56M
        let mut selector_state = original_state;
518
1.56M
        let result = parse_relative_selector(parser, input, &mut selector_state, nesting_requirement);
519
1.56M
        if selector_state.contains(SelectorParsingState::AFTER_NESTING) {
520
20.3k
          state.insert(SelectorParsingState::AFTER_NESTING)
521
1.54M
        }
522
1.56M
        result
523
1.56M
      });
<parcel_selectors::parser::SelectorList<lightningcss::selector::private::Selectors>>::parse_relative_with_state::<lightningcss::selector::SelectorParser>::{closure#0}
Line
Count
Source
516
1.56M
      let selector = input.parse_until_before(Delimiter::Comma, |input| {
517
1.56M
        let mut selector_state = original_state;
518
1.56M
        let result = parse_relative_selector(parser, input, &mut selector_state, nesting_requirement);
519
1.56M
        if selector_state.contains(SelectorParsingState::AFTER_NESTING) {
520
20.3k
          state.insert(SelectorParsingState::AFTER_NESTING)
521
1.54M
        }
522
1.56M
        result
523
1.56M
      });
Unexecuted instantiation: <parcel_selectors::parser::SelectorList<_>>::parse_relative_with_state::<_>::{closure#0}
524
525
1.56M
      let was_ok = selector.is_ok();
526
1.56M
      match selector {
527
1.20M
        Ok(selector) => values.push(selector),
528
369k
        Err(err) => match recovery {
529
368k
          ParseErrorRecovery::DiscardList => return Err(err),
530
169
          ParseErrorRecovery::IgnoreInvalidSelector => {}
531
        },
532
      }
533
534
      loop {
535
1.20M
        match input.next() {
536
1.03M
          Err(_) => return Ok(SelectorList(values)),
537
162k
          Ok(&Token::Comma) => break,
538
          Ok(_) => {
539
0
            debug_assert!(!was_ok, "Shouldn't have got a selector if getting here");
540
          }
541
        }
542
      }
543
    }
544
1.40M
  }
<parcel_selectors::parser::SelectorList<lightningcss::selector::private::Selectors>>::parse_relative_with_state::<lightningcss::selector::SelectorParser>
Line
Count
Source
503
1.40M
  fn parse_relative_with_state<'t, P>(
504
1.40M
    parser: &P,
505
1.40M
    input: &mut CssParser<'i, 't>,
506
1.40M
    state: &mut SelectorParsingState,
507
1.40M
    recovery: ParseErrorRecovery,
508
1.40M
    nesting_requirement: NestingRequirement,
509
1.40M
  ) -> Result<Self, ParseError<'i, P::Error>>
510
1.40M
  where
511
1.40M
    P: Parser<'i, Impl = Impl>,
512
  {
513
1.40M
    let original_state = *state;
514
1.40M
    let mut values = SmallVec::new();
515
    loop {
516
1.56M
      let selector = input.parse_until_before(Delimiter::Comma, |input| {
517
        let mut selector_state = original_state;
518
        let result = parse_relative_selector(parser, input, &mut selector_state, nesting_requirement);
519
        if selector_state.contains(SelectorParsingState::AFTER_NESTING) {
520
          state.insert(SelectorParsingState::AFTER_NESTING)
521
        }
522
        result
523
      });
524
525
1.56M
      let was_ok = selector.is_ok();
526
1.56M
      match selector {
527
1.20M
        Ok(selector) => values.push(selector),
528
369k
        Err(err) => match recovery {
529
368k
          ParseErrorRecovery::DiscardList => return Err(err),
530
169
          ParseErrorRecovery::IgnoreInvalidSelector => {}
531
        },
532
      }
533
534
      loop {
535
1.20M
        match input.next() {
536
1.03M
          Err(_) => return Ok(SelectorList(values)),
537
162k
          Ok(&Token::Comma) => break,
538
          Ok(_) => {
539
0
            debug_assert!(!was_ok, "Shouldn't have got a selector if getting here");
540
          }
541
        }
542
      }
543
    }
544
1.40M
  }
Unexecuted instantiation: <parcel_selectors::parser::SelectorList<_>>::parse_relative_with_state::<_>
545
546
  /// Creates a new SelectorList.
547
0
  pub fn new(v: SmallVec<[Selector<'i, Impl>; 1]>) -> Self {
548
0
    SelectorList(v)
549
0
  }
550
551
  /// Creates a SelectorList from a Vec of selectors. Used in tests.
552
0
  pub fn from_vec(v: Vec<Selector<'i, Impl>>) -> Self {
553
0
    SelectorList(SmallVec::from_vec(v))
554
0
  }
555
}
556
557
impl<'i, Impl: SelectorImpl<'i>> From<Selector<'i, Impl>> for SelectorList<'i, Impl> {
558
0
  fn from(selector: Selector<'i, Impl>) -> Self {
559
0
    SelectorList(smallvec![selector])
560
0
  }
561
}
562
563
impl<'i, Impl: SelectorImpl<'i>> From<Component<'i, Impl>> for SelectorList<'i, Impl> {
564
0
  fn from(component: Component<'i, Impl>) -> Self {
565
0
    SelectorList::from(Selector::from(component))
566
0
  }
567
}
568
569
/// Parses one compound selector suitable for nested stuff like :-moz-any, etc.
570
1
fn parse_inner_compound_selector<'i, 't, P, Impl>(
571
1
  parser: &P,
572
1
  input: &mut CssParser<'i, 't>,
573
1
  state: &mut SelectorParsingState,
574
1
) -> Result<Selector<'i, Impl>, ParseError<'i, P::Error>>
575
1
where
576
1
  P: Parser<'i, Impl = Impl>,
577
1
  Impl: SelectorImpl<'i>,
578
{
579
1
  let mut child_state =
580
1
    *state | SelectorParsingState::DISALLOW_PSEUDOS | SelectorParsingState::DISALLOW_COMBINATORS;
581
1
  let result = parse_selector(parser, input, &mut child_state, NestingRequirement::None)?;
582
1
  if child_state.contains(SelectorParsingState::AFTER_NESTING) {
583
0
    state.insert(SelectorParsingState::AFTER_NESTING)
584
1
  }
585
1
  Ok(result)
586
1
}
parcel_selectors::parser::parse_inner_compound_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>
Line
Count
Source
570
1
fn parse_inner_compound_selector<'i, 't, P, Impl>(
571
1
  parser: &P,
572
1
  input: &mut CssParser<'i, 't>,
573
1
  state: &mut SelectorParsingState,
574
1
) -> Result<Selector<'i, Impl>, ParseError<'i, P::Error>>
575
1
where
576
1
  P: Parser<'i, Impl = Impl>,
577
1
  Impl: SelectorImpl<'i>,
578
{
579
1
  let mut child_state =
580
1
    *state | SelectorParsingState::DISALLOW_PSEUDOS | SelectorParsingState::DISALLOW_COMBINATORS;
581
1
  let result = parse_selector(parser, input, &mut child_state, NestingRequirement::None)?;
582
1
  if child_state.contains(SelectorParsingState::AFTER_NESTING) {
583
0
    state.insert(SelectorParsingState::AFTER_NESTING)
584
1
  }
585
1
  Ok(result)
586
1
}
Unexecuted instantiation: parcel_selectors::parser::parse_inner_compound_selector::<_, _>
587
588
/// Ancestor hashes for the bloom filter. We precompute these and store them
589
/// inline with selectors to optimize cache performance during matching.
590
/// This matters a lot.
591
///
592
/// We use 4 hashes, which is copied from Gecko, who copied it from WebKit.
593
/// Note that increasing the number of hashes here will adversely affect the
594
/// cache hit when fast-rejecting long lists of Rules with inline hashes.
595
///
596
/// Because the bloom filter only uses the bottom 24 bits of the hash, we pack
597
/// the fourth hash into the upper bits of the first three hashes in order to
598
/// shrink Rule (whose size matters a lot). This scheme minimizes the runtime
599
/// overhead of the packing for the first three hashes (we just need to mask
600
/// off the upper bits) at the expense of making the fourth somewhat more
601
/// complicated to assemble, because we often bail out before checking all the
602
/// hashes.
603
#[derive(Clone, Debug, Eq, PartialEq)]
604
pub struct AncestorHashes {
605
  pub packed_hashes: [u32; 3],
606
}
607
608
0
fn collect_ancestor_hashes<'i, Impl: SelectorImpl<'i>>(
609
0
  iter: SelectorIter<'_, 'i, Impl>,
610
0
  quirks_mode: QuirksMode,
611
0
  hashes: &mut [u32; 4],
612
0
  len: &mut usize,
613
0
) -> bool
614
0
where
615
0
  Impl::Identifier: PrecomputedHash,
616
0
  Impl::LocalName: PrecomputedHash,
617
0
  Impl::NamespaceUrl: PrecomputedHash,
618
{
619
0
  for component in AncestorIter::new(iter) {
620
0
    let hash = match *component {
621
      Component::LocalName(LocalName {
622
0
        ref name,
623
0
        ref lower_name,
624
      }) => {
625
        // Only insert the local-name into the filter if it's all
626
        // lowercase.  Otherwise we would need to test both hashes, and
627
        // our data structures aren't really set up for that.
628
0
        if name != lower_name {
629
0
          continue;
630
0
        }
631
0
        name.precomputed_hash()
632
      }
633
0
      Component::DefaultNamespace(ref url) | Component::Namespace(_, ref url) => url.precomputed_hash(),
634
      // In quirks mode, class and id selectors should match
635
      // case-insensitively, so just avoid inserting them into the filter.
636
0
      Component::ID(ref id) if quirks_mode != QuirksMode::Quirks => id.precomputed_hash(),
637
0
      Component::Class(ref class) if quirks_mode != QuirksMode::Quirks => class.precomputed_hash(),
638
0
      Component::Is(ref list) | Component::Where(ref list) => {
639
        // :where and :is OR their selectors, so we can't put any hash
640
        // in the filter if there's more than one selector, as that'd
641
        // exclude elements that may match one of the other selectors.
642
0
        if list.len() == 1 && !collect_ancestor_hashes(list[0].iter(), quirks_mode, hashes, len) {
643
0
          return false;
644
0
        }
645
0
        continue;
646
      }
647
0
      _ => continue,
648
    };
649
650
0
    hashes[*len] = hash & BLOOM_HASH_MASK;
651
0
    *len += 1;
652
0
    if *len == hashes.len() {
653
0
      return false;
654
0
    }
655
  }
656
0
  true
657
0
}
658
659
impl AncestorHashes {
660
0
  pub fn new<'i, Impl: SelectorImpl<'i>>(selector: &Selector<'i, Impl>, quirks_mode: QuirksMode) -> Self
661
0
  where
662
0
    Impl::Identifier: PrecomputedHash,
663
0
    Impl::LocalName: PrecomputedHash,
664
0
    Impl::NamespaceUrl: PrecomputedHash,
665
  {
666
    // Compute ancestor hashes for the bloom filter.
667
0
    let mut hashes = [0u32; 4];
668
0
    let mut len = 0;
669
0
    collect_ancestor_hashes(selector.iter(), quirks_mode, &mut hashes, &mut len);
670
0
    debug_assert!(len <= 4);
671
672
    // Now, pack the fourth hash (if it exists) into the upper byte of each of
673
    // the other three hashes.
674
0
    if len == 4 {
675
0
      let fourth = hashes[3];
676
0
      hashes[0] |= (fourth & 0x000000ff) << 24;
677
0
      hashes[1] |= (fourth & 0x0000ff00) << 16;
678
0
      hashes[2] |= (fourth & 0x00ff0000) << 8;
679
0
    }
680
681
0
    AncestorHashes {
682
0
      packed_hashes: [hashes[0], hashes[1], hashes[2]],
683
0
    }
684
0
  }
685
686
  /// Returns the fourth hash, reassembled from parts.
687
0
  pub fn fourth_hash(&self) -> u32 {
688
0
    ((self.packed_hashes[0] & 0xff000000) >> 24)
689
0
      | ((self.packed_hashes[1] & 0xff000000) >> 16)
690
0
      | ((self.packed_hashes[2] & 0xff000000) >> 8)
691
0
  }
692
}
693
694
0
pub fn namespace_empty_string<'i, Impl: SelectorImpl<'i>>() -> Impl::NamespaceUrl {
695
  // Rust type’s default, not default namespace
696
0
  Impl::NamespaceUrl::default()
697
0
}
698
699
/// A Selector stores a sequence of simple selectors and combinators. The
700
/// iterator classes allow callers to iterate at either the raw sequence level or
701
/// at the level of sequences of simple selectors separated by combinators. Most
702
/// callers want the higher-level iterator.
703
///
704
/// We store compound selectors internally right-to-left (in matching order).
705
/// Additionally, we invert the order of top-level compound selectors so that
706
/// each one matches left-to-right. This is because matching namespace, local name,
707
/// id, and class are all relatively cheap, whereas matching pseudo-classes might
708
/// be expensive (depending on the pseudo-class). Since authors tend to put the
709
/// pseudo-classes on the right, it's faster to start matching on the left.
710
///
711
/// This reordering doesn't change the semantics of selector matching, and we
712
/// handle it in to_css to make it invisible to serialization.
713
#[derive(Clone, PartialEq, Eq, Hash)]
714
pub struct Selector<'i, Impl: SelectorImpl<'i>>(SpecificityAndFlags, Vec<Component<'i, Impl>>);
715
716
#[cfg(feature = "into_owned")]
717
impl<'any, 'i, Impl: SelectorImpl<'i>, NewSel> static_self::IntoOwned<'any> for Selector<'i, Impl>
718
where
719
  Impl: static_self::IntoOwned<'any, Owned = NewSel>,
720
  NewSel: SelectorImpl<'any>,
721
  Component<'i, Impl>: static_self::IntoOwned<'any, Owned = Component<'any, NewSel>>,
722
{
723
  type Owned = Selector<'any, NewSel>;
724
725
  fn into_owned(self) -> Self::Owned {
726
    Selector(self.0, self.1.into_owned())
727
  }
728
}
729
730
impl<'i, Impl: SelectorImpl<'i>> Selector<'i, Impl> {
731
  #[inline]
732
24.6k
  pub fn specificity(&self) -> u32 {
733
24.6k
    self.0.specificity()
734
24.6k
  }
<parcel_selectors::parser::Selector<lightningcss::selector::private::Selectors>>::specificity
Line
Count
Source
732
24.6k
  pub fn specificity(&self) -> u32 {
733
24.6k
    self.0.specificity()
734
24.6k
  }
Unexecuted instantiation: <parcel_selectors::parser::Selector<_>>::specificity
735
736
  #[inline]
737
0
  pub fn has_pseudo_element(&self) -> bool {
738
0
    self.0.has_pseudo_element()
739
0
  }
740
741
  #[inline]
742
0
  pub fn is_slotted(&self) -> bool {
743
0
    self.0.is_slotted()
744
0
  }
745
746
  #[inline]
747
0
  pub fn is_part(&self) -> bool {
748
0
    self.0.is_part()
749
0
  }
750
751
  #[inline]
752
0
  pub fn append(&mut self, component: Component<'i, Impl>) {
753
0
    let index = self
754
0
      .1
755
0
      .iter()
756
0
      .position(|c| matches!(*c, Component::Combinator(..) | Component::PseudoElement(..)))
757
0
      .unwrap_or(self.1.len());
758
0
    self.1.insert(index, component);
759
0
  }
760
761
  #[inline]
762
0
  pub fn parts(&self) -> Option<&[Impl::Identifier]> {
763
0
    if !self.is_part() {
764
0
      return None;
765
0
    }
766
767
0
    let mut iter = self.iter();
768
0
    if self.has_pseudo_element() {
769
      // Skip the pseudo-element.
770
0
      for _ in &mut iter {}
771
772
0
      let combinator = iter.next_sequence()?;
773
0
      debug_assert_eq!(combinator, Combinator::PseudoElement);
774
0
    }
775
776
0
    for component in iter {
777
0
      if let Component::Part(ref part) = *component {
778
0
        return Some(part);
779
0
      }
780
    }
781
782
0
    debug_assert!(false, "is_part() lied somehow?");
783
0
    None
784
0
  }
785
786
  #[inline]
787
0
  pub fn pseudo_element(&self) -> Option<&Impl::PseudoElement> {
788
0
    if !self.has_pseudo_element() {
789
0
      return None;
790
0
    }
791
792
0
    for component in self.iter() {
793
0
      if let Component::PseudoElement(ref pseudo) = *component {
794
0
        return Some(pseudo);
795
0
      }
796
    }
797
798
0
    debug_assert!(false, "has_pseudo_element lied!");
799
0
    None
800
0
  }
801
802
  /// Whether this selector (pseudo-element part excluded) matches every element.
803
  ///
804
  /// Used for "pre-computed" pseudo-elements in components/style/stylist.rs
805
  #[inline]
806
0
  pub fn is_universal(&self) -> bool {
807
0
    self.iter_raw_match_order().all(|c| {
808
0
      matches!(
809
0
        *c,
810
        Component::ExplicitUniversalType
811
          | Component::ExplicitAnyNamespace
812
          | Component::Combinator(Combinator::PseudoElement)
813
          | Component::PseudoElement(..)
814
      )
815
0
    })
816
0
  }
817
818
  #[inline]
819
0
  pub fn has_combinator(&self) -> bool {
820
0
    self
821
0
      .iter_raw_match_order()
822
0
      .any(|c| matches!(*c, Component::Combinator(combinator) if combinator.is_tree_combinator()))
Unexecuted instantiation: <parcel_selectors::parser::Selector<lightningcss::selector::private::Selectors>>::has_combinator::{closure#0}
Unexecuted instantiation: <parcel_selectors::parser::Selector<_>>::has_combinator::{closure#0}
823
0
  }
Unexecuted instantiation: <parcel_selectors::parser::Selector<lightningcss::selector::private::Selectors>>::has_combinator
Unexecuted instantiation: <parcel_selectors::parser::Selector<_>>::has_combinator
824
825
  /// Returns an iterator over this selector in matching order (right-to-left).
826
  /// When a combinator is reached, the iterator will return None, and
827
  /// next_sequence() may be called to continue to the next sequence.
828
  #[inline]
829
0
  pub fn iter(&self) -> SelectorIter<'_, 'i, Impl> {
830
0
    SelectorIter {
831
0
      iter: self.iter_raw_match_order(),
832
0
      next_combinator: None,
833
0
    }
834
0
  }
835
836
  /// Whether this selector is a featureless :host selector, with no
837
  /// combinators to the left, and optionally has a pseudo-element to the
838
  /// right.
839
  #[inline]
840
0
  pub fn is_featureless_host_selector_or_pseudo_element(&self) -> bool {
841
0
    let mut iter = self.iter();
842
0
    if !self.has_pseudo_element() {
843
0
      return iter.is_featureless_host_selector();
844
0
    }
845
846
    // Skip the pseudo-element.
847
0
    for _ in &mut iter {}
848
849
0
    match iter.next_sequence() {
850
0
      None => return false,
851
0
      Some(combinator) => {
852
0
        debug_assert_eq!(combinator, Combinator::PseudoElement);
853
      }
854
    }
855
856
0
    iter.is_featureless_host_selector()
857
0
  }
858
859
  /// Returns an iterator over this selector in matching order (right-to-left),
860
  /// skipping the rightmost |offset| Components.
861
  #[inline]
862
0
  pub fn iter_from(&self, offset: usize) -> SelectorIter<'_, 'i, Impl> {
863
0
    let iter = self.1[offset..].iter();
864
0
    SelectorIter {
865
0
      iter,
866
0
      next_combinator: None,
867
0
    }
868
0
  }
869
870
  /// Returns the combinator at index `index` (zero-indexed from the right),
871
  /// or panics if the component is not a combinator.
872
  #[inline]
873
0
  pub fn combinator_at_match_order(&self, index: usize) -> Combinator {
874
0
    match self.1[index] {
875
0
      Component::Combinator(c) => c,
876
0
      ref other => panic!("Not a combinator: {:?}, {:?}, index: {}", other, self, index),
877
    }
878
0
  }
879
880
  /// Returns an iterator over the entire sequence of simple selectors and
881
  /// combinators, in matching order (from right to left).
882
  #[inline]
883
708k
  pub fn iter_raw_match_order(&self) -> slice::Iter<'_, Component<'i, Impl>> {
884
708k
    self.1.iter()
885
708k
  }
<parcel_selectors::parser::Selector<lightningcss::selector::private::Selectors>>::iter_raw_match_order
Line
Count
Source
883
708k
  pub fn iter_raw_match_order(&self) -> slice::Iter<'_, Component<'i, Impl>> {
884
708k
    self.1.iter()
885
708k
  }
Unexecuted instantiation: <parcel_selectors::parser::Selector<_>>::iter_raw_match_order
886
887
  #[inline]
888
0
  pub fn iter_mut_raw_match_order(&mut self) -> slice::IterMut<'_, Component<'i, Impl>> {
889
0
    self.1.iter_mut()
890
0
  }
Unexecuted instantiation: <parcel_selectors::parser::Selector<lightningcss::selector::private::Selectors>>::iter_mut_raw_match_order
Unexecuted instantiation: <parcel_selectors::parser::Selector<_>>::iter_mut_raw_match_order
891
892
  /// Returns the combinator at index `index` (zero-indexed from the left),
893
  /// or panics if the component is not a combinator.
894
  #[inline]
895
0
  pub fn combinator_at_parse_order(&self, index: usize) -> Combinator {
896
0
    match self.1[self.len() - index - 1] {
897
0
      Component::Combinator(c) => c,
898
0
      ref other => panic!("Not a combinator: {:?}, {:?}, index: {}", other, self, index),
899
    }
900
0
  }
901
902
  /// Returns an iterator over the sequence of simple selectors and
903
  /// combinators, in parse order (from left to right), starting from
904
  /// `offset`.
905
  #[inline]
906
0
  pub fn iter_raw_parse_order_from(&self, offset: usize) -> Rev<slice::Iter<'_, Component<'i, Impl>>> {
907
0
    self.1[..self.len() - offset].iter().rev()
908
0
  }
909
910
  /// Creates a Selector from a vec of Components, specified in parse order. Used in tests.
911
  #[allow(unused)]
912
0
  pub(crate) fn from_vec(vec: Vec<Component<'i, Impl>>, specificity: u32, flags: SelectorFlags) -> Self {
913
0
    let mut builder = SelectorBuilder::default();
914
0
    for component in vec.into_iter() {
915
0
      if let Some(combinator) = component.as_combinator() {
916
0
        builder.push_combinator(combinator);
917
0
      } else {
918
0
        builder.push_simple_selector(component);
919
0
      }
920
    }
921
0
    let spec = SpecificityAndFlags { specificity, flags };
922
0
    let (spec, components) = builder.build_with_specificity_and_flags(spec);
923
0
    Selector(spec, components)
924
0
  }
925
926
  #[cfg(feature = "serde")]
927
  #[inline]
928
  pub(crate) fn new(spec: SpecificityAndFlags, components: Vec<Component<'i, Impl>>) -> Self {
929
    Selector(spec, components)
930
  }
931
932
  /// Returns count of simple selectors and combinators in the Selector.
933
  #[inline]
934
0
  pub fn len(&self) -> usize {
935
0
    self.1.len()
936
0
  }
Unexecuted instantiation: <parcel_selectors::parser::Selector<lightningcss::selector::private::Selectors>>::len
Unexecuted instantiation: <parcel_selectors::parser::Selector<_>>::len
937
938
  /// Traverse selector components inside `self`.
939
  ///
940
  /// Implementations of this method should call `SelectorVisitor` methods
941
  /// or other impls of `Visit` as appropriate based on the fields of `Self`.
942
  ///
943
  /// A return value of `false` indicates terminating the traversal.
944
  /// It should be propagated with an early return.
945
  /// On the contrary, `true` indicates that all fields of `self` have been traversed:
946
  ///
947
  /// ```rust,ignore
948
  /// if !visitor.visit_simple_selector(&self.some_simple_selector) {
949
  ///     return false;
950
  /// }
951
  /// if !self.some_component.visit(visitor) {
952
  ///     return false;
953
  /// }
954
  /// true
955
  /// ```
956
0
  pub fn visit<V>(&self, visitor: &mut V) -> bool
957
0
  where
958
0
    V: SelectorVisitor<'i, Impl = Impl>,
959
  {
960
0
    let mut current = self.iter();
961
0
    let mut combinator = None;
962
    loop {
963
0
      if !visitor.visit_complex_selector(combinator) {
964
0
        return false;
965
0
      }
966
967
0
      for selector in &mut current {
968
0
        if !selector.visit(visitor) {
969
0
          return false;
970
0
        }
971
      }
972
973
0
      combinator = current.next_sequence();
974
0
      if combinator.is_none() {
975
0
        break;
976
0
      }
977
    }
978
979
0
    true
980
0
  }
981
}
982
983
impl<'i, Impl: SelectorImpl<'i>> From<Component<'i, Impl>> for Selector<'i, Impl> {
984
0
  fn from(component: Component<'i, Impl>) -> Self {
985
0
    let mut builder = SelectorBuilder::default();
986
0
    if let Some(combinator) = component.as_combinator() {
987
0
      builder.push_combinator(combinator);
988
0
    } else {
989
0
      builder.push_simple_selector(component);
990
0
    }
991
0
    let (spec, components) = builder.build(false, false, false);
992
0
    Selector(spec, components)
993
0
  }
Unexecuted instantiation: <parcel_selectors::parser::Selector<lightningcss::selector::private::Selectors> as core::convert::From<parcel_selectors::parser::Component<lightningcss::selector::private::Selectors>>>::from
Unexecuted instantiation: <parcel_selectors::parser::Selector<_> as core::convert::From<parcel_selectors::parser::Component<_>>>::from
994
}
995
996
impl<'i, Impl: SelectorImpl<'i>> From<Vec<Component<'i, Impl>>> for Selector<'i, Impl> {
997
0
  fn from(vec: Vec<Component<'i, Impl>>) -> Self {
998
0
    let mut builder = SelectorBuilder::default();
999
0
    for component in vec.into_iter() {
1000
0
      if let Some(combinator) = component.as_combinator() {
1001
0
        builder.push_combinator(combinator);
1002
0
      } else {
1003
0
        builder.push_simple_selector(component);
1004
0
      }
1005
    }
1006
0
    let (spec, components) = builder.build(false, false, false);
1007
0
    Selector(spec, components)
1008
0
  }
1009
}
1010
1011
#[derive(Clone)]
1012
pub struct SelectorIter<'a, 'i, Impl: SelectorImpl<'i>> {
1013
  iter: slice::Iter<'a, Component<'i, Impl>>,
1014
  next_combinator: Option<Combinator>,
1015
}
1016
1017
impl<'a, 'i, Impl: 'a + SelectorImpl<'i>> SelectorIter<'a, 'i, Impl> {
1018
  /// Prepares this iterator to point to the next sequence to the left,
1019
  /// returning the combinator if the sequence was found.
1020
  #[inline]
1021
0
  pub fn next_sequence(&mut self) -> Option<Combinator> {
1022
0
    self.next_combinator.take()
1023
0
  }
1024
1025
  /// Whether this selector is a featureless host selector, with no
1026
  /// combinators to the left.
1027
  #[inline]
1028
0
  pub(crate) fn is_featureless_host_selector(&mut self) -> bool {
1029
0
    self.selector_length() > 0
1030
0
      && self.all(|component| matches!(*component, Component::Host(..)))
1031
0
      && self.next_sequence().is_none()
1032
0
  }
1033
1034
  #[inline]
1035
0
  pub(crate) fn matches_for_stateless_pseudo_element(&mut self) -> bool {
1036
0
    let first = match self.next() {
1037
0
      Some(c) => c,
1038
      // Note that this is the common path that we keep inline: the
1039
      // pseudo-element not having anything to its right.
1040
0
      None => return true,
1041
    };
1042
0
    self.matches_for_stateless_pseudo_element_internal(first)
1043
0
  }
1044
1045
  #[inline(never)]
1046
0
  fn matches_for_stateless_pseudo_element_internal(&mut self, first: &Component<'i, Impl>) -> bool {
1047
0
    if !first.matches_for_stateless_pseudo_element() {
1048
0
      return false;
1049
0
    }
1050
0
    for component in self {
1051
      // The only other parser-allowed Components in this sequence are
1052
      // state pseudo-classes, or one of the other things that can contain
1053
      // them.
1054
0
      if !component.matches_for_stateless_pseudo_element() {
1055
0
        return false;
1056
0
      }
1057
    }
1058
0
    true
1059
0
  }
1060
1061
  /// Returns remaining count of the simple selectors and combinators in the Selector.
1062
  #[inline]
1063
0
  pub fn selector_length(&self) -> usize {
1064
0
    self.iter.len()
1065
0
  }
1066
}
1067
1068
impl<'a, 'i, Impl: SelectorImpl<'i>> Iterator for SelectorIter<'a, 'i, Impl> {
1069
  type Item = &'a Component<'i, Impl>;
1070
1071
  #[inline]
1072
0
  fn next(&mut self) -> Option<Self::Item> {
1073
0
    debug_assert!(self.next_combinator.is_none(), "You should call next_sequence!");
1074
0
    match *self.iter.next()? {
1075
0
      Component::Combinator(c) => {
1076
0
        self.next_combinator = Some(c);
1077
0
        None
1078
      }
1079
0
      ref x => Some(x),
1080
    }
1081
0
  }
1082
}
1083
1084
impl<'a, 'i, Impl: SelectorImpl<'i>> fmt::Debug for SelectorIter<'a, 'i, Impl> {
1085
0
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1086
0
    let iter = self.iter.clone().rev();
1087
0
    for component in iter {
1088
0
      component.to_css(f)?
1089
    }
1090
0
    Ok(())
1091
0
  }
1092
}
1093
1094
/// An iterator over all simple selectors belonging to ancestors.
1095
struct AncestorIter<'a, 'i, Impl: SelectorImpl<'i>>(SelectorIter<'a, 'i, Impl>);
1096
impl<'a, 'i, Impl: 'a + SelectorImpl<'i>> AncestorIter<'a, 'i, Impl> {
1097
  /// Creates an AncestorIter. The passed-in iterator is assumed to point to
1098
  /// the beginning of the child sequence, which will be skipped.
1099
0
  fn new(inner: SelectorIter<'a, 'i, Impl>) -> Self {
1100
0
    let mut result = AncestorIter(inner);
1101
0
    result.skip_until_ancestor();
1102
0
    result
1103
0
  }
1104
1105
  /// Skips a sequence of simple selectors and all subsequent sequences until
1106
  /// a non-pseudo-element ancestor combinator is reached.
1107
0
  fn skip_until_ancestor(&mut self) {
1108
    loop {
1109
0
      while self.0.next().is_some() {}
1110
      // If this is ever changed to stop at the "pseudo-element"
1111
      // combinator, we will need to fix the way we compute hashes for
1112
      // revalidation selectors.
1113
0
      if self
1114
0
        .0
1115
0
        .next_sequence()
1116
0
        .map_or(true, |x| matches!(x, Combinator::Child | Combinator::Descendant))
1117
      {
1118
0
        break;
1119
0
      }
1120
    }
1121
0
  }
1122
}
1123
1124
impl<'a, 'i, Impl: SelectorImpl<'i>> Iterator for AncestorIter<'a, 'i, Impl> {
1125
  type Item = &'a Component<'i, Impl>;
1126
0
  fn next(&mut self) -> Option<Self::Item> {
1127
    // Grab the next simple selector in the sequence if available.
1128
0
    let next = self.0.next();
1129
0
    if next.is_some() {
1130
0
      return next;
1131
0
    }
1132
1133
    // See if there are more sequences. If so, skip any non-ancestor sequences.
1134
0
    if let Some(combinator) = self.0.next_sequence() {
1135
0
      if !matches!(combinator, Combinator::Child | Combinator::Descendant) {
1136
0
        self.skip_until_ancestor();
1137
0
      }
1138
0
    }
1139
1140
0
    self.0.next()
1141
0
  }
1142
}
1143
1144
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
1145
#[cfg_attr(
1146
  feature = "serde",
1147
  derive(serde::Serialize, serde::Deserialize),
1148
  serde(rename_all = "kebab-case")
1149
)]
1150
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
1151
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
1152
pub enum Combinator {
1153
  Child,        //  >
1154
  Descendant,   // space
1155
  NextSibling,  // +
1156
  LaterSibling, // ~
1157
  /// A dummy combinator we use to the left of pseudo-elements.
1158
  ///
1159
  /// It serializes as the empty string, and acts effectively as a child
1160
  /// combinator in most cases.  If we ever actually start using a child
1161
  /// combinator for this, we will need to fix up the way hashes are computed
1162
  /// for revalidation selectors.
1163
  PseudoElement,
1164
  /// Another combinator used for ::slotted(), which represent the jump from
1165
  /// a node to its assigned slot.
1166
  SlotAssignment,
1167
  /// Another combinator used for `::part()`, which represents the jump from
1168
  /// the part to the containing shadow host.
1169
  Part,
1170
1171
  /// Non-standard Vue >>> combinator.
1172
  /// https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors
1173
  DeepDescendant,
1174
  /// Non-standard /deep/ combinator.
1175
  /// Appeared in early versions of the css-scoping-1 specification:
1176
  /// https://www.w3.org/TR/2014/WD-css-scoping-1-20140403/#deep-combinator
1177
  /// And still supported as an alias for >>> by Vue.
1178
  Deep,
1179
}
1180
1181
impl Combinator {
1182
  /// Returns true if this combinator is a child or descendant combinator.
1183
  #[inline]
1184
0
  pub fn is_ancestor(&self) -> bool {
1185
0
    matches!(
1186
0
      *self,
1187
      Combinator::Child | Combinator::Descendant | Combinator::PseudoElement | Combinator::SlotAssignment
1188
    )
1189
0
  }
1190
1191
  /// Returns true if this combinator is a pseudo-element combinator.
1192
  #[inline]
1193
0
  pub fn is_pseudo_element(&self) -> bool {
1194
0
    matches!(*self, Combinator::PseudoElement)
1195
0
  }
1196
1197
  /// Returns true if this combinator is a next- or later-sibling combinator.
1198
  #[inline]
1199
0
  pub fn is_sibling(&self) -> bool {
1200
0
    matches!(*self, Combinator::NextSibling | Combinator::LaterSibling)
1201
0
  }
1202
1203
  #[inline]
1204
0
  pub fn is_tree_combinator(&self) -> bool {
1205
0
    matches!(
1206
0
      *self,
1207
      Combinator::Child | Combinator::Descendant | Combinator::NextSibling | Combinator::LaterSibling
1208
    )
1209
0
  }
Unexecuted instantiation: <parcel_selectors::parser::Combinator>::is_tree_combinator
Unexecuted instantiation: <parcel_selectors::parser::Combinator>::is_tree_combinator
1210
}
1211
1212
/// An enum for the different types of :nth- pseudoclasses
1213
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
1214
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
1215
pub enum NthType {
1216
  Child,
1217
  LastChild,
1218
  OnlyChild,
1219
  OfType,
1220
  LastOfType,
1221
  OnlyOfType,
1222
  Col,
1223
  LastCol,
1224
}
1225
1226
impl NthType {
1227
0
  pub fn is_only(self) -> bool {
1228
0
    self == Self::OnlyChild || self == Self::OnlyOfType
1229
0
  }
1230
1231
0
  pub fn is_of_type(self) -> bool {
1232
0
    self == Self::OfType || self == Self::LastOfType || self == Self::OnlyOfType
1233
0
  }
1234
1235
0
  pub fn is_from_end(self) -> bool {
1236
0
    self == Self::LastChild || self == Self::LastOfType || self == Self::LastCol
1237
0
  }
1238
1239
0
  pub fn allows_of_selector(self) -> bool {
1240
0
    self == Self::Child || self == Self::LastChild
1241
0
  }
1242
}
1243
1244
/// The properties that comprise an :nth- pseudoclass as of Selectors 3 (e.g.,
1245
/// nth-child(An+B)).
1246
/// https://www.w3.org/TR/selectors-3/#nth-child-pseudo
1247
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
1248
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
1249
pub struct NthSelectorData {
1250
  pub ty: NthType,
1251
  pub is_function: bool,
1252
  pub a: i32,
1253
  pub b: i32,
1254
}
1255
1256
impl NthSelectorData {
1257
  /// Returns selector data for :only-{child,of-type}
1258
  #[inline]
1259
11
  pub const fn only(of_type: bool) -> Self {
1260
    Self {
1261
11
      ty: if of_type {
1262
10
        NthType::OnlyOfType
1263
      } else {
1264
1
        NthType::OnlyChild
1265
      },
1266
      is_function: false,
1267
      a: 0,
1268
      b: 1,
1269
    }
1270
11
  }
<parcel_selectors::parser::NthSelectorData>::only
Line
Count
Source
1259
11
  pub const fn only(of_type: bool) -> Self {
1260
    Self {
1261
11
      ty: if of_type {
1262
10
        NthType::OnlyOfType
1263
      } else {
1264
1
        NthType::OnlyChild
1265
      },
1266
      is_function: false,
1267
      a: 0,
1268
      b: 1,
1269
    }
1270
11
  }
Unexecuted instantiation: <parcel_selectors::parser::NthSelectorData>::only
1271
1272
  /// Returns selector data for :first-{child,of-type}
1273
  #[inline]
1274
2
  pub const fn first(of_type: bool) -> Self {
1275
    Self {
1276
2
      ty: if of_type { NthType::OfType } else { NthType::Child },
1277
      is_function: false,
1278
      a: 0,
1279
      b: 1,
1280
    }
1281
2
  }
<parcel_selectors::parser::NthSelectorData>::first
Line
Count
Source
1274
2
  pub const fn first(of_type: bool) -> Self {
1275
    Self {
1276
2
      ty: if of_type { NthType::OfType } else { NthType::Child },
1277
      is_function: false,
1278
      a: 0,
1279
      b: 1,
1280
    }
1281
2
  }
Unexecuted instantiation: <parcel_selectors::parser::NthSelectorData>::first
1282
1283
  /// Returns selector data for :last-{child,of-type}
1284
  #[inline]
1285
2
  pub const fn last(of_type: bool) -> Self {
1286
    Self {
1287
2
      ty: if of_type {
1288
1
        NthType::LastOfType
1289
      } else {
1290
1
        NthType::LastChild
1291
      },
1292
      is_function: false,
1293
      a: 0,
1294
      b: 1,
1295
    }
1296
2
  }
<parcel_selectors::parser::NthSelectorData>::last
Line
Count
Source
1285
2
  pub const fn last(of_type: bool) -> Self {
1286
    Self {
1287
2
      ty: if of_type {
1288
1
        NthType::LastOfType
1289
      } else {
1290
1
        NthType::LastChild
1291
      },
1292
      is_function: false,
1293
      a: 0,
1294
      b: 1,
1295
    }
1296
2
  }
Unexecuted instantiation: <parcel_selectors::parser::NthSelectorData>::last
1297
1298
  #[inline]
1299
0
  pub fn is_function(&self) -> bool {
1300
0
    self.a != 0 || self.b != 1
1301
0
  }
1302
1303
  /// Writes the beginning of the selector.
1304
  #[inline]
1305
0
  pub fn write_start<W: fmt::Write>(&self, dest: &mut W, is_function: bool) -> fmt::Result {
1306
0
    dest.write_str(match self.ty {
1307
0
      NthType::Child if is_function => ":nth-child(",
1308
0
      NthType::Child => ":first-child",
1309
0
      NthType::LastChild if is_function => ":nth-last-child(",
1310
0
      NthType::LastChild => ":last-child",
1311
0
      NthType::OfType if is_function => ":nth-of-type(",
1312
0
      NthType::OfType => ":first-of-type",
1313
0
      NthType::LastOfType if is_function => ":nth-last-of-type(",
1314
0
      NthType::LastOfType => ":last-of-type",
1315
0
      NthType::OnlyChild => ":only-child",
1316
0
      NthType::OnlyOfType => ":only-of-type",
1317
0
      NthType::Col => ":nth-col(",
1318
0
      NthType::LastCol => ":nth-last-col(",
1319
    })
1320
0
  }
1321
1322
  /// Serialize <an+b> (part of the CSS Syntax spec, but currently only used here).
1323
  /// <https://drafts.csswg.org/css-syntax-3/#serialize-an-anb-value>
1324
  #[inline]
1325
0
  pub fn write_affine<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
1326
0
    match (self.a, self.b) {
1327
0
      (0, 0) => dest.write_char('0'),
1328
1329
0
      (1, 0) => dest.write_char('n'),
1330
0
      (-1, 0) => dest.write_str("-n"),
1331
0
      (_, 0) => write!(dest, "{}n", self.a),
1332
1333
0
      (2, 1) => dest.write_str("odd"),
1334
1335
0
      (0, _) => write!(dest, "{}", self.b),
1336
0
      (1, _) => write!(dest, "n{:+}", self.b),
1337
0
      (-1, _) => write!(dest, "-n{:+}", self.b),
1338
0
      (_, _) => write!(dest, "{}n{:+}", self.a, self.b),
1339
    }
1340
0
  }
1341
}
1342
1343
/// The properties that comprise an :nth- pseudoclass as of Selectors 4 (e.g.,
1344
/// nth-child(An+B [of S]?)).
1345
/// https://www.w3.org/TR/selectors-4/#nth-child-pseudo
1346
#[derive(Clone, PartialEq, Eq, Hash)]
1347
pub struct NthOfSelectorData<'i, Impl: SelectorImpl<'i>>(NthSelectorData, Box<[Selector<'i, Impl>]>);
1348
1349
#[cfg(feature = "into_owned")]
1350
impl<'any, 'i, Impl: SelectorImpl<'i>, NewSel> static_self::IntoOwned<'any> for NthOfSelectorData<'i, Impl>
1351
where
1352
  Impl: static_self::IntoOwned<'any, Owned = NewSel>,
1353
  NewSel: SelectorImpl<'any>,
1354
  Component<'i, Impl>: static_self::IntoOwned<'any, Owned = Component<'any, NewSel>>,
1355
{
1356
  type Owned = NthOfSelectorData<'any, NewSel>;
1357
1358
  fn into_owned(self) -> Self::Owned {
1359
    NthOfSelectorData(self.0, self.1.into_owned())
1360
  }
1361
}
1362
1363
impl<'i, Impl: SelectorImpl<'i>> NthOfSelectorData<'i, Impl> {
1364
  /// Returns selector data for :nth-{,last-}{child,of-type}(An+B [of S])
1365
  #[inline]
1366
0
  pub fn new(nth_data: NthSelectorData, selectors: Box<[Selector<'i, Impl>]>) -> Self {
1367
0
    Self(nth_data, selectors)
1368
0
  }
Unexecuted instantiation: <parcel_selectors::parser::NthOfSelectorData<lightningcss::selector::private::Selectors>>::new
Unexecuted instantiation: <parcel_selectors::parser::NthOfSelectorData<_>>::new
1369
1370
  /// Returns the An+B part of the selector
1371
  #[inline]
1372
0
  pub fn nth_data(&self) -> &NthSelectorData {
1373
0
    &self.0
1374
0
  }
1375
1376
  /// Returns the selector list part of the selector
1377
  #[inline]
1378
0
  pub fn selectors(&self) -> &[Selector<'i, Impl>] {
1379
0
    &*self.1
1380
0
  }
Unexecuted instantiation: <parcel_selectors::parser::NthOfSelectorData<lightningcss::selector::private::Selectors>>::selectors
Unexecuted instantiation: <parcel_selectors::parser::NthOfSelectorData<_>>::selectors
1381
1382
0
  pub fn clone_selectors(&self) -> Box<[Selector<'i, Impl>]> {
1383
0
    self.1.clone()
1384
0
  }
1385
}
1386
1387
/// A CSS simple selector or combinator. We store both in the same enum for
1388
/// optimal packing and cache performance, see [1].
1389
///
1390
/// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1357973
1391
#[derive(Clone, PartialEq, Eq, Hash)]
1392
pub enum Component<'i, Impl: SelectorImpl<'i>> {
1393
  Combinator(Combinator),
1394
1395
  ExplicitAnyNamespace,
1396
  ExplicitNoNamespace,
1397
  DefaultNamespace(Impl::NamespaceUrl),
1398
  Namespace(Impl::NamespacePrefix, Impl::NamespaceUrl),
1399
1400
  ExplicitUniversalType,
1401
  LocalName(LocalName<'i, Impl>),
1402
1403
  ID(Impl::Identifier),
1404
  Class(Impl::Identifier),
1405
1406
  AttributeInNoNamespaceExists {
1407
    local_name: Impl::LocalName,
1408
    local_name_lower: Impl::LocalName,
1409
  },
1410
  // Used only when local_name is already lowercase.
1411
  AttributeInNoNamespace {
1412
    local_name: Impl::LocalName,
1413
    operator: AttrSelectorOperator,
1414
    value: Impl::AttrValue,
1415
    case_sensitivity: ParsedCaseSensitivity,
1416
    never_matches: bool,
1417
  },
1418
  // Use a Box in the less common cases with more data to keep size_of::<Component>() small.
1419
  AttributeOther(Box<AttrSelectorWithOptionalNamespace<'i, Impl>>),
1420
1421
  /// Pseudo-classes
1422
  Negation(Box<[Selector<'i, Impl>]>),
1423
  Root,
1424
  Empty,
1425
  Scope,
1426
  Nth(NthSelectorData),
1427
  NthOf(NthOfSelectorData<'i, Impl>),
1428
  NonTSPseudoClass(Impl::NonTSPseudoClass),
1429
  /// The ::slotted() pseudo-element:
1430
  ///
1431
  /// https://drafts.csswg.org/css-scoping/#slotted-pseudo
1432
  ///
1433
  /// The selector here is a compound selector, that is, no combinators.
1434
  ///
1435
  /// NOTE(emilio): This should support a list of selectors, but as of this
1436
  /// writing no other browser does, and that allows them to put ::slotted()
1437
  /// in the rule hash, so we do that too.
1438
  ///
1439
  /// See https://github.com/w3c/csswg-drafts/issues/2158
1440
  Slotted(Selector<'i, Impl>),
1441
  /// The `::part` pseudo-element.
1442
  ///   https://drafts.csswg.org/css-shadow-parts/#part
1443
  Part(Box<[Impl::Identifier]>),
1444
  /// The `:host` pseudo-class:
1445
  ///
1446
  /// https://drafts.csswg.org/css-scoping/#host-selector
1447
  ///
1448
  /// NOTE(emilio): This should support a list of selectors, but as of this
1449
  /// writing no other browser does, and that allows them to put :host()
1450
  /// in the rule hash, so we do that too.
1451
  ///
1452
  /// See https://github.com/w3c/csswg-drafts/issues/2158
1453
  Host(Option<Selector<'i, Impl>>),
1454
  /// The `:where` pseudo-class.
1455
  ///
1456
  /// https://drafts.csswg.org/selectors/#zero-matches
1457
  ///
1458
  /// The inner argument is conceptually a SelectorList, but we move the
1459
  /// selectors to the heap to keep Component small.
1460
  Where(Box<[Selector<'i, Impl>]>),
1461
  /// The `:is` pseudo-class.
1462
  ///
1463
  /// https://drafts.csswg.org/selectors/#matches-pseudo
1464
  ///
1465
  /// Same comment as above re. the argument.
1466
  Is(Box<[Selector<'i, Impl>]>),
1467
  Any(Impl::VendorPrefix, Box<[Selector<'i, Impl>]>),
1468
  /// The `:has` pseudo-class.
1469
  ///
1470
  /// https://www.w3.org/TR/selectors/#relational
1471
  Has(Box<[Selector<'i, Impl>]>),
1472
  /// An implementation-dependent pseudo-element selector.
1473
  PseudoElement(Impl::PseudoElement),
1474
  /// A nesting selector:
1475
  ///
1476
  /// https://drafts.csswg.org/css-nesting-1/#nest-selector
1477
  ///
1478
  /// NOTE: This is a lightningcss addition.
1479
  Nesting,
1480
}
1481
1482
#[cfg(feature = "into_owned")]
1483
impl<'any, 'i, Impl: SelectorImpl<'i>, NewSel> static_self::IntoOwned<'any> for Component<'i, Impl>
1484
where
1485
  Impl: static_self::IntoOwned<'any, Owned = NewSel>,
1486
  NewSel: SelectorImpl<'any>,
1487
  Impl::NamespaceUrl: static_self::IntoOwned<'any, Owned = NewSel::NamespaceUrl>,
1488
  Impl::NamespacePrefix: static_self::IntoOwned<'any, Owned = NewSel::NamespacePrefix>,
1489
  Impl::Identifier: static_self::IntoOwned<'any, Owned = NewSel::Identifier>,
1490
  Impl::LocalName: static_self::IntoOwned<'any, Owned = NewSel::LocalName>,
1491
  Impl::AttrValue: static_self::IntoOwned<'any, Owned = NewSel::AttrValue>,
1492
  Impl::NonTSPseudoClass: static_self::IntoOwned<'any, Owned = NewSel::NonTSPseudoClass>,
1493
  Impl::PseudoElement: static_self::IntoOwned<'any, Owned = NewSel::PseudoElement>,
1494
  Impl::VendorPrefix: static_self::IntoOwned<'any, Owned = NewSel::VendorPrefix>,
1495
{
1496
  type Owned = Component<'any, NewSel>;
1497
1498
  fn into_owned(self) -> Self::Owned {
1499
    match self {
1500
      Component::Combinator(c) => Component::Combinator(c.into_owned()),
1501
      Component::ExplicitAnyNamespace => Component::ExplicitAnyNamespace,
1502
      Component::ExplicitNoNamespace => Component::ExplicitNoNamespace,
1503
      Component::DefaultNamespace(c) => Component::DefaultNamespace(c.into_owned()),
1504
      Component::Namespace(a, b) => Component::Namespace(a.into_owned(), b.into_owned()),
1505
      Component::ExplicitUniversalType => Component::ExplicitUniversalType,
1506
      Component::LocalName(c) => Component::LocalName(c.into_owned()),
1507
      Component::ID(c) => Component::ID(c.into_owned()),
1508
      Component::Class(c) => Component::Class(c.into_owned()),
1509
      Component::AttributeInNoNamespaceExists {
1510
        local_name,
1511
        local_name_lower,
1512
      } => Component::AttributeInNoNamespaceExists {
1513
        local_name: local_name.into_owned(),
1514
        local_name_lower: local_name_lower.into_owned(),
1515
      },
1516
      Component::AttributeInNoNamespace {
1517
        local_name,
1518
        operator,
1519
        value,
1520
        case_sensitivity,
1521
        never_matches,
1522
      } => {
1523
        let value = value.into_owned();
1524
        Component::AttributeInNoNamespace {
1525
          local_name: local_name.into_owned(),
1526
          operator,
1527
          value,
1528
          case_sensitivity,
1529
          never_matches,
1530
        }
1531
      }
1532
      Component::AttributeOther(c) => Component::AttributeOther(c.into_owned()),
1533
      Component::Negation(c) => Component::Negation(c.into_owned()),
1534
      Component::Root => Component::Root,
1535
      Component::Empty => Component::Empty,
1536
      Component::Scope => Component::Scope,
1537
      Component::Nth(c) => Component::Nth(c.into_owned()),
1538
      Component::NthOf(c) => Component::NthOf(c.into_owned()),
1539
      Component::NonTSPseudoClass(c) => Component::NonTSPseudoClass(c.into_owned()),
1540
      Component::Slotted(c) => Component::Slotted(c.into_owned()),
1541
      Component::Part(c) => Component::Part(c.into_owned()),
1542
      Component::Host(c) => Component::Host(c.into_owned()),
1543
      Component::Where(c) => Component::Where(c.into_owned()),
1544
      Component::Is(c) => Component::Is(c.into_owned()),
1545
      Component::Any(a, b) => Component::Any(a.into_owned(), b.into_owned()),
1546
      Component::Has(c) => Component::Has(c.into_owned()),
1547
      Component::PseudoElement(c) => Component::PseudoElement(c.into_owned()),
1548
      Component::Nesting => Component::Nesting,
1549
    }
1550
  }
1551
}
1552
1553
impl<'i, Impl: SelectorImpl<'i>> Component<'i, Impl> {
1554
  /// Returns true if this is a combinator.
1555
2.91M
  pub fn is_combinator(&self) -> bool {
1556
2.91M
    matches!(*self, Component::Combinator(_))
1557
2.91M
  }
<parcel_selectors::parser::Component<lightningcss::selector::private::Selectors>>::is_combinator
Line
Count
Source
1555
2.91M
  pub fn is_combinator(&self) -> bool {
1556
2.91M
    matches!(*self, Component::Combinator(_))
1557
2.91M
  }
Unexecuted instantiation: <parcel_selectors::parser::Component<_>>::is_combinator
1558
1559
  /// Returns the value as a combinator if applicable, None otherwise.
1560
0
  pub fn as_combinator(&self) -> Option<Combinator> {
1561
0
    match *self {
1562
0
      Component::Combinator(c) => Some(c),
1563
0
      _ => None,
1564
    }
1565
0
  }
Unexecuted instantiation: <parcel_selectors::parser::Component<lightningcss::selector::private::Selectors>>::as_combinator
Unexecuted instantiation: <parcel_selectors::parser::Component<_>>::as_combinator
1566
1567
  /// Whether this component is valid after a pseudo-element. Only intended
1568
  /// for sanity-checking.
1569
0
  pub fn maybe_allowed_after_pseudo_element(&self) -> bool {
1570
0
    match *self {
1571
0
      Component::NonTSPseudoClass(..) => true,
1572
0
      Component::Negation(ref selectors) | Component::Is(ref selectors) | Component::Where(ref selectors) => {
1573
0
        selectors
1574
0
          .iter()
1575
0
          .all(|selector| selector.iter_raw_match_order().all(|c| c.maybe_allowed_after_pseudo_element()))
1576
      }
1577
0
      _ => false,
1578
    }
1579
0
  }
1580
1581
  /// Whether a given selector should match for stateless pseudo-elements.
1582
  ///
1583
  /// This is a bit subtle: Only selectors that return true in
1584
  /// `maybe_allowed_after_pseudo_element` should end up here, and
1585
  /// `NonTSPseudoClass` never matches (as it is a stateless pseudo after
1586
  /// all).
1587
0
  fn matches_for_stateless_pseudo_element(&self) -> bool {
1588
0
    debug_assert!(
1589
0
      self.maybe_allowed_after_pseudo_element(),
1590
      "Someone messed up pseudo-element parsing: {:?}",
1591
      *self
1592
    );
1593
0
    match *self {
1594
0
      Component::Negation(ref selectors) => !selectors.iter().all(|selector| {
1595
0
        selector
1596
0
          .iter_raw_match_order()
1597
0
          .all(|c| c.matches_for_stateless_pseudo_element())
1598
0
      }),
1599
0
      Component::Is(ref selectors) | Component::Where(ref selectors) => selectors.iter().any(|selector| {
1600
0
        selector
1601
0
          .iter_raw_match_order()
1602
0
          .all(|c| c.matches_for_stateless_pseudo_element())
1603
0
      }),
1604
0
      _ => false,
1605
    }
1606
0
  }
1607
1608
0
  pub fn visit<V>(&self, visitor: &mut V) -> bool
1609
0
  where
1610
0
    V: SelectorVisitor<'i, Impl = Impl>,
1611
  {
1612
    use self::Component::*;
1613
0
    if !visitor.visit_simple_selector(self) {
1614
0
      return false;
1615
0
    }
1616
1617
0
    match *self {
1618
0
      Slotted(ref selector) => {
1619
0
        if !selector.visit(visitor) {
1620
0
          return false;
1621
0
        }
1622
      }
1623
0
      Host(Some(ref selector)) => {
1624
0
        if !selector.visit(visitor) {
1625
0
          return false;
1626
0
        }
1627
      }
1628
      AttributeInNoNamespaceExists {
1629
0
        ref local_name,
1630
0
        ref local_name_lower,
1631
      } => {
1632
0
        if !visitor.visit_attribute_selector(
1633
0
          &NamespaceConstraint::Specific(&namespace_empty_string::<Impl>()),
1634
0
          local_name,
1635
0
          local_name_lower,
1636
0
        ) {
1637
0
          return false;
1638
0
        }
1639
      }
1640
      AttributeInNoNamespace {
1641
0
        ref local_name,
1642
0
        never_matches,
1643
        ..
1644
0
      } if !never_matches => {
1645
0
        if !visitor.visit_attribute_selector(
1646
0
          &NamespaceConstraint::Specific(&namespace_empty_string::<Impl>()),
1647
0
          local_name,
1648
0
          local_name,
1649
0
        ) {
1650
0
          return false;
1651
0
        }
1652
      }
1653
0
      AttributeOther(ref attr_selector) if !attr_selector.never_matches => {
1654
        let empty_string;
1655
0
        let namespace = match attr_selector.namespace() {
1656
0
          Some(ns) => ns,
1657
          None => {
1658
0
            empty_string = crate::parser::namespace_empty_string::<Impl>();
1659
0
            NamespaceConstraint::Specific(&empty_string)
1660
          }
1661
        };
1662
0
        if !visitor.visit_attribute_selector(
1663
0
          &namespace,
1664
0
          &attr_selector.local_name,
1665
0
          &attr_selector.local_name_lower,
1666
0
        ) {
1667
0
          return false;
1668
0
        }
1669
      }
1670
1671
0
      NonTSPseudoClass(ref pseudo_class) => {
1672
0
        if !pseudo_class.visit(visitor) {
1673
0
          return false;
1674
0
        }
1675
      }
1676
1677
0
      Negation(ref list) | Is(ref list) | Where(ref list) => {
1678
0
        if !visitor.visit_selector_list(&list) {
1679
0
          return false;
1680
0
        }
1681
      }
1682
0
      NthOf(ref nth_of_data) => {
1683
0
        if !visitor.visit_selector_list(nth_of_data.selectors()) {
1684
0
          return false;
1685
0
        }
1686
      }
1687
0
      _ => {}
1688
    }
1689
1690
0
    true
1691
0
  }
1692
}
1693
1694
#[derive(Clone, Eq, PartialEq, Hash)]
1695
pub struct LocalName<'i, Impl: SelectorImpl<'i>> {
1696
  pub name: Impl::LocalName,
1697
  pub lower_name: Impl::LocalName,
1698
}
1699
1700
#[cfg(feature = "into_owned")]
1701
impl<'any, 'i, Impl: SelectorImpl<'i>, NewSel> static_self::IntoOwned<'any> for LocalName<'i, Impl>
1702
where
1703
  Impl: static_self::IntoOwned<'any, Owned = NewSel>,
1704
  NewSel: SelectorImpl<'any>,
1705
  Impl::LocalName: static_self::IntoOwned<'any, Owned = NewSel::LocalName>,
1706
{
1707
  type Owned = LocalName<'any, NewSel>;
1708
1709
  fn into_owned(self) -> Self::Owned {
1710
    LocalName {
1711
      name: self.name.into_owned(),
1712
      lower_name: self.lower_name.into_owned(),
1713
    }
1714
  }
1715
}
1716
1717
impl<'i, Impl: SelectorImpl<'i>> Debug for Selector<'i, Impl> {
1718
0
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1719
0
    f.write_str("Selector(")?;
1720
0
    self.to_css(f)?;
1721
0
    write!(f, ", specificity = 0x{:x})", self.specificity())
1722
0
  }
1723
}
1724
1725
impl<'i, Impl: SelectorImpl<'i>> Debug for Component<'i, Impl> {
1726
0
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1727
0
    self.to_css(f)
1728
0
  }
1729
}
1730
impl<'i, Impl: SelectorImpl<'i>> Debug for AttrSelectorWithOptionalNamespace<'i, Impl> {
1731
0
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1732
0
    self.to_css(f)
1733
0
  }
1734
}
1735
impl<'i, Impl: SelectorImpl<'i>> Debug for LocalName<'i, Impl> {
1736
0
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1737
0
    self.to_css(f)
1738
0
  }
1739
}
1740
1741
#[cfg(feature = "serde")]
1742
impl<'i, Impl: SelectorImpl<'i>> serde::Serialize for LocalName<'i, Impl>
1743
where
1744
  Impl::LocalName: serde::Serialize,
1745
{
1746
  fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1747
  where
1748
    S: serde::Serializer,
1749
  {
1750
    self.name.serialize(serializer)
1751
  }
1752
}
1753
1754
#[cfg(feature = "serde")]
1755
impl<'i, 'de: 'i, Impl: SelectorImpl<'i>> serde::Deserialize<'de> for LocalName<'i, Impl>
1756
where
1757
  Impl::LocalName: serde::Deserialize<'de>,
1758
{
1759
  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1760
  where
1761
    D: serde::Deserializer<'de>,
1762
  {
1763
    let name = Impl::LocalName::deserialize(deserializer)?;
1764
    let lower_name = to_ascii_lowercase(name.as_ref().to_string().into()).into();
1765
    Ok(LocalName { name, lower_name })
1766
  }
1767
}
1768
1769
0
fn serialize_selector_list<'a, 'i: 'a, Impl, I, W>(iter: I, dest: &mut W) -> fmt::Result
1770
0
where
1771
0
  Impl: SelectorImpl<'i>,
1772
0
  I: Iterator<Item = &'a Selector<'i, Impl>>,
1773
0
  W: fmt::Write,
1774
{
1775
0
  let mut first = true;
1776
0
  for selector in iter {
1777
0
    if !first {
1778
0
      dest.write_str(", ")?;
1779
0
    }
1780
0
    first = false;
1781
0
    selector.to_css(dest)?;
1782
  }
1783
0
  Ok(())
1784
0
}
1785
1786
impl<'i, Impl: SelectorImpl<'i>> ToCss for SelectorList<'i, Impl> {
1787
0
  fn to_css<W>(&self, dest: &mut W) -> fmt::Result
1788
0
  where
1789
0
    W: fmt::Write,
1790
  {
1791
0
    serialize_selector_list(self.0.iter(), dest)
1792
0
  }
1793
}
1794
1795
impl<'i, Impl: SelectorImpl<'i>> fmt::Display for SelectorList<'i, Impl> {
1796
0
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
1797
0
    Impl::to_css(self, f)
1798
0
  }
1799
}
1800
1801
impl<'i, Impl: SelectorImpl<'i>> ToCss for Selector<'i, Impl> {
1802
0
  fn to_css<W>(&self, dest: &mut W) -> fmt::Result
1803
0
  where
1804
0
    W: fmt::Write,
1805
  {
1806
    // Compound selectors invert the order of their contents, so we need to
1807
    // undo that during serialization.
1808
    //
1809
    // This two-iterator strategy involves walking over the selector twice.
1810
    // We could do something more clever, but selector serialization probably
1811
    // isn't hot enough to justify it, and the stringification likely
1812
    // dominates anyway.
1813
    //
1814
    // NB: A parse-order iterator is a Rev<>, which doesn't expose as_slice(),
1815
    // which we need for |split|. So we split by combinators on a match-order
1816
    // sequence and then reverse.
1817
1818
0
    let mut combinators = self.iter_raw_match_order().rev().filter_map(|x| x.as_combinator());
1819
0
    let compound_selectors = self.iter_raw_match_order().as_slice().split(|x| x.is_combinator()).rev();
1820
1821
0
    let mut combinators_exhausted = false;
1822
0
    for compound in compound_selectors {
1823
0
      debug_assert!(!combinators_exhausted);
1824
1825
      // https://drafts.csswg.org/cssom/#serializing-selectors
1826
0
      if compound.is_empty() {
1827
0
        continue;
1828
0
      }
1829
1830
      // 1. If there is only one simple selector in the compound selectors
1831
      //    which is a universal selector, append the result of
1832
      //    serializing the universal selector to s.
1833
      //
1834
      // Check if `!compound.empty()` first--this can happen if we have
1835
      // something like `... > ::before`, because we store `>` and `::`
1836
      // both as combinators internally.
1837
      //
1838
      // If we are in this case, after we have serialized the universal
1839
      // selector, we skip Step 2 and continue with the algorithm.
1840
0
      let (can_elide_namespace, first_non_namespace) = match compound[0] {
1841
0
        Component::ExplicitAnyNamespace | Component::ExplicitNoNamespace | Component::Namespace(..) => (false, 1),
1842
0
        Component::DefaultNamespace(..) => (true, 1),
1843
0
        _ => (true, 0),
1844
      };
1845
0
      let mut perform_step_2 = true;
1846
0
      let next_combinator = combinators.next();
1847
0
      if first_non_namespace == compound.len() - 1 {
1848
0
        match (next_combinator, &compound[first_non_namespace]) {
1849
          // We have to be careful here, because if there is a
1850
          // pseudo element "combinator" there isn't really just
1851
          // the one simple selector. Technically this compound
1852
          // selector contains the pseudo element selector as well
1853
          // -- Combinator::PseudoElement, just like
1854
          // Combinator::SlotAssignment, don't exist in the
1855
          // spec.
1856
0
          (Some(Combinator::PseudoElement), _) | (Some(Combinator::SlotAssignment), _) => (),
1857
          (_, &Component::ExplicitUniversalType) => {
1858
            // Iterate over everything so we serialize the namespace
1859
            // too.
1860
0
            for simple in compound.iter() {
1861
0
              simple.to_css(dest)?;
1862
            }
1863
            // Skip step 2, which is an "otherwise".
1864
0
            perform_step_2 = false;
1865
          }
1866
0
          _ => (),
1867
        }
1868
0
      }
1869
1870
      // 2. Otherwise, for each simple selector in the compound selectors
1871
      //    that is not a universal selector of which the namespace prefix
1872
      //    maps to a namespace that is not the default namespace
1873
      //    serialize the simple selector and append the result to s.
1874
      //
1875
      // See https://github.com/w3c/csswg-drafts/issues/1606, which is
1876
      // proposing to change this to match up with the behavior asserted
1877
      // in cssom/serialize-namespaced-type-selectors.html, which the
1878
      // following code tries to match.
1879
0
      if perform_step_2 {
1880
0
        for simple in compound.iter() {
1881
0
          if let Component::ExplicitUniversalType = *simple {
1882
            // Can't have a namespace followed by a pseudo-element
1883
            // selector followed by a universal selector in the same
1884
            // compound selector, so we don't have to worry about the
1885
            // real namespace being in a different `compound`.
1886
0
            if can_elide_namespace {
1887
0
              continue;
1888
0
            }
1889
0
          }
1890
0
          simple.to_css(dest)?;
1891
        }
1892
0
      }
1893
1894
      // 3. If this is not the last part of the chain of the selector
1895
      //    append a single SPACE (U+0020), followed by the combinator
1896
      //    ">", "+", "~", ">>", "||", as appropriate, followed by another
1897
      //    single SPACE (U+0020) if the combinator was not whitespace, to
1898
      //    s.
1899
0
      match next_combinator {
1900
0
        Some(c) => c.to_css(dest)?,
1901
0
        None => combinators_exhausted = true,
1902
      };
1903
1904
      // 4. If this is the last part of the chain of the selector and
1905
      //    there is a pseudo-element, append "::" followed by the name of
1906
      //    the pseudo-element, to s.
1907
      //
1908
      // (we handle this above)
1909
    }
1910
1911
0
    Ok(())
1912
0
  }
1913
}
1914
1915
impl ToCss for Combinator {
1916
0
  fn to_css<W>(&self, dest: &mut W) -> fmt::Result
1917
0
  where
1918
0
    W: fmt::Write,
1919
  {
1920
0
    match *self {
1921
0
      Combinator::Child => dest.write_str(" > "),
1922
0
      Combinator::Descendant => dest.write_str(" "),
1923
0
      Combinator::NextSibling => dest.write_str(" + "),
1924
0
      Combinator::LaterSibling => dest.write_str(" ~ "),
1925
0
      Combinator::DeepDescendant => dest.write_str(" >>> "),
1926
0
      Combinator::Deep => dest.write_str(" /deep/ "),
1927
0
      Combinator::PseudoElement | Combinator::Part | Combinator::SlotAssignment => Ok(()),
1928
    }
1929
0
  }
1930
}
1931
1932
impl<'i, Impl: SelectorImpl<'i>> ToCss for Component<'i, Impl> {
1933
0
  fn to_css<W>(&self, dest: &mut W) -> fmt::Result
1934
0
  where
1935
0
    W: fmt::Write,
1936
  {
1937
    use self::Component::*;
1938
1939
0
    match *self {
1940
0
      Combinator(ref c) => c.to_css(dest),
1941
0
      Slotted(ref selector) => {
1942
0
        dest.write_str("::slotted(")?;
1943
0
        selector.to_css(dest)?;
1944
0
        dest.write_char(')')
1945
      }
1946
0
      Part(ref part_names) => {
1947
0
        dest.write_str("::part(")?;
1948
0
        for (i, name) in part_names.iter().enumerate() {
1949
0
          if i != 0 {
1950
0
            dest.write_char(' ')?;
1951
0
          }
1952
0
          name.to_css(dest)?;
1953
        }
1954
0
        dest.write_char(')')
1955
      }
1956
0
      PseudoElement(ref p) => p.to_css(dest),
1957
0
      ID(ref s) => {
1958
0
        dest.write_char('#')?;
1959
0
        s.to_css(dest)
1960
      }
1961
0
      Class(ref s) => {
1962
0
        dest.write_char('.')?;
1963
0
        s.to_css(dest)
1964
      }
1965
0
      LocalName(ref s) => s.to_css(dest),
1966
0
      ExplicitUniversalType => dest.write_char('*'),
1967
1968
0
      DefaultNamespace(_) => Ok(()),
1969
0
      ExplicitNoNamespace => dest.write_char('|'),
1970
0
      ExplicitAnyNamespace => dest.write_str("*|"),
1971
0
      Namespace(ref prefix, _) => {
1972
0
        prefix.to_css(dest)?;
1973
0
        dest.write_char('|')
1974
      }
1975
1976
0
      AttributeInNoNamespaceExists { ref local_name, .. } => {
1977
0
        dest.write_char('[')?;
1978
0
        local_name.to_css(dest)?;
1979
0
        dest.write_char(']')
1980
      }
1981
      AttributeInNoNamespace {
1982
0
        ref local_name,
1983
0
        operator,
1984
0
        ref value,
1985
0
        case_sensitivity,
1986
        ..
1987
      } => {
1988
0
        dest.write_char('[')?;
1989
0
        local_name.to_css(dest)?;
1990
0
        operator.to_css(dest)?;
1991
0
        value.to_css(dest)?;
1992
0
        match case_sensitivity {
1993
          ParsedCaseSensitivity::CaseSensitive
1994
0
          | ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {}
1995
0
          ParsedCaseSensitivity::AsciiCaseInsensitive => dest.write_str(" i")?,
1996
0
          ParsedCaseSensitivity::ExplicitCaseSensitive => dest.write_str(" s")?,
1997
        }
1998
0
        dest.write_char(']')
1999
      }
2000
0
      AttributeOther(ref attr_selector) => attr_selector.to_css(dest),
2001
2002
      // Pseudo-classes
2003
0
      Root => dest.write_str(":root"),
2004
0
      Empty => dest.write_str(":empty"),
2005
0
      Scope => dest.write_str(":scope"),
2006
0
      Host(ref selector) => {
2007
0
        dest.write_str(":host")?;
2008
0
        if let Some(ref selector) = *selector {
2009
0
          dest.write_char('(')?;
2010
0
          selector.to_css(dest)?;
2011
0
          dest.write_char(')')?;
2012
0
        }
2013
0
        Ok(())
2014
      }
2015
0
      Nth(ref nth_data) => {
2016
0
        nth_data.write_start(dest, nth_data.is_function())?;
2017
0
        if nth_data.is_function() {
2018
0
          nth_data.write_affine(dest)?;
2019
0
          dest.write_char(')')?;
2020
0
        }
2021
0
        Ok(())
2022
      }
2023
0
      NthOf(ref nth_of_data) => {
2024
0
        let nth_data = nth_of_data.nth_data();
2025
0
        nth_data.write_start(dest, true)?;
2026
0
        debug_assert!(
2027
0
          nth_data.is_function,
2028
          "A selector must be a function to hold An+B notation"
2029
        );
2030
0
        nth_data.write_affine(dest)?;
2031
0
        debug_assert!(
2032
0
          matches!(nth_data.ty, NthType::Child | NthType::LastChild),
2033
          "Only :nth-child or :nth-last-child can be of a selector list"
2034
        );
2035
0
        debug_assert!(
2036
0
          !nth_of_data.selectors().is_empty(),
2037
          "The selector list should not be empty"
2038
        );
2039
0
        dest.write_str(" of ")?;
2040
0
        serialize_selector_list(nth_of_data.selectors().iter(), dest)?;
2041
0
        dest.write_char(')')
2042
      }
2043
0
      Is(ref list) | Where(ref list) | Negation(ref list) | Has(ref list) | Any(_, ref list) => {
2044
0
        match *self {
2045
0
          Where(..) => dest.write_str(":where(")?,
2046
0
          Is(..) => dest.write_str(":is(")?,
2047
0
          Negation(..) => dest.write_str(":not(")?,
2048
0
          Has(..) => dest.write_str(":has(")?,
2049
0
          Any(ref prefix, _) => {
2050
0
            dest.write_char(':')?;
2051
0
            prefix.to_css(dest)?;
2052
0
            dest.write_str("any(")?;
2053
          }
2054
0
          _ => unreachable!(),
2055
        }
2056
0
        serialize_selector_list(list.iter(), dest)?;
2057
0
        dest.write_str(")")
2058
      }
2059
0
      NonTSPseudoClass(ref pseudo) => pseudo.to_css(dest),
2060
0
      Nesting => dest.write_char('&'),
2061
    }
2062
0
  }
2063
}
2064
2065
impl<'i, Impl: SelectorImpl<'i>> ToCss for AttrSelectorWithOptionalNamespace<'i, Impl> {
2066
0
  fn to_css<W>(&self, dest: &mut W) -> fmt::Result
2067
0
  where
2068
0
    W: fmt::Write,
2069
  {
2070
0
    dest.write_char('[')?;
2071
0
    match self.namespace {
2072
0
      Some(NamespaceConstraint::Specific((ref prefix, _))) => {
2073
0
        prefix.to_css(dest)?;
2074
0
        dest.write_char('|')?
2075
      }
2076
0
      Some(NamespaceConstraint::Any) => dest.write_str("*|")?,
2077
0
      None => {}
2078
    }
2079
0
    self.local_name.to_css(dest)?;
2080
0
    match self.operation {
2081
0
      ParsedAttrSelectorOperation::Exists => {}
2082
      ParsedAttrSelectorOperation::WithValue {
2083
0
        operator,
2084
0
        case_sensitivity,
2085
0
        ref expected_value,
2086
      } => {
2087
0
        operator.to_css(dest)?;
2088
0
        expected_value.to_css(dest)?;
2089
0
        match case_sensitivity {
2090
          ParsedCaseSensitivity::CaseSensitive
2091
0
          | ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {}
2092
0
          ParsedCaseSensitivity::AsciiCaseInsensitive => dest.write_str(" i")?,
2093
0
          ParsedCaseSensitivity::ExplicitCaseSensitive => dest.write_str(" s")?,
2094
        }
2095
      }
2096
    }
2097
0
    dest.write_char(']')
2098
0
  }
2099
}
2100
2101
impl<'i, Impl: SelectorImpl<'i>> ToCss for LocalName<'i, Impl> {
2102
0
  fn to_css<W>(&self, dest: &mut W) -> fmt::Result
2103
0
  where
2104
0
    W: fmt::Write,
2105
  {
2106
0
    self.name.to_css(dest)
2107
0
  }
2108
}
2109
2110
/// Build up a Selector.
2111
/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ;
2112
///
2113
/// `Err` means invalid selector.
2114
1.90M
fn parse_selector<'i, 't, P, Impl>(
2115
1.90M
  parser: &P,
2116
1.90M
  input: &mut CssParser<'i, 't>,
2117
1.90M
  state: &mut SelectorParsingState,
2118
1.90M
  nesting_requirement: NestingRequirement,
2119
1.90M
) -> Result<Selector<'i, Impl>, ParseError<'i, P::Error>>
2120
1.90M
where
2121
1.90M
  P: Parser<'i, Impl = Impl>,
2122
1.90M
  Impl: SelectorImpl<'i>,
2123
{
2124
1.90M
  if nesting_requirement == NestingRequirement::Prefixed {
2125
0
    let state = input.state();
2126
0
    if !input.expect_delim('&').is_ok() {
2127
0
      return Err(input.new_custom_error(SelectorParseErrorKind::MissingNestingPrefix));
2128
0
    }
2129
0
    input.reset(&state);
2130
1.90M
  }
2131
2132
1.90M
  let mut builder = SelectorBuilder::default();
2133
2134
  'outer_loop: loop {
2135
    // Parse a sequence of simple selectors.
2136
2.48M
    let empty = parse_compound_selector(parser, state, input, &mut builder)?;
2137
2.31M
    if empty {
2138
165k
      return Err(input.new_custom_error(if builder.has_combinators() {
2139
40.6k
        SelectorParseErrorKind::DanglingCombinator
2140
      } else {
2141
124k
        SelectorParseErrorKind::EmptySelector
2142
      }));
2143
2.14M
    }
2144
2145
2.14M
    if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
2146
      // Input should be exhausted here.
2147
96
      let source_location = input.current_source_location();
2148
96
      if let Ok(next) = input.next() {
2149
48
        let next = next.clone();
2150
48
        return Err(
2151
48
          source_location.new_custom_error(SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(next)),
2152
48
        );
2153
48
      }
2154
48
      break;
2155
2.14M
    }
2156
2157
    // Parse a combinator.
2158
    let combinator;
2159
2.14M
    let mut any_whitespace = false;
2160
    loop {
2161
3.01M
      let before_this_token = input.state();
2162
3.01M
      match input.next_including_whitespace() {
2163
1.35M
        Err(_e) => break 'outer_loop,
2164
866k
        Ok(&Token::WhiteSpace(_)) => any_whitespace = true,
2165
        Ok(&Token::Delim('>')) => {
2166
22.0k
          if parser.deep_combinator_enabled()
2167
0
            && input
2168
0
              .try_parse(|input| {
2169
0
                input.expect_delim('>')?;
2170
0
                input.expect_delim('>')
2171
0
              })
Unexecuted instantiation: parcel_selectors::parser::parse_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#0}
Unexecuted instantiation: parcel_selectors::parser::parse_selector::<_, _>::{closure#0}
2172
0
              .is_ok()
2173
0
          {
2174
0
            combinator = Combinator::DeepDescendant;
2175
22.0k
          } else {
2176
22.0k
            combinator = Combinator::Child;
2177
22.0k
          }
2178
22.0k
          break;
2179
        }
2180
        Ok(&Token::Delim('+')) => {
2181
9.24k
          combinator = Combinator::NextSibling;
2182
9.24k
          break;
2183
        }
2184
        Ok(&Token::Delim('~')) => {
2185
30.1k
          combinator = Combinator::LaterSibling;
2186
30.1k
          break;
2187
        }
2188
7.46k
        Ok(&Token::Delim('/')) if parser.deep_combinator_enabled() => {
2189
0
          if input
2190
0
            .try_parse(|input| {
2191
0
              input.expect_ident_matching("deep")?;
2192
0
              input.expect_delim('/')
2193
0
            })
Unexecuted instantiation: parcel_selectors::parser::parse_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#1}
Unexecuted instantiation: parcel_selectors::parser::parse_selector::<_, _>::{closure#1}
2194
0
            .is_ok()
2195
          {
2196
0
            combinator = Combinator::Deep;
2197
0
            break;
2198
          } else {
2199
0
            break 'outer_loop;
2200
          }
2201
        }
2202
        Ok(_) => {
2203
729k
          input.reset(&before_this_token);
2204
729k
          if any_whitespace {
2205
520k
            combinator = Combinator::Descendant;
2206
520k
            break;
2207
          } else {
2208
208k
            break 'outer_loop;
2209
          }
2210
        }
2211
      }
2212
    }
2213
2214
581k
    if !state.allows_combinators() {
2215
0
      return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
2216
581k
    }
2217
2218
581k
    builder.push_combinator(combinator);
2219
  }
2220
2221
1.56M
  if !state.contains(SelectorParsingState::AFTER_NESTING) {
2222
1.53M
    match nesting_requirement {
2223
1.33M
      NestingRequirement::Implicit => {
2224
1.33M
        builder.add_nesting_prefix();
2225
1.33M
      }
2226
      NestingRequirement::Contained | NestingRequirement::Prefixed => {
2227
72
        return Err(input.new_custom_error(SelectorParseErrorKind::MissingNestingSelector));
2228
      }
2229
198k
      _ => {}
2230
    }
2231
35.5k
  }
2232
2233
1.56M
  let has_pseudo_element = state
2234
1.56M
    .intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT | SelectorParsingState::AFTER_UNKNOWN_PSEUDO_ELEMENT);
2235
1.56M
  let slotted = state.intersects(SelectorParsingState::AFTER_SLOTTED);
2236
1.56M
  let part = state.intersects(SelectorParsingState::AFTER_PART);
2237
1.56M
  let (spec, components) = builder.build(has_pseudo_element, slotted, part);
2238
1.56M
  Ok(Selector(spec, components))
2239
1.90M
}
parcel_selectors::parser::parse_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>
Line
Count
Source
2114
1.90M
fn parse_selector<'i, 't, P, Impl>(
2115
1.90M
  parser: &P,
2116
1.90M
  input: &mut CssParser<'i, 't>,
2117
1.90M
  state: &mut SelectorParsingState,
2118
1.90M
  nesting_requirement: NestingRequirement,
2119
1.90M
) -> Result<Selector<'i, Impl>, ParseError<'i, P::Error>>
2120
1.90M
where
2121
1.90M
  P: Parser<'i, Impl = Impl>,
2122
1.90M
  Impl: SelectorImpl<'i>,
2123
{
2124
1.90M
  if nesting_requirement == NestingRequirement::Prefixed {
2125
0
    let state = input.state();
2126
0
    if !input.expect_delim('&').is_ok() {
2127
0
      return Err(input.new_custom_error(SelectorParseErrorKind::MissingNestingPrefix));
2128
0
    }
2129
0
    input.reset(&state);
2130
1.90M
  }
2131
2132
1.90M
  let mut builder = SelectorBuilder::default();
2133
2134
  'outer_loop: loop {
2135
    // Parse a sequence of simple selectors.
2136
2.48M
    let empty = parse_compound_selector(parser, state, input, &mut builder)?;
2137
2.31M
    if empty {
2138
165k
      return Err(input.new_custom_error(if builder.has_combinators() {
2139
40.6k
        SelectorParseErrorKind::DanglingCombinator
2140
      } else {
2141
124k
        SelectorParseErrorKind::EmptySelector
2142
      }));
2143
2.14M
    }
2144
2145
2.14M
    if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
2146
      // Input should be exhausted here.
2147
96
      let source_location = input.current_source_location();
2148
96
      if let Ok(next) = input.next() {
2149
48
        let next = next.clone();
2150
48
        return Err(
2151
48
          source_location.new_custom_error(SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(next)),
2152
48
        );
2153
48
      }
2154
48
      break;
2155
2.14M
    }
2156
2157
    // Parse a combinator.
2158
    let combinator;
2159
2.14M
    let mut any_whitespace = false;
2160
    loop {
2161
3.01M
      let before_this_token = input.state();
2162
3.01M
      match input.next_including_whitespace() {
2163
1.35M
        Err(_e) => break 'outer_loop,
2164
866k
        Ok(&Token::WhiteSpace(_)) => any_whitespace = true,
2165
        Ok(&Token::Delim('>')) => {
2166
22.0k
          if parser.deep_combinator_enabled()
2167
0
            && input
2168
0
              .try_parse(|input| {
2169
                input.expect_delim('>')?;
2170
                input.expect_delim('>')
2171
              })
2172
0
              .is_ok()
2173
0
          {
2174
0
            combinator = Combinator::DeepDescendant;
2175
22.0k
          } else {
2176
22.0k
            combinator = Combinator::Child;
2177
22.0k
          }
2178
22.0k
          break;
2179
        }
2180
        Ok(&Token::Delim('+')) => {
2181
9.24k
          combinator = Combinator::NextSibling;
2182
9.24k
          break;
2183
        }
2184
        Ok(&Token::Delim('~')) => {
2185
30.1k
          combinator = Combinator::LaterSibling;
2186
30.1k
          break;
2187
        }
2188
7.46k
        Ok(&Token::Delim('/')) if parser.deep_combinator_enabled() => {
2189
0
          if input
2190
0
            .try_parse(|input| {
2191
              input.expect_ident_matching("deep")?;
2192
              input.expect_delim('/')
2193
            })
2194
0
            .is_ok()
2195
          {
2196
0
            combinator = Combinator::Deep;
2197
0
            break;
2198
          } else {
2199
0
            break 'outer_loop;
2200
          }
2201
        }
2202
        Ok(_) => {
2203
729k
          input.reset(&before_this_token);
2204
729k
          if any_whitespace {
2205
520k
            combinator = Combinator::Descendant;
2206
520k
            break;
2207
          } else {
2208
208k
            break 'outer_loop;
2209
          }
2210
        }
2211
      }
2212
    }
2213
2214
581k
    if !state.allows_combinators() {
2215
0
      return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
2216
581k
    }
2217
2218
581k
    builder.push_combinator(combinator);
2219
  }
2220
2221
1.56M
  if !state.contains(SelectorParsingState::AFTER_NESTING) {
2222
1.53M
    match nesting_requirement {
2223
1.33M
      NestingRequirement::Implicit => {
2224
1.33M
        builder.add_nesting_prefix();
2225
1.33M
      }
2226
      NestingRequirement::Contained | NestingRequirement::Prefixed => {
2227
72
        return Err(input.new_custom_error(SelectorParseErrorKind::MissingNestingSelector));
2228
      }
2229
198k
      _ => {}
2230
    }
2231
35.5k
  }
2232
2233
1.56M
  let has_pseudo_element = state
2234
1.56M
    .intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT | SelectorParsingState::AFTER_UNKNOWN_PSEUDO_ELEMENT);
2235
1.56M
  let slotted = state.intersects(SelectorParsingState::AFTER_SLOTTED);
2236
1.56M
  let part = state.intersects(SelectorParsingState::AFTER_PART);
2237
1.56M
  let (spec, components) = builder.build(has_pseudo_element, slotted, part);
2238
1.56M
  Ok(Selector(spec, components))
2239
1.90M
}
Unexecuted instantiation: parcel_selectors::parser::parse_selector::<_, _>
2240
2241
impl<'i, Impl: SelectorImpl<'i>> Selector<'i, Impl> {
2242
  /// Parse a selector, without any pseudo-element.
2243
  #[inline]
2244
315
  pub fn parse<'t, P>(parser: &P, input: &mut CssParser<'i, 't>) -> Result<Self, ParseError<'i, P::Error>>
2245
315
  where
2246
315
    P: Parser<'i, Impl = Impl>,
2247
  {
2248
315
    parse_selector(
2249
315
      parser,
2250
315
      input,
2251
315
      &mut SelectorParsingState::empty(),
2252
315
      NestingRequirement::None,
2253
    )
2254
315
  }
<parcel_selectors::parser::Selector<lightningcss::selector::private::Selectors>>::parse::<lightningcss::selector::SelectorParser>
Line
Count
Source
2244
315
  pub fn parse<'t, P>(parser: &P, input: &mut CssParser<'i, 't>) -> Result<Self, ParseError<'i, P::Error>>
2245
315
  where
2246
315
    P: Parser<'i, Impl = Impl>,
2247
  {
2248
315
    parse_selector(
2249
315
      parser,
2250
315
      input,
2251
315
      &mut SelectorParsingState::empty(),
2252
315
      NestingRequirement::None,
2253
    )
2254
315
  }
Unexecuted instantiation: <parcel_selectors::parser::Selector<_>>::parse::<_>
2255
}
2256
2257
1.56M
fn parse_relative_selector<'i, 't, P, Impl>(
2258
1.56M
  parser: &P,
2259
1.56M
  input: &mut CssParser<'i, 't>,
2260
1.56M
  state: &mut SelectorParsingState,
2261
1.56M
  mut nesting_requirement: NestingRequirement,
2262
1.56M
) -> Result<Selector<'i, Impl>, ParseError<'i, P::Error>>
2263
1.56M
where
2264
1.56M
  P: Parser<'i, Impl = Impl>,
2265
1.56M
  Impl: SelectorImpl<'i>,
2266
{
2267
  // https://www.w3.org/TR/selectors-4/#parse-relative-selector
2268
1.56M
  let s = input.state();
2269
1.56M
  let combinator = match input.next()? {
2270
779
    Token::Delim('>') => Some(Combinator::Child),
2271
1.20k
    Token::Delim('+') => Some(Combinator::NextSibling),
2272
368
    Token::Delim('~') => Some(Combinator::LaterSibling),
2273
    _ => {
2274
1.53M
      input.reset(&s);
2275
1.53M
      None
2276
    }
2277
  };
2278
2279
1.54M
  let scope = if nesting_requirement == NestingRequirement::Implicit {
2280
1.54M
    Component::Nesting
2281
  } else {
2282
124
    Component::Scope
2283
  };
2284
2285
1.54M
  if combinator.is_some() {
2286
2.34k
    nesting_requirement = NestingRequirement::None;
2287
1.53M
  }
2288
2289
1.54M
  let mut selector = parse_selector(parser, input, state, nesting_requirement)?;
2290
1.35M
  if let Some(combinator) = combinator {
2291
1.55k
    // https://www.w3.org/TR/selectors/#absolutizing
2292
1.55k
    selector.1.push(Component::Combinator(combinator));
2293
1.55k
    selector.1.push(scope);
2294
1.35M
  }
2295
2296
1.35M
  Ok(selector)
2297
1.56M
}
parcel_selectors::parser::parse_relative_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>
Line
Count
Source
2257
1.56M
fn parse_relative_selector<'i, 't, P, Impl>(
2258
1.56M
  parser: &P,
2259
1.56M
  input: &mut CssParser<'i, 't>,
2260
1.56M
  state: &mut SelectorParsingState,
2261
1.56M
  mut nesting_requirement: NestingRequirement,
2262
1.56M
) -> Result<Selector<'i, Impl>, ParseError<'i, P::Error>>
2263
1.56M
where
2264
1.56M
  P: Parser<'i, Impl = Impl>,
2265
1.56M
  Impl: SelectorImpl<'i>,
2266
{
2267
  // https://www.w3.org/TR/selectors-4/#parse-relative-selector
2268
1.56M
  let s = input.state();
2269
1.56M
  let combinator = match input.next()? {
2270
779
    Token::Delim('>') => Some(Combinator::Child),
2271
1.20k
    Token::Delim('+') => Some(Combinator::NextSibling),
2272
368
    Token::Delim('~') => Some(Combinator::LaterSibling),
2273
    _ => {
2274
1.53M
      input.reset(&s);
2275
1.53M
      None
2276
    }
2277
  };
2278
2279
1.54M
  let scope = if nesting_requirement == NestingRequirement::Implicit {
2280
1.54M
    Component::Nesting
2281
  } else {
2282
124
    Component::Scope
2283
  };
2284
2285
1.54M
  if combinator.is_some() {
2286
2.34k
    nesting_requirement = NestingRequirement::None;
2287
1.53M
  }
2288
2289
1.54M
  let mut selector = parse_selector(parser, input, state, nesting_requirement)?;
2290
1.35M
  if let Some(combinator) = combinator {
2291
1.55k
    // https://www.w3.org/TR/selectors/#absolutizing
2292
1.55k
    selector.1.push(Component::Combinator(combinator));
2293
1.55k
    selector.1.push(scope);
2294
1.35M
  }
2295
2296
1.35M
  Ok(selector)
2297
1.56M
}
Unexecuted instantiation: parcel_selectors::parser::parse_relative_selector::<_, _>
2298
2299
/// * `Err(())`: Invalid selector, abort
2300
/// * `Ok(false)`: Not a type selector, could be something else. `input` was not consumed.
2301
/// * `Ok(true)`: Length 0 (`*|*`), 1 (`*|E` or `ns|*`) or 2 (`|E` or `ns|E`)
2302
2.48M
fn parse_type_selector<'i, 't, P, Impl, S>(
2303
2.48M
  parser: &P,
2304
2.48M
  input: &mut CssParser<'i, 't>,
2305
2.48M
  state: SelectorParsingState,
2306
2.48M
  sink: &mut S,
2307
2.48M
) -> Result<bool, ParseError<'i, P::Error>>
2308
2.48M
where
2309
2.48M
  P: Parser<'i, Impl = Impl>,
2310
2.48M
  Impl: SelectorImpl<'i>,
2311
2.48M
  S: Push<Component<'i, Impl>>,
2312
{
2313
2.48M
  match parse_qualified_name(parser, input, /* in_attr_selector = */ false) {
2314
    Err(ParseError {
2315
      kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
2316
      ..
2317
    })
2318
446k
    | Ok(OptionalQName::None(_)) => Ok(false),
2319
2.02M
    Ok(OptionalQName::Some(namespace, local_name)) => {
2320
2.02M
      if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
2321
0
        return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
2322
2.02M
      }
2323
2.02M
      match namespace {
2324
1.99M
        QNamePrefix::ImplicitAnyNamespace => {}
2325
0
        QNamePrefix::ImplicitDefaultNamespace(url) => sink.push(Component::DefaultNamespace(url)),
2326
7.57k
        QNamePrefix::ExplicitNamespace(prefix, url) => sink.push(match parser.default_namespace() {
2327
0
          Some(ref default_url) if url == *default_url => Component::DefaultNamespace(url),
2328
7.57k
          _ => Component::Namespace(prefix, url),
2329
        }),
2330
21.2k
        QNamePrefix::ExplicitNoNamespace => sink.push(Component::ExplicitNoNamespace),
2331
        QNamePrefix::ExplicitAnyNamespace => {
2332
          // Element type selectors that have no namespace
2333
          // component (no namespace separator) represent elements
2334
          // without regard to the element's namespace (equivalent
2335
          // to "*|") unless a default namespace has been declared
2336
          // for namespaced selectors (e.g. in CSS, in the style
2337
          // sheet). If a default namespace has been declared,
2338
          // such selectors will represent only elements in the
2339
          // default namespace.
2340
          // -- Selectors § 6.1.1
2341
          // So we'll have this act the same as the
2342
          // QNamePrefix::ImplicitAnyNamespace case.
2343
          // For lightning css this logic was removed, should be handled when matching.
2344
44
          sink.push(Component::ExplicitAnyNamespace)
2345
        }
2346
        QNamePrefix::ImplicitNoNamespace => {
2347
0
          unreachable!() // Not returned with in_attr_selector = false
2348
        }
2349
      }
2350
2.02M
      match local_name {
2351
2.01M
        Some(name) => sink.push(Component::LocalName(LocalName {
2352
2.01M
          lower_name: to_ascii_lowercase(name.clone()).into(),
2353
2.01M
          name: name.into(),
2354
2.01M
        })),
2355
10.3k
        None => sink.push(Component::ExplicitUniversalType),
2356
      }
2357
2.02M
      Ok(true)
2358
    }
2359
9.58k
    Err(e) => Err(e),
2360
  }
2361
2.48M
}
parcel_selectors::parser::parse_type_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors, parcel_selectors::builder::SelectorBuilder<lightningcss::selector::private::Selectors>>
Line
Count
Source
2302
2.48M
fn parse_type_selector<'i, 't, P, Impl, S>(
2303
2.48M
  parser: &P,
2304
2.48M
  input: &mut CssParser<'i, 't>,
2305
2.48M
  state: SelectorParsingState,
2306
2.48M
  sink: &mut S,
2307
2.48M
) -> Result<bool, ParseError<'i, P::Error>>
2308
2.48M
where
2309
2.48M
  P: Parser<'i, Impl = Impl>,
2310
2.48M
  Impl: SelectorImpl<'i>,
2311
2.48M
  S: Push<Component<'i, Impl>>,
2312
{
2313
2.48M
  match parse_qualified_name(parser, input, /* in_attr_selector = */ false) {
2314
    Err(ParseError {
2315
      kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
2316
      ..
2317
    })
2318
446k
    | Ok(OptionalQName::None(_)) => Ok(false),
2319
2.02M
    Ok(OptionalQName::Some(namespace, local_name)) => {
2320
2.02M
      if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
2321
0
        return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
2322
2.02M
      }
2323
2.02M
      match namespace {
2324
1.99M
        QNamePrefix::ImplicitAnyNamespace => {}
2325
0
        QNamePrefix::ImplicitDefaultNamespace(url) => sink.push(Component::DefaultNamespace(url)),
2326
7.57k
        QNamePrefix::ExplicitNamespace(prefix, url) => sink.push(match parser.default_namespace() {
2327
0
          Some(ref default_url) if url == *default_url => Component::DefaultNamespace(url),
2328
7.57k
          _ => Component::Namespace(prefix, url),
2329
        }),
2330
21.2k
        QNamePrefix::ExplicitNoNamespace => sink.push(Component::ExplicitNoNamespace),
2331
        QNamePrefix::ExplicitAnyNamespace => {
2332
          // Element type selectors that have no namespace
2333
          // component (no namespace separator) represent elements
2334
          // without regard to the element's namespace (equivalent
2335
          // to "*|") unless a default namespace has been declared
2336
          // for namespaced selectors (e.g. in CSS, in the style
2337
          // sheet). If a default namespace has been declared,
2338
          // such selectors will represent only elements in the
2339
          // default namespace.
2340
          // -- Selectors § 6.1.1
2341
          // So we'll have this act the same as the
2342
          // QNamePrefix::ImplicitAnyNamespace case.
2343
          // For lightning css this logic was removed, should be handled when matching.
2344
44
          sink.push(Component::ExplicitAnyNamespace)
2345
        }
2346
        QNamePrefix::ImplicitNoNamespace => {
2347
0
          unreachable!() // Not returned with in_attr_selector = false
2348
        }
2349
      }
2350
2.02M
      match local_name {
2351
2.01M
        Some(name) => sink.push(Component::LocalName(LocalName {
2352
2.01M
          lower_name: to_ascii_lowercase(name.clone()).into(),
2353
2.01M
          name: name.into(),
2354
2.01M
        })),
2355
10.3k
        None => sink.push(Component::ExplicitUniversalType),
2356
      }
2357
2.02M
      Ok(true)
2358
    }
2359
9.58k
    Err(e) => Err(e),
2360
  }
2361
2.48M
}
Unexecuted instantiation: parcel_selectors::parser::parse_type_selector::<_, _, _>
2362
2363
#[derive(Debug)]
2364
enum SimpleSelectorParseResult<'i, Impl: SelectorImpl<'i>> {
2365
  SimpleSelector(Component<'i, Impl>),
2366
  PseudoElement(Impl::PseudoElement),
2367
  SlottedPseudo(Selector<'i, Impl>),
2368
  PartPseudo(Box<[Impl::Identifier]>),
2369
}
2370
2371
#[derive(Debug)]
2372
enum QNamePrefix<'i, Impl: SelectorImpl<'i>> {
2373
  ImplicitNoNamespace,                                          // `foo` in attr selectors
2374
  ImplicitAnyNamespace,                                         // `foo` in type selectors, without a default ns
2375
  ImplicitDefaultNamespace(Impl::NamespaceUrl),                 // `foo` in type selectors, with a default ns
2376
  ExplicitNoNamespace,                                          // `|foo`
2377
  ExplicitAnyNamespace,                                         // `*|foo`
2378
  ExplicitNamespace(Impl::NamespacePrefix, Impl::NamespaceUrl), // `prefix|foo`
2379
}
2380
2381
enum OptionalQName<'i, Impl: SelectorImpl<'i>> {
2382
  Some(QNamePrefix<'i, Impl>, Option<CowRcStr<'i>>),
2383
  None(Token<'i>),
2384
}
2385
2386
/// * `Err(())`: Invalid selector, abort
2387
/// * `Ok(None(token))`: Not a simple selector, could be something else. `input` was not consumed,
2388
///                      but the token is still returned.
2389
/// * `Ok(Some(namespace, local_name))`: `None` for the local name means a `*` universal selector
2390
2.48M
fn parse_qualified_name<'i, 't, P, Impl>(
2391
2.48M
  parser: &P,
2392
2.48M
  input: &mut CssParser<'i, 't>,
2393
2.48M
  in_attr_selector: bool,
2394
2.48M
) -> Result<OptionalQName<'i, Impl>, ParseError<'i, P::Error>>
2395
2.48M
where
2396
2.48M
  P: Parser<'i, Impl = Impl>,
2397
2.48M
  Impl: SelectorImpl<'i>,
2398
{
2399
2.48M
  let default_namespace = |local_name| {
2400
1.99M
    let namespace = match parser.default_namespace() {
2401
0
      Some(url) => QNamePrefix::ImplicitDefaultNamespace(url),
2402
1.99M
      None => QNamePrefix::ImplicitAnyNamespace,
2403
    };
2404
1.99M
    Ok(OptionalQName::Some(namespace, local_name))
2405
1.99M
  };
parcel_selectors::parser::parse_qualified_name::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#0}
Line
Count
Source
2399
1.99M
  let default_namespace = |local_name| {
2400
1.99M
    let namespace = match parser.default_namespace() {
2401
0
      Some(url) => QNamePrefix::ImplicitDefaultNamespace(url),
2402
1.99M
      None => QNamePrefix::ImplicitAnyNamespace,
2403
    };
2404
1.99M
    Ok(OptionalQName::Some(namespace, local_name))
2405
1.99M
  };
Unexecuted instantiation: parcel_selectors::parser::parse_qualified_name::<_, _>::{closure#0}
2406
2407
2.48M
  let explicit_namespace = |input: &mut CssParser<'i, 't>, namespace| {
2408
57.0k
    let location = input.current_source_location();
2409
57.0k
    match input.next_including_whitespace() {
2410
12
      Ok(&Token::Delim('*')) if !in_attr_selector => Ok(OptionalQName::Some(namespace, None)),
2411
28.8k
      Ok(&Token::Ident(ref local_name)) => Ok(OptionalQName::Some(namespace, Some(local_name.clone()))),
2412
3
      Ok(t) if in_attr_selector => {
2413
3
        let e = SelectorParseErrorKind::InvalidQualNameInAttr(t.clone());
2414
3
        Err(location.new_custom_error(e))
2415
      }
2416
9.58k
      Ok(t) => Err(location.new_custom_error(SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(t.clone()))),
2417
18.5k
      Err(e) => Err(e.into()),
2418
    }
2419
57.0k
  };
parcel_selectors::parser::parse_qualified_name::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#1}
Line
Count
Source
2407
57.0k
  let explicit_namespace = |input: &mut CssParser<'i, 't>, namespace| {
2408
57.0k
    let location = input.current_source_location();
2409
57.0k
    match input.next_including_whitespace() {
2410
12
      Ok(&Token::Delim('*')) if !in_attr_selector => Ok(OptionalQName::Some(namespace, None)),
2411
28.8k
      Ok(&Token::Ident(ref local_name)) => Ok(OptionalQName::Some(namespace, Some(local_name.clone()))),
2412
3
      Ok(t) if in_attr_selector => {
2413
3
        let e = SelectorParseErrorKind::InvalidQualNameInAttr(t.clone());
2414
3
        Err(location.new_custom_error(e))
2415
      }
2416
9.58k
      Ok(t) => Err(location.new_custom_error(SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(t.clone()))),
2417
18.5k
      Err(e) => Err(e.into()),
2418
    }
2419
57.0k
  };
Unexecuted instantiation: parcel_selectors::parser::parse_qualified_name::<_, _>::{closure#1}
2420
2421
2.48M
  let start = input.state();
2422
  // FIXME: remove clone() when lifetimes are non-lexical
2423
2.48M
  match input.next_including_whitespace().map(|t| t.clone()) {
parcel_selectors::parser::parse_qualified_name::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#2}
Line
Count
Source
2423
2.47M
  match input.next_including_whitespace().map(|t| t.clone()) {
Unexecuted instantiation: parcel_selectors::parser::parse_qualified_name::<_, _>::{closure#2}
2424
1.99M
    Ok(Token::Ident(value)) => {
2425
1.99M
      let after_ident = input.state();
2426
1.99M
      match input.next_including_whitespace() {
2427
        Ok(&Token::Delim('|')) => {
2428
7.99k
          let prefix = value.clone().into();
2429
7.99k
          let result = parser.namespace_for_prefix(&prefix);
2430
7.99k
          let url = result.ok_or(
2431
7.99k
            after_ident
2432
7.99k
              .source_location()
2433
7.99k
              .new_custom_error(SelectorParseErrorKind::ExpectedNamespace(value)),
2434
0
          )?;
2435
7.99k
          explicit_namespace(input, QNamePrefix::ExplicitNamespace(prefix, url))
2436
        }
2437
        _ => {
2438
1.98M
          input.reset(&after_ident);
2439
1.98M
          if in_attr_selector {
2440
717
            Ok(OptionalQName::Some(QNamePrefix::ImplicitNoNamespace, Some(value)))
2441
          } else {
2442
1.98M
            default_namespace(Some(value))
2443
          }
2444
        }
2445
      }
2446
    }
2447
    Ok(Token::Delim('*')) => {
2448
10.4k
      let after_star = input.state();
2449
      // FIXME: remove clone() when lifetimes are non-lexical
2450
10.4k
      match input.next_including_whitespace().map(|t| t.clone()) {
parcel_selectors::parser::parse_qualified_name::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#3}
Line
Count
Source
2450
2.74k
      match input.next_including_whitespace().map(|t| t.clone()) {
Unexecuted instantiation: parcel_selectors::parser::parse_qualified_name::<_, _>::{closure#3}
2451
49
        Ok(Token::Delim('|')) => explicit_namespace(input, QNamePrefix::ExplicitAnyNamespace),
2452
10.3k
        result => {
2453
10.3k
          input.reset(&after_star);
2454
10.3k
          if in_attr_selector {
2455
6
            match result {
2456
6
              Ok(t) => Err(
2457
6
                after_star
2458
6
                  .source_location()
2459
6
                  .new_custom_error(SelectorParseErrorKind::ExpectedBarInAttr(t)),
2460
6
              ),
2461
0
              Err(e) => Err(e.into()),
2462
            }
2463
          } else {
2464
10.3k
            default_namespace(None)
2465
          }
2466
        }
2467
      }
2468
    }
2469
48.9k
    Ok(Token::Delim('|')) => explicit_namespace(input, QNamePrefix::ExplicitNoNamespace),
2470
414k
    Ok(t) => {
2471
414k
      input.reset(&start);
2472
414k
      Ok(OptionalQName::None(t))
2473
    }
2474
14.0k
    Err(e) => {
2475
14.0k
      input.reset(&start);
2476
14.0k
      Err(e.into())
2477
    }
2478
  }
2479
2.48M
}
parcel_selectors::parser::parse_qualified_name::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>
Line
Count
Source
2390
2.48M
fn parse_qualified_name<'i, 't, P, Impl>(
2391
2.48M
  parser: &P,
2392
2.48M
  input: &mut CssParser<'i, 't>,
2393
2.48M
  in_attr_selector: bool,
2394
2.48M
) -> Result<OptionalQName<'i, Impl>, ParseError<'i, P::Error>>
2395
2.48M
where
2396
2.48M
  P: Parser<'i, Impl = Impl>,
2397
2.48M
  Impl: SelectorImpl<'i>,
2398
{
2399
2.48M
  let default_namespace = |local_name| {
2400
    let namespace = match parser.default_namespace() {
2401
      Some(url) => QNamePrefix::ImplicitDefaultNamespace(url),
2402
      None => QNamePrefix::ImplicitAnyNamespace,
2403
    };
2404
    Ok(OptionalQName::Some(namespace, local_name))
2405
  };
2406
2407
2.48M
  let explicit_namespace = |input: &mut CssParser<'i, 't>, namespace| {
2408
    let location = input.current_source_location();
2409
    match input.next_including_whitespace() {
2410
      Ok(&Token::Delim('*')) if !in_attr_selector => Ok(OptionalQName::Some(namespace, None)),
2411
      Ok(&Token::Ident(ref local_name)) => Ok(OptionalQName::Some(namespace, Some(local_name.clone()))),
2412
      Ok(t) if in_attr_selector => {
2413
        let e = SelectorParseErrorKind::InvalidQualNameInAttr(t.clone());
2414
        Err(location.new_custom_error(e))
2415
      }
2416
      Ok(t) => Err(location.new_custom_error(SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(t.clone()))),
2417
      Err(e) => Err(e.into()),
2418
    }
2419
  };
2420
2421
2.48M
  let start = input.state();
2422
  // FIXME: remove clone() when lifetimes are non-lexical
2423
2.48M
  match input.next_including_whitespace().map(|t| t.clone()) {
2424
1.99M
    Ok(Token::Ident(value)) => {
2425
1.99M
      let after_ident = input.state();
2426
1.99M
      match input.next_including_whitespace() {
2427
        Ok(&Token::Delim('|')) => {
2428
7.99k
          let prefix = value.clone().into();
2429
7.99k
          let result = parser.namespace_for_prefix(&prefix);
2430
7.99k
          let url = result.ok_or(
2431
7.99k
            after_ident
2432
7.99k
              .source_location()
2433
7.99k
              .new_custom_error(SelectorParseErrorKind::ExpectedNamespace(value)),
2434
0
          )?;
2435
7.99k
          explicit_namespace(input, QNamePrefix::ExplicitNamespace(prefix, url))
2436
        }
2437
        _ => {
2438
1.98M
          input.reset(&after_ident);
2439
1.98M
          if in_attr_selector {
2440
717
            Ok(OptionalQName::Some(QNamePrefix::ImplicitNoNamespace, Some(value)))
2441
          } else {
2442
1.98M
            default_namespace(Some(value))
2443
          }
2444
        }
2445
      }
2446
    }
2447
    Ok(Token::Delim('*')) => {
2448
10.4k
      let after_star = input.state();
2449
      // FIXME: remove clone() when lifetimes are non-lexical
2450
10.4k
      match input.next_including_whitespace().map(|t| t.clone()) {
2451
49
        Ok(Token::Delim('|')) => explicit_namespace(input, QNamePrefix::ExplicitAnyNamespace),
2452
10.3k
        result => {
2453
10.3k
          input.reset(&after_star);
2454
10.3k
          if in_attr_selector {
2455
6
            match result {
2456
6
              Ok(t) => Err(
2457
6
                after_star
2458
6
                  .source_location()
2459
6
                  .new_custom_error(SelectorParseErrorKind::ExpectedBarInAttr(t)),
2460
6
              ),
2461
0
              Err(e) => Err(e.into()),
2462
            }
2463
          } else {
2464
10.3k
            default_namespace(None)
2465
          }
2466
        }
2467
      }
2468
    }
2469
48.9k
    Ok(Token::Delim('|')) => explicit_namespace(input, QNamePrefix::ExplicitNoNamespace),
2470
414k
    Ok(t) => {
2471
414k
      input.reset(&start);
2472
414k
      Ok(OptionalQName::None(t))
2473
    }
2474
14.0k
    Err(e) => {
2475
14.0k
      input.reset(&start);
2476
14.0k
      Err(e.into())
2477
    }
2478
  }
2479
2.48M
}
Unexecuted instantiation: parcel_selectors::parser::parse_qualified_name::<_, _>
2480
2481
1.49k
fn parse_attribute_selector<'i, 't, P, Impl>(
2482
1.49k
  parser: &P,
2483
1.49k
  input: &mut CssParser<'i, 't>,
2484
1.49k
) -> Result<Component<'i, Impl>, ParseError<'i, P::Error>>
2485
1.49k
where
2486
1.49k
  P: Parser<'i, Impl = Impl>,
2487
1.49k
  Impl: SelectorImpl<'i>,
2488
{
2489
  let namespace;
2490
  let local_name;
2491
2492
1.49k
  input.skip_whitespace();
2493
2494
1.49k
  match parse_qualified_name(parser, input, /* in_attr_selector = */ true)? {
2495
725
    OptionalQName::None(t) => {
2496
725
      return Err(input.new_custom_error(SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(t)));
2497
    }
2498
0
    OptionalQName::Some(_, None) => unreachable!(),
2499
733
    OptionalQName::Some(ns, Some(ln)) => {
2500
733
      local_name = ln;
2501
733
      namespace = match ns {
2502
718
        QNamePrefix::ImplicitNoNamespace | QNamePrefix::ExplicitNoNamespace => None,
2503
15
        QNamePrefix::ExplicitNamespace(prefix, url) => Some(NamespaceConstraint::Specific((prefix, url))),
2504
0
        QNamePrefix::ExplicitAnyNamespace => Some(NamespaceConstraint::Any),
2505
        QNamePrefix::ImplicitAnyNamespace | QNamePrefix::ImplicitDefaultNamespace(_) => {
2506
0
          unreachable!() // Not returned with in_attr_selector = true
2507
        }
2508
      }
2509
    }
2510
  }
2511
2512
733
  let location = input.current_source_location();
2513
733
  let operator = match input.next() {
2514
    // [foo]
2515
    Err(_) => {
2516
105
      let local_name_lower = to_ascii_lowercase(local_name.clone()).into();
2517
105
      let local_name = local_name.into();
2518
105
      if let Some(namespace) = namespace {
2519
8
        return Ok(Component::AttributeOther(Box::new(AttrSelectorWithOptionalNamespace {
2520
8
          namespace: Some(namespace),
2521
8
          local_name,
2522
8
          local_name_lower,
2523
8
          operation: ParsedAttrSelectorOperation::Exists,
2524
8
          never_matches: false,
2525
8
        })));
2526
      } else {
2527
97
        return Ok(Component::AttributeInNoNamespaceExists {
2528
97
          local_name,
2529
97
          local_name_lower,
2530
97
        });
2531
      }
2532
    }
2533
2534
    // [foo=bar]
2535
219
    Ok(&Token::Delim('=')) => AttrSelectorOperator::Equal,
2536
    // [foo~=bar]
2537
36
    Ok(&Token::IncludeMatch) => AttrSelectorOperator::Includes,
2538
    // [foo|=bar]
2539
8
    Ok(&Token::DashMatch) => AttrSelectorOperator::DashMatch,
2540
    // [foo^=bar]
2541
1
    Ok(&Token::PrefixMatch) => AttrSelectorOperator::Prefix,
2542
    // [foo*=bar]
2543
11
    Ok(&Token::SubstringMatch) => AttrSelectorOperator::Substring,
2544
    // [foo$=bar]
2545
3
    Ok(&Token::SuffixMatch) => AttrSelectorOperator::Suffix,
2546
350
    Ok(t) => {
2547
350
      return Err(
2548
350
        location.new_custom_error(SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(t.clone())),
2549
350
      );
2550
    }
2551
  };
2552
2553
278
  let value = match input.expect_ident_or_string() {
2554
263
    Ok(t) => t.clone(),
2555
    Err(BasicParseError {
2556
12
      kind: BasicParseErrorKind::UnexpectedToken(t),
2557
12
      location,
2558
12
    }) => return Err(location.new_custom_error(SelectorParseErrorKind::BadValueInAttr(t))),
2559
3
    Err(e) => return Err(e.into()),
2560
  };
2561
263
  let never_matches = match operator {
2562
214
    AttrSelectorOperator::Equal | AttrSelectorOperator::DashMatch => false,
2563
2564
36
    AttrSelectorOperator::Includes => value.is_empty() || value.contains(SELECTOR_WHITESPACE),
2565
2566
    AttrSelectorOperator::Prefix | AttrSelectorOperator::Substring | AttrSelectorOperator::Suffix => {
2567
13
      value.is_empty()
2568
    }
2569
  };
2570
2571
263
  let attribute_flags = parse_attribute_flags(input)?;
2572
2573
187
  let value = value.into();
2574
  let case_sensitivity;
2575
  // copied from to_ascii_lowercase function, so we can know whether it is already lower case.
2576
187
  let (local_name_lower_cow, local_name_is_ascii_lowercase) =
2577
8.98k
    if let Some(first_uppercase) = local_name.bytes().position(|byte| byte >= b'A' && byte <= b'Z') {
parcel_selectors::parser::parse_attribute_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#0}
Line
Count
Source
2577
8.98k
    if let Some(first_uppercase) = local_name.bytes().position(|byte| byte >= b'A' && byte <= b'Z') {
Unexecuted instantiation: parcel_selectors::parser::parse_attribute_selector::<_, _>::{closure#0}
2578
135
      let mut string = local_name.to_string();
2579
135
      string[first_uppercase..].make_ascii_lowercase();
2580
135
      (string.into(), false)
2581
    } else {
2582
52
      (local_name.clone(), true)
2583
    };
2584
187
  case_sensitivity = attribute_flags.to_case_sensitivity(local_name_lower_cow.as_ref(), namespace.is_some());
2585
187
  let local_name_lower = local_name_lower_cow.into();
2586
187
  let local_name = local_name.into();
2587
187
  if namespace.is_some() || !local_name_is_ascii_lowercase {
2588
136
    Ok(Component::AttributeOther(Box::new(AttrSelectorWithOptionalNamespace {
2589
136
      namespace,
2590
136
      local_name,
2591
136
      local_name_lower,
2592
136
      never_matches,
2593
136
      operation: ParsedAttrSelectorOperation::WithValue {
2594
136
        operator,
2595
136
        case_sensitivity,
2596
136
        expected_value: value,
2597
136
      },
2598
136
    })))
2599
  } else {
2600
51
    Ok(Component::AttributeInNoNamespace {
2601
51
      local_name,
2602
51
      operator,
2603
51
      value,
2604
51
      case_sensitivity,
2605
51
      never_matches,
2606
51
    })
2607
  }
2608
1.49k
}
parcel_selectors::parser::parse_attribute_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>
Line
Count
Source
2481
1.49k
fn parse_attribute_selector<'i, 't, P, Impl>(
2482
1.49k
  parser: &P,
2483
1.49k
  input: &mut CssParser<'i, 't>,
2484
1.49k
) -> Result<Component<'i, Impl>, ParseError<'i, P::Error>>
2485
1.49k
where
2486
1.49k
  P: Parser<'i, Impl = Impl>,
2487
1.49k
  Impl: SelectorImpl<'i>,
2488
{
2489
  let namespace;
2490
  let local_name;
2491
2492
1.49k
  input.skip_whitespace();
2493
2494
1.49k
  match parse_qualified_name(parser, input, /* in_attr_selector = */ true)? {
2495
725
    OptionalQName::None(t) => {
2496
725
      return Err(input.new_custom_error(SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(t)));
2497
    }
2498
0
    OptionalQName::Some(_, None) => unreachable!(),
2499
733
    OptionalQName::Some(ns, Some(ln)) => {
2500
733
      local_name = ln;
2501
733
      namespace = match ns {
2502
718
        QNamePrefix::ImplicitNoNamespace | QNamePrefix::ExplicitNoNamespace => None,
2503
15
        QNamePrefix::ExplicitNamespace(prefix, url) => Some(NamespaceConstraint::Specific((prefix, url))),
2504
0
        QNamePrefix::ExplicitAnyNamespace => Some(NamespaceConstraint::Any),
2505
        QNamePrefix::ImplicitAnyNamespace | QNamePrefix::ImplicitDefaultNamespace(_) => {
2506
0
          unreachable!() // Not returned with in_attr_selector = true
2507
        }
2508
      }
2509
    }
2510
  }
2511
2512
733
  let location = input.current_source_location();
2513
733
  let operator = match input.next() {
2514
    // [foo]
2515
    Err(_) => {
2516
105
      let local_name_lower = to_ascii_lowercase(local_name.clone()).into();
2517
105
      let local_name = local_name.into();
2518
105
      if let Some(namespace) = namespace {
2519
8
        return Ok(Component::AttributeOther(Box::new(AttrSelectorWithOptionalNamespace {
2520
8
          namespace: Some(namespace),
2521
8
          local_name,
2522
8
          local_name_lower,
2523
8
          operation: ParsedAttrSelectorOperation::Exists,
2524
8
          never_matches: false,
2525
8
        })));
2526
      } else {
2527
97
        return Ok(Component::AttributeInNoNamespaceExists {
2528
97
          local_name,
2529
97
          local_name_lower,
2530
97
        });
2531
      }
2532
    }
2533
2534
    // [foo=bar]
2535
219
    Ok(&Token::Delim('=')) => AttrSelectorOperator::Equal,
2536
    // [foo~=bar]
2537
36
    Ok(&Token::IncludeMatch) => AttrSelectorOperator::Includes,
2538
    // [foo|=bar]
2539
8
    Ok(&Token::DashMatch) => AttrSelectorOperator::DashMatch,
2540
    // [foo^=bar]
2541
1
    Ok(&Token::PrefixMatch) => AttrSelectorOperator::Prefix,
2542
    // [foo*=bar]
2543
11
    Ok(&Token::SubstringMatch) => AttrSelectorOperator::Substring,
2544
    // [foo$=bar]
2545
3
    Ok(&Token::SuffixMatch) => AttrSelectorOperator::Suffix,
2546
350
    Ok(t) => {
2547
350
      return Err(
2548
350
        location.new_custom_error(SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(t.clone())),
2549
350
      );
2550
    }
2551
  };
2552
2553
278
  let value = match input.expect_ident_or_string() {
2554
263
    Ok(t) => t.clone(),
2555
    Err(BasicParseError {
2556
12
      kind: BasicParseErrorKind::UnexpectedToken(t),
2557
12
      location,
2558
12
    }) => return Err(location.new_custom_error(SelectorParseErrorKind::BadValueInAttr(t))),
2559
3
    Err(e) => return Err(e.into()),
2560
  };
2561
263
  let never_matches = match operator {
2562
214
    AttrSelectorOperator::Equal | AttrSelectorOperator::DashMatch => false,
2563
2564
36
    AttrSelectorOperator::Includes => value.is_empty() || value.contains(SELECTOR_WHITESPACE),
2565
2566
    AttrSelectorOperator::Prefix | AttrSelectorOperator::Substring | AttrSelectorOperator::Suffix => {
2567
13
      value.is_empty()
2568
    }
2569
  };
2570
2571
263
  let attribute_flags = parse_attribute_flags(input)?;
2572
2573
187
  let value = value.into();
2574
  let case_sensitivity;
2575
  // copied from to_ascii_lowercase function, so we can know whether it is already lower case.
2576
187
  let (local_name_lower_cow, local_name_is_ascii_lowercase) =
2577
187
    if let Some(first_uppercase) = local_name.bytes().position(|byte| byte >= b'A' && byte <= b'Z') {
2578
135
      let mut string = local_name.to_string();
2579
135
      string[first_uppercase..].make_ascii_lowercase();
2580
135
      (string.into(), false)
2581
    } else {
2582
52
      (local_name.clone(), true)
2583
    };
2584
187
  case_sensitivity = attribute_flags.to_case_sensitivity(local_name_lower_cow.as_ref(), namespace.is_some());
2585
187
  let local_name_lower = local_name_lower_cow.into();
2586
187
  let local_name = local_name.into();
2587
187
  if namespace.is_some() || !local_name_is_ascii_lowercase {
2588
136
    Ok(Component::AttributeOther(Box::new(AttrSelectorWithOptionalNamespace {
2589
136
      namespace,
2590
136
      local_name,
2591
136
      local_name_lower,
2592
136
      never_matches,
2593
136
      operation: ParsedAttrSelectorOperation::WithValue {
2594
136
        operator,
2595
136
        case_sensitivity,
2596
136
        expected_value: value,
2597
136
      },
2598
136
    })))
2599
  } else {
2600
51
    Ok(Component::AttributeInNoNamespace {
2601
51
      local_name,
2602
51
      operator,
2603
51
      value,
2604
51
      case_sensitivity,
2605
51
      never_matches,
2606
51
    })
2607
  }
2608
1.49k
}
Unexecuted instantiation: parcel_selectors::parser::parse_attribute_selector::<_, _>
2609
2610
/// An attribute selector can have 's' or 'i' as flags, or no flags at all.
2611
enum AttributeFlags {
2612
  // Matching should be case-sensitive ('s' flag).
2613
  CaseSensitive,
2614
  // Matching should be case-insensitive ('i' flag).
2615
  AsciiCaseInsensitive,
2616
  // No flags.  Matching behavior depends on the name of the attribute.
2617
  CaseSensitivityDependsOnName,
2618
}
2619
2620
impl AttributeFlags {
2621
187
  fn to_case_sensitivity(self, local_name: &str, have_namespace: bool) -> ParsedCaseSensitivity {
2622
187
    match self {
2623
0
      AttributeFlags::CaseSensitive => ParsedCaseSensitivity::ExplicitCaseSensitive,
2624
0
      AttributeFlags::AsciiCaseInsensitive => ParsedCaseSensitivity::AsciiCaseInsensitive,
2625
      AttributeFlags::CaseSensitivityDependsOnName => {
2626
187
        if !have_namespace
2627
186
          && include!(concat!(env!("OUT_DIR"), "/ascii_case_insensitive_html_attributes.rs")).contains(local_name)
2628
        {
2629
0
          ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument
2630
        } else {
2631
187
          ParsedCaseSensitivity::CaseSensitive
2632
        }
2633
      }
2634
    }
2635
187
  }
2636
}
2637
2638
263
fn parse_attribute_flags<'i, 't>(input: &mut CssParser<'i, 't>) -> Result<AttributeFlags, BasicParseError<'i>> {
2639
263
  let location = input.current_source_location();
2640
263
  let token = match input.next() {
2641
76
    Ok(t) => t,
2642
    Err(..) => {
2643
      // Selectors spec says language-defined; HTML says it depends on the
2644
      // exact attribute name.
2645
187
      return Ok(AttributeFlags::CaseSensitivityDependsOnName);
2646
    }
2647
  };
2648
2649
76
  let ident = match *token {
2650
12
    Token::Ident(ref i) => i,
2651
64
    ref other => return Err(location.new_basic_unexpected_token_error(other.clone())),
2652
  };
2653
2654
12
  Ok(match_ignore_ascii_case! {
2655
12
      ident,
2656
12
      "i" => AttributeFlags::AsciiCaseInsensitive,
2657
12
      "s" => AttributeFlags::CaseSensitive,
2658
12
      _ => return Err(location.new_basic_unexpected_token_error(token.clone())),
2659
  })
2660
263
}
2661
2662
/// Level 3: Parse **one** simple_selector.  (Though we might insert a second
2663
/// implied "<defaultns>|*" type selector.)
2664
0
fn parse_negation<'i, 't, P, Impl>(
2665
0
  parser: &P,
2666
0
  input: &mut CssParser<'i, 't>,
2667
0
  state: &mut SelectorParsingState,
2668
0
) -> Result<Component<'i, Impl>, ParseError<'i, P::Error>>
2669
0
where
2670
0
  P: Parser<'i, Impl = Impl>,
2671
0
  Impl: SelectorImpl<'i>,
2672
{
2673
0
  let mut child_state =
2674
0
    *state | SelectorParsingState::SKIP_DEFAULT_NAMESPACE | SelectorParsingState::DISALLOW_PSEUDOS;
2675
0
  let list = SelectorList::parse_with_state(
2676
0
    parser,
2677
0
    input,
2678
0
    &mut child_state,
2679
0
    ParseErrorRecovery::DiscardList,
2680
0
    NestingRequirement::None,
2681
0
  )?;
2682
2683
0
  if child_state.contains(SelectorParsingState::AFTER_NESTING) {
2684
0
    state.insert(SelectorParsingState::AFTER_NESTING)
2685
0
  }
2686
2687
0
  Ok(Component::Negation(list.0.into_vec().into_boxed_slice()))
2688
0
}
Unexecuted instantiation: parcel_selectors::parser::parse_negation::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>
Unexecuted instantiation: parcel_selectors::parser::parse_negation::<_, _>
2689
2690
/// simple_selector_sequence
2691
/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]*
2692
/// | [ HASH | class | attrib | pseudo | negation ]+
2693
///
2694
/// `Err(())` means invalid selector.
2695
/// `Ok(true)` is an empty selector
2696
2.48M
fn parse_compound_selector<'i, 't, P, Impl>(
2697
2.48M
  parser: &P,
2698
2.48M
  state: &mut SelectorParsingState,
2699
2.48M
  input: &mut CssParser<'i, 't>,
2700
2.48M
  builder: &mut SelectorBuilder<'i, Impl>,
2701
2.48M
) -> Result<bool, ParseError<'i, P::Error>>
2702
2.48M
where
2703
2.48M
  P: Parser<'i, Impl = Impl>,
2704
2.48M
  Impl: SelectorImpl<'i>,
2705
{
2706
2.48M
  input.skip_whitespace();
2707
2708
2.48M
  let mut empty = true;
2709
2.48M
  if parser.is_nesting_allowed() && input.try_parse(|input| input.expect_delim('&')).is_ok() {
parcel_selectors::parser::parse_compound_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#0}
Line
Count
Source
2709
2.48M
  if parser.is_nesting_allowed() && input.try_parse(|input| input.expect_delim('&')).is_ok() {
Unexecuted instantiation: parcel_selectors::parser::parse_compound_selector::<_, _>::{closure#0}
2710
2.61k
    state.insert(SelectorParsingState::AFTER_NESTING);
2711
2.61k
    builder.push_simple_selector(Component::Nesting);
2712
2.61k
    empty = false;
2713
2.48M
  }
2714
2715
2.48M
  if parse_type_selector(parser, input, *state, builder)? {
2716
2.02M
    empty = false;
2717
2.02M
  }
2718
2719
  loop {
2720
3.32M
    let result = match parse_one_simple_selector(parser, input, state)? {
2721
2.31M
      None => break,
2722
853k
      Some(result) => result,
2723
    };
2724
2725
853k
    if empty {
2726
214k
      if let Some(url) = parser.default_namespace() {
2727
        // If there was no explicit type selector, but there is a
2728
        // default namespace, there is an implicit "<defaultns>|*" type
2729
        // selector. Except for :host() or :not() / :is() / :where(),
2730
        // where we ignore it.
2731
        //
2732
        // https://drafts.csswg.org/css-scoping/#host-element-in-tree:
2733
        //
2734
        //     When considered within its own shadow trees, the shadow
2735
        //     host is featureless. Only the :host, :host(), and
2736
        //     :host-context() pseudo-classes are allowed to match it.
2737
        //
2738
        // https://drafts.csswg.org/selectors-4/#featureless:
2739
        //
2740
        //     A featureless element does not match any selector at all,
2741
        //     except those it is explicitly defined to match. If a
2742
        //     given selector is allowed to match a featureless element,
2743
        //     it must do so while ignoring the default namespace.
2744
        //
2745
        // https://drafts.csswg.org/selectors-4/#matches
2746
        //
2747
        //     Default namespace declarations do not affect the compound
2748
        //     selector representing the subject of any selector within
2749
        //     a :is() pseudo-class, unless that compound selector
2750
        //     contains an explicit universal selector or type selector.
2751
        //
2752
        //     (Similar quotes for :where() / :not())
2753
        //
2754
0
        let ignore_default_ns = state.intersects(SelectorParsingState::SKIP_DEFAULT_NAMESPACE)
2755
0
          || matches!(result, SimpleSelectorParseResult::SimpleSelector(Component::Host(..)));
2756
0
        if !ignore_default_ns {
2757
0
          builder.push_simple_selector(Component::DefaultNamespace(url));
2758
0
        }
2759
214k
      }
2760
639k
    }
2761
2762
853k
    empty = false;
2763
2764
853k
    match result {
2765
788k
      SimpleSelectorParseResult::SimpleSelector(s) => {
2766
788k
        builder.push_simple_selector(s);
2767
788k
      }
2768
0
      SimpleSelectorParseResult::PartPseudo(part_names) => {
2769
0
        state.insert(SelectorParsingState::AFTER_PART);
2770
0
        builder.push_combinator(Combinator::Part);
2771
0
        builder.push_simple_selector(Component::Part(part_names));
2772
0
      }
2773
0
      SimpleSelectorParseResult::SlottedPseudo(selector) => {
2774
0
        state.insert(SelectorParsingState::AFTER_SLOTTED);
2775
0
        builder.push_combinator(Combinator::SlotAssignment);
2776
0
        builder.push_simple_selector(Component::Slotted(selector));
2777
0
      }
2778
65.6k
      SimpleSelectorParseResult::PseudoElement(p) => {
2779
65.6k
        if !p.is_unknown() {
2780
135
          state.insert(SelectorParsingState::AFTER_PSEUDO_ELEMENT);
2781
135
          builder.push_combinator(Combinator::PseudoElement);
2782
65.5k
        } else {
2783
65.5k
          state.insert(SelectorParsingState::AFTER_UNKNOWN_PSEUDO_ELEMENT);
2784
65.5k
        }
2785
65.6k
        if !p.accepts_state_pseudo_classes() {
2786
0
          state.insert(SelectorParsingState::AFTER_NON_STATEFUL_PSEUDO_ELEMENT);
2787
65.6k
        }
2788
65.6k
        if p.is_webkit_scrollbar() {
2789
2
          state.insert(SelectorParsingState::AFTER_WEBKIT_SCROLLBAR);
2790
65.6k
        }
2791
65.6k
        if p.is_view_transition() {
2792
0
          state.insert(SelectorParsingState::AFTER_VIEW_TRANSITION);
2793
65.6k
        }
2794
65.6k
        builder.push_simple_selector(Component::PseudoElement(p));
2795
      }
2796
    }
2797
  }
2798
2.31M
  Ok(empty)
2799
2.48M
}
parcel_selectors::parser::parse_compound_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>
Line
Count
Source
2696
2.48M
fn parse_compound_selector<'i, 't, P, Impl>(
2697
2.48M
  parser: &P,
2698
2.48M
  state: &mut SelectorParsingState,
2699
2.48M
  input: &mut CssParser<'i, 't>,
2700
2.48M
  builder: &mut SelectorBuilder<'i, Impl>,
2701
2.48M
) -> Result<bool, ParseError<'i, P::Error>>
2702
2.48M
where
2703
2.48M
  P: Parser<'i, Impl = Impl>,
2704
2.48M
  Impl: SelectorImpl<'i>,
2705
{
2706
2.48M
  input.skip_whitespace();
2707
2708
2.48M
  let mut empty = true;
2709
2.48M
  if parser.is_nesting_allowed() && input.try_parse(|input| input.expect_delim('&')).is_ok() {
2710
2.61k
    state.insert(SelectorParsingState::AFTER_NESTING);
2711
2.61k
    builder.push_simple_selector(Component::Nesting);
2712
2.61k
    empty = false;
2713
2.48M
  }
2714
2715
2.48M
  if parse_type_selector(parser, input, *state, builder)? {
2716
2.02M
    empty = false;
2717
2.02M
  }
2718
2719
  loop {
2720
3.32M
    let result = match parse_one_simple_selector(parser, input, state)? {
2721
2.31M
      None => break,
2722
853k
      Some(result) => result,
2723
    };
2724
2725
853k
    if empty {
2726
214k
      if let Some(url) = parser.default_namespace() {
2727
        // If there was no explicit type selector, but there is a
2728
        // default namespace, there is an implicit "<defaultns>|*" type
2729
        // selector. Except for :host() or :not() / :is() / :where(),
2730
        // where we ignore it.
2731
        //
2732
        // https://drafts.csswg.org/css-scoping/#host-element-in-tree:
2733
        //
2734
        //     When considered within its own shadow trees, the shadow
2735
        //     host is featureless. Only the :host, :host(), and
2736
        //     :host-context() pseudo-classes are allowed to match it.
2737
        //
2738
        // https://drafts.csswg.org/selectors-4/#featureless:
2739
        //
2740
        //     A featureless element does not match any selector at all,
2741
        //     except those it is explicitly defined to match. If a
2742
        //     given selector is allowed to match a featureless element,
2743
        //     it must do so while ignoring the default namespace.
2744
        //
2745
        // https://drafts.csswg.org/selectors-4/#matches
2746
        //
2747
        //     Default namespace declarations do not affect the compound
2748
        //     selector representing the subject of any selector within
2749
        //     a :is() pseudo-class, unless that compound selector
2750
        //     contains an explicit universal selector or type selector.
2751
        //
2752
        //     (Similar quotes for :where() / :not())
2753
        //
2754
0
        let ignore_default_ns = state.intersects(SelectorParsingState::SKIP_DEFAULT_NAMESPACE)
2755
0
          || matches!(result, SimpleSelectorParseResult::SimpleSelector(Component::Host(..)));
2756
0
        if !ignore_default_ns {
2757
0
          builder.push_simple_selector(Component::DefaultNamespace(url));
2758
0
        }
2759
214k
      }
2760
639k
    }
2761
2762
853k
    empty = false;
2763
2764
853k
    match result {
2765
788k
      SimpleSelectorParseResult::SimpleSelector(s) => {
2766
788k
        builder.push_simple_selector(s);
2767
788k
      }
2768
0
      SimpleSelectorParseResult::PartPseudo(part_names) => {
2769
0
        state.insert(SelectorParsingState::AFTER_PART);
2770
0
        builder.push_combinator(Combinator::Part);
2771
0
        builder.push_simple_selector(Component::Part(part_names));
2772
0
      }
2773
0
      SimpleSelectorParseResult::SlottedPseudo(selector) => {
2774
0
        state.insert(SelectorParsingState::AFTER_SLOTTED);
2775
0
        builder.push_combinator(Combinator::SlotAssignment);
2776
0
        builder.push_simple_selector(Component::Slotted(selector));
2777
0
      }
2778
65.6k
      SimpleSelectorParseResult::PseudoElement(p) => {
2779
65.6k
        if !p.is_unknown() {
2780
135
          state.insert(SelectorParsingState::AFTER_PSEUDO_ELEMENT);
2781
135
          builder.push_combinator(Combinator::PseudoElement);
2782
65.5k
        } else {
2783
65.5k
          state.insert(SelectorParsingState::AFTER_UNKNOWN_PSEUDO_ELEMENT);
2784
65.5k
        }
2785
65.6k
        if !p.accepts_state_pseudo_classes() {
2786
0
          state.insert(SelectorParsingState::AFTER_NON_STATEFUL_PSEUDO_ELEMENT);
2787
65.6k
        }
2788
65.6k
        if p.is_webkit_scrollbar() {
2789
2
          state.insert(SelectorParsingState::AFTER_WEBKIT_SCROLLBAR);
2790
65.6k
        }
2791
65.6k
        if p.is_view_transition() {
2792
0
          state.insert(SelectorParsingState::AFTER_VIEW_TRANSITION);
2793
65.6k
        }
2794
65.6k
        builder.push_simple_selector(Component::PseudoElement(p));
2795
      }
2796
    }
2797
  }
2798
2.31M
  Ok(empty)
2799
2.48M
}
Unexecuted instantiation: parcel_selectors::parser::parse_compound_selector::<_, _>
2800
2801
3.61k
fn parse_is_or_where<'i, 't, P, Impl>(
2802
3.61k
  parser: &P,
2803
3.61k
  input: &mut CssParser<'i, 't>,
2804
3.61k
  state: &mut SelectorParsingState,
2805
3.61k
  component: impl FnOnce(Box<[Selector<'i, Impl>]>) -> Component<'i, Impl>,
2806
3.61k
) -> Result<Component<'i, Impl>, ParseError<'i, P::Error>>
2807
3.61k
where
2808
3.61k
  P: Parser<'i, Impl = Impl>,
2809
3.61k
  Impl: SelectorImpl<'i>,
2810
{
2811
3.61k
  debug_assert!(parser.parse_is_and_where());
2812
  // https://drafts.csswg.org/selectors/#matches-pseudo:
2813
  //
2814
  //     Pseudo-elements cannot be represented by the matches-any
2815
  //     pseudo-class; they are not valid within :is().
2816
  //
2817
3.61k
  let mut child_state =
2818
3.61k
    *state | SelectorParsingState::SKIP_DEFAULT_NAMESPACE | SelectorParsingState::DISALLOW_PSEUDOS;
2819
3.61k
  let inner = SelectorList::parse_with_state(
2820
3.61k
    parser,
2821
3.61k
    input,
2822
3.61k
    &mut child_state,
2823
3.61k
    parser.is_and_where_error_recovery(),
2824
3.61k
    NestingRequirement::None,
2825
0
  )?;
2826
3.61k
  if child_state.contains(SelectorParsingState::AFTER_NESTING) {
2827
2.02k
    state.insert(SelectorParsingState::AFTER_NESTING)
2828
1.58k
  }
2829
3.61k
  Ok(component(inner.0.into_vec().into_boxed_slice()))
2830
3.61k
}
parcel_selectors::parser::parse_is_or_where::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors, parcel_selectors::parser::parse_functional_pseudo_class<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#0}>
Line
Count
Source
2801
2.82k
fn parse_is_or_where<'i, 't, P, Impl>(
2802
2.82k
  parser: &P,
2803
2.82k
  input: &mut CssParser<'i, 't>,
2804
2.82k
  state: &mut SelectorParsingState,
2805
2.82k
  component: impl FnOnce(Box<[Selector<'i, Impl>]>) -> Component<'i, Impl>,
2806
2.82k
) -> Result<Component<'i, Impl>, ParseError<'i, P::Error>>
2807
2.82k
where
2808
2.82k
  P: Parser<'i, Impl = Impl>,
2809
2.82k
  Impl: SelectorImpl<'i>,
2810
{
2811
2.82k
  debug_assert!(parser.parse_is_and_where());
2812
  // https://drafts.csswg.org/selectors/#matches-pseudo:
2813
  //
2814
  //     Pseudo-elements cannot be represented by the matches-any
2815
  //     pseudo-class; they are not valid within :is().
2816
  //
2817
2.82k
  let mut child_state =
2818
2.82k
    *state | SelectorParsingState::SKIP_DEFAULT_NAMESPACE | SelectorParsingState::DISALLOW_PSEUDOS;
2819
2.82k
  let inner = SelectorList::parse_with_state(
2820
2.82k
    parser,
2821
2.82k
    input,
2822
2.82k
    &mut child_state,
2823
2.82k
    parser.is_and_where_error_recovery(),
2824
2.82k
    NestingRequirement::None,
2825
0
  )?;
2826
2.82k
  if child_state.contains(SelectorParsingState::AFTER_NESTING) {
2827
1.45k
    state.insert(SelectorParsingState::AFTER_NESTING)
2828
1.37k
  }
2829
2.82k
  Ok(component(inner.0.into_vec().into_boxed_slice()))
2830
2.82k
}
parcel_selectors::parser::parse_is_or_where::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors, parcel_selectors::parser::Component<lightningcss::selector::private::Selectors>::Is>
Line
Count
Source
2801
672
fn parse_is_or_where<'i, 't, P, Impl>(
2802
672
  parser: &P,
2803
672
  input: &mut CssParser<'i, 't>,
2804
672
  state: &mut SelectorParsingState,
2805
672
  component: impl FnOnce(Box<[Selector<'i, Impl>]>) -> Component<'i, Impl>,
2806
672
) -> Result<Component<'i, Impl>, ParseError<'i, P::Error>>
2807
672
where
2808
672
  P: Parser<'i, Impl = Impl>,
2809
672
  Impl: SelectorImpl<'i>,
2810
{
2811
672
  debug_assert!(parser.parse_is_and_where());
2812
  // https://drafts.csswg.org/selectors/#matches-pseudo:
2813
  //
2814
  //     Pseudo-elements cannot be represented by the matches-any
2815
  //     pseudo-class; they are not valid within :is().
2816
  //
2817
672
  let mut child_state =
2818
672
    *state | SelectorParsingState::SKIP_DEFAULT_NAMESPACE | SelectorParsingState::DISALLOW_PSEUDOS;
2819
672
  let inner = SelectorList::parse_with_state(
2820
672
    parser,
2821
672
    input,
2822
672
    &mut child_state,
2823
672
    parser.is_and_where_error_recovery(),
2824
672
    NestingRequirement::None,
2825
0
  )?;
2826
672
  if child_state.contains(SelectorParsingState::AFTER_NESTING) {
2827
561
    state.insert(SelectorParsingState::AFTER_NESTING)
2828
111
  }
2829
672
  Ok(component(inner.0.into_vec().into_boxed_slice()))
2830
672
}
parcel_selectors::parser::parse_is_or_where::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors, parcel_selectors::parser::Component<lightningcss::selector::private::Selectors>::Where>
Line
Count
Source
2801
113
fn parse_is_or_where<'i, 't, P, Impl>(
2802
113
  parser: &P,
2803
113
  input: &mut CssParser<'i, 't>,
2804
113
  state: &mut SelectorParsingState,
2805
113
  component: impl FnOnce(Box<[Selector<'i, Impl>]>) -> Component<'i, Impl>,
2806
113
) -> Result<Component<'i, Impl>, ParseError<'i, P::Error>>
2807
113
where
2808
113
  P: Parser<'i, Impl = Impl>,
2809
113
  Impl: SelectorImpl<'i>,
2810
{
2811
113
  debug_assert!(parser.parse_is_and_where());
2812
  // https://drafts.csswg.org/selectors/#matches-pseudo:
2813
  //
2814
  //     Pseudo-elements cannot be represented by the matches-any
2815
  //     pseudo-class; they are not valid within :is().
2816
  //
2817
113
  let mut child_state =
2818
113
    *state | SelectorParsingState::SKIP_DEFAULT_NAMESPACE | SelectorParsingState::DISALLOW_PSEUDOS;
2819
113
  let inner = SelectorList::parse_with_state(
2820
113
    parser,
2821
113
    input,
2822
113
    &mut child_state,
2823
113
    parser.is_and_where_error_recovery(),
2824
113
    NestingRequirement::None,
2825
0
  )?;
2826
113
  if child_state.contains(SelectorParsingState::AFTER_NESTING) {
2827
11
    state.insert(SelectorParsingState::AFTER_NESTING)
2828
102
  }
2829
113
  Ok(component(inner.0.into_vec().into_boxed_slice()))
2830
113
}
Unexecuted instantiation: parcel_selectors::parser::parse_is_or_where::<_, _, _>
2831
2832
16
fn parse_has<'i, 't, P, Impl>(
2833
16
  parser: &P,
2834
16
  input: &mut CssParser<'i, 't>,
2835
16
  state: &mut SelectorParsingState,
2836
16
) -> Result<Component<'i, Impl>, ParseError<'i, P::Error>>
2837
16
where
2838
16
  P: Parser<'i, Impl = Impl>,
2839
16
  Impl: SelectorImpl<'i>,
2840
{
2841
16
  let mut child_state = *state;
2842
16
  let inner = SelectorList::parse_relative_with_state(
2843
16
    parser,
2844
16
    input,
2845
16
    &mut child_state,
2846
16
    parser.is_and_where_error_recovery(),
2847
16
    NestingRequirement::None,
2848
0
  )?;
2849
16
  if child_state.contains(SelectorParsingState::AFTER_NESTING) {
2850
0
    state.insert(SelectorParsingState::AFTER_NESTING)
2851
16
  }
2852
16
  Ok(Component::Has(inner.0.into_vec().into_boxed_slice()))
2853
16
}
parcel_selectors::parser::parse_has::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>
Line
Count
Source
2832
16
fn parse_has<'i, 't, P, Impl>(
2833
16
  parser: &P,
2834
16
  input: &mut CssParser<'i, 't>,
2835
16
  state: &mut SelectorParsingState,
2836
16
) -> Result<Component<'i, Impl>, ParseError<'i, P::Error>>
2837
16
where
2838
16
  P: Parser<'i, Impl = Impl>,
2839
16
  Impl: SelectorImpl<'i>,
2840
{
2841
16
  let mut child_state = *state;
2842
16
  let inner = SelectorList::parse_relative_with_state(
2843
16
    parser,
2844
16
    input,
2845
16
    &mut child_state,
2846
16
    parser.is_and_where_error_recovery(),
2847
16
    NestingRequirement::None,
2848
0
  )?;
2849
16
  if child_state.contains(SelectorParsingState::AFTER_NESTING) {
2850
0
    state.insert(SelectorParsingState::AFTER_NESTING)
2851
16
  }
2852
16
  Ok(Component::Has(inner.0.into_vec().into_boxed_slice()))
2853
16
}
Unexecuted instantiation: parcel_selectors::parser::parse_has::<_, _>
2854
2855
7.48k
fn parse_functional_pseudo_class<'i, 't, P, Impl>(
2856
7.48k
  parser: &P,
2857
7.48k
  input: &mut CssParser<'i, 't>,
2858
7.48k
  name: CowRcStr<'i>,
2859
7.48k
  state: &mut SelectorParsingState,
2860
7.48k
) -> Result<Component<'i, Impl>, ParseError<'i, P::Error>>
2861
7.48k
where
2862
7.48k
  P: Parser<'i, Impl = Impl>,
2863
7.48k
  Impl: SelectorImpl<'i>,
2864
{
2865
7.48k
  match_ignore_ascii_case! { &name,
2866
7.48k
      "nth-child" => return parse_nth_pseudo_class(parser, input, *state, NthType::Child),
2867
7.48k
      "nth-of-type" => return parse_nth_pseudo_class(parser, input, *state, NthType::OfType),
2868
7.48k
      "nth-last-child" => return parse_nth_pseudo_class(parser, input, *state, NthType::LastChild),
2869
7.48k
      "nth-last-of-type" => return parse_nth_pseudo_class(parser, input, *state, NthType::LastOfType),
2870
7.48k
      "nth-col" => return parse_nth_pseudo_class(parser, input, *state, NthType::Col),
2871
7.48k
      "nth-last-col" => return parse_nth_pseudo_class(parser, input, *state, NthType::LastCol),
2872
7.48k
      "is" if parser.parse_is_and_where() => return parse_is_or_where(parser, input, state, Component::Is),
2873
6.81k
      "where" if parser.parse_is_and_where() => return parse_is_or_where(parser, input, state, Component::Where),
2874
6.69k
      "has" => return parse_has(parser, input, state),
2875
6.68k
      "host" => {
2876
1
          if !state.allows_tree_structural_pseudo_classes() {
2877
0
              return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
2878
1
          }
2879
1
          return Ok(Component::Host(Some(parse_inner_compound_selector(parser, input, state)?)));
2880
      },
2881
6.68k
      "not" => {
2882
0
          return parse_negation(parser, input, state)
2883
      },
2884
6.68k
      _ => {}
2885
  }
2886
2887
6.68k
  if let Some(prefix) = parser.parse_any_prefix(&name) {
2888
2.82k
    return parse_is_or_where(parser, input, state, |selectors| Component::Any(prefix, selectors));
parcel_selectors::parser::parse_functional_pseudo_class::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#0}
Line
Count
Source
2888
2.82k
    return parse_is_or_where(parser, input, state, |selectors| Component::Any(prefix, selectors));
Unexecuted instantiation: parcel_selectors::parser::parse_functional_pseudo_class::<_, _>::{closure#0}
2889
3.85k
  }
2890
2891
3.85k
  if !state.allows_custom_functional_pseudo_classes() {
2892
0
    return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
2893
3.85k
  }
2894
2895
3.85k
  P::parse_non_ts_functional_pseudo_class(parser, name, input).map(Component::NonTSPseudoClass)
2896
7.48k
}
parcel_selectors::parser::parse_functional_pseudo_class::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>
Line
Count
Source
2855
7.48k
fn parse_functional_pseudo_class<'i, 't, P, Impl>(
2856
7.48k
  parser: &P,
2857
7.48k
  input: &mut CssParser<'i, 't>,
2858
7.48k
  name: CowRcStr<'i>,
2859
7.48k
  state: &mut SelectorParsingState,
2860
7.48k
) -> Result<Component<'i, Impl>, ParseError<'i, P::Error>>
2861
7.48k
where
2862
7.48k
  P: Parser<'i, Impl = Impl>,
2863
7.48k
  Impl: SelectorImpl<'i>,
2864
{
2865
7.48k
  match_ignore_ascii_case! { &name,
2866
7.48k
      "nth-child" => return parse_nth_pseudo_class(parser, input, *state, NthType::Child),
2867
7.48k
      "nth-of-type" => return parse_nth_pseudo_class(parser, input, *state, NthType::OfType),
2868
7.48k
      "nth-last-child" => return parse_nth_pseudo_class(parser, input, *state, NthType::LastChild),
2869
7.48k
      "nth-last-of-type" => return parse_nth_pseudo_class(parser, input, *state, NthType::LastOfType),
2870
7.48k
      "nth-col" => return parse_nth_pseudo_class(parser, input, *state, NthType::Col),
2871
7.48k
      "nth-last-col" => return parse_nth_pseudo_class(parser, input, *state, NthType::LastCol),
2872
7.48k
      "is" if parser.parse_is_and_where() => return parse_is_or_where(parser, input, state, Component::Is),
2873
6.81k
      "where" if parser.parse_is_and_where() => return parse_is_or_where(parser, input, state, Component::Where),
2874
6.69k
      "has" => return parse_has(parser, input, state),
2875
6.68k
      "host" => {
2876
1
          if !state.allows_tree_structural_pseudo_classes() {
2877
0
              return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
2878
1
          }
2879
1
          return Ok(Component::Host(Some(parse_inner_compound_selector(parser, input, state)?)));
2880
      },
2881
6.68k
      "not" => {
2882
0
          return parse_negation(parser, input, state)
2883
      },
2884
6.68k
      _ => {}
2885
  }
2886
2887
6.68k
  if let Some(prefix) = parser.parse_any_prefix(&name) {
2888
2.82k
    return parse_is_or_where(parser, input, state, |selectors| Component::Any(prefix, selectors));
2889
3.85k
  }
2890
2891
3.85k
  if !state.allows_custom_functional_pseudo_classes() {
2892
0
    return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
2893
3.85k
  }
2894
2895
3.85k
  P::parse_non_ts_functional_pseudo_class(parser, name, input).map(Component::NonTSPseudoClass)
2896
7.48k
}
Unexecuted instantiation: parcel_selectors::parser::parse_functional_pseudo_class::<_, _>
2897
2898
5
fn parse_nth_pseudo_class<'i, 't, P, Impl>(
2899
5
  parser: &P,
2900
5
  input: &mut CssParser<'i, 't>,
2901
5
  state: SelectorParsingState,
2902
5
  ty: NthType,
2903
5
) -> Result<Component<'i, Impl>, ParseError<'i, P::Error>>
2904
5
where
2905
5
  P: Parser<'i, Impl = Impl>,
2906
5
  Impl: SelectorImpl<'i>,
2907
{
2908
5
  if !state.allows_tree_structural_pseudo_classes() {
2909
0
    return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
2910
5
  }
2911
5
  let (a, b) = parse_nth(input)?;
2912
0
  let nth_data = NthSelectorData {
2913
0
    ty,
2914
0
    is_function: true,
2915
0
    a,
2916
0
    b,
2917
0
  };
2918
0
  if !ty.allows_of_selector() {
2919
0
    return Ok(Component::Nth(nth_data));
2920
0
  }
2921
2922
  // Try to parse "of <selector-list>".
2923
0
  if input.try_parse(|i| i.expect_ident_matching("of")).is_err() {
Unexecuted instantiation: parcel_selectors::parser::parse_nth_pseudo_class::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#0}
Unexecuted instantiation: parcel_selectors::parser::parse_nth_pseudo_class::<_, _>::{closure#0}
2924
0
    return Ok(Component::Nth(nth_data));
2925
0
  }
2926
  // Whitespace between "of" and the selector list is optional
2927
  // https://github.com/w3c/csswg-drafts/issues/8285
2928
0
  let mut child_state =
2929
0
    state | SelectorParsingState::SKIP_DEFAULT_NAMESPACE | SelectorParsingState::DISALLOW_PSEUDOS;
2930
0
  let selectors = SelectorList::parse_with_state(
2931
0
    parser,
2932
0
    input,
2933
0
    &mut child_state,
2934
0
    ParseErrorRecovery::IgnoreInvalidSelector,
2935
0
    NestingRequirement::None,
2936
0
  )?;
2937
0
  Ok(Component::NthOf(NthOfSelectorData::new(
2938
0
    nth_data,
2939
0
    selectors.0.into_vec().into_boxed_slice(),
2940
0
  )))
2941
5
}
parcel_selectors::parser::parse_nth_pseudo_class::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>
Line
Count
Source
2898
5
fn parse_nth_pseudo_class<'i, 't, P, Impl>(
2899
5
  parser: &P,
2900
5
  input: &mut CssParser<'i, 't>,
2901
5
  state: SelectorParsingState,
2902
5
  ty: NthType,
2903
5
) -> Result<Component<'i, Impl>, ParseError<'i, P::Error>>
2904
5
where
2905
5
  P: Parser<'i, Impl = Impl>,
2906
5
  Impl: SelectorImpl<'i>,
2907
{
2908
5
  if !state.allows_tree_structural_pseudo_classes() {
2909
0
    return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
2910
5
  }
2911
5
  let (a, b) = parse_nth(input)?;
2912
0
  let nth_data = NthSelectorData {
2913
0
    ty,
2914
0
    is_function: true,
2915
0
    a,
2916
0
    b,
2917
0
  };
2918
0
  if !ty.allows_of_selector() {
2919
0
    return Ok(Component::Nth(nth_data));
2920
0
  }
2921
2922
  // Try to parse "of <selector-list>".
2923
0
  if input.try_parse(|i| i.expect_ident_matching("of")).is_err() {
2924
0
    return Ok(Component::Nth(nth_data));
2925
0
  }
2926
  // Whitespace between "of" and the selector list is optional
2927
  // https://github.com/w3c/csswg-drafts/issues/8285
2928
0
  let mut child_state =
2929
0
    state | SelectorParsingState::SKIP_DEFAULT_NAMESPACE | SelectorParsingState::DISALLOW_PSEUDOS;
2930
0
  let selectors = SelectorList::parse_with_state(
2931
0
    parser,
2932
0
    input,
2933
0
    &mut child_state,
2934
0
    ParseErrorRecovery::IgnoreInvalidSelector,
2935
0
    NestingRequirement::None,
2936
0
  )?;
2937
0
  Ok(Component::NthOf(NthOfSelectorData::new(
2938
0
    nth_data,
2939
0
    selectors.0.into_vec().into_boxed_slice(),
2940
0
  )))
2941
5
}
Unexecuted instantiation: parcel_selectors::parser::parse_nth_pseudo_class::<_, _>
2942
2943
/// Returns whether the name corresponds to a CSS2 pseudo-element that
2944
/// can be specified with the single colon syntax (in addition to the
2945
/// double-colon syntax, which can be used for all pseudo-elements).
2946
297k
fn is_css2_pseudo_element(name: &str) -> bool {
2947
  // ** Do not add to this list! **
2948
297k
  match_ignore_ascii_case! { name,
2949
297k
      "before" | "after" | "first-line" | "first-letter" => true,
2950
297k
      _ => false,
2951
  }
2952
297k
}
2953
2954
/// Parse a simple selector other than a type selector.
2955
///
2956
/// * `Err(())`: Invalid selector, abort
2957
/// * `Ok(None)`: Not a simple selector, could be something else. `input` was not consumed.
2958
/// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element
2959
3.32M
fn parse_one_simple_selector<'i, 't, P, Impl>(
2960
3.32M
  parser: &P,
2961
3.32M
  input: &mut CssParser<'i, 't>,
2962
3.32M
  state: &mut SelectorParsingState,
2963
3.32M
) -> Result<Option<SimpleSelectorParseResult<'i, Impl>>, ParseError<'i, P::Error>>
2964
3.32M
where
2965
3.32M
  P: Parser<'i, Impl = Impl>,
2966
3.32M
  Impl: SelectorImpl<'i>,
2967
{
2968
3.32M
  let start = input.state();
2969
3.32M
  let token_location = input.current_source_location();
2970
3.32M
  let token = match input.next_including_whitespace().map(|t| t.clone()) {
parcel_selectors::parser::parse_one_simple_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#0}
Line
Count
Source
2970
2.26M
  let token = match input.next_including_whitespace().map(|t| t.clone()) {
Unexecuted instantiation: parcel_selectors::parser::parse_one_simple_selector::<_, _>::{closure#0}
2971
2.26M
    Ok(t) => t,
2972
    Err(..) => {
2973
1.06M
      input.reset(&start);
2974
1.06M
      return Ok(None);
2975
    }
2976
  };
2977
2978
54.8k
  Ok(Some(match token {
2979
20.8k
    Token::IDHash(id) => {
2980
20.8k
      if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
2981
2
        return Err(token_location.new_custom_error(
2982
2
          SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(Token::IDHash(id)),
2983
2
        ));
2984
20.8k
      }
2985
20.8k
      let id = Component::ID(id.into());
2986
20.8k
      SimpleSelectorParseResult::SimpleSelector(id)
2987
    }
2988
    Token::Delim('.') => {
2989
456k
      if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
2990
15
        return Err(token_location.new_custom_error(
2991
15
          SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(Token::Delim('.')),
2992
15
        ));
2993
456k
      }
2994
456k
      let location = input.current_source_location();
2995
456k
      let class = match *input.next_including_whitespace()? {
2996
416k
        Token::Ident(ref class) => class.clone(),
2997
31.1k
        ref t => {
2998
31.1k
          let e = SelectorParseErrorKind::ClassNeedsIdent(t.clone());
2999
31.1k
          return Err(location.new_custom_error(e));
3000
        }
3001
      };
3002
416k
      let class = Component::Class(class.into());
3003
416k
      SimpleSelectorParseResult::SimpleSelector(class)
3004
    }
3005
    Token::SquareBracketBlock => {
3006
1.50k
      if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
3007
1
        return Err(token_location.new_custom_error(
3008
1
          SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(Token::SquareBracketBlock),
3009
1
        ));
3010
1.49k
      }
3011
1.49k
      let attr = input.parse_nested_block(|input| parse_attribute_selector(parser, input))?;
parcel_selectors::parser::parse_one_simple_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#1}
Line
Count
Source
3011
1.49k
      let attr = input.parse_nested_block(|input| parse_attribute_selector(parser, input))?;
Unexecuted instantiation: parcel_selectors::parser::parse_one_simple_selector::<_, _>::{closure#1}
3012
292
      SimpleSelectorParseResult::SimpleSelector(attr)
3013
    }
3014
    Token::Colon => {
3015
481k
      let location = input.current_source_location();
3016
481k
      let (is_single_colon, next_token) = match input.next_including_whitespace()?.clone() {
3017
71.4k
        Token::Colon => (false, input.next_including_whitespace()?.clone()),
3018
403k
        t => (true, t),
3019
      };
3020
473k
      let (name, is_functional) = match next_token {
3021
355k
        Token::Ident(name) => (name, false),
3022
7.67k
        Token::Function(name) => (name, true),
3023
110k
        t => {
3024
110k
          let e = SelectorParseErrorKind::PseudoElementExpectedIdent(t);
3025
110k
          return Err(input.new_custom_error(e));
3026
        }
3027
      };
3028
362k
      let is_pseudo_element = !is_single_colon || is_css2_pseudo_element(&name);
3029
362k
      if is_pseudo_element {
3030
65.8k
        if !state.allows_pseudos() {
3031
112
          return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
3032
65.7k
        }
3033
65.7k
        let pseudo_element = if is_functional {
3034
189
          if P::parse_part(parser) && name.eq_ignore_ascii_case("part") {
3035
0
            if !state.allows_part() {
3036
0
              return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
3037
0
            }
3038
0
            let names = input.parse_nested_block(|input| {
3039
0
              let mut result = Vec::with_capacity(1);
3040
0
              result.push(input.expect_ident_cloned()?.into());
3041
0
              while !input.is_exhausted() {
3042
0
                result.push(input.expect_ident_cloned()?.into());
3043
              }
3044
0
              Ok(result.into_boxed_slice())
3045
0
            })?;
Unexecuted instantiation: parcel_selectors::parser::parse_one_simple_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#2}
Unexecuted instantiation: parcel_selectors::parser::parse_one_simple_selector::<_, _>::{closure#2}
3046
0
            return Ok(Some(SimpleSelectorParseResult::PartPseudo(names)));
3047
189
          }
3048
189
          if P::parse_slotted(parser) && name.eq_ignore_ascii_case("slotted") {
3049
0
            if !state.allows_slotted() {
3050
0
              return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
3051
0
            }
3052
0
            let selector =
3053
0
              input.parse_nested_block(|input| parse_inner_compound_selector(parser, input, state))?;
Unexecuted instantiation: parcel_selectors::parser::parse_one_simple_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#3}
Unexecuted instantiation: parcel_selectors::parser::parse_one_simple_selector::<_, _>::{closure#3}
3054
0
            return Ok(Some(SimpleSelectorParseResult::SlottedPseudo(selector)));
3055
189
          }
3056
189
          input.parse_nested_block(|input| P::parse_functional_pseudo_element(parser, name, input))?
parcel_selectors::parser::parse_one_simple_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#4}
Line
Count
Source
3056
189
          input.parse_nested_block(|input| P::parse_functional_pseudo_element(parser, name, input))?
Unexecuted instantiation: parcel_selectors::parser::parse_one_simple_selector::<_, _>::{closure#4}
3057
        } else {
3058
65.5k
          P::parse_pseudo_element(parser, location, name)?
3059
        };
3060
3061
65.6k
        if state.intersects(SelectorParsingState::AFTER_SLOTTED) && !pseudo_element.valid_after_slotted() {
3062
0
          return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
3063
65.6k
        }
3064
65.6k
        SimpleSelectorParseResult::PseudoElement(pseudo_element)
3065
      } else {
3066
297k
        let pseudo_class = if is_functional {
3067
7.48k
          input.parse_nested_block(|input| parse_functional_pseudo_class(parser, input, name, state))?
parcel_selectors::parser::parse_one_simple_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>::{closure#5}
Line
Count
Source
3067
7.48k
          input.parse_nested_block(|input| parse_functional_pseudo_class(parser, input, name, state))?
Unexecuted instantiation: parcel_selectors::parser::parse_one_simple_selector::<_, _>::{closure#5}
3068
        } else {
3069
289k
          parse_simple_pseudo_class(parser, location, name, *state)?
3070
        };
3071
295k
        SimpleSelectorParseResult::SimpleSelector(pseudo_class)
3072
      }
3073
    }
3074
54.8k
    Token::Delim('&') if parser.is_nesting_allowed() => {
3075
54.8k
      *state |= SelectorParsingState::AFTER_NESTING;
3076
54.8k
      SimpleSelectorParseResult::SimpleSelector(Component::Nesting)
3077
    }
3078
    _ => {
3079
1.25M
      input.reset(&start);
3080
1.25M
      return Ok(None);
3081
    }
3082
  }))
3083
3.32M
}
parcel_selectors::parser::parse_one_simple_selector::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>
Line
Count
Source
2959
3.32M
fn parse_one_simple_selector<'i, 't, P, Impl>(
2960
3.32M
  parser: &P,
2961
3.32M
  input: &mut CssParser<'i, 't>,
2962
3.32M
  state: &mut SelectorParsingState,
2963
3.32M
) -> Result<Option<SimpleSelectorParseResult<'i, Impl>>, ParseError<'i, P::Error>>
2964
3.32M
where
2965
3.32M
  P: Parser<'i, Impl = Impl>,
2966
3.32M
  Impl: SelectorImpl<'i>,
2967
{
2968
3.32M
  let start = input.state();
2969
3.32M
  let token_location = input.current_source_location();
2970
3.32M
  let token = match input.next_including_whitespace().map(|t| t.clone()) {
2971
2.26M
    Ok(t) => t,
2972
    Err(..) => {
2973
1.06M
      input.reset(&start);
2974
1.06M
      return Ok(None);
2975
    }
2976
  };
2977
2978
54.8k
  Ok(Some(match token {
2979
20.8k
    Token::IDHash(id) => {
2980
20.8k
      if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
2981
2
        return Err(token_location.new_custom_error(
2982
2
          SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(Token::IDHash(id)),
2983
2
        ));
2984
20.8k
      }
2985
20.8k
      let id = Component::ID(id.into());
2986
20.8k
      SimpleSelectorParseResult::SimpleSelector(id)
2987
    }
2988
    Token::Delim('.') => {
2989
456k
      if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
2990
15
        return Err(token_location.new_custom_error(
2991
15
          SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(Token::Delim('.')),
2992
15
        ));
2993
456k
      }
2994
456k
      let location = input.current_source_location();
2995
456k
      let class = match *input.next_including_whitespace()? {
2996
416k
        Token::Ident(ref class) => class.clone(),
2997
31.1k
        ref t => {
2998
31.1k
          let e = SelectorParseErrorKind::ClassNeedsIdent(t.clone());
2999
31.1k
          return Err(location.new_custom_error(e));
3000
        }
3001
      };
3002
416k
      let class = Component::Class(class.into());
3003
416k
      SimpleSelectorParseResult::SimpleSelector(class)
3004
    }
3005
    Token::SquareBracketBlock => {
3006
1.50k
      if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
3007
1
        return Err(token_location.new_custom_error(
3008
1
          SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(Token::SquareBracketBlock),
3009
1
        ));
3010
1.49k
      }
3011
1.49k
      let attr = input.parse_nested_block(|input| parse_attribute_selector(parser, input))?;
3012
292
      SimpleSelectorParseResult::SimpleSelector(attr)
3013
    }
3014
    Token::Colon => {
3015
481k
      let location = input.current_source_location();
3016
481k
      let (is_single_colon, next_token) = match input.next_including_whitespace()?.clone() {
3017
71.4k
        Token::Colon => (false, input.next_including_whitespace()?.clone()),
3018
403k
        t => (true, t),
3019
      };
3020
473k
      let (name, is_functional) = match next_token {
3021
355k
        Token::Ident(name) => (name, false),
3022
7.67k
        Token::Function(name) => (name, true),
3023
110k
        t => {
3024
110k
          let e = SelectorParseErrorKind::PseudoElementExpectedIdent(t);
3025
110k
          return Err(input.new_custom_error(e));
3026
        }
3027
      };
3028
362k
      let is_pseudo_element = !is_single_colon || is_css2_pseudo_element(&name);
3029
362k
      if is_pseudo_element {
3030
65.8k
        if !state.allows_pseudos() {
3031
112
          return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
3032
65.7k
        }
3033
65.7k
        let pseudo_element = if is_functional {
3034
189
          if P::parse_part(parser) && name.eq_ignore_ascii_case("part") {
3035
0
            if !state.allows_part() {
3036
0
              return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
3037
0
            }
3038
0
            let names = input.parse_nested_block(|input| {
3039
              let mut result = Vec::with_capacity(1);
3040
              result.push(input.expect_ident_cloned()?.into());
3041
              while !input.is_exhausted() {
3042
                result.push(input.expect_ident_cloned()?.into());
3043
              }
3044
              Ok(result.into_boxed_slice())
3045
0
            })?;
3046
0
            return Ok(Some(SimpleSelectorParseResult::PartPseudo(names)));
3047
189
          }
3048
189
          if P::parse_slotted(parser) && name.eq_ignore_ascii_case("slotted") {
3049
0
            if !state.allows_slotted() {
3050
0
              return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
3051
0
            }
3052
0
            let selector =
3053
0
              input.parse_nested_block(|input| parse_inner_compound_selector(parser, input, state))?;
3054
0
            return Ok(Some(SimpleSelectorParseResult::SlottedPseudo(selector)));
3055
189
          }
3056
189
          input.parse_nested_block(|input| P::parse_functional_pseudo_element(parser, name, input))?
3057
        } else {
3058
65.5k
          P::parse_pseudo_element(parser, location, name)?
3059
        };
3060
3061
65.6k
        if state.intersects(SelectorParsingState::AFTER_SLOTTED) && !pseudo_element.valid_after_slotted() {
3062
0
          return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
3063
65.6k
        }
3064
65.6k
        SimpleSelectorParseResult::PseudoElement(pseudo_element)
3065
      } else {
3066
297k
        let pseudo_class = if is_functional {
3067
7.48k
          input.parse_nested_block(|input| parse_functional_pseudo_class(parser, input, name, state))?
3068
        } else {
3069
289k
          parse_simple_pseudo_class(parser, location, name, *state)?
3070
        };
3071
295k
        SimpleSelectorParseResult::SimpleSelector(pseudo_class)
3072
      }
3073
    }
3074
54.8k
    Token::Delim('&') if parser.is_nesting_allowed() => {
3075
54.8k
      *state |= SelectorParsingState::AFTER_NESTING;
3076
54.8k
      SimpleSelectorParseResult::SimpleSelector(Component::Nesting)
3077
    }
3078
    _ => {
3079
1.25M
      input.reset(&start);
3080
1.25M
      return Ok(None);
3081
    }
3082
  }))
3083
3.32M
}
Unexecuted instantiation: parcel_selectors::parser::parse_one_simple_selector::<_, _>
3084
3085
289k
fn parse_simple_pseudo_class<'i, P, Impl>(
3086
289k
  parser: &P,
3087
289k
  location: SourceLocation,
3088
289k
  name: CowRcStr<'i>,
3089
289k
  state: SelectorParsingState,
3090
289k
) -> Result<Component<'i, Impl>, ParseError<'i, P::Error>>
3091
289k
where
3092
289k
  P: Parser<'i, Impl = Impl>,
3093
289k
  Impl: SelectorImpl<'i>,
3094
{
3095
289k
  if !state.allows_non_functional_pseudo_classes() {
3096
0
    return Err(location.new_custom_error(SelectorParseErrorKind::InvalidState));
3097
289k
  }
3098
3099
289k
  if state.allows_tree_structural_pseudo_classes() {
3100
289k
    match_ignore_ascii_case! { &name,
3101
289k
        "first-child" => return Ok(Component::Nth(NthSelectorData::first(/* of_type = */ false))),
3102
289k
        "last-child" => return Ok(Component::Nth(NthSelectorData::last(/* of_type = */ false))),
3103
289k
        "only-child" => return Ok(Component::Nth(NthSelectorData::only(/* of_type = */ false))),
3104
289k
        "root" => return Ok(Component::Root),
3105
289k
        "empty" => return Ok(Component::Empty),
3106
289k
        "scope" => return Ok(Component::Scope),
3107
289k
        "host" if P::parse_host(parser) => return Ok(Component::Host(None)),
3108
289k
        "first-of-type" => return Ok(Component::Nth(NthSelectorData::first(/* of_type = */ true))),
3109
289k
        "last-of-type" => return Ok(Component::Nth(NthSelectorData::last(/* of_type = */ true))),
3110
289k
        "only-of-type" => return Ok(Component::Nth(NthSelectorData::only(/* of_type = */ true))),
3111
289k
        _ => {},
3112
    }
3113
18
  }
3114
3115
  // The view-transition pseudo elements accept the :only-child pseudo class.
3116
  // https://w3c.github.io/csswg-drafts/css-view-transitions-1/#pseudo-root
3117
289k
  if state.intersects(SelectorParsingState::AFTER_VIEW_TRANSITION) {
3118
0
    match_ignore_ascii_case! { &name,
3119
0
        "only-child" => return Ok(Component::Nth(NthSelectorData::only(/* of_type = */ false))),
3120
0
        _ => {}
3121
    }
3122
289k
  }
3123
3124
289k
  let pseudo_class = P::parse_non_ts_pseudo_class(parser, location, name)?;
3125
289k
  if state.intersects(SelectorParsingState::AFTER_WEBKIT_SCROLLBAR) {
3126
0
    if !pseudo_class.is_valid_after_webkit_scrollbar() {
3127
0
      return Err(location.new_custom_error(SelectorParseErrorKind::InvalidPseudoClassAfterWebKitScrollbar));
3128
0
    }
3129
289k
  } else if state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT) {
3130
18
    if !pseudo_class.is_user_action_state() {
3131
18
      return Err(location.new_custom_error(SelectorParseErrorKind::InvalidPseudoClassAfterPseudoElement));
3132
0
    }
3133
289k
  } else if !pseudo_class.is_valid_before_webkit_scrollbar() {
3134
83
    return Err(location.new_custom_error(SelectorParseErrorKind::InvalidPseudoClassBeforeWebKitScrollbar));
3135
289k
  }
3136
289k
  Ok(Component::NonTSPseudoClass(pseudo_class))
3137
289k
}
parcel_selectors::parser::parse_simple_pseudo_class::<lightningcss::selector::SelectorParser, lightningcss::selector::private::Selectors>
Line
Count
Source
3085
289k
fn parse_simple_pseudo_class<'i, P, Impl>(
3086
289k
  parser: &P,
3087
289k
  location: SourceLocation,
3088
289k
  name: CowRcStr<'i>,
3089
289k
  state: SelectorParsingState,
3090
289k
) -> Result<Component<'i, Impl>, ParseError<'i, P::Error>>
3091
289k
where
3092
289k
  P: Parser<'i, Impl = Impl>,
3093
289k
  Impl: SelectorImpl<'i>,
3094
{
3095
289k
  if !state.allows_non_functional_pseudo_classes() {
3096
0
    return Err(location.new_custom_error(SelectorParseErrorKind::InvalidState));
3097
289k
  }
3098
3099
289k
  if state.allows_tree_structural_pseudo_classes() {
3100
289k
    match_ignore_ascii_case! { &name,
3101
289k
        "first-child" => return Ok(Component::Nth(NthSelectorData::first(/* of_type = */ false))),
3102
289k
        "last-child" => return Ok(Component::Nth(NthSelectorData::last(/* of_type = */ false))),
3103
289k
        "only-child" => return Ok(Component::Nth(NthSelectorData::only(/* of_type = */ false))),
3104
289k
        "root" => return Ok(Component::Root),
3105
289k
        "empty" => return Ok(Component::Empty),
3106
289k
        "scope" => return Ok(Component::Scope),
3107
289k
        "host" if P::parse_host(parser) => return Ok(Component::Host(None)),
3108
289k
        "first-of-type" => return Ok(Component::Nth(NthSelectorData::first(/* of_type = */ true))),
3109
289k
        "last-of-type" => return Ok(Component::Nth(NthSelectorData::last(/* of_type = */ true))),
3110
289k
        "only-of-type" => return Ok(Component::Nth(NthSelectorData::only(/* of_type = */ true))),
3111
289k
        _ => {},
3112
    }
3113
18
  }
3114
3115
  // The view-transition pseudo elements accept the :only-child pseudo class.
3116
  // https://w3c.github.io/csswg-drafts/css-view-transitions-1/#pseudo-root
3117
289k
  if state.intersects(SelectorParsingState::AFTER_VIEW_TRANSITION) {
3118
0
    match_ignore_ascii_case! { &name,
3119
0
        "only-child" => return Ok(Component::Nth(NthSelectorData::only(/* of_type = */ false))),
3120
0
        _ => {}
3121
    }
3122
289k
  }
3123
3124
289k
  let pseudo_class = P::parse_non_ts_pseudo_class(parser, location, name)?;
3125
289k
  if state.intersects(SelectorParsingState::AFTER_WEBKIT_SCROLLBAR) {
3126
0
    if !pseudo_class.is_valid_after_webkit_scrollbar() {
3127
0
      return Err(location.new_custom_error(SelectorParseErrorKind::InvalidPseudoClassAfterWebKitScrollbar));
3128
0
    }
3129
289k
  } else if state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT) {
3130
18
    if !pseudo_class.is_user_action_state() {
3131
18
      return Err(location.new_custom_error(SelectorParseErrorKind::InvalidPseudoClassAfterPseudoElement));
3132
0
    }
3133
289k
  } else if !pseudo_class.is_valid_before_webkit_scrollbar() {
3134
83
    return Err(location.new_custom_error(SelectorParseErrorKind::InvalidPseudoClassBeforeWebKitScrollbar));
3135
289k
  }
3136
289k
  Ok(Component::NonTSPseudoClass(pseudo_class))
3137
289k
}
Unexecuted instantiation: parcel_selectors::parser::parse_simple_pseudo_class::<_, _>
3138
3139
// NB: pub module in order to access the DummyParser
3140
#[cfg(test)]
3141
pub mod tests {
3142
  use super::*;
3143
  use crate::builder::SelectorFlags;
3144
  use crate::parser;
3145
  use cssparser::{serialize_identifier, serialize_string, Parser as CssParser, ParserInput, ToCss};
3146
  use std::collections::HashMap;
3147
  use std::fmt;
3148
3149
  #[derive(Clone, Debug, Eq, PartialEq, Hash)]
3150
  pub enum PseudoClass {
3151
    Hover,
3152
    Active,
3153
    Lang(String),
3154
  }
3155
3156
  #[derive(Clone, Debug, Eq, PartialEq, Hash)]
3157
  pub enum PseudoElement {
3158
    Before,
3159
    After,
3160
  }
3161
3162
  impl<'i> parser::PseudoElement<'i> for PseudoElement {
3163
    type Impl = DummySelectorImpl;
3164
3165
    fn accepts_state_pseudo_classes(&self) -> bool {
3166
      true
3167
    }
3168
3169
    fn valid_after_slotted(&self) -> bool {
3170
      true
3171
    }
3172
  }
3173
3174
  impl<'i> parser::NonTSPseudoClass<'i> for PseudoClass {
3175
    type Impl = DummySelectorImpl;
3176
3177
    #[inline]
3178
    fn is_active_or_hover(&self) -> bool {
3179
      matches!(*self, PseudoClass::Active | PseudoClass::Hover)
3180
    }
3181
3182
    #[inline]
3183
    fn is_user_action_state(&self) -> bool {
3184
      self.is_active_or_hover()
3185
    }
3186
  }
3187
3188
  impl ToCss for PseudoClass {
3189
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
3190
    where
3191
      W: fmt::Write,
3192
    {
3193
      match *self {
3194
        PseudoClass::Hover => dest.write_str(":hover"),
3195
        PseudoClass::Active => dest.write_str(":active"),
3196
        PseudoClass::Lang(ref lang) => {
3197
          dest.write_str(":lang(")?;
3198
          serialize_identifier(lang, dest)?;
3199
          dest.write_char(')')
3200
        }
3201
      }
3202
    }
3203
  }
3204
3205
  impl ToCss for PseudoElement {
3206
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
3207
    where
3208
      W: fmt::Write,
3209
    {
3210
      match *self {
3211
        PseudoElement::Before => dest.write_str("::before"),
3212
        PseudoElement::After => dest.write_str("::after"),
3213
      }
3214
    }
3215
  }
3216
3217
  #[derive(Clone, Debug, PartialEq)]
3218
  pub struct DummySelectorImpl;
3219
3220
  #[derive(Default)]
3221
  pub struct DummyParser {
3222
    default_ns: Option<DummyAtom>,
3223
    ns_prefixes: HashMap<DummyAtom, DummyAtom>,
3224
  }
3225
3226
  impl DummyParser {
3227
    fn default_with_namespace(default_ns: DummyAtom) -> DummyParser {
3228
      DummyParser {
3229
        default_ns: Some(default_ns),
3230
        ns_prefixes: Default::default(),
3231
      }
3232
    }
3233
  }
3234
3235
  impl<'i> SelectorImpl<'i> for DummySelectorImpl {
3236
    type ExtraMatchingData = ();
3237
    type AttrValue = DummyAttrValue;
3238
    type Identifier = DummyAtom;
3239
    type LocalName = DummyAtom;
3240
    type NamespaceUrl = DummyAtom;
3241
    type NamespacePrefix = DummyAtom;
3242
    type BorrowedLocalName = DummyAtom;
3243
    type BorrowedNamespaceUrl = DummyAtom;
3244
    type NonTSPseudoClass = PseudoClass;
3245
    type PseudoElement = PseudoElement;
3246
    type VendorPrefix = u8;
3247
  }
3248
3249
  #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
3250
  pub struct DummyAttrValue(String);
3251
3252
  impl ToCss for DummyAttrValue {
3253
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
3254
    where
3255
      W: fmt::Write,
3256
    {
3257
      serialize_string(&self.0, dest)
3258
    }
3259
  }
3260
3261
  impl AsRef<str> for DummyAttrValue {
3262
    fn as_ref(&self) -> &str {
3263
      self.0.as_ref()
3264
    }
3265
  }
3266
3267
  impl<'a> From<&'a str> for DummyAttrValue {
3268
    fn from(string: &'a str) -> Self {
3269
      Self(string.into())
3270
    }
3271
  }
3272
3273
  impl<'a> From<std::borrow::Cow<'a, str>> for DummyAttrValue {
3274
    fn from(string: std::borrow::Cow<'a, str>) -> Self {
3275
      Self(string.to_string())
3276
    }
3277
  }
3278
3279
  impl<'a> From<CowRcStr<'a>> for DummyAttrValue {
3280
    fn from(string: CowRcStr<'a>) -> Self {
3281
      Self(string.to_string())
3282
    }
3283
  }
3284
3285
  #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
3286
  pub struct DummyAtom(String);
3287
3288
  impl ToCss for DummyAtom {
3289
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
3290
    where
3291
      W: fmt::Write,
3292
    {
3293
      serialize_identifier(&self.0, dest)
3294
    }
3295
  }
3296
3297
  impl From<String> for DummyAtom {
3298
    fn from(string: String) -> Self {
3299
      DummyAtom(string)
3300
    }
3301
  }
3302
3303
  impl<'a> From<&'a str> for DummyAtom {
3304
    fn from(string: &'a str) -> Self {
3305
      DummyAtom(string.into())
3306
    }
3307
  }
3308
3309
  impl<'a> From<CowRcStr<'a>> for DummyAtom {
3310
    fn from(string: CowRcStr<'a>) -> Self {
3311
      DummyAtom(string.to_string())
3312
    }
3313
  }
3314
3315
  impl AsRef<str> for DummyAtom {
3316
    fn as_ref(&self) -> &str {
3317
      self.0.as_ref()
3318
    }
3319
  }
3320
3321
  impl<'a> From<std::borrow::Cow<'a, str>> for DummyAtom {
3322
    fn from(string: std::borrow::Cow<'a, str>) -> Self {
3323
      Self(string.to_string())
3324
    }
3325
  }
3326
3327
  impl<'i> Parser<'i> for DummyParser {
3328
    type Impl = DummySelectorImpl;
3329
    type Error = SelectorParseErrorKind<'i>;
3330
3331
    fn parse_slotted(&self) -> bool {
3332
      true
3333
    }
3334
3335
    fn parse_is_and_where(&self) -> bool {
3336
      true
3337
    }
3338
3339
    fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
3340
      ParseErrorRecovery::DiscardList
3341
    }
3342
3343
    fn parse_part(&self) -> bool {
3344
      true
3345
    }
3346
3347
    fn parse_non_ts_pseudo_class(
3348
      &self,
3349
      location: SourceLocation,
3350
      name: CowRcStr<'i>,
3351
    ) -> Result<PseudoClass, SelectorParseError<'i>> {
3352
      match_ignore_ascii_case! { &name,
3353
          "hover" => return Ok(PseudoClass::Hover),
3354
          "active" => return Ok(PseudoClass::Active),
3355
          _ => {}
3356
      }
3357
      Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClass(name)))
3358
    }
3359
3360
    fn parse_non_ts_functional_pseudo_class<'t>(
3361
      &self,
3362
      name: CowRcStr<'i>,
3363
      parser: &mut CssParser<'i, 't>,
3364
    ) -> Result<PseudoClass, SelectorParseError<'i>> {
3365
      match_ignore_ascii_case! { &name,
3366
          "lang" => {
3367
              let lang = parser.expect_ident_or_string()?.as_ref().to_owned();
3368
              return Ok(PseudoClass::Lang(lang));
3369
          },
3370
          _ => {}
3371
      }
3372
      Err(parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClass(name)))
3373
    }
3374
3375
    fn parse_pseudo_element(
3376
      &self,
3377
      location: SourceLocation,
3378
      name: CowRcStr<'i>,
3379
    ) -> Result<PseudoElement, SelectorParseError<'i>> {
3380
      match_ignore_ascii_case! { &name,
3381
          "before" => return Ok(PseudoElement::Before),
3382
          "after" => return Ok(PseudoElement::After),
3383
          _ => {}
3384
      }
3385
      Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoElement(name)))
3386
    }
3387
3388
    fn default_namespace(&self) -> Option<DummyAtom> {
3389
      self.default_ns.clone()
3390
    }
3391
3392
    fn namespace_for_prefix(&self, prefix: &DummyAtom) -> Option<DummyAtom> {
3393
      self.ns_prefixes.get(prefix).cloned()
3394
    }
3395
  }
3396
3397
  fn parse<'i>(input: &'i str) -> Result<SelectorList<'i, DummySelectorImpl>, SelectorParseError<'i>> {
3398
    parse_ns(input, &DummyParser::default())
3399
  }
3400
3401
  // fn parse_expected<'i, 'a>(
3402
  //   input: &'i str,
3403
  //   expected: Option<&'a str>,
3404
  // ) -> Result<SelectorList<'i, DummySelectorImpl>, SelectorParseError<'i>> {
3405
  //   parse_ns_expected(input, &DummyParser::default(), expected)
3406
  // }
3407
3408
  fn parse_ns<'i>(
3409
    input: &'i str,
3410
    parser: &DummyParser,
3411
  ) -> Result<SelectorList<'i, DummySelectorImpl>, SelectorParseError<'i>> {
3412
    parse_ns_expected(input, parser, None)
3413
  }
3414
3415
  fn parse_ns_expected<'i, 'a>(
3416
    input: &'i str,
3417
    parser: &DummyParser,
3418
    expected: Option<&'a str>,
3419
  ) -> Result<SelectorList<'i, DummySelectorImpl>, SelectorParseError<'i>> {
3420
    let mut parser_input = ParserInput::new(input);
3421
    let result = SelectorList::parse(
3422
      parser,
3423
      &mut CssParser::new(&mut parser_input),
3424
      ParseErrorRecovery::DiscardList,
3425
      NestingRequirement::None,
3426
    );
3427
    if let Ok(ref selectors) = result {
3428
      assert_eq!(selectors.0.len(), 1);
3429
      // We can't assume that the serialized parsed selector will equal
3430
      // the input; for example, if there is no default namespace, '*|foo'
3431
      // should serialize to 'foo'.
3432
      assert_eq!(
3433
        selectors.0[0].to_css_string(),
3434
        match expected {
3435
          Some(x) => x,
3436
          None => input,
3437
        }
3438
      );
3439
    }
3440
    result
3441
  }
3442
3443
  fn specificity(a: u32, b: u32, c: u32) -> u32 {
3444
    a << 20 | b << 10 | c
3445
  }
3446
3447
  #[test]
3448
  fn test_empty() {
3449
    let mut input = ParserInput::new(":empty");
3450
    let list = SelectorList::parse(
3451
      &DummyParser::default(),
3452
      &mut CssParser::new(&mut input),
3453
      ParseErrorRecovery::DiscardList,
3454
      NestingRequirement::None,
3455
    );
3456
    assert!(list.is_ok());
3457
  }
3458
3459
  const MATHML: &str = "http://www.w3.org/1998/Math/MathML";
3460
  const SVG: &str = "http://www.w3.org/2000/svg";
3461
3462
  #[test]
3463
  fn test_parsing() {
3464
    assert!(parse("").is_err());
3465
    assert!(parse(":lang(4)").is_err());
3466
    assert!(parse(":lang(en US)").is_err());
3467
    assert_eq!(
3468
      parse("EeÉ"),
3469
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3470
        vec![Component::LocalName(LocalName {
3471
          name: DummyAtom::from("EeÉ"),
3472
          lower_name: DummyAtom::from("eeÉ"),
3473
        })],
3474
        specificity(0, 0, 1),
3475
        Default::default(),
3476
      )]))
3477
    );
3478
    assert_eq!(
3479
      parse("|e"),
3480
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3481
        vec![
3482
          Component::ExplicitNoNamespace,
3483
          Component::LocalName(LocalName {
3484
            name: DummyAtom::from("e"),
3485
            lower_name: DummyAtom::from("e"),
3486
          }),
3487
        ],
3488
        specificity(0, 0, 1),
3489
        Default::default(),
3490
      )]))
3491
    );
3492
    // When the default namespace is not set, *| should be elided.
3493
    // https://github.com/servo/servo/pull/17537
3494
    // assert_eq!(
3495
    //   parse_expected("*|e", Some("e")),
3496
    //   Ok(SelectorList::from_vec(vec![Selector::from_vec(
3497
    //     vec![Component::LocalName(LocalName {
3498
    //       name: DummyAtom::from("e"),
3499
    //       lower_name: DummyAtom::from("e"),
3500
    //     })],
3501
    //     specificity(0, 0, 1),
3502
    //     Default::default(),
3503
    //   )]))
3504
    // );
3505
    // When the default namespace is set, *| should _not_ be elided (as foo
3506
    // is no longer equivalent to *|foo--the former is only for foo in the
3507
    // default namespace).
3508
    // https://github.com/servo/servo/issues/16020
3509
    // assert_eq!(
3510
    //   parse_ns(
3511
    //     "*|e",
3512
    //     &DummyParser::default_with_namespace(DummyAtom::from("https://mozilla.org"))
3513
    //   ),
3514
    //   Ok(SelectorList::from_vec(vec![Selector::from_vec(
3515
    //     vec![
3516
    //       Component::ExplicitAnyNamespace,
3517
    //       Component::LocalName(LocalName {
3518
    //         name: DummyAtom::from("e"),
3519
    //         lower_name: DummyAtom::from("e"),
3520
    //       }),
3521
    //     ],
3522
    //     specificity(0, 0, 1),
3523
    //     Default::default(),
3524
    //   )]))
3525
    // );
3526
    assert_eq!(
3527
      parse("*"),
3528
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3529
        vec![Component::ExplicitUniversalType],
3530
        specificity(0, 0, 0),
3531
        Default::default(),
3532
      )]))
3533
    );
3534
    assert_eq!(
3535
      parse("|*"),
3536
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3537
        vec![Component::ExplicitNoNamespace, Component::ExplicitUniversalType,],
3538
        specificity(0, 0, 0),
3539
        Default::default(),
3540
      )]))
3541
    );
3542
    // assert_eq!(
3543
    //   parse_expected("*|*", Some("*")),
3544
    //   Ok(SelectorList::from_vec(vec![Selector::from_vec(
3545
    //     vec![Component::ExplicitUniversalType],
3546
    //     specificity(0, 0, 0),
3547
    //     Default::default(),
3548
    //   )]))
3549
    // );
3550
    assert_eq!(
3551
      parse_ns(
3552
        "*|*",
3553
        &DummyParser::default_with_namespace(DummyAtom::from("https://mozilla.org"))
3554
      ),
3555
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3556
        vec![Component::ExplicitAnyNamespace, Component::ExplicitUniversalType,],
3557
        specificity(0, 0, 0),
3558
        Default::default(),
3559
      )]))
3560
    );
3561
    assert_eq!(
3562
      parse(".foo:lang(en-US)"),
3563
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3564
        vec![
3565
          Component::Class(DummyAtom::from("foo")),
3566
          Component::NonTSPseudoClass(PseudoClass::Lang("en-US".to_owned())),
3567
        ],
3568
        specificity(0, 2, 0),
3569
        Default::default(),
3570
      )]))
3571
    );
3572
    assert_eq!(
3573
      parse("#bar"),
3574
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3575
        vec![Component::ID(DummyAtom::from("bar"))],
3576
        specificity(1, 0, 0),
3577
        Default::default(),
3578
      )]))
3579
    );
3580
    assert_eq!(
3581
      parse("e.foo#bar"),
3582
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3583
        vec![
3584
          Component::LocalName(LocalName {
3585
            name: DummyAtom::from("e"),
3586
            lower_name: DummyAtom::from("e"),
3587
          }),
3588
          Component::Class(DummyAtom::from("foo")),
3589
          Component::ID(DummyAtom::from("bar")),
3590
        ],
3591
        specificity(1, 1, 1),
3592
        Default::default(),
3593
      )]))
3594
    );
3595
    assert_eq!(
3596
      parse("e.foo #bar"),
3597
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3598
        vec![
3599
          Component::LocalName(LocalName {
3600
            name: DummyAtom::from("e"),
3601
            lower_name: DummyAtom::from("e"),
3602
          }),
3603
          Component::Class(DummyAtom::from("foo")),
3604
          Component::Combinator(Combinator::Descendant),
3605
          Component::ID(DummyAtom::from("bar")),
3606
        ],
3607
        specificity(1, 1, 1),
3608
        Default::default(),
3609
      )]))
3610
    );
3611
    // Default namespace does not apply to attribute selectors
3612
    // https://github.com/mozilla/servo/pull/1652
3613
    let mut parser = DummyParser::default();
3614
    assert_eq!(
3615
      parse_ns("[Foo]", &parser),
3616
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3617
        vec![Component::AttributeInNoNamespaceExists {
3618
          local_name: DummyAtom::from("Foo"),
3619
          local_name_lower: DummyAtom::from("foo"),
3620
        }],
3621
        specificity(0, 1, 0),
3622
        Default::default(),
3623
      )]))
3624
    );
3625
    assert!(parse_ns("svg|circle", &parser).is_err());
3626
    parser.ns_prefixes.insert(DummyAtom("svg".into()), DummyAtom(SVG.into()));
3627
    assert_eq!(
3628
      parse_ns("svg|circle", &parser),
3629
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3630
        vec![
3631
          Component::Namespace(DummyAtom("svg".into()), SVG.into()),
3632
          Component::LocalName(LocalName {
3633
            name: DummyAtom::from("circle"),
3634
            lower_name: DummyAtom::from("circle"),
3635
          }),
3636
        ],
3637
        specificity(0, 0, 1),
3638
        Default::default(),
3639
      )]))
3640
    );
3641
    assert_eq!(
3642
      parse_ns("svg|*", &parser),
3643
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3644
        vec![
3645
          Component::Namespace(DummyAtom("svg".into()), SVG.into()),
3646
          Component::ExplicitUniversalType,
3647
        ],
3648
        specificity(0, 0, 0),
3649
        Default::default(),
3650
      )]))
3651
    );
3652
    // Default namespace does not apply to attribute selectors
3653
    // https://github.com/mozilla/servo/pull/1652
3654
    // but it does apply to implicit type selectors
3655
    // https://github.com/servo/rust-selectors/pull/82
3656
    parser.default_ns = Some(MATHML.into());
3657
    assert_eq!(
3658
      parse_ns("[Foo]", &parser),
3659
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3660
        vec![
3661
          Component::DefaultNamespace(MATHML.into()),
3662
          Component::AttributeInNoNamespaceExists {
3663
            local_name: DummyAtom::from("Foo"),
3664
            local_name_lower: DummyAtom::from("foo"),
3665
          },
3666
        ],
3667
        specificity(0, 1, 0),
3668
        Default::default(),
3669
      )]))
3670
    );
3671
    // Default namespace does apply to type selectors
3672
    assert_eq!(
3673
      parse_ns("e", &parser),
3674
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3675
        vec![
3676
          Component::DefaultNamespace(MATHML.into()),
3677
          Component::LocalName(LocalName {
3678
            name: DummyAtom::from("e"),
3679
            lower_name: DummyAtom::from("e"),
3680
          }),
3681
        ],
3682
        specificity(0, 0, 1),
3683
        Default::default(),
3684
      )]))
3685
    );
3686
    assert_eq!(
3687
      parse_ns("*", &parser),
3688
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3689
        vec![
3690
          Component::DefaultNamespace(MATHML.into()),
3691
          Component::ExplicitUniversalType,
3692
        ],
3693
        specificity(0, 0, 0),
3694
        Default::default(),
3695
      )]))
3696
    );
3697
    assert_eq!(
3698
      parse_ns("*|*", &parser),
3699
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3700
        vec![Component::ExplicitAnyNamespace, Component::ExplicitUniversalType,],
3701
        specificity(0, 0, 0),
3702
        Default::default(),
3703
      )]))
3704
    );
3705
    // Default namespace applies to universal and type selectors inside :not and :matches,
3706
    // but not otherwise.
3707
    assert_eq!(
3708
      parse_ns(":not(.cl)", &parser),
3709
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3710
        vec![
3711
          Component::DefaultNamespace(MATHML.into()),
3712
          Component::Negation(
3713
            vec![Selector::from_vec(
3714
              vec![Component::Class(DummyAtom::from("cl"))],
3715
              specificity(0, 1, 0),
3716
              Default::default(),
3717
            )]
3718
            .into_boxed_slice()
3719
          ),
3720
        ],
3721
        specificity(0, 1, 0),
3722
        Default::default(),
3723
      )]))
3724
    );
3725
    assert_eq!(
3726
      parse_ns(":not(*)", &parser),
3727
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3728
        vec![
3729
          Component::DefaultNamespace(MATHML.into()),
3730
          Component::Negation(
3731
            vec![Selector::from_vec(
3732
              vec![
3733
                Component::DefaultNamespace(MATHML.into()),
3734
                Component::ExplicitUniversalType,
3735
              ],
3736
              specificity(0, 0, 0),
3737
              Default::default(),
3738
            )]
3739
            .into_boxed_slice(),
3740
          ),
3741
        ],
3742
        specificity(0, 0, 0),
3743
        Default::default(),
3744
      )]))
3745
    );
3746
    assert_eq!(
3747
      parse_ns(":not(e)", &parser),
3748
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3749
        vec![
3750
          Component::DefaultNamespace(MATHML.into()),
3751
          Component::Negation(
3752
            vec![Selector::from_vec(
3753
              vec![
3754
                Component::DefaultNamespace(MATHML.into()),
3755
                Component::LocalName(LocalName {
3756
                  name: DummyAtom::from("e"),
3757
                  lower_name: DummyAtom::from("e"),
3758
                }),
3759
              ],
3760
              specificity(0, 0, 1),
3761
              Default::default(),
3762
            ),]
3763
            .into_boxed_slice()
3764
          ),
3765
        ],
3766
        specificity(0, 0, 1),
3767
        Default::default(),
3768
      )]))
3769
    );
3770
    assert_eq!(
3771
      parse("[attr|=\"foo\"]"),
3772
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3773
        vec![Component::AttributeInNoNamespace {
3774
          local_name: DummyAtom::from("attr"),
3775
          operator: AttrSelectorOperator::DashMatch,
3776
          value: DummyAttrValue::from("foo"),
3777
          never_matches: false,
3778
          case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
3779
        }],
3780
        specificity(0, 1, 0),
3781
        Default::default(),
3782
      )]))
3783
    );
3784
    // https://github.com/mozilla/servo/issues/1723
3785
    assert_eq!(
3786
      parse("::before"),
3787
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3788
        vec![
3789
          Component::Combinator(Combinator::PseudoElement),
3790
          Component::PseudoElement(PseudoElement::Before),
3791
        ],
3792
        specificity(0, 0, 1),
3793
        SelectorFlags::HAS_PSEUDO,
3794
      )]))
3795
    );
3796
    assert_eq!(
3797
      parse("::before:hover"),
3798
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3799
        vec![
3800
          Component::Combinator(Combinator::PseudoElement),
3801
          Component::PseudoElement(PseudoElement::Before),
3802
          Component::NonTSPseudoClass(PseudoClass::Hover),
3803
        ],
3804
        specificity(0, 1, 1),
3805
        SelectorFlags::HAS_PSEUDO,
3806
      )]))
3807
    );
3808
    assert_eq!(
3809
      parse("::before:hover:hover"),
3810
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3811
        vec![
3812
          Component::Combinator(Combinator::PseudoElement),
3813
          Component::PseudoElement(PseudoElement::Before),
3814
          Component::NonTSPseudoClass(PseudoClass::Hover),
3815
          Component::NonTSPseudoClass(PseudoClass::Hover),
3816
        ],
3817
        specificity(0, 2, 1),
3818
        SelectorFlags::HAS_PSEUDO,
3819
      )]))
3820
    );
3821
    assert!(parse("::before:hover:lang(foo)").is_err());
3822
    assert!(parse("::before:hover .foo").is_err());
3823
    assert!(parse("::before .foo").is_err());
3824
    assert!(parse("::before ~ bar").is_err());
3825
    assert!(parse("::before:active").is_ok());
3826
3827
    // https://github.com/servo/servo/issues/15335
3828
    assert!(parse(":: before").is_err());
3829
    assert_eq!(
3830
      parse("div ::after"),
3831
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3832
        vec![
3833
          Component::LocalName(LocalName {
3834
            name: DummyAtom::from("div"),
3835
            lower_name: DummyAtom::from("div"),
3836
          }),
3837
          Component::Combinator(Combinator::Descendant),
3838
          Component::Combinator(Combinator::PseudoElement),
3839
          Component::PseudoElement(PseudoElement::After),
3840
        ],
3841
        specificity(0, 0, 2),
3842
        SelectorFlags::HAS_PSEUDO,
3843
      )]))
3844
    );
3845
    assert_eq!(
3846
      parse("#d1 > .ok"),
3847
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3848
        vec![
3849
          Component::ID(DummyAtom::from("d1")),
3850
          Component::Combinator(Combinator::Child),
3851
          Component::Class(DummyAtom::from("ok")),
3852
        ],
3853
        (1 << 20) + (1 << 10) + (0 << 0),
3854
        Default::default(),
3855
      )]))
3856
    );
3857
    parser.default_ns = None;
3858
    assert!(parse(":not(#provel.old)").is_ok());
3859
    assert!(parse(":not(#provel > old)").is_ok());
3860
    assert!(parse("table[rules]:not([rules=\"none\"]):not([rules=\"\"])").is_ok());
3861
    // https://github.com/servo/servo/issues/16017
3862
    assert_eq!(
3863
      parse_ns(":not(*)", &parser),
3864
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3865
        vec![Component::Negation(
3866
          vec![Selector::from_vec(
3867
            vec![Component::ExplicitUniversalType],
3868
            specificity(0, 0, 0),
3869
            Default::default(),
3870
          )]
3871
          .into_boxed_slice()
3872
        )],
3873
        specificity(0, 0, 0),
3874
        Default::default(),
3875
      )]))
3876
    );
3877
    assert_eq!(
3878
      parse_ns(":not(|*)", &parser),
3879
      Ok(SelectorList::from_vec(vec![Selector::from_vec(
3880
        vec![Component::Negation(
3881
          vec![Selector::from_vec(
3882
            vec![Component::ExplicitNoNamespace, Component::ExplicitUniversalType,],
3883
            specificity(0, 0, 0),
3884
            Default::default(),
3885
          )]
3886
          .into_boxed_slice(),
3887
        )],
3888
        specificity(0, 0, 0),
3889
        Default::default(),
3890
      )]))
3891
    );
3892
    // *| should be elided if there is no default namespace.
3893
    // https://github.com/servo/servo/pull/17537
3894
    // assert_eq!(
3895
    //   parse_ns_expected(":not(*|*)", &parser, Some(":not(*)")),
3896
    //   Ok(SelectorList::from_vec(vec![Selector::from_vec(
3897
    //     vec![Component::Negation(
3898
    //       vec![Selector::from_vec(
3899
    //         vec![Component::ExplicitUniversalType],
3900
    //         specificity(0, 0, 0),
3901
    //         Default::default()
3902
    //       )]
3903
    //       .into_boxed_slice()
3904
    //     )],
3905
    //     specificity(0, 0, 0),
3906
    //     Default::default(),
3907
    //   )]))
3908
    // );
3909
3910
    assert!(parse("::slotted()").is_err());
3911
    assert!(parse("::slotted(div)").is_ok());
3912
    assert!(parse("::slotted(div).foo").is_err());
3913
    assert!(parse("::slotted(div + bar)").is_err());
3914
    assert!(parse("::slotted(div) + foo").is_err());
3915
3916
    assert!(parse("::part()").is_err());
3917
    assert!(parse("::part(42)").is_err());
3918
    assert!(parse("::part(foo bar)").is_ok());
3919
    assert!(parse("::part(foo):hover").is_ok());
3920
    assert!(parse("::part(foo) + bar").is_err());
3921
3922
    assert!(parse("div ::slotted(div)").is_ok());
3923
    assert!(parse("div + slot::slotted(div)").is_ok());
3924
    assert!(parse("div + slot::slotted(div.foo)").is_ok());
3925
    assert!(parse("slot::slotted(div,foo)::first-line").is_err());
3926
    assert!(parse("::slotted(div)::before").is_ok());
3927
    assert!(parse("slot::slotted(div,foo)").is_err());
3928
3929
    assert!(parse("foo:where()").is_err());
3930
    assert!(parse("foo:where(div, foo, .bar baz)").is_ok());
3931
    assert!(parse("foo:where(::before)").is_err());
3932
3933
    assert!(parse("foo::details-content").is_ok());
3934
    assert!(parse("foo::target-text").is_ok());
3935
    assert!(parse("foo::search-text").is_ok());
3936
    assert!(parse(":current::search-text").is_ok());
3937
3938
    assert!(parse("::highlight").is_err());
3939
    assert!(parse("::highlight()").is_err());
3940
    assert!(parse("::highlight(custom-highlight-name)").is_ok());
3941
3942
    assert!(parse("select::picker").is_err());
3943
    assert!(parse("::picker()").is_err());
3944
    assert!(parse("::picker(select)").is_ok());
3945
    assert!(parse("select::picker-icon").is_ok());
3946
    assert!(parse("option::checkmark").is_ok());
3947
3948
    assert!(parse("::grammar-error").is_ok());
3949
    assert!(parse("::spelling-error").is_ok());
3950
    assert!(parse("::part(mypart)::grammar-error").is_ok());
3951
    assert!(parse("::part(mypart)::spelling-error").is_ok());
3952
  }
3953
3954
  #[test]
3955
  fn test_pseudo_iter() {
3956
    let selector = &parse("q::before").unwrap().0[0];
3957
    assert!(!selector.is_universal());
3958
    let mut iter = selector.iter();
3959
    assert_eq!(iter.next(), Some(&Component::PseudoElement(PseudoElement::Before)));
3960
    assert_eq!(iter.next(), None);
3961
    let combinator = iter.next_sequence();
3962
    assert_eq!(combinator, Some(Combinator::PseudoElement));
3963
    assert!(matches!(iter.next(), Some(&Component::LocalName(..))));
3964
    assert_eq!(iter.next(), None);
3965
    assert_eq!(iter.next_sequence(), None);
3966
  }
3967
3968
  #[test]
3969
  fn test_universal() {
3970
    let selector = &parse_ns(
3971
      "*|*::before",
3972
      &DummyParser::default_with_namespace(DummyAtom::from("https://mozilla.org")),
3973
    )
3974
    .unwrap()
3975
    .0[0];
3976
    assert!(selector.is_universal());
3977
  }
3978
3979
  #[test]
3980
  fn test_empty_pseudo_iter() {
3981
    let selector = &parse("::before").unwrap().0[0];
3982
    assert!(selector.is_universal());
3983
    let mut iter = selector.iter();
3984
    assert_eq!(iter.next(), Some(&Component::PseudoElement(PseudoElement::Before)));
3985
    assert_eq!(iter.next(), None);
3986
    assert_eq!(iter.next_sequence(), Some(Combinator::PseudoElement));
3987
    assert_eq!(iter.next(), None);
3988
    assert_eq!(iter.next_sequence(), None);
3989
  }
3990
3991
  struct TestVisitor {
3992
    seen: Vec<String>,
3993
  }
3994
3995
  impl<'i> SelectorVisitor<'i> for TestVisitor {
3996
    type Impl = DummySelectorImpl;
3997
3998
    fn visit_simple_selector(&mut self, s: &Component<DummySelectorImpl>) -> bool {
3999
      let mut dest = String::new();
4000
      s.to_css(&mut dest).unwrap();
4001
      self.seen.push(dest);
4002
      true
4003
    }
4004
  }
4005
4006
  #[test]
4007
  fn visitor() {
4008
    let mut test_visitor = TestVisitor { seen: vec![] };
4009
    parse(":not(:hover) ~ label").unwrap().0[0].visit(&mut test_visitor);
4010
    assert!(test_visitor.seen.contains(&":hover".into()));
4011
4012
    let mut test_visitor = TestVisitor { seen: vec![] };
4013
    parse("::before:hover").unwrap().0[0].visit(&mut test_visitor);
4014
    assert!(test_visitor.seen.contains(&":hover".into()));
4015
  }
4016
}