LibWeb/CSS: Implement CSSStyleValue.parse() and .parseAll()

The "subdivide into iterations" part is left as a FIXME for now, until
we have a way of knowing if a property is a list or not.

The parse_a_css_style_value() helper has an unwieldy return type because
of the requirement that it return either one value or a list of values,
but sticking to the spec here seems worthwhile for now.
This commit is contained in:
Sam Atkins 2025-08-14 13:55:45 +01:00
commit 213a548b1f
Notes: github-actions[bot] 2025-08-21 09:23:18 +00:00
8 changed files with 85 additions and 22 deletions

View file

@ -7,6 +7,8 @@
#include "CSSStyleValue.h"
#include <LibWeb/Bindings/CSSStyleValuePrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/PropertyName.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::CSS {
@ -36,6 +38,59 @@ void CSSStyleValue::initialize(JS::Realm& realm)
Base::initialize(realm);
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssstylevalue-parse
WebIDL::ExceptionOr<GC::Ref<CSSStyleValue>> CSSStyleValue::parse(JS::VM& vm, String property, String css_text)
{
// The parse(property, cssText) method, when invoked, must parse a CSSStyleValue with property property, cssText
// cssText, and parseMultiple set to false, and return the result.
auto result = parse_a_css_style_value(vm, property, css_text, ParseMultiple::No);
if (result.is_exception())
return result.release_error();
return result.value().get<GC::Ref<CSSStyleValue>>();
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssstylevalue-parseall
WebIDL::ExceptionOr<GC::RootVector<GC::Ref<CSSStyleValue>>> CSSStyleValue::parse_all(JS::VM& vm, String property, String css_text)
{
// The parseAll(property, cssText) method, when invoked, must parse a CSSStyleValue with property property, cssText
// cssText, and parseMultiple set to true, and return the result.
auto result = parse_a_css_style_value(vm, property, css_text, ParseMultiple::Yes);
if (result.is_exception())
return result.release_error();
return result.value().get<GC::RootVector<GC::Ref<CSSStyleValue>>>();
}
// https://drafts.css-houdini.org/css-typed-om-1/#parse-a-cssstylevalue
WebIDL::ExceptionOr<Variant<GC::Ref<CSSStyleValue>, GC::RootVector<GC::Ref<CSSStyleValue>>>> CSSStyleValue::parse_a_css_style_value(JS::VM& vm, String property, String css_text, ParseMultiple parse_multiple)
{
// 1. If property is not a custom property name string, set property to property ASCII lowercased.
if (!is_a_custom_property_name_string(property))
property = property.to_ascii_lowercase();
// 2. If property is not a valid CSS property, throw a TypeError.
if (!is_a_valid_css_property(property))
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("'{}' is not a valid CSS property", property)) };
// 3. Attempt to parse cssText according to propertys grammar.
// If this fails, throw a TypeError.
// Otherwise, let whole value be the parsed result.
auto property_id = property_id_from_string(property).release_value();
auto whole_value = parse_css_value(Parser::ParsingParams {}, css_text, property_id);
if (!whole_value)
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Failed to parse '{}' as a value for '{}' property", css_text, property)) };
// FIXME: 4. Subdivide into iterations whole value, according to property, and let values be the result.
// 5. For each value in values, replace it with the result of reifying value for property.
GC::RootVector<GC::Ref<CSSStyleValue>> reified_values { vm.heap() };
reified_values.append(whole_value->reify(*vm.current_realm(), property));
// 6. If parseMultiple is false, return values[0]. Otherwise, return values.
if (parse_multiple == ParseMultiple::No)
return reified_values.take_first();
return reified_values;
}
// https://drafts.css-houdini.org/css-typed-om-1/#stylevalue-serialization
String CSSStyleValue::to_string() const
{

View file

@ -22,6 +22,9 @@ public:
virtual void initialize(JS::Realm&) override;
static WebIDL::ExceptionOr<GC::Ref<CSSStyleValue>> parse(JS::VM&, String property, String css_text);
static WebIDL::ExceptionOr<GC::RootVector<GC::Ref<CSSStyleValue>>> parse_all(JS::VM&, String property, String css_text);
virtual String to_string() const;
protected:
@ -30,6 +33,12 @@ protected:
private:
explicit CSSStyleValue(JS::Realm&, String associated_property, String constructed_from_string);
enum class ParseMultiple : u8 {
No,
Yes,
};
static WebIDL::ExceptionOr<Variant<GC::Ref<CSSStyleValue>, GC::RootVector<GC::Ref<CSSStyleValue>>>> parse_a_css_style_value(JS::VM&, String property, String css_text, ParseMultiple);
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssstylevalue-associatedproperty-slot
Optional<String> m_associated_property;

View file

@ -2,6 +2,6 @@
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSStyleValue {
stringifier;
[FIXME, Exposed=Window] static CSSStyleValue parse(USVString property, USVString cssText);
[FIXME, Exposed=Window] static sequence<CSSStyleValue> parseAll(USVString property, USVString cssText);
[Exposed=Window] static CSSStyleValue parse(USVString property, USVString cssText);
[Exposed=Window] static sequence<CSSStyleValue> parseAll(USVString property, USVString cssText);
};

View file

@ -2,7 +2,6 @@ Harness status: OK
Found 2 tests
1 Pass
1 Fail
Fail CSS identifiers are normalized from String to CSSKeywordValues
2 Pass
Pass CSS identifiers are normalized from String to CSSKeywordValues
Pass CSS identifiers are normalized from CSSOM to CSSKeywordValues

View file

@ -2,9 +2,9 @@ Harness status: OK
Found 5 tests
5 Fail
Fail CSSStyleValue.parse() with a valid property returns a CSSStyleValue
Fail CSSStyleValue.parse() is not case sensitive
Fail CSSStyleValue.parse() with a valid list-valued property returns a CSSStyleValue
Fail CSSStyleValue.parse() with a valid shorthand property returns a CSSStyleValue
Fail CSSStyleValue.parse() with a valid custom property returns a CSSStyleValue
5 Pass
Pass CSSStyleValue.parse() with a valid property returns a CSSStyleValue
Pass CSSStyleValue.parse() is not case sensitive
Pass CSSStyleValue.parse() with a valid list-valued property returns a CSSStyleValue
Pass CSSStyleValue.parse() with a valid shorthand property returns a CSSStyleValue
Pass CSSStyleValue.parse() with a valid custom property returns a CSSStyleValue

View file

@ -2,9 +2,10 @@ Harness status: OK
Found 5 tests
5 Fail
Fail CSSStyleValue.parseAll() with a valid property returns a list with a single CSSStyleValue
Fail CSSStyleValue.parseAll() is not case sensitive
4 Pass
1 Fail
Pass CSSStyleValue.parseAll() with a valid property returns a list with a single CSSStyleValue
Pass CSSStyleValue.parseAll() is not case sensitive
Fail CSSStyleValue.parseAll() with a valid list-valued property returns a list with a single CSSStyleValue
Fail CSSStyleValue.parseAll() with a valid shorthand property returns a CSSStyleValue
Fail CSSStyleValue.parseAll() with a valid custom property returns a list with a single CSSStyleValue
Pass CSSStyleValue.parseAll() with a valid shorthand property returns a CSSStyleValue
Pass CSSStyleValue.parseAll() with a valid custom property returns a list with a single CSSStyleValue

View file

@ -2,9 +2,8 @@ Harness status: OK
Found 4 tests
3 Pass
1 Fail
4 Pass
Pass CSSKeywordValue constructed from IDL serializes correctly
Pass CSSKeywordValue constructed from IDL serializes to escaped strings
Fail CSSKeywordValue from DOMString modified through "value" setter serializes correctly
Pass CSSKeywordValue from DOMString modified through "value" setter serializes correctly
Pass CSSKeywordValue from CSSOM modified through "value" setter serializes correctly

View file

@ -2,6 +2,6 @@ Harness status: OK
Found 2 tests
2 Fail
Fail CSSStyleValue parsed from string serializes to given string
Fail Shorthand CSSStyleValue parsed from string serializes to given string
2 Pass
Pass CSSStyleValue parsed from string serializes to given string
Pass Shorthand CSSStyleValue parsed from string serializes to given string