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    }
193  }
194}
195
196/// A rule prelude for at-rule with block.
197#[derive(Debug)]
198#[allow(dead_code)]
199pub enum AtRulePrelude<'i, T> {
200  /// A @font-face rule prelude.
201  FontFace,
202  /// A @font-feature-values rule prelude, with its FamilyName list.
203  FontFeatureValues(Vec<FamilyName<'i>>),
204  /// A @font-palette-values rule prelude, with its name.
205  FontPaletteValues(DashedIdent<'i>),
206  /// A @counter-style rule prelude, with its counter style name.
207  CounterStyle(CustomIdent<'i>),
208  /// A @media rule prelude, with its media queries.
209  Media(MediaList<'i>),
210  /// A @custom-media rule prelude.
211  CustomMedia(DashedIdent<'i>, MediaList<'i>),
212  /// An @supports rule, with its conditional
213  Supports(SupportsCondition<'i>),
214  /// A @viewport rule prelude.
215  Viewport(VendorPrefix),
216  /// A @keyframes rule, with its animation name and vendor prefix if exists.
217  Keyframes(KeyframesName<'i>, VendorPrefix),
218  /// A @page rule prelude.
219  Page(Vec<PageSelector<'i>>),
220  /// A @-moz-document rule.
221  MozDocument,
222  /// A @import rule prelude.
223  Import(
224    CowRcStr<'i>,
225    MediaList<'i>,
226    Option<SupportsCondition<'i>>,
227    Option<Option<LayerName<'i>>>,
228  ),
229  /// A @namespace rule prelude.
230  Namespace(Option<CowRcStr<'i>>, CowRcStr<'i>),
231  /// A @charset rule prelude.
232  Charset,
233  /// A @nest prelude.
234  Nest(SelectorList<'i>),
235  /// An @layer prelude.
236  Layer(Vec<LayerName<'i>>),
237  /// An @property prelude.
238  Property(DashedIdent<'i>),
239  /// An @position-try prelude.
240  PositionTry(DashedIdent<'i>),
241  /// A @container prelude.
242  /// Spec: https://drafts.csswg.org/css-conditional-5/#container-rule
243  /// @container [ <container-name>? <container-query>? ]!
244  Container(Option<ContainerName<'i>>, Option<ContainerCondition<'i>>),
245  /// A @starting-style prelude.
246  StartingStyle,
247  /// A @scope rule prelude.
248  Scope(Option<SelectorList<'i>>, Option<SelectorList<'i>>),
249  /// A @view-transition rule prelude.
250  ViewTransition,
251  /// An unknown prelude.
252  Unknown(CowArcStr<'i>, TokenList<'i>),
253  /// A custom prelude.
254  Custom(T),
255}
256
257impl<'i, T> AtRulePrelude<'i, T> {
258  // https://drafts.csswg.org/css-nesting/#conditionals
259  //     In addition to nested style rules, this specification allows nested group rules inside
260  //     of style rules: any at-rule whose body contains style rules can be nested inside of a
261  //     style rule as well.
262  fn allowed_in_style_rule(&self) -> bool {
263    match *self {
264      Self::Media(..)
265      | Self::Supports(..)
266      | Self::Container(..)
267      | Self::MozDocument
268      | Self::Layer(..)
269      | Self::StartingStyle
270      | Self::Scope(..)
271      | Self::Nest(..)
272      | Self::Unknown(..)
273      | Self::Custom(..) => true,
274
275      Self::Namespace(..)
276      | Self::FontFace
277      | Self::FontFeatureValues(..)
278      | Self::FontPaletteValues(..)
279      | Self::CounterStyle(..)
280      | Self::Keyframes(..)
281      | Self::Page(..)
282      | Self::Property(..)
283      | Self::PositionTry(..)
284      | Self::Import(..)
285      | Self::CustomMedia(..)
286      | Self::Viewport(..)
287      | Self::Charset
288      | Self::ViewTransition => false,
289    }
290  }
291}
292
293impl<'a, 'i, T: crate::traits::AtRuleParser<'i>> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i, T> {
294  type Prelude = AtRulePrelude<'i, T::Prelude>;
295  type AtRule = ();
296  type Error = ParserError<'i>;
297
298  fn parse_prelude<'t>(
299    &mut self,
300    name: CowRcStr<'i>,
301    input: &mut Parser<'i, 't>,
302  ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
303    match_ignore_ascii_case! { &*name,
304      "import" => {
305        if self.state > State::Imports {
306          return Err(input.new_custom_error(ParserError::UnexpectedImportRule))
307        }
308
309        let url_string = input.expect_url_or_string()?.clone();
310
311        let layer = if input.try_parse(|input| input.expect_ident_matching("layer")).is_ok() {
312          Some(None)
313        } else if input.try_parse(|input| input.expect_function_matching("layer")).is_ok() {
314          let name = input.parse_nested_block(LayerName::parse).map(|name| Some(name))?;
315          Some(name)
316        } else {
317          None
318        };
319
320        let supports = if input.try_parse(|input| input.expect_function_matching("supports")).is_ok() {
321          Some(input.parse_nested_block(|input| {
322            input.try_parse(SupportsCondition::parse).or_else(|_| SupportsCondition::parse_declaration(input))
323          })?)
324        } else {
325          None
326        };
327        let media = MediaList::parse(input, &self.options)?;
328        return Ok(AtRulePrelude::Import(url_string, media, supports, layer));
329      },
330      "namespace" => {
331        if self.state > State::Namespaces {
332          return Err(input.new_custom_error(ParserError::UnexpectedNamespaceRule))
333        }
334
335        let prefix = input.try_parse(|input| input.expect_ident_cloned()).ok();
336        let namespace = input.expect_url_or_string()?;
337        let prelude = AtRulePrelude::Namespace(prefix, namespace);
338        return Ok(prelude);
339      },
340      "charset" => {
341        // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet.
342        // Anything left is technically invalid, however, users often concatenate CSS files
343        // together, so we are more lenient and simply ignore @charset rules in the middle of a file.
344        input.expect_string()?;
345        return Ok(AtRulePrelude::Charset)
346      },
347      "custom-media" if self.options.flags.contains(ParserFlags::CUSTOM_MEDIA) => {
348        let name = DashedIdent::parse(input)?;
349        let media = MediaList::parse(input, &self.options)?;
350        return Ok(AtRulePrelude::CustomMedia(name, media))
351      },
352      _ => {}
353    }
354
355    AtRuleParser::parse_prelude(&mut self.nested(), name, input)
356  }
357
358  #[inline]
359  fn parse_block<'t>(
360    &mut self,
361    prelude: Self::Prelude,
362    start: &ParserState,
363    input: &mut Parser<'i, 't>,
364  ) -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
365    self.state = State::Body;
366    AtRuleParser::parse_block(&mut self.nested(), prelude, start, input)
367  }
368
369  #[inline]
370  fn rule_without_block(
371    &mut self,
372    prelude: AtRulePrelude<'i, T::Prelude>,
373    start: &ParserState,
374  ) -> Result<Self::AtRule, ()> {
375    let loc = start.source_location();
376    let loc = Location {
377      source_index: self.options.source_index,
378      line: loc.line,
379      column: loc.column,
380    };
381
382    match prelude {
383      AtRulePrelude::Import(url, media, supports, layer) => {
384        self.state = State::Imports;
385        self.rules.0.push(CssRule::Import(ImportRule {
386          url: url.into(),
387          layer,
388          supports,
389          media,
390          loc,
391        }));
392        Ok(())
393      }
394      AtRulePrelude::Namespace(prefix, url) => {
395        self.state = State::Namespaces;
396
397        self.rules.0.push(CssRule::Namespace(NamespaceRule {
398          prefix: prefix.map(|x| x.into()),
399          url: url.into(),
400          loc,
401        }));
402        Ok(())
403      }
404      AtRulePrelude::CustomMedia(name, query) => {
405        self.state = State::Body;
406        self.rules.0.push(CssRule::CustomMedia(CustomMediaRule { name, query, loc }));
407        Ok(())
408      }
409      AtRulePrelude::Layer(_) => {
410        // @layer statements are allowed before @import rules, but cannot be interleaved.
411        if self.state <= State::Layers {
412          self.state = State::Layers;
413        } else {
414          self.state = State::Body;
415        }
416        AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)
417      }
418      AtRulePrelude::Charset => Ok(()),
419      AtRulePrelude::Unknown(name, prelude) => {
420        self.rules.0.push(CssRule::Unknown(UnknownAtRule {
421          name,
422          prelude,
423          block: None,
424          loc,
425        }));
426        Ok(())
427      }
428      AtRulePrelude::Custom(_) => {
429        self.state = State::Body;
430        AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)
431      }
432      _ => Err(()),
433    }
434  }
435}
436
437impl<'a, 'i, T: crate::traits::AtRuleParser<'i>> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, 'i, T> {
438  type Prelude = SelectorList<'i>;
439  type QualifiedRule = ();
440  type Error = ParserError<'i>;
441
442  #[inline]
443  fn parse_prelude<'t>(
444    &mut self,
445    input: &mut Parser<'i, 't>,
446  ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
447    self.state = State::Body;
448    QualifiedRuleParser::parse_prelude(&mut self.nested(), input)
449  }
450
451  #[inline]
452  fn parse_block<'t>(
453    &mut self,
454    prelude: Self::Prelude,
455    start: &ParserState,
456    input: &mut Parser<'i, 't>,
457  ) -> Result<Self::QualifiedRule, ParseError<'i, Self::Error>> {
458    QualifiedRuleParser::parse_block(&mut self.nested(), prelude, start, input)
459  }
460}
461
462pub struct NestedRuleParser<'a, 'i, T: crate::traits::AtRuleParser<'i>> {
463  pub options: &'a ParserOptions<'i>,
464  pub at_rule_parser: &'a mut T,
465  declarations: DeclarationList<'i>,
466  important_declarations: DeclarationList<'i>,
467  rules: &'a mut CssRuleList<'i, T::AtRule>,
468  is_in_style_rule: bool,
469  allow_declarations: bool,
470}
471
472impl<'a, 'b, 'i, T: crate::traits::AtRuleParser<'i>> NestedRuleParser<'a, 'i, T> {
473  pub fn parse_nested<'t>(
474    &mut self,
475    input: &mut Parser<'i, 't>,
476    is_style_rule: bool,
477  ) -> Result<(DeclarationBlock<'i>, CssRuleList<'i, T::AtRule>), ParseError<'i, ParserError<'i>>> {
478    let mut rules = CssRuleList(vec![]);
479    let mut nested_parser = NestedRuleParser {
480      options: self.options,
481      at_rule_parser: self.at_rule_parser,
482      declarations: DeclarationList::new(),
483      important_declarations: DeclarationList::new(),
484      rules: &mut rules,
485      is_in_style_rule: self.is_in_style_rule || is_style_rule,
486      allow_declarations: self.allow_declarations || self.is_in_style_rule || is_style_rule,
487    };
488
489    let parse_declarations = nested_parser.parse_declarations();
490    let mut errors = Vec::new();
491    let mut iter = RuleBodyParser::new(input, &mut nested_parser);
492    while let Some(result) = iter.next() {
493      match result {
494        Ok(()) => {}
495        Err((e, _)) => {
496          if parse_declarations {
497            iter.parser.declarations.clear();
498            iter.parser.important_declarations.clear();
499            errors.push(e);
500          } else {
501            if iter.parser.options.error_recovery {
502              iter.parser.options.warn(e);
503              continue;
504            }
505            return Err(e);
506          }
507        }
508      }
509    }
510
511    if parse_declarations {
512      if !errors.is_empty() {
513        if self.options.error_recovery {
514          for err in errors {
515            self.options.warn(err);
516          }
517        } else {
518          return Err(errors.remove(0));
519        }
520      }
521    }
522
523    Ok((
524      DeclarationBlock {
525        declarations: nested_parser.declarations,
526        important_declarations: nested_parser.important_declarations,
527      },
528      rules,
529    ))
530  }
531
532  fn parse_style_block<'t>(
533    &mut self,
534    input: &mut Parser<'i, 't>,
535  ) -> Result<CssRuleList<'i, T::AtRule>, ParseError<'i, ParserError<'i>>> {
536    let loc = input.current_source_location();
537    let loc = Location {
538      source_index: self.options.source_index,
539      line: loc.line,
540      column: loc.column,
541    };
542
543    // Declarations can be immediately within @media and @supports blocks that are nested within a parent style rule.
544    // These are wrapped in an (invisible) NestedDeclarationsRule.
545    let (declarations, mut rules) = self.parse_nested(input, false)?;
546
547    if declarations.len() > 0 {
548      rules.0.insert(
549        0,
550        CssRule::NestedDeclarations(NestedDeclarationsRule { declarations, loc }),
551      )
552    }
553
554    Ok(rules)
555  }
556
557  fn loc(&self, start: &ParserState) -> Location {
558    let loc = start.source_location();
559    Location {
560      source_index: self.options.source_index,
561      line: loc.line,
562      column: loc.column,
563    }
564  }
565}
566
567impl<'a, 'b, 'i, T: crate::traits::AtRuleParser<'i>> AtRuleParser<'i> for NestedRuleParser<'a, 'i, T> {
568  type Prelude = AtRulePrelude<'i, T::Prelude>;
569  type AtRule = ();
570  type Error = ParserError<'i>;
571
572  fn parse_prelude<'t>(
573    &mut self,
574    name: CowRcStr<'i>,
575    input: &mut Parser<'i, 't>,
576  ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
577    let result = match_ignore_ascii_case! { &*name,
578      "media" => {
579        let media = MediaList::parse(input, &self.options)?;
580        AtRulePrelude::Media(media)
581      },
582      "supports" => {
583        let cond = SupportsCondition::parse(input, )?;
584        AtRulePrelude::Supports(cond)
585      },
586      "font-face" => {
587        AtRulePrelude::FontFace
588      },
589      // "font-feature-values" => {
590      //     if !cfg!(feature = "gecko") {
591      //         // Support for this rule is not fully implemented in Servo yet.
592      //         return Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
593      //     }
594      //     let family_names = parse_family_name_list(self.context, input)?;
595      //     Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::FontFeatureValues(family_names)))
596      // },
597      "font-feature-values" => {
598        let names = match Vec::<FamilyName>::parse(input) {
599          Ok(names) => names,
600          Err(e) => return Err(e)
601        };
602
603        AtRulePrelude::FontFeatureValues(names)
604      },
605      "font-palette-values" => {
606        let name = DashedIdent::parse(input)?;
607        AtRulePrelude::FontPaletteValues(name)
608      },
609      "counter-style" => {
610        let name = CustomIdent::parse(input)?;
611        AtRulePrelude::CounterStyle(name)
612      },
613      "viewport" | "-ms-viewport" => {
614        let prefix = if starts_with_ignore_ascii_case(&*name, "-ms") {
615          VendorPrefix::Ms
616        } else {
617          VendorPrefix::None
618        };
619        AtRulePrelude::Viewport(prefix)
620      },
621      "keyframes" | "-webkit-keyframes" | "-moz-keyframes" | "-o-keyframes" | "-ms-keyframes" => {
622        let prefix = if starts_with_ignore_ascii_case(&*name, "-webkit-") {
623          VendorPrefix::WebKit
624        } else if starts_with_ignore_ascii_case(&*name, "-moz-") {
625          VendorPrefix::Moz
626        } else if starts_with_ignore_ascii_case(&*name, "-o-") {
627          VendorPrefix::O
628        } else if starts_with_ignore_ascii_case(&*name, "-ms-") {
629          VendorPrefix::Ms
630        } else {
631          VendorPrefix::None
632        };
633
634        let name = input.try_parse(KeyframesName::parse)?;
635        AtRulePrelude::Keyframes(name, prefix)
636      },
637      "page" => {
638        let selectors = input.try_parse(|input| input.parse_comma_separated(PageSelector::parse)).unwrap_or_default();
639        AtRulePrelude::Page(selectors)
640      },
641      "-moz-document" => {
642        // Firefox only supports the url-prefix() function with no arguments as a legacy CSS hack.
643        // See https://css-tricks.com/snippets/css/css-hacks-targeting-firefox/
644        input.expect_function_matching("url-prefix")?;
645        input.parse_nested_block(|input| {
646          // Firefox also allows an empty string as an argument...
647          // https://github.com/mozilla/gecko-dev/blob/0077f2248712a1b45bf02f0f866449f663538164/servo/components/style/stylesheets/document_rule.rs#L303
648          let _ = input.try_parse(|input| -> Result<(), ParseError<'i, Self::Error>> {
649            let s = input.expect_string()?;
650            if !s.is_empty() {
651              return Err(input.new_custom_error(ParserError::InvalidValue))
652            }
653            Ok(())
654          });
655          input.expect_exhausted()?;
656          Ok(())
657        })?;
658
659        AtRulePrelude::MozDocument
660      },
661      "layer" => {
662        let names = match Vec::<LayerName>::parse(input) {
663          Ok(names) => names,
664          Err(ParseError { kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput), .. }) => Vec::new(),
665          Err(e) => return Err(e)
666        };
667        AtRulePrelude::Layer(names)
668      },
669      "container" => {
670        let name = input.try_parse(ContainerName::parse).ok();
671        match input.try_parse(|input| ContainerCondition::parse_with_options(input, &self.options)) {
672          Ok(condition) => AtRulePrelude::Container(name, Some(condition)),
673          Err(e) => {
674            if name.is_some() && input.is_exhausted() {
675              // name only, no condition - allowed by new syntax
676              AtRulePrelude::Container(name, None)
677            } else {
678              // condition parsing failed (e.g., empty brackets or invalid tokens)
679              return Err(e);
680            }
681          }
682        }
683      },
684      "starting-style" => {
685        AtRulePrelude::StartingStyle
686      },
687      "scope" => {
688        let selector_parser = SelectorParser {
689          is_nesting_allowed: true,
690          options: &self.options,
691        };
692
693        let scope_start = if input.try_parse(|input| input.expect_parenthesis_block()).is_ok() {
694          Some(input.parse_nested_block(|input| {
695            // https://drafts.csswg.org/css-cascade-6/#scoped-rules
696            // TODO: disallow pseudo elements?
697            SelectorList::parse_relative(&selector_parser, input, ParseErrorRecovery::IgnoreInvalidSelector, NestingRequirement::None)
698          })?)
699        } else {
700          None
701        };
702
703        let scope_end = if input.try_parse(|input| input.expect_ident_matching("to")).is_ok() {
704          input.expect_parenthesis_block()?;
705          Some(input.parse_nested_block(|input| {
706            SelectorList::parse_relative(&selector_parser, input, ParseErrorRecovery::IgnoreInvalidSelector, NestingRequirement::None)
707          })?)
708        } else {
709          None
710        };
711
712        AtRulePrelude::Scope(scope_start, scope_end)
713      },
714      "view-transition" => {
715        AtRulePrelude::ViewTransition
716      },
717      "nest" if self.is_in_style_rule => {
718        self.options.warn(input.new_custom_error(ParserError::DeprecatedNestRule));
719        let selector_parser = SelectorParser {
720          is_nesting_allowed: true,
721          options: &self.options,
722        };
723        let selectors = SelectorList::parse(&selector_parser, input, ParseErrorRecovery::DiscardList, NestingRequirement::Contained)?;
724        AtRulePrelude::Nest(selectors)
725      },
726
727      "value" if self.options.css_modules.is_some() => {
728        return Err(input.new_custom_error(ParserError::DeprecatedCssModulesValueRule));
729      },
730
731      "property" => {
732        let name = DashedIdent::parse(input)?;
733        return Ok(AtRulePrelude::Property(name))
734      },
735
736      "position-try" => {
737        let name = DashedIdent::parse(input)?;
738        return Ok(AtRulePrelude::PositionTry(name))
739      },
740
741      _ => parse_custom_at_rule_prelude(&name, input, self.options, self.at_rule_parser)?
742    };
743
744    if self.is_in_style_rule && !result.allowed_in_style_rule() {
745      return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone())));
746    }
747
748    Ok(result)
749  }
750
751  #[inline]
752  fn rule_without_block(
753    &mut self,
754    prelude: AtRulePrelude<'i, T::Prelude>,
755    start: &ParserState,
756  ) -> Result<Self::AtRule, ()> {
757    let loc = self.loc(start);
758    match prelude {
759      AtRulePrelude::Layer(names) => {
760        if self.is_in_style_rule || names.is_empty() {
761          return Err(());
762        }
763
764        self.rules.0.push(CssRule::LayerStatement(LayerStatementRule { names, loc }));
765        Ok(())
766      }
767      AtRulePrelude::Unknown(name, prelude) => {
768        self.rules.0.push(CssRule::Unknown(UnknownAtRule {
769          name,
770          prelude,
771          block: None,
772          loc,
773        }));
774        Ok(())
775      }
776      AtRulePrelude::Custom(prelude) => {
777        self.rules.0.push(parse_custom_at_rule_without_block(
778          prelude,
779          start,
780          self.options,
781          self.at_rule_parser,
782          self.is_in_style_rule,
783        )?);
784        Ok(())
785      }
786      _ => Err(()),
787    }
788  }
789
790  fn parse_block<'t>(
791    &mut self,
792    prelude: Self::Prelude,
793    start: &ParserState,
794    input: &mut Parser<'i, 't>,
795  ) -> Result<(), ParseError<'i, Self::Error>> {
796    let loc = self.loc(start);
797    match prelude {
798      AtRulePrelude::FontFace => {
799        let mut decl_parser = FontFaceDeclarationParser;
800        let mut parser = RuleBodyParser::new(input, &mut decl_parser);
801        let mut properties = vec![];
802        while let Some(decl) = parser.next() {
803          if let Ok(decl) = decl {
804            properties.push(decl);
805          }
806        }
807        self.rules.0.push(CssRule::FontFace(FontFaceRule { properties, loc }));
808        Ok(())
809      }
810      // AtRuleBlockPrelude::FontFeatureValues(family_names) => {
811      //     let context = ParserContext::new_with_rule_type(
812      //         self.context,
813      //         CssRuleType::FontFeatureValues,
814      //         self.namespaces,
815      //     );
816
817      //     Ok(CssRule::FontFeatureValues(Arc::new(self.shared_lock.wrap(
818      //         FontFeatureValuesRule::parse(
819      //             &context,
820      //             input,
821      //             family_names,
822      //             start.source_location(),
823      //         ),
824      //     ))))
825      // },
826      AtRulePrelude::FontPaletteValues(name) => {
827        let rule = FontPaletteValuesRule::parse(name, input, loc)?;
828        self.rules.0.push(CssRule::FontPaletteValues(rule));
829        Ok(())
830      }
831      AtRulePrelude::CounterStyle(name) => {
832        self.rules.0.push(CssRule::CounterStyle(CounterStyleRule {
833          name,
834          declarations: DeclarationBlock::parse(input, self.options)?,
835          loc,
836        }));
837        Ok(())
838      }
839      AtRulePrelude::Media(query) => {
840        let rules = self.parse_style_block(input)?;
841        self.rules.0.push(CssRule::Media(MediaRule { query, rules, loc }));
842        Ok(())
843      }
844      AtRulePrelude::Supports(condition) => {
845        let rules = self.parse_style_block(input)?;
846        self.rules.0.push(CssRule::Supports(SupportsRule { condition, rules, loc }));
847        Ok(())
848      }
849      AtRulePrelude::Container(name, condition) => {
850        let rules = self.parse_style_block(input)?;
851        self.rules.0.push(CssRule::Container(ContainerRule {
852          name,
853          condition,
854          rules,
855          loc,
856        }));
857        Ok(())
858      }
859      AtRulePrelude::Scope(scope_start, scope_end) => {
860        let rules = self.parse_style_block(input)?;
861        self.rules.0.push(CssRule::Scope(ScopeRule {
862          scope_start,
863          scope_end,
864          rules,
865          loc,
866        }));
867        Ok(())
868      }
869      AtRulePrelude::Viewport(vendor_prefix) => {
870        self.rules.0.push(CssRule::Viewport(ViewportRule {
871          vendor_prefix,
872          // TODO: parse viewport descriptors rather than properties
873          // https://drafts.csswg.org/css-device-adapt/#viewport-desc
874          declarations: DeclarationBlock::parse(input, self.options)?,
875          loc,
876        }));
877        Ok(())
878      }
879      AtRulePrelude::Keyframes(name, vendor_prefix) => {
880        let mut parser = KeyframeListParser;
881        let iter = RuleBodyParser::new(input, &mut parser);
882        self.rules.0.push(CssRule::Keyframes(KeyframesRule {
883          name,
884          keyframes: iter.filter_map(Result::ok).collect(),
885          vendor_prefix,
886          loc,
887        }));
888        Ok(())
889      }
890      AtRulePrelude::Page(selectors) => {
891        let rule = PageRule::parse(selectors, input, loc, self.options)?;
892        self.rules.0.push(CssRule::Page(rule));
893        Ok(())
894      }
895      AtRulePrelude::MozDocument => {
896        let rules = self.parse_style_block(input)?;
897        self.rules.0.push(CssRule::MozDocument(MozDocumentRule { rules, loc }));
898        Ok(())
899      }
900      AtRulePrelude::Layer(names) => {
901        let name = if names.is_empty() {
902          None
903        } else if names.len() == 1 {
904          names.into_iter().next()
905        } else {
906          return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid));
907        };
908
909        let rules = self.parse_style_block(input)?;
910        self.rules.0.push(CssRule::LayerBlock(LayerBlockRule { name, rules, loc }));
911        Ok(())
912      }
913      AtRulePrelude::Property(name) => {
914        self.rules.0.push(CssRule::Property(PropertyRule::parse(name, input, loc)?));
915        Ok(())
916      }
917      AtRulePrelude::PositionTry(name) => {
918        self.rules.0.push(CssRule::PositionTry(PositionTryRule {
919          name,
920          declarations: DeclarationBlock::parse(input, self.options)?,
921          loc,
922        }));
923        Ok(())
924      }
925      AtRulePrelude::Import(..)
926      | AtRulePrelude::Namespace(..)
927      | AtRulePrelude::CustomMedia(..)
928      | AtRulePrelude::Charset => {
929        // These rules don't have blocks.
930        Err(input.new_unexpected_token_error(Token::CurlyBracketBlock))
931      }
932      AtRulePrelude::StartingStyle => {
933        let rules = self.parse_style_block(input)?;
934        self.rules.0.push(CssRule::StartingStyle(StartingStyleRule { rules, loc }));
935        Ok(())
936      }
937      AtRulePrelude::ViewTransition => {
938        self
939          .rules
940          .0
941          .push(CssRule::ViewTransition(ViewTransitionRule::parse(input, loc)?));
942        Ok(())
943      }
944      AtRulePrelude::Nest(selectors) => {
945        let (declarations, rules) = self.parse_nested(input, true)?;
946        self.rules.0.push(CssRule::Nesting(NestingRule {
947          style: StyleRule {
948            selectors,
949            declarations,
950            vendor_prefix: VendorPrefix::empty(),
951            rules,
952            loc,
953          },
954          loc,
955        }));
956        Ok(())
957      }
958      AtRulePrelude::FontFeatureValues(family_names) => {
959        let rule = FontFeatureValuesRule::parse(family_names, input, loc, self.options)?;
960        self.rules.0.push(CssRule::FontFeatureValues(rule));
961        Ok(())
962      }
963      AtRulePrelude::Unknown(name, prelude) => {
964        self.rules.0.push(CssRule::Unknown(UnknownAtRule {
965          name,
966          prelude,
967          block: Some(TokenList::parse(input, &self.options, 0)?),
968          loc,
969        }));
970        Ok(())
971      }
972      AtRulePrelude::Custom(prelude) => {
973        self.rules.0.push(parse_custom_at_rule_body(
974          prelude,
975          input,
976          start,
977          self.options,
978          self.at_rule_parser,
979          self.is_in_style_rule,
980        )?);
981        Ok(())
982      }
983    }
984  }
985}
986
987impl<'a, 'b, 'i, T: crate::traits::AtRuleParser<'i>> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'i, T> {
988  type Prelude = SelectorList<'i>;
989  type QualifiedRule = ();
990  type Error = ParserError<'i>;
991
992  fn parse_prelude<'t>(
993    &mut self,
994    input: &mut Parser<'i, 't>,
995  ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
996    let selector_parser = SelectorParser {
997      is_nesting_allowed: true,
998      options: &self.options,
999    };
1000    if self.is_in_style_rule {
1001      SelectorList::parse_relative(
1002        &selector_parser,
1003        input,
1004        ParseErrorRecovery::DiscardList,
1005        NestingRequirement::Implicit,
1006      )
1007    } else {
1008      SelectorList::parse(
1009        &selector_parser,
1010        input,
1011        ParseErrorRecovery::DiscardList,
1012        NestingRequirement::None,
1013      )
1014    }
1015  }
1016
1017  fn parse_block<'t>(
1018    &mut self,
1019    selectors: Self::Prelude,
1020    start: &ParserState,
1021    input: &mut Parser<'i, 't>,
1022  ) -> Result<(), ParseError<'i, Self::Error>> {
1023    let loc = self.loc(start);
1024    let (declarations, rules) = self.parse_nested(input, true)?;
1025    self.rules.0.push(CssRule::Style(StyleRule {
1026      selectors,
1027      vendor_prefix: VendorPrefix::empty(),
1028      declarations,
1029      rules,
1030      loc,
1031    }));
1032    Ok(())
1033  }
1034}
1035
1036/// Parse a declaration within {} block: `color: blue`
1037impl<'a, 'i, T: crate::traits::AtRuleParser<'i>> cssparser::DeclarationParser<'i> for NestedRuleParser<'a, 'i, T> {
1038  type Declaration = ();
1039  type Error = ParserError<'i>;
1040
1041  fn parse_value<'t>(
1042    &mut self,
1043    name: CowRcStr<'i>,
1044    input: &mut cssparser::Parser<'i, 't>,
1045    _declaration_start: &ParserState,
1046  ) -> Result<Self::Declaration, cssparser::ParseError<'i, Self::Error>> {
1047    if self.rules.0.is_empty() {
1048      parse_declaration(
1049        name,
1050        input,
1051        &mut self.declarations,
1052        &mut self.important_declarations,
1053        &self.options,
1054      )
1055    } else if let Some(CssRule::NestedDeclarations(last)) = self.rules.0.last_mut() {
1056      parse_declaration(
1057        name,
1058        input,
1059        &mut last.declarations.declarations,
1060        &mut last.declarations.important_declarations,
1061        &self.options,
1062      )
1063    } else {
1064      let loc = self.loc(&input.state());
1065      let mut nested = NestedDeclarationsRule {
1066        declarations: DeclarationBlock::new(),
1067        loc,
1068      };
1069
1070      parse_declaration(
1071        name,
1072        input,
1073        &mut nested.declarations.declarations,
1074        &mut nested.declarations.important_declarations,
1075        &self.options,
1076      )?;
1077
1078      self.rules.0.push(CssRule::NestedDeclarations(nested));
1079      Ok(())
1080    }
1081  }
1082}
1083
1084impl<'a, 'b, 'i, T: crate::traits::AtRuleParser<'i>> RuleBodyItemParser<'i, (), ParserError<'i>>
1085  for NestedRuleParser<'a, 'i, T>
1086{
1087  fn parse_qualified(&self) -> bool {
1088    true
1089  }
1090
1091  fn parse_declarations(&self) -> bool {
1092    self.allow_declarations
1093  }
1094}
1095
1096fn parse_custom_at_rule_prelude<'i, 't, T: crate::traits::AtRuleParser<'i>>(
1097  name: &CowRcStr<'i>,
1098  input: &mut Parser<'i, 't>,
1099  options: &ParserOptions<'i>,
1100  at_rule_parser: &mut T,
1101) -> Result<AtRulePrelude<'i, T::Prelude>, ParseError<'i, ParserError<'i>>> {
1102  match at_rule_parser.parse_prelude(name.clone(), input, options) {
1103    Ok(prelude) => return Ok(AtRulePrelude::Custom(prelude)),
1104    Err(ParseError {
1105      kind: ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(..)),
1106      ..
1107    }) => {}
1108    Err(err) => {
1109      return Err(match &err.kind {
1110        ParseErrorKind::Basic(kind) => ParseError {
1111          kind: ParseErrorKind::Basic(kind.clone()),
1112          location: err.location,
1113        },
1114        _ => input.new_custom_error(ParserError::AtRulePreludeInvalid),
1115      })
1116    }
1117  }
1118
1119  options.warn(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone())));
1120  input.skip_whitespace();
1121  let tokens = TokenList::parse(input, &options, 0)?;
1122  Ok(AtRulePrelude::Unknown(name.into(), tokens))
1123}
1124
1125fn parse_custom_at_rule_body<'i, 't, T: crate::traits::AtRuleParser<'i>>(
1126  prelude: T::Prelude,
1127  input: &mut Parser<'i, 't>,
1128  start: &ParserState,
1129  options: &ParserOptions<'i>,
1130  at_rule_parser: &mut T,
1131  is_nested: bool,
1132) -> Result<CssRule<'i, T::AtRule>, ParseError<'i, ParserError<'i>>> {
1133  at_rule_parser
1134    .parse_block(prelude, start, input, options, is_nested)
1135    .map(|prelude| CssRule::Custom(prelude))
1136    .map_err(|err| match &err.kind {
1137      ParseErrorKind::Basic(kind) => ParseError {
1138        kind: ParseErrorKind::Basic(kind.clone()),
1139        location: err.location,
1140      },
1141      _ => input.new_error(BasicParseErrorKind::AtRuleBodyInvalid),
1142    })
1143}
1144
1145fn parse_custom_at_rule_without_block<'i, 't, T: crate::traits::AtRuleParser<'i>>(
1146  prelude: T::Prelude,
1147  start: &ParserState,
1148  options: &ParserOptions<'i>,
1149  at_rule_parser: &mut T,
1150  is_nested: bool,
1151) -> Result<CssRule<'i, T::AtRule>, ()> {
1152  at_rule_parser
1153    .rule_without_block(prelude, start, options, is_nested)
1154    .map(|prelude| CssRule::Custom(prelude))
1155}
1156
1157pub fn parse_rule_list<'a, 'i, 't, T: crate::traits::AtRuleParser<'i>>(
1158  input: &mut Parser<'i, 't>,
1159  options: &'a ParserOptions<'i>,
1160  at_rule_parser: &mut T,
1161) -> Result<CssRuleList<'i, T::AtRule>, ParseError<'i, ParserError<'i>>> {
1162  let mut parser = NestedRuleParser {
1163    options,
1164    at_rule_parser,
1165    declarations: DeclarationList::new(),
1166    important_declarations: DeclarationList::new(),
1167    rules: &mut CssRuleList(Vec::new()),
1168    is_in_style_rule: false,
1169    allow_declarations: false,
1170  };
1171
1172  let (_, rules) = parser.parse_nested(input, false)?;
1173  Ok(rules)
1174}
1175
1176pub fn parse_style_block<'a, 'i, 't, T: crate::traits::AtRuleParser<'i>>(
1177  input: &mut Parser<'i, 't>,
1178  options: &'a ParserOptions<'i>,
1179  at_rule_parser: &mut T,
1180  is_nested: bool,
1181) -> Result<CssRuleList<'i, T::AtRule>, ParseError<'i, ParserError<'i>>> {
1182  let mut parser = NestedRuleParser {
1183    options,
1184    at_rule_parser,
1185    declarations: DeclarationList::new(),
1186    important_declarations: DeclarationList::new(),
1187    rules: &mut CssRuleList(Vec::new()),
1188    is_in_style_rule: is_nested,
1189    allow_declarations: true,
1190  };
1191
1192  parser.parse_style_block(input)
1193}
1194
1195#[inline]
1196pub fn starts_with_ignore_ascii_case(string: &str, prefix: &str) -> bool {
1197  string.len() >= prefix.len() && string.as_bytes()[0..prefix.len()].eq_ignore_ascii_case(prefix.as_bytes())
1198}