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/CSSImportRule.cpp
CSS/CSSKeyframeRule.cpp CSS/CSSKeyframeRule.cpp
CSS/CSSKeyframesRule.cpp CSS/CSSKeyframesRule.cpp
CSS/CSSKeywordValue.cpp
CSS/CSSLayerBlockRule.cpp CSS/CSSLayerBlockRule.cpp
CSS/CSSLayerStatementRule.cpp CSS/CSSLayerStatementRule.cpp
CSS/CSSMarginRule.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)); 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) CSSStyleValue::CSSStyleValue(JS::Realm& realm, String associated_property, String constructed_from_string)
: PlatformObject(realm) : PlatformObject(realm)
, m_associated_property(move(associated_property)) , 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. // the serialization is the USVString from which the value was constructed.
return m_constructed_from_string.value(); 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. // 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 // FIXME: otherwise, if the value was extracted from the CSSOM
{ {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,8 +2,9 @@ Harness status: OK
Found 4 tests Found 4 tests
4 Fail 2 Pass
Fail CSSKeywordValue constructed from IDL serializes correctly 2 Fail
Fail CSSKeywordValue constructed from IDL serializes to escaped strings 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 DOMString modified through "value" setter serializes correctly
Fail CSSKeywordValue from CSSOM 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 Found 1 tests
1 Fail 1 Pass
Fail Constructing CSSKeywordValue with an empty string throws a TypeError Pass Constructing CSSKeywordValue with an empty string throws a TypeError

View file

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

View file

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