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