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    DeclarationParserState, PropertyDeclarationBlock, parse_property_declaration_list,
16};
17use crate::properties_and_values::rule::{PropertyRuleName, parse_property_block};
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    FontFeatureValuesBlockType, parse_family_name_list,
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    AtRuleParser, BasicParseError, BasicParseErrorKind, CowRcStr, DeclarationParser, Parser,
42    ParserState, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourcePosition,
43    match_ignore_ascii_case,
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                    && matches!(*non_layer, CssRule::Import(..) | CssRule::Namespace(..))
82                {
83                    return State::EarlyLayers;
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                    && let Some(rule_type) = MarginRuleType::from_name(name) {
305                        return Some(Self::Margin(rule_type));
306                    }
307
308                // The font feature value at-rules supported within @font-feature-values.
309                if cfg!(feature = "gecko") && FontFeatureValuesBlockType::from_name(name).is_some() {
310                    return Some(Self::FontFeatureValuesBlock);
311                }
312
313                return None;
314            },
315        })
316    }
317
318    /// Returns whether the given at-rule is supported in any context.
319    ///
320    /// https://drafts.csswg.org/css-conditional-5/#support-definition-at-rules
321    pub fn is_supported(name: &str, context: &ParserContext) -> bool {
322        AtRuleType::from_name(name, context).is_some()
323    }
324}
325
326/// A rule prelude for at-rule with block.
327pub enum AtRulePrelude {
328    /// A @font-face rule prelude.
329    FontFace,
330    /// A @font-feature-values rule prelude, with its FamilyName list.
331    FontFeatureValues(Vec<FamilyName>),
332    /// A @font-palette-values rule prelude, with its identifier.
333    FontPaletteValues(DashedIdent),
334    /// A @counter-style rule prelude, with its counter style name.
335    CounterStyle(CustomIdent),
336    /// A @media rule prelude, with its media queries.
337    Media(Arc<Locked<MediaList>>),
338    /// A @container rule prelude.
339    Container(ArcSlice<ContainerCondition>),
340    /// An @supports rule, with its conditional
341    Supports(SupportsCondition),
342    /// A @keyframes rule, with its animation name and vendor prefix if exists.
343    Keyframes(KeyframesName, Option<VendorPrefix>),
344    /// A @page rule prelude, with its page name if it exists.
345    Page(PageSelectors),
346    /// A @property rule prelude.
347    Property(PropertyRuleName),
348    /// A @document rule, with its conditional.
349    Document(DocumentCondition),
350    /// A @import rule prelude.
351    Import(
352        CssUrl,
353        Arc<Locked<MediaList>>,
354        Option<ImportSupportsCondition>,
355        ImportLayer,
356    ),
357    /// A @margin rule prelude.
358    Margin(MarginRuleType),
359    /// A @namespace rule prelude.
360    Namespace(Option<Prefix>, Namespace),
361    /// A @layer rule prelude.
362    Layer(Vec<LayerName>),
363    /// A @scope rule prelude.
364    Scope(ScopeBounds),
365    /// A @starting-style prelude.
366    StartingStyle,
367    /// A @appearance-base prelude (UA sheets only).
368    AppearanceBase,
369    /// A @position-try prelude for Anchor Positioning.
370    PositionTry(DashedIdent),
371    /// A @custom-media prelude.
372    CustomMedia(DashedIdent, CustomMediaCondition),
373    /// A @view-transition prelude.
374    ViewTransition,
375}
376
377impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i> {
378    type Prelude = AtRulePrelude;
379    type AtRule = SourcePosition;
380    type Error = StyleParseErrorKind;
381
382    fn parse_prelude(
383        &mut self,
384        name: CowRcStr<'i>,
385        input: &mut Parser<'i>,
386    ) -> Result<AtRulePrelude, ParseError> {
387        // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet
388        // anything left is invalid.
389        if name.eq_ignore_ascii_case("charset") {
390            self.dom_error = Some(RulesMutateError::HierarchyRequest);
391            return Err(ParseError::custom(
392                StyleParseErrorKind::UnexpectedCharsetRule,
393            ));
394        }
395
396        let Some(rule_type) = AtRuleType::from_name(&name, &self.context) else {
397            return Err(ParseError::from_basic_kind(
398                BasicParseErrorKind::AtRuleInvalid,
399            ));
400        };
401
402        match rule_type {
403            AtRuleType::Import => {
404                if !self.check_state(State::Imports) {
405                    return Err(ParseError::custom(
406                        StyleParseErrorKind::UnexpectedImportRule,
407                    ));
408                }
409
410                if let AllowImportRules::No = self.allow_import_rules {
411                    return Err(ParseError::custom(
412                        StyleParseErrorKind::DisallowedImportRule,
413                    ));
414                }
415
416                // FIXME(emilio): We should always be able to have a loader
417                // around! See bug 1533783.
418                if self.loader.is_none() {
419                    error!("Saw @import rule, but no way to trigger the load");
420                    return Err(ParseError::custom(
421                        StyleParseErrorKind::UnexpectedImportRule,
422                    ));
423                }
424
425                let url_string = input.expect_url_or_string()?.as_ref().to_owned();
426                let url =
427                    CssUrl::new_from_untainted_string(url_string, &self.context, CorsMode::None);
428
429                let (layer, supports) =
430                    ImportRule::parse_layer_and_supports(input, &mut self.context);
431
432                let media = MediaList::parse(&mut self.context, input);
433                let media = Arc::new(self.shared_lock.wrap(media));
434
435                return Ok(AtRulePrelude::Import(url, media, supports, layer));
436            },
437            AtRuleType::Namespace => {
438                if !self.check_state(State::Namespaces) {
439                    return Err(ParseError::custom(
440                        StyleParseErrorKind::UnexpectedNamespaceRule,
441                    ));
442                }
443
444                let prefix = input
445                    .try_parse(|i| i.expect_ident_cloned())
446                    .map(|s| Prefix::from(s.as_ref()))
447                    .ok();
448                let maybe_namespace = match input.expect_url_or_string() {
449                    Ok(url_or_string) => url_or_string,
450                    Err(BasicParseError {
451                        kind: BasicParseErrorKind::UnexpectedToken,
452                    }) => {
453                        return Err(ParseError::custom(
454                            StyleParseErrorKind::UnexpectedTokenWithinNamespace,
455                        ));
456                    },
457                    Err(e) => return Err(e.into()),
458                };
459                let url = Namespace::from(maybe_namespace.as_ref());
460                return Ok(AtRulePrelude::Namespace(prefix, url));
461            },
462            AtRuleType::Layer => {
463                let state_to_check = if self.state <= State::EarlyLayers {
464                    // The real state depends on whether there's a block or not.
465                    // We don't know that yet, but the parse_block check deals
466                    // with that.
467                    State::EarlyLayers
468                } else {
469                    State::Body
470                };
471                if !self.check_state(state_to_check) {
472                    return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
473                }
474            },
475            _ => {
476                // All other rules have blocks, so we do this check early in
477                // parse_block instead.
478            },
479        }
480
481        self.nested().parse_at_rule_prelude(rule_type, input)
482    }
483
484    #[inline]
485    fn parse_block(
486        &mut self,
487        prelude: AtRulePrelude,
488        start: &ParserState,
489        input: &mut Parser<'i>,
490    ) -> Result<Self::AtRule, ParseError> {
491        if !self.check_state(State::Body) {
492            return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
493        }
494        AtRuleParser::parse_block(self.nested(), prelude, start, input)?;
495        self.state = State::Body;
496        Ok(start.position())
497    }
498
499    #[inline]
500    fn rule_without_block(
501        &mut self,
502        prelude: AtRulePrelude,
503        start: &ParserState,
504    ) -> Result<Self::AtRule, ()> {
505        match prelude {
506            AtRulePrelude::Import(url, media, supports, layer) => {
507                let loader = self
508                    .loader
509                    .expect("Expected a stylesheet loader for @import");
510
511                let import_rule = loader.request_stylesheet(
512                    url,
513                    start.source_location(),
514                    self.shared_lock,
515                    media,
516                    supports,
517                    layer,
518                );
519
520                self.state = State::Imports;
521                self.rules.push(CssRule::Import(import_rule))
522            },
523            AtRulePrelude::Namespace(prefix, url) => {
524                let namespaces = self.context.namespaces.to_mut();
525                let prefix = if let Some(prefix) = prefix {
526                    namespaces.prefixes.insert(prefix.clone(), url.clone());
527                    Some(prefix)
528                } else {
529                    namespaces.default = Some(url.clone());
530                    None
531                };
532
533                self.state = State::Namespaces;
534                self.rules.push(CssRule::Namespace(Arc::new(NamespaceRule {
535                    prefix,
536                    url,
537                    source_location: start.source_location(),
538                })));
539            },
540            AtRulePrelude::Layer(..) => {
541                AtRuleParser::rule_without_block(self.nested(), prelude, start)?;
542                if self.state <= State::EarlyLayers {
543                    self.state = State::EarlyLayers;
544                } else {
545                    self.state = State::Body;
546                }
547            },
548            _ => AtRuleParser::rule_without_block(self.nested(), prelude, start)?,
549        };
550
551        Ok(start.position())
552    }
553}
554
555impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, 'i> {
556    type Prelude = SelectorList<SelectorImpl>;
557    type QualifiedRule = SourcePosition;
558    type Error = StyleParseErrorKind;
559
560    #[inline]
561    fn parse_prelude(&mut self, input: &mut Parser<'i>) -> Result<Self::Prelude, ParseError> {
562        if !self.check_state(State::Body) {
563            return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
564        }
565
566        QualifiedRuleParser::parse_prelude(self.nested(), input)
567    }
568
569    #[inline]
570    fn parse_block(
571        &mut self,
572        prelude: Self::Prelude,
573        start: &ParserState,
574        input: &mut Parser<'i>,
575    ) -> Result<Self::QualifiedRule, ParseError> {
576        QualifiedRuleParser::parse_block(self.nested(), prelude, start, input)?;
577        self.state = State::Body;
578        Ok(start.position())
579    }
580}
581
582#[repr(transparent)]
583#[derive(Deref, DerefMut)]
584struct NestedRuleParser<'a, 'i>(TopLevelRuleParser<'a, 'i>);
585
586struct NestedParseResult {
587    first_declaration_block: PropertyDeclarationBlock,
588    rules: Vec<CssRule>,
589}
590
591impl<'a, 'i> NestedRuleParser<'a, 'i> {
592    fn parse_at_rule_prelude(
593        &mut self,
594        rule_type: AtRuleType,
595        input: &mut Parser<'i>,
596    ) -> Result<AtRulePrelude, ParseError> {
597        Ok(match rule_type {
598            AtRuleType::Media => {
599                let media_queries = MediaList::parse(&mut self.context, input);
600                let arc = Arc::new(self.shared_lock.wrap(media_queries));
601                AtRulePrelude::Media(arc)
602            },
603            AtRuleType::Supports => {
604                let cond = SupportsCondition::parse(input)?;
605                AtRulePrelude::Supports(cond)
606            },
607            AtRuleType::FontFace => AtRulePrelude::FontFace,
608            AtRuleType::Container => {
609                let conditions = input.parse_comma_separated(|input| {
610                    ContainerCondition::parse(&self.context, input)
611                })?;
612                // Container rules must have at least one condition.
613                debug_assert!(!conditions.is_empty());
614                let conditions = ArcSlice::from_iter(conditions.into_iter());
615                AtRulePrelude::Container(conditions)
616            },
617            AtRuleType::Layer => {
618                let names = input
619                    .try_parse(|input| {
620                        input.parse_comma_separated(|input| LayerName::parse(&self.context, input))
621                    })
622                    .unwrap_or_default();
623                AtRulePrelude::Layer(names)
624            },
625            AtRuleType::FontFeatureValues => {
626                let family_names = parse_family_name_list(&self.context, input)?;
627                AtRulePrelude::FontFeatureValues(family_names)
628            },
629            AtRuleType::FontPaletteValues => {
630                let name = DashedIdent::parse(&self.context, input)?;
631                AtRulePrelude::FontPaletteValues(name)
632            },
633            AtRuleType::CounterStyle => {
634                let name = parse_counter_style_name_definition(input)?;
635                AtRulePrelude::CounterStyle(name)
636            },
637            AtRuleType::Keyframes(prefix) => {
638                let name = KeyframesName::parse(&self.context, input)?;
639                AtRulePrelude::Keyframes(name, prefix)
640            },
641            AtRuleType::Page => AtRulePrelude::Page(
642                input
643                    .try_parse(|i| PageSelectors::parse(&self.context, i))
644                    .unwrap_or_default(),
645            ),
646            AtRuleType::Property => {
647                let name = input.expect_ident_cloned()?;
648                let name = parse_custom_property_name(&name)
649                    .map_err(|_| ParseError::custom(StyleParseErrorKind::UnexpectedIdent))?;
650                AtRulePrelude::Property(PropertyRuleName(Atom::from(name)))
651            },
652            AtRuleType::Document => {
653                let cond = DocumentCondition::parse(&self.context, input)?;
654                AtRulePrelude::Document(cond)
655            },
656            AtRuleType::Scope => {
657                let bounds = ScopeBounds::parse(&self.context, input, self.parse_relative())?;
658                AtRulePrelude::Scope(bounds)
659            },
660            AtRuleType::StartingStyle => AtRulePrelude::StartingStyle,
661            AtRuleType::AppearanceBase => {
662                // We allow parsing this in chrome sheets mostly just so that
663                // browser_parsable_css.js checks UA sheets properly.
664                AtRulePrelude::AppearanceBase
665            },
666            AtRuleType::PositionTry => {
667                let name = DashedIdent::parse(&self.context, input)?;
668                AtRulePrelude::PositionTry(name)
669            },
670            AtRuleType::CustomMedia => {
671                let name = DashedIdent::parse(&self.context, input)?;
672                let condition = input
673                    .try_parse(CustomMediaCondition::parse_keyword)
674                    .unwrap_or_else(|_| {
675                        CustomMediaCondition::MediaList(Arc::new(
676                            self.shared_lock
677                                .wrap(MediaList::parse(&mut self.context, input)),
678                        ))
679                    });
680                AtRulePrelude::CustomMedia(name, condition)
681            },
682            AtRuleType::ViewTransition => AtRulePrelude::ViewTransition,
683            AtRuleType::Margin(rule_type) => AtRulePrelude::Margin(rule_type),
684            AtRuleType::Import | AtRuleType::Namespace | AtRuleType::FontFeatureValuesBlock => {
685                // @import and @namespace are only valid and handled at the
686                // top level, and @font-feature-values uses its own parser.
687                return Err(ParseError::from_basic_kind(
688                    BasicParseErrorKind::AtRuleInvalid,
689                ));
690            },
691        })
692    }
693
694    #[inline]
695    fn parse_relative(&self) -> ParseRelative {
696        self.context.nesting_context.parse_relative
697    }
698
699    // https://drafts.csswg.org/css-nesting/#conditionals
700    //     In addition to nested style rules, this specification allows nested group rules inside
701    //     of style rules: any at-rule whose body contains style rules can be nested inside of a
702    //     style rule as well.
703    fn at_rule_allowed(&self, prelude: &AtRulePrelude) -> bool {
704        match prelude {
705            AtRulePrelude::Media(..)
706            | AtRulePrelude::Supports(..)
707            | AtRulePrelude::Container(..)
708            | AtRulePrelude::Document(..)
709            | AtRulePrelude::Layer(..)
710            | AtRulePrelude::CustomMedia(..)
711            | AtRulePrelude::Scope(..)
712            | AtRulePrelude::StartingStyle
713            | AtRulePrelude::AppearanceBase => true,
714
715            AtRulePrelude::Namespace(..)
716            | AtRulePrelude::FontFace
717            | AtRulePrelude::FontFeatureValues(..)
718            | AtRulePrelude::FontPaletteValues(..)
719            | AtRulePrelude::CounterStyle(..)
720            | AtRulePrelude::Keyframes(..)
721            | AtRulePrelude::Page(..)
722            | AtRulePrelude::Property(..)
723            | AtRulePrelude::Import(..)
724            | AtRulePrelude::PositionTry(..)
725            | AtRulePrelude::ViewTransition => !self.in_style_or_page_rule(),
726            AtRulePrelude::Margin(..) => self.in_page_rule(),
727        }
728    }
729
730    fn nest_for_rule<R>(&mut self, rule_type: CssRuleType, cb: impl FnOnce(&mut Self) -> R) -> R {
731        let old = self.context.nesting_context.save(rule_type);
732        let r = cb(self);
733        self.context.nesting_context.restore(old);
734        r
735    }
736
737    fn parse_nested_rules(
738        &mut self,
739        input: &mut Parser<'i>,
740        rule_type: CssRuleType,
741    ) -> Arc<Locked<CssRules>> {
742        let rules = self
743            .parse_nested(
744                input, rule_type, /* wants_first_declaration_block = */ false,
745            )
746            .rules;
747        CssRules::new(rules, self.shared_lock)
748    }
749
750    fn parse_nested(
751        &mut self,
752        input: &mut Parser<'i>,
753        rule_type: CssRuleType,
754        wants_first_declaration_block: bool,
755    ) -> NestedParseResult {
756        debug_assert!(
757            !self.wants_first_declaration_block,
758            "Should've flushed previous declarations"
759        );
760        self.nest_for_rule(rule_type, |parser| {
761            parser.wants_first_declaration_block = wants_first_declaration_block;
762            let parse_declarations = parser.parse_declarations();
763            let mut rules = std::mem::take(&mut parser.rules);
764            let mut first_declaration_block = std::mem::take(&mut parser.first_declaration_block);
765            let mut iter = RuleBodyParser::new(input, parser);
766            while let Some(result) = iter.next() {
767                match result {
768                    Ok(()) => {},
769                    Err((error, slice, location)) => {
770                        if parse_declarations {
771                            let top = &mut **iter.parser;
772                            top.declaration_parser_state.did_error(
773                                &top.context,
774                                error,
775                                slice,
776                                location,
777                            );
778                        } else {
779                            let error = ContextualParseError::InvalidRule(slice, error);
780                            iter.parser.context.log_css_error(location, error);
781                        }
782                    },
783                }
784            }
785            parser.flush_declarations();
786            debug_assert!(
787                !parser.wants_first_declaration_block,
788                "Flushing declarations should take care of this."
789            );
790            debug_assert!(
791                !parser.declaration_parser_state.has_parsed_declarations(),
792                "Parsed but didn't consume declarations"
793            );
794            std::mem::swap(&mut parser.rules, &mut rules);
795            std::mem::swap(
796                &mut parser.first_declaration_block,
797                &mut first_declaration_block,
798            );
799            NestedParseResult {
800                first_declaration_block,
801                rules,
802            }
803        })
804    }
805
806    #[inline(never)]
807    fn handle_error_reporting_selectors_pre(
808        &mut self,
809        start: &ParserState,
810        selectors: &SelectorList<SelectorImpl>,
811    ) {
812        use cssparser::ToCss;
813        debug_assert!(self.context.error_reporting_enabled());
814        self.error_reporting_state.push(selectors.clone());
815        'selector_loop: for selector in selectors.slice().iter() {
816            let mut current = selector.iter();
817            loop {
818                let mut found_host = false;
819                let mut found_non_host = false;
820                for component in &mut current {
821                    if component.is_host() {
822                        found_host = true;
823                    } else {
824                        found_non_host = true;
825                    }
826                    if found_host && found_non_host {
827                        self.context.log_css_error(
828                            start.source_location(),
829                            ContextualParseError::NeverMatchingHostSelector(
830                                selector.to_css_string(),
831                            ),
832                        );
833                        continue 'selector_loop;
834                    }
835                }
836                if current.next_sequence().is_none() {
837                    break;
838                }
839            }
840        }
841    }
842
843    fn handle_error_reporting_selectors_post(&mut self) {
844        self.error_reporting_state.pop();
845    }
846
847    #[inline]
848    fn flush_declarations(&mut self) {
849        let parser = &mut **self;
850        let wants_first_declaration_block = parser.wants_first_declaration_block;
851        parser.wants_first_declaration_block = false;
852        parser
853            .declaration_parser_state
854            .report_errors_if_needed(&parser.context, &parser.error_reporting_state);
855        if !parser.declaration_parser_state.has_parsed_declarations() {
856            return;
857        }
858        let source_location = parser.declaration_parser_state.first_declaration_start();
859        let declarations = parser.declaration_parser_state.take_declarations();
860        if wants_first_declaration_block {
861            debug_assert!(parser.first_declaration_block.is_empty(), "How?");
862            parser.first_declaration_block = declarations;
863        } else {
864            let nested_rule = CssRule::NestedDeclarations(Arc::new(parser.shared_lock.wrap(
865                NestedDeclarationsRule {
866                    block: Arc::new(parser.shared_lock.wrap(declarations)),
867                    source_location,
868                },
869            )));
870            parser.rules.push(nested_rule);
871        }
872    }
873}
874
875impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
876    type Prelude = AtRulePrelude;
877    type AtRule = ();
878    type Error = StyleParseErrorKind;
879
880    fn parse_prelude(
881        &mut self,
882        name: CowRcStr<'i>,
883        input: &mut Parser<'i>,
884    ) -> Result<Self::Prelude, ParseError> {
885        let Some(rule_type) = AtRuleType::from_name(&name, &self.context) else {
886            return Err(ParseError::from_basic_kind(
887                BasicParseErrorKind::AtRuleInvalid,
888            ));
889        };
890
891        self.parse_at_rule_prelude(rule_type, input)
892    }
893
894    fn parse_block(
895        &mut self,
896        prelude: AtRulePrelude,
897        start: &ParserState,
898        input: &mut Parser<'i>,
899    ) -> Result<(), ParseError> {
900        if !self.at_rule_allowed(&prelude) {
901            self.dom_error = Some(RulesMutateError::HierarchyRequest);
902            return Err(ParseError::from_basic_kind(
903                BasicParseErrorKind::AtRuleInvalid,
904            ));
905        }
906        let source_location = start.source_location();
907        self.flush_declarations();
908        let rule = match prelude {
909            AtRulePrelude::FontFace => self.nest_for_rule(CssRuleType::FontFace, |p| {
910                CssRule::FontFace(Arc::new(p.shared_lock.wrap(parse_font_face_block(
911                    &p.context,
912                    input,
913                    source_location,
914                ))))
915            }),
916            AtRulePrelude::FontFeatureValues(family_names) => {
917                self.nest_for_rule(CssRuleType::FontFeatureValues, |p| {
918                    CssRule::FontFeatureValues(Arc::new(FontFeatureValuesRule::parse(
919                        &p.context,
920                        input,
921                        family_names,
922                        source_location,
923                    )))
924                })
925            },
926            AtRulePrelude::FontPaletteValues(name) => {
927                self.nest_for_rule(CssRuleType::FontPaletteValues, |p| {
928                    CssRule::FontPaletteValues(Arc::new(FontPaletteValuesRule::parse(
929                        &p.context,
930                        input,
931                        name,
932                        source_location,
933                    )))
934                })
935            },
936            AtRulePrelude::CounterStyle(name) => {
937                let body = self.nest_for_rule(CssRuleType::CounterStyle, |p| {
938                    parse_counter_style_body(name, &p.context, input, source_location)
939                })?;
940                CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(body)))
941            },
942            AtRulePrelude::Media(media_queries) => CssRule::Media(Arc::new(MediaRule {
943                media_queries,
944                rules: self.parse_nested_rules(input, CssRuleType::Media),
945                source_location,
946            })),
947            AtRulePrelude::Supports(condition) => {
948                let enabled =
949                    self.nest_for_rule(CssRuleType::Style, |p| condition.eval(&mut p.context));
950                CssRule::Supports(Arc::new(SupportsRule {
951                    condition,
952                    rules: self.parse_nested_rules(input, CssRuleType::Supports),
953                    enabled,
954                    source_location,
955                }))
956            },
957            AtRulePrelude::Keyframes(name, vendor_prefix) => {
958                self.nest_for_rule(CssRuleType::Keyframe, |p| {
959                    let top = &mut **p;
960                    CssRule::Keyframes(Arc::new(top.shared_lock.wrap(KeyframesRule {
961                        name,
962                        keyframes: parse_keyframe_list(&mut top.context, input, top.shared_lock),
963                        vendor_prefix,
964                        source_location,
965                    })))
966                })
967            },
968            AtRulePrelude::Page(selectors) => {
969                let page_rule = if !crate::pref!("layout.css.margin-rules.enabled") {
970                    let declarations = self.nest_for_rule(CssRuleType::Page, |p| {
971                        parse_property_declaration_list(&mut p.context, input, &[])
972                    });
973                    PageRule {
974                        selectors,
975                        rules: CssRules::new(vec![], self.shared_lock),
976                        block: Arc::new(self.shared_lock.wrap(declarations)),
977                        source_location,
978                    }
979                } else {
980                    let result = self.parse_nested(input, CssRuleType::Page, true);
981                    PageRule {
982                        selectors,
983                        rules: CssRules::new(result.rules, self.shared_lock),
984                        block: Arc::new(self.shared_lock.wrap(result.first_declaration_block)),
985                        source_location,
986                    }
987                };
988                CssRule::Page(Arc::new(self.shared_lock.wrap(page_rule)))
989            },
990            AtRulePrelude::Property(name) => self.nest_for_rule(CssRuleType::Property, |p| {
991                let rule_data = parse_property_block(&p.context, input, name, source_location)?;
992                Ok::<CssRule, ParseError>(CssRule::Property(Arc::new(rule_data)))
993            })?,
994            AtRulePrelude::Document(condition) => {
995                if !cfg!(feature = "gecko") {
996                    unreachable!()
997                }
998                CssRule::Document(Arc::new(DocumentRule {
999                    condition,
1000                    rules: self.parse_nested_rules(input, CssRuleType::Document),
1001                    source_location,
1002                }))
1003            },
1004            AtRulePrelude::Container(conditions) => {
1005                let source_location = start.source_location();
1006                CssRule::Container(Arc::new(ContainerRule {
1007                    conditions: ContainerConditions(conditions),
1008                    rules: self.parse_nested_rules(input, CssRuleType::Container),
1009                    source_location,
1010                }))
1011            },
1012            AtRulePrelude::Layer(names) => {
1013                let name = match names.len() {
1014                    0 | 1 => names.into_iter().next(),
1015                    _ => {
1016                        return Err(ParseError::from_basic_kind(
1017                            BasicParseErrorKind::AtRuleBodyInvalid,
1018                        ));
1019                    },
1020                };
1021                CssRule::LayerBlock(Arc::new(LayerBlockRule {
1022                    name,
1023                    rules: self.parse_nested_rules(input, CssRuleType::LayerBlock),
1024                    source_location,
1025                }))
1026            },
1027            AtRulePrelude::Margin(rule_type) => {
1028                let declarations = self.nest_for_rule(CssRuleType::Margin, |p| {
1029                    parse_property_declaration_list(&mut p.context, input, &[])
1030                });
1031                CssRule::Margin(Arc::new(MarginRule {
1032                    rule_type,
1033                    block: Arc::new(self.shared_lock.wrap(declarations)),
1034                    source_location,
1035                }))
1036            },
1037            AtRulePrelude::CustomMedia(..)
1038            | AtRulePrelude::Import(..)
1039            | AtRulePrelude::Namespace(..) => {
1040                // These rules don't have blocks.
1041                return Err(ParseError::unexpected_token());
1042            },
1043            AtRulePrelude::Scope(bounds) => CssRule::Scope(Arc::new(ScopeRule {
1044                bounds,
1045                rules: self.parse_nested_rules(input, CssRuleType::Scope),
1046                source_location,
1047            })),
1048            AtRulePrelude::StartingStyle => CssRule::StartingStyle(Arc::new(StartingStyleRule {
1049                rules: self.parse_nested_rules(input, CssRuleType::StartingStyle),
1050                source_location,
1051            })),
1052            AtRulePrelude::AppearanceBase => {
1053                CssRule::AppearanceBase(Arc::new(AppearanceBaseRule {
1054                    rules: self.parse_nested_rules(input, CssRuleType::AppearanceBase),
1055                    source_location,
1056                }))
1057            },
1058            AtRulePrelude::PositionTry(name) => {
1059                let declarations = self.nest_for_rule(CssRuleType::PositionTry, |p| {
1060                    parse_property_declaration_list(&mut p.context, input, &[])
1061                });
1062                CssRule::PositionTry(Arc::new(self.shared_lock.wrap(PositionTryRule {
1063                    name,
1064                    block: Arc::new(self.shared_lock.wrap(declarations)),
1065                    source_location,
1066                })))
1067            },
1068            AtRulePrelude::ViewTransition => self.nest_for_rule(CssRuleType::ViewTransition, |p| {
1069                CssRule::ViewTransition(Arc::new(ViewTransitionRule::parse(
1070                    &p.context,
1071                    input,
1072                    source_location,
1073                )))
1074            }),
1075        };
1076        self.rules.push(rule);
1077        Ok(())
1078    }
1079
1080    #[inline]
1081    fn rule_without_block(
1082        &mut self,
1083        prelude: AtRulePrelude,
1084        start: &ParserState,
1085    ) -> Result<(), ()> {
1086        if self.in_style_rule() {
1087            return Err(());
1088        }
1089        let source_location = start.source_location();
1090        let rule = match prelude {
1091            AtRulePrelude::CustomMedia(name, condition) => {
1092                CssRule::CustomMedia(Arc::new(CustomMediaRule {
1093                    name,
1094                    condition,
1095                    source_location,
1096                }))
1097            },
1098            AtRulePrelude::Layer(names) => {
1099                if names.is_empty() {
1100                    return Err(());
1101                }
1102                CssRule::LayerStatement(Arc::new(LayerStatementRule {
1103                    names,
1104                    source_location,
1105                }))
1106            },
1107            _ => return Err(()),
1108        };
1109        self.flush_declarations();
1110        self.rules.push(rule);
1111        Ok(())
1112    }
1113}
1114
1115impl<'a, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'i> {
1116    type Prelude = SelectorList<SelectorImpl>;
1117    type QualifiedRule = ();
1118    type Error = StyleParseErrorKind;
1119
1120    fn parse_prelude(&mut self, input: &mut Parser<'i>) -> Result<Self::Prelude, ParseError> {
1121        let selector_parser = SelectorParser {
1122            stylesheet_origin: self.context.stylesheet_origin,
1123            namespaces: &self.context.namespaces,
1124            url_data: self.context.url_data,
1125            for_supports_rule: false,
1126        };
1127        SelectorList::parse(&selector_parser, input, self.parse_relative())
1128    }
1129
1130    fn parse_block(
1131        &mut self,
1132        selectors: Self::Prelude,
1133        start: &ParserState,
1134        input: &mut Parser<'i>,
1135    ) -> Result<(), ParseError> {
1136        let source_location = start.source_location();
1137        let reporting_errors = self.context.error_reporting_enabled();
1138        if reporting_errors {
1139            self.handle_error_reporting_selectors_pre(start, &selectors);
1140        }
1141        self.flush_declarations();
1142        let result = self.parse_nested(input, CssRuleType::Style, true);
1143        if reporting_errors {
1144            self.handle_error_reporting_selectors_post();
1145        }
1146        let block = Arc::new(self.shared_lock.wrap(result.first_declaration_block));
1147        let top = &mut **self;
1148        top.rules
1149            .push(CssRule::Style(Arc::new(top.shared_lock.wrap(StyleRule {
1150                selectors,
1151                block,
1152                rules: if result.rules.is_empty() {
1153                    None
1154                } else {
1155                    Some(CssRules::new(result.rules, top.shared_lock))
1156                },
1157                source_location,
1158            }))));
1159        Ok(())
1160    }
1161}
1162
1163impl<'a, 'i> DeclarationParser<'i> for NestedRuleParser<'a, 'i> {
1164    type Declaration = ();
1165    type Error = StyleParseErrorKind;
1166    fn parse_value(
1167        &mut self,
1168        name: CowRcStr<'i>,
1169        input: &mut Parser<'i>,
1170        declaration_start: &ParserState,
1171    ) -> Result<(), ParseError> {
1172        let top = &mut **self;
1173        top.declaration_parser_state
1174            .parse_value(&mut top.context, name, input, declaration_start)
1175    }
1176}
1177
1178impl<'a, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind> for NestedRuleParser<'a, 'i> {
1179    fn parse_qualified(&self) -> bool {
1180        true
1181    }
1182
1183    /// If nesting is disabled, we can't get there for a non-style-rule. If it's enabled, we parse
1184    /// raw declarations there.
1185    fn parse_declarations(&self) -> bool {
1186        self.can_parse_declarations()
1187    }
1188}