LibWeb/CSS: Parse an ident in :dir(), not a keyword

The spec requires us to accept any ident here, not just ltr/rtl, and
also serialize it back out. That means we need to keep the original
string around.

In order to not call keyword_from_string() every time we want to match
a :dir() selector, we still attempt to parse the keyword and keep it
around.

A small behaviour change is that now we'll serialize the ident with its
original casing, instead of always lowercase. Chrome and Firefox
disagree on this, so I think either is fine until that can be
officially decided.

Gets us 2 WPT passes (including 1 from the as-yet-unmerged :dir() test).
This commit is contained in:
Sam Atkins 2025-05-16 14:57:18 +01:00 committed by Andreas Kling
commit 8536e23674
Notes: github-actions[bot] 2025-05-16 22:31:53 +00:00
7 changed files with 25 additions and 21 deletions

View file

@ -697,24 +697,24 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
case PseudoClassMetadata::ParameterType::Ident: {
auto function_token_stream = TokenStream(pseudo_function.value);
function_token_stream.discard_whitespace();
auto const& maybe_keyword_token = function_token_stream.consume_a_token();
auto const& maybe_ident_token = function_token_stream.consume_a_token();
function_token_stream.discard_whitespace();
if (!maybe_keyword_token.is(Token::Type::Ident) || function_token_stream.has_next_token()) {
dbgln_if(CSS_PARSER_DEBUG, "Failed to parse :{}() parameter as a keyword: not an ident", pseudo_function.name);
if (!maybe_ident_token.is(Token::Type::Ident) || function_token_stream.has_next_token()) {
dbgln_if(CSS_PARSER_DEBUG, "Failed to parse :{}() parameter: not an ident", pseudo_function.name);
return ParseError::SyntaxError;
}
auto maybe_keyword = keyword_from_string(maybe_keyword_token.token().ident());
if (!maybe_keyword.has_value()) {
dbgln_if(CSS_PARSER_DEBUG, "Failed to parse :{}() parameter as a keyword: unrecognized keyword", pseudo_function.name);
return ParseError::SyntaxError;
}
auto& ident = maybe_ident_token.token().ident();
return Selector::SimpleSelector {
.type = Selector::SimpleSelector::Type::PseudoClass,
.value = Selector::SimpleSelector::PseudoClassSelector {
.type = pseudo_class,
.keyword = maybe_keyword.value() }
.ident = Selector::SimpleSelector::PseudoClassSelector::Ident {
.keyword = keyword_from_string(ident).value_or(Keyword::Invalid),
.string_value = ident,
},
}
};
}
case PseudoClassMetadata::ParameterType::LanguageRanges: {