LibWeb: Return Utf16String from CSS Typed OM serialization

Make CSS Typed OM to_string() implementations produce Utf16String
instead of building UTF-8 strings and converting at the JS binding
boundary. Use Utf16StringBuilder for numeric, math, and transform
serialization paths that already feed CSS Typed OM stringification.

Keep the older CSSOM StyleValue serializer on StringBuilder for now, and
convert only where Typed OM source values or parser re-entry still need
the existing UTF-8 representation.
This commit is contained in:
Andreas Kling 2026-06-22 14:15:11 +02:00 committed by Andreas Kling
parent a1bc2a6223
commit 26b0801a78
40 changed files with 173 additions and 146 deletions

View file

@ -32,7 +32,7 @@ void CSSImageValue::initialize(JS::Realm& realm)
}
// https://drafts.css-houdini.org/css-typed-om-1/#stylevalue-serialization
WebIDL::ExceptionOr<String> CSSImageValue::to_string() const
WebIDL::ExceptionOr<Utf16String> CSSImageValue::to_string() const
{
// AD-HOC: The spec doesn't say how to serialize this, as it's intentionally a black box.
// We just rely on CSSStyleValue serializing its held StyleValue.

View file

@ -20,7 +20,7 @@ public:
virtual ~CSSImageValue() override = default;
virtual WebIDL::ExceptionOr<String> to_string() const override;
virtual WebIDL::ExceptionOr<Utf16String> to_string() const override;
virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&, PerformTypeCheck) const override;
private:

View file

@ -5,7 +5,6 @@
*/
#include "CSSKeywordValue.h"
#include <AK/StringBuilder.h>
#include <LibWeb/Bindings/CSSKeywordValue.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/Keyword.h>
@ -61,19 +60,19 @@ WebIDL::ExceptionOr<void> CSSKeywordValue::set_value(FlyString value)
}
// https://drafts.css-houdini.org/css-typed-om-1/#keywordvalue-serialization
void CSSKeywordValue::serialize(StringBuilder& builder) const
void CSSKeywordValue::serialize(Utf16StringBuilder& 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);
builder.append(Utf16String::from_utf8_without_validation(serialize_an_identifier(m_value)));
}
WebIDL::ExceptionOr<String> CSSKeywordValue::to_string() const
WebIDL::ExceptionOr<Utf16String> CSSKeywordValue::to_string() const
{
StringBuilder builder;
Utf16StringBuilder builder;
serialize(builder);
return builder.to_string_without_validation();
return builder.to_string();
}
// https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/FlyString.h>
#include <AK/Utf16StringBuilder.h>
#include <LibWeb/CSS/CSSStyleValue.h>
namespace Web::CSS {
@ -28,8 +29,8 @@ public:
FlyString const& value() const { return m_value; }
WebIDL::ExceptionOr<void> set_value(FlyString value);
void serialize(StringBuilder&) const;
virtual WebIDL::ExceptionOr<String> to_string() const override;
void serialize(Utf16StringBuilder&) const;
virtual WebIDL::ExceptionOr<Utf16String> to_string() const override;
virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&, PerformTypeCheck) const override;
private:

View file

@ -68,16 +68,16 @@ void CSSMathClamp::visit_edges(Visitor& visitor)
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-cssmathvalue
void CSSMathClamp::serialize_math_value(StringBuilder& s, Nested, Parens) const
void CSSMathClamp::serialize_math_value(Utf16StringBuilder& s, Nested, Parens) const
{
// AD-HOC: The spec is missing serialization rules for CSSMathClamp: https://github.com/w3c/css-houdini-drafts/issues/1152
s.append("clamp("sv);
s.append_ascii("clamp("sv);
m_lower->serialize(s, { .nested = true, .parenless = true });
s.append(", "sv);
s.append_ascii(", "sv);
m_value->serialize(s, { .nested = true, .parenless = true });
s.append(", "sv);
s.append_ascii(", "sv);
m_upper->serialize(s, { .nested = true, .parenless = true });
s.append(')');
s.append_ascii(')');
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathclamp-lower

View file

@ -28,7 +28,7 @@ public:
GC::Ref<CSSNumericValue> value() const;
GC::Ref<CSSNumericValue> upper() const;
virtual void serialize_math_value(StringBuilder&, Nested, Parens) const override;
virtual void serialize_math_value(Utf16StringBuilder&, Nested, Parens) const override;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
virtual Optional<SumValue> create_a_sum_value() const override;

View file

@ -54,7 +54,7 @@ void CSSMathInvert::visit_edges(Visitor& visitor)
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-cssmathvalue
void CSSMathInvert::serialize_math_value(StringBuilder& s, Nested nested, Parens parens) const
void CSSMathInvert::serialize_math_value(Utf16StringBuilder& s, Nested nested, Parens parens) const
{
// NB: Only steps 1 and 6 apply here.
// 1. Let s initially be the empty string.
@ -65,21 +65,21 @@ void CSSMathInvert::serialize_math_value(StringBuilder& s, Nested nested, Parens
// otherwise, append "calc(" to s.
if (parens == Parens::With) {
if (nested == Nested::Yes) {
s.append('(');
s.append_ascii('(');
} else {
s.append("calc("sv);
s.append_ascii("calc("sv);
}
}
// 2. Append "1 / " to s.
s.append("1 / "sv);
s.append_ascii("1 / "sv);
// 3. Serialize thiss value internal slot with nested set to true, and append the result to s.
m_value->serialize(s, { .nested = true });
// 4. If paren-less is false, append ")" to s,
if (parens == Parens::With)
s.append(')');
s.append_ascii(')');
// 5. Return s.
}

View file

@ -26,7 +26,7 @@ public:
GC::Ref<CSSNumericValue> value() const;
virtual void serialize_math_value(StringBuilder&, Nested, Parens) const override;
virtual void serialize_math_value(Utf16StringBuilder&, Nested, Parens) const override;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
virtual Optional<SumValue> create_a_sum_value() const override;

View file

@ -86,7 +86,7 @@ void CSSMathMax::visit_edges(Visitor& visitor)
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-cssmathvalue
void CSSMathMax::serialize_math_value(StringBuilder& s, Nested, Parens) const
void CSSMathMax::serialize_math_value(Utf16StringBuilder& s, Nested, Parens) const
{
// NB: Only steps 1 and 2 apply here.
// 1. Let s initially be the empty string.
@ -94,7 +94,7 @@ void CSSMathMax::serialize_math_value(StringBuilder& s, Nested, Parens) const
// 2. If this is a CSSMathMin or CSSMathMax:
{
// 1. Append "min(" or "max(" to s, as appropriate.
s.append("max("sv);
s.append_ascii("max("sv);
// 2. For each arg in thiss values internal slot, serialize arg with nested and paren-less both true, and
// append the result to s, appending a ", " between successive values.
@ -103,13 +103,13 @@ void CSSMathMax::serialize_math_value(StringBuilder& s, Nested, Parens) const
if (first) {
first = false;
} else {
s.append(", "sv);
s.append_ascii(", "sv);
}
arg->serialize(s, { .nested = true, .parenless = true });
}
// 3. Append ")" to s and return s.
s.append(')');
s.append_ascii(')');
}
}

View file

@ -27,7 +27,7 @@ public:
GC::Ref<CSSNumericArray> values() const;
virtual void serialize_math_value(StringBuilder&, Nested, Parens) const override;
virtual void serialize_math_value(Utf16StringBuilder&, Nested, Parens) const override;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
virtual Optional<SumValue> create_a_sum_value() const override;

View file

@ -87,7 +87,7 @@ void CSSMathMin::visit_edges(Visitor& visitor)
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-cssmathvalue
void CSSMathMin::serialize_math_value(StringBuilder& s, Nested, Parens) const
void CSSMathMin::serialize_math_value(Utf16StringBuilder& s, Nested, Parens) const
{
// NB: Only steps 1 and 2 apply here.
// 1. Let s initially be the empty string.
@ -95,7 +95,7 @@ void CSSMathMin::serialize_math_value(StringBuilder& s, Nested, Parens) const
// 2. If this is a CSSMathMin or CSSMathMax:
{
// 1. Append "min(" or "max(" to s, as appropriate.
s.append("min("sv);
s.append_ascii("min("sv);
// 2. For each arg in thiss values internal slot, serialize arg with nested and paren-less both true, and
// append the result to s, appending a ", " between successive values.
@ -104,13 +104,13 @@ void CSSMathMin::serialize_math_value(StringBuilder& s, Nested, Parens) const
if (first) {
first = false;
} else {
s.append(", "sv);
s.append_ascii(", "sv);
}
arg->serialize(s, { .nested = true, .parenless = true });
}
// 3. Append ")" to s and return s.
s.append(')');
s.append_ascii(')');
}
}

View file

@ -27,7 +27,7 @@ public:
GC::Ref<CSSNumericArray> values() const;
virtual void serialize_math_value(StringBuilder&, Nested, Parens) const override;
virtual void serialize_math_value(Utf16StringBuilder&, Nested, Parens) const override;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
virtual Optional<SumValue> create_a_sum_value() const override;

View file

@ -51,7 +51,7 @@ void CSSMathNegate::visit_edges(Visitor& visitor)
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-cssmathvalue
void CSSMathNegate::serialize_math_value(StringBuilder& s, Nested nested, Parens parens) const
void CSSMathNegate::serialize_math_value(Utf16StringBuilder& s, Nested nested, Parens parens) const
{
// NB: Only steps 1 and 4 apply here.
// 1. Let s initially be the empty string.
@ -62,21 +62,21 @@ void CSSMathNegate::serialize_math_value(StringBuilder& s, Nested nested, Parens
// otherwise, append "calc(" to s.
if (parens == Parens::With) {
if (nested == Nested::Yes) {
s.append('(');
s.append_ascii('(');
} else {
s.append("calc("sv);
s.append_ascii("calc("sv);
}
}
// 2. Append "-" to s.
s.append('-');
s.append_ascii('-');
// 3. Serialize thiss value internal slot with nested set to true, and append the result to s.
m_value->serialize(s, { .nested = true });
// 4. If paren-less is false, append ")" to s,
if (parens == Parens::With)
s.append(')');
s.append_ascii(')');
// 5. Return s.
}

View file

@ -26,7 +26,7 @@ public:
GC::Ref<CSSNumericValue> value() const;
virtual void serialize_math_value(StringBuilder&, Nested, Parens) const override;
virtual void serialize_math_value(Utf16StringBuilder&, Nested, Parens) const override;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
virtual Optional<SumValue> create_a_sum_value() const override;

View file

@ -86,7 +86,7 @@ void CSSMathProduct::visit_edges(Visitor& visitor)
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-cssmathvalue
void CSSMathProduct::serialize_math_value(StringBuilder& s, Nested nested, Parens parens) const
void CSSMathProduct::serialize_math_value(Utf16StringBuilder& s, Nested nested, Parens parens) const
{
// NB: Only steps 1 and 5 apply here.
// 1. Let s initially be the empty string.
@ -97,9 +97,9 @@ void CSSMathProduct::serialize_math_value(StringBuilder& s, Nested nested, Paren
// otherwise, append "calc(" to s.
if (parens == Parens::With) {
if (nested == Nested::Yes) {
s.append('(');
s.append_ascii('(');
} else {
s.append("calc("sv);
s.append_ascii("calc("sv);
}
}
@ -117,20 +117,20 @@ void CSSMathProduct::serialize_math_value(StringBuilder& s, Nested nested, Paren
// 1. If arg is a CSSMathInvert, append " / " to s, then serialize args value internal slot with nested
// set to true, and append the result to s.
if (auto* invert = as_if<CSSMathInvert>(*arg)) {
s.append(" / "sv);
s.append_ascii(" / "sv);
invert->value()->serialize(s, { .nested = true });
}
// 2. Otherwise, append " * " to s, then serialize arg with nested set to true, and append the result to s.
else {
s.append(" * "sv);
s.append_ascii(" * "sv);
arg->serialize(s, { .nested = true });
}
}
// 4. If paren-less is false, append ")" to s,
if (parens == Parens::With)
s.append(')');
s.append_ascii(')');
// 5. Return s.
}

View file

@ -27,7 +27,7 @@ public:
GC::Ref<CSSNumericArray> values() const;
virtual void serialize_math_value(StringBuilder&, Nested, Parens) const override;
virtual void serialize_math_value(Utf16StringBuilder&, Nested, Parens) const override;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
virtual Optional<SumValue> create_a_sum_value() const override;

View file

@ -84,7 +84,7 @@ void CSSMathSum::visit_edges(Visitor& visitor)
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-cssmathvalue
void CSSMathSum::serialize_math_value(StringBuilder& s, Nested nested, Parens parens) const
void CSSMathSum::serialize_math_value(Utf16StringBuilder& s, Nested nested, Parens parens) const
{
// NB: Only steps 1 and 3 apply here.
// 1. Let s initially be the empty string.
@ -95,9 +95,9 @@ void CSSMathSum::serialize_math_value(StringBuilder& s, Nested nested, Parens pa
// otherwise, append "calc(" to s.
if (parens == Parens::With) {
if (nested == Nested::Yes) {
s.append('(');
s.append_ascii('(');
} else {
s.append("calc("sv);
s.append_ascii("calc("sv);
}
}
@ -116,20 +116,20 @@ void CSSMathSum::serialize_math_value(StringBuilder& s, Nested nested, Parens pa
// 1. If arg is a CSSMathNegate, append " - " to s, then serialize args value internal slot with nested
// set to true, and append the result to s.
if (auto* negate = as_if<CSSMathNegate>(*arg)) {
s.append(" - "sv);
s.append_ascii(" - "sv);
negate->value()->serialize(s, { .nested = true });
}
// 2. Otherwise, append " + " to s, then serialize arg with nested set to true, and append the result to s.
else {
s.append(" + "sv);
s.append_ascii(" + "sv);
arg->serialize(s, { .nested = true });
}
}
// 4. If paren-less is false, append ")" to s,
if (parens == Parens::With)
s.append(')');
s.append_ascii(')');
// 5. Return s.
}

View file

@ -27,7 +27,7 @@ public:
GC::Ref<CSSNumericArray> values() const;
virtual void serialize_math_value(StringBuilder&, Nested, Parens) const override;
virtual void serialize_math_value(Utf16StringBuilder&, Nested, Parens) const override;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
virtual Optional<SumValue> create_a_sum_value() const override;

View file

@ -30,7 +30,7 @@ public:
With,
Without,
};
virtual void serialize_math_value(StringBuilder&, Nested, Parens) const = 0;
virtual void serialize_math_value(Utf16StringBuilder&, Nested, Parens) const = 0;
virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&, PerformTypeCheck) const final override;

View file

@ -5,7 +5,6 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/StringBuilder.h>
#include <LibWeb/Bindings/CSSNumericValue.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSMathInvert.h>
@ -404,7 +403,7 @@ Bindings::CSSNumericType CSSNumericValue::type_for_bindings() const
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-cssnumericvalue
void CSSNumericValue::serialize(StringBuilder& builder, SerializationParams const& params) const
void CSSNumericValue::serialize(Utf16StringBuilder& builder, SerializationParams const& params) const
{
// To serialize a CSSNumericValue this, given an optional minimum, a numeric value, and optional maximum, a numeric value:
// 1. If this is a CSSUnitValue, serialize a CSSUnitValue from this, passing minimum and maximum. Return the result.
@ -419,11 +418,11 @@ void CSSNumericValue::serialize(StringBuilder& builder, SerializationParams cons
params.parenless ? CSSMathValue::Parens::Without : CSSMathValue::Parens::With);
}
String CSSNumericValue::to_string(SerializationParams const& params) const
Utf16String CSSNumericValue::to_string(SerializationParams const& params) const
{
StringBuilder builder;
Utf16StringBuilder builder;
serialize(builder, params);
return builder.to_string_without_validation();
return builder.to_string();
}
// https://drafts.css-houdini.org/css-typed-om-1/#rectify-a-numberish-value

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/FlyString.h>
#include <AK/Utf16StringBuilder.h>
#include <LibWeb/Bindings/CSSNumericValue.h>
#include <LibWeb/CSS/CSSStyleValue.h>
#include <LibWeb/CSS/NumericType.h>
@ -56,9 +57,9 @@ public:
Bindings::CSSNumericType type_for_bindings() const;
NumericType const& type() const { return m_type; }
virtual WebIDL::ExceptionOr<String> to_string() const final override { return to_string({}); }
void serialize(StringBuilder&, SerializationParams const&) const;
String to_string(SerializationParams const&) const;
virtual WebIDL::ExceptionOr<Utf16String> to_string() const final override { return to_string({}); }
void serialize(Utf16StringBuilder&, SerializationParams const&) const;
Utf16String to_string(SerializationParams const&) const;
static WebIDL::ExceptionOr<GC::Ref<CSSNumericValue>> parse(JS::VM&, String const& css_text);

View file

@ -87,22 +87,23 @@ void CSSPerspective::visit_edges(Visitor& visitor)
WebIDL::ExceptionOr<Utf16String> CSSPerspective::to_string() const
{
// 1. Let s initially be "perspective(".
StringBuilder builder { StringBuilder::Mode::UTF16 };
builder.append("perspective("sv);
Utf16StringBuilder builder;
builder.append_ascii("perspective("sv);
// 2. Serialize thiss length internal slot, with a minimum of 0px, and append it to s.
auto serialized_length = TRY(m_length.visit(
[](GC::Ref<CSSNumericValue> const& numeric_value) -> WebIDL::ExceptionOr<String> {
return numeric_value->to_string({ .minimum = 0 });
TRY(m_length.visit(
[&](GC::Ref<CSSNumericValue> const& numeric_value) -> WebIDL::ExceptionOr<void> {
numeric_value->serialize(builder, { .minimum = 0 });
return {};
},
[](GC::Ref<CSSKeywordValue> const& keyword_value) -> WebIDL::ExceptionOr<String> {
return keyword_value->to_string();
[&](GC::Ref<CSSKeywordValue> const& keyword_value) -> WebIDL::ExceptionOr<void> {
builder.append(TRY(keyword_value->to_string()));
return {};
}));
builder.append(serialized_length);
// 3. Append ")" to s, and return s.
builder.append(")"sv);
return builder.to_utf16_string();
builder.append_ascii(')');
return builder.to_string();
}
WebIDL::ExceptionOr<GC::Ref<Geometry::DOMMatrix>> CSSPerspective::to_matrix() const

View file

@ -98,49 +98,49 @@ void CSSRotate::visit_edges(Visitor& visitor)
WebIDL::ExceptionOr<Utf16String> CSSRotate::to_string() const
{
// 1. Let s initially be the empty string.
StringBuilder builder { StringBuilder::Mode::UTF16 };
Utf16StringBuilder builder;
// 2. If thiss is2D internal slot is false:
if (!is_2d()) {
// 1. Append "rotate3d(" to s.
builder.append("rotate3d("sv);
builder.append_ascii("rotate3d("sv);
// 2. Serialize thiss x internal slot, and append it to s.
m_x->serialize(builder, {});
// 3. Append ", " to s.
builder.append(", "sv);
builder.append_ascii(", "sv);
// 4. Serialize thiss y internal slot, and append it to s.
m_y->serialize(builder, {});
// 5. Append ", " to s.
builder.append(", "sv);
builder.append_ascii(", "sv);
// 6. Serialize thiss z internal slot, and append it to s.
m_z->serialize(builder, {});
// 7. Append "," to s.
builder.append(", "sv);
builder.append_ascii(", "sv);
// 8. Serialize thiss angle internal slot, and append it to s.
m_angle->serialize(builder, {});
// 9. Append ")" to s, and return s.
builder.append(")"sv);
return builder.to_utf16_string();
builder.append_ascii(')');
return builder.to_string();
}
// 2. Otherwise:
else {
// 1. Append "rotate(" to s.
builder.append("rotate("sv);
builder.append_ascii("rotate("sv);
// 2. Serialize thiss angle internal slot, and append it to s.
m_angle->serialize(builder, {});
// 3. Append ")" to s, and return s.
builder.append(")"sv);
return builder.to_utf16_string();
builder.append_ascii(')');
return builder.to_string();
}
}

View file

@ -82,38 +82,38 @@ void CSSScale::visit_edges(Visitor& visitor)
WebIDL::ExceptionOr<Utf16String> CSSScale::to_string() const
{
// 1. Let s initially be the empty string.
StringBuilder builder { StringBuilder::Mode::UTF16 };
Utf16StringBuilder builder;
// 2. If thiss is2D internal slot is false:
if (!is_2d()) {
// 1. Append "scale3d(" to s.
builder.append("scale3d("sv);
builder.append_ascii("scale3d("sv);
// 2. Serialize thiss x internal slot, and append it to s.
m_x->serialize(builder, {});
// 3. Append ", " to s.
builder.append(", "sv);
builder.append_ascii(", "sv);
// 4. Serialize thiss y internal slot, and append it to s.
m_y->serialize(builder, {});
// 5. Append ", " to s.
builder.append(", "sv);
builder.append_ascii(", "sv);
// 6. Serialize thiss z internal slot, and append it to s.
m_z->serialize(builder, {});
// 7. Append ")" to s, and return s.
builder.append(")"sv);
return builder.to_utf16_string();
builder.append_ascii(')');
return builder.to_string();
}
// 3. Otherwise:
else {
// 1. Append "scale(" to s.
builder.append("scale("sv);
builder.append_ascii("scale("sv);
// 2. Serialize thiss x internal slot, and append it to s.
m_x->serialize(builder, {});
@ -123,14 +123,14 @@ WebIDL::ExceptionOr<Utf16String> CSSScale::to_string() const
// Upstream issue: https://github.com/w3c/css-houdini-drafts/issues/1161
// 4. Otherwise, append ", " to s.
builder.append(", "sv);
builder.append_ascii(", "sv);
// 5. Serialize thiss y internal slot, and append it to s.
m_y->serialize(builder, {});
// 6. Append ")" to s, and return s.
builder.append(")"sv);
return builder.to_utf16_string();
builder.append_ascii(')');
return builder.to_string();
}
}

View file

@ -65,27 +65,27 @@ void CSSSkew::visit_edges(Visitor& visitor)
WebIDL::ExceptionOr<Utf16String> CSSSkew::to_string() const
{
// 1. Let s initially be "skew(".
StringBuilder builder { StringBuilder::Mode::UTF16 };
builder.append("skew("sv);
Utf16StringBuilder builder;
builder.append_ascii("skew("sv);
// 2. Serialize thiss ax internal slot, and append it to s.
m_ax->serialize(builder, {});
// 3. If thiss ay internal slot is a CSSUnitValue with a value of 0, then append ")" to s and return s.
if (auto* ay_unit_value = as_if<CSSUnitValue>(*m_ay); ay_unit_value && ay_unit_value->value() == 0) {
builder.append(")"sv);
return builder.to_utf16_string();
builder.append_ascii(')');
return builder.to_string();
}
// 4. Otherwise, append ", " to s.
builder.append(", "sv);
builder.append_ascii(", "sv);
// 5. Serialize thiss ay internal slot, and append it to s.
m_ay->serialize(builder, {});
// 6. Append ")" to s, and return s.
builder.append(")"sv);
return builder.to_utf16_string();
builder.append_ascii(')');
return builder.to_string();
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-csstransformcomponent-tomatrix

View file

@ -60,15 +60,15 @@ void CSSSkewX::visit_edges(Visitor& visitor)
WebIDL::ExceptionOr<Utf16String> CSSSkewX::to_string() const
{
// 1. Let s initially be "skewX(".
StringBuilder builder { StringBuilder::Mode::UTF16 };
builder.append("skewX("sv);
Utf16StringBuilder builder;
builder.append_ascii("skewX("sv);
// 2. Serialize thiss ax internal slot, and append it to s.
m_ax->serialize(builder, {});
// 3. Append ")" to s, and return s.
builder.append(")"sv);
return builder.to_utf16_string();
builder.append_ascii(')');
return builder.to_string();
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-csstransformcomponent-tomatrix

View file

@ -60,15 +60,15 @@ void CSSSkewY::visit_edges(Visitor& visitor)
WebIDL::ExceptionOr<Utf16String> CSSSkewY::to_string() const
{
// 1. Let s initially be "skewY(".
StringBuilder builder { StringBuilder::Mode::UTF16 };
builder.append("skewY("sv);
Utf16StringBuilder builder;
builder.append_ascii("skewY("sv);
// 2. Serialize thiss ay internal slot, and append it to s.
m_ay->serialize(builder, {});
// 3. Append ")" to s, and return s.
builder.append(")"sv);
return builder.to_utf16_string();
builder.append_ascii(')');
return builder.to_string();
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-csstransformcomponent-tomatrix

View file

@ -104,7 +104,7 @@ WebIDL::ExceptionOr<Variant<GC::Ref<CSSStyleValue>, GC::RootVector<GC::Ref<CSSSt
}
// https://drafts.css-houdini.org/css-typed-om-1/#stylevalue-serialization
WebIDL::ExceptionOr<String> CSSStyleValue::to_string() const
WebIDL::ExceptionOr<Utf16String> CSSStyleValue::to_string() const
{
// FIXME: if the value was constructed from a USVString
// NB: Basically, if this was constructed with "parse a CSSStyleValue", regardless of what CSSStyleValue type it is now.
@ -119,11 +119,11 @@ WebIDL::ExceptionOr<String> CSSStyleValue::to_string() const
// FIXME: otherwise, if the value was extracted from the CSSOM
// NB: For CSSStyleValue itself, we use the source value we were created from.
if (m_source_value)
return m_source_value->to_string(SerializationMode::Normal);
return Utf16String::from_utf8_without_validation(m_source_value->to_string(SerializationMode::Normal));
{
// the serialization is specified in §6.7 Serialization from CSSOM Values below.
}
return String {};
return Utf16String::from_utf8_without_validation(""sv);
}
// https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/Utf16FlyString.h>
#include <AK/Utf16String.h>
#include <LibWeb/Bindings/PlatformObject.h>
namespace Web::CSS {
@ -35,7 +36,7 @@ public:
};
static WebIDL::ExceptionOr<Variant<GC::Ref<CSSStyleValue>, GC::RootVector<GC::Ref<CSSStyleValue>>>> parse_a_css_style_value(JS::VM&, Utf16FlyString property, String css_text, ParseMultiple);
virtual WebIDL::ExceptionOr<String> to_string() const;
virtual WebIDL::ExceptionOr<Utf16String> to_string() const;
// FIXME: Temporary hack. Really we want to pass something like a CalculationContext with the valid types and ranges.
enum class PerformTypeCheck : u8 {

View file

@ -5,6 +5,7 @@
*/
#include "CSSTransformValue.h"
#include <AK/Utf16StringBuilder.h>
#include <LibWeb/Bindings/CSSTransformValue.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSTransformComponent.h>
@ -141,19 +142,19 @@ WebIDL::ExceptionOr<GC::Ref<Geometry::DOMMatrix>> CSSTransformValue::to_matrix()
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-csstransformvalue
WebIDL::ExceptionOr<String> CSSTransformValue::to_string() const
WebIDL::ExceptionOr<Utf16String> CSSTransformValue::to_string() const
{
// 1. Return the result of serializing each item in thiss values to iterate over, then concatenating them
// separated by " ".
StringBuilder builder;
Utf16StringBuilder builder;
bool first = true;
for (auto const& transform : m_transforms) {
if (!first)
builder.append(" "sv);
builder.append_ascii(' ');
first = false;
builder.append(TRY(transform->to_string()));
}
return builder.to_string_without_validation();
return builder.to_string();
}
// https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation

View file

@ -30,7 +30,7 @@ public:
bool is_2d() const;
WebIDL::ExceptionOr<GC::Ref<Geometry::DOMMatrix>> to_matrix() const;
virtual WebIDL::ExceptionOr<String> to_string() const override;
virtual WebIDL::ExceptionOr<Utf16String> to_string() const override;
virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&, PerformTypeCheck) const override;

View file

@ -81,49 +81,49 @@ void CSSTranslate::visit_edges(Visitor& visitor)
WebIDL::ExceptionOr<Utf16String> CSSTranslate::to_string() const
{
// 1. Let s initially be the empty string.
StringBuilder builder { StringBuilder::Mode::UTF16 };
Utf16StringBuilder builder;
// 2. If thiss is2D internal slot is false:
if (!is_2d()) {
// 1. Append "translate3d(" to s.
builder.append("translate3d("sv);
builder.append_ascii("translate3d("sv);
// 2. Serialize thiss x internal slot, and append it to s.
m_x->serialize(builder, {});
// 3. Append ", " to s.
builder.append(", "sv);
builder.append_ascii(", "sv);
// 4. Serialize thiss y internal slot, and append it to s.
m_y->serialize(builder, {});
// 5. Append ", " to s.
builder.append(", "sv);
builder.append_ascii(", "sv);
// 6. Serialize thiss z internal slot, and append it to s.
m_z->serialize(builder, {});
// 7. Append ")" to s, and return s.
builder.append(")"sv);
return builder.to_utf16_string();
builder.append_ascii(')');
return builder.to_string();
}
// 3. Otherwise:
else {
// 1. Append "translate(" to s.
builder.append("translate("sv);
builder.append_ascii("translate("sv);
// 2. Serialize thiss x internal slot, and append it to s.
m_x->serialize(builder, {});
// 3. Append ", " to s.
builder.append(", "sv);
builder.append_ascii(", "sv);
// 4. Serialize thiss y internal slot, and append it to s.
m_y->serialize(builder, {});
// 5. Append ")" to s, and return s.
builder.append(")"sv);
return builder.to_utf16_string();
builder.append_ascii(')');
return builder.to_string();
}
}

View file

@ -92,7 +92,7 @@ void CSSUnitValue::set_value(double value)
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-cssunitvalue
void CSSUnitValue::serialize_unit_value(StringBuilder& builder, Optional<double> minimum, Optional<double> maximum) const
void CSSUnitValue::serialize_unit_value(Utf16StringBuilder& builder, Optional<double> minimum, Optional<double> maximum) const
{
// To serialize a CSSUnitValue this, with optional arguments minimum, a numeric value, and maximum, a numeric value:
@ -107,7 +107,7 @@ void CSSUnitValue::serialize_unit_value(StringBuilder& builder, Optional<double>
|| (maximum.has_value() && m_value > maximum.value());
if (needs_calc_wrapper)
builder.append("calc("sv);
builder.append_ascii("calc("sv);
// 2. Set s to the result of serializing a <number> from value, per CSSOM §6.7.2 Serializing CSS Values.
serialize_a_number(builder, m_value);
@ -120,16 +120,16 @@ void CSSUnitValue::serialize_unit_value(StringBuilder& builder, Optional<double>
// -> "percent"
else if (m_unit == "percent"_fly_string) {
// Append "%" to s.
builder.append('%');
builder.append_ascii('%');
}
// -> anything else
else {
// Append unit to s.
builder.append(m_unit.to_ascii_lowercase());
builder.append_ascii(m_unit.to_ascii_lowercase().bytes_as_string_view());
}
if (needs_calc_wrapper)
builder.append(')');
builder.append_ascii(')');
// 5. Return s.
}

View file

@ -28,7 +28,7 @@ public:
FlyString const& unit() const { return m_unit; }
void serialize_unit_value(StringBuilder&, Optional<double> minimum, Optional<double> maximum) const;
void serialize_unit_value(Utf16StringBuilder&, Optional<double> minimum, Optional<double> maximum) const;
GC::Ptr<CSSUnitValue> converted_to_unit(FlyString const& unit) const;

View file

@ -5,6 +5,7 @@
*/
#include "CSSUnparsedValue.h"
#include <AK/Utf16StringBuilder.h>
#include <LibWeb/Bindings/CSSUnparsedValue.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSVariableReferenceValue.h>
@ -131,17 +132,17 @@ bool CSSUnparsedValue::contains_unparsed_value(CSSUnparsedValue const& needle) c
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-cssunparsedvalue
WebIDL::ExceptionOr<String> CSSUnparsedValue::to_string() const
WebIDL::ExceptionOr<Utf16String> CSSUnparsedValue::to_string() const
{
// AD-HOC: It's possible for one of the m_tokens to contain this in its fallback slot, or a similar situation with
// more levels of nesting. To avoid crashing, do a scan for that first and return the empty string.
// Spec issue: https://github.com/w3c/css-houdini-drafts/issues/1158
if (contains_unparsed_value(*this))
return ""_string;
return Utf16String::from_utf8_without_validation(""sv);
// To serialize a CSSUnparsedValue this:
// 1. Let s initially be the empty string.
StringBuilder s;
Utf16StringBuilder s;
// 2. For each item in thiss [[tokens]] internal slot:
for (auto const& item : m_tokens) {
@ -150,7 +151,7 @@ WebIDL::ExceptionOr<String> CSSUnparsedValue::to_string() const
TRY(item.visit(
// 1. If item is a USVString, append it to s.
[&](String const& string) -> WebIDL::ExceptionOr<void> {
s.append(string);
s.append(Utf16String::from_utf8_without_validation(string));
return {};
},
// 2. Otherwise, item is a CSSVariableReferenceValue. Serialize it, then append the result to s.
@ -161,7 +162,7 @@ WebIDL::ExceptionOr<String> CSSUnparsedValue::to_string() const
}
// 3. Return s.
return s.to_string_without_validation();
return s.to_string();
}
// https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation
@ -181,7 +182,8 @@ WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSUnparsedValue::create_an
// NB: CSSUnparsedValue stores a list of strings, each of which may contain any number of tokens. So the simplest
// way to convert it to ComponentValues is to serialize and then parse it.
auto string = TRY(to_string());
auto utf16_string = TRY(to_string());
auto string = MUST(utf16_string.utf16_view().to_utf8());
auto parser = Parser::Parser::create(Parser::ParsingParams {}, string);
auto component_values = parser.parse_as_list_of_component_values();

View file

@ -29,7 +29,7 @@ public:
virtual WebIDL::ExceptionOr<void> set_value_of_existing_indexed_property(u32, JS::Value) override;
virtual WebIDL::ExceptionOr<void> set_value_of_new_indexed_property(u32, JS::Value) override;
virtual WebIDL::ExceptionOr<String> to_string() const override;
virtual WebIDL::ExceptionOr<Utf16String> to_string() const override;
virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&, PerformTypeCheck) const override;
private:

View file

@ -5,6 +5,7 @@
*/
#include "CSSVariableReferenceValue.h"
#include <AK/Utf16StringBuilder.h>
#include <LibWeb/Bindings/CSSVariableReferenceValue.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSUnparsedValue.h>
@ -89,26 +90,26 @@ WebIDL::ExceptionOr<void> CSSVariableReferenceValue::set_fallback(GC::Ptr<CSSUnp
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-cssvariablereferencevalue
WebIDL::ExceptionOr<String> CSSVariableReferenceValue::to_string() const
WebIDL::ExceptionOr<Utf16String> CSSVariableReferenceValue::to_string() const
{
// To serialize a CSSVariableReferenceValue this:
// 1. Let s initially be "var(".
StringBuilder s;
s.append("var("sv);
Utf16StringBuilder s;
s.append_ascii("var("sv);
// 2. Append thiss variable internal slot to s.
s.append(m_variable);
s.append(Utf16String::from_utf8_without_validation(m_variable));
// 3. If thiss fallback internal slot is not null, append ", " to s, then serialize the fallback internal slot and append it to s.
if (m_fallback) {
// AD-HOC: Tested behaviour requires we append "," without the space. https://github.com/w3c/css-houdini-drafts/issues/1148
s.append(","sv);
s.append_ascii(',');
s.append(TRY(m_fallback->to_string()));
}
// 4. Append ")" to s and return s.
s.append(")"sv);
return s.to_string_without_validation();
s.append_ascii(')');
return s.to_string();
}
}

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/FlyString.h>
#include <AK/Utf16String.h>
#include <LibWeb/Bindings/PlatformObject.h>
namespace Web::CSS {
@ -27,7 +28,7 @@ public:
GC::Ptr<CSSUnparsedValue> fallback() const;
WebIDL::ExceptionOr<void> set_fallback(GC::Ptr<CSSUnparsedValue>);
WebIDL::ExceptionOr<String> to_string() const;
WebIDL::ExceptionOr<Utf16String> to_string() const;
private:
CSSVariableReferenceValue(JS::Realm&, FlyString variable, GC::Ptr<CSSUnparsedValue> fallback);

View file

@ -149,6 +149,24 @@ void serialize_a_number(StringBuilder& builder, double value)
builder.appendff("{:.6}", value);
}
void serialize_a_number(Utf16StringBuilder& builder, double value)
{
// -> <number>
// A base-ten number using digits 0-9 (U+0030 to U+0039) in the shortest form possible, using "." to separate
// decimals (if any), rounding the value if necessary to not produce more than 6 decimals, preceded by "-"
// (U+002D) if it is negative.
// NOTE: scientific notation is not used.
// AD-HOC: If the number is small enough that it would not print any digits when rounded, serialize it as 0.
if (AK::abs(value) < 0.0000005) {
builder.append_ascii('0');
return;
}
// FIXME: Prevent scientific notation for large values.
builder.appendff("{:.6}", value);
}
String serialize_an_identifier(StringView ident)
{
StringBuilder builder;

View file

@ -9,6 +9,7 @@
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <AK/StringView.h>
#include <AK/Utf16StringBuilder.h>
#include <AK/Vector.h>
#include <LibGfx/Color.h>
#include <LibGfx/Font/UnicodeRange.h>
@ -25,6 +26,7 @@ void serialize_a_string(StringBuilder&, StringView string);
WEB_API void serialize_a_url(StringBuilder&, StringView url);
void serialize_unicode_ranges(StringBuilder&, Vector<Gfx::UnicodeRange> const& unicode_ranges);
WEB_API void serialize_a_number(StringBuilder&, double value);
WEB_API void serialize_a_number(Utf16StringBuilder&, double value);
String serialize_an_identifier(StringView ident);
String serialize_a_string(StringView string);