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>
RefPtr<Supports> Parser::parse_a_supports(TokenStream<T>& tokens)
{
auto transaction = tokens.begin_transaction();
auto component_values = parse_a_list_of_component_values(tokens);
TokenStream<ComponentValue> token_stream { component_values };
m_rule_context.append(RuleContext::SupportsCondition);
auto maybe_condition = parse_boolean_expression(token_stream, MatchResult::False, [this](auto& tokens) { return parse_supports_feature(tokens); });
m_rule_context.take_last();
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 {};
}

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)) {
auto component_value = tokens.consume_a_token();
TokenStream supports_tokens { component_value.function().value };
if (supports_tokens.next_token().is_block()) {
supports = parse_a_supports(supports_tokens);
} else {
supports = parse_a_supports(supports_tokens);
if (!supports) {
m_rule_context.append(RuleContext::SupportsCondition);
auto declaration = consume_a_declaration(supports_tokens);
m_rule_context.take_last();

View file

@ -2,13 +2,13 @@ Harness status: OK
Found 22 tests
9 Pass
13 Fail
16 Pass
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(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: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) 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
@ -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
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") 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
Fail @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
Fail @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(selector(a)); should be a valid supports() import rule
Pass @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
Pass @import url("nonexist.css") supports(selector(p + a)); should be a valid supports() import rule
Pass @import url("nonexist.css") supports(font-tech(color-colrv1)); 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 "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