Skip to main content

lightningcss/
selector.rs

1//! CSS selectors.
2
3use crate::compat::Feature;
4use crate::error::{ParserError, PrinterError, SelectorError};
5use crate::parser::ParserFlags;
6use crate::printer::Printer;
7use crate::properties::custom::TokenList;
8use crate::rules::StyleContext;
9use crate::stylesheet::{ParserOptions, PrinterOptions};
10use crate::targets::{should_compile, Targets};
11use crate::traits::{Parse, ParseWithOptions, ToCss};
12use crate::values::ident::{CustomIdent, Ident};
13use crate::values::string::CSSString;
14use crate::vendor_prefix::VendorPrefix;
15#[cfg(feature = "visitor")]
16use crate::visitor::{Visit, VisitTypes, Visitor};
17use crate::{macros::enum_property, values::string::CowArcStr};
18use cssparser::*;
19use parcel_selectors::parser::{NthType, SelectorParseErrorKind};
20use parcel_selectors::{
21  attr::{AttrSelectorOperator, ParsedAttrSelectorOperation, ParsedCaseSensitivity},
22  parser::SelectorImpl,
23};
24use smallvec::SmallVec;
25use std::collections::HashSet;
26use std::fmt;
27
28#[cfg(feature = "serde")]
29use crate::serialization::*;
30
31mod private {
32  #[derive(Debug, Clone, PartialEq, Eq, Hash)]
33  #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
34  pub struct Selectors;
35
36  #[cfg(feature = "into_owned")]
37  impl<'any> ::static_self::IntoOwned<'any> for Selectors {
38    type Owned = Self;
39
40    fn into_owned(self) -> Self::Owned {
41      self
42    }
43  }
44}
45
46#[cfg(feature = "into_owned")]
47fn _assert_into_owned() {
48  // Ensure that we provide into_owned
49
50  use static_self::IntoOwned;
51  fn _assert<'any, T: IntoOwned<'any>>() {}
52
53  _assert::<SelectorList>();
54}
55
56use private::Selectors;
57
58/// A list of selectors.
59pub type SelectorList<'i> = parcel_selectors::SelectorList<'i, Selectors>;
60/// A CSS selector, including a list of components.
61pub type Selector<'i> = parcel_selectors::parser::Selector<'i, Selectors>;
62/// An individual component within a selector.
63pub type Component<'i> = parcel_selectors::parser::Component<'i, Selectors>;
64/// A combinator.
65pub use parcel_selectors::parser::Combinator;
66
67impl<'i> SelectorImpl<'i> for Selectors {
68  type AttrValue = CSSString<'i>;
69  type Identifier = Ident<'i>;
70  type LocalName = Ident<'i>;
71  type NamespacePrefix = Ident<'i>;
72  type NamespaceUrl = CowArcStr<'i>;
73  type BorrowedNamespaceUrl = CowArcStr<'i>;
74  type BorrowedLocalName = Ident<'i>;
75
76  type NonTSPseudoClass = PseudoClass<'i>;
77  type PseudoElement = PseudoElement<'i>;
78  type VendorPrefix = VendorPrefix;
79
80  type ExtraMatchingData = ();
81
82  fn to_css<W: fmt::Write>(selectors: &SelectorList<'i>, dest: &mut W) -> std::fmt::Result {
83    let mut printer = Printer::new(dest, PrinterOptions::default());
84    serialize_selector_list(selectors.0.iter(), &mut printer, None, false).map_err(|_| std::fmt::Error)
85  }
86}
87
88pub(crate) struct SelectorParser<'a, 'i> {
89  pub is_nesting_allowed: bool,
90  pub options: &'a ParserOptions<'i>,
91}
92
93impl<'a, 'i> parcel_selectors::parser::Parser<'i> for SelectorParser<'a, 'i> {
94  type Impl = Selectors;
95  type Error = ParserError<'i>;
96
97  fn parse_non_ts_pseudo_class(
98    &self,
99    loc: SourceLocation,
100    name: CowRcStr<'i>,
101  ) -> Result<PseudoClass<'i>, ParseError<'i, Self::Error>> {
102    use PseudoClass::*;
103    let scroll_navigation_controls = self.options.flags.contains(ParserFlags::SCROLL_NAVIGATION_CONTROLS);
104    let pseudo_class = match_ignore_ascii_case! { &name,
105      // https://drafts.csswg.org/selectors-4/#useraction-pseudos
106      "hover" => Hover,
107      "active" => Active,
108      "focus" => Focus,
109      "focus-visible" => FocusVisible,
110      "focus-within" => FocusWithin,
111
112      // https://drafts.csswg.org/selectors-4/#time-pseudos
113      "current" => Current,
114      "past" => Past,
115      "future" => Future,
116
117      // https://drafts.csswg.org/selectors-4/#resource-pseudos
118      "playing" => Playing,
119      "paused" => Paused,
120      "seeking" => Seeking,
121      "buffering" => Buffering,
122      "stalled" => Stalled,
123      "muted" => Muted,
124      "volume-locked" => VolumeLocked,
125
126      // https://fullscreen.spec.whatwg.org/#:fullscreen-pseudo-class
127      "fullscreen" => Fullscreen(VendorPrefix::None),
128      "-webkit-full-screen" => Fullscreen(VendorPrefix::WebKit),
129      "-moz-full-screen" => Fullscreen(VendorPrefix::Moz),
130      "-ms-fullscreen" => Fullscreen(VendorPrefix::Ms),
131
132      // https://drafts.csswg.org/selectors/#display-state-pseudos
133      "open" => Open,
134      "closed" => Closed,
135      "modal" => Modal,
136      "picture-in-picture" => PictureInPicture,
137
138      // https://html.spec.whatwg.org/multipage/semantics-other.html#selector-popover-open
139      "popover-open" => PopoverOpen,
140
141      // https://drafts.csswg.org/selectors-4/#the-defined-pseudo
142      "defined" => Defined,
143
144      // https://drafts.csswg.org/selectors-4/#location
145      "any-link" => AnyLink(VendorPrefix::None),
146      "-webkit-any-link" => AnyLink(VendorPrefix::WebKit),
147      "-moz-any-link" => AnyLink(VendorPrefix::Moz),
148      "link" => Link,
149      "local-link" => LocalLink,
150      "target" => Target,
151      "target-within" => TargetWithin,
152
153      // https://drafts.csswg.org/css-overflow-5/#active-before-after-scroll-markers
154      "target-current" if scroll_navigation_controls => TargetCurrent,
155      "target-before" if scroll_navigation_controls => TargetBefore,
156      "target-after" if scroll_navigation_controls => TargetAfter,
157
158      "visited" => Visited,
159
160      // https://drafts.csswg.org/selectors-4/#input-pseudos
161      "enabled" => Enabled,
162      "disabled" => Disabled,
163      "read-only" => ReadOnly(VendorPrefix::None),
164      "-moz-read-only" => ReadOnly(VendorPrefix::Moz),
165      "read-write" => ReadWrite(VendorPrefix::None),
166      "-moz-read-write" => ReadWrite(VendorPrefix::Moz),
167      "placeholder-shown" => PlaceholderShown(VendorPrefix::None),
168      "-moz-placeholder" => PlaceholderShown(VendorPrefix::Moz),
169      "-ms-input-placeholder" => PlaceholderShown(VendorPrefix::Ms),
170      "default" => Default,
171      "checked" => Checked,
172      "indeterminate" => Indeterminate,
173      "blank" => Blank,
174      "valid" => Valid,
175      "invalid" => Invalid,
176      "in-range" => InRange,
177      "out-of-range" => OutOfRange,
178      "required" => Required,
179      "optional" => Optional,
180      "user-valid" => UserValid,
181      "user-invalid" => UserInvalid,
182
183      // https://html.spec.whatwg.org/multipage/semantics-other.html#selector-autofill
184      "autofill" => Autofill(VendorPrefix::None),
185      "-webkit-autofill" => Autofill(VendorPrefix::WebKit),
186      "-o-autofill" => Autofill(VendorPrefix::O),
187
188      // https://drafts.csswg.org/css-view-transitions-2/#pseudo-classes-for-selective-vt
189      "active-view-transition" => ActiveViewTransition,
190
191      // https://webkit.org/blog/363/styling-scrollbars/
192      "horizontal" => WebKitScrollbar(WebKitScrollbarPseudoClass::Horizontal),
193      "vertical" => WebKitScrollbar(WebKitScrollbarPseudoClass::Vertical),
194      "decrement" => WebKitScrollbar(WebKitScrollbarPseudoClass::Decrement),
195      "increment" => WebKitScrollbar(WebKitScrollbarPseudoClass::Increment),
196      "start" => WebKitScrollbar(WebKitScrollbarPseudoClass::Start),
197      "end" => WebKitScrollbar(WebKitScrollbarPseudoClass::End),
198      "double-button" => WebKitScrollbar(WebKitScrollbarPseudoClass::DoubleButton),
199      "single-button" => WebKitScrollbar(WebKitScrollbarPseudoClass::SingleButton),
200      "no-button" => WebKitScrollbar(WebKitScrollbarPseudoClass::NoButton),
201      "corner-present" => WebKitScrollbar(WebKitScrollbarPseudoClass::CornerPresent),
202      "window-inactive" => WebKitScrollbar(WebKitScrollbarPseudoClass::WindowInactive),
203
204      "local" | "global" if self.options.css_modules.is_some() => {
205        return Err(loc.new_custom_error(SelectorParseErrorKind::AmbiguousCssModuleClass(name.clone())))
206      },
207
208      _ => {
209        if !name.starts_with('-') {
210          self.options.warn(loc.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClass(name.clone())));
211        }
212        Custom { name: name.into() }
213      }
214    };
215
216    Ok(pseudo_class)
217  }
218
219  fn parse_non_ts_functional_pseudo_class<'t>(
220    &self,
221    name: CowRcStr<'i>,
222    parser: &mut cssparser::Parser<'i, 't>,
223  ) -> Result<PseudoClass<'i>, ParseError<'i, Self::Error>> {
224    use PseudoClass::*;
225    let pseudo_class = match_ignore_ascii_case! { &name,
226      "lang" => {
227        let languages = parser.parse_comma_separated(|parser| {
228          parser.expect_ident_or_string()
229            .map(|s| s.into())
230            .map_err(|e| e.into())
231        })?;
232        Lang { languages }
233      },
234      "dir" => Dir { direction: Direction::parse(parser)? },
235      // https://drafts.csswg.org/css-view-transitions-2/#the-active-view-transition-type-pseudo
236      "active-view-transition-type" => {
237        let kind = Parse::parse(parser)?;
238        ActiveViewTransitionType { kind }
239      },
240      "state" => {
241        let state = CustomIdent::parse(parser)?;
242        State { state }
243      },
244      "local" if self.options.css_modules.is_some() => Local { selector: Box::new(Selector::parse(self, parser)?) },
245      "global" if self.options.css_modules.is_some() => Global { selector: Box::new(Selector::parse(self, parser)?) },
246      _ => {
247        if !name.starts_with('-') {
248          self.options.warn(parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClass(name.clone())));
249        }
250        let mut args = Vec::new();
251        TokenList::parse_raw(parser, &mut args, &self.options, 0)?;
252        CustomFunction {
253          name: name.into(),
254          arguments: TokenList(args)
255        }
256      },
257    };
258
259    Ok(pseudo_class)
260  }
261
262  fn parse_any_prefix<'t>(&self, name: &str) -> Option<VendorPrefix> {
263    match_ignore_ascii_case! { &name,
264      "-webkit-any" => Some(VendorPrefix::WebKit),
265      "-moz-any" => Some(VendorPrefix::Moz),
266      _ => None
267    }
268  }
269
270  fn parse_pseudo_element(
271    &self,
272    loc: SourceLocation,
273    name: CowRcStr<'i>,
274  ) -> Result<PseudoElement<'i>, ParseError<'i, Self::Error>> {
275    use PseudoElement::*;
276    let pseudo_element = match_ignore_ascii_case! { &name,
277      "before" => Before,
278      "after" => After,
279      "first-line" => FirstLine,
280      "first-letter" => FirstLetter,
281      "details-content" => DetailsContent,
282      "target-text" => TargetText,
283      "search-text" => SearchText,
284      "cue" => Cue,
285      "cue-region" => CueRegion,
286      "selection" => Selection(VendorPrefix::None),
287      "-moz-selection" => Selection(VendorPrefix::Moz),
288      "placeholder" => Placeholder(VendorPrefix::None),
289      "-webkit-input-placeholder" => Placeholder(VendorPrefix::WebKit),
290      "-moz-placeholder" => Placeholder(VendorPrefix::Moz),
291      "-ms-input-placeholder" => Placeholder(VendorPrefix::Moz),
292      "marker" => Marker,
293      "backdrop" => Backdrop(VendorPrefix::None),
294      "-webkit-backdrop" => Backdrop(VendorPrefix::WebKit),
295      "file-selector-button" => FileSelectorButton(VendorPrefix::None),
296      "-webkit-file-upload-button" => FileSelectorButton(VendorPrefix::WebKit),
297      "-ms-browse" => FileSelectorButton(VendorPrefix::Ms),
298
299      "-webkit-scrollbar" => WebKitScrollbar(WebKitScrollbarPseudoElement::Scrollbar),
300      "-webkit-scrollbar-button" => WebKitScrollbar(WebKitScrollbarPseudoElement::Button),
301      "-webkit-scrollbar-track" => WebKitScrollbar(WebKitScrollbarPseudoElement::Track),
302      "-webkit-scrollbar-track-piece" => WebKitScrollbar(WebKitScrollbarPseudoElement::TrackPiece),
303      "-webkit-scrollbar-thumb" => WebKitScrollbar(WebKitScrollbarPseudoElement::Thumb),
304      "-webkit-scrollbar-corner" => WebKitScrollbar(WebKitScrollbarPseudoElement::Corner),
305      "-webkit-resizer" => WebKitScrollbar(WebKitScrollbarPseudoElement::Resizer),
306
307      "picker-icon" => PickerIcon,
308      "checkmark" => Checkmark,
309
310      "view-transition" => ViewTransition,
311
312      "grammar-error" => GrammarError,
313      "spelling-error" => SpellingError,
314
315      _ => {
316        if !name.starts_with('-') {
317          self.options.warn(loc.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoElement(name.clone())));
318        }
319        Custom { name: name.into() }
320      }
321    };
322
323    Ok(pseudo_element)
324  }
325
326  fn parse_functional_pseudo_element<'t>(
327    &self,
328    name: CowRcStr<'i>,
329    arguments: &mut Parser<'i, 't>,
330  ) -> Result<<Self::Impl as SelectorImpl<'i>>::PseudoElement, ParseError<'i, Self::Error>> {
331    use PseudoElement::*;
332    let pseudo_element = match_ignore_ascii_case! { &name,
333      "cue" => CueFunction { selector: Box::new(Selector::parse(self, arguments)?) },
334      "cue-region" => CueRegionFunction { selector: Box::new(Selector::parse(self, arguments)?) },
335      "highlight" => HighlightFunction { name: CustomIdent::parse(arguments)? },
336      "picker" => PickerFunction { identifier: Ident::parse(arguments)? },
337      "view-transition-group" => ViewTransitionGroup { part: ViewTransitionPartSelector::parse(arguments)? },
338      "view-transition-image-pair" => ViewTransitionImagePair { part: ViewTransitionPartSelector::parse(arguments)? },
339      "view-transition-old" => ViewTransitionOld { part: ViewTransitionPartSelector::parse(arguments)? },
340      "view-transition-new" => ViewTransitionNew { part: ViewTransitionPartSelector::parse(arguments)? },
341      _ => {
342        if !name.starts_with('-') {
343          self.options.warn(arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoElement(name.clone())));
344        }
345        let mut args = Vec::new();
346        TokenList::parse_raw(arguments, &mut args, &self.options, 0)?;
347        CustomFunction { name: name.into(), arguments: TokenList(args) }
348      }
349    };
350
351    Ok(pseudo_element)
352  }
353
354  #[inline]
355  fn parse_slotted(&self) -> bool {
356    true
357  }
358
359  #[inline]
360  fn parse_host(&self) -> bool {
361    true
362  }
363
364  #[inline]
365  fn parse_is_and_where(&self) -> bool {
366    true
367  }
368
369  #[inline]
370  fn parse_part(&self) -> bool {
371    true
372  }
373
374  fn default_namespace(&self) -> Option<CowArcStr<'i>> {
375    None
376  }
377
378  fn namespace_for_prefix(&self, prefix: &Ident<'i>) -> Option<CowArcStr<'i>> {
379    Some(prefix.0.clone())
380  }
381
382  #[inline]
383  fn is_nesting_allowed(&self) -> bool {
384    self.is_nesting_allowed
385  }
386
387  fn deep_combinator_enabled(&self) -> bool {
388    self.options.flags.contains(ParserFlags::DEEP_SELECTOR_COMBINATOR)
389  }
390}
391
392enum_property! {
393  /// The [:dir()](https://drafts.csswg.org/selectors-4/#the-dir-pseudo) pseudo class.
394  #[derive(Eq, Hash)]
395  pub enum Direction {
396    /// Left to right
397    Ltr,
398    /// Right to left
399    Rtl,
400  }
401}
402
403/// A pseudo class.
404#[derive(Clone, PartialEq, Eq, Hash)]
405#[cfg_attr(
406  feature = "serde",
407  derive(serde::Serialize, serde::Deserialize),
408  serde(tag = "kind", rename_all = "kebab-case")
409)]
410#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
411#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
412pub enum PseudoClass<'i> {
413  // https://drafts.csswg.org/selectors-4/#linguistic-pseudos
414  /// The [:lang()](https://drafts.csswg.org/selectors-4/#the-lang-pseudo) pseudo class.
415  Lang {
416    /// A list of language codes.
417    #[cfg_attr(feature = "serde", serde(borrow))]
418    languages: Vec<CowArcStr<'i>>,
419  },
420  /// The [:dir()](https://drafts.csswg.org/selectors-4/#the-dir-pseudo) pseudo class.
421  Dir {
422    /// A direction.
423    direction: Direction,
424  },
425
426  // https://drafts.csswg.org/selectors-4/#useraction-pseudos
427  /// The [:hover](https://drafts.csswg.org/selectors-4/#the-hover-pseudo) pseudo class.
428  Hover,
429  /// The [:active](https://drafts.csswg.org/selectors-4/#the-active-pseudo) pseudo class.
430  Active,
431  /// The [:focus](https://drafts.csswg.org/selectors-4/#the-focus-pseudo) pseudo class.
432  Focus,
433  /// The [:focus-visible](https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo) pseudo class.
434  FocusVisible,
435  /// The [:focus-within](https://drafts.csswg.org/selectors-4/#the-focus-within-pseudo) pseudo class.
436  FocusWithin,
437
438  // https://drafts.csswg.org/selectors-4/#time-pseudos
439  /// The [:current](https://drafts.csswg.org/selectors-4/#the-current-pseudo) pseudo class.
440  Current,
441  /// The [:past](https://drafts.csswg.org/selectors-4/#the-past-pseudo) pseudo class.
442  Past,
443  /// The [:future](https://drafts.csswg.org/selectors-4/#the-future-pseudo) pseudo class.
444  Future,
445
446  // https://drafts.csswg.org/selectors-4/#resource-pseudos
447  /// The [:playing](https://drafts.csswg.org/selectors-4/#selectordef-playing) pseudo class.
448  Playing,
449  /// The [:paused](https://drafts.csswg.org/selectors-4/#selectordef-paused) pseudo class.
450  Paused,
451  /// The [:seeking](https://drafts.csswg.org/selectors-4/#selectordef-seeking) pseudo class.
452  Seeking,
453  /// The [:buffering](https://drafts.csswg.org/selectors-4/#selectordef-buffering) pseudo class.
454  Buffering,
455  /// The [:stalled](https://drafts.csswg.org/selectors-4/#selectordef-stalled) pseudo class.
456  Stalled,
457  /// The [:muted](https://drafts.csswg.org/selectors-4/#selectordef-muted) pseudo class.
458  Muted,
459  /// The [:volume-locked](https://drafts.csswg.org/selectors-4/#selectordef-volume-locked) pseudo class.
460  VolumeLocked,
461
462  /// The [:fullscreen](https://fullscreen.spec.whatwg.org/#:fullscreen-pseudo-class) pseudo class.
463  #[cfg_attr(feature = "serde", serde(with = "PrefixWrapper"))]
464  Fullscreen(VendorPrefix),
465
466  // https://drafts.csswg.org/selectors/#display-state-pseudos
467  /// The [:open](https://drafts.csswg.org/selectors/#selectordef-open) pseudo class.
468  Open,
469  /// The [:closed](https://drafts.csswg.org/selectors/#selectordef-closed) pseudo class.
470  Closed,
471  /// The [:modal](https://drafts.csswg.org/selectors/#modal-state) pseudo class.
472  Modal,
473  /// The [:picture-in-picture](https://drafts.csswg.org/selectors/#pip-state) pseudo class.
474  PictureInPicture,
475
476  // https://html.spec.whatwg.org/multipage/semantics-other.html#selector-popover-open
477  /// The [:popover-open](https://html.spec.whatwg.org/multipage/semantics-other.html#selector-popover-open) pseudo class.
478  PopoverOpen,
479
480  /// The [:defined](https://drafts.csswg.org/selectors-4/#the-defined-pseudo) pseudo class.
481  Defined,
482
483  // https://drafts.csswg.org/selectors-4/#location
484  /// The [:any-link](https://drafts.csswg.org/selectors-4/#the-any-link-pseudo) pseudo class.
485  #[cfg_attr(feature = "serde", serde(with = "PrefixWrapper"))]
486  AnyLink(VendorPrefix),
487  /// The [:link](https://drafts.csswg.org/selectors-4/#link-pseudo) pseudo class.
488  Link,
489  /// The [:local-link](https://drafts.csswg.org/selectors-4/#the-local-link-pseudo) pseudo class.
490  LocalLink,
491  /// The [:target](https://drafts.csswg.org/selectors-4/#the-target-pseudo) pseudo class.
492  Target,
493
494  /// The [:target-current](https://drafts.csswg.org/css-overflow-5/#selectordef-target-current) pseudo class.
495  TargetCurrent,
496  /// The [:target-before](https://drafts.csswg.org/css-overflow-5/#selectordef-target-before) pseudo class.
497  TargetBefore,
498  /// The [:target-after](https://drafts.csswg.org/css-overflow-5/#selectordef-target-after) pseudo class.
499  TargetAfter,
500  /// The [:target-within](https://drafts.csswg.org/selectors-4/#the-target-within-pseudo) pseudo class.
501  TargetWithin,
502  /// The [:visited](https://drafts.csswg.org/selectors-4/#visited-pseudo) pseudo class.
503  Visited,
504
505  // https://drafts.csswg.org/selectors-4/#input-pseudos
506  /// The [:enabled](https://drafts.csswg.org/selectors-4/#enabled-pseudo) pseudo class.
507  Enabled,
508  /// The [:disabled](https://drafts.csswg.org/selectors-4/#disabled-pseudo) pseudo class.
509  Disabled,
510  /// The [:read-only](https://drafts.csswg.org/selectors-4/#read-only-pseudo) pseudo class.
511  #[cfg_attr(feature = "serde", serde(with = "PrefixWrapper"))]
512  ReadOnly(VendorPrefix),
513  /// The [:read-write](https://drafts.csswg.org/selectors-4/#read-write-pseudo) pseudo class.
514  #[cfg_attr(feature = "serde", serde(with = "PrefixWrapper"))]
515  ReadWrite(VendorPrefix),
516  /// The [:placeholder-shown](https://drafts.csswg.org/selectors-4/#placeholder) pseudo class.
517  #[cfg_attr(feature = "serde", serde(with = "PrefixWrapper"))]
518  PlaceholderShown(VendorPrefix),
519  /// The [:default](https://drafts.csswg.org/selectors-4/#the-default-pseudo) pseudo class.
520  Default,
521  /// The [:checked](https://drafts.csswg.org/selectors-4/#checked) pseudo class.
522  Checked,
523  /// The [:indeterminate](https://drafts.csswg.org/selectors-4/#indeterminate) pseudo class.
524  Indeterminate,
525  /// The [:blank](https://drafts.csswg.org/selectors-4/#blank) pseudo class.
526  Blank,
527  /// The [:valid](https://drafts.csswg.org/selectors-4/#valid-pseudo) pseudo class.
528  Valid,
529  /// The [:invalid](https://drafts.csswg.org/selectors-4/#invalid-pseudo) pseudo class.
530  Invalid,
531  /// The [:in-range](https://drafts.csswg.org/selectors-4/#in-range-pseudo) pseudo class.
532  InRange,
533  /// The [:out-of-range](https://drafts.csswg.org/selectors-4/#out-of-range-pseudo) pseudo class.
534  OutOfRange,
535  /// The [:required](https://drafts.csswg.org/selectors-4/#required-pseudo) pseudo class.
536  Required,
537  /// The [:optional](https://drafts.csswg.org/selectors-4/#optional-pseudo) pseudo class.
538  Optional,
539  /// The [:user-valid](https://drafts.csswg.org/selectors-4/#user-valid-pseudo) pseudo class.
540  UserValid,
541  /// The [:used-invalid](https://drafts.csswg.org/selectors-4/#user-invalid-pseudo) pseudo class.
542  UserInvalid,
543
544  /// The [:autofill](https://html.spec.whatwg.org/multipage/semantics-other.html#selector-autofill) pseudo class.
545  #[cfg_attr(feature = "serde", serde(with = "PrefixWrapper"))]
546  Autofill(VendorPrefix),
547
548  /// The [:active-view-transition](https://drafts.csswg.org/css-view-transitions-2/#the-active-view-transition-pseudo) pseudo class.
549  ActiveViewTransition,
550  /// The [:active-view-transition-type()](https://drafts.csswg.org/css-view-transitions-2/#the-active-view-transition-type-pseudo) pseudo class.
551  ActiveViewTransitionType {
552    /// A view transition type.
553    #[cfg_attr(feature = "serde", serde(rename = "type"))]
554    kind: SmallVec<[CustomIdent<'i>; 1]>,
555  },
556
557  /// The [:state()](https://developer.mozilla.org/en-US/docs/Web/CSS/:state) pseudo class for custom element states.
558  State {
559    /// The custom state identifier.
560    state: CustomIdent<'i>,
561  },
562
563  // CSS modules
564  /// The CSS modules :local() pseudo class.
565  Local {
566    /// A local selector.
567    selector: Box<Selector<'i>>,
568  },
569  /// The CSS modules :global() pseudo class.
570  Global {
571    /// A global selector.
572    selector: Box<Selector<'i>>,
573  },
574
575  /// A [webkit scrollbar](https://webkit.org/blog/363/styling-scrollbars/) pseudo class.
576  // https://webkit.org/blog/363/styling-scrollbars/
577  #[cfg_attr(
578    feature = "serde",
579    serde(rename = "webkit-scrollbar", with = "ValueWrapper::<WebKitScrollbarPseudoClass>")
580  )]
581  WebKitScrollbar(WebKitScrollbarPseudoClass),
582  /// An unknown pseudo class.
583  Custom {
584    /// The pseudo class name.
585    name: CowArcStr<'i>,
586  },
587  /// An unknown functional pseudo class.
588  CustomFunction {
589    /// The pseudo class name.
590    name: CowArcStr<'i>,
591    /// The arguments of the pseudo class function.
592    arguments: TokenList<'i>,
593  },
594}
595
596/// A [webkit scrollbar](https://webkit.org/blog/363/styling-scrollbars/) pseudo class.
597#[derive(Clone, Eq, PartialEq, Hash)]
598#[cfg_attr(
599  feature = "serde",
600  derive(serde::Serialize, serde::Deserialize),
601  serde(rename_all = "kebab-case")
602)]
603#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
604#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
605pub enum WebKitScrollbarPseudoClass {
606  /// :horizontal
607  Horizontal,
608  /// :vertical
609  Vertical,
610  /// :decrement
611  Decrement,
612  /// :increment
613  Increment,
614  /// :start
615  Start,
616  /// :end
617  End,
618  /// :double-button
619  DoubleButton,
620  /// :single-button
621  SingleButton,
622  /// :no-button
623  NoButton,
624  /// :corner-present
625  CornerPresent,
626  /// :window-inactive
627  WindowInactive,
628}
629
630impl<'i> parcel_selectors::parser::NonTSPseudoClass<'i> for PseudoClass<'i> {
631  type Impl = Selectors;
632
633  fn is_active_or_hover(&self) -> bool {
634    matches!(*self, PseudoClass::Active | PseudoClass::Hover)
635  }
636
637  fn is_user_action_state(&self) -> bool {
638    matches!(
639      *self,
640      PseudoClass::Active
641        | PseudoClass::Hover
642        | PseudoClass::Focus
643        | PseudoClass::FocusWithin
644        | PseudoClass::FocusVisible
645    )
646  }
647
648  fn is_valid_before_webkit_scrollbar(&self) -> bool {
649    !matches!(*self, PseudoClass::WebKitScrollbar(..))
650  }
651
652  fn is_valid_after_webkit_scrollbar(&self) -> bool {
653    // https://github.com/WebKit/WebKit/blob/02fbf9b7aa435edb96cbf563a8d4dcf1aa73b4b3/Source/WebCore/css/parser/CSSSelectorParser.cpp#L285
654    matches!(
655      *self,
656      PseudoClass::WebKitScrollbar(..)
657        | PseudoClass::Enabled
658        | PseudoClass::Disabled
659        | PseudoClass::Hover
660        | PseudoClass::Active
661    )
662  }
663}
664
665impl<'i> cssparser::ToCss for PseudoClass<'i> {
666  fn to_css<W>(&self, dest: &mut W) -> std::fmt::Result
667  where
668    W: fmt::Write,
669  {
670    let mut s = String::new();
671    serialize_pseudo_class(self, &mut Printer::new(&mut s, Default::default()), None)
672      .map_err(|_| std::fmt::Error)?;
673    write!(dest, "{}", s)
674  }
675}
676
677fn serialize_pseudo_class<'a, 'i, W>(
678  pseudo_class: &PseudoClass<'i>,
679  dest: &mut Printer<W>,
680  context: Option<&StyleContext>,
681) -> Result<(), PrinterError>
682where
683  W: fmt::Write,
684{
685  use PseudoClass::*;
686  match pseudo_class {
687    Lang { languages: lang } => {
688      dest.write_str(":lang(")?;
689      let mut first = true;
690      for lang in lang {
691        if first {
692          first = false;
693        } else {
694          dest.delim(',', false)?;
695        }
696        serialize_identifier(lang, dest)?;
697      }
698      return dest.write_str(")");
699    }
700    Dir { direction: dir } => {
701      dest.write_str(":dir(")?;
702      dir.to_css(dest)?;
703      return dest.write_str(")");
704    }
705    State { state } => {
706      dest.write_str(":state(")?;
707      state.to_css(dest)?;
708      return dest.write_str(")");
709    }
710    _ => {}
711  }
712
713  macro_rules! write_prefixed {
714    ($prefix: ident, $val: expr) => {{
715      dest.write_char(':')?;
716      // If the printer has a vendor prefix override, use that.
717      let vp = if !dest.vendor_prefix.is_empty() {
718        (dest.vendor_prefix & *$prefix).or_none()
719      } else {
720        *$prefix
721      };
722      vp.to_css(dest)?;
723      dest.write_str($val)
724    }};
725  }
726
727  macro_rules! pseudo {
728    ($key: ident, $s: literal) => {{
729      let class = if let Some(pseudo_classes) = &dest.pseudo_classes {
730        pseudo_classes.$key
731      } else {
732        None
733      };
734
735      if let Some(class) = class {
736        dest.write_char('.')?;
737        dest.write_ident(class, true)
738      } else {
739        dest.write_str($s)
740      }
741    }};
742  }
743
744  match pseudo_class {
745    // https://drafts.csswg.org/selectors-4/#useraction-pseudos
746    Hover => pseudo!(hover, ":hover"),
747    Active => pseudo!(active, ":active"),
748    Focus => pseudo!(focus, ":focus"),
749    FocusVisible => pseudo!(focus_visible, ":focus-visible"),
750    FocusWithin => pseudo!(focus_within, ":focus-within"),
751
752    // https://drafts.csswg.org/selectors-4/#time-pseudos
753    Current => dest.write_str(":current"),
754    Past => dest.write_str(":past"),
755    Future => dest.write_str(":future"),
756
757    // https://drafts.csswg.org/selectors-4/#resource-pseudos
758    Playing => dest.write_str(":playing"),
759    Paused => dest.write_str(":paused"),
760    Seeking => dest.write_str(":seeking"),
761    Buffering => dest.write_str(":buffering"),
762    Stalled => dest.write_str(":stalled"),
763    Muted => dest.write_str(":muted"),
764    VolumeLocked => dest.write_str(":volume-locked"),
765
766    // https://fullscreen.spec.whatwg.org/#:fullscreen-pseudo-class
767    Fullscreen(prefix) => {
768      dest.write_char(':')?;
769      let vp = if !dest.vendor_prefix.is_empty() {
770        (dest.vendor_prefix & *prefix).or_none()
771      } else {
772        *prefix
773      };
774      vp.to_css(dest)?;
775      if vp == VendorPrefix::WebKit || vp == VendorPrefix::Moz {
776        dest.write_str("full-screen")
777      } else {
778        dest.write_str("fullscreen")
779      }
780    }
781
782    // https://drafts.csswg.org/selectors/#display-state-pseudos
783    Open => dest.write_str(":open"),
784    Closed => dest.write_str(":closed"),
785    Modal => dest.write_str(":modal"),
786    PictureInPicture => dest.write_str(":picture-in-picture"),
787
788    // https://html.spec.whatwg.org/multipage/semantics-other.html#selector-popover-open
789    PopoverOpen => dest.write_str(":popover-open"),
790
791    // https://drafts.csswg.org/selectors-4/#the-defined-pseudo
792    Defined => dest.write_str(":defined"),
793
794    // https://drafts.csswg.org/selectors-4/#location
795    AnyLink(prefix) => write_prefixed!(prefix, "any-link"),
796    Link => dest.write_str(":link"),
797    LocalLink => dest.write_str(":local-link"),
798    Target => dest.write_str(":target"),
799    TargetWithin => dest.write_str(":target-within"),
800
801    // https://drafts.csswg.org/css-overflow-5/#active-before-after-scroll-markers
802    TargetCurrent => dest.write_str(":target-current"),
803    TargetBefore => dest.write_str(":target-before"),
804    TargetAfter => dest.write_str(":target-after"),
805
806    Visited => dest.write_str(":visited"),
807
808    // https://drafts.csswg.org/selectors-4/#input-pseudos
809    Enabled => dest.write_str(":enabled"),
810    Disabled => dest.write_str(":disabled"),
811    ReadOnly(prefix) => write_prefixed!(prefix, "read-only"),
812    ReadWrite(prefix) => write_prefixed!(prefix, "read-write"),
813    PlaceholderShown(prefix) => write_prefixed!(prefix, "placeholder-shown"),
814    Default => dest.write_str(":default"),
815    Checked => dest.write_str(":checked"),
816    Indeterminate => dest.write_str(":indeterminate"),
817    Blank => dest.write_str(":blank"),
818    Valid => dest.write_str(":valid"),
819    Invalid => dest.write_str(":invalid"),
820    InRange => dest.write_str(":in-range"),
821    OutOfRange => dest.write_str(":out-of-range"),
822    Required => dest.write_str(":required"),
823    Optional => dest.write_str(":optional"),
824    UserValid => dest.write_str(":user-valid"),
825    UserInvalid => dest.write_str(":user-invalid"),
826
827    // https://html.spec.whatwg.org/multipage/semantics-other.html#selector-autofill
828    Autofill(prefix) => write_prefixed!(prefix, "autofill"),
829
830    ActiveViewTransition => dest.write_str(":active-view-transition"),
831    ActiveViewTransitionType { kind } => {
832      dest.write_str(":active-view-transition-type(")?;
833      kind.to_css(dest)?;
834      dest.write_char(')')
835    }
836
837    Local { selector } => serialize_selector(selector, dest, context, false),
838    Global { selector } => {
839      let css_module = std::mem::take(&mut dest.css_module);
840      serialize_selector(selector, dest, context, false)?;
841      dest.css_module = css_module;
842      Ok(())
843    }
844
845    // https://webkit.org/blog/363/styling-scrollbars/
846    WebKitScrollbar(s) => {
847      use WebKitScrollbarPseudoClass::*;
848      dest.write_str(match s {
849        Horizontal => ":horizontal",
850        Vertical => ":vertical",
851        Decrement => ":decrement",
852        Increment => ":increment",
853        Start => ":start",
854        End => ":end",
855        DoubleButton => ":double-button",
856        SingleButton => ":single-button",
857        NoButton => ":no-button",
858        CornerPresent => ":corner-present",
859        WindowInactive => ":window-inactive",
860      })
861    }
862
863    Lang { languages: _ } | Dir { direction: _ } | State { .. } => unreachable!(),
864    Custom { name } => {
865      dest.write_char(':')?;
866      return dest.write_str(&name);
867    }
868    CustomFunction { name, arguments: args } => {
869      dest.write_char(':')?;
870      dest.write_str(name)?;
871      dest.write_char('(')?;
872      args.to_css_raw(dest)?;
873      dest.write_char(')')
874    }
875  }
876}
877
878impl<'i> PseudoClass<'i> {
879  pub(crate) fn is_equivalent(&self, other: &PseudoClass<'i>) -> bool {
880    use PseudoClass::*;
881    match (self, other) {
882      (Fullscreen(_), Fullscreen(_))
883      | (AnyLink(_), AnyLink(_))
884      | (ReadOnly(_), ReadOnly(_))
885      | (ReadWrite(_), ReadWrite(_))
886      | (PlaceholderShown(_), PlaceholderShown(_))
887      | (Autofill(_), Autofill(_)) => true,
888      (a, b) => a == b,
889    }
890  }
891
892  pub(crate) fn get_prefix(&self) -> VendorPrefix {
893    use PseudoClass::*;
894    match self {
895      Fullscreen(p) | AnyLink(p) | ReadOnly(p) | ReadWrite(p) | PlaceholderShown(p) | Autofill(p) => *p,
896      _ => VendorPrefix::empty(),
897    }
898  }
899
900  pub(crate) fn get_necessary_prefixes(&mut self, targets: Targets) -> VendorPrefix {
901    use crate::prefixes::Feature;
902    use PseudoClass::*;
903    let (p, feature) = match self {
904      Fullscreen(p) => (p, Feature::PseudoClassFullscreen),
905      AnyLink(p) => (p, Feature::PseudoClassAnyLink),
906      ReadOnly(p) => (p, Feature::PseudoClassReadOnly),
907      ReadWrite(p) => (p, Feature::PseudoClassReadWrite),
908      PlaceholderShown(p) => (p, Feature::PseudoClassPlaceholderShown),
909      Autofill(p) => (p, Feature::PseudoClassAutofill),
910      _ => return VendorPrefix::empty(),
911    };
912
913    *p = targets.prefixes(*p, feature);
914    *p
915  }
916}
917
918/// A pseudo element.
919#[derive(PartialEq, Eq, Hash, Clone, Debug)]
920#[cfg_attr(
921  feature = "serde",
922  derive(serde::Serialize, serde::Deserialize),
923  serde(tag = "kind", rename_all = "kebab-case")
924)]
925#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
926#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
927pub enum PseudoElement<'i> {
928  /// The [::after](https://drafts.csswg.org/css-pseudo-4/#selectordef-after) pseudo element.
929  After,
930  /// The [::before](https://drafts.csswg.org/css-pseudo-4/#selectordef-before) pseudo element.
931  Before,
932  /// The [::first-line](https://drafts.csswg.org/css-pseudo-4/#first-line-pseudo) pseudo element.
933  FirstLine,
934  /// The [::first-letter](https://drafts.csswg.org/css-pseudo-4/#first-letter-pseudo) pseudo element.
935  FirstLetter,
936  /// The [::details-content](https://drafts.csswg.org/css-pseudo-4/#details-content-pseudo)
937  DetailsContent,
938  /// The [::target-text](https://drafts.csswg.org/css-pseudo-4/#selectordef-target-text)
939  TargetText,
940  /// The [::search-text](https://drafts.csswg.org/css-pseudo-4/#selectordef-search-text) pseudo element.
941  SearchText,
942  /// The [::selection](https://drafts.csswg.org/css-pseudo-4/#selectordef-selection) pseudo element.
943  #[cfg_attr(feature = "serde", serde(with = "PrefixWrapper"))]
944  Selection(VendorPrefix),
945  /// The [::placeholder](https://drafts.csswg.org/css-pseudo-4/#placeholder-pseudo) pseudo element.
946  #[cfg_attr(feature = "serde", serde(with = "PrefixWrapper"))]
947  Placeholder(VendorPrefix),
948  /// The [::highlight()](https://drafts.csswg.org/css-highlight-api/#custom-highlight-pseudo) functional pseudo element.
949  HighlightFunction {
950    /// A custom highlight name.
951    name: CustomIdent<'i>,
952  },
953  ///  The [::marker](https://drafts.csswg.org/css-pseudo-4/#marker-pseudo) pseudo element.
954  Marker,
955  /// The [::backdrop](https://fullscreen.spec.whatwg.org/#::backdrop-pseudo-element) pseudo element.
956  #[cfg_attr(feature = "serde", serde(with = "PrefixWrapper"))]
957  Backdrop(VendorPrefix),
958  /// The [::file-selector-button](https://drafts.csswg.org/css-pseudo-4/#file-selector-button-pseudo) pseudo element.
959  #[cfg_attr(feature = "serde", serde(with = "PrefixWrapper"))]
960  FileSelectorButton(VendorPrefix),
961  /// A [webkit scrollbar](https://webkit.org/blog/363/styling-scrollbars/) pseudo element.
962  #[cfg_attr(
963    feature = "serde",
964    serde(rename = "webkit-scrollbar", with = "ValueWrapper::<WebKitScrollbarPseudoElement>")
965  )]
966  WebKitScrollbar(WebKitScrollbarPseudoElement),
967  /// The [::cue](https://w3c.github.io/webvtt/#the-cue-pseudo-element) pseudo element.
968  Cue,
969  /// The [::cue-region](https://w3c.github.io/webvtt/#the-cue-region-pseudo-element) pseudo element.
970  CueRegion,
971  /// The [::cue()](https://w3c.github.io/webvtt/#cue-selector) functional pseudo element.
972  CueFunction {
973    /// The selector argument.
974    selector: Box<Selector<'i>>,
975  },
976  /// The [::cue-region()](https://w3c.github.io/webvtt/#cue-region-selector) functional pseudo element.
977  CueRegionFunction {
978    /// The selector argument.
979    selector: Box<Selector<'i>>,
980  },
981  /// The [::view-transition](https://w3c.github.io/csswg-drafts/css-view-transitions-1/#view-transition) pseudo element.
982  ViewTransition,
983  /// The [::view-transition-group()](https://w3c.github.io/csswg-drafts/css-view-transitions-1/#view-transition-group-pt-name-selector) functional pseudo element.
984  #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
985  ViewTransitionGroup {
986    /// A part name selector.
987    part: ViewTransitionPartSelector<'i>,
988  },
989  /// The [::view-transition-image-pair()](https://w3c.github.io/csswg-drafts/css-view-transitions-1/#view-transition-image-pair-pt-name-selector) functional pseudo element.
990  #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
991  ViewTransitionImagePair {
992    /// A part name selector.
993    part: ViewTransitionPartSelector<'i>,
994  },
995  /// The [::view-transition-old()](https://w3c.github.io/csswg-drafts/css-view-transitions-1/#view-transition-old-pt-name-selector) functional pseudo element.
996  #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
997  ViewTransitionOld {
998    /// A part name selector.
999    part: ViewTransitionPartSelector<'i>,
1000  },
1001  /// The [::view-transition-new()](https://w3c.github.io/csswg-drafts/css-view-transitions-1/#view-transition-new-pt-name-selector) functional pseudo element.
1002  #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1003  ViewTransitionNew {
1004    /// A part name selector.
1005    part: ViewTransitionPartSelector<'i>,
1006  },
1007  /// The [::picker()](https://drafts.csswg.org/css-forms-1/#the-picker-pseudo-element) functional pseudo element.
1008  PickerFunction {
1009    /// A form control identifier.
1010    identifier: Ident<'i>,
1011  },
1012  /// The [::picker-icon](https://drafts.csswg.org/css-forms-1/#picker-opener-icon-the-picker-icon-pseudo-element) pseudo element.
1013  PickerIcon,
1014  /// The [::checkmark](https://drafts.csswg.org/css-forms-1/#styling-checkmarks-the-checkmark-pseudo-element) pseudo element.
1015  Checkmark,
1016  /// The [::grammar-error](https://drafts.csswg.org/css-pseudo/#selectordef-grammar-error) pseudo element.
1017  GrammarError,
1018  /// The [::spelling-error](https://drafts.csswg.org/css-pseudo/#selectordef-spelling-error) pseudo element.
1019  SpellingError,
1020  /// An unknown pseudo element.
1021  Custom {
1022    /// The name of the pseudo element.
1023    #[cfg_attr(feature = "serde", serde(borrow))]
1024    name: CowArcStr<'i>,
1025  },
1026  /// An unknown functional pseudo element.
1027  CustomFunction {
1028    ///The name of the pseudo element.
1029    name: CowArcStr<'i>,
1030    /// The arguments of the pseudo element function.
1031    arguments: TokenList<'i>,
1032  },
1033}
1034
1035/// A [webkit scrollbar](https://webkit.org/blog/363/styling-scrollbars/) pseudo element.
1036#[derive(PartialEq, Eq, Clone, Debug, Hash)]
1037#[cfg_attr(
1038  feature = "serde",
1039  derive(serde::Serialize, serde::Deserialize),
1040  serde(rename_all = "kebab-case")
1041)]
1042#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
1043#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
1044pub enum WebKitScrollbarPseudoElement {
1045  /// ::-webkit-scrollbar
1046  Scrollbar,
1047  /// ::-webkit-scrollbar-button
1048  Button,
1049  /// ::-webkit-scrollbar-track
1050  Track,
1051  /// ::-webkit-scrollbar-track-piece
1052  TrackPiece,
1053  /// ::-webkit-scrollbar-thumb
1054  Thumb,
1055  /// ::-webkit-scrollbar-corner
1056  Corner,
1057  /// ::-webkit-resizer
1058  Resizer,
1059}
1060
1061/// A [view transition part name](https://w3c.github.io/csswg-drafts/css-view-transitions-1/#typedef-pt-name-selector).
1062#[derive(PartialEq, Eq, Clone, Debug, Hash)]
1063#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1064#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
1065pub enum ViewTransitionPartName<'i> {
1066  /// *
1067  #[cfg_attr(feature = "serde", serde(rename = "*"))]
1068  All,
1069  /// <custom-ident>
1070  #[cfg_attr(feature = "serde", serde(borrow, untagged))]
1071  Name(CustomIdent<'i>),
1072}
1073
1074#[cfg(feature = "jsonschema")]
1075#[cfg_attr(docsrs, doc(cfg(feature = "jsonschema")))]
1076impl<'a> schemars::JsonSchema for ViewTransitionPartName<'a> {
1077  fn is_referenceable() -> bool {
1078    true
1079  }
1080
1081  fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
1082    str::json_schema(gen)
1083  }
1084
1085  fn schema_name() -> String {
1086    "ViewTransitionPartName".into()
1087  }
1088}
1089
1090impl<'i> Parse<'i> for ViewTransitionPartName<'i> {
1091  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
1092    if input.try_parse(|input| input.expect_delim('*')).is_ok() {
1093      return Ok(ViewTransitionPartName::All);
1094    }
1095
1096    Ok(ViewTransitionPartName::Name(CustomIdent::parse(input)?))
1097  }
1098}
1099
1100impl<'i> ToCss for ViewTransitionPartName<'i> {
1101  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
1102  where
1103    W: std::fmt::Write,
1104  {
1105    match self {
1106      ViewTransitionPartName::All => dest.write_char('*'),
1107      ViewTransitionPartName::Name(name) => name.to_css(dest),
1108    }
1109  }
1110}
1111
1112/// A [view transition part selector](https://w3c.github.io/csswg-drafts/css-view-transitions-1/#typedef-pt-name-selector).
1113#[derive(PartialEq, Eq, Clone, Debug, Hash)]
1114#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1115#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
1116#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
1117pub struct ViewTransitionPartSelector<'i> {
1118  /// The view transition part name.
1119  #[cfg_attr(feature = "serde", serde(borrow))]
1120  name: Option<ViewTransitionPartName<'i>>,
1121  /// A list of view transition classes.
1122  classes: Vec<CustomIdent<'i>>,
1123}
1124
1125impl<'i> Parse<'i> for ViewTransitionPartSelector<'i> {
1126  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
1127    input.skip_whitespace();
1128    let name = input.try_parse(ViewTransitionPartName::parse).ok();
1129    let mut classes = Vec::new();
1130    while let Ok(token) = input.next_including_whitespace() {
1131      if matches!(token, Token::Delim('.')) {
1132        match input.next_including_whitespace() {
1133          Ok(Token::Ident(id)) => classes.push(CustomIdent(id.into())),
1134          _ => return Err(input.new_custom_error(ParserError::SelectorError(SelectorError::InvalidState))),
1135        }
1136      } else {
1137        break;
1138      }
1139    }
1140
1141    if !input.is_exhausted() || (name.is_none() && classes.is_empty()) {
1142      return Err(input.new_custom_error(ParserError::SelectorError(SelectorError::InvalidState)));
1143    }
1144
1145    Ok(ViewTransitionPartSelector { name, classes })
1146  }
1147}
1148
1149impl<'i> ToCss for ViewTransitionPartSelector<'i> {
1150  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
1151  where
1152    W: std::fmt::Write,
1153  {
1154    if let Some(name) = &self.name {
1155      name.to_css(dest)?;
1156    }
1157    for class in &self.classes {
1158      dest.write_char('.')?;
1159      class.to_css(dest)?;
1160    }
1161    Ok(())
1162  }
1163}
1164
1165impl<'i> cssparser::ToCss for PseudoElement<'i> {
1166  fn to_css<W>(&self, dest: &mut W) -> std::fmt::Result
1167  where
1168    W: fmt::Write,
1169  {
1170    let mut s = String::new();
1171    serialize_pseudo_element(self, &mut Printer::new(&mut s, Default::default()), None)
1172      .map_err(|_| std::fmt::Error)?;
1173    write!(dest, "{}", s)
1174  }
1175}
1176
1177fn serialize_pseudo_element<'a, 'i, W>(
1178  pseudo_element: &PseudoElement,
1179  dest: &mut Printer<W>,
1180  context: Option<&StyleContext>,
1181) -> Result<(), PrinterError>
1182where
1183  W: fmt::Write,
1184{
1185  use PseudoElement::*;
1186
1187  macro_rules! write_prefix {
1188    ($prefix: ident) => {{
1189      dest.write_str("::")?;
1190      // If the printer has a vendor prefix override, use that.
1191      let vp = if !dest.vendor_prefix.is_empty() {
1192        (dest.vendor_prefix & *$prefix).or_none()
1193      } else {
1194        *$prefix
1195      };
1196      vp.to_css(dest)?;
1197      vp
1198    }};
1199  }
1200
1201  macro_rules! write_prefixed {
1202    ($prefix: ident, $val: expr) => {{
1203      write_prefix!($prefix);
1204      dest.write_str($val)
1205    }};
1206  }
1207
1208  match pseudo_element {
1209    // CSS2 pseudo elements support a single colon syntax in addition
1210    // to the more correct double colon for other pseudo elements.
1211    // We use that here because it's supported everywhere and is shorter.
1212    After => dest.write_str(":after"),
1213    Before => dest.write_str(":before"),
1214    FirstLine => dest.write_str(":first-line"),
1215    FirstLetter => dest.write_str(":first-letter"),
1216    DetailsContent => dest.write_str("::details-content"),
1217    TargetText => dest.write_str("::target-text"),
1218    SearchText => dest.write_str("::search-text"),
1219    HighlightFunction { name } => {
1220      dest.write_str("::highlight(")?;
1221      name.to_css(dest)?;
1222      dest.write_char(')')
1223    }
1224    Marker => dest.write_str("::marker"),
1225    Selection(prefix) => write_prefixed!(prefix, "selection"),
1226    Cue => dest.write_str("::cue"),
1227    CueRegion => dest.write_str("::cue-region"),
1228    CueFunction { selector } => {
1229      dest.write_str("::cue(")?;
1230      serialize_selector(selector, dest, context, false)?;
1231      dest.write_char(')')
1232    }
1233    CueRegionFunction { selector } => {
1234      dest.write_str("::cue-region(")?;
1235      serialize_selector(selector, dest, context, false)?;
1236      dest.write_char(')')
1237    }
1238    Placeholder(prefix) => {
1239      let vp = write_prefix!(prefix);
1240      if vp == VendorPrefix::WebKit || vp == VendorPrefix::Ms {
1241        dest.write_str("input-placeholder")
1242      } else {
1243        dest.write_str("placeholder")
1244      }
1245    }
1246    Backdrop(prefix) => write_prefixed!(prefix, "backdrop"),
1247    FileSelectorButton(prefix) => {
1248      let vp = write_prefix!(prefix);
1249      if vp == VendorPrefix::WebKit {
1250        dest.write_str("file-upload-button")
1251      } else if vp == VendorPrefix::Ms {
1252        dest.write_str("browse")
1253      } else {
1254        dest.write_str("file-selector-button")
1255      }
1256    }
1257    WebKitScrollbar(s) => {
1258      use WebKitScrollbarPseudoElement::*;
1259      dest.write_str(match s {
1260        Scrollbar => "::-webkit-scrollbar",
1261        Button => "::-webkit-scrollbar-button",
1262        Track => "::-webkit-scrollbar-track",
1263        TrackPiece => "::-webkit-scrollbar-track-piece",
1264        Thumb => "::-webkit-scrollbar-thumb",
1265        Corner => "::-webkit-scrollbar-corner",
1266        Resizer => "::-webkit-resizer",
1267      })
1268    }
1269    ViewTransition => dest.write_str("::view-transition"),
1270    ViewTransitionGroup { part } => {
1271      dest.write_str("::view-transition-group(")?;
1272      part.to_css(dest)?;
1273      dest.write_char(')')
1274    }
1275    ViewTransitionImagePair { part } => {
1276      dest.write_str("::view-transition-image-pair(")?;
1277      part.to_css(dest)?;
1278      dest.write_char(')')
1279    }
1280    ViewTransitionOld { part } => {
1281      dest.write_str("::view-transition-old(")?;
1282      part.to_css(dest)?;
1283      dest.write_char(')')
1284    }
1285    ViewTransitionNew { part } => {
1286      dest.write_str("::view-transition-new(")?;
1287      part.to_css(dest)?;
1288      dest.write_char(')')
1289    }
1290    PickerFunction { identifier } => {
1291      dest.write_str("::picker(")?;
1292      identifier.to_css(dest)?;
1293      dest.write_char(')')
1294    }
1295    PickerIcon => dest.write_str("::picker-icon"),
1296    Checkmark => dest.write_str("::checkmark"),
1297    GrammarError => dest.write_str("::grammar-error"),
1298    SpellingError => dest.write_str("::spelling-error"),
1299    Custom { name: val } => {
1300      dest.write_str("::")?;
1301      return dest.write_str(val);
1302    }
1303    CustomFunction { name, arguments: args } => {
1304      dest.write_str("::")?;
1305      dest.write_str(name)?;
1306      dest.write_char('(')?;
1307      args.to_css_raw(dest)?;
1308      dest.write_char(')')
1309    }
1310  }
1311}
1312
1313impl<'i> parcel_selectors::parser::PseudoElement<'i> for PseudoElement<'i> {
1314  type Impl = Selectors;
1315
1316  fn accepts_state_pseudo_classes(&self) -> bool {
1317    // Be lenient.
1318    true
1319  }
1320
1321  fn valid_after_slotted(&self) -> bool {
1322    // ::slotted() should support all tree-abiding pseudo-elements, see
1323    // https://drafts.csswg.org/css-scoping/#slotted-pseudo
1324    // https://drafts.csswg.org/css-pseudo-4/#treelike
1325    matches!(
1326      *self,
1327      PseudoElement::Before
1328        | PseudoElement::After
1329        | PseudoElement::Marker
1330        | PseudoElement::Placeholder(_)
1331        | PseudoElement::FileSelectorButton(_)
1332    )
1333  }
1334
1335  fn is_webkit_scrollbar(&self) -> bool {
1336    matches!(*self, PseudoElement::WebKitScrollbar(..))
1337  }
1338
1339  fn is_view_transition(&self) -> bool {
1340    matches!(
1341      *self,
1342      PseudoElement::ViewTransitionGroup { .. }
1343        | PseudoElement::ViewTransitionImagePair { .. }
1344        | PseudoElement::ViewTransitionNew { .. }
1345        | PseudoElement::ViewTransitionOld { .. }
1346    )
1347  }
1348
1349  fn is_unknown(&self) -> bool {
1350    matches!(
1351      *self,
1352      PseudoElement::Custom { .. } | PseudoElement::CustomFunction { .. },
1353    )
1354  }
1355}
1356
1357impl<'i> PseudoElement<'i> {
1358  pub(crate) fn is_equivalent(&self, other: &PseudoElement<'i>) -> bool {
1359    use PseudoElement::*;
1360    match (self, other) {
1361      (Selection(_), Selection(_))
1362      | (Placeholder(_), Placeholder(_))
1363      | (Backdrop(_), Backdrop(_))
1364      | (FileSelectorButton(_), FileSelectorButton(_)) => true,
1365      (a, b) => a == b,
1366    }
1367  }
1368
1369  pub(crate) fn get_prefix(&self) -> VendorPrefix {
1370    use PseudoElement::*;
1371    match self {
1372      Selection(p) | Placeholder(p) | Backdrop(p) | FileSelectorButton(p) => *p,
1373      _ => VendorPrefix::empty(),
1374    }
1375  }
1376
1377  pub(crate) fn get_necessary_prefixes(&mut self, targets: Targets) -> VendorPrefix {
1378    use crate::prefixes::Feature;
1379    use PseudoElement::*;
1380    let (p, feature) = match self {
1381      Selection(p) => (p, Feature::PseudoElementSelection),
1382      Placeholder(p) => (p, Feature::PseudoElementPlaceholder),
1383      Backdrop(p) => (p, Feature::PseudoElementBackdrop),
1384      FileSelectorButton(p) => (p, Feature::PseudoElementFileSelectorButton),
1385      _ => return VendorPrefix::empty(),
1386    };
1387
1388    *p = targets.prefixes(*p, feature);
1389    *p
1390  }
1391}
1392
1393impl<'a, 'i> ToCss for SelectorList<'i> {
1394  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
1395  where
1396    W: fmt::Write,
1397  {
1398    serialize_selector_list(self.0.iter(), dest, dest.context(), false)
1399  }
1400}
1401
1402impl ToCss for Combinator {
1403  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
1404  where
1405    W: fmt::Write,
1406  {
1407    match *self {
1408      Combinator::Child => dest.delim('>', true),
1409      Combinator::Descendant => dest.write_str(" "),
1410      Combinator::NextSibling => dest.delim('+', true),
1411      Combinator::LaterSibling => dest.delim('~', true),
1412      Combinator::Deep => dest.write_str(" /deep/ "),
1413      Combinator::DeepDescendant => {
1414        dest.whitespace()?;
1415        dest.write_str(">>>")?;
1416        dest.whitespace()
1417      }
1418      Combinator::PseudoElement | Combinator::Part | Combinator::SlotAssignment => Ok(()),
1419    }
1420  }
1421}
1422
1423// Copied from the selectors crate and modified to override to_css implementation.
1424impl<'a, 'i> ToCss for Selector<'i> {
1425  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
1426  where
1427    W: fmt::Write,
1428  {
1429    serialize_selector(self, dest, dest.context(), false)
1430  }
1431}
1432
1433fn serialize_selector<'a, 'i, W>(
1434  selector: &Selector<'i>,
1435  dest: &mut Printer<W>,
1436  context: Option<&StyleContext>,
1437  mut is_relative: bool,
1438) -> Result<(), PrinterError>
1439where
1440  W: fmt::Write,
1441{
1442  use parcel_selectors::parser::*;
1443  // Compound selectors invert the order of their contents, so we need to
1444  // undo that during serialization.
1445  //
1446  // This two-iterator strategy involves walking over the selector twice.
1447  // We could do something more clever, but selector serialization probably
1448  // isn't hot enough to justify it, and the stringification likely
1449  // dominates anyway.
1450  //
1451  // NB: A parse-order iterator is a Rev<>, which doesn't expose as_slice(),
1452  // which we need for |split|. So we split by combinators on a match-order
1453  // sequence and then reverse.
1454
1455  let mut combinators = selector.iter_raw_match_order().rev().filter_map(|x| x.as_combinator());
1456  let compound_selectors = selector.iter_raw_match_order().as_slice().split(|x| x.is_combinator()).rev();
1457  let should_compile_nesting = should_compile!(dest.targets.current, Nesting);
1458
1459  let mut first = true;
1460  let mut combinators_exhausted = false;
1461  for mut compound in compound_selectors {
1462    debug_assert!(!combinators_exhausted);
1463
1464    // Skip implicit :scope in relative selectors (e.g. :has(:scope > foo) -> :has(> foo))
1465    if is_relative && matches!(compound.get(0), Some(Component::Scope)) {
1466      if let Some(combinator) = combinators.next() {
1467        combinator.to_css(dest)?;
1468      }
1469      compound = &compound[1..];
1470      is_relative = false;
1471    }
1472
1473    // https://drafts.csswg.org/cssom/#serializing-selectors
1474    if compound.is_empty() {
1475      continue;
1476    }
1477
1478    let has_leading_nesting = first && matches!(compound[0], Component::Nesting);
1479    let first_index = if has_leading_nesting { 1 } else { 0 };
1480    first = false;
1481
1482    // 1. If there is only one simple selector in the compound selectors
1483    //    which is a universal selector, append the result of
1484    //    serializing the universal selector to s.
1485    //
1486    // Check if `!compound.empty()` first--this can happen if we have
1487    // something like `... > ::before`, because we store `>` and `::`
1488    // both as combinators internally.
1489    //
1490    // If we are in this case, after we have serialized the universal
1491    // selector, we skip Step 2 and continue with the algorithm.
1492    let (can_elide_namespace, first_non_namespace) = match compound.get(first_index) {
1493      Some(Component::ExplicitAnyNamespace)
1494      | Some(Component::ExplicitNoNamespace)
1495      | Some(Component::Namespace(..)) => (false, first_index + 1),
1496      Some(Component::DefaultNamespace(..)) => (true, first_index + 1),
1497      _ => (true, first_index),
1498    };
1499    let mut perform_step_2 = true;
1500    let next_combinator = combinators.next();
1501    if first_non_namespace == compound.len() - 1 {
1502      match (next_combinator, &compound[first_non_namespace]) {
1503        // We have to be careful here, because if there is a
1504        // pseudo element "combinator" there isn't really just
1505        // the one simple selector. Technically this compound
1506        // selector contains the pseudo element selector as well
1507        // -- Combinator::PseudoElement, just like
1508        // Combinator::SlotAssignment, don't exist in the
1509        // spec.
1510        (Some(Combinator::PseudoElement), _) | (Some(Combinator::SlotAssignment), _) => (),
1511        (_, &Component::ExplicitUniversalType) => {
1512          // Iterate over everything so we serialize the namespace
1513          // too.
1514          let mut iter = compound.iter();
1515          let swap_nesting = has_leading_nesting && should_compile_nesting;
1516          if swap_nesting {
1517            // Swap nesting and type selector (e.g. &div -> div&).
1518            iter.next();
1519          }
1520
1521          for simple in iter {
1522            serialize_component(simple, dest, context)?;
1523          }
1524
1525          if swap_nesting {
1526            serialize_nesting(dest, context, false)?;
1527          }
1528
1529          // Skip step 2, which is an "otherwise".
1530          perform_step_2 = false;
1531        }
1532        _ => (),
1533      }
1534    }
1535
1536    // 2. Otherwise, for each simple selector in the compound selectors
1537    //    that is not a universal selector of which the namespace prefix
1538    //    maps to a namespace that is not the default namespace
1539    //    serialize the simple selector and append the result to s.
1540    //
1541    // See https://github.com/w3c/csswg-drafts/issues/1606, which is
1542    // proposing to change this to match up with the behavior asserted
1543    // in cssom/serialize-namespaced-type-selectors.html, which the
1544    // following code tries to match.
1545    if perform_step_2 {
1546      let mut iter = compound.iter();
1547      if has_leading_nesting && should_compile_nesting && is_type_selector(compound.get(first_non_namespace)) {
1548        // Swap nesting and type selector (e.g. &div -> div&).
1549        // This ensures that the compiled selector is valid. e.g. (div.foo is valid, .foodiv is not).
1550        let nesting = iter.next().unwrap();
1551        let local = iter.next().unwrap();
1552        serialize_component(local, dest, context)?;
1553
1554        // Also check the next item in case of namespaces.
1555        if first_non_namespace > first_index {
1556          let local = iter.next().unwrap();
1557          serialize_component(local, dest, context)?;
1558        }
1559
1560        serialize_component(nesting, dest, context)?;
1561      } else if has_leading_nesting && should_compile_nesting {
1562        // Nesting selector may serialize differently if it is leading, due to type selectors.
1563        iter.next();
1564        serialize_nesting(dest, context, true)?;
1565      }
1566
1567      for simple in iter {
1568        if let Component::ExplicitUniversalType = *simple {
1569          // Can't have a namespace followed by a pseudo-element
1570          // selector followed by a universal selector in the same
1571          // compound selector, so we don't have to worry about the
1572          // real namespace being in a different `compound`.
1573          if can_elide_namespace {
1574            continue;
1575          }
1576        }
1577        serialize_component(simple, dest, context)?;
1578      }
1579    }
1580
1581    // 3. If this is not the last part of the chain of the selector
1582    //    append a single SPACE (U+0020), followed by the combinator
1583    //    ">", "+", "~", ">>", "||", as appropriate, followed by another
1584    //    single SPACE (U+0020) if the combinator was not whitespace, to
1585    //    s.
1586    match next_combinator {
1587      Some(c) => c.to_css(dest)?,
1588      None => combinators_exhausted = true,
1589    };
1590
1591    // 4. If this is the last part of the chain of the selector and
1592    //    there is a pseudo-element, append "::" followed by the name of
1593    //    the pseudo-element, to s.
1594    //
1595    // (we handle this above)
1596  }
1597
1598  Ok(())
1599}
1600
1601fn serialize_component<'a, 'i, W>(
1602  component: &Component,
1603  dest: &mut Printer<W>,
1604  context: Option<&StyleContext>,
1605) -> Result<(), PrinterError>
1606where
1607  W: fmt::Write,
1608{
1609  match component {
1610    Component::Combinator(ref c) => c.to_css(dest),
1611    Component::AttributeInNoNamespace {
1612      ref local_name,
1613      operator,
1614      ref value,
1615      case_sensitivity,
1616      ..
1617    } => {
1618      dest.write_char('[')?;
1619      cssparser::ToCss::to_css(local_name, dest)?;
1620      cssparser::ToCss::to_css(operator, dest)?;
1621
1622      if dest.minify {
1623        // Serialize as both an identifier and a string and choose the shorter one.
1624        let mut id = String::new();
1625        serialize_identifier(&value, &mut id)?;
1626
1627        let s = value.to_css_string(Default::default())?;
1628
1629        if id.len() > 0 && id.len() < s.len() {
1630          dest.write_str(&id)?;
1631        } else {
1632          dest.write_str(&s)?;
1633        }
1634      } else {
1635        value.to_css(dest)?;
1636      }
1637
1638      match case_sensitivity {
1639        parcel_selectors::attr::ParsedCaseSensitivity::CaseSensitive
1640        | parcel_selectors::attr::ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {}
1641        parcel_selectors::attr::ParsedCaseSensitivity::AsciiCaseInsensitive => dest.write_str(" i")?,
1642        parcel_selectors::attr::ParsedCaseSensitivity::ExplicitCaseSensitive => dest.write_str(" s")?,
1643      }
1644      dest.write_char(']')
1645    }
1646    Component::Is(ref list)
1647    | Component::Where(ref list)
1648    | Component::Negation(ref list)
1649    | Component::Any(_, ref list) => {
1650      match *component {
1651        Component::Where(..) => dest.write_str(":where(")?,
1652        Component::Is(ref selectors) => {
1653          // If there's only one simple selector, serialize it directly.
1654          if should_unwrap_is(selectors) {
1655            serialize_selector(selectors.first().unwrap(), dest, context, false)?;
1656            return Ok(());
1657          }
1658
1659          let vp = dest.vendor_prefix;
1660          if vp.intersects(VendorPrefix::WebKit | VendorPrefix::Moz) {
1661            dest.write_char(':')?;
1662            vp.to_css(dest)?;
1663            dest.write_str("any(")?;
1664          } else {
1665            dest.write_str(":is(")?;
1666          }
1667        }
1668        Component::Negation(_) => {
1669          dest.write_str(":not(")?;
1670        }
1671        Component::Any(prefix, ..) => {
1672          let vp = dest.vendor_prefix.or(prefix);
1673          if vp.intersects(VendorPrefix::WebKit | VendorPrefix::Moz) {
1674            dest.write_char(':')?;
1675            vp.to_css(dest)?;
1676            dest.write_str("any(")?;
1677          } else {
1678            dest.write_str(":is(")?;
1679          }
1680        }
1681        _ => unreachable!(),
1682      }
1683      serialize_selector_list(list.iter(), dest, context, false)?;
1684      dest.write_str(")")
1685    }
1686    Component::Has(ref list) => {
1687      dest.write_str(":has(")?;
1688      serialize_selector_list(list.iter(), dest, context, true)?;
1689      dest.write_str(")")
1690    }
1691    Component::NonTSPseudoClass(pseudo) => serialize_pseudo_class(pseudo, dest, context),
1692    Component::PseudoElement(pseudo) => serialize_pseudo_element(pseudo, dest, context),
1693    Component::Nesting => serialize_nesting(dest, context, false),
1694    Component::Class(ref class) => {
1695      dest.write_char('.')?;
1696      dest.write_ident(&class.0, true)
1697    }
1698    Component::ID(ref id) => {
1699      dest.write_char('#')?;
1700      dest.write_ident(&id.0, true)
1701    }
1702    Component::Host(selector) => {
1703      dest.write_str(":host")?;
1704      if let Some(ref selector) = *selector {
1705        dest.write_char('(')?;
1706        selector.to_css(dest)?;
1707        dest.write_char(')')?;
1708      }
1709      Ok(())
1710    }
1711    Component::Slotted(ref selector) => {
1712      dest.write_str("::slotted(")?;
1713      selector.to_css(dest)?;
1714      dest.write_char(')')
1715    }
1716    Component::NthOf(ref nth_of_data) => {
1717      let nth_data = nth_of_data.nth_data();
1718      nth_data.write_start(dest, true)?;
1719      nth_data.write_affine(dest)?;
1720      dest.write_str(" of ")?;
1721      serialize_selector_list(nth_of_data.selectors().iter(), dest, context, true)?;
1722      dest.write_char(')')
1723    }
1724    _ => {
1725      cssparser::ToCss::to_css(component, dest)?;
1726      Ok(())
1727    }
1728  }
1729}
1730
1731fn should_unwrap_is<'i>(selectors: &Box<[Selector<'i>]>) -> bool {
1732  if selectors.len() == 1 {
1733    let first = selectors.first().unwrap();
1734    if !has_type_selector(first) && is_simple(first) {
1735      return true;
1736    }
1737  }
1738
1739  false
1740}
1741
1742fn serialize_nesting<W>(
1743  dest: &mut Printer<W>,
1744  context: Option<&StyleContext>,
1745  first: bool,
1746) -> Result<(), PrinterError>
1747where
1748  W: fmt::Write,
1749{
1750  if let Some(ctx) = context {
1751    // If there's only one simple selector, just serialize it directly.
1752    // Otherwise, use an :is() pseudo class.
1753    // Type selectors are only allowed at the start of a compound selector,
1754    // so use :is() if that is not the case.
1755    if ctx.selectors.0.len() == 1
1756      && (first || (!has_type_selector(&ctx.selectors.0[0]) && is_simple(&ctx.selectors.0[0])))
1757    {
1758      serialize_selector(ctx.selectors.0.first().unwrap(), dest, ctx.parent, false)
1759    } else {
1760      dest.write_str(":is(")?;
1761      serialize_selector_list(ctx.selectors.0.iter(), dest, ctx.parent, false)?;
1762      dest.write_char(')')
1763    }
1764  } else {
1765    // If there is no context, we are at the root if nesting is supported. This is equivalent to :scope.
1766    // Otherwise, if nesting is supported, serialize the nesting selector directly.
1767    if should_compile!(dest.targets.current, Nesting) {
1768      dest.write_str(":scope")
1769    } else {
1770      dest.write_char('&')
1771    }
1772  }
1773}
1774
1775#[inline]
1776fn has_type_selector(selector: &Selector) -> bool {
1777  // For input:checked the component vector is
1778  // [input, :checked] so we have to check it using matching order.
1779  //
1780  // This both happens for input:checked and is(input:checked)
1781  let mut iter = selector.iter_raw_match_order();
1782  let first = iter.next();
1783
1784  if is_namespace(first) {
1785    is_type_selector(iter.next())
1786  } else {
1787    is_type_selector(first)
1788  }
1789}
1790
1791#[inline]
1792fn is_simple(selector: &Selector) -> bool {
1793  !selector.iter_raw_match_order().any(|component| component.is_combinator())
1794}
1795
1796#[inline]
1797fn is_type_selector(component: Option<&Component>) -> bool {
1798  matches!(
1799    component,
1800    Some(Component::LocalName(_)) | Some(Component::ExplicitUniversalType)
1801  )
1802}
1803
1804#[inline]
1805fn is_namespace(component: Option<&Component>) -> bool {
1806  matches!(
1807    component,
1808    Some(Component::ExplicitAnyNamespace)
1809      | Some(Component::ExplicitNoNamespace)
1810      | Some(Component::Namespace(..))
1811      | Some(Component::DefaultNamespace(_))
1812  )
1813}
1814
1815fn serialize_selector_list<'a, 'i: 'a, I, W>(
1816  iter: I,
1817  dest: &mut Printer<W>,
1818  context: Option<&StyleContext>,
1819  is_relative: bool,
1820) -> Result<(), PrinterError>
1821where
1822  I: Iterator<Item = &'a Selector<'i>>,
1823  W: fmt::Write,
1824{
1825  let mut first = true;
1826  for selector in iter {
1827    if !first {
1828      dest.delim(',', false)?;
1829    }
1830    first = false;
1831    serialize_selector(selector, dest, context, is_relative)?;
1832  }
1833  Ok(())
1834}
1835
1836pub(crate) fn is_compatible(selectors: &[Selector], targets: Targets) -> bool {
1837  for selector in selectors {
1838    let iter = selector.iter_raw_match_order();
1839    for component in iter {
1840      let feature = match component {
1841        Component::ID(_) | Component::Class(_) | Component::LocalName(_) => continue,
1842
1843        Component::ExplicitAnyNamespace
1844        | Component::ExplicitNoNamespace
1845        | Component::DefaultNamespace(_)
1846        | Component::Namespace(_, _) => Feature::Namespaces,
1847
1848        Component::ExplicitUniversalType => Feature::Selectors2,
1849
1850        Component::AttributeInNoNamespaceExists { .. } => Feature::Selectors2,
1851        Component::AttributeInNoNamespace {
1852          operator,
1853          case_sensitivity,
1854          ..
1855        } => {
1856          if *case_sensitivity != ParsedCaseSensitivity::CaseSensitive {
1857            Feature::CaseInsensitive
1858          } else {
1859            match operator {
1860              AttrSelectorOperator::Equal | AttrSelectorOperator::Includes | AttrSelectorOperator::DashMatch => {
1861                Feature::Selectors2
1862              }
1863              AttrSelectorOperator::Prefix | AttrSelectorOperator::Substring | AttrSelectorOperator::Suffix => {
1864                Feature::Selectors3
1865              }
1866            }
1867          }
1868        }
1869        Component::AttributeOther(attr) => match attr.operation {
1870          ParsedAttrSelectorOperation::Exists => Feature::Selectors2,
1871          ParsedAttrSelectorOperation::WithValue {
1872            operator,
1873            case_sensitivity,
1874            ..
1875          } => {
1876            if case_sensitivity != ParsedCaseSensitivity::CaseSensitive {
1877              Feature::CaseInsensitive
1878            } else {
1879              match operator {
1880                AttrSelectorOperator::Equal | AttrSelectorOperator::Includes | AttrSelectorOperator::DashMatch => {
1881                  Feature::Selectors2
1882                }
1883                AttrSelectorOperator::Prefix | AttrSelectorOperator::Substring | AttrSelectorOperator::Suffix => {
1884                  Feature::Selectors3
1885                }
1886              }
1887            }
1888          }
1889        },
1890
1891        Component::Empty | Component::Root => Feature::Selectors3,
1892        Component::Negation(selectors) => {
1893          // :not() selector list is not forgiving.
1894          if !targets.is_compatible(Feature::Selectors3) || !is_compatible(&*selectors, targets) {
1895            return false;
1896          }
1897          continue;
1898        }
1899
1900        Component::Nth(data) => match data.ty {
1901          NthType::Child if data.a == 0 && data.b == 1 => Feature::Selectors2,
1902          NthType::Col | NthType::LastCol => return false,
1903          _ => Feature::Selectors3,
1904        },
1905        Component::NthOf(n) => {
1906          if !targets.is_compatible(Feature::NthChildOf) || !is_compatible(n.selectors(), targets) {
1907            return false;
1908          }
1909          continue;
1910        }
1911
1912        // These support forgiving selector lists, so no need to check nested selectors.
1913        Component::Is(selectors) => {
1914          // ... except if we are going to unwrap them.
1915          if should_unwrap_is(selectors) && is_compatible(selectors, targets) {
1916            continue;
1917          }
1918
1919          Feature::IsSelector
1920        }
1921        Component::Where(_) | Component::Nesting => Feature::IsSelector,
1922        Component::Any(..) => return false,
1923        Component::Has(selectors) => {
1924          if !targets.is_compatible(Feature::HasSelector) || !is_compatible(&*selectors, targets) {
1925            return false;
1926          }
1927          continue;
1928        }
1929
1930        Component::Scope | Component::Host(_) | Component::Slotted(_) => Feature::Shadowdomv1,
1931
1932        Component::Part(_) => Feature::PartPseudo,
1933
1934        Component::NonTSPseudoClass(pseudo) => {
1935          match pseudo {
1936            PseudoClass::Link
1937            | PseudoClass::Visited
1938            | PseudoClass::Active
1939            | PseudoClass::Hover
1940            | PseudoClass::Focus
1941            | PseudoClass::Lang { languages: _ } => Feature::Selectors2,
1942
1943            PseudoClass::Checked | PseudoClass::Disabled | PseudoClass::Enabled | PseudoClass::Target => {
1944              Feature::Selectors3
1945            }
1946
1947            PseudoClass::AnyLink(prefix) if *prefix == VendorPrefix::None => Feature::AnyLink,
1948            PseudoClass::Indeterminate => Feature::IndeterminatePseudo,
1949
1950            PseudoClass::Fullscreen(prefix) if *prefix == VendorPrefix::None => Feature::Fullscreen,
1951
1952            PseudoClass::FocusVisible => Feature::FocusVisible,
1953            PseudoClass::FocusWithin => Feature::FocusWithin,
1954            PseudoClass::Default => Feature::DefaultPseudo,
1955            PseudoClass::Dir { direction: _ } => Feature::DirSelector,
1956            PseudoClass::Optional => Feature::OptionalPseudo,
1957            PseudoClass::PlaceholderShown(prefix) if *prefix == VendorPrefix::None => Feature::PlaceholderShown,
1958
1959            PseudoClass::TargetCurrent => Feature::TargetCurrent,
1960            PseudoClass::TargetBefore | PseudoClass::TargetAfter => Feature::TargetBeforeAfter,
1961
1962            PseudoClass::ReadOnly(prefix) | PseudoClass::ReadWrite(prefix) if *prefix == VendorPrefix::None => {
1963              Feature::ReadOnlyWrite
1964            }
1965
1966            PseudoClass::Valid | PseudoClass::Invalid | PseudoClass::Required => Feature::FormValidation,
1967
1968            PseudoClass::InRange | PseudoClass::OutOfRange => Feature::InOutOfRange,
1969
1970            PseudoClass::Autofill(prefix) if *prefix == VendorPrefix::None => Feature::Autofill,
1971
1972            PseudoClass::State { .. } => Feature::StatePseudoClass,
1973
1974            // Experimental, no browser support.
1975            PseudoClass::Current
1976            | PseudoClass::Past
1977            | PseudoClass::Future
1978            | PseudoClass::Playing
1979            | PseudoClass::Paused
1980            | PseudoClass::Seeking
1981            | PseudoClass::Stalled
1982            | PseudoClass::Buffering
1983            | PseudoClass::Muted
1984            | PseudoClass::VolumeLocked
1985            | PseudoClass::TargetWithin
1986            | PseudoClass::LocalLink
1987            | PseudoClass::Blank
1988            | PseudoClass::UserInvalid
1989            | PseudoClass::UserValid
1990            | PseudoClass::Defined
1991            | PseudoClass::ActiveViewTransition
1992            | PseudoClass::ActiveViewTransitionType { .. } => return false,
1993
1994            PseudoClass::Custom { .. } | _ => return false,
1995          }
1996        }
1997
1998        Component::PseudoElement(pseudo) => match pseudo {
1999          PseudoElement::After | PseudoElement::Before => Feature::Gencontent,
2000          PseudoElement::FirstLine => Feature::FirstLine,
2001          PseudoElement::FirstLetter => Feature::FirstLetter,
2002          PseudoElement::DetailsContent => Feature::DetailsContent,
2003          PseudoElement::TargetText => Feature::TargetText,
2004          PseudoElement::SearchText => Feature::SearchText,
2005          PseudoElement::Selection(prefix) if *prefix == VendorPrefix::None => Feature::Selection,
2006          PseudoElement::Placeholder(prefix) if *prefix == VendorPrefix::None => Feature::Placeholder,
2007          PseudoElement::HighlightFunction { name: _ } => Feature::Highlight,
2008          PseudoElement::Marker => Feature::MarkerPseudo,
2009          PseudoElement::Backdrop(prefix) if *prefix == VendorPrefix::None => Feature::Dialog,
2010          PseudoElement::Cue => Feature::Cue,
2011          PseudoElement::CueFunction { selector: _ } => Feature::CueFunction,
2012          PseudoElement::ViewTransition
2013          | PseudoElement::ViewTransitionNew { .. }
2014          | PseudoElement::ViewTransitionOld { .. }
2015          | PseudoElement::ViewTransitionGroup { .. }
2016          | PseudoElement::ViewTransitionImagePair { .. } => Feature::ViewTransition,
2017          PseudoElement::PickerFunction { identifier: _ } => Feature::Picker,
2018          PseudoElement::PickerIcon => Feature::PickerIcon,
2019          PseudoElement::Checkmark => Feature::Checkmark,
2020          PseudoElement::GrammarError => Feature::GrammarError,
2021          PseudoElement::SpellingError => Feature::SpellingError,
2022          PseudoElement::Custom { name: _ } | _ => return false,
2023        },
2024
2025        Component::Combinator(combinator) => match combinator {
2026          Combinator::Child | Combinator::NextSibling => Feature::Selectors2,
2027          Combinator::LaterSibling => Feature::Selectors3,
2028          _ => continue,
2029        },
2030      };
2031
2032      if !targets.is_compatible(feature) {
2033        return false;
2034      }
2035    }
2036  }
2037
2038  true
2039}
2040
2041/// Returns whether two selector lists are equivalent, i.e. the same minus any vendor prefix differences.
2042pub(crate) fn is_equivalent<'i>(selectors: &[Selector<'i>], other: &[Selector<'i>]) -> bool {
2043  if selectors.len() != other.len() {
2044    return false;
2045  }
2046
2047  for (i, a) in selectors.iter().enumerate() {
2048    let b = &other[i];
2049    if a.len() != b.len() {
2050      return false;
2051    }
2052
2053    for (a, b) in a.iter_raw_match_order().zip(b.iter_raw_match_order()) {
2054      let is_equivalent = match (a, b) {
2055        (Component::NonTSPseudoClass(a_ps), Component::NonTSPseudoClass(b_ps)) => a_ps.is_equivalent(b_ps),
2056        (Component::PseudoElement(a_pe), Component::PseudoElement(b_pe)) => a_pe.is_equivalent(b_pe),
2057        (Component::Any(_, a), Component::Is(b))
2058        | (Component::Is(a), Component::Any(_, b))
2059        | (Component::Any(_, a), Component::Any(_, b))
2060        | (Component::Is(a), Component::Is(b)) => is_equivalent(&*a, &*b),
2061        (a, b) => a == b,
2062      };
2063
2064      if !is_equivalent {
2065        return false;
2066      }
2067    }
2068  }
2069
2070  true
2071}
2072
2073/// Returns the vendor prefix (if any) used in the given selector list.
2074/// If multiple vendor prefixes are seen, this is invalid, and an empty result is returned.
2075pub(crate) fn get_prefix(selectors: &SelectorList) -> VendorPrefix {
2076  let mut prefix = VendorPrefix::empty();
2077  for selector in &selectors.0 {
2078    for component in selector.iter_raw_match_order() {
2079      let p = match component {
2080        // Return none rather than empty for these so that we call downlevel_selectors.
2081        Component::NonTSPseudoClass(PseudoClass::Lang { .. })
2082        | Component::NonTSPseudoClass(PseudoClass::Dir { .. })
2083        | Component::Is(..)
2084        | Component::Where(..)
2085        | Component::Has(..)
2086        | Component::Negation(..) => VendorPrefix::None,
2087        Component::Any(prefix, _) => *prefix,
2088        Component::NonTSPseudoClass(pc) => pc.get_prefix(),
2089        Component::PseudoElement(pe) => pe.get_prefix(),
2090        _ => VendorPrefix::empty(),
2091      };
2092
2093      if !p.is_empty() {
2094        // Allow none to be mixed with a prefix.
2095        let prefix_without_none = prefix - VendorPrefix::None;
2096        if prefix_without_none.is_empty() || prefix_without_none == p {
2097          prefix |= p;
2098        } else {
2099          return VendorPrefix::empty();
2100        }
2101      }
2102    }
2103  }
2104
2105  prefix
2106}
2107
2108const RTL_LANGS: &[&str] = &[
2109  "ae", "ar", "arc", "bcc", "bqi", "ckb", "dv", "fa", "glk", "he", "ku", "mzn", "nqo", "pnb", "ps", "sd", "ug",
2110  "ur", "yi",
2111];
2112
2113/// Downlevels the given selectors to be compatible with the given browser targets.
2114/// Returns the necessary vendor prefixes.
2115pub(crate) fn downlevel_selectors(selectors: &mut [Selector], targets: Targets) -> VendorPrefix {
2116  let mut necessary_prefixes = VendorPrefix::empty();
2117  for selector in selectors {
2118    for component in selector.iter_mut_raw_match_order() {
2119      necessary_prefixes |= downlevel_component(component, targets);
2120    }
2121  }
2122
2123  necessary_prefixes
2124}
2125
2126fn downlevel_component<'i>(component: &mut Component<'i>, targets: Targets) -> VendorPrefix {
2127  match component {
2128    Component::NonTSPseudoClass(pc) => {
2129      match pc {
2130        PseudoClass::Dir { direction: dir } => {
2131          if should_compile!(targets, DirSelector) {
2132            *component = downlevel_dir(*dir, targets);
2133            downlevel_component(component, targets)
2134          } else {
2135            VendorPrefix::empty()
2136          }
2137        }
2138        PseudoClass::Lang { languages: langs } => {
2139          // :lang() with multiple languages is not supported everywhere.
2140          // compile this to :is(:lang(a), :lang(b)) etc.
2141          if langs.len() > 1 && should_compile!(targets, LangSelectorList) {
2142            *component = Component::Is(lang_list_to_selectors(&langs));
2143            downlevel_component(component, targets)
2144          } else {
2145            VendorPrefix::empty()
2146          }
2147        }
2148        _ => pc.get_necessary_prefixes(targets),
2149      }
2150    }
2151    Component::PseudoElement(pe) => pe.get_necessary_prefixes(targets),
2152    Component::Is(selectors) => {
2153      let mut necessary_prefixes = downlevel_selectors(&mut **selectors, targets);
2154
2155      // Convert :is to :-webkit-any/:-moz-any if needed.
2156      // All selectors must be simple, no combinators are supported.
2157      if should_compile!(targets, IsSelector)
2158        && !should_unwrap_is(selectors)
2159        && selectors.iter().all(|selector| !selector.has_combinator())
2160      {
2161        necessary_prefixes |= targets.prefixes(VendorPrefix::None, crate::prefixes::Feature::AnyPseudo)
2162      } else {
2163        necessary_prefixes |= VendorPrefix::None
2164      }
2165
2166      necessary_prefixes
2167    }
2168    Component::Negation(selectors) => {
2169      let mut necessary_prefixes = downlevel_selectors(&mut **selectors, targets);
2170
2171      // Downlevel :not(.a, .b) -> :not(:is(.a, .b)) if not list is unsupported.
2172      // We need to use :is() / :-webkit-any() rather than :not(.a):not(.b) to ensure the specificity is equivalent.
2173      // https://drafts.csswg.org/selectors/#specificity-rules
2174      if selectors.len() > 1 && should_compile!(targets, NotSelectorList) {
2175        *component =
2176          Component::Negation(vec![Selector::from(Component::Is(selectors.clone()))].into_boxed_slice());
2177
2178        if should_compile!(targets, IsSelector) {
2179          necessary_prefixes |= targets.prefixes(VendorPrefix::None, crate::prefixes::Feature::AnyPseudo)
2180        } else {
2181          necessary_prefixes |= VendorPrefix::None
2182        }
2183      }
2184
2185      necessary_prefixes
2186    }
2187    Component::Where(selectors) | Component::Any(_, selectors) | Component::Has(selectors) => {
2188      downlevel_selectors(&mut **selectors, targets)
2189    }
2190    _ => VendorPrefix::empty(),
2191  }
2192}
2193
2194fn lang_list_to_selectors<'i>(langs: &Vec<CowArcStr<'i>>) -> Box<[Selector<'i>]> {
2195  langs
2196    .iter()
2197    .map(|lang| {
2198      Selector::from(Component::NonTSPseudoClass(PseudoClass::Lang {
2199        languages: vec![lang.clone()],
2200      }))
2201    })
2202    .collect::<Vec<Selector>>()
2203    .into_boxed_slice()
2204}
2205
2206fn downlevel_dir<'i>(dir: Direction, targets: Targets) -> Component<'i> {
2207  // Convert :dir to :lang. If supported, use a list of languages in a single :lang,
2208  // otherwise, use :is/:not, which may be further downleveled to e.g. :-webkit-any.
2209  let langs = RTL_LANGS.iter().map(|lang| (*lang).into()).collect();
2210  if !should_compile!(targets, LangSelectorList) {
2211    let c = Component::NonTSPseudoClass(PseudoClass::Lang { languages: langs });
2212    if dir == Direction::Ltr {
2213      Component::Negation(vec![Selector::from(c)].into_boxed_slice())
2214    } else {
2215      c
2216    }
2217  } else {
2218    if dir == Direction::Ltr {
2219      Component::Negation(lang_list_to_selectors(&langs))
2220    } else {
2221      Component::Is(lang_list_to_selectors(&langs))
2222    }
2223  }
2224}
2225
2226/// Determines whether a selector list contains only unused selectors.
2227/// A selector is considered unused if it contains a class or id component that exists in the set of unused symbols.
2228pub(crate) fn is_unused(
2229  selectors: &mut std::slice::Iter<Selector>,
2230  unused_symbols: &HashSet<String>,
2231  parent_is_unused: bool,
2232) -> bool {
2233  if unused_symbols.is_empty() {
2234    return false;
2235  }
2236
2237  selectors.all(|selector| {
2238    for component in selector.iter_raw_match_order() {
2239      match component {
2240        Component::Class(name) | Component::ID(name) => {
2241          if unused_symbols.contains(&name.0.to_string()) {
2242            return true;
2243          }
2244        }
2245        Component::Is(is) | Component::Where(is) | Component::Any(_, is) => {
2246          if is_unused(&mut is.iter(), unused_symbols, parent_is_unused) {
2247            return true;
2248          }
2249        }
2250        Component::Nesting => {
2251          if parent_is_unused {
2252            return true;
2253          }
2254        }
2255        _ => {}
2256      }
2257    }
2258
2259    false
2260  })
2261}
2262
2263/// Returns whether the selector has any class or id components.
2264pub(crate) fn is_pure_css_modules_selector(selector: &Selector) -> bool {
2265  use parcel_selectors::parser::Component;
2266  selector.iter_raw_match_order().any(|c| match c {
2267    Component::Class(_) | Component::ID(_) => true,
2268    Component::Is(s) | Component::Where(s) | Component::Has(s) | Component::Any(_, s) | Component::Negation(s) => {
2269      s.iter().any(is_pure_css_modules_selector)
2270    }
2271    Component::NthOf(nth) => nth.selectors().iter().any(is_pure_css_modules_selector),
2272    Component::Slotted(s) => is_pure_css_modules_selector(&s),
2273    Component::Host(s) => s.as_ref().map(is_pure_css_modules_selector).unwrap_or(false),
2274    Component::NonTSPseudoClass(pc) => match pc {
2275      PseudoClass::Local { selector } => is_pure_css_modules_selector(&*selector),
2276      _ => false,
2277    },
2278    _ => false,
2279  })
2280}
2281
2282#[cfg(feature = "visitor")]
2283#[cfg_attr(docsrs, doc(cfg(feature = "visitor")))]
2284impl<'i, T: Visit<'i, T, V>, V: ?Sized + Visitor<'i, T>> Visit<'i, T, V> for SelectorList<'i> {
2285  const CHILD_TYPES: VisitTypes = VisitTypes::SELECTORS;
2286
2287  fn visit(&mut self, visitor: &mut V) -> Result<(), V::Error> {
2288    if visitor.visit_types().contains(VisitTypes::SELECTORS) {
2289      visitor.visit_selector_list(self)
2290    } else {
2291      self.visit_children(visitor)
2292    }
2293  }
2294
2295  fn visit_children(&mut self, visitor: &mut V) -> Result<(), V::Error> {
2296    self.0.iter_mut().try_for_each(|selector| Visit::visit(selector, visitor))
2297  }
2298}
2299
2300#[cfg(feature = "visitor")]
2301#[cfg_attr(docsrs, doc(cfg(feature = "visitor")))]
2302impl<'i, T: Visit<'i, T, V>, V: ?Sized + Visitor<'i, T>> Visit<'i, T, V> for Selector<'i> {
2303  const CHILD_TYPES: VisitTypes = VisitTypes::SELECTORS;
2304
2305  fn visit(&mut self, visitor: &mut V) -> Result<(), V::Error> {
2306    visitor.visit_selector(self)
2307  }
2308
2309  fn visit_children(&mut self, _visitor: &mut V) -> Result<(), V::Error> {
2310    Ok(())
2311  }
2312}
2313
2314impl<'i> ParseWithOptions<'i> for Selector<'i> {
2315  fn parse_with_options<'t>(
2316    input: &mut Parser<'i, 't>,
2317    options: &ParserOptions<'i>,
2318  ) -> Result<Self, ParseError<'i, ParserError<'i>>> {
2319    Selector::parse(
2320      &SelectorParser {
2321        is_nesting_allowed: true,
2322        options: &options,
2323      },
2324      input,
2325    )
2326  }
2327}
2328
2329impl<'i> ParseWithOptions<'i> for SelectorList<'i> {
2330  fn parse_with_options<'t>(
2331    input: &mut Parser<'i, 't>,
2332    options: &ParserOptions<'i>,
2333  ) -> Result<Self, ParseError<'i, ParserError<'i>>> {
2334    SelectorList::parse(
2335      &SelectorParser {
2336        is_nesting_allowed: true,
2337        options: &options,
2338      },
2339      input,
2340      parcel_selectors::parser::ParseErrorRecovery::DiscardList,
2341      parcel_selectors::parser::NestingRequirement::None,
2342    )
2343  }
2344}