Skip to main content

style/stylesheets/
rule_parser.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Parsing of the stylesheet contents.
6
7use crate::counter_style::{parse_counter_style_body, parse_counter_style_name_definition};
8use crate::custom_properties::parse_name as parse_custom_property_name;
9use crate::derives::*;
10use crate::error_reporting::ContextualParseError;
11use crate::font_face::parse_font_face_block;
12use crate::media_queries::MediaList;
13use crate::parser::{Parse, ParserContext};
14use crate::properties::declaration_block::{
15    parse_property_declaration_list, DeclarationParserState, PropertyDeclarationBlock,
16};
17use crate::properties_and_values::rule::{parse_property_block, PropertyRuleName};
18use crate::selector_parser::{SelectorImpl, SelectorParser};
19use crate::shared_lock::{Locked, SharedRwLock};
20use crate::stylesheets::container_rule::{ContainerCondition, ContainerConditions, ContainerRule};
21use crate::stylesheets::document_rule::DocumentCondition;
22use crate::stylesheets::font_feature_values_rule::{
23    parse_family_name_list, FontFeatureValuesBlockType,
24};
25use crate::stylesheets::import_rule::{ImportLayer, ImportRule, ImportSupportsCondition};
26use crate::stylesheets::keyframes_rule::parse_keyframe_list;
27use crate::stylesheets::layer_rule::{LayerBlockRule, LayerName, LayerStatementRule};
28use crate::stylesheets::scope_rule::{ScopeBounds, ScopeRule};
29use crate::stylesheets::supports_rule::SupportsCondition;
30use crate::stylesheets::{
31    AllowImportRules, AppearanceBaseRule, CorsMode, CssRule, CssRuleType, CssRuleTypes, CssRules,
32    CustomMediaCondition, CustomMediaRule, DocumentRule, FontFeatureValuesRule,
33    FontPaletteValuesRule, KeyframesRule, MarginRule, MarginRuleType, MediaRule, NamespaceRule,
34    NestedDeclarationsRule, PageRule, PageSelectors, PositionTryRule, RulesMutateError,
35    StartingStyleRule, StyleRule, StylesheetLoader, SupportsRule, ViewTransitionRule,
36};
37use crate::values::computed::font::FamilyName;
38use crate::values::{CssUrl, CustomIdent, DashedIdent, KeyframesName};
39use crate::{Atom, Namespace, Prefix};
40use cssparser::{
41    match_ignore_ascii_case, AtRuleParser, BasicParseError, BasicParseErrorKind, CowRcStr,
42    DeclarationParser, Parser, ParserState, QualifiedRuleParser, RuleBodyItemParser,
43    RuleBodyParser, SourcePosition,
44};
45use selectors::parser::{ParseRelative, SelectorList};
46use servo_arc::Arc;
47use style_traits::arc_slice::ArcSlice;
48use style_traits::{ParseError, StyleParseErrorKind};
49
50/// The information we need particularly to do CSSOM insertRule stuff.
51pub struct InsertRuleContext<'a> {
52    /// The rule list we're about to insert into.
53    pub rule_list: &'a [CssRule],
54    /// The index we're about to get inserted at.
55    pub index: usize,
56    /// The containing rule types of our ancestors.
57    pub containing_rule_types: CssRuleTypes,
58    /// Rule type determining if and how we parse relative selector syntax.
59    pub parse_relative_rule_type: Option<CssRuleType>,
60}
61
62impl<'a> InsertRuleContext<'a> {
63    /// Returns the max rule state allowable for insertion at a given index in
64    /// the rule list.
65    pub fn max_rule_state_at_index(&self, index: usize) -> State {
66        let rule = match self.rule_list.get(index) {
67            Some(rule) => rule,
68            None => return State::Body,
69        };
70        match rule {
71            CssRule::Import(..) => State::Imports,
72            CssRule::Namespace(..) => State::Namespaces,
73            CssRule::LayerStatement(..) => {
74                // If there are @import / @namespace after this layer, then
75                // we're in the early-layers phase, otherwise we're in the body
76                // and everything is fair game.
77                let next_non_layer_statement_rule = self.rule_list[index + 1..]
78                    .iter()
79                    .find(|r| !matches!(*r, CssRule::LayerStatement(..)));
80                if let Some(non_layer) = next_non_layer_statement_rule {
81                    if matches!(*non_layer, CssRule::Import(..) | CssRule::Namespace(..)) {
82                        return State::EarlyLayers;
83                    }
84                }
85                State::Body
86            },
87            _ => State::Body,
88        }
89    }
90}
91
92/// The parser for the top-level rules in a stylesheet.
93pub struct TopLevelRuleParser<'a, 'i> {
94    /// A reference to the lock we need to use to create rules.
95    pub shared_lock: &'a SharedRwLock,
96    /// A reference to a stylesheet loader if applicable, for `@import` rules.
97    pub loader: Option<&'a dyn StylesheetLoader>,
98    /// The top-level parser context.
99    pub context: ParserContext<'a>,
100    /// The current state of the parser.
101    pub state: State,
102    /// Whether we have tried to parse was invalid due to being in the wrong
103    /// place (e.g. an @import rule was found while in the `Body` state). Reset
104    /// to `false` when `take_had_hierarchy_error` is called.
105    pub dom_error: Option<RulesMutateError>,
106    /// The info we need insert a rule in a list.
107    pub insert_rule_context: Option<InsertRuleContext<'a>>,
108    /// Whether @import rules will be allowed.
109    pub allow_import_rules: AllowImportRules,
110    /// Whether to keep declarations into first_declaration_block, rather than turning it into a
111    /// nested declarations rule.
112    pub wants_first_declaration_block: bool,
113    /// The first declaration block, only relevant when wants_first_declaration_block is true.
114    pub first_declaration_block: PropertyDeclarationBlock,
115    /// Parser state for declaration blocks in either nested rules or style rules.
116    pub declaration_parser_state: DeclarationParserState<'i>,
117    /// State we keep around only for error reporting purposes. Right now that contains just the
118    /// selectors stack for nesting, if any.
119    ///
120    /// TODO(emilio): This isn't populated properly for `insertRule()` but...
121    pub error_reporting_state: Vec<SelectorList<SelectorImpl>>,
122    /// The rules we've parsed so far.
123    pub rules: Vec<CssRule>,
124}
125
126impl<'a, 'i> TopLevelRuleParser<'a, 'i> {
127    #[inline]
128    fn nested(&mut self) -> &mut NestedRuleParser<'a, 'i> {
129        // SAFETY: NestedRuleParser is just a repr(transparent) wrapper over TopLevelRuleParser
130        const_assert!(
131            std::mem::size_of::<TopLevelRuleParser<'static, 'static>>()
132                == std::mem::size_of::<NestedRuleParser<'static, 'static>>()
133        );
134        const_assert!(
135            std::mem::align_of::<TopLevelRuleParser<'static, 'static>>()
136                == std::mem::align_of::<NestedRuleParser<'static, 'static>>()
137        );
138        unsafe { &mut *(self as *mut _ as *mut NestedRuleParser<'a, 'i>) }
139    }
140
141    /// Returns the current state of the parser.
142    #[inline]
143    pub fn state(&self) -> State {
144        self.state
145    }
146
147    /// If we're in a nested state, this returns whether declarations can be parsed. See
148    /// RuleBodyItemParser::parse_declarations().
149    #[inline]
150    pub fn can_parse_declarations(&self) -> bool {
151        // We also have to check for page rules here because we currently don't
152        // have a bespoke parser for page rules, and parse them as though they
153        // are style rules.
154        // Scope rules can have direct declarations, behaving as if `:where(:scope)`.
155        // See https://drafts.csswg.org/css-cascade-6/#scoped-declarations
156        self.in_specified_rule(
157            CssRuleType::Style.bit() | CssRuleType::Page.bit() | CssRuleType::Scope.bit(),
158        )
159    }
160
161    #[inline]
162    fn in_style_rule(&self) -> bool {
163        self.context
164            .nesting_context
165            .rule_types
166            .contains(CssRuleType::Style)
167    }
168
169    #[inline]
170    fn in_page_rule(&self) -> bool {
171        self.context
172            .nesting_context
173            .rule_types
174            .contains(CssRuleType::Page)
175    }
176
177    #[inline]
178    fn in_specified_rule(&self, bits: u32) -> bool {
179        let types = CssRuleTypes::from_bits(bits);
180        self.context.nesting_context.rule_types.intersects(types)
181    }
182
183    #[inline]
184    fn in_style_or_page_rule(&self) -> bool {
185        self.in_specified_rule(CssRuleType::Style.bit() | CssRuleType::Page.bit())
186    }
187
188    /// Checks whether we can parse a rule that would transition us to
189    /// `new_state`.
190    ///
191    /// This is usually a simple branch, but we may need more bookkeeping if
192    /// doing `insertRule` from CSSOM.
193    fn check_state(&mut self, new_state: State) -> bool {
194        if self.state > new_state {
195            self.dom_error = Some(RulesMutateError::HierarchyRequest);
196            return false;
197        }
198
199        let ctx = match self.insert_rule_context {
200            Some(ref ctx) => ctx,
201            None => return true,
202        };
203
204        let max_rule_state = ctx.max_rule_state_at_index(ctx.index);
205        if new_state > max_rule_state {
206            self.dom_error = Some(RulesMutateError::HierarchyRequest);
207            return false;
208        }
209
210        // If there's anything that isn't a namespace rule (or import rule, but
211        // we checked that already at the beginning), reject with a
212        // StateError.
213        if new_state == State::Namespaces
214            && ctx.rule_list[ctx.index..]
215                .iter()
216                .any(|r| !matches!(*r, CssRule::Namespace(..)))
217        {
218            self.dom_error = Some(RulesMutateError::InvalidState);
219            return false;
220        }
221
222        true
223    }
224}
225
226/// The current state of the parser.
227#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
228pub enum State {
229    /// We haven't started parsing rules.
230    Start = 1,
231    /// We're parsing early `@layer` statement rules.
232    EarlyLayers = 2,
233    /// We're parsing `@import` and early `@layer` statement rules.
234    Imports = 3,
235    /// We're parsing `@namespace` rules.
236    Namespaces = 4,
237    /// We're parsing the main body of the stylesheet.
238    Body = 5,
239}
240
241#[derive(Clone, Debug, MallocSizeOf, ToShmem)]
242/// Vendor prefix.
243pub enum VendorPrefix {
244    /// -moz prefix.
245    Moz,
246    /// -webkit prefix.
247    WebKit,
248}
249
250#[allow(missing_docs)]
251pub enum AtRuleType {
252    FontFace,
253    FontFeatureValues,
254    FontFeatureValuesBlock,
255    FontPaletteValues,
256    CounterStyle,
257    Media,
258    CustomMedia,
259    Container,
260    Supports,
261    Keyframes(Option<VendorPrefix>),
262    Page,
263    Property,
264    Document,
265    Import,
266    Namespace,
267    Layer,
268    Scope,
269    StartingStyle,
270    AppearanceBase,
271    PositionTry,
272    ViewTransition,
273    Margin(MarginRuleType),
274}
275
276impl AtRuleType {
277    fn from_name(name: &str, context: &ParserContext) -> Option<Self> {
278        Some(match_ignore_ascii_case! { name,
279            "font-face" => Self::FontFace,
280            "font-feature-values" => Self::FontFeatureValues,
281            "font-palette-values" if crate::pref!("layout.css.font-palette.enabled", gecko = true) => Self::FontPaletteValues,
282            "counter-style" if cfg!(feature = "gecko") => Self::CounterStyle,
283            "media" => Self::Media,
284            "custom-media" if crate::pref!("layout.css.custom-media.enabled") => Self::CustomMedia,
285            "container" if cfg!(feature = "gecko") => Self::Container,
286            "supports" => Self::Supports,
287            "keyframes" => Self::Keyframes(None),
288            "-webkit-keyframes" => Self::Keyframes(Some(VendorPrefix::WebKit)),
289            "-moz-keyframes" if cfg!(feature = "gecko") => Self::Keyframes(Some(VendorPrefix::Moz)),
290            "page" if cfg!(feature = "gecko") => Self::Page,
291            "property" if crate::pref!("layout.css.properties-and-values.enabled") => Self::Property,
292            "-moz-document" if cfg!(feature = "gecko") => Self::Document,
293            "import" => Self::Import,
294            "namespace" => Self::Namespace,
295            "layer" => Self::Layer,
296            "scope" if crate::pref!("layout.css.at-scope.enabled") => Self::Scope,
297            "starting-style" if crate::pref!("layout.css.starting-style-at-rules.enabled") => Self::StartingStyle,
298            "appearance-base" if context.chrome_rules_enabled() => Self::AppearanceBase,
299            "position-try" if crate::pref!("layout.css.anchor-positioning.enabled", gecko = true) => Self::PositionTry,
300            "view-transition" if crate::pref!("dom.viewTransitions.cross-document.enabled") => Self::ViewTransition,
301            _ => {
302                // The margin at-rules supported within @page.
303                if cfg!(feature = "gecko") && crate::pref!("layout.css.margin-rules.enabled") {
304                    if let Some(rule_type) = MarginRuleType::from_name(name) {
305                        return Some(Self::Margin(rule_type));
306                    }
307                }
308
309                // The font feature value at-rules supported within @font-feature-values.
310                if cfg!(feature = "gecko") && FontFeatureValuesBlockType::from_name(name).is_some() {
311                    return Some(Self::FontFeatureValuesBlock);
312                }
313
314                return None;
315            },
316        })
317    }
318
319    /// Returns whether the given at-rule is supported in any context.
320    ///
321    /// https://drafts.csswg.org/css-conditional-5/#support-definition-at-rules
322    pub fn is_supported(name: &str, context: &ParserContext) -> bool {
323        AtRuleType::from_name(name, context).is_some()
324    }
325}
326
327/// A rule prelude for at-rule with block.
328pub enum AtRulePrelude {
329    /// A @font-face rule prelude.
330    FontFace,
331    /// A @font-feature-values rule prelude, with its FamilyName list.
332    FontFeatureValues(Vec<FamilyName>),
333    /// A @font-palette-values rule prelude, with its identifier.
334    FontPaletteValues(DashedIdent),
335    /// A @counter-style rule prelude, with its counter style name.
336    CounterStyle(CustomIdent),
337    /// A @media rule prelude, with its media queries.
338    Media(Arc<Locked<MediaList>>),
339    /// A @container rule prelude.
340    Container(ArcSlice<ContainerCondition>),
341    /// An @supports rule, with its conditional
342    Supports(SupportsCondition),
343    /// A @keyframes rule, with its animation name and vendor prefix if exists.
344    Keyframes(KeyframesName, Option<VendorPrefix>),
345    /// A @page rule prelude, with its page name if it exists.
346    Page(PageSelectors),
347    /// A @property rule prelude.
348    Property(PropertyRuleName),
349    /// A @document rule, with its conditional.
350    Document(DocumentCondition),
351    /// A @import rule prelude.
352    Import(
353        CssUrl,
354        Arc<Locked<MediaList>>,
355        Option<ImportSupportsCondition>,
356        ImportLayer,
357    ),
358    /// A @margin rule prelude.
359    Margin(MarginRuleType),
360    /// A @namespace rule prelude.
361    Namespace(Option<Prefix>, Namespace),
362    /// A @layer rule prelude.
363    Layer(Vec<LayerName>),
364    /// A @scope rule prelude.
365    Scope(ScopeBounds),
366    /// A @starting-style prelude.
367    StartingStyle,
368    /// A @appearance-base prelude (UA sheets only).
369    AppearanceBase,
370    /// A @position-try prelude for Anchor Positioning.
371    PositionTry(DashedIdent),
372    /// A @custom-media prelude.
373    CustomMedia(DashedIdent, CustomMediaCondition),
374    /// A @view-transition prelude.
375    ViewTransition,
376}
377
378impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i> {
379    type Prelude = AtRulePrelude;
380    type AtRule = SourcePosition;
381    type Error = StyleParseErrorKind;
382
383    fn parse_prelude(
384        &mut self,
385        name: CowRcStr<'i>,
386        input: &mut Parser<'i>,
387    ) -> Result<AtRulePrelude, ParseError> {
388        // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet
389        // anything left is invalid.
390        if name.eq_ignore_ascii_case("charset") {
391            self.dom_error = Some(RulesMutateError::HierarchyRequest);
392            return Err(ParseError::custom(
393                StyleParseErrorKind::UnexpectedCharsetRule,
394            ));
395        }
396
397        let Some(rule_type) = AtRuleType::from_name(&name, &self.context) else {
398            return Err(ParseError::from_basic_kind(
399                BasicParseErrorKind::AtRuleInvalid,
400            ));
401        };
402
403        match rule_type {
404            AtRuleType::Import => {
405                if !self.check_state(State::Imports) {
406                    return Err(ParseError::custom(
407                        StyleParseErrorKind::UnexpectedImportRule,
408                    ));
409                }
410
411                if let AllowImportRules::No = self.allow_import_rules {
412                    return Err(ParseError::custom(
413                        StyleParseErrorKind::DisallowedImportRule,
414                    ));
415                }
416
417                // FIXME(emilio): We should always be able to have a loader
418                // around! See bug 1533783.
419                if self.loader.is_none() {
420                    error!("Saw @import rule, but no way to trigger the load");
421                    return Err(ParseError::custom(
422                        StyleParseErrorKind::UnexpectedImportRule,
423                    ));
424                }
425
426                let url_string = input.expect_url_or_string()?.as_ref().to_owned();
427                let url =
428                    CssUrl::new_from_untainted_string(url_string, &self.context, CorsMode::None);
429
430                let (layer, supports) =
431                    ImportRule::parse_layer_and_supports(input, &mut self.context);
432
433                let media = MediaList::parse(&mut self.context, input);
434                let media = Arc::new(self.shared_lock.wrap(media));
435
436                return Ok(AtRulePrelude::Import(url, media, supports, layer));
437            },
438            AtRuleType::Namespace => {
439                if !self.check_state(State::Namespaces) {
440                    return Err(ParseError::custom(
441                        StyleParseErrorKind::UnexpectedNamespaceRule,
442                    ));
443                }
444
445                let prefix = input
446                    .try_parse(|i| i.expect_ident_cloned())
447                    .map(|s| Prefix::from(s.as_ref()))
448                    .ok();
449                let maybe_namespace = match input.expect_url_or_string() {
450                    Ok(url_or_string) => url_or_string,
451                    Err(BasicParseError {
452                        kind: BasicParseErrorKind::UnexpectedToken,
453                    }) => {
454                        return Err(ParseError::custom(
455                            StyleParseErrorKind::UnexpectedTokenWithinNamespace,
456                        ))
457                    },
458                    Err(e) => return Err(e.into()),
459                };
460                let url = Namespace::from(maybe_namespace.as_ref());
461                return Ok(AtRulePrelude::Namespace(prefix, url));
462            },
463            AtRuleType::Layer => {
464                let state_to_check = if self.state <= State::EarlyLayers {
465                    // The real state depends on whether there's a block or not.
466                    // We don't know that yet, but the parse_block check deals
467                    // with that.
468                    State::EarlyLayers
469                } else {
470                    State::Body
471                };
472                if !self.check_state(state_to_check) {
473                    return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
474                }
475            },
476            _ => {
477                // All other rules have blocks, so we do this check early in
478                // parse_block instead.
479            },
480        }
481
482        self.nested().parse_at_rule_prelude(rule_type, input)
483    }
484
485    #[inline]
486    fn parse_block(
487        &mut self,
488        prelude: AtRulePrelude,
489        start: &ParserState,
490        input: &mut Parser<'i>,
491    ) -> Result<Self::AtRule, ParseError> {
492        if !self.check_state(State::Body) {
493            return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
494        }
495        AtRuleParser::parse_block(self.nested(), prelude, start, input)?;
496        self.state = State::Body;
497        Ok(start.position())
498    }
499
500    #[inline]
501    fn rule_without_block(
502        &mut self,
503        prelude: AtRulePrelude,
504        start: &ParserState,
505    ) -> Result<Self::AtRule, ()> {
506        match prelude {
507            AtRulePrelude::Import(url, media, supports, layer) => {
508                let loader = self
509                    .loader
510                    .expect("Expected a stylesheet loader for @import");
511
512                let import_rule = loader.request_stylesheet(
513                    url,
514                    start.source_location(),
515                    self.shared_lock,
516                    media,
517                    supports,
518                    layer,
519                );
520
521                self.state = State::Imports;
522                self.rules.push(CssRule::Import(import_rule))
523            },
524            AtRulePrelude::Namespace(prefix, url) => {
525                let namespaces = self.context.namespaces.to_mut();
526                let prefix = if let Some(prefix) = prefix {
527                    namespaces.prefixes.insert(prefix.clone(), url.clone());
528                    Some(prefix)
529                } else {
530                    namespaces.default = Some(url.clone());
531                    None
532                };
533
534                self.state = State::Namespaces;
535                self.rules.push(CssRule::Namespace(Arc::new(NamespaceRule {
536                    prefix,
537                    url,
538                    source_location: start.source_location(),
539                })));
540            },
541            AtRulePrelude::Layer(..) => {
542                AtRuleParser::rule_without_block(self.nested(), prelude, start)?;
543                if self.state <= State::EarlyLayers {
544                    self.state = State::EarlyLayers;
545                } else {
546                    self.state = State::Body;
547                }
548            },
549            _ => AtRuleParser::rule_without_block(self.nested(), prelude, start)?,
550        };
551
552        Ok(start.position())
553    }
554}
555
556impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, 'i> {
557    type Prelude = SelectorList<SelectorImpl>;
558    type QualifiedRule = SourcePosition;
559    type Error = StyleParseErrorKind;
560
561    #[inline]
562    fn parse_prelude(&mut self, input: &mut Parser<'i>) -> Result<Self::Prelude, ParseError> {
563        if !self.check_state(State::Body) {
564            return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
565        }
566
567        QualifiedRuleParser::parse_prelude(self.nested(), input)
568    }
569
570    #[inline]
571    fn parse_block(
572        &mut self,
573        prelude: Self::Prelude,
574        start: &ParserState,
575        input: &mut Parser<'i>,
576    ) -> Result<Self::QualifiedRule, ParseError> {
577        QualifiedRuleParser::parse_block(self.nested(), prelude, start, input)?;
578        self.state = State::Body;
579        Ok(start.position())
580    }
581}
582
583#[repr(transparent)]
584#[derive(Deref, DerefMut)]
585struct NestedRuleParser<'a, 'i>(TopLevelRuleParser<'a, 'i>);
586
587struct NestedParseResult {
588    first_declaration_block: PropertyDeclarationBlock,
589    rules: Vec<CssRule>,
590}
591
592impl<'a, 'i> NestedRuleParser<'a, 'i> {
593    fn parse_at_rule_prelude(
594        &mut self,
595        rule_type: AtRuleType,
596        input: &mut Parser<'i>,
597    ) -> Result<AtRulePrelude, ParseError> {
598        Ok(match rule_type {
599            AtRuleType::Media => {
600                let media_queries = MediaList::parse(&mut self.context, input);
601                let arc = Arc::new(self.shared_lock.wrap(media_queries));
602                AtRulePrelude::Media(arc)
603            },
604            AtRuleType::Supports => {
605                let cond = SupportsCondition::parse(input)?;
606                AtRulePrelude::Supports(cond)
607            },
608            AtRuleType::FontFace => AtRulePrelude::FontFace,
609            AtRuleType::Container => {
610                let conditions = input.parse_comma_separated(|input| {
611                    ContainerCondition::parse(&self.context, input)
612                })?;
613                // Container rules must have at least one condition.
614                debug_assert!(!conditions.is_empty());
615                let conditions = ArcSlice::from_iter(conditions.into_iter());
616                AtRulePrelude::Container(conditions)
617            },
618            AtRuleType::Layer => {
619                let names = input
620                    .try_parse(|input| {
621                        input.parse_comma_separated(|input| LayerName::parse(&self.context, input))
622                    })
623                    .unwrap_or_default();
624                AtRulePrelude::Layer(names)
625            },
626            AtRuleType::FontFeatureValues => {
627                let family_names = parse_family_name_list(&self.context, input)?;
628                AtRulePrelude::FontFeatureValues(family_names)
629            },
630            AtRuleType::FontPaletteValues => {
631                let name = DashedIdent::parse(&self.context, input)?;
632                AtRulePrelude::FontPaletteValues(name)
633            },
634            AtRuleType::CounterStyle => {
635                let name = parse_counter_style_name_definition(input)?;
636                AtRulePrelude::CounterStyle(name)
637            },
638            AtRuleType::Keyframes(prefix) => {
639                let name = KeyframesName::parse(&self.context, input)?;
640                AtRulePrelude::Keyframes(name, prefix)
641            },
642            AtRuleType::Page => AtRulePrelude::Page(
643                input
644                    .try_parse(|i| PageSelectors::parse(&self.context, i))
645                    .unwrap_or_default(),
646            ),
647            AtRuleType::Property => {
648                let name = input.expect_ident_cloned()?;
649                let name = parse_custom_property_name(&name)
650                    .map_err(|_| ParseError::custom(StyleParseErrorKind::UnexpectedIdent))?;
651                AtRulePrelude::Property(PropertyRuleName(Atom::from(name)))
652            },
653            AtRuleType::Document => {
654                let cond = DocumentCondition::parse(&self.context, input)?;
655                AtRulePrelude::Document(cond)
656            },
657            AtRuleType::Scope => {
658                let bounds = ScopeBounds::parse(&self.context, input, self.parse_relative())?;
659                AtRulePrelude::Scope(bounds)
660            },
661            AtRuleType::StartingStyle => AtRulePrelude::StartingStyle,
662            AtRuleType::AppearanceBase => {
663                // We allow parsing this in chrome sheets mostly just so that
664                // browser_parsable_css.js checks UA sheets properly.
665                AtRulePrelude::AppearanceBase
666            },
667            AtRuleType::PositionTry => {
668                let name = DashedIdent::parse(&self.context, input)?;
669                AtRulePrelude::PositionTry(name)
670            },
671            AtRuleType::CustomMedia => {
672                let name = DashedIdent::parse(&self.context, input)?;
673                let condition = input
674                    .try_parse(CustomMediaCondition::parse_keyword)
675                    .unwrap_or_else(|_| {
676                        CustomMediaCondition::MediaList(Arc::new(
677                            self.shared_lock
678                                .wrap(MediaList::parse(&mut self.context, input)),
679                        ))
680                    });
681                AtRulePrelude::CustomMedia(name, condition)
682            },
683            AtRuleType::ViewTransition => AtRulePrelude::ViewTransition,
684            AtRuleType::Margin(rule_type) => AtRulePrelude::Margin(rule_type),
685            AtRuleType::Import | AtRuleType::Namespace | AtRuleType::FontFeatureValuesBlock => {
686                // @import and @namespace are only valid and handled at the
687                // top level, and @font-feature-values uses its own parser.
688                return Err(ParseError::from_basic_kind(
689                    BasicParseErrorKind::AtRuleInvalid,
690                ));
691            },
692        })
693    }
694
695    #[inline]
696    fn parse_relative(&self) -> ParseRelative {
697        self.context.nesting_context.parse_relative
698    }
699
700    // https://drafts.csswg.org/css-nesting/#conditionals
701    //     In addition to nested style rules, this specification allows nested group rules inside
702    //     of style rules: any at-rule whose body contains style rules can be nested inside of a
703    //     style rule as well.
704    fn at_rule_allowed(&self, prelude: &AtRulePrelude) -> bool {
705        match prelude {
706            AtRulePrelude::Media(..)
707            | AtRulePrelude::Supports(..)
708            | AtRulePrelude::Container(..)
709            | AtRulePrelude::Document(..)
710            | AtRulePrelude::Layer(..)
711            | AtRulePrelude::CustomMedia(..)
712            | AtRulePrelude::Scope(..)
713            | AtRulePrelude::StartingStyle
714            | AtRulePrelude::AppearanceBase => true,
715
716            AtRulePrelude::Namespace(..)
717            | AtRulePrelude::FontFace
718            | AtRulePrelude::FontFeatureValues(..)
719            | AtRulePrelude::FontPaletteValues(..)
720            | AtRulePrelude::CounterStyle(..)
721            | AtRulePrelude::Keyframes(..)
722            | AtRulePrelude::Page(..)
723            | AtRulePrelude::Property(..)
724            | AtRulePrelude::Import(..)
725            | AtRulePrelude::PositionTry(..)
726            | AtRulePrelude::ViewTransition => !self.in_style_or_page_rule(),
727            AtRulePrelude::Margin(..) => self.in_page_rule(),
728        }
729    }
730
731    fn nest_for_rule<R>(&mut self, rule_type: CssRuleType, cb: impl FnOnce(&mut Self) -> R) -> R {
732        let old = self.context.nesting_context.save(rule_type);
733        let r = cb(self);
734        self.context.nesting_context.restore(old);
735        r
736    }
737
738    fn parse_nested_rules(
739        &mut self,
740        input: &mut Parser<'i>,
741        rule_type: CssRuleType,
742    ) -> Arc<Locked<CssRules>> {
743        let rules = self
744            .parse_nested(
745                input, rule_type, /* wants_first_declaration_block = */ false,
746            )
747            .rules;
748        CssRules::new(rules, self.shared_lock)
749    }
750
751    fn parse_nested(
752        &mut self,
753        input: &mut Parser<'i>,
754        rule_type: CssRuleType,
755        wants_first_declaration_block: bool,
756    ) -> NestedParseResult {
757        debug_assert!(
758            !self.wants_first_declaration_block,
759            "Should've flushed previous declarations"
760        );
761        self.nest_for_rule(rule_type, |parser| {
762            parser.wants_first_declaration_block = wants_first_declaration_block;
763            let parse_declarations = parser.parse_declarations();
764            let mut rules = std::mem::take(&mut parser.rules);
765            let mut first_declaration_block = std::mem::take(&mut parser.first_declaration_block);
766            let mut iter = RuleBodyParser::new(input, parser);
767            while let Some(result) = iter.next() {
768                match result {
769                    Ok(()) => {},
770                    Err((error, slice, location)) => {
771                        if parse_declarations {
772                            let top = &mut **iter.parser;
773                            top.declaration_parser_state.did_error(
774                                &top.context,
775                                error,
776                                slice,
777                                location,
778                            );
779                        } else {
780                            let error = ContextualParseError::InvalidRule(slice, error);
781                            iter.parser.context.log_css_error(location, error);
782                        }
783                    },
784                }
785            }
786            parser.flush_declarations();
787            debug_assert!(
788                !parser.wants_first_declaration_block,
789                "Flushing declarations should take care of this."
790            );
791            debug_assert!(
792                !parser.declaration_parser_state.has_parsed_declarations(),
793                "Parsed but didn't consume declarations"
794            );
795            std::mem::swap(&mut parser.rules, &mut rules);
796            std::mem::swap(
797                &mut parser.first_declaration_block,
798                &mut first_declaration_block,
799            );
800            NestedParseResult {
801                first_declaration_block,
802                rules,
803            }
804        })
805    }
806
807    #[inline(never)]
808    fn handle_error_reporting_selectors_pre(
809        &mut self,
810        start: &ParserState,
811        selectors: &SelectorList<SelectorImpl>,
812    ) {
813        use cssparser::ToCss;
814        debug_assert!(self.context.error_reporting_enabled());
815        self.error_reporting_state.push(selectors.clone());
816        'selector_loop: for selector in selectors.slice().iter() {
817            let mut current = selector.iter();
818            loop {
819                let mut found_host = false;
820                let mut found_non_host = false;
821                for component in &mut current {
822                    if component.is_host() {
823                        found_host = true;
824                    } else {
825                        found_non_host = true;
826                    }
827                    if found_host && found_non_host {
828                        self.context.log_css_error(
829                            start.source_location(),
830                            ContextualParseError::NeverMatchingHostSelector(
831                                selector.to_css_string(),
832                            ),
833                        );
834                        continue 'selector_loop;
835                    }
836                }
837                if current.next_sequence().is_none() {
838                    break;
839                }
840            }
841        }
842    }
843
844    fn handle_error_reporting_selectors_post(&mut self) {
845        self.error_reporting_state.pop();
846    }
847
848    #[inline]
849    fn flush_declarations(&mut self) {
850        let parser = &mut **self;
851        let wants_first_declaration_block = parser.wants_first_declaration_block;
852        parser.wants_first_declaration_block = false;
853        parser
854            .declaration_parser_state
855            .report_errors_if_needed(&parser.context, &parser.error_reporting_state);
856        if !parser.declaration_parser_state.has_parsed_declarations() {
857            return;
858        }
859        let source_location = parser.declaration_parser_state.first_declaration_start();
860        let declarations = parser.declaration_parser_state.take_declarations();
861        if wants_first_declaration_block {
862            debug_assert!(parser.first_declaration_block.is_empty(), "How?");
863            parser.first_declaration_block = declarations;
864        } else {
865            let nested_rule = CssRule::NestedDeclarations(Arc::new(parser.shared_lock.wrap(
866                NestedDeclarationsRule {
867                    block: Arc::new(parser.shared_lock.wrap(declarations)),
868                    source_location,
869                },
870            )));
871            parser.rules.push(nested_rule);
872        }
873    }
874}
875
876impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
877    type Prelude = AtRulePrelude;
878    type AtRule = ();
879    type Error = StyleParseErrorKind;
880
881    fn parse_prelude(
882        &mut self,
883        name: CowRcStr<'i>,
884        input: &mut Parser<'i>,
885    ) -> Result<Self::Prelude, ParseError> {
886        let Some(rule_type) = AtRuleType::from_name(&name, &self.context) else {
887            return Err(ParseError::from_basic_kind(
888                BasicParseErrorKind::AtRuleInvalid,
889            ));
890        };
891
892        self.parse_at_rule_prelude(rule_type, input)
893    }
894
895    fn parse_block(
896        &mut self,
897        prelude: AtRulePrelude,
898        start: &ParserState,
899        input: &mut Parser<'i>,
900    ) -> Result<(), ParseError> {
901        if !self.at_rule_allowed(&prelude) {
902            self.dom_error = Some(RulesMutateError::HierarchyRequest);
903            return Err(ParseError::from_basic_kind(
904                BasicParseErrorKind::AtRuleInvalid,
905            ));
906        }
907        let source_location = start.source_location();
908        self.flush_declarations();
909        let rule = match prelude {
910            AtRulePrelude::FontFace => self.nest_for_rule(CssRuleType::FontFace, |p| {
911                CssRule::FontFace(Arc::new(p.shared_lock.wrap(parse_font_face_block(
912                    &p.context,
913                    input,
914                    source_location,
915                ))))
916            }),
917            AtRulePrelude::FontFeatureValues(family_names) => {
918                self.nest_for_rule(CssRuleType::FontFeatureValues, |p| {
919                    CssRule::FontFeatureValues(Arc::new(FontFeatureValuesRule::parse(
920                        &p.context,
921                        input,
922                        family_names,
923                        source_location,
924                    )))
925                })
926            },
927            AtRulePrelude::FontPaletteValues(name) => {
928                self.nest_for_rule(CssRuleType::FontPaletteValues, |p| {
929                    CssRule::FontPaletteValues(Arc::new(FontPaletteValuesRule::parse(
930                        &p.context,
931                        input,
932                        name,
933                        source_location,
934                    )))
935                })
936            },
937            AtRulePrelude::CounterStyle(name) => {
938                let body = self.nest_for_rule(CssRuleType::CounterStyle, |p| {
939                    parse_counter_style_body(name, &p.context, input, source_location)
940                })?;
941                CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(body)))
942            },
943            AtRulePrelude::Media(media_queries) => CssRule::Media(Arc::new(MediaRule {
944                media_queries,
945                rules: self.parse_nested_rules(input, CssRuleType::Media),
946                source_location,
947            })),
948            AtRulePrelude::Supports(condition) => {
949                let enabled =
950                    self.nest_for_rule(CssRuleType::Style, |p| condition.eval(&p.context));
951                CssRule::Supports(Arc::new(SupportsRule {
952                    condition,
953                    rules: self.parse_nested_rules(input, CssRuleType::Supports),
954                    enabled,
955                    source_location,
956                }))
957            },
958            AtRulePrelude::Keyframes(name, vendor_prefix) => {
959                self.nest_for_rule(CssRuleType::Keyframe, |p| {
960                    let top = &mut **p;
961                    CssRule::Keyframes(Arc::new(top.shared_lock.wrap(KeyframesRule {
962                        name,
963                        keyframes: parse_keyframe_list(&mut top.context, input, top.shared_lock),
964                        vendor_prefix,
965                        source_location,
966                    })))
967                })
968            },
969            AtRulePrelude::Page(selectors) => {
970                let page_rule = if !crate::pref!("layout.css.margin-rules.enabled") {
971                    let declarations = self.nest_for_rule(CssRuleType::Page, |p| {
972                        parse_property_declaration_list(&p.context, input, &[])
973                    });
974                    PageRule {
975                        selectors,
976                        rules: CssRules::new(vec![], self.shared_lock),
977                        block: Arc::new(self.shared_lock.wrap(declarations)),
978                        source_location,
979                    }
980                } else {
981                    let result = self.parse_nested(input, CssRuleType::Page, true);
982                    PageRule {
983                        selectors,
984                        rules: CssRules::new(result.rules, self.shared_lock),
985                        block: Arc::new(self.shared_lock.wrap(result.first_declaration_block)),
986                        source_location,
987                    }
988                };
989                CssRule::Page(Arc::new(self.shared_lock.wrap(page_rule)))
990            },
991            AtRulePrelude::Property(name) => self.nest_for_rule(CssRuleType::Property, |p| {
992                let rule_data = parse_property_block(&p.context, input, name, source_location)?;
993                Ok::<CssRule, ParseError>(CssRule::Property(Arc::new(rule_data)))
994            })?,
995            AtRulePrelude::Document(condition) => {
996                if !cfg!(feature = "gecko") {
997                    unreachable!()
998                }
999                CssRule::Document(Arc::new(DocumentRule {
1000                    condition,
1001                    rules: self.parse_nested_rules(input, CssRuleType::Document),
1002                    source_location,
1003                }))
1004            },
1005            AtRulePrelude::Container(conditions) => {
1006                let source_location = start.source_location();
1007                CssRule::Container(Arc::new(ContainerRule {
1008                    conditions: ContainerConditions(conditions),
1009                    rules: self.parse_nested_rules(input, CssRuleType::Container),
1010                    source_location,
1011                }))
1012            },
1013            AtRulePrelude::Layer(names) => {
1014                let name = match names.len() {
1015                    0 | 1 => names.into_iter().next(),
1016                    _ => {
1017                        return Err(ParseError::from_basic_kind(
1018                            BasicParseErrorKind::AtRuleBodyInvalid,
1019                        ))
1020                    },
1021                };
1022                CssRule::LayerBlock(Arc::new(LayerBlockRule {
1023                    name,
1024                    rules: self.parse_nested_rules(input, CssRuleType::LayerBlock),
1025                    source_location,
1026                }))
1027            },
1028            AtRulePrelude::Margin(rule_type) => {
1029                let declarations = self.nest_for_rule(CssRuleType::Margin, |p| {
1030                    parse_property_declaration_list(&p.context, input, &[])
1031                });
1032                CssRule::Margin(Arc::new(MarginRule {
1033                    rule_type,
1034                    block: Arc::new(self.shared_lock.wrap(declarations)),
1035                    source_location,
1036                }))
1037            },
1038            AtRulePrelude::CustomMedia(..)
1039            | AtRulePrelude::Import(..)
1040            | AtRulePrelude::Namespace(..) => {
1041                // These rules don't have blocks.
1042                return Err(ParseError::unexpected_token());
1043            },
1044            AtRulePrelude::Scope(bounds) => CssRule::Scope(Arc::new(ScopeRule {
1045                bounds,
1046                rules: self.parse_nested_rules(input, CssRuleType::Scope),
1047                source_location,
1048            })),
1049            AtRulePrelude::StartingStyle => CssRule::StartingStyle(Arc::new(StartingStyleRule {
1050                rules: self.parse_nested_rules(input, CssRuleType::StartingStyle),
1051                source_location,
1052            })),
1053            AtRulePrelude::AppearanceBase => {
1054                CssRule::AppearanceBase(Arc::new(AppearanceBaseRule {
1055                    rules: self.parse_nested_rules(input, CssRuleType::AppearanceBase),
1056                    source_location,
1057                }))
1058            },
1059            AtRulePrelude::PositionTry(name) => {
1060                let declarations = self.nest_for_rule(CssRuleType::PositionTry, |p| {
1061                    parse_property_declaration_list(&p.context, input, &[])
1062                });
1063                CssRule::PositionTry(Arc::new(self.shared_lock.wrap(PositionTryRule {
1064                    name,
1065                    block: Arc::new(self.shared_lock.wrap(declarations)),
1066                    source_location,
1067                })))
1068            },
1069            AtRulePrelude::ViewTransition => self.nest_for_rule(CssRuleType::ViewTransition, |p| {
1070                CssRule::ViewTransition(Arc::new(ViewTransitionRule::parse(
1071                    &p.context,
1072                    input,
1073                    source_location,
1074                )))
1075            }),
1076        };
1077        self.rules.push(rule);
1078        Ok(())
1079    }
1080
1081    #[inline]
1082    fn rule_without_block(
1083        &mut self,
1084        prelude: AtRulePrelude,
1085        start: &ParserState,
1086    ) -> Result<(), ()> {
1087        if self.in_style_rule() {
1088            return Err(());
1089        }
1090        let source_location = start.source_location();
1091        let rule = match prelude {
1092            AtRulePrelude::CustomMedia(name, condition) => {
1093                CssRule::CustomMedia(Arc::new(CustomMediaRule {
1094                    name,
1095                    condition,
1096                    source_location,
1097                }))
1098            },
1099            AtRulePrelude::Layer(names) => {
1100                if names.is_empty() {
1101                    return Err(());
1102                }
1103                CssRule::LayerStatement(Arc::new(LayerStatementRule {
1104                    names,
1105                    source_location,
1106                }))
1107            },
1108            _ => return Err(()),
1109        };
1110        self.flush_declarations();
1111        self.rules.push(rule);
1112        Ok(())
1113    }
1114}
1115
1116impl<'a, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'i> {
1117    type Prelude = SelectorList<SelectorImpl>;
1118    type QualifiedRule = ();
1119    type Error = StyleParseErrorKind;
1120
1121    fn parse_prelude(&mut self, input: &mut Parser<'i>) -> Result<Self::Prelude, ParseError> {
1122        let selector_parser = SelectorParser {
1123            stylesheet_origin: self.context.stylesheet_origin,
1124            namespaces: &self.context.namespaces,
1125            url_data: self.context.url_data,
1126            for_supports_rule: false,
1127        };
1128        SelectorList::parse(&selector_parser, input, self.parse_relative())
1129    }
1130
1131    fn parse_block(
1132        &mut self,
1133        selectors: Self::Prelude,
1134        start: &ParserState,
1135        input: &mut Parser<'i>,
1136    ) -> Result<(), ParseError> {
1137        let source_location = start.source_location();
1138        let reporting_errors = self.context.error_reporting_enabled();
1139        if reporting_errors {
1140            self.handle_error_reporting_selectors_pre(start, &selectors);
1141        }
1142        self.flush_declarations();
1143        let result = self.parse_nested(input, CssRuleType::Style, true);
1144        if reporting_errors {
1145            self.handle_error_reporting_selectors_post();
1146        }
1147        let block = Arc::new(self.shared_lock.wrap(result.first_declaration_block));
1148        let top = &mut **self;
1149        top.rules
1150            .push(CssRule::Style(Arc::new(top.shared_lock.wrap(StyleRule {
1151                selectors,
1152                block,
1153                rules: if result.rules.is_empty() {
1154                    None
1155                } else {
1156                    Some(CssRules::new(result.rules, top.shared_lock))
1157                },
1158                source_location,
1159            }))));
1160        Ok(())
1161    }
1162}
1163
1164impl<'a, 'i> DeclarationParser<'i> for NestedRuleParser<'a, 'i> {
1165    type Declaration = ();
1166    type Error = StyleParseErrorKind;
1167    fn parse_value(
1168        &mut self,
1169        name: CowRcStr<'i>,
1170        input: &mut Parser<'i>,
1171        declaration_start: &ParserState,
1172    ) -> Result<(), ParseError> {
1173        let top = &mut **self;
1174        top.declaration_parser_state
1175            .parse_value(&top.context, name, input, declaration_start)
1176    }
1177}
1178
1179impl<'a, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind> for NestedRuleParser<'a, 'i> {
1180    fn parse_qualified(&self) -> bool {
1181        true
1182    }
1183
1184    /// If nesting is disabled, we can't get there for a non-style-rule. If it's enabled, we parse
1185    /// raw declarations there.
1186    fn parse_declarations(&self) -> bool {
1187        self.can_parse_declarations()
1188    }
1189}