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