LibWeb/CSS: Generate property-filtering code for pseudo-elements

Pseudo-elements have specific rules about which CSS properties can be
applied to them. This is a first step to supporting that.

- If a property whitelist isn't present, all properties are allowed.
- Properties are named as in CSS.
- Names of property groups are prefixed with `#`, which makes this match
  the spec more clearly. These groups are implemented directly in the
  code generator for now.
- Any property name beginning with "FIXME:" is ignored, so we can mark
  properties we don't implement yet.
This commit is contained in:
Sam Atkins 2025-03-20 14:55:52 +00:00
parent 193adee164
commit 1108988656
Notes: github-actions[bot] 2025-03-24 09:51:00 +00:00
2 changed files with 287 additions and 4 deletions

View file

@ -50,19 +50,61 @@
"spec": "https://drafts.csswg.org/css-forms-1/#selectordef-fill"
},
"first-letter": {
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-first-letter"
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-first-letter",
"property-whitelist": [
"#font-properties",
"color",
"opacity",
"#background-properties",
"#inline-typesetting-properties",
"#text-decoration-properties",
"#inline-layout-properties",
"#margin-properties",
"#padding-properties",
"#border-properties",
"box-shadow"
]
},
"first-line": {
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-first-line"
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-first-line",
"property-whitelist": [
"#font-properties",
"color",
"opacity",
"#background-properties",
"#inline-typesetting-properties",
"#text-decoration-properties",
"FIXME: ruby-position",
"#inline-layout-properties"
]
},
"marker": {
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-marker"
},
"placeholder": {
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-placeholder"
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-placeholder",
"property-whitelist": [
"#font-properties",
"color",
"opacity",
"#background-properties",
"#inline-typesetting-properties",
"#text-decoration-properties",
"FIXME: ruby-position"
]
},
"selection": {
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-selection"
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-selection",
"property-whitelist": [
"color",
"background-color",
"#text-decoration-properties",
"text-shadow",
"FIXME: stroke-color",
"FIXME: fill-color",
"stroke-width",
"#custom-properties"
]
},
"thumb": {
"spec": "https://drafts.csswg.org/css-forms-1/#selectordef-thumb"

View file

@ -58,6 +58,7 @@ ErrorOr<void> generate_header_file(JsonObject& pseudo_elements_data, Core::File&
#include <AK/Optional.h>
#include <AK/StringView.h>
#include <LibWeb/CSS/PropertyID.h>
namespace Web::CSS {
@ -85,6 +86,7 @@ Optional<PseudoElement> aliased_pseudo_element_from_string(StringView);
StringView pseudo_element_name(PseudoElement);
bool is_has_allowed_pseudo_element(PseudoElement);
bool pseudo_element_supports_property(PseudoElement, PropertyID);
enum class GeneratedPseudoElement : @generated_pseudo_element_underlying_type@ {
)~~~");
@ -222,6 +224,245 @@ bool is_has_allowed_pseudo_element(PseudoElement pseudo_element)
}
}
bool pseudo_element_supports_property(PseudoElement pseudo_element, PropertyID property_id)
{
switch (pseudo_element) {
)~~~");
pseudo_elements_data.for_each_member([&](auto& name, JsonValue const& value) {
auto& pseudo_element = value.as_object();
if (pseudo_element.has("alias-for"sv))
return;
auto property_whitelist = pseudo_element.get_array("property-whitelist"sv);
// No whitelist = accept everything, by falling back to the default case.
if (!property_whitelist.has_value())
return;
auto member_generator = generator.fork();
member_generator.set("name:titlecase", title_casify(name));
member_generator.append(R"~~~(
case PseudoElement::@name:titlecase@:
switch (property_id) {
)~~~");
property_whitelist->for_each([&](JsonValue const& entry) {
auto& property = entry.as_string();
if (property.starts_with_bytes("FIXME:"sv))
return;
auto append_property = [&](StringView const& property_name) {
auto property_generator = member_generator.fork();
property_generator.set("property:titlecase", title_casify(property_name));
property_generator.appendln(" case PropertyID::@property:titlecase@:");
};
if (!property.starts_with('#')) {
append_property(property);
return;
}
// Categories
// TODO: Maybe define these in data somewhere too?
if (property == "#background-properties"sv) {
// https://drafts.csswg.org/css-backgrounds/#property-index
append_property("background"sv);
append_property("background-attachment"sv);
append_property("background-clip"sv);
append_property("background-color"sv);
append_property("background-image"sv);
append_property("background-origin"sv);
append_property("background-position"sv);
append_property("background-position-x"sv);
append_property("background-position-y"sv);
append_property("background-repeat"sv);
append_property("background-size"sv);
return;
}
if (property == "#border-properties"sv) {
// https://drafts.csswg.org/css-backgrounds/#property-index
append_property("border"sv);
append_property("border-block-end"sv);
append_property("border-block-end-color"sv);
append_property("border-block-end-style"sv);
append_property("border-block-end-width"sv);
append_property("border-block-start"sv);
append_property("border-block-start-color"sv);
append_property("border-block-start-style"sv);
append_property("border-block-start-width"sv);
append_property("border-bottom"sv);
append_property("border-bottom-color"sv);
append_property("border-bottom-left-radius"sv);
append_property("border-bottom-right-radius"sv);
append_property("border-bottom-style"sv);
append_property("border-bottom-width"sv);
append_property("border-color"sv);
append_property("border-inline-end"sv);
append_property("border-inline-end-color"sv);
append_property("border-inline-end-style"sv);
append_property("border-inline-end-width"sv);
append_property("border-inline-start"sv);
append_property("border-inline-start-color"sv);
append_property("border-inline-start-style"sv);
append_property("border-inline-start-width"sv);
append_property("border-left"sv);
append_property("border-left-color"sv);
append_property("border-left-style"sv);
append_property("border-left-width"sv);
append_property("border-radius"sv);
append_property("border-right"sv);
append_property("border-right-color"sv);
append_property("border-right-style"sv);
append_property("border-right-width"sv);
append_property("border-style"sv);
append_property("border-top"sv);
append_property("border-top-color"sv);
append_property("border-top-left-radius"sv);
append_property("border-top-right-radius"sv);
append_property("border-top-style"sv);
append_property("border-top-width"sv);
append_property("border-width"sv);
return;
}
if (property == "#custom-properties"sv) {
append_property("custom"sv);
return;
}
if (property == "#font-properties"sv) {
// https://drafts.csswg.org/css-fonts/#property-index
append_property("font"sv);
append_property("font-family"sv);
append_property("font-feature-settings"sv);
// FIXME: font-kerning
append_property("font-language-override"sv);
// FIXME: font-optical-sizing
// FIXME: font-palette
append_property("font-size"sv);
// FIXME: font-size-adjust
append_property("font-style"sv);
// FIXME: font-synthesis and longhands
append_property("font-variant"sv);
append_property("font-variant-alternates"sv);
append_property("font-variant-caps"sv);
append_property("font-variant-east-asian"sv);
append_property("font-variant-emoji"sv);
append_property("font-variant-ligatures"sv);
append_property("font-variant-numeric"sv);
append_property("font-variant-position"sv);
append_property("font-variation-settings"sv);
append_property("font-weight"sv);
append_property("font-width"sv);
return;
}
if (property == "#inline-layout-properties"sv) {
// https://drafts.csswg.org/css-inline/#property-index
// FIXME: alignment-baseline
// FIXME: baseline-shift
// FIXME: baseline-source
// FIXME: dominant-baseline
// FIXME: initial-letter
// FIXME: initial-letter-align
// FIXME: initial-letter-wrap
// FIXME: inline-sizing
// FIXME: line-edge-fit
append_property("line-height"sv);
// FIXME: text-box
// FIXME: text-box-edge
// FIXME: text-box-trim
append_property("vertical-align"sv);
return;
}
if (property == "#inline-typesetting-properties"sv) {
// https://drafts.csswg.org/css-text-4/#property-index
// FIXME: hanging-punctuation
// FIXME: hyphenate-character
// FIXME: hyphenate-limit-chars
// FIXME: hyphenate-limit-last
// FIXME: hyphenate-limit-lines
// FIXME: hyphenate-limit-zone
// FIXME: hyphens
append_property("letter-spacing"sv);
// FIXME: line-break
// FIXME: line-padding
// FIXME: overflow-wrap
append_property("tab-size"sv);
append_property("text-align"sv);
// FIXME: text-align-all
// FIXME: text-align-last
// FIXME: text-autospace
// FIXME: text-group-align
append_property("text-indent"sv);
append_property("text-justify"sv);
// FIXME: text-spacing
// FIXME: text-spacing-trim
append_property("text-transform"sv);
// FIXME: text-wrap
// FIXME: text-wrap-mode
// FIXME: text-wrap-style
append_property("white-space"sv);
// FIXME: white-space-collapse
// FIXME: white-space-trim
append_property("word-break"sv);
// FIXME: word-space-transform
append_property("word-spacing"sv);
append_property("word-wrap"sv);
// FIXME: wrap-after
// FIXME: wrap-before
// FIXME: wrap-inside
return;
}
if (property == "#margin-properties"sv) {
append_property("margin"sv);
append_property("margin-block"sv);
append_property("margin-block-end"sv);
append_property("margin-block-start"sv);
append_property("margin-bottom"sv);
append_property("margin-inline"sv);
append_property("margin-inline-end"sv);
append_property("margin-inline-start"sv);
append_property("margin-left"sv);
append_property("margin-right"sv);
append_property("margin-top"sv);
return;
}
if (property == "#padding-properties"sv) {
append_property("padding"sv);
append_property("padding-block"sv);
append_property("padding-block-end"sv);
append_property("padding-block-start"sv);
append_property("padding-bottom"sv);
append_property("padding-inline"sv);
append_property("padding-inline-end"sv);
append_property("padding-inline-start"sv);
append_property("padding-left"sv);
append_property("padding-right"sv);
append_property("padding-top"sv);
return;
}
if (property == "#text-decoration-properties"sv) {
append_property("text-decoration"sv);
append_property("text-decoration-color"sv);
append_property("text-decoration-line"sv);
append_property("text-decoration-style"sv);
append_property("text-decoration-thickness"sv);
return;
}
outln("Error: Unrecognized property group name '{}' in {}", property, name);
exit(1);
});
member_generator.append(R"~~~(
return true;
default:
return false;
}
)~~~");
});
generator.append(R"~~~(
default:
return true;
}
}
Optional<GeneratedPseudoElement> to_generated_pseudo_element(PseudoElement pseudo_element)
{
switch (pseudo_element) {