Skip to content

Commit 76180ef

Browse files
committed
parser: Implement recent syntax spec change to avoid parsing custom properties that look like selectors. r=#style,#layout
This implements the: > If the first two non-<whitespace-token> values of rule’s prelude are > an <ident-token> whose value starts with "--" followed by a > <colon-token>, then... From https://drafts.csswg.org/css-syntax/#consume-qualified-rule Mozilla Bug: 1884879 Differential Revision: https://phabricator.services.mozilla.com/D207796
1 parent cda06ee commit 76180ef

File tree

2 files changed

+61
-24
lines changed

2 files changed

+61
-24
lines changed

src/parser.rs

+16-10
Original file line numberDiff line numberDiff line change
@@ -117,17 +117,29 @@ impl SourceLocation {
117117
/// Create a new BasicParseError at this location for an unexpected token
118118
#[inline]
119119
pub fn new_basic_unexpected_token_error(self, token: Token<'_>) -> BasicParseError<'_> {
120+
self.new_basic_error(BasicParseErrorKind::UnexpectedToken(token))
121+
}
122+
123+
/// Create a new BasicParseError at this location
124+
#[inline]
125+
pub fn new_basic_error(self, kind: BasicParseErrorKind<'_>) -> BasicParseError<'_> {
120126
BasicParseError {
121-
kind: BasicParseErrorKind::UnexpectedToken(token),
127+
kind,
122128
location: self,
123129
}
124130
}
125131

126132
/// Create a new ParseError at this location for an unexpected token
127133
#[inline]
128134
pub fn new_unexpected_token_error<E>(self, token: Token<'_>) -> ParseError<'_, E> {
135+
self.new_error(BasicParseErrorKind::UnexpectedToken(token))
136+
}
137+
138+
/// Create a new basic ParseError at the current location
139+
#[inline]
140+
pub fn new_error<E>(self, kind: BasicParseErrorKind<'_>) -> ParseError<'_, E> {
129141
ParseError {
130-
kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(token)),
142+
kind: ParseErrorKind::Basic(kind),
131143
location: self,
132144
}
133145
}
@@ -450,19 +462,13 @@ impl<'i: 't, 't> Parser<'i, 't> {
450462
/// Create a new BasicParseError at the current location
451463
#[inline]
452464
pub fn new_basic_error(&self, kind: BasicParseErrorKind<'i>) -> BasicParseError<'i> {
453-
BasicParseError {
454-
kind,
455-
location: self.current_source_location(),
456-
}
465+
self.current_source_location().new_basic_error(kind)
457466
}
458467

459468
/// Create a new basic ParseError at the current location
460469
#[inline]
461470
pub fn new_error<E>(&self, kind: BasicParseErrorKind<'i>) -> ParseError<'i, E> {
462-
ParseError {
463-
kind: ParseErrorKind::Basic(kind),
464-
location: self.current_source_location(),
465-
}
471+
self.current_source_location().new_error(kind)
466472
}
467473

468474
/// Create a new custom BasicParseError at the current location

src/rules_and_declarations.rs

+45-14
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44

55
// https://drafts.csswg.org/css-syntax/#parsing
66

7-
use super::{
8-
BasicParseError, BasicParseErrorKind, Delimiter, Delimiters, ParseError, Parser, Token,
9-
};
7+
use super::{BasicParseError, BasicParseErrorKind, Delimiter, ParseError, Parser, Token};
108
use crate::cow_rc_str::CowRcStr;
119
use crate::parser::{parse_nested_block, parse_until_after, ParseUntilErrorBehavior, ParserState};
1210

@@ -293,7 +291,7 @@ where
293291
&start,
294292
self.input,
295293
&mut *self.parser,
296-
Delimiter::Semicolon | Delimiter::CurlyBracketBlock,
294+
/* nested = */ true,
297295
) {
298296
return Some(Ok(qual));
299297
}
@@ -304,12 +302,8 @@ where
304302
token => {
305303
let result = if self.parser.parse_qualified() {
306304
self.input.reset(&start);
307-
let delimiters = if self.parser.parse_declarations() {
308-
Delimiter::Semicolon | Delimiter::CurlyBracketBlock
309-
} else {
310-
Delimiter::CurlyBracketBlock
311-
};
312-
parse_qualified_rule(&start, self.input, &mut *self.parser, delimiters)
305+
let nested = self.parser.parse_declarations();
306+
parse_qualified_rule(&start, self.input, &mut *self.parser, nested)
313307
} else {
314308
let token = token.clone();
315309
self.input.parse_until_after(Delimiter::Semicolon, |_| {
@@ -398,7 +392,7 @@ where
398392
&start,
399393
self.input,
400394
&mut *self.parser,
401-
Delimiter::CurlyBracketBlock,
395+
/* nested = */ false,
402396
);
403397
return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))));
404398
}
@@ -451,7 +445,7 @@ where
451445
if let Some(name) = at_keyword {
452446
parse_at_rule(&start, name, input, parser).map_err(|e| e.0)
453447
} else {
454-
parse_qualified_rule(&start, input, parser, Delimiter::CurlyBracketBlock)
448+
parse_qualified_rule(&start, input, parser, /* nested = */ false)
455449
}
456450
})
457451
}
@@ -491,16 +485,53 @@ where
491485
}
492486
}
493487

488+
// If the first two non-<whitespace-token> values of rule’s prelude are an <ident-token> whose
489+
// value starts with "--" followed by a <colon-token>, then...
490+
fn looks_like_a_custom_property(input: &mut Parser) -> bool {
491+
let ident = match input.expect_ident() {
492+
Ok(i) => i,
493+
Err(..) => return false,
494+
};
495+
ident.starts_with("--") && input.expect_colon().is_ok()
496+
}
497+
498+
// https://drafts.csswg.org/css-syntax/#consume-a-qualified-rule
494499
fn parse_qualified_rule<'i, 't, P, E>(
495500
start: &ParserState,
496501
input: &mut Parser<'i, 't>,
497502
parser: &mut P,
498-
delimiters: Delimiters,
503+
nested: bool,
499504
) -> Result<<P as QualifiedRuleParser<'i>>::QualifiedRule, ParseError<'i, E>>
500505
where
501506
P: QualifiedRuleParser<'i, Error = E>,
502507
{
503-
let prelude = input.parse_until_before(delimiters, |input| parser.parse_prelude(input));
508+
input.skip_whitespace();
509+
let prelude = {
510+
let state = input.state();
511+
if looks_like_a_custom_property(input) {
512+
let error = Err(state
513+
.source_location()
514+
.new_error(BasicParseErrorKind::QualifiedRuleInvalid));
515+
// If nested is true, consume the remnants of a bad declaration from input, with
516+
// nested set to true, and return nothing.
517+
// If nested is false, consume a block from input, and return nothing.
518+
let delimiters = if nested {
519+
Delimiter::Semicolon
520+
} else {
521+
Delimiter::CurlyBracketBlock
522+
};
523+
let _: Result<(), ParseError<()>> = input.parse_until_after(delimiters, |_| Ok(()));
524+
return error;
525+
}
526+
let delimiters = if nested {
527+
Delimiter::Semicolon | Delimiter::CurlyBracketBlock
528+
} else {
529+
Delimiter::CurlyBracketBlock
530+
};
531+
input.reset(&state);
532+
input.parse_until_before(delimiters, |input| parser.parse_prelude(input))
533+
};
534+
504535
input.expect_curly_bracket_block()?;
505536
// Do this here so that we consume the `{` even if the prelude is `Err`.
506537
let prelude = prelude?;

0 commit comments

Comments
 (0)