LibWeb: Generate CSS <paint> parsing

This changes the shape of the parsed value when we don't have a fallback
color for a `<url>` value from having no second value in the
`StyleValueList` to having an `EmptyOptionalStyleValue`.
This commit is contained in:
Callum Law 2026-05-06 14:23:30 +12:00 committed by Sam Atkins
parent 099c3fa590
commit 33705dc12f
5 changed files with 11 additions and 53 deletions

View file

@ -335,8 +335,10 @@ public:
if (style_value->is_value_list()) {
auto const& values = style_value->as_value_list().values();
if (values.size() == 1)
return { values[0]->as_url().url(), {} };
VERIFY(values.size() == 2);
if (values[1]->is_empty_optional())
return values[0]->as_url().url();
return { values[0]->as_url().url(), values[1]->to_color(color_resolution_context) };
}

View file

@ -435,7 +435,6 @@ private:
RefPtr<AbstractImageStyleValue const> parse_image_value(TokenStream<ComponentValue>&);
RefPtr<AbstractImageStyleValue const> parse_image_value(TokenStream<ComponentValue>&, AllowImageSet);
RefPtr<ImageSetStyleValue const> parse_image_set_function(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_paint_value(TokenStream<ComponentValue>&);
enum class PositionParsingMode {
Normal,
BackgroundPosition,

View file

@ -2783,56 +2783,6 @@ RefPtr<AbstractImageStyleValue const> Parser::parse_image_value(TokenStream<Comp
return nullptr;
}
// https://svgwg.org/svg2-draft/painting.html#SpecifyingPaint
RefPtr<StyleValue const> Parser::parse_paint_value(TokenStream<ComponentValue>& tokens)
{
// `<paint> = none | <color> | <url> [none | <color>]? | context-fill | context-stroke`
auto parse_color_or_none = [&]() -> Optional<RefPtr<StyleValue const>> {
if (auto color = parse_color_value(tokens))
return color;
// NOTE: <color> also accepts identifiers, so we do this identifier check last.
if (tokens.next_token().is(Token::Type::Ident)) {
auto maybe_keyword = keyword_from_string(tokens.next_token().token().ident());
if (maybe_keyword.has_value()) {
// FIXME: Accept `context-fill` and `context-stroke`
switch (*maybe_keyword) {
case Keyword::None:
tokens.discard_a_token();
return KeywordStyleValue::create(*maybe_keyword);
default:
return nullptr;
}
}
}
return OptionalNone {};
};
// FIXME: Allow context-fill/context-stroke here
if (auto color_or_none = parse_color_or_none(); color_or_none.has_value())
return *color_or_none;
if (auto url = parse_url_value(tokens)) {
tokens.discard_whitespace();
StyleValueVector values;
values.ensure_capacity(2);
values.unchecked_append(url.release_nonnull());
if (auto color_or_none = parse_color_or_none(); color_or_none == nullptr) {
// Fail to parse if the fallback is invalid, but otherwise ignore it.
return nullptr;
} else if (color_or_none.has_value() && *color_or_none && (*color_or_none)->has_color()) {
values.unchecked_append(color_or_none->release_nonnull());
}
return StyleValueList::create(move(values), StyleValueList::Separator::Space, StyleValueList::Collapsible::No);
}
return nullptr;
}
// https://www.w3.org/TR/css-values-4/#position
RefPtr<PositionStyleValue const> Parser::parse_position_value(TokenStream<ComponentValue>& tokens, PositionParsingMode position_parsing_mode)
{

View file

@ -3,6 +3,11 @@
"spec": "https://drafts.csswg.org/css-fonts-4/#font-weight-absolute-values",
"grammar": "[ normal | bold | <number [1,1000]> ]"
},
"<paint>": {
"spec": "https://svgwg.org/svg2-draft/painting.html#SpecifyingPaint",
"grammar": "none | <color> | <url> [none | <color>]?",
"__comment": "FIXME: Support context-fill and context-stroke values here"
},
"<symbol>": {
"spec": "https://drafts.csswg.org/css-counter-styles-3/#typedef-symbol",
"grammar": "<string> | <custom-ident>",

View file

@ -97,6 +97,8 @@ def generate_implementation_file(out: TextIO, value_type_data: dict[str, Any]) -
#include <LibWeb/CSS/StyleValues/CustomIdentStyleValue.h>
#include <LibWeb/CSS/StyleValues/EmptyOptionalStyleValue.h>
#include <LibWeb/CSS/StyleValues/StringStyleValue.h>
#include <LibWeb/CSS/StyleValues/StyleValueList.h>
#include <LibWeb/CSS/StyleValues/URLStyleValue.h>
namespace Web::CSS::Parser {