Skip to main content

lightningcss/
parser.rs

1use crate::declaration::{parse_declaration, DeclarationBlock, DeclarationList};
2use crate::error::{Error, ParserError, PrinterError};
3use crate::media_query::*;
4use crate::printer::Printer;
5use crate::properties::custom::TokenList;
6use crate::rules::container::{ContainerCondition, ContainerName, ContainerRule};
7use crate::rules::font_feature_values::FontFeatureValuesRule;
8use crate::rules::font_palette_values::FontPaletteValuesRule;
9use crate::rules::layer::{LayerBlockRule, LayerStatementRule};
10use crate::rules::nesting::NestedDeclarationsRule;
11use crate::rules::position_try::PositionTryRule;
12use crate::rules::property::PropertyRule;
13use crate::rules::scope::ScopeRule;
14use crate::rules::starting_style::StartingStyleRule;
15use crate::rules::view_transition::ViewTransitionRule;
16use crate::rules::viewport::ViewportRule;
17
18use crate::properties::font::FamilyName;
19use crate::rules::{
20  counter_style::CounterStyleRule,
21  custom_media::CustomMediaRule,
22  document::MozDocumentRule,
23  font_face::{FontFaceDeclarationParser, FontFaceRule},
24  import::ImportRule,
25  keyframes::{KeyframeListParser, KeyframesName, KeyframesRule},
26  layer::LayerName,
27  media::MediaRule,
28  namespace::NamespaceRule,
29  nesting::NestingRule,
30  page::{PageRule, PageSelector},
31  style::StyleRule,
32  supports::{SupportsCondition, SupportsRule},
33  unknown::UnknownAtRule,
34  CssRule, CssRuleList, Location,
35};
36use crate::selector::{SelectorList, SelectorParser};
37use crate::traits::{Parse, ParseWithOptions};
38use crate::values::ident::{CustomIdent, DashedIdent};
39use crate::values::string::CowArcStr;
40use crate::vendor_prefix::VendorPrefix;
41#[cfg(feature = "visitor")]
42use crate::visitor::{Visit, VisitTypes, Visitor};
43use bitflags::bitflags;
44use cssparser::*;
45use parcel_selectors::parser::{NestingRequirement, ParseErrorRecovery};
46use std::sync::{Arc, RwLock};
47
48bitflags! {
49  /// Parser feature flags to enable.
50  #[derive(Clone, Debug, Default)]
51  #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
52  pub struct ParserFlags: u8 {
53    /// Whether the enable the [CSS nesting](https://www.w3.org/TR/css-nesting-1/) draft syntax.
54    const NESTING = 1 << 0;
55    /// Whether to enable the [custom media](https://drafts.csswg.org/mediaqueries-5/#custom-mq) draft syntax.
56    const CUSTOM_MEDIA = 1 << 1;
57    /// Whether to enable the non-standard >>> and /deep/ selector combinators used by Vue and Angular.
58    const DEEP_SELECTOR_COMBINATOR = 1 << 2;
59    /// Whether to enable the [scroll navigation controls](https://drafts.csswg.org/css-overflow-5/#scroll-navigation-controls) draft syntax.
60    const SCROLL_NAVIGATION_CONTROLS = 1 << 3;
61  }
62}
63
64/// CSS parsing options.
65#[derive(Clone, Debug, Default)]
66pub struct ParserOptions<'i> {
67  /// Filename to use in error messages.
68  pub filename: String,
69  /// Whether the enable [CSS modules](https://github.com/css-modules/css-modules).
70  pub css_modules: Option<crate::css_modules::Config>,
71  /// The source index to assign to all parsed rules. Impacts the source map when
72  /// the style sheet is serialized.
73  pub source_index: u32,
74  /// Whether to ignore invalid rules and declarations rather than erroring.
75  pub error_recovery: bool,
76  /// A list that will be appended to when a warning occurs.
77  pub warnings: Option<Arc<RwLock<Vec<Error<ParserError<'i>>>>>>,
78  /// Feature flags to enable.
79  pub flags: ParserFlags,
80}
81
82#[cfg(feature = "into_owned")]
83impl<'i, 'any> static_self::IntoOwned<'any> for ParserOptions<'i> {
84  type Owned = ParserOptions<'any>;
85
86  fn into_owned(self) -> Self::Owned {
87    ParserOptions {
88      warnings: self.warnings.and_then(|warnings| {
89        if let Ok(errors) = warnings.read() {
90          Some(Arc::new(RwLock::new(
91            errors.iter().map(|e| e.clone().into_owned()).collect(),
92          )))
93        } else {
94          None
95        }
96      }),
97      ..self
98    }
99  }
100}
101
102impl<'i> ParserOptions<'i> {
103  #[inline]
104  pub(crate) fn warn(&self, warning: ParseError<'i, ParserError<'i>>) {
105    if let Some(warnings) = &self.warnings {
106      if let Ok(mut warnings) = warnings.write() {
107        warnings.push(Error::from(warning, self.filename.clone()));
108      }
109    }
110  }
111}
112
113#[derive(Clone, Default)]
114#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
115pub struct DefaultAtRuleParser;
116impl<'i> crate::traits::AtRuleParser<'i> for DefaultAtRuleParser {
117  type AtRule = DefaultAtRule;
118  type Error = ();
119  type Prelude = ();
120}
121
122#[derive(PartialEq, Clone, Debug)]
123#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
124#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
125pub struct DefaultAtRule;
126impl crate::traits::ToCss for DefaultAtRule {
127  fn to_css<W: std::fmt::Write>(&self, _: &mut Printer<W>) -> Result<(), PrinterError> {
128    Err(PrinterError {
129      kind: crate::error::PrinterErrorKind::FmtError,
130      loc: None,
131    })
132  }
133}
134
135#[cfg(feature = "into_owned")]
136impl<'any> static_self::IntoOwned<'any> for DefaultAtRule {
137  type Owned = Self;
138  fn into_owned(self) -> Self {
139    self
140  }
141}
142
143#[cfg(feature = "visitor")]
144#[cfg_attr(docsrs, doc(cfg(feature = "visitor")))]
145impl<'i, V: Visitor<'i, DefaultAtRule>> Visit<'i, DefaultAtRule, V> for DefaultAtRule {
146  const CHILD_TYPES: VisitTypes = VisitTypes::empty();
147  fn visit_children(&mut self, _: &mut V) -> Result<(), V::Error> {
148    Ok(())
149  }
150}
151
152#[derive(PartialEq, PartialOrd)]
153enum State {
154  Start = 1,
155  Layers = 2,
156  Imports = 3,
157  Namespaces = 4,
158  Body = 5,
159}
160
161/// The parser for the top-level rules in a stylesheet.
162pub struct TopLevelRuleParser<'a, 'i, T: crate::traits::AtRuleParser<'i>> {
163  pub options: &'a ParserOptions<'i>,
164  state: State,
165  at_rule_parser: &'a mut T,
166  rules: &'a mut CssRuleList<'i, T::AtRule>,
167}
168
169impl<'a, 'b, 'i, T: crate::traits::AtRuleParser<'i>> TopLevelRuleParser<'a, 'i, T> {
170  pub fn new(
171    options: &'a ParserOptions<'i>,
172    at_rule_parser: &'a mut T,
173    rules: &'a mut CssRuleList<'i, T::AtRule>,
174  ) -> Self {
175    TopLevelRuleParser {
176      options,
177      state: State::Start,
178      at_rule_parser,
179      rules,
180    }
181  }
182
183  pub fn nested<'x: 'b>(&'x mut self) -> NestedRuleParser<'x, 'i, T> {
184    NestedRuleParser {
185      options: &self.options,
186      at_rule_parser: self.at_rule_parser,
187      declarations: DeclarationList::new(),
188      important_declarations: DeclarationList::new(),
189      rules: &mut self.rules,
190      is_in_style_rule: false,
191      allow_declarations: false,
192      is_scope_body: false,
193    }
194  }
195}
196
197/// A rule prelude for at-rule with block.
198#[derive(Debug)]
199#[allow(dead_code)]
200pub enum AtRulePrelude<'i, T> {
201  /// A @font-face rule prelude.
202  FontFace,
203  /// A @font-feature-values rule prelude, with its FamilyName list.
204  FontFeatureValues(Vec<FamilyName<'i>>),
205  /// A @font-palette-values rule prelude, with its name.
206  FontPaletteValues(DashedIdent<'i>),
207  /// A @counter-style rule prelude, with its counter style name.
208  CounterStyle(CustomIdent<'i>),
209  /// A @media rule prelude, with its media queries.
210  Media(MediaList<'i>),
211  /// A @custom-media rule prelude.
212  CustomMedia(DashedIdent<'i>, MediaList<'i>),
213  /// An @supports rule, with its conditional
214  Supports(SupportsCondition<'i>),
215  /// A @viewport rule prelude.
216  Viewport(VendorPrefix),
217  /// A @keyframes rule, with its animation name and vendor prefix if exists.
218  Keyframes(KeyframesName<'i>, VendorPrefix),
219  /// A @page rule prelude.
220  Page(Vec<PageSelector<'i>>),
221  /// A @-moz-document rule.
222  MozDocument,
223  /// A @import rule prelude.
224  Import(
225    CowRcStr<'i>,
226    MediaList<'i>,
227    Option<SupportsCondition<'i>>,
228    Option<Option<LayerName<'i>>>,
229  ),
230  /// A @namespace rule prelude.
231  Namespace(Option<CowRcStr<'i>>, CowRcStr<'i>),
232  /// A @charset rule prelude.
233  Charset,
234  /// A @nest prelude.
235  Nest(SelectorList<'i>),
236  /// An @layer prelude.
237  Layer(Vec<LayerName<'i>>),
238  /// An @property prelude.
239  Property(DashedIdent<'i>),
240  /// An @position-try prelude.
241  PositionTry(DashedIdent<'i>),
242  /// A @container prelude.
243  /// Spec: https://drafts.csswg.org/css-conditional-5/#container-rule
244  /// @container [ <container-name>? <container-query>? ]!
245  Container(Option<ContainerName<'i>>, Option<ContainerCondition<'i>>),
246  /// A @starting-style prelude.
247  StartingStyle,
248  /// A @scope rule prelude.
249  Scope(Option<SelectorList<'i>>, Option<SelectorList<'i>>),
250  /// A @view-transition rule prelude.
251  ViewTransition,
252  /// An unknown prelude.
253  Unknown(CowArcStr<'i>, TokenList<'i>),
254  /// A custom prelude.
255  Custom(T),
256}
257
258impl<'i, T> AtRulePrelude<'i, T> {
259  // https://drafts.csswg.org/css-nesting/#conditionals
260  //     In addition to nested style rules, this specification allows nested group rules inside
261  //     of style rules: any at-rule whose body contains style rules can be nested inside of a
262  //     style rule as well.
263  fn allowed_in_style_rule(&self) -> bool {
264    match *self {
265      Self::Media(..)
266      | Self::Supports(..)
267      | Self::Container(..)
268      | Self::MozDocument
269      | Self::Layer(..)
270      | Self::StartingStyle
271      | Self::Scope(..)
272      | Self::Nest(..)
273      | Self::Unknown(..)
274      | Self::Custom(..) => true,
275
276      Self::Namespace(..)
277      | Self::FontFace
278      | Self::FontFeatureValues(..)
279      | Self::FontPaletteValues(..)
280      | Self::CounterStyle(..)
281      | Self::Keyframes(..)
282      | Self::Page(..)
283      | Self::Property(..)
284      | Self::PositionTry(..)
285      | Self::Import(..)
286      | Self::CustomMedia(..)
287      | Self::Viewport(..)
288      | Self::Charset
289      | Self::ViewTransition => false,
290    }
291  }
292}
293
294impl<'a, 'i, T: crate::traits::AtRuleParser<'i>> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i, T> {
295  type Prelude = AtRulePrelude<'i, T::Prelude>;
296  type AtRule = ();
297  type Error = ParserError<'i>;
298
299  fn parse_prelude<'t>(
300    &mut self,
301    name: CowRcStr<'i>,
302    input: &mut Parser<'i, 't>,
303  ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
304    match_ignore_ascii_case! { &*name,
305      "import" => {
306        if self.state > State::Imports {
307          return Err(input.new_custom_error(ParserError::UnexpectedImportRule))
308        }
309
310        let url_string = input.expect_url_or_string()?.clone();
311
312        let layer = if input.try_parse(|input| input.expect_ident_matching("layer")).is_ok() {
313          Some(None)
314        } else if input.try_parse(|input| input.expect_function_matching("layer")).is_ok() {
315          let name = input.parse_nested_block(LayerName::parse).map(|name| Some(name))?;
316          Some(name)
317        } else {
318          None
319        };
320
321        let supports = if input.try_parse(|input| input.expect_function_matching("supports")).is_ok() {
322          Some(input.parse_nested_block(|input| {
323            input.try_parse(SupportsCondition::parse).or_else(|_| SupportsCondition::parse_declaration(input))
324          })?)
325        } else {
326          None
327        };
328        let media = MediaList::parse(input, &self.options)?;
329        return Ok(AtRulePrelude::Import(url_string, media, supports, layer));
330      },
331      "namespace" => {
332        if self.state > State::Namespaces {
333          return Err(input.new_custom_error(ParserError::UnexpectedNamespaceRule))
334        }
335
336        let prefix = input.try_parse(|input| input.expect_ident_cloned()).ok();
337        let namespace = input.expect_url_or_string()?;
338        let prelude = AtRulePrelude::Namespace(prefix, namespace);
339        return Ok(prelude);
340      },
341      "charset" => {
342        // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet.
343        // Anything left is technically invalid, however, users often concatenate CSS files
344        // together, so we are more lenient and simply ignore @charset rules in the middle of a file.
345        input.expect_string()?;
346        return Ok(AtRulePrelude::Charset)
347      },
348      "custom-media" if self.options.flags.contains(ParserFlags::CUSTOM_MEDIA) => {
349        let name = DashedIdent::parse(input)?;
350        let media = MediaList::parse(input, &self.options)?;
351        return Ok(AtRulePrelude::CustomMedia(name, media))
352      },
353      _ => {}
354    }
355
356    AtRuleParser::parse_prelude(&mut self.nested(), name, input)
357  }
358
359  #[inline]
360  fn parse_block<'t>(
361    &mut self,
362    prelude: Self::Prelude,
363    start: &ParserState,
364    input: &mut Parser<'i, 't>,
365  ) -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
366    self.state = State::Body;
367    AtRuleParser::parse_block(&mut self.nested(), prelude, start, input)
368  }
369
370  #[inline]
371  fn rule_without_block(
372    &mut self,
373    prelude: AtRulePrelude<'i, T::Prelude>,
374    start: &ParserState,
375  ) -> Result<Self::AtRule, ()> {
376    let loc = start.source_location();
377    let loc = Location {
378      source_index: self.options.source_index,
379      line: loc.line,
380      column: loc.column,
381    };
382
383    match prelude {
384      AtRulePrelude::Import(url, media, supports, layer) => {
385        self.state = State::Imports;
386        self.rules.0.push(CssRule::Import(ImportRule {
387          url: url.into(),
388          layer,
389          supports,
390          media,
391          loc,
392        }));
393        Ok(())
394      }
395      AtRulePrelude::Namespace(prefix, url) => {
396        self.state = State::Namespaces;
397
398        self.rules.0.push(CssRule::Namespace(NamespaceRule {
399          prefix: prefix.map(|x| x.into()),
400          url: url.into(),
401          loc,
402        }));
403        Ok(())
404      }
405      AtRulePrelude::CustomMedia(name, query) => {
406        self.state = State::Body;
407        self.rules.0.push(CssRule::CustomMedia(CustomMediaRule { name, query, loc }));
408        Ok(())
409      }
410      AtRulePrelude::Layer(_) => {
411        // @layer statements are allowed before @import rules, but cannot be interleaved.
412        if self.state <= State::Layers {
413          self.state = State::Layers;
414        } else {
415          self.state = State::Body;
416        }
417        AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)
418      }
419      AtRulePrelude::Charset => Ok(()),
420      AtRulePrelude::Unknown(name, prelude) => {
421        self.rules.0.push(CssRule::Unknown(UnknownAtRule {
422          name,
423          prelude,
424          block: None,
425          loc,
426        }));
427        Ok(())
428      }
429      AtRulePrelude::Custom(_) => {
430        self.state = State::Body;
431        AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)
432      }
433      _ => Err(()),
434    }
435  }
436}
437
438impl<'a, 'i, T: crate::traits::AtRuleParser<'i>> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, 'i, T> {
439  type Prelude = SelectorList<'i>;
440  type QualifiedRule = ();
441  type Error = ParserError<'i>;
442
443  #[inline]
444  fn parse_prelude<'t>(
445    &mut self,
446    input: &mut Parser<'i, 't>,
447  ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
448    self.state = State::Body;
449    QualifiedRuleParser::parse_prelude(&mut self.nested(), input)
450  }
451
452  #[inline]
453  fn parse_block<'t>(
454    &mut self,
455    prelude: Self::Prelude,
456    start: &ParserState,
457    input: &mut Parser<'i, 't>,
458  ) -> Result<Self::QualifiedRule, ParseError<'i, Self::Error>> {
459    QualifiedRuleParser::parse_block(&mut self.nested(), prelude, start, input)
460  }
461}
462
463pub struct NestedRuleParser<'a, 'i, T: crate::traits::AtRuleParser<'i>> {
464  pub options: &'a ParserOptions<'i>,
465  pub at_rule_parser: &'a mut T,
466  declarations: DeclarationList<'i>,
467  important_declarations: DeclarationList<'i>,
468  rules: &'a mut CssRuleList<'i, T::AtRule>,
469  is_in_style_rule: bool,
470  allow_declarations: bool,
471  // Vize: rules in an `@scope` body are relative to its scoping root, not to an
472  // enclosing style rule, so they take no implicit `&`.
473  is_scope_body: bool,
474}
475
476impl<'a, 'b, 'i, T: crate::traits::AtRuleParser<'i>> NestedRuleParser<'a, 'i, T> {
477  pub fn parse_nested<'t>(
478    &mut self,
479    input: &mut Parser<'i, 't>,
480    is_style_rule: bool,
481  ) -> Result<(DeclarationBlock<'i>, CssRuleList<'i, T::AtRule>), ParseError<'i, ParserError<'i>>> {
482    self.parse_nested_body(input, is_style_rule, false)
483  }
484
485  fn parse_nested_body<'t>(
486    &mut self,
487    input: &mut Parser<'i, 't>,
488    is_style_rule: bool,
489    is_scope_body: bool,
490  ) -> Result<(DeclarationBlock<'i>, CssRuleList<'i, T::AtRule>), ParseError<'i, ParserError<'i>>> {
491    let mut rules = CssRuleList(vec![]);
492    let mut nested_parser = NestedRuleParser {
493      options: self.options,
494      at_rule_parser: self.at_rule_parser,
495      declarations: DeclarationList::new(),
496      important_declarations: DeclarationList::new(),
497      rules: &mut rules,
498      is_in_style_rule: self.is_in_style_rule || is_style_rule,
499      allow_declarations: self.allow_declarations || self.is_in_style_rule || is_style_rule,
500      // Conditional blocks inside a scope body stay relative to its root.
501      is_scope_body: is_scope_body || (self.is_scope_body && !is_style_rule),
502    };
503
504    let parse_declarations = nested_parser.parse_declarations();
505    let mut errors = Vec::new();
506    let mut iter = RuleBodyParser::new(input, &mut nested_parser);
507    while let Some(result) = iter.next() {
508      match result {
509        Ok(()) => {}
510        Err((e, _)) => {
511          if parse_declarations {
512            iter.parser.declarations.clear();
513            iter.parser.important_declarations.clear();
514            errors.push(e);
515          } else {
516            if iter.parser.options.error_recovery {
517              iter.parser.options.warn(e);
518              continue;
519            }
520            return Err(e);
521          }
522        }
523      }
524    }
525
526    if parse_declarations {
527      if !errors.is_empty() {
528        if self.options.error_recovery {
529          for err in errors {
530            self.options.warn(err);
531          }
532        } else {
533          return Err(errors.remove(0));
534        }
535      }
536    }
537
538    Ok((
539      DeclarationBlock {
540        declarations: nested_parser.declarations,
541        important_declarations: nested_parser.important_declarations,
542      },
543      rules,
544    ))
545  }
546
547  fn parse_style_block<'t>(
548    &mut self,
549    input: &mut Parser<'i, 't>,
550  ) -> Result<CssRuleList<'i, T::AtRule>, ParseError<'i, ParserError<'i>>> {
551    self.parse_block_body(input, false)
552  }
553
554  fn parse_block_body<'t>(
555    &mut self,
556    input: &mut Parser<'i, 't>,
557    is_scope_body: bool,
558  ) -> Result<CssRuleList<'i, T::AtRule>, ParseError<'i, ParserError<'i>>> {
559    let loc = input.current_source_location();
560    let loc = Location {
561      source_index: self.options.source_index,
562      line: loc.line,
563      column: loc.column,
564    };
565
566    // Declarations can be immediately within @media and @supports blocks that are nested within a parent style rule.
567    // These are wrapped in an (invisible) NestedDeclarationsRule.
568    let (declarations, mut rules) = self.parse_nested_body(input, false, is_scope_body)?;
569
570    if declarations.len() > 0 {
571      rules.0.insert(
572        0,
573        CssRule::NestedDeclarations(NestedDeclarationsRule { declarations, loc }),
574      )
575    }
576
577    Ok(rules)
578  }
579
580  fn loc(&self, start: &ParserState) -> Location {
581    let loc = start.source_location();
582    Location {
583      source_index: self.options.source_index,
584      line: loc.line,
585      column: loc.column,
586    }
587  }
588}
589
590impl<'a, 'b, 'i, T: crate::traits::AtRuleParser<'i>> AtRuleParser<'i> for NestedRuleParser<'a, 'i, T> {
591  type Prelude = AtRulePrelude<'i, T::Prelude>;
592  type AtRule = ();
593  type Error = ParserError<'i>;
594
595  fn parse_prelude<'t>(
596    &mut self,
597    name: CowRcStr<'i>,
598    input: &mut Parser<'i, 't>,
599  ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
600    let result = match_ignore_ascii_case! { &*name,
601      "media" => {
602        let media = MediaList::parse(input, &self.options)?;
603        AtRulePrelude::Media(media)
604      },
605      "supports" => {
606        let cond = SupportsCondition::parse(input, )?;
607        AtRulePrelude::Supports(cond)
608      },
609      "font-face" => {
610        AtRulePrelude::FontFace
611      },
612      // "font-feature-values" => {
613      //     if !cfg!(feature = "gecko") {
614      //         // Support for this rule is not fully implemented in Servo yet.
615      //         return Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
616      //     }
617      //     let family_names = parse_family_name_list(self.context, input)?;
618      //     Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::FontFeatureValues(family_names)))
619      // },
620      "font-feature-values" => {
621        let names = match Vec::<FamilyName>::parse(input) {
622          Ok(names) => names,
623          Err(e) => return Err(e)
624        };
625
626        AtRulePrelude::FontFeatureValues(names)
627      },
628      "font-palette-values" => {
629        let name = DashedIdent::parse(input)?;
630        AtRulePrelude::FontPaletteValues(name)
631      },
632      "counter-style" => {
633        let name = CustomIdent::parse(input)?;
634        AtRulePrelude::CounterStyle(name)
635      },
636      "viewport" | "-ms-viewport" => {
637        let prefix = if starts_with_ignore_ascii_case(&*name, "-ms") {
638          VendorPrefix::Ms
639        } else {
640          VendorPrefix::None
641        };
642        AtRulePrelude::Viewport(prefix)
643      },
644      "keyframes" | "-webkit-keyframes" | "-moz-keyframes" | "-o-keyframes" | "-ms-keyframes" => {
645        let prefix = if starts_with_ignore_ascii_case(&*name, "-webkit-") {
646          VendorPrefix::WebKit
647        } else if starts_with_ignore_ascii_case(&*name, "-moz-") {
648          VendorPrefix::Moz
649        } else if starts_with_ignore_ascii_case(&*name, "-o-") {
650          VendorPrefix::O
651        } else if starts_with_ignore_ascii_case(&*name, "-ms-") {
652          VendorPrefix::Ms
653        } else {
654          VendorPrefix::None
655        };
656
657        let name = input.try_parse(KeyframesName::parse)?;
658        AtRulePrelude::Keyframes(name, prefix)
659      },
660      "page" => {
661        let selectors = input.try_parse(|input| input.parse_comma_separated(PageSelector::parse)).unwrap_or_default();
662        AtRulePrelude::Page(selectors)
663      },
664      "-moz-document" => {
665        // Firefox only supports the url-prefix() function with no arguments as a legacy CSS hack.
666        // See https://css-tricks.com/snippets/css/css-hacks-targeting-firefox/
667        input.expect_function_matching("url-prefix")?;
668        input.parse_nested_block(|input| {
669          // Firefox also allows an empty string as an argument...
670          // https://github.com/mozilla/gecko-dev/blob/0077f2248712a1b45bf02f0f866449f663538164/servo/components/style/stylesheets/document_rule.rs#L303
671          let _ = input.try_parse(|input| -> Result<(), ParseError<'i, Self::Error>> {
672            let s = input.expect_string()?;
673            if !s.is_empty() {
674              return Err(input.new_custom_error(ParserError::InvalidValue))
675            }
676            Ok(())
677          });
678          input.expect_exhausted()?;
679          Ok(())
680        })?;
681
682        AtRulePrelude::MozDocument
683      },
684      "layer" => {
685        let names = match Vec::<LayerName>::parse(input) {
686          Ok(names) => names,
687          Err(ParseError { kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput), .. }) => Vec::new(),
688          Err(e) => return Err(e)
689        };
690        AtRulePrelude::Layer(names)
691      },
692      "container" => {
693        let name = input.try_parse(ContainerName::parse).ok();
694        match input.try_parse(|input| ContainerCondition::parse_with_options(input, &self.options)) {
695          Ok(condition) => AtRulePrelude::Container(name, Some(condition)),
696          Err(e) => {
697            if name.is_some() && input.is_exhausted() {
698              // name only, no condition - allowed by new syntax
699              AtRulePrelude::Container(name, None)
700            } else {
701              // condition parsing failed (e.g., empty brackets or invalid tokens)
702              return Err(e);
703            }
704          }
705        }
706      },
707      "starting-style" => {
708        AtRulePrelude::StartingStyle
709      },
710      "scope" => {
711        let selector_parser = SelectorParser {
712          is_nesting_allowed: true,
713          options: &self.options,
714        };
715
716        let scope_start = if input.try_parse(|input| input.expect_parenthesis_block()).is_ok() {
717          Some(input.parse_nested_block(|input| {
718            // https://drafts.csswg.org/css-cascade-6/#scoped-rules
719            // TODO: disallow pseudo elements?
720            SelectorList::parse_relative(&selector_parser, input, ParseErrorRecovery::IgnoreInvalidSelector, NestingRequirement::None)
721          })?)
722        } else {
723          None
724        };
725
726        let scope_end = if input.try_parse(|input| input.expect_ident_matching("to")).is_ok() {
727          input.expect_parenthesis_block()?;
728          Some(input.parse_nested_block(|input| {
729            SelectorList::parse_relative(&selector_parser, input, ParseErrorRecovery::IgnoreInvalidSelector, NestingRequirement::None)
730          })?)
731        } else {
732          None
733        };
734
735        AtRulePrelude::Scope(scope_start, scope_end)
736      },
737      "view-transition" => {
738        AtRulePrelude::ViewTransition
739      },
740      "nest" if self.is_in_style_rule => {
741        self.options.warn(input.new_custom_error(ParserError::DeprecatedNestRule));
742        let selector_parser = SelectorParser {
743          is_nesting_allowed: true,
744          options: &self.options,
745        };
746        let selectors = SelectorList::parse(&selector_parser, input, ParseErrorRecovery::DiscardList, NestingRequirement::Contained)?;
747        AtRulePrelude::Nest(selectors)
748      },
749
750      "value" if self.options.css_modules.is_some() => {
751        return Err(input.new_custom_error(ParserError::DeprecatedCssModulesValueRule));
752      },
753
754      "property" => {
755        let name = DashedIdent::parse(input)?;
756        return Ok(AtRulePrelude::Property(name))
757      },
758
759      "position-try" => {
760        let name = DashedIdent::parse(input)?;
761        return Ok(AtRulePrelude::PositionTry(name))
762      },
763
764      _ => parse_custom_at_rule_prelude(&name, input, self.options, self.at_rule_parser)?
765    };
766
767    if self.is_in_style_rule && !result.allowed_in_style_rule() {
768      return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone())));
769    }
770
771    Ok(result)
772  }
773
774  #[inline]
775  fn rule_without_block(
776    &mut self,
777    prelude: AtRulePrelude<'i, T::Prelude>,
778    start: &ParserState,
779  ) -> Result<Self::AtRule, ()> {
780    let loc = self.loc(start);
781    match prelude {
782      AtRulePrelude::Layer(names) => {
783        if self.is_in_style_rule || names.is_empty() {
784          return Err(());
785        }
786
787        self.rules.0.push(CssRule::LayerStatement(LayerStatementRule { names, loc }));
788        Ok(())
789      }
790      AtRulePrelude::Unknown(name, prelude) => {
791        self.rules.0.push(CssRule::Unknown(UnknownAtRule {
792          name,
793          prelude,
794          block: None,
795          loc,
796        }));
797        Ok(())
798      }
799      AtRulePrelude::Custom(prelude) => {
800        self.rules.0.push(parse_custom_at_rule_without_block(
801          prelude,
802          start,
803          self.options,
804          self.at_rule_parser,
805          self.is_in_style_rule,
806        )?);
807        Ok(())
808      }
809      _ => Err(()),
810    }
811  }
812
813  fn parse_block<'t>(
814    &mut self,
815    prelude: Self::Prelude,
816    start: &ParserState,
817    input: &mut Parser<'i, 't>,
818  ) -> Result<(), ParseError<'i, Self::Error>> {
819    let loc = self.loc(start);
820    match prelude {
821      AtRulePrelude::FontFace => {
822        let mut decl_parser = FontFaceDeclarationParser;
823        let mut parser = RuleBodyParser::new(input, &mut decl_parser);
824        let mut properties = vec![];
825        while let Some(decl) = parser.next() {
826          if let Ok(decl) = decl {
827            properties.push(decl);
828          }
829        }
830        self.rules.0.push(CssRule::FontFace(FontFaceRule { properties, loc }));
831        Ok(())
832      }
833      // AtRuleBlockPrelude::FontFeatureValues(family_names) => {
834      //     let context = ParserContext::new_with_rule_type(
835      //         self.context,
836      //         CssRuleType::FontFeatureValues,
837      //         self.namespaces,
838      //     );
839
840      //     Ok(CssRule::FontFeatureValues(Arc::new(self.shared_lock.wrap(
841      //         FontFeatureValuesRule::parse(
842      //             &context,
843      //             input,
844      //             family_names,
845      //             start.source_location(),
846      //         ),
847      //     ))))
848      // },
849      AtRulePrelude::FontPaletteValues(name) => {
850        let rule = FontPaletteValuesRule::parse(name, input, loc)?;
851        self.rules.0.push(CssRule::FontPaletteValues(rule));
852        Ok(())
853      }
854      AtRulePrelude::CounterStyle(name) => {
855        self.rules.0.push(CssRule::CounterStyle(CounterStyleRule {
856          name,
857          declarations: DeclarationBlock::parse(input, self.options)?,
858          loc,
859        }));
860        Ok(())
861      }
862      AtRulePrelude::Media(query) => {
863        let rules = self.parse_style_block(input)?;
864        self.rules.0.push(CssRule::Media(MediaRule { query, rules, loc }));
865        Ok(())
866      }
867      AtRulePrelude::Supports(condition) => {
868        let rules = self.parse_style_block(input)?;
869        self.rules.0.push(CssRule::Supports(SupportsRule { condition, rules, loc }));
870        Ok(())
871      }
872      AtRulePrelude::Container(name, condition) => {
873        let rules = self.parse_style_block(input)?;
874        self.rules.0.push(CssRule::Container(ContainerRule {
875          name,
876          condition,
877          rules,
878          loc,
879        }));
880        Ok(())
881      }
882      AtRulePrelude::Scope(scope_start, scope_end) => {
883        let rules = self.parse_block_body(input, true)?;
884        self.rules.0.push(CssRule::Scope(ScopeRule {
885          scope_start,
886          scope_end,
887          rules,
888          loc,
889        }));
890        Ok(())
891      }
892      AtRulePrelude::Viewport(vendor_prefix) => {
893        self.rules.0.push(CssRule::Viewport(ViewportRule {
894          vendor_prefix,
895          // TODO: parse viewport descriptors rather than properties
896          // https://drafts.csswg.org/css-device-adapt/#viewport-desc
897          declarations: DeclarationBlock::parse(input, self.options)?,
898          loc,
899        }));
900        Ok(())
901      }
902      AtRulePrelude::Keyframes(name, vendor_prefix) => {
903        let mut parser = KeyframeListParser;
904        let iter = RuleBodyParser::new(input, &mut parser);
905        self.rules.0.push(CssRule::Keyframes(KeyframesRule {
906          name,
907          keyframes: iter.filter_map(Result::ok).collect(),
908          vendor_prefix,
909          loc,
910        }));
911        Ok(())
912      }
913      AtRulePrelude::Page(selectors) => {
914        let rule = PageRule::parse(selectors, input, loc, self.options)?;
915        self.rules.0.push(CssRule::Page(rule));
916        Ok(())
917      }
918      AtRulePrelude::MozDocument => {
919        let rules = self.parse_style_block(input)?;
920        self.rules.0.push(CssRule::MozDocument(MozDocumentRule { rules, loc }));
921        Ok(())
922      }
923      AtRulePrelude::Layer(names) => {
924        let name = if names.is_empty() {
925          None
926        } else if names.len() == 1 {
927          names.into_iter().next()
928        } else {
929          return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid));
930        };
931
932        let rules = self.parse_style_block(input)?;
933        self.rules.0.push(CssRule::LayerBlock(LayerBlockRule { name, rules, loc }));
934        Ok(())
935      }
936      AtRulePrelude::Property(name) => {
937        self.rules.0.push(CssRule::Property(PropertyRule::parse(name, input, loc)?));
938        Ok(())
939      }
940      AtRulePrelude::PositionTry(name) => {
941        self.rules.0.push(CssRule::PositionTry(PositionTryRule {
942          name,
943          declarations: DeclarationBlock::parse(input, self.options)?,
944          loc,
945        }));
946        Ok(())
947      }
948      AtRulePrelude::Import(..)
949      | AtRulePrelude::Namespace(..)
950      | AtRulePrelude::CustomMedia(..)
951      | AtRulePrelude::Charset => {
952        // These rules don't have blocks.
953        Err(input.new_unexpected_token_error(Token::CurlyBracketBlock))
954      }
955      AtRulePrelude::StartingStyle => {
956        let rules = self.parse_style_block(input)?;
957        self.rules.0.push(CssRule::StartingStyle(StartingStyleRule { rules, loc }));
958        Ok(())
959      }
960      AtRulePrelude::ViewTransition => {
961        self
962          .rules
963          .0
964          .push(CssRule::ViewTransition(ViewTransitionRule::parse(input, loc)?));
965        Ok(())
966      }
967      AtRulePrelude::Nest(selectors) => {
968        let (declarations, rules) = self.parse_nested(input, true)?;
969        self.rules.0.push(CssRule::Nesting(NestingRule {
970          style: StyleRule {
971            selectors,
972            declarations,
973            vendor_prefix: VendorPrefix::empty(),
974            rules,
975            loc,
976          },
977          loc,
978        }));
979        Ok(())
980      }
981      AtRulePrelude::FontFeatureValues(family_names) => {
982        let rule = FontFeatureValuesRule::parse(family_names, input, loc, self.options)?;
983        self.rules.0.push(CssRule::FontFeatureValues(rule));
984        Ok(())
985      }
986      AtRulePrelude::Unknown(name, prelude) => {
987        self.rules.0.push(CssRule::Unknown(UnknownAtRule {
988          name,
989          prelude,
990          block: Some(TokenList::parse(input, &self.options, 0)?),
991          loc,
992        }));
993        Ok(())
994      }
995      AtRulePrelude::Custom(prelude) => {
996        self.rules.0.push(parse_custom_at_rule_body(
997          prelude,
998          input,
999          start,
1000          self.options,
1001          self.at_rule_parser,
1002          self.is_in_style_rule,
1003        )?);
1004        Ok(())
1005      }
1006    }
1007  }
1008}
1009
1010impl<'a, 'b, 'i, T: crate::traits::AtRuleParser<'i>> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'i, T> {
1011  type Prelude = SelectorList<'i>;
1012  type QualifiedRule = ();
1013  type Error = ParserError<'i>;
1014
1015  fn parse_prelude<'t>(
1016    &mut self,
1017    input: &mut Parser<'i, 't>,
1018  ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
1019    let selector_parser = SelectorParser {
1020      is_nesting_allowed: true,
1021      options: &self.options,
1022    };
1023    if self.is_scope_body {
1024      // Scoped rules accept relative selectors, but an implied scope anchor
1025      // must not become an explicit :scope with higher specificity. Parse
1026      // each list item independently so an authored :scope or & is untouched.
1027      let lists = input.parse_comma_separated(|input| {
1028        let state = input.state();
1029        let leading_combinator = matches!(input.next(), Ok(Token::Delim('>' | '+' | '~')));
1030        input.reset(&state);
1031        if leading_combinator {
1032          SelectorList::parse_relative(
1033            &selector_parser,
1034            input,
1035            ParseErrorRecovery::DiscardList,
1036            NestingRequirement::Implicit,
1037          )
1038        } else {
1039          SelectorList::parse(
1040            &selector_parser,
1041            input,
1042            ParseErrorRecovery::DiscardList,
1043            NestingRequirement::None,
1044          )
1045        }
1046      })?;
1047      Ok(SelectorList::from_vec(
1048        lists.into_iter().flat_map(|list| list.0).collect(),
1049      ))
1050    } else if self.is_in_style_rule {
1051      SelectorList::parse_relative(
1052        &selector_parser,
1053        input,
1054        ParseErrorRecovery::DiscardList,
1055        NestingRequirement::Implicit,
1056      )
1057    } else {
1058      SelectorList::parse(
1059        &selector_parser,
1060        input,
1061        ParseErrorRecovery::DiscardList,
1062        NestingRequirement::None,
1063      )
1064    }
1065  }
1066
1067  fn parse_block<'t>(
1068    &mut self,
1069    selectors: Self::Prelude,
1070    start: &ParserState,
1071    input: &mut Parser<'i, 't>,
1072  ) -> Result<(), ParseError<'i, Self::Error>> {
1073    let loc = self.loc(start);
1074    let (declarations, rules) = self.parse_nested(input, true)?;
1075    self.rules.0.push(CssRule::Style(StyleRule {
1076      selectors,
1077      vendor_prefix: VendorPrefix::empty(),
1078      declarations,
1079      rules,
1080      loc,
1081    }));
1082    Ok(())
1083  }
1084}
1085
1086/// Parse a declaration within {} block: `color: blue`
1087impl<'a, 'i, T: crate::traits::AtRuleParser<'i>> cssparser::DeclarationParser<'i> for NestedRuleParser<'a, 'i, T> {
1088  type Declaration = ();
1089  type Error = ParserError<'i>;
1090
1091  fn parse_value<'t>(
1092    &mut self,
1093    name: CowRcStr<'i>,
1094    input: &mut cssparser::Parser<'i, 't>,
1095    _declaration_start: &ParserState,
1096  ) -> Result<Self::Declaration, cssparser::ParseError<'i, Self::Error>> {
1097    if self.rules.0.is_empty() {
1098      parse_declaration(
1099        name,
1100        input,
1101        &mut self.declarations,
1102        &mut self.important_declarations,
1103        &self.options,
1104      )
1105    } else if let Some(CssRule::NestedDeclarations(last)) = self.rules.0.last_mut() {
1106      parse_declaration(
1107        name,
1108        input,
1109        &mut last.declarations.declarations,
1110        &mut last.declarations.important_declarations,
1111        &self.options,
1112      )
1113    } else {
1114      let loc = self.loc(&input.state());
1115      let mut nested = NestedDeclarationsRule {
1116        declarations: DeclarationBlock::new(),
1117        loc,
1118      };
1119
1120      parse_declaration(
1121        name,
1122        input,
1123        &mut nested.declarations.declarations,
1124        &mut nested.declarations.important_declarations,
1125        &self.options,
1126      )?;
1127
1128      self.rules.0.push(CssRule::NestedDeclarations(nested));
1129      Ok(())
1130    }
1131  }
1132}
1133
1134impl<'a, 'b, 'i, T: crate::traits::AtRuleParser<'i>> RuleBodyItemParser<'i, (), ParserError<'i>>
1135  for NestedRuleParser<'a, 'i, T>
1136{
1137  fn parse_qualified(&self) -> bool {
1138    true
1139  }
1140
1141  fn parse_declarations(&self) -> bool {
1142    self.allow_declarations
1143  }
1144}
1145
1146fn parse_custom_at_rule_prelude<'i, 't, T: crate::traits::AtRuleParser<'i>>(
1147  name: &CowRcStr<'i>,
1148  input: &mut Parser<'i, 't>,
1149  options: &ParserOptions<'i>,
1150  at_rule_parser: &mut T,
1151) -> Result<AtRulePrelude<'i, T::Prelude>, ParseError<'i, ParserError<'i>>> {
1152  match at_rule_parser.parse_prelude(name.clone(), input, options) {
1153    Ok(prelude) => return Ok(AtRulePrelude::Custom(prelude)),
1154    Err(ParseError {
1155      kind: ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(..)),
1156      ..
1157    }) => {}
1158    Err(err) => {
1159      return Err(match &err.kind {
1160        ParseErrorKind::Basic(kind) => ParseError {
1161          kind: ParseErrorKind::Basic(kind.clone()),
1162          location: err.location,
1163        },
1164        _ => input.new_custom_error(ParserError::AtRulePreludeInvalid),
1165      })
1166    }
1167  }
1168
1169  options.warn(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone())));
1170  input.skip_whitespace();
1171  let tokens = TokenList::parse(input, &options, 0)?;
1172  Ok(AtRulePrelude::Unknown(name.into(), tokens))
1173}
1174
1175fn parse_custom_at_rule_body<'i, 't, T: crate::traits::AtRuleParser<'i>>(
1176  prelude: T::Prelude,
1177  input: &mut Parser<'i, 't>,
1178  start: &ParserState,
1179  options: &ParserOptions<'i>,
1180  at_rule_parser: &mut T,
1181  is_nested: bool,
1182) -> Result<CssRule<'i, T::AtRule>, ParseError<'i, ParserError<'i>>> {
1183  at_rule_parser
1184    .parse_block(prelude, start, input, options, is_nested)
1185    .map(|prelude| CssRule::Custom(prelude))
1186    .map_err(|err| match &err.kind {
1187      ParseErrorKind::Basic(kind) => ParseError {
1188        kind: ParseErrorKind::Basic(kind.clone()),
1189        location: err.location,
1190      },
1191      _ => input.new_error(BasicParseErrorKind::AtRuleBodyInvalid),
1192    })
1193}
1194
1195fn parse_custom_at_rule_without_block<'i, 't, T: crate::traits::AtRuleParser<'i>>(
1196  prelude: T::Prelude,
1197  start: &ParserState,
1198  options: &ParserOptions<'i>,
1199  at_rule_parser: &mut T,
1200  is_nested: bool,
1201) -> Result<CssRule<'i, T::AtRule>, ()> {
1202  at_rule_parser
1203    .rule_without_block(prelude, start, options, is_nested)
1204    .map(|prelude| CssRule::Custom(prelude))
1205}
1206
1207pub fn parse_rule_list<'a, 'i, 't, T: crate::traits::AtRuleParser<'i>>(
1208  input: &mut Parser<'i, 't>,
1209  options: &'a ParserOptions<'i>,
1210  at_rule_parser: &mut T,
1211) -> Result<CssRuleList<'i, T::AtRule>, ParseError<'i, ParserError<'i>>> {
1212  let mut parser = NestedRuleParser {
1213    options,
1214    at_rule_parser,
1215    declarations: DeclarationList::new(),
1216    important_declarations: DeclarationList::new(),
1217    rules: &mut CssRuleList(Vec::new()),
1218    is_in_style_rule: false,
1219    allow_declarations: false,
1220    is_scope_body: false,
1221  };
1222
1223  let (_, rules) = parser.parse_nested(input, false)?;
1224  Ok(rules)
1225}
1226
1227pub fn parse_style_block<'a, 'i, 't, T: crate::traits::AtRuleParser<'i>>(
1228  input: &mut Parser<'i, 't>,
1229  options: &'a ParserOptions<'i>,
1230  at_rule_parser: &mut T,
1231  is_nested: bool,
1232) -> Result<CssRuleList<'i, T::AtRule>, ParseError<'i, ParserError<'i>>> {
1233  let mut parser = NestedRuleParser {
1234    options,
1235    at_rule_parser,
1236    declarations: DeclarationList::new(),
1237    important_declarations: DeclarationList::new(),
1238    rules: &mut CssRuleList(Vec::new()),
1239    is_in_style_rule: is_nested,
1240    allow_declarations: true,
1241    is_scope_body: false,
1242  };
1243
1244  parser.parse_style_block(input)
1245}
1246
1247#[inline]
1248pub fn starts_with_ignore_ascii_case(string: &str, prefix: &str) -> bool {
1249  string.len() >= prefix.len() && string.as_bytes()[0..prefix.len()].eq_ignore_ascii_case(prefix.as_bytes())
1250}