1use 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
49pub struct InsertRuleContext<'a> {
51 pub rule_list: &'a [CssRule],
53 pub index: usize,
55 pub containing_rule_types: CssRuleTypes,
57 pub parse_relative_rule_type: Option<CssRuleType>,
59}
60
61impl<'a> InsertRuleContext<'a> {
62 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 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
91pub struct TopLevelRuleParser<'a, 'i> {
93 pub shared_lock: &'a SharedRwLock,
95 pub loader: Option<&'a dyn StylesheetLoader>,
97 pub context: ParserContext<'a>,
99 pub state: State,
101 pub dom_error: Option<RulesMutateError>,
105 pub insert_rule_context: Option<InsertRuleContext<'a>>,
107 pub allow_import_rules: AllowImportRules,
109 pub wants_first_declaration_block: bool,
112 pub first_declaration_block: PropertyDeclarationBlock,
114 pub declaration_parser_state: DeclarationParserState<'i>,
116 pub error_reporting_state: Vec<SelectorList<SelectorImpl>>,
121 pub rules: Vec<CssRule>,
123}
124
125impl<'a, 'i> TopLevelRuleParser<'a, 'i> {
126 #[inline]
127 fn nested(&mut self) -> &mut NestedRuleParser<'a, 'i> {
128 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 #[inline]
142 pub fn state(&self) -> State {
143 self.state
144 }
145
146 #[inline]
149 pub fn can_parse_declarations(&self) -> bool {
150 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 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 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#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
227pub enum State {
228 Start = 1,
230 EarlyLayers = 2,
232 Imports = 3,
234 Namespaces = 4,
236 Body = 5,
238}
239
240#[derive(Clone, Debug, MallocSizeOf, ToShmem)]
241pub enum VendorPrefix {
243 Moz,
245 WebKit,
247}
248
249pub enum AtRulePrelude {
251 FontFace,
253 FontFeatureValues(Vec<FamilyName>),
255 FontPaletteValues(DashedIdent),
257 CounterStyle(CustomIdent),
259 Media(Arc<Locked<MediaList>>),
261 Container(ArcSlice<ContainerCondition>),
263 Supports(SupportsCondition),
265 Keyframes(KeyframesName, Option<VendorPrefix>),
267 Page(PageSelectors),
269 Property(PropertyRuleName),
271 Document(DocumentCondition),
273 Import(
275 CssUrl,
276 Arc<Locked<MediaList>>,
277 Option<ImportSupportsCondition>,
278 ImportLayer,
279 ),
280 Margin(MarginRuleType),
282 Namespace(Option<Prefix>, Namespace),
284 Layer(Vec<LayerName>),
286 Scope(ScopeBounds),
288 StartingStyle,
290 AppearanceBase,
292 PositionTry(DashedIdent),
294 CustomMedia(DashedIdent, CustomMediaCondition),
296 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 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" => {
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 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 }
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 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, 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 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 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 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 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 fn parse_declarations(&self) -> bool {
1110 self.can_parse_declarations()
1111 }
1112}