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