mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 12:19:54 +00:00
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:
parent
7aed541ed0
commit
8536e23674
Notes:
github-actions[bot]
2025-05-16 22:31:53 +00:00
Author: https://github.com/AtkinsSJ
Commit: 8536e23674
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4780
7 changed files with 25 additions and 21 deletions
|
@ -697,24 +697,24 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
|
||||||
case PseudoClassMetadata::ParameterType::Ident: {
|
case PseudoClassMetadata::ParameterType::Ident: {
|
||||||
auto function_token_stream = TokenStream(pseudo_function.value);
|
auto function_token_stream = TokenStream(pseudo_function.value);
|
||||||
function_token_stream.discard_whitespace();
|
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();
|
function_token_stream.discard_whitespace();
|
||||||
if (!maybe_keyword_token.is(Token::Type::Ident) || function_token_stream.has_next_token()) {
|
if (!maybe_ident_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);
|
dbgln_if(CSS_PARSER_DEBUG, "Failed to parse :{}() parameter: not an ident", pseudo_function.name);
|
||||||
return ParseError::SyntaxError;
|
return ParseError::SyntaxError;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto maybe_keyword = keyword_from_string(maybe_keyword_token.token().ident());
|
auto& ident = maybe_ident_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Selector::SimpleSelector {
|
return Selector::SimpleSelector {
|
||||||
.type = Selector::SimpleSelector::Type::PseudoClass,
|
.type = Selector::SimpleSelector::Type::PseudoClass,
|
||||||
.value = Selector::SimpleSelector::PseudoClassSelector {
|
.value = Selector::SimpleSelector::PseudoClassSelector {
|
||||||
.type = pseudo_class,
|
.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: {
|
case PseudoClassMetadata::ParameterType::LanguageRanges: {
|
||||||
|
|
|
@ -463,7 +463,7 @@ String Selector::SimpleSelector::serialize() const
|
||||||
s.append(serialize_a_group_of_selectors(pseudo_class.argument_selector_list));
|
s.append(serialize_a_group_of_selectors(pseudo_class.argument_selector_list));
|
||||||
break;
|
break;
|
||||||
case PseudoClassMetadata::ParameterType::Ident:
|
case PseudoClassMetadata::ParameterType::Ident:
|
||||||
s.append(string_from_keyword(pseudo_class.keyword.value()));
|
s.append(serialize_an_identifier(pseudo_class.ident->string_value));
|
||||||
break;
|
break;
|
||||||
case PseudoClassMetadata::ParameterType::LanguageRanges:
|
case PseudoClassMetadata::ParameterType::LanguageRanges:
|
||||||
// The serialization of a comma-separated list of each argument’s serialization as a string, preserving relative order.
|
// The serialization of a comma-separated list of each argument’s serialization as a string, preserving relative order.
|
||||||
|
|
|
@ -133,7 +133,11 @@ public:
|
||||||
Vector<FlyString> languages {};
|
Vector<FlyString> languages {};
|
||||||
|
|
||||||
// Used by :dir()
|
// Used by :dir()
|
||||||
Optional<Keyword> keyword {};
|
struct Ident {
|
||||||
|
Keyword keyword;
|
||||||
|
FlyString string_value;
|
||||||
|
};
|
||||||
|
Optional<Ident> ident {};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Name {
|
struct Name {
|
||||||
|
|
|
@ -812,13 +812,15 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
||||||
case CSS::PseudoClass::Dir: {
|
case CSS::PseudoClass::Dir: {
|
||||||
// "Values other than ltr and rtl are not invalid, but do not match anything."
|
// "Values other than ltr and rtl are not invalid, but do not match anything."
|
||||||
// - https://www.w3.org/TR/selectors-4/#the-dir-pseudo
|
// - https://www.w3.org/TR/selectors-4/#the-dir-pseudo
|
||||||
if (!first_is_one_of(pseudo_class.keyword, CSS::Keyword::Ltr, CSS::Keyword::Rtl))
|
if (!pseudo_class.ident.has_value())
|
||||||
|
return false;
|
||||||
|
if (!first_is_one_of(pseudo_class.ident->keyword, CSS::Keyword::Ltr, CSS::Keyword::Rtl))
|
||||||
return false;
|
return false;
|
||||||
switch (element.directionality()) {
|
switch (element.directionality()) {
|
||||||
case DOM::Element::Directionality::Ltr:
|
case DOM::Element::Directionality::Ltr:
|
||||||
return pseudo_class.keyword == CSS::Keyword::Ltr;
|
return pseudo_class.ident->keyword == CSS::Keyword::Ltr;
|
||||||
case DOM::Element::Directionality::Rtl:
|
case DOM::Element::Directionality::Rtl:
|
||||||
return pseudo_class.keyword == CSS::Keyword::Rtl;
|
return pseudo_class.ident->keyword == CSS::Keyword::Rtl;
|
||||||
}
|
}
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
|
@ -582,7 +582,7 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector, int in
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CSS::PseudoClassMetadata::ParameterType::Ident:
|
case CSS::PseudoClassMetadata::ParameterType::Ident:
|
||||||
builder.appendff("(keyword={})", string_from_keyword(pseudo_class.keyword.value()));
|
builder.appendff("(ident={})", pseudo_class.ident->string_value);
|
||||||
break;
|
break;
|
||||||
case CSS::PseudoClassMetadata::ParameterType::LanguageRanges: {
|
case CSS::PseudoClassMetadata::ParameterType::LanguageRanges: {
|
||||||
builder.append('(');
|
builder.append('(');
|
||||||
|
|
|
@ -2,15 +2,14 @@ Harness status: OK
|
||||||
|
|
||||||
Found 10 tests
|
Found 10 tests
|
||||||
|
|
||||||
9 Pass
|
10 Pass
|
||||||
1 Fail
|
|
||||||
Pass ":dir(rtl)" should be a valid selector
|
Pass ":dir(rtl)" should be a valid selector
|
||||||
Pass ":dir( rtl )" should be a valid selector
|
Pass ":dir( rtl )" should be a valid selector
|
||||||
Pass ":dir(ltr):dir(rtl)" should be a valid selector
|
Pass ":dir(ltr):dir(rtl)" should be a valid selector
|
||||||
Pass "foo:dir(RTL)" should be a valid selector
|
Pass "foo:dir(RTL)" should be a valid selector
|
||||||
Pass ":dir(auto)" should be a valid selector
|
Pass ":dir(auto)" should be a valid selector
|
||||||
Pass ":dir(none)" should be a valid selector
|
Pass ":dir(none)" should be a valid selector
|
||||||
Fail ":dir(something-made-up)" should be a valid selector
|
Pass ":dir(something-made-up)" should be a valid selector
|
||||||
Pass ":dir()" should be an invalid selector
|
Pass ":dir()" should be an invalid selector
|
||||||
Pass ":dir(\"ltr\")" should be an invalid selector
|
Pass ":dir(\"ltr\")" should be an invalid selector
|
||||||
Pass ":dir(ltr, rtl)" should be an invalid selector
|
Pass ":dir(ltr, rtl)" should be an invalid selector
|
|
@ -2,9 +2,8 @@ Harness status: OK
|
||||||
|
|
||||||
Found 4 tests
|
Found 4 tests
|
||||||
|
|
||||||
3 Pass
|
4 Pass
|
||||||
1 Fail
|
Pass :dir() allows any ident value but strings other than ltr/rtl don't match
|
||||||
Fail :dir() allows any ident value but strings other than ltr/rtl don't match
|
|
||||||
Pass :dir() requires exactly an ident argument
|
Pass :dir() requires exactly an ident argument
|
||||||
Pass :dir() works in compound selectors
|
Pass :dir() works in compound selectors
|
||||||
Pass :dir() works in complex selectors
|
Pass :dir() works in complex selectors
|
Loading…
Add table
Add a link
Reference in a new issue