LibWeb/CSS: Support legacy selector aliases for pseudo-classes

These are replaced with the pseudo-class they are an alias of, during
parsing.
This commit is contained in:
Sam Atkins
2025-12-18 12:47:44 +00:00
committed by Jelle Raaijmakers
parent 36a9b653ae
commit 2b2e5a1db3
3 changed files with 28 additions and 10 deletions

View File

@@ -199,9 +199,12 @@ The generated code provides these for each enum, using "foo" as an example:
This is a single JSON object, with selector pseudo-class names as keys and the values being objects with fields for the pseudo-class.
This generates `PseudoClass.h` and `PseudoClass.cpp`.
Each entry has a single required property, `argument`, which is a string containing the grammar for the pseudo-class's
function parameters - for identifier-style pseudo-classes it is left blank.
The grammar is taken directly from the spec.
Each entry has the following properties:
| Field | Required | Default | Description |
|----------------------|----------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `argument` | Unless `legacy-alias-for` is specified | Nothing | A string containing the grammar for the pseudo-class's function parameters - for identifier-style pseudo-classes it is left blank. The grammar is taken directly from the spec. |
| `legacy-alias-for` | No | Nothing | Use to specify that this should be treated as a [legacy selector alias](https://drafts.csswg.org/selectors/#legacy-selector-alias) for the named pseudo-class. |
The generated code provides:
- A `PseudoClass` enum listing every pseudo-class name

View File

@@ -669,8 +669,6 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
if (pseudo_class_token.is(Token::Type::Ident)) {
auto pseudo_name = pseudo_class_token.token().ident();
if (has_ignored_vendor_prefix(pseudo_name))
return ParseError::IncludesIgnoredVendorPrefix;
auto make_pseudo_class_selector = [](auto pseudo_class) {
return Selector::SimpleSelector {
@@ -691,6 +689,9 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
return make_pseudo_class_selector(pseudo_class.value());
}
if (has_ignored_vendor_prefix(pseudo_name))
return ParseError::IncludesIgnoredVendorPrefix;
// Single-colon syntax allowed for ::after, ::before, ::first-letter and ::first-line for compatibility.
// https://www.w3.org/TR/selectors/#pseudo-element-syntax
if (auto pseudo_element = pseudo_element_from_string(pseudo_name); pseudo_element.has_value()) {

View File

@@ -53,7 +53,10 @@ namespace Web::CSS {
enum class PseudoClass {
)~~~");
pseudo_classes_data.for_each_member([&](auto& name, auto&) {
pseudo_classes_data.for_each_member([&](auto& name, JsonValue const& value) {
if (value.as_object().has("legacy-alias-for"sv))
return;
auto member_generator = generator.fork();
member_generator.set("name:titlecase", title_casify(name));
@@ -106,10 +109,15 @@ Optional<PseudoClass> pseudo_class_from_string(StringView string)
{
)~~~");
pseudo_classes_data.for_each_member([&](auto& name, auto&) {
pseudo_classes_data.for_each_member([&](auto& name, JsonValue const& value) {
auto member_generator = generator.fork();
member_generator.set("name", name);
member_generator.set("name:titlecase", title_casify(name));
if (auto alias_for = value.as_object().get_string("legacy-alias-for"sv); alias_for.has_value()) {
member_generator.set("name:titlecase", title_casify(alias_for.value()));
} else {
member_generator.set("name:titlecase", title_casify(name));
}
member_generator.append(R"~~~(
if (string.equals_ignoring_ascii_case("@name@"sv))
@@ -129,7 +137,10 @@ StringView pseudo_class_name(PseudoClass pseudo_class)
VERIFY_NOT_REACHED();
)~~~");
pseudo_classes_data.for_each_member([&](auto& name, auto&) {
pseudo_classes_data.for_each_member([&](auto& name, JsonValue const& value) {
if (value.as_object().has("legacy-alias-for"sv))
return;
auto member_generator = generator.fork();
member_generator.set("name", name);
member_generator.set("name:titlecase", title_casify(name));
@@ -153,8 +164,11 @@ PseudoClassMetadata pseudo_class_metadata(PseudoClass pseudo_class)
)~~~");
pseudo_classes_data.for_each_member([&](auto& name, JsonValue const& value) {
auto member_generator = generator.fork();
auto& pseudo_class = value.as_object();
if (pseudo_class.has("legacy-alias-for"sv))
return;
auto member_generator = generator.fork();
auto argument_string = pseudo_class.get_string("argument"sv).value();
bool is_valid_as_identifier = argument_string.is_empty();