From 7c4ec6e9cc2970df7cf127c42e5aa3165d15902f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Wed, 17 May 2023 18:18:29 +0000 Subject: [PATCH] style: [css-nesting] Do a first pass at parsing nested rules mixed with declarations Plumb everything up. This factors out declaration and rule parsing so we share the code with the regular declaration parser. This could be made a bit nicer in the future. We need to decide what to do for @page and @keyframe (it seems conditional rules inside ought to work, but that's not so easy because per spec we create a nested style rule). But this is a first pass that passes a good chunk of the tests. There are other fixups to cssom, and I think some of the tests we fail are actually wrong... Differential Revision: https://phabricator.services.mozilla.com/D178266 --- components/selectors/parser.rs | 17 + components/style/gecko/selector_parser.rs | 4 + components/style/parser.rs | 21 +- .../style/properties/declaration_block.rs | 217 ++++++---- .../style/properties/properties.mako.rs | 51 ++- components/style/stylesheets/document_rule.rs | 10 +- .../stylesheets/font_feature_values_rule.rs | 2 +- .../style/stylesheets/keyframes_rule.rs | 4 +- components/style/stylesheets/mod.rs | 41 +- components/style/stylesheets/rule_parser.rs | 405 ++++++++++++------ components/style/stylesheets/stylesheet.rs | 31 +- components/style/stylesheets/supports_rule.rs | 4 +- components/style_traits/lib.rs | 2 - 13 files changed, 523 insertions(+), 286 deletions(-) diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index f9f316b52b3..6a963211475 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -392,6 +392,11 @@ enum ParseRelative { } impl SelectorList { + /// Returns a selector list with a single `&` + pub fn ampersand() -> Self { + Self(smallvec::smallvec![Selector::ampersand()]) + } + /// Parse a comma-separated list of Selectors. /// /// @@ -647,6 +652,18 @@ pub struct Selector( ); impl Selector { + /// See Arc::mark_as_intentionally_leaked + pub fn mark_as_intentionally_leaked(&self) { + self.0.with_arc(|a| a.mark_as_intentionally_leaked()) + } + + fn ampersand() -> Self { + Self(ThinArc::from_header_and_iter(SpecificityAndFlags { + specificity: 0, + flags: SelectorFlags::HAS_PARENT, + }, std::iter::once(Component::ParentSelector))) + } + #[inline] pub fn specificity(&self) -> u32 { self.0.header.header.specificity() diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index 3ec9fbd0c0e..9be8bf6623d 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -311,6 +311,10 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { type Impl = SelectorImpl; type Error = StyleParseErrorKind<'i>; + fn parse_parent_selector(&self) -> bool { + static_prefs::pref!("layout.css.nesting.enabled") + } + #[inline] fn parse_slotted(&self) -> bool { true diff --git a/components/style/parser.rs b/components/style/parser.rs index 11ef4b51a68..cb97f12d368 100644 --- a/components/style/parser.rs +++ b/components/style/parser.rs @@ -6,7 +6,7 @@ use crate::context::QuirksMode; use crate::error_reporting::{ContextualParseError, ParseErrorReporter}; -use crate::stylesheets::{CssRuleType, Namespaces, Origin, UrlExtraData}; +use crate::stylesheets::{CssRuleType, CssRuleTypes, Namespaces, Origin, UrlExtraData}; use crate::use_counters::UseCounters; use cssparser::{Parser, SourceLocation, UnicodeRange}; use std::borrow::Cow; @@ -45,8 +45,8 @@ pub struct ParserContext<'a> { pub stylesheet_origin: Origin, /// The extra data we need for resolving url values. pub url_data: &'a UrlExtraData, - /// The current rule type, if any. - pub rule_type: Option, + /// The current rule types, if any. + pub rule_types: CssRuleTypes, /// The mode to use when parsing. pub parsing_mode: ParsingMode, /// The quirks mode of this stylesheet. @@ -75,7 +75,7 @@ impl<'a> ParserContext<'a> { Self { stylesheet_origin, url_data, - rule_type, + rule_types: rule_type.map(CssRuleTypes::from).unwrap_or_default(), parsing_mode, quirks_mode, error_reporter, @@ -86,23 +86,22 @@ impl<'a> ParserContext<'a> { /// Temporarily sets the rule_type and executes the callback function, returning its result. pub fn nest_for_rule(&mut self, rule_type: CssRuleType, cb: impl FnOnce(&mut Self) -> R) -> R { - let old_rule_type = std::mem::replace(&mut self.rule_type, Some(rule_type)); + let old_rule_types = self.rule_types; + self.rule_types.insert(rule_type); let r = cb(self); - self.rule_type = old_rule_type; + self.rule_types = old_rule_types; r } /// Whether we're in a @page rule. #[inline] pub fn in_page_rule(&self) -> bool { - self.rule_type - .map_or(false, |rule_type| rule_type == CssRuleType::Page) + self.rule_types.contains(CssRuleType::Page) } /// Get the rule type, which assumes that one is available. - pub fn rule_type(&self) -> CssRuleType { - self.rule_type - .expect("Rule type expected, but none was found.") + pub fn rule_types(&self) -> CssRuleTypes { + self.rule_types } /// Returns whether CSS error reporting is enabled. diff --git a/components/style/properties/declaration_block.rs b/components/style/properties/declaration_block.rs index 68f69f04eda..77b359b5d3d 100644 --- a/components/style/properties/declaration_block.rs +++ b/components/style/properties/declaration_block.rs @@ -92,12 +92,18 @@ pub enum Importance { Important, } +impl Default for Importance { + fn default() -> Self { + Self::Normal + } +} + impl Importance { /// Return whether this is an important declaration. pub fn important(self) -> bool { match self { - Importance::Normal => false, - Importance::Important => true, + Self::Normal => false, + Self::Important => true, } } } @@ -146,7 +152,7 @@ impl PropertyDeclarationIdSet { /// Overridden declarations are skipped. #[cfg_attr(feature = "gecko", derive(MallocSizeOf))] -#[derive(Clone, ToShmem)] +#[derive(Clone, ToShmem, Default)] pub struct PropertyDeclarationBlock { /// The group of declarations, along with their importance. /// @@ -284,6 +290,12 @@ impl PropertyDeclarationBlock { self.declarations.len() } + /// Returns whether the block is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.declarations.is_empty() + } + /// Create an empty block #[inline] pub fn new() -> Self { @@ -1357,24 +1369,127 @@ pub fn parse_one_declaration_into( } /// A struct to parse property declarations. -struct PropertyDeclarationParser<'a, 'b: 'a> { +struct PropertyDeclarationParser<'a, 'b: 'a, 'i> { context: &'a ParserContext<'b>, - declarations: &'a mut SourcePropertyDeclaration, - /// The last parsed property id if any. + state: &'a mut DeclarationParserState<'i>, +} + +/// The state needed to parse a declaration block. +/// +/// It stores declarations in output_block. +#[derive(Default)] +pub struct DeclarationParserState<'i> { + /// The output block where results are stored. + output_block: PropertyDeclarationBlock, + /// Declarations from the last declaration parsed. (note that a shorthand might expand to + /// multiple declarations). + declarations: SourcePropertyDeclaration, + /// The importance from the last declaration parsed. + importance: Importance, + /// A list of errors that have happened so far. Not all of them might be reported. + errors: SmallParseErrorVec<'i>, + /// The last parsed property id, if any. last_parsed_property_id: Option, } +impl<'i> DeclarationParserState<'i> { + /// Returns whether any parsed declarations have been parsed so far. + pub fn has_parsed_declarations(&self) -> bool { + !self.output_block.is_empty() + } + + /// Takes the parsed declarations. + pub fn take_declarations(&mut self) -> PropertyDeclarationBlock { + std::mem::take(&mut self.output_block) + } + + /// Parse a single declaration value. + pub fn parse_value<'t>( + &mut self, + context: &ParserContext, + name: CowRcStr<'i>, + input: &mut Parser<'i, 't>, + ) -> Result<(), ParseError<'i>> { + let id = match PropertyId::parse(&name, context) { + Ok(id) => id, + Err(..) => { + return Err(input.new_custom_error(StyleParseErrorKind::UnknownProperty(name))); + }, + }; + if context.error_reporting_enabled() { + self.last_parsed_property_id = Some(id.clone()); + } + input.parse_until_before(Delimiter::Bang, |input| { + PropertyDeclaration::parse_into(&mut self.declarations, id, context, input) + })?; + self.importance = match input.try_parse(parse_important) { + Ok(()) => Importance::Important, + Err(_) => Importance::Normal, + }; + // In case there is still unparsed text in the declaration, we should roll back. + input.expect_exhausted()?; + self.output_block.extend(self.declarations.drain(), self.importance); + // We've successfully parsed a declaration, so forget about + // `last_parsed_property_id`. It'd be wrong to associate any + // following error with this property. + self.last_parsed_property_id = None; + Ok(()) + } + + /// Reports any CSS errors that have ocurred if needed. + #[inline] + pub fn report_errors_if_needed( + &mut self, + context: &ParserContext, + selectors: Option<&SelectorList>, + ) { + if self.errors.is_empty() { + return; + } + self.do_report_css_errors(context, selectors); + } + + #[cold] + fn do_report_css_errors( + &mut self, + context: &ParserContext, + selectors: Option<&SelectorList>, + ) { + for (error, slice, property) in self.errors.drain(..) { + report_one_css_error( + context, + Some(&self.output_block), + selectors, + error, + slice, + property, + ) + } + } + + /// Resets the declaration parser state, and reports the error if needed. + #[inline] + pub fn did_error(&mut self, context: &ParserContext, error: ParseError<'i>, slice: &'i str) { + self.declarations.clear(); + if !context.error_reporting_enabled() { + return; + } + let property = self.last_parsed_property_id.take(); + self.errors.push((error, slice, property)); + } +} + /// Default methods reject all at rules. -impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'b> { +impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> { type Prelude = (); - type AtRule = Importance; + type AtRule = (); type Error = StyleParseErrorKind<'i>; } /// Default methods reject all rules. -impl<'a, 'b, 'i> QualifiedRuleParser<'i> for PropertyDeclarationParser<'a, 'b> { +impl<'a, 'b, 'i> QualifiedRuleParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> { type Prelude = (); - type QualifiedRule = Importance; + type QualifiedRule = (); type Error = StyleParseErrorKind<'i>; } @@ -1383,41 +1498,29 @@ fn is_non_mozilla_vendor_identifier(name: &str) -> bool { (name.starts_with("-") && !name.starts_with("-moz-")) || name.starts_with("_") } -impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b> { - type Declaration = Importance; +impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> { + type Declaration = (); type Error = StyleParseErrorKind<'i>; fn parse_value<'t>( &mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>, - ) -> Result> { - let id = match PropertyId::parse(&name, self.context) { - Ok(id) => id, - Err(..) => { - return Err(input.new_custom_error(StyleParseErrorKind::UnknownProperty(name))); - }, - }; - if self.context.error_reporting_enabled() { - self.last_parsed_property_id = Some(id.clone()); - } - input.parse_until_before(Delimiter::Bang, |input| { - PropertyDeclaration::parse_into(self.declarations, id, self.context, input) - })?; - let importance = match input.try_parse(parse_important) { - Ok(()) => Importance::Important, - Err(_) => Importance::Normal, - }; - // In case there is still unparsed text in the declaration, we should roll back. - input.expect_exhausted()?; - Ok(importance) + ) -> Result<(), ParseError<'i>> { + self.state.parse_value(self.context, name, input) } } -impl<'a, 'b, 'i> RuleBodyItemParser<'i, Importance, StyleParseErrorKind<'i>> for PropertyDeclarationParser<'a, 'b> { - fn parse_declarations(&self) -> bool { true } +impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> + for PropertyDeclarationParser<'a, 'b, 'i> +{ + fn parse_declarations(&self) -> bool { + true + } // TODO(emilio): Nesting. - fn parse_qualified(&self) -> bool { false } + fn parse_qualified(&self) -> bool { + false + } } type SmallParseErrorVec<'i> = SmallVec<[(ParseError<'i>, &'i str, Option); 2]>; @@ -1486,18 +1589,6 @@ fn report_one_css_error<'i>( context.log_css_error(location, error); } -#[cold] -fn report_css_errors( - context: &ParserContext, - block: &PropertyDeclarationBlock, - selectors: Option<&SelectorList>, - errors: &mut SmallParseErrorVec, -) { - for (error, slice, property) in errors.drain(..) { - report_one_css_error(context, Some(block), selectors, error, slice, property) - } -} - /// Parse a list of property declarations and return a property declaration /// block. pub fn parse_property_declaration_list( @@ -1505,38 +1596,18 @@ pub fn parse_property_declaration_list( input: &mut Parser, selectors: Option<&SelectorList>, ) -> PropertyDeclarationBlock { - let mut declarations = SourcePropertyDeclaration::new(); - let mut block = PropertyDeclarationBlock::new(); + let mut state = DeclarationParserState::default(); let mut parser = PropertyDeclarationParser { context, - last_parsed_property_id: None, - declarations: &mut declarations, + state: &mut state, }; let mut iter = RuleBodyParser::new(input, &mut parser); - let mut errors = SmallParseErrorVec::new(); while let Some(declaration) = iter.next() { match declaration { - Ok(importance) => { - block.extend(iter.parser.declarations.drain(), importance); - // We've successfully parsed a declaration, so forget about - // `last_parsed_property_id`. It'd be wrong to associate any - // following error with this property. - iter.parser.last_parsed_property_id = None; - }, - Err((error, slice)) => { - iter.parser.declarations.clear(); - - if context.error_reporting_enabled() { - let property = iter.parser.last_parsed_property_id.take(); - errors.push((error, slice, property)); - } - }, + Ok(()) => {}, + Err((error, slice)) => iter.parser.state.did_error(context, error, slice), } } - - if !errors.is_empty() { - report_css_errors(context, &block, selectors, &mut errors) - } - - block + parser.state.report_errors_if_needed(context, selectors); + state.output_block } diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index a6d713193c1..20baba8a3f0 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -37,7 +37,7 @@ use crate::selector_parser::PseudoElement; use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode}; use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; use to_shmem::impl_trivial_to_shmem; -use crate::stylesheets::{CssRuleType, Origin, UrlExtraData}; +use crate::stylesheets::{CssRuleType, CssRuleTypes, Origin, UrlExtraData}; use crate::use_counters::UseCounters; use crate::values::generics::text::LineHeight; use crate::values::{computed, resolved}; @@ -572,30 +572,29 @@ impl NonCustomPropertyId { /// Returns whether a given rule allows a given property. #[inline] - pub fn allowed_in_rule(self, rule_type: CssRuleType) -> bool { + pub fn allowed_in_rule(self, rule_types: CssRuleTypes) -> bool { debug_assert!( - matches!( - rule_type, - CssRuleType::Keyframe | CssRuleType::Page | CssRuleType::Style - ), + rule_types.contains(CssRuleType::Keyframe) || + rule_types.contains(CssRuleType::Page) || + rule_types.contains(CssRuleType::Style), "Declarations are only expected inside a keyframe, page, or style rule." ); - static MAP: [u8; NON_CUSTOM_PROPERTY_ID_COUNT] = [ + static MAP: [u32; NON_CUSTOM_PROPERTY_ID_COUNT] = [ % for property in data.longhands + data.shorthands + data.all_aliases(): - ${property.rule_types_allowed}, + % for name in RULE_VALUES: + % if property.rule_types_allowed & RULE_VALUES[name] != 0: + CssRuleType::${name}.bit() | + % endif + % endfor + 0, % endfor ]; - match rule_type { - % for name in RULE_VALUES: - CssRuleType::${name} => MAP[self.0] & ${RULE_VALUES[name]} != 0, - % endfor - _ => true - } + MAP[self.0] & rule_types.bits() != 0 } fn allowed_in(self, context: &ParserContext) -> bool { - if !self.allowed_in_rule(context.rule_type()) { + if !self.allowed_in_rule(context.rule_types()) { return false; } @@ -728,7 +727,7 @@ impl From for NonCustomPropertyId { } /// A set of all properties -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Default)] pub struct NonCustomPropertyIdSet { storage: [u32; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + 32) / 32] } @@ -1792,7 +1791,7 @@ impl UnparsedValue { Some(shorthand) => shorthand, }; - let mut decls = SourcePropertyDeclaration::new(); + let mut decls = SourcePropertyDeclaration::default(); // parse_into takes care of doing `parse_entirely` for us. if shorthand.parse_into(&mut decls, &context, &mut input).is_err() { return invalid_at_computed_value_time(); @@ -2606,6 +2605,7 @@ pub type SubpropertiesVec = ArrayVec; /// A stack-allocated vector of `PropertyDeclaration` /// large enough to parse one CSS `key: value` declaration. /// (Shorthands expand to multiple `PropertyDeclaration`s.) +#[derive(Default)] pub struct SourcePropertyDeclaration { /// The storage for the actual declarations (except for all). pub declarations: SubpropertiesVec, @@ -2618,19 +2618,10 @@ pub struct SourcePropertyDeclaration { size_of_test!(SourcePropertyDeclaration, 568); impl SourcePropertyDeclaration { - /// Create one. It’s big, try not to move it around. - #[inline] - pub fn new() -> Self { - SourcePropertyDeclaration { - declarations: ::arrayvec::ArrayVec::new(), - all_shorthand: AllShorthand::NotSet, - } - } - /// Create one with a single PropertyDeclaration. #[inline] pub fn with_one(decl: PropertyDeclaration) -> Self { - let mut result = Self::new(); + let mut result = Self::default(); result.declarations.push(decl); result } @@ -2677,6 +2668,12 @@ pub enum AllShorthand { WithVariables(Arc) } +impl Default for AllShorthand { + fn default() -> Self { + Self::NotSet + } +} + impl AllShorthand { /// Iterates property declarations from the given all shorthand value. #[inline] diff --git a/components/style/stylesheets/document_rule.rs b/components/style/stylesheets/document_rule.rs index baf2dd21e71..871c7a791ed 100644 --- a/components/style/stylesheets/document_rule.rs +++ b/components/style/stylesheets/document_rule.rs @@ -13,7 +13,7 @@ use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; use crate::str::CssStringWriter; use crate::stylesheets::CssRules; use crate::values::CssUrl; -use cssparser::{Parser, SourceLocation}; +use cssparser::{Parser, SourceLocation, BasicParseErrorKind}; #[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; use servo_arc::Arc; @@ -260,11 +260,9 @@ impl DocumentCondition { let condition = DocumentCondition(conditions); if !condition.allowed_in(context) { - return Err( - input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule( - "-moz-document".into(), - )), - ); + return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid( + "-moz-document".into(), + ))); } Ok(condition) } diff --git a/components/style/stylesheets/font_feature_values_rule.rs b/components/style/stylesheets/font_feature_values_rule.rs index f5ce737e068..f6ea3a1f477 100644 --- a/components/style/stylesheets/font_feature_values_rule.rs +++ b/components/style/stylesheets/font_feature_values_rule.rs @@ -410,7 +410,7 @@ macro_rules! font_feature_values_blocks { _: &ParserState, input: &mut Parser<'i, 't> ) -> Result> { - debug_assert_eq!(self.context.rule_type(), CssRuleType::FontFeatureValues); + debug_assert!(self.context.rule_types().contains(CssRuleType::FontFeatureValues)); match prelude { $( BlockType::$ident_camel => { diff --git a/components/style/stylesheets/keyframes_rule.rs b/components/style/stylesheets/keyframes_rule.rs index 0b61ba65528..9b4caaa0fd6 100644 --- a/components/style/stylesheets/keyframes_rule.rs +++ b/components/style/stylesheets/keyframes_rule.rs @@ -225,7 +225,7 @@ impl Keyframe { let mut input = ParserInput::new(css); let mut input = Parser::new(&mut input); - let mut declarations = SourcePropertyDeclaration::new(); + let mut declarations = SourcePropertyDeclaration::default(); let mut rule_parser = KeyframeListParser { context: &mut context, shared_lock: &lock, @@ -539,7 +539,7 @@ pub fn parse_keyframe_list<'a>( input: &mut Parser, shared_lock: &SharedRwLock, ) -> Vec>> { - let mut declarations = SourcePropertyDeclaration::new(); + let mut declarations = SourcePropertyDeclaration::default(); let mut parser = KeyframeListParser { context, shared_lock, diff --git a/components/style/stylesheets/mod.rs b/components/style/stylesheets/mod.rs index d2d6252ac91..4c6027219ba 100644 --- a/components/style/stylesheets/mod.rs +++ b/components/style/stylesheets/mod.rs @@ -352,6 +352,43 @@ pub enum CssRuleType { FontPaletteValues = 19, } +impl CssRuleType { + /// Returns a bit that identifies this rule type. + #[inline] + pub const fn bit(self) -> u32 { + 1 << self as u32 + } +} + +/// Set of rule types. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct CssRuleTypes(u32); + +impl From for CssRuleTypes { + fn from(ty: CssRuleType) -> Self { + Self(ty.bit()) + } +} + +impl CssRuleTypes { + /// Returns whether the rule is in the current set. + #[inline] + pub fn contains(self, ty: CssRuleType) -> bool { + self.0 & ty.bit() != 0 + } + + /// Returns all the rules specified in the set. + pub fn bits(self) -> u32 { + self.0 + } + + /// Inserts a rule type into the set. + #[inline] + pub fn insert(&mut self, ty: CssRuleType) { + self.0 |= ty.bit() + } +} + #[allow(missing_docs)] pub enum RulesMutateError { Syntax, @@ -422,10 +459,12 @@ impl CssRule { dom_error: None, insert_rule_context: Some(insert_rule_context), allow_import_rules, + declaration_parser_state: Default::default(), + rules: Default::default(), }; match parse_one_rule(&mut input, &mut rule_parser) { - Ok((_, rule)) => Ok(rule), + Ok(_) => Ok(rule_parser.rules.pop().unwrap()), Err(_) => Err(rule_parser.dom_error.unwrap_or(RulesMutateError::Syntax)), } } diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs index 19c1720c16c..0e8b6bb3e18 100644 --- a/components/style/stylesheets/rule_parser.rs +++ b/components/style/stylesheets/rule_parser.rs @@ -9,14 +9,16 @@ use crate::error_reporting::ContextualParseError; use crate::font_face::parse_font_face_block; use crate::media_queries::MediaList; use crate::parser::{Parse, ParserContext}; -use crate::properties::parse_property_declaration_list; +use crate::properties::declaration_block::{ + parse_property_declaration_list, DeclarationParserState, PropertyDeclarationBlock, +}; use crate::selector_parser::{SelectorImpl, SelectorParser}; use crate::shared_lock::{Locked, SharedRwLock}; use crate::str::starts_with_ignore_ascii_case; use crate::stylesheets::container_rule::{ContainerCondition, ContainerRule}; use crate::stylesheets::document_rule::DocumentCondition; use crate::stylesheets::font_feature_values_rule::parse_family_name_list; -use crate::stylesheets::import_rule::{ImportRule, ImportLayer, ImportSupportsCondition}; +use crate::stylesheets::import_rule::{ImportLayer, ImportRule, ImportSupportsCondition}; use crate::stylesheets::keyframes_rule::parse_keyframe_list; use crate::stylesheets::layer_rule::{LayerBlockRule, LayerName, LayerStatementRule}; use crate::stylesheets::supports_rule::SupportsCondition; @@ -31,7 +33,8 @@ use crate::values::{CssUrl, CustomIdent, DashedIdent, KeyframesName}; use crate::{Namespace, Prefix}; use cssparser::{ AtRuleParser, BasicParseError, BasicParseErrorKind, CowRcStr, DeclarationParser, Parser, - ParserState, QualifiedRuleParser, RuleBodyParser, RuleBodyItemParser, SourcePosition, + ParserState, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, + SourcePosition, }; use selectors::SelectorList; use servo_arc::Arc; @@ -76,7 +79,7 @@ impl<'a> InsertRuleContext<'a> { } /// The parser for the top-level rules in a stylesheet. -pub struct TopLevelRuleParser<'a> { +pub struct TopLevelRuleParser<'a, 'i> { /// A reference to the lock we need to use to create rules. pub shared_lock: &'a SharedRwLock, /// A reference to a stylesheet loader if applicable, for `@import` rules. @@ -93,13 +96,19 @@ pub struct TopLevelRuleParser<'a> { pub insert_rule_context: Option>, /// Whether @import rules will be allowed. pub allow_import_rules: AllowImportRules, + /// Parser state for declaration blocks in either nested rules or style rules. + pub declaration_parser_state: DeclarationParserState<'i>, + /// The rules we've parsed so far. + pub rules: Vec, } -impl<'a> TopLevelRuleParser<'a> { - fn nested<'b>(&'b mut self) -> NestedRuleParser<'b, 'a> { +impl<'a, 'i> TopLevelRuleParser<'a, 'i> { + fn nested<'b>(&'b mut self) -> NestedRuleParser<'b, 'a, 'i> { NestedRuleParser { shared_lock: self.shared_lock, context: &mut self.context, + declaration_parser_state: &mut self.declaration_parser_state, + rules: &mut self.rules, } } @@ -133,8 +142,8 @@ impl<'a> TopLevelRuleParser<'a> { // If there's anything that isn't a namespace rule (or import rule, but // we checked that already at the beginning), reject with a // StateError. - if new_state == State::Namespaces && - ctx.rule_list[ctx.index..] + if new_state == State::Namespaces + && ctx.rule_list[ctx.index..] .iter() .any(|r| !matches!(*r, CssRule::Namespace(..))) { @@ -195,16 +204,21 @@ pub enum AtRulePrelude { /// A @document rule, with its conditional. Document(DocumentCondition), /// A @import rule prelude. - Import(CssUrl, Arc>, Option, ImportLayer), + Import( + CssUrl, + Arc>, + Option, + ImportLayer, + ), /// A @namespace rule prelude. Namespace(Option, Namespace), /// A @layer rule prelude. Layer(Vec), } -impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> { +impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i> { type Prelude = AtRulePrelude; - type AtRule = (SourcePosition, CssRule); + type AtRule = SourcePosition; type Error = StyleParseErrorKind<'i>; fn parse_prelude<'t>( @@ -294,9 +308,9 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> { if !self.check_state(State::Body) { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); } - let rule = AtRuleParser::parse_block(&mut self.nested(), prelude, start, input)?; + AtRuleParser::parse_block(&mut self.nested(), prelude, start, input)?; self.state = State::Body; - Ok((start.position(), rule)) + Ok(start.position()) } #[inline] @@ -305,7 +319,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> { prelude: AtRulePrelude, start: &ParserState, ) -> Result { - let rule = match prelude { + match prelude { AtRulePrelude::Import(url, media, supports, layer) => { let loader = self .loader @@ -322,7 +336,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> { ); self.state = State::Imports; - CssRule::Import(import_rule) + self.rules.push(CssRule::Import(import_rule)) }, AtRulePrelude::Namespace(prefix, url) => { let namespaces = self.context.namespaces.to_mut(); @@ -335,34 +349,33 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> { }; self.state = State::Namespaces; - CssRule::Namespace(Arc::new(self.shared_lock.wrap(NamespaceRule { - prefix, - url, - source_location: start.source_location(), - }))) + self.rules + .push(CssRule::Namespace(Arc::new(self.shared_lock.wrap( + NamespaceRule { + prefix, + url, + source_location: start.source_location(), + }, + )))); }, - AtRulePrelude::Layer(ref names) => { - if names.is_empty() { - return Err(()); - } + AtRulePrelude::Layer(..) => { + AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)?; if self.state <= State::EarlyLayers { self.state = State::EarlyLayers; } else { self.state = State::Body; } - AtRuleParser::rule_without_block(&mut self.nested(), prelude, start) - .expect("All validity checks on the nested parser should be done before changing self.state") }, _ => AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)?, }; - Ok((start.position(), rule)) + Ok(start.position()) } } -impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> { +impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, 'i> { type Prelude = SelectorList; - type QualifiedRule = (SourcePosition, CssRule); + type QualifiedRule = SourcePosition; type Error = StyleParseErrorKind<'i>; #[inline] @@ -384,45 +397,117 @@ impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> { start: &ParserState, input: &mut Parser<'i, 't>, ) -> Result> { - let rule = QualifiedRuleParser::parse_block(&mut self.nested(), prelude, start, input)?; + QualifiedRuleParser::parse_block(&mut self.nested(), prelude, start, input)?; self.state = State::Body; - Ok((start.position(), rule)) + Ok(start.position()) } } -struct NestedRuleParser<'a, 'b: 'a> { +struct NestedRuleParser<'a, 'b: 'a, 'i> { shared_lock: &'a SharedRwLock, context: &'a mut ParserContext<'b>, + declaration_parser_state: &'a mut DeclarationParserState<'i>, + rules: &'a mut Vec, } -impl<'a, 'b> NestedRuleParser<'a, 'b> { +struct NestedParseResult { + rules: Vec, + declarations: PropertyDeclarationBlock, +} + +impl NestedParseResult { + fn into_rules( + mut self, + shared_lock: &SharedRwLock, + source_location: SourceLocation, + ) -> Arc> { + lazy_static! { + static ref AMPERSAND: SelectorList = { + let list = SelectorList::ampersand(); + list.0 + .iter() + .for_each(|selector| selector.mark_as_intentionally_leaked()); + list + }; + }; + + if !self.declarations.is_empty() { + self.rules.insert( + 0, + CssRule::Style(Arc::new(shared_lock.wrap(StyleRule { + selectors: AMPERSAND.clone(), + block: Arc::new(shared_lock.wrap(self.declarations)), + rules: None, + source_location, + }))), + ) + } + + CssRules::new(self.rules, shared_lock) + } +} + +impl<'a, 'b, 'i> NestedRuleParser<'a, 'b, 'i> { + /// When nesting is disabled, we prevent parsing at rules and qualified rules inside style + /// rules. + fn allow_at_and_qualified_rules(&self) -> bool { + if !self.context.rule_types.contains(CssRuleType::Style) { + return true; + } + static_prefs::pref!("layout.css.nesting.enabled") + } + fn nest_for_rule(&mut self, rule_type: CssRuleType, cb: impl FnOnce(&mut Self) -> R) -> R { - let old_rule_type = self.context.rule_type.take(); - self.context.rule_type = Some(rule_type); + let old_rule_types = self.context.rule_types; + self.context.rule_types.insert(rule_type); let r = cb(self); - self.context.rule_type = old_rule_type; + self.context.rule_types = old_rule_types; r } - fn parse_nested_rules( + fn parse_nested( &mut self, - input: &mut Parser, + input: &mut Parser<'i, '_>, rule_type: CssRuleType, - ) -> Arc> { + selectors: Option<&SelectorList>, + ) -> NestedParseResult { self.nest_for_rule(rule_type, |parser| { + let parse_declarations = parser.parse_declarations(); + let mut old_declaration_state = std::mem::take(parser.declaration_parser_state); + let mut rules = std::mem::take(parser.rules); let mut iter = RuleBodyParser::new(input, parser); - let mut rules = Vec::new(); while let Some(result) = iter.next() { match result { - Ok(rule) => rules.push(rule), + Ok(()) => {}, Err((error, slice)) => { - let location = error.location; - let error = ContextualParseError::InvalidRule(slice, error); - iter.parser.context.log_css_error(location, error); + if parse_declarations { + iter.parser.declaration_parser_state.did_error(iter.parser.context, error, slice); + } else { + let location = error.location; + let error = ContextualParseError::InvalidRule(slice, error); + iter.parser.context.log_css_error(location, error); + } }, } } - CssRules::new(rules, iter.parser.shared_lock) + let declarations = if parse_declarations { + parser + .declaration_parser_state + .report_errors_if_needed(parser.context, selectors); + parser.declaration_parser_state.take_declarations() + } else { + PropertyDeclarationBlock::default() + }; + debug_assert!( + !parser.declaration_parser_state.has_parsed_declarations(), + "Parsed but didn't consume declarations" + ); + std::mem::swap(parser.declaration_parser_state, &mut old_declaration_state); + std::mem::swap(parser.rules, &mut rules); + NestedParseResult { + rules, + declarations, + } }) } } @@ -437,9 +522,9 @@ fn container_queries_enabled() -> bool { .unwrap_or(false); } -impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> { +impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b, 'i> { type Prelude = AtRulePrelude; - type AtRule = CssRule; + type AtRule = (); type Error = StyleParseErrorKind<'i>; fn parse_prelude<'t>( @@ -447,6 +532,9 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> { name: CowRcStr<'i>, input: &mut Parser<'i, 't>, ) -> Result> { + if !self.allow_at_and_qualified_rules() { + return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name))) + } Ok(match_ignore_ascii_case! { &*name, "media" => { let media_queries = MediaList::parse(self.context, input); @@ -499,7 +587,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> { if cfg!(feature = "servo") && prefix.as_ref().map_or(false, |p| matches!(*p, VendorPrefix::Moz)) { // Servo should not support @-moz-keyframes. - return Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone()))) + return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone()))) } let name = KeyframesName::parse(self.context, input)?; AtRulePrelude::Keyframes(name, prefix) @@ -513,7 +601,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> { let cond = DocumentCondition::parse(self.context, input)?; AtRulePrelude::Document(cond) }, - _ => return Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone()))) + _ => return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone()))) }) } @@ -522,135 +610,147 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> { prelude: AtRulePrelude, start: &ParserState, input: &mut Parser<'i, 't>, - ) -> Result> { - match prelude { - AtRulePrelude::FontFace => { - self.nest_for_rule(CssRuleType::FontFace, |p| { - Ok(CssRule::FontFace(Arc::new(p.shared_lock.wrap( - parse_font_face_block(&p.context, input, start.source_location()).into(), - )))) - }) - }, + ) -> Result<(), ParseError<'i>> { + let rule = match prelude { + AtRulePrelude::FontFace => self.nest_for_rule(CssRuleType::FontFace, |p| { + CssRule::FontFace(Arc::new(p.shared_lock.wrap( + parse_font_face_block(&p.context, input, start.source_location()).into(), + ))) + }), AtRulePrelude::FontFeatureValues(family_names) => { self.nest_for_rule(CssRuleType::FontFeatureValues, |p| { - Ok(CssRule::FontFeatureValues(Arc::new(p.shared_lock.wrap( + CssRule::FontFeatureValues(Arc::new(p.shared_lock.wrap( FontFeatureValuesRule::parse( &p.context, input, family_names, start.source_location(), ), - )))) + ))) }) }, AtRulePrelude::FontPaletteValues(name) => { self.nest_for_rule(CssRuleType::FontPaletteValues, |p| { - Ok(CssRule::FontPaletteValues(Arc::new(p.shared_lock.wrap( + CssRule::FontPaletteValues(Arc::new(p.shared_lock.wrap( FontPaletteValuesRule::parse( &p.context, input, name, start.source_location(), ), - )))) + ))) }) }, AtRulePrelude::CounterStyle(name) => { - self.nest_for_rule(CssRuleType::CounterStyle, |p| { - Ok(CssRule::CounterStyle(Arc::new( - p.shared_lock.wrap( - parse_counter_style_body(name, &p.context, input, start.source_location())? - .into(), - ), - ))) - }) + let body = self.nest_for_rule(CssRuleType::CounterStyle, |p| { + parse_counter_style_body(name, &p.context, input, start.source_location()) + })?; + CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(body))) }, AtRulePrelude::Media(media_queries) => { - Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule { - media_queries, - rules: self.parse_nested_rules(input, CssRuleType::Media), - source_location: start.source_location(), - })))) + let source_location = start.source_location(); + CssRule::Media(Arc::new( + self.shared_lock.wrap(MediaRule { + media_queries, + rules: self + .parse_nested(input, CssRuleType::Media, None) + .into_rules(self.shared_lock, source_location), + source_location, + }), + )) }, AtRulePrelude::Supports(condition) => { - let enabled = self.nest_for_rule(CssRuleType::Style, |p| { - condition.eval(&p.context) - }); - Ok(CssRule::Supports(Arc::new(self.shared_lock.wrap( - SupportsRule { + let enabled = + self.nest_for_rule(CssRuleType::Style, |p| condition.eval(&p.context)); + let source_location = start.source_location(); + CssRule::Supports(Arc::new( + self.shared_lock.wrap(SupportsRule { condition, - rules: self.parse_nested_rules(input, CssRuleType::Supports), + rules: self + .parse_nested(input, CssRuleType::Supports, None) + .into_rules(self.shared_lock, source_location), enabled, - source_location: start.source_location(), - }, - )))) + source_location, + }), + )) }, AtRulePrelude::Viewport => { - self.nest_for_rule(CssRuleType::Viewport, |p| { - Ok(CssRule::Viewport(Arc::new( - p.shared_lock.wrap(ViewportRule::parse(&p.context, input)?), - ))) - }) + let body = self.nest_for_rule(CssRuleType::Viewport, |p| { + ViewportRule::parse(&p.context, input) + })?; + CssRule::Viewport(Arc::new(self.shared_lock.wrap(body))) }, AtRulePrelude::Keyframes(name, vendor_prefix) => { self.nest_for_rule(CssRuleType::Keyframe, |p| { - Ok(CssRule::Keyframes(Arc::new(p.shared_lock.wrap( - KeyframesRule { - name, - keyframes: parse_keyframe_list(&mut p.context, input, p.shared_lock), - vendor_prefix, - source_location: start.source_location(), - }, - )))) + CssRule::Keyframes(Arc::new(p.shared_lock.wrap(KeyframesRule { + name, + keyframes: parse_keyframe_list(&mut p.context, input, p.shared_lock), + vendor_prefix, + source_location: start.source_location(), + }))) }) }, AtRulePrelude::Page(selectors) => { let declarations = self.nest_for_rule(CssRuleType::Page, |p| { + // TODO: Support nesting in @page rules? parse_property_declaration_list(&p.context, input, None) }); - Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule { + CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule { selectors, block: Arc::new(self.shared_lock.wrap(declarations)), source_location: start.source_location(), - })))) + }))) }, AtRulePrelude::Document(condition) => { if !cfg!(feature = "gecko") { unreachable!() } - Ok(CssRule::Document(Arc::new(self.shared_lock.wrap( - DocumentRule { + let source_location = start.source_location(); + CssRule::Document(Arc::new( + self.shared_lock.wrap(DocumentRule { condition, - rules: self.parse_nested_rules(input, CssRuleType::Document), - source_location: start.source_location(), - }, - )))) + rules: self + .parse_nested(input, CssRuleType::Document, None) + .into_rules(self.shared_lock, source_location), + source_location, + }), + )) + }, + AtRulePrelude::Container(condition) => { + let source_location = start.source_location(); + CssRule::Container(Arc::new( + self.shared_lock.wrap(ContainerRule { + condition, + rules: self + .parse_nested(input, CssRuleType::Container, None) + .into_rules(self.shared_lock, source_location), + source_location, + }), + )) }, - AtRulePrelude::Container(condition) => Ok(CssRule::Container(Arc::new( - self.shared_lock.wrap(ContainerRule { - condition, - rules: self.parse_nested_rules(input, CssRuleType::Container), - source_location: start.source_location(), - }), - ))), AtRulePrelude::Layer(names) => { let name = match names.len() { 0 | 1 => names.into_iter().next(), _ => return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)), }; - Ok(CssRule::LayerBlock(Arc::new(self.shared_lock.wrap( - LayerBlockRule { + let source_location = start.source_location(); + CssRule::LayerBlock(Arc::new( + self.shared_lock.wrap(LayerBlockRule { name, - rules: self.parse_nested_rules(input, CssRuleType::LayerBlock), - source_location: start.source_location(), - }, - )))) + rules: self + .parse_nested(input, CssRuleType::LayerBlock, None) + .into_rules(self.shared_lock, source_location), + source_location, + }), + )) }, AtRulePrelude::Import(..) | AtRulePrelude::Namespace(..) => { // These rules don't have blocks. - Err(input.new_unexpected_token_error(cssparser::Token::CurlyBracketBlock)) + return Err(input.new_unexpected_token_error(cssparser::Token::CurlyBracketBlock)); }, - } + }; + self.rules.push(rule); + Ok(()) } #[inline] @@ -658,8 +758,8 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> { &mut self, prelude: AtRulePrelude, start: &ParserState, - ) -> Result { - Ok(match prelude { + ) -> Result<(), ()> { + let rule = match prelude { AtRulePrelude::Layer(names) => { if names.is_empty() { return Err(()); @@ -670,7 +770,9 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> { }))) }, _ => return Err(()), - }) + }; + self.rules.push(rule); + Ok(()) } } @@ -709,9 +811,9 @@ fn check_for_useless_selector( } } -impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> { +impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b, 'i> { type Prelude = SelectorList; - type QualifiedRule = CssRule; + type QualifiedRule = (); type Error = StyleParseErrorKind<'i>; fn parse_prelude<'t>( @@ -736,27 +838,46 @@ impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> { selectors: Self::Prelude, start: &ParserState, input: &mut Parser<'i, 't>, - ) -> Result> { - let declarations = self.nest_for_rule(CssRuleType::Style, |p| { - parse_property_declaration_list(&p.context, input, Some(&selectors)) - }); - let block = Arc::new(self.shared_lock.wrap(declarations)); - Ok(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule { - selectors, - block, - rules: None, // TODO(nesting) - source_location: start.source_location(), - })))) + ) -> Result<(), ParseError<'i>> { + let result = self.parse_nested(input, CssRuleType::Style, Some(&selectors)); + let block = Arc::new(self.shared_lock.wrap(result.declarations)); + self.rules + .push(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule { + selectors, + block, + rules: if result.rules.is_empty() { + None + } else { + Some(CssRules::new(result.rules, self.shared_lock)) + }, + source_location: start.source_location(), + })))); + Ok(()) } } -impl<'a, 'b, 'i> DeclarationParser<'i> for NestedRuleParser<'a, 'b> { - type Declaration = CssRule; +impl<'a, 'b, 'i> DeclarationParser<'i> for NestedRuleParser<'a, 'b, 'i> { + type Declaration = (); type Error = StyleParseErrorKind<'i>; + fn parse_value<'t>( + &mut self, + name: CowRcStr<'i>, + input: &mut Parser<'i, 't>, + ) -> Result<(), ParseError<'i>> { + self.declaration_parser_state.parse_value(self.context, name, input) + } } -impl<'a, 'b, 'i> RuleBodyItemParser<'i, CssRule, StyleParseErrorKind<'i>> for NestedRuleParser<'a, 'b> { - fn parse_qualified(&self) -> bool { true } - // TODO: Nesting. - fn parse_declarations(&self) -> bool { false } +impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> + for NestedRuleParser<'a, 'b, 'i> +{ + fn parse_qualified(&self) -> bool { + self.allow_at_and_qualified_rules() + } + + /// If nesting is disabled, we can't get there for a non-style-rule. If it's enabled, we parse + /// raw declarations there. + fn parse_declarations(&self) -> bool { + self.context.rule_types.contains(CssRuleType::Style) + } } diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs index 155a40122e2..2089468c3de 100644 --- a/components/style/stylesheets/stylesheet.rs +++ b/components/style/stylesheets/stylesheet.rs @@ -461,7 +461,6 @@ impl Stylesheet { allow_import_rules: AllowImportRules, mut sanitization_data: Option<&mut SanitizationData>, ) -> (Namespaces, Vec, Option, Option) { - let mut rules = Vec::new(); let mut input = ParserInput::new_with_line_number_offset(css, line_number_offset); let mut input = Parser::new(&mut input); @@ -484,32 +483,26 @@ impl Stylesheet { dom_error: None, insert_rule_context: None, allow_import_rules, + declaration_parser_state: Default::default(), + rules: Vec::new(), }; { let mut iter = StyleSheetParser::new(&mut input, &mut rule_parser); - - loop { - let result = match iter.next() { - Some(result) => result, - None => break, - }; + while let Some(result) = iter.next() { match result { - Ok((rule_start, rule)) => { + Ok(rule_start) => { + // TODO(emilio, nesting): sanitize nested CSS rules, probably? if let Some(ref mut data) = sanitization_data { - if !data.kind.allows(&rule) { - continue; + if let Some(ref rule) = iter.parser.rules.last() { + if !data.kind.allows(rule) { + iter.parser.rules.pop(); + continue; + } } let end = iter.input.position().byte_index(); data.output.push_str(&css[rule_start.byte_index()..end]); } - // Use a fallible push here, and if it fails, just fall - // out of the loop. This will cause the page to be - // shown incorrectly, but it's better than OOMing. - if rules.try_reserve(1).is_err() { - break; - } - rules.push(rule); }, Err((error, slice)) => { let location = error.location; @@ -522,7 +515,7 @@ impl Stylesheet { let source_map_url = input.current_source_map_url().map(String::from); let source_url = input.current_source_url().map(String::from); - (rule_parser.context.namespaces.into_owned(), rules, source_map_url, source_url) + (rule_parser.context.namespaces.into_owned(), rule_parser.rules, source_map_url, source_url) } /// Creates an empty stylesheet and parses it with a given base url, origin @@ -601,7 +594,7 @@ impl Clone for Stylesheet { Stylesheet { contents, - media: media, + media, shared_lock: lock, disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)), } diff --git a/components/style/stylesheets/supports_rule.rs b/components/style/stylesheets/supports_rule.rs index 880af04d997..ea00fadd2ff 100644 --- a/components/style/stylesheets/supports_rule.rs +++ b/components/style/stylesheets/supports_rule.rs @@ -427,7 +427,7 @@ impl Declaration { /// /// pub fn eval(&self, context: &ParserContext) -> bool { - debug_assert_eq!(context.rule_type(), CssRuleType::Style); + debug_assert!(context.rule_types().contains(CssRuleType::Style)); let mut input = ParserInput::new(&self.0); let mut input = Parser::new(&mut input); @@ -439,7 +439,7 @@ impl Declaration { let id = PropertyId::parse(&prop, context).map_err(|_| input.new_custom_error(()))?; - let mut declarations = SourcePropertyDeclaration::new(); + let mut declarations = SourcePropertyDeclaration::default(); input.parse_until_before(Delimiter::Bang, |input| { PropertyDeclaration::parse_into(&mut declarations, id, &context, input) .map_err(|_| input.new_custom_error(())) diff --git a/components/style_traits/lib.rs b/components/style_traits/lib.rs index cff19722513..1517d411ea3 100644 --- a/components/style_traits/lib.rs +++ b/components/style_traits/lib.rs @@ -121,8 +121,6 @@ pub enum StyleParseErrorKind<'i> { DisallowedImportRule, /// Unexpected @charset rule encountered. UnexpectedCharsetRule, - /// Unsupported @ rule - UnsupportedAtRule(CowRcStr<'i>), /// A placeholder for many sources of errors that require more specific variants. UnspecifiedError, /// An unexpected token was found within a namespace rule.