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