ladybird/Libraries/LibWeb/CSS/CSSKeywordValue.cpp
Shannon Booth 637fd51595 LibWeb: Unify WebIDL C++ type generation
Represent WebIDL C++ types with a single CppType model that tracks
nullability, optional presence, and contained storage.

GC-like values now use GC::Ref/GC::Ptr directly, while containers choose
"plain", "Root", or "Conservative" container types depending on what
they contain. For example, sequence<Element> becomes a RootVector of
GC::Ref values, while sequence<SomeDictionary> becomes a
ConservativeVector only when the dictionary contains GC-like values.
This moves the generated bindings away from wrapping GC values in
GC::Root by default.

This has broad fallout as the types passed to interfaces for GC
objects changes almost fully across the board.
2026-05-23 18:26:12 +02:00

137 lines
5.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "CSSKeywordValue.h"
#include <AK/StringBuilder.h>
#include <LibWeb/Bindings/CSSKeywordValue.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/Keyword.h>
#include <LibWeb/CSS/PropertyNameAndID.h>
#include <LibWeb/CSS/Serialize.h>
#include <LibWeb/CSS/StyleValues/CustomIdentStyleValue.h>
#include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
#include <LibWeb/CSS/StyleValues/KeywordStyleValue.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
void CSSKeywordValue::serialize(StringBuilder& builder) 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
serialize_an_identifier(builder, m_value);
}
WebIDL::ExceptionOr<String> CSSKeywordValue::to_string() const
{
StringBuilder builder;
serialize(builder);
return builder.to_string_without_validation();
}
// https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation
WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSKeywordValue::create_an_internal_representation(PropertyNameAndID const& property, PerformTypeCheck perform_type_check) const
{
// If value is a CSSStyleValue subclass,
// If value does not match the grammar of a list-valued property iteration of property, throw a TypeError.
bool const matches_grammar = [&] {
// https://drafts.css-houdini.org/css-typed-om-1/#cssstylevalue-match-a-grammar
// A CSSKeywordValue matches an <ident> specified in a grammar if its value internal slot matches the
// identifier.
// If case-folding rules are in effect normally for that <ident> (such as Auto matching the keyword auto
// specified in the grammar for width), they apply to this comparison as well.
if (is_css_wide_keyword(m_value))
return true;
if (property.is_custom_property()) {
// FIXME: If this is a registered custom property, check if that allows the keyword.
return true;
}
auto keyword = keyword_from_string(m_value);
return keyword.has_value() && property_accepts_keyword(property.id(), keyword.value());
}();
if (perform_type_check == PerformTypeCheck::Yes && !matches_grammar) {
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Property '{}' does not accept the keyword '{}'", property.name(), m_value)) };
}
// If any component of propertys CSS grammar has a limited numeric range, and the corresponding part of value
// is a CSSUnitValue that is outside of that range, replace that value with the result of wrapping it in a
// fresh CSSMathSum whose values internal slot contains only that part of value.
// NB: Non-applicable.
// Return the value.
if (auto keyword = keyword_from_string(m_value); keyword.has_value()) {
if (!is_css_wide_keyword(m_value)) {
// NB: Non-css-wide keyword `display` values are represented internally by DisplayStyleValue, not KeywordStyleValue.
if (property.id() == PropertyID::Display)
return DisplayStyleValue::create(Display::from_keyword(keyword.release_value()));
}
return KeywordStyleValue::create(*keyword);
}
return CustomIdentStyleValue::create(m_value);
}
// https://drafts.css-houdini.org/css-typed-om-1/#rectify-a-keywordish-value
GC::Ref<CSSKeywordValue> rectify_a_keywordish_value(JS::Realm& realm, CSSKeywordish const& keywordish)
{
// To rectify a keywordish value val, perform the following steps:
return keywordish.visit(
// 1. If val is a CSSKeywordValue, return val.
[](GC::Ref<CSSKeywordValue> const& value) -> GC::Ref<CSSKeywordValue> {
return value;
},
// 2. If val is a DOMString, return a new CSSKeywordValue with its value internal slot set to val.
[&realm](FlyString const& value) -> GC::Ref<CSSKeywordValue> {
return CSSKeywordValue::create(realm, value);
});
}
}