LibWeb/CSS: Implement CSSKeywordValue

CSSStyleValue is adjusted to allow for subclasses. Serialization for
CSSKeywordValue is implemented differently than the spec says because
of a spec bug: https://github.com/w3c/csswg-drafts/issues/12545
This commit is contained in:
Sam Atkins 2025-08-14 12:33:40 +01:00
commit a93c6a347f
Notes: github-actions[bot] 2025-08-21 09:23:30 +00:00
14 changed files with 150 additions and 21 deletions

View file

@ -112,6 +112,7 @@ set(SOURCES
CSS/CSSImportRule.cpp
CSS/CSSKeyframeRule.cpp
CSS/CSSKeyframesRule.cpp
CSS/CSSKeywordValue.cpp
CSS/CSSLayerBlockRule.cpp
CSS/CSSLayerStatementRule.cpp
CSS/CSSMarginRule.cpp

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "CSSKeywordValue.h"
#include <LibWeb/Bindings/CSSKeywordValuePrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/Serialize.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::CSS {
GC_DEFINE_ALLOCATOR(CSSKeywordValue);
GC::Ref<CSSKeywordValue> CSSKeywordValue::create(JS::Realm& realm, FlyString value)
{
return realm.create<CSSKeywordValue>(realm, move(value));
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-csskeywordvalue-csskeywordvalue
WebIDL::ExceptionOr<GC::Ref<CSSKeywordValue>> CSSKeywordValue::construct_impl(JS::Realm& realm, FlyString value)
{
// 1. If value is an empty string, throw a TypeError.
if (value.is_empty())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot create a CSSKeywordValue with an empty string as the value"sv };
// 2. Otherwise, return a new CSSKeywordValue with its value internal slot set to value.
return CSSKeywordValue::create(realm, move(value));
}
CSSKeywordValue::CSSKeywordValue(JS::Realm& realm, FlyString value)
: CSSStyleValue(realm)
, m_value(move(value))
{
}
void CSSKeywordValue::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSKeywordValue);
Base::initialize(realm);
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-csskeywordvalue-value
WebIDL::ExceptionOr<void> CSSKeywordValue::set_value(FlyString value)
{
// 1. If value is an empty string, throw a TypeError.
if (value.is_empty())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot set CSSKeywordValue.value to an empty string"sv };
// 2. Otherwise, set thiss value internal slot, to value.
m_value = move(value);
return {};
}
// https://drafts.css-houdini.org/css-typed-om-1/#keywordvalue-serialization
String CSSKeywordValue::to_string() const
{
// To serialize a CSSKeywordValue this:
// 1. Return thiss value internal slot.
// AD-HOC: Serialize it as an identifier. Spec issue: https://github.com/w3c/csswg-drafts/issues/12545
return serialize_an_identifier(m_value);
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/FlyString.h>
#include <LibWeb/CSS/CSSStyleValue.h>
namespace Web::CSS {
// https://drafts.css-houdini.org/css-typed-om-1/#csskeywordvalue
class CSSKeywordValue : public CSSStyleValue {
WEB_PLATFORM_OBJECT(CSSKeywordValue, CSSStyleValue);
GC_DECLARE_ALLOCATOR(CSSKeywordValue);
public:
[[nodiscard]] static GC::Ref<CSSKeywordValue> create(JS::Realm&, FlyString value);
static WebIDL::ExceptionOr<GC::Ref<CSSKeywordValue>> construct_impl(JS::Realm&, FlyString value);
virtual ~CSSKeywordValue() override = default;
FlyString const& value() const { return m_value; }
WebIDL::ExceptionOr<void> set_value(FlyString value);
virtual String to_string() const override;
private:
explicit CSSKeywordValue(JS::Realm&, FlyString value);
virtual void initialize(JS::Realm&) override;
FlyString m_value;
};
}

View file

@ -0,0 +1,10 @@
#import <CSS/CSSStyleValue.idl>
// https://drafts.css-houdini.org/css-typed-om-1/#csskeywordvalue
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSKeywordValue : CSSStyleValue {
constructor(USVString value);
attribute USVString value;
};
typedef (DOMString or CSSKeywordValue) CSSKeywordish;

View file

@ -18,6 +18,11 @@ GC::Ref<CSSStyleValue> CSSStyleValue::create(JS::Realm& realm, String associated
return realm.create<CSSStyleValue>(realm, move(associated_property), move(constructed_from_string));
}
CSSStyleValue::CSSStyleValue(JS::Realm& realm)
: PlatformObject(realm)
{
}
CSSStyleValue::CSSStyleValue(JS::Realm& realm, String associated_property, String constructed_from_string)
: PlatformObject(realm)
, m_associated_property(move(associated_property))
@ -39,9 +44,10 @@ String CSSStyleValue::to_string() const
// the serialization is the USVString from which the value was constructed.
return m_constructed_from_string.value();
}
// FIXME: otherwise, if the value was constructed using an IDL constructor
// otherwise, if the value was constructed using an IDL constructor
{
// the serialization is specified in the sections below.
// NB: This is handled by subclasses overriding this to_string() method.
}
// FIXME: otherwise, if the value was extracted from the CSSOM
{

View file

@ -20,13 +20,16 @@ public:
virtual ~CSSStyleValue() override = default;
virtual void initialize(JS::Realm&) override;
virtual String to_string() const;
protected:
explicit CSSStyleValue(JS::Realm&);
private:
explicit CSSStyleValue(JS::Realm&, String associated_property, String constructed_from_string);
virtual void initialize(JS::Realm&) override;
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssstylevalue-associatedproperty-slot
Optional<String> m_associated_property;

View file

@ -238,6 +238,7 @@ class CSSGroupingRule;
class CSSImportRule;
class CSSKeyframeRule;
class CSSKeyframesRule;
class CSSKeywordValue;
class CSSLayerBlockRule;
class CSSLayerStatementRule;
class CSSMarginRule;

View file

@ -32,6 +32,7 @@ libweb_js_bindings(CSS/CSSGroupingRule)
libweb_js_bindings(CSS/CSSImportRule)
libweb_js_bindings(CSS/CSSKeyframeRule)
libweb_js_bindings(CSS/CSSKeyframesRule)
libweb_js_bindings(CSS/CSSKeywordValue)
libweb_js_bindings(CSS/CSSLayerBlockRule)
libweb_js_bindings(CSS/CSSLayerStatementRule)
libweb_js_bindings(CSS/CSSMarginRule)

View file

@ -54,6 +54,7 @@ static bool is_platform_object(Type const& type)
"Credential"sv,
"CredentialsContainer"sv,
"CryptoKey"sv,
"CSSKeywordValue"sv,
"CSSStyleValue"sv,
"CustomStateSet"sv,
"DataTransfer"sv,

View file

@ -45,6 +45,7 @@ CSSGroupingRule
CSSImportRule
CSSKeyframeRule
CSSKeyframesRule
CSSKeywordValue
CSSLayerBlockRule
CSSLayerStatementRule
CSSMarginRule

View file

@ -2,8 +2,9 @@ Harness status: OK
Found 4 tests
4 Fail
Fail CSSKeywordValue constructed from IDL serializes correctly
Fail CSSKeywordValue constructed from IDL serializes to escaped strings
2 Pass
2 Fail
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
Fail CSSKeywordValue from CSSOM modified through "value" setter serializes correctly

View file

@ -2,5 +2,5 @@ Harness status: OK
Found 1 tests
1 Fail
Fail Constructing CSSKeywordValue with an empty string throws a TypeError
1 Pass
Pass Constructing CSSKeywordValue with an empty string throws a TypeError

View file

@ -2,10 +2,10 @@ Harness status: OK
Found 6 tests
6 Fail
Fail CSSKeywordValue.value can be updated to a CSS wide keyword
Fail CSSKeywordValue.value can be updated to a CSS keyword
Fail CSSKeywordValue.value can be updated to an unsupported CSS keyword
Fail CSSKeywordValue.value can be updated to a string containing multiple tokens
Fail CSSKeywordValue.value can be updated to a unicode string
Fail Updating CSSKeywordValue.value with an empty string throws a TypeError
6 Pass
Pass CSSKeywordValue.value can be updated to a CSS wide keyword
Pass CSSKeywordValue.value can be updated to a CSS keyword
Pass CSSKeywordValue.value can be updated to an unsupported CSS keyword
Pass CSSKeywordValue.value can be updated to a string containing multiple tokens
Pass CSSKeywordValue.value can be updated to a unicode string
Pass Updating CSSKeywordValue.value with an empty string throws a TypeError

View file

@ -2,9 +2,9 @@ Harness status: OK
Found 5 tests
5 Fail
Fail CSSKeywordValue can be constructed from a CSS wide keyword
Fail CSSKeywordValue can be constructed from a CSS keyword
Fail CSSKeywordValue can be constructed from an unsupported CSS keyword
Fail CSSKeywordValue can be constructed from a string containing multiple tokens
Fail CSSKeywordValue can be constructed from a unicode string
5 Pass
Pass CSSKeywordValue can be constructed from a CSS wide keyword
Pass CSSKeywordValue can be constructed from a CSS keyword
Pass CSSKeywordValue can be constructed from an unsupported CSS keyword
Pass CSSKeywordValue can be constructed from a string containing multiple tokens
Pass CSSKeywordValue can be constructed from a unicode string