LibWeb/CSS: Parse all supports features in @import supports()

The previous implementation assumed that the contents of `supports()`
was either a raw declaration, or a block containing some number of them.
This meant we wouldn't parse things like `supports(not (a:b))` or
`supports(selector(*))`.

`parse_a_supports()` actually does what we want in every case except for
raw declarations (`supports(a: b)`), so let's always call it first, and
then fall back to parsing a single declaration.
This commit is contained in:
Sam Atkins 2025-12-01 12:39:33 +00:00
parent d9abfdf2ab
commit 4ed1c56267
Notes: github-actions[bot] 2025-12-02 09:51:17 +00:00
3 changed files with 15 additions and 13 deletions

View file

@ -192,14 +192,17 @@ RefPtr<Supports> Parser::parse_as_supports()
template<typename T> template<typename T>
RefPtr<Supports> Parser::parse_a_supports(TokenStream<T>& tokens) RefPtr<Supports> Parser::parse_a_supports(TokenStream<T>& tokens)
{ {
auto transaction = tokens.begin_transaction();
auto component_values = parse_a_list_of_component_values(tokens); auto component_values = parse_a_list_of_component_values(tokens);
TokenStream<ComponentValue> token_stream { component_values }; TokenStream<ComponentValue> token_stream { component_values };
m_rule_context.append(RuleContext::SupportsCondition); m_rule_context.append(RuleContext::SupportsCondition);
auto maybe_condition = parse_boolean_expression(token_stream, MatchResult::False, [this](auto& tokens) { return parse_supports_feature(tokens); }); auto maybe_condition = parse_boolean_expression(token_stream, MatchResult::False, [this](auto& tokens) { return parse_supports_feature(tokens); });
m_rule_context.take_last(); m_rule_context.take_last();
token_stream.discard_whitespace(); token_stream.discard_whitespace();
if (maybe_condition && !token_stream.has_next_token()) if (maybe_condition && !token_stream.has_next_token()) {
transaction.commit();
return Supports::create(maybe_condition.release_nonnull()); return Supports::create(maybe_condition.release_nonnull());
}
return {}; return {};
} }

View file

@ -242,9 +242,8 @@ GC::Ptr<CSSImportRule> Parser::convert_to_import_rule(AtRule const& rule)
if (tokens.next_token().is_function("supports"sv)) { if (tokens.next_token().is_function("supports"sv)) {
auto component_value = tokens.consume_a_token(); auto component_value = tokens.consume_a_token();
TokenStream supports_tokens { component_value.function().value }; TokenStream supports_tokens { component_value.function().value };
if (supports_tokens.next_token().is_block()) { supports = parse_a_supports(supports_tokens);
supports = parse_a_supports(supports_tokens); if (!supports) {
} else {
m_rule_context.append(RuleContext::SupportsCondition); m_rule_context.append(RuleContext::SupportsCondition);
auto declaration = consume_a_declaration(supports_tokens); auto declaration = consume_a_declaration(supports_tokens);
m_rule_context.take_last(); m_rule_context.take_last();

View file

@ -2,13 +2,13 @@ Harness status: OK
Found 22 tests Found 22 tests
9 Pass 16 Pass
13 Fail 6 Fail
Pass @import url("nonexist.css") supports(); should be an invalid import rule due to an invalid supports() declaration Pass @import url("nonexist.css") supports(); should be an invalid import rule due to an invalid supports() declaration
Pass @import url("nonexist.css") supports(foo: bar); should be an invalid import rule due to an invalid supports() declaration Pass @import url("nonexist.css") supports(foo: bar); should be an invalid import rule due to an invalid supports() declaration
Fail @import url("nonexist.css") supports(display:block); should be a valid supports() import rule Fail @import url("nonexist.css") supports(display:block); should be a valid supports() import rule
Fail @import url("nonexist.css") supports((display:flex)); should be a valid supports() import rule Fail @import url("nonexist.css") supports((display:flex)); should be a valid supports() import rule
Fail @import url("nonexist.css") supports(not (display: flex)); should be a valid supports() import rule Pass @import url("nonexist.css") supports(not (display: flex)); should be a valid supports() import rule
Pass @import url("nonexist.css") supports((display: flex) and (display: block)); should be a valid supports() import rule Pass @import url("nonexist.css") supports((display: flex) and (display: block)); should be a valid supports() import rule
Pass @import url("nonexist.css") supports((display: flex) or (display: block)); should be a valid supports() import rule Pass @import url("nonexist.css") supports((display: flex) or (display: block)); should be a valid supports() import rule
Pass @import url("nonexist.css") supports((display: flex) or (foo: bar)); should be a valid supports() import rule Pass @import url("nonexist.css") supports((display: flex) or (foo: bar)); should be a valid supports() import rule
@ -17,12 +17,12 @@ Pass @import url("nonexist.css") layer supports(); should be an invalid import r
Pass @import url("nonexist.css") layer supports(foo: bar); should be an invalid import rule due to an invalid supports() declaration Pass @import url("nonexist.css") layer supports(foo: bar); should be an invalid import rule due to an invalid supports() declaration
Fail @import url("nonexist.css") layer(A) supports((display: flex) or (foo: bar)); should be a valid supports() import rule Fail @import url("nonexist.css") layer(A) supports((display: flex) or (foo: bar)); should be a valid supports() import rule
Fail @import url("nonexist.css") layer(A.B) supports((display: flex) and (foo: bar)); should be a valid supports() import rule Fail @import url("nonexist.css") layer(A.B) supports((display: flex) and (foo: bar)); should be a valid supports() import rule
Fail @import url("nonexist.css") supports(selector(a)); should be a valid supports() import rule Pass @import url("nonexist.css") supports(selector(a)); should be a valid supports() import rule
Fail @import url("nonexist.css") supports(selector(p a)); should be a valid supports() import rule Pass @import url("nonexist.css") supports(selector(p a)); should be a valid supports() import rule
Fail @import url("nonexist.css") supports(selector(p > a)); should be a valid supports() import rule Pass @import url("nonexist.css") supports(selector(p > a)); should be a valid supports() import rule
Fail @import url("nonexist.css") supports(selector(p + a)); should be a valid supports() import rule Pass @import url("nonexist.css") supports(selector(p + a)); should be a valid supports() import rule
Fail @import url("nonexist.css") supports(font-tech(color-colrv1)); should be a valid supports() import rule Pass @import url("nonexist.css") supports(font-tech(color-colrv1)); should be a valid supports() import rule
Fail @import url("nonexist.css") supports(font-format(opentype)); should be a valid supports() import rule Pass @import url("nonexist.css") supports(font-format(opentype)); should be a valid supports() import rule
Fail @import url(nonexist.css) supports(display:block); should be a valid supports() import rule Fail @import url(nonexist.css) supports(display:block); should be a valid supports() import rule
Fail @import "nonexist.css" supports(display:block); should be a valid supports() import rule Fail @import "nonexist.css" supports(display:block); should be a valid supports() import rule
Pass @import url("nonexist.css") supports; should still be a valid import rule with an invalid supports() declaration Pass @import url("nonexist.css") supports; should still be a valid import rule with an invalid supports() declaration