From 76180ef57e5a32a19164b30d0781b52ebbc88366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Wed, 17 Apr 2024 22:46:32 +0200 Subject: [PATCH] parser: Implement recent syntax spec change to avoid parsing custom properties that look like selectors. r=#style,#layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implements the: > If the first two non- values of rule’s prelude are > an whose value starts with "--" followed by a > , then... From https://drafts.csswg.org/css-syntax/#consume-qualified-rule Mozilla Bug: 1884879 Differential Revision: https://phabricator.services.mozilla.com/D207796 --- src/parser.rs | 26 +++++++++------ src/rules_and_declarations.rs | 59 ++++++++++++++++++++++++++--------- 2 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index e65cb013..dd35fc50 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -117,8 +117,14 @@ impl SourceLocation { /// Create a new BasicParseError at this location for an unexpected token #[inline] pub fn new_basic_unexpected_token_error(self, token: Token<'_>) -> BasicParseError<'_> { + self.new_basic_error(BasicParseErrorKind::UnexpectedToken(token)) + } + + /// Create a new BasicParseError at this location + #[inline] + pub fn new_basic_error(self, kind: BasicParseErrorKind<'_>) -> BasicParseError<'_> { BasicParseError { - kind: BasicParseErrorKind::UnexpectedToken(token), + kind, location: self, } } @@ -126,8 +132,14 @@ impl SourceLocation { /// Create a new ParseError at this location for an unexpected token #[inline] pub fn new_unexpected_token_error(self, token: Token<'_>) -> ParseError<'_, E> { + self.new_error(BasicParseErrorKind::UnexpectedToken(token)) + } + + /// Create a new basic ParseError at the current location + #[inline] + pub fn new_error(self, kind: BasicParseErrorKind<'_>) -> ParseError<'_, E> { ParseError { - kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(token)), + kind: ParseErrorKind::Basic(kind), location: self, } } @@ -450,19 +462,13 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Create a new BasicParseError at the current location #[inline] pub fn new_basic_error(&self, kind: BasicParseErrorKind<'i>) -> BasicParseError<'i> { - BasicParseError { - kind, - location: self.current_source_location(), - } + self.current_source_location().new_basic_error(kind) } /// Create a new basic ParseError at the current location #[inline] pub fn new_error(&self, kind: BasicParseErrorKind<'i>) -> ParseError<'i, E> { - ParseError { - kind: ParseErrorKind::Basic(kind), - location: self.current_source_location(), - } + self.current_source_location().new_error(kind) } /// Create a new custom BasicParseError at the current location diff --git a/src/rules_and_declarations.rs b/src/rules_and_declarations.rs index 9240f6de..9c5f5fa6 100644 --- a/src/rules_and_declarations.rs +++ b/src/rules_and_declarations.rs @@ -4,9 +4,7 @@ // https://drafts.csswg.org/css-syntax/#parsing -use super::{ - BasicParseError, BasicParseErrorKind, Delimiter, Delimiters, ParseError, Parser, Token, -}; +use super::{BasicParseError, BasicParseErrorKind, Delimiter, ParseError, Parser, Token}; use crate::cow_rc_str::CowRcStr; use crate::parser::{parse_nested_block, parse_until_after, ParseUntilErrorBehavior, ParserState}; @@ -293,7 +291,7 @@ where &start, self.input, &mut *self.parser, - Delimiter::Semicolon | Delimiter::CurlyBracketBlock, + /* nested = */ true, ) { return Some(Ok(qual)); } @@ -304,12 +302,8 @@ where token => { let result = if self.parser.parse_qualified() { self.input.reset(&start); - let delimiters = if self.parser.parse_declarations() { - Delimiter::Semicolon | Delimiter::CurlyBracketBlock - } else { - Delimiter::CurlyBracketBlock - }; - parse_qualified_rule(&start, self.input, &mut *self.parser, delimiters) + let nested = self.parser.parse_declarations(); + parse_qualified_rule(&start, self.input, &mut *self.parser, nested) } else { let token = token.clone(); self.input.parse_until_after(Delimiter::Semicolon, |_| { @@ -398,7 +392,7 @@ where &start, self.input, &mut *self.parser, - Delimiter::CurlyBracketBlock, + /* nested = */ false, ); return Some(result.map_err(|e| (e, self.input.slice_from(start.position())))); } @@ -451,7 +445,7 @@ where if let Some(name) = at_keyword { parse_at_rule(&start, name, input, parser).map_err(|e| e.0) } else { - parse_qualified_rule(&start, input, parser, Delimiter::CurlyBracketBlock) + parse_qualified_rule(&start, input, parser, /* nested = */ false) } }) } @@ -491,16 +485,53 @@ where } } +// If the first two non- values of rule’s prelude are an whose +// value starts with "--" followed by a , then... +fn looks_like_a_custom_property(input: &mut Parser) -> bool { + let ident = match input.expect_ident() { + Ok(i) => i, + Err(..) => return false, + }; + ident.starts_with("--") && input.expect_colon().is_ok() +} + +// https://drafts.csswg.org/css-syntax/#consume-a-qualified-rule fn parse_qualified_rule<'i, 't, P, E>( start: &ParserState, input: &mut Parser<'i, 't>, parser: &mut P, - delimiters: Delimiters, + nested: bool, ) -> Result<

>::QualifiedRule, ParseError<'i, E>> where P: QualifiedRuleParser<'i, Error = E>, { - let prelude = input.parse_until_before(delimiters, |input| parser.parse_prelude(input)); + input.skip_whitespace(); + let prelude = { + let state = input.state(); + if looks_like_a_custom_property(input) { + let error = Err(state + .source_location() + .new_error(BasicParseErrorKind::QualifiedRuleInvalid)); + // If nested is true, consume the remnants of a bad declaration from input, with + // nested set to true, and return nothing. + // If nested is false, consume a block from input, and return nothing. + let delimiters = if nested { + Delimiter::Semicolon + } else { + Delimiter::CurlyBracketBlock + }; + let _: Result<(), ParseError<()>> = input.parse_until_after(delimiters, |_| Ok(())); + return error; + } + let delimiters = if nested { + Delimiter::Semicolon | Delimiter::CurlyBracketBlock + } else { + Delimiter::CurlyBracketBlock + }; + input.reset(&state); + input.parse_until_before(delimiters, |input| parser.parse_prelude(input)) + }; + input.expect_curly_bracket_block()?; // Do this here so that we consume the `{` even if the prelude is `Err`. let prelude = prelude?;