LibWeb/CSS: Implement CSSMathSum

This commit is contained in:
Sam Atkins 2025-08-19 15:11:43 +01:00 committed by Andreas Kling
commit e17052a343
Notes: github-actions[bot] 2025-08-29 09:59:57 +00:00
16 changed files with 387 additions and 19 deletions

View file

@ -116,6 +116,7 @@ set(SOURCES
CSS/CSSLayerBlockRule.cpp CSS/CSSLayerBlockRule.cpp
CSS/CSSLayerStatementRule.cpp CSS/CSSLayerStatementRule.cpp
CSS/CSSMarginRule.cpp CSS/CSSMarginRule.cpp
CSS/CSSMathSum.cpp
CSS/CSSMathValue.cpp CSS/CSSMathValue.cpp
CSS/CSSMediaRule.cpp CSS/CSSMediaRule.cpp
CSS/CSSNamespaceRule.cpp CSS/CSSNamespaceRule.cpp

View file

@ -0,0 +1,140 @@
/*
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "CSSMathSum.h"
#include <LibWeb/Bindings/CSSMathSumPrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSNumericArray.h>
#include <LibWeb/WebIDL/DOMException.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::CSS {
GC_DEFINE_ALLOCATOR(CSSMathSum);
GC::Ref<CSSMathSum> CSSMathSum::create(JS::Realm& realm, NumericType type, GC::Ref<CSSNumericArray> values)
{
return realm.create<CSSMathSum>(realm, move(type), move(values));
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathsum-cssmathsum
WebIDL::ExceptionOr<GC::Ref<CSSMathSum>> CSSMathSum::construct_impl(JS::Realm& realm, Vector<CSSNumberish> values)
{
// The CSSMathSum(...args) constructor must, when called, perform the following steps:
// 1. Replace each item of args with the result of rectifying a numberish value for the item.
GC::RootVector<GC::Ref<CSSNumericValue>> converted_values { realm.heap() };
converted_values.ensure_capacity(values.size());
for (auto const& value : values) {
converted_values.append(rectify_a_numberish_value(realm, value));
}
// 2. If args is empty, throw a SyntaxError.
if (converted_values.is_empty())
return WebIDL::SyntaxError::create(realm, "Cannot create an empty CSSMathSum"_utf16);
// 3. Let type be the result of adding the types of all the items of args. If type is failure, throw a TypeError.
auto type = converted_values.first()->type();
bool first = true;
for (auto const& value : converted_values) {
if (first) {
first = false;
continue;
}
if (auto added_types = type.added_to(value->type()); added_types.has_value()) {
type = added_types.release_value();
} else {
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot create a CSSMathSum with values of incompatible types"sv };
}
}
// 4. Return a new CSSMathSum whose values internal slot is set to args.
auto values_array = CSSNumericArray::create(realm, { converted_values });
return CSSMathSum::create(realm, move(type), move(values_array));
}
CSSMathSum::CSSMathSum(JS::Realm& realm, NumericType type, GC::Ref<CSSNumericArray> values)
: CSSMathValue(realm, Bindings::CSSMathOperator::Sum, move(type))
, m_values(move(values))
{
}
CSSMathSum::~CSSMathSum() = default;
void CSSMathSum::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSMathSum);
Base::initialize(realm);
}
void CSSMathSum::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_values);
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-cssmathvalue
String CSSMathSum::serialize_math_value(Nested nested, Parens parens) const
{
// NB: Only steps 1 and 3 apply here.
// 1. Let s initially be the empty string.
StringBuilder s;
// 3. Otherwise, if this is a CSSMathSum:
{
// 1. If paren-less is true, continue to the next step; otherwise, if nested is true, append "(" to s;
// otherwise, append "calc(" to s.
if (parens == Parens::With) {
if (nested == Nested::Yes) {
s.append("("sv);
} else {
s.append("calc("sv);
}
}
// 2. Serialize the first item in thiss values internal slot with nested set to true, and append the result
// to s.
s.append(m_values->values().first()->to_string({ .nested = true }));
// 3. For each arg in thiss values internal slot beyond the first:
bool first = true;
for (auto const& arg : m_values->values()) {
if (first) {
first = false;
continue;
}
// 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.
// FIXME: Detect CSSMathNegate once that's implemented.
if (false) {
s.append(" - "sv);
s.append(arg->to_string({ .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(arg->to_string({ .nested = true }));
}
}
// 4. If paren-less is false, append ")" to s,
if (parens == Parens::With)
s.append(")"sv);
// 5. Return s.
return s.to_string_without_validation();
}
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathsum-values
GC::Ref<CSSNumericArray> CSSMathSum::values() const
{
return m_values;
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/CSS/CSSMathValue.h>
namespace Web::CSS {
// https://drafts.css-houdini.org/css-typed-om-1/#cssmathsum
class CSSMathSum : public CSSMathValue {
WEB_PLATFORM_OBJECT(CSSMathSum, CSSMathValue);
GC_DECLARE_ALLOCATOR(CSSMathSum);
public:
[[nodiscard]] static GC::Ref<CSSMathSum> create(JS::Realm&, NumericType, GC::Ref<CSSNumericArray>);
static WebIDL::ExceptionOr<GC::Ref<CSSMathSum>> construct_impl(JS::Realm&, Vector<CSSNumberish>);
virtual ~CSSMathSum() override;
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Visitor&) override;
GC::Ref<CSSNumericArray> values() const;
virtual String serialize_math_value(Nested, Parens) const override;
private:
CSSMathSum(JS::Realm&, NumericType, GC::Ref<CSSNumericArray>);
GC::Ref<CSSNumericArray> m_values;
};
}

View file

@ -0,0 +1,9 @@
#import <CSS/CSSMathValue.idl>
#import <CSS/CSSNumericArray.idl>
// https://drafts.css-houdini.org/css-typed-om-1/#cssmathsum
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSMathSum : CSSMathValue {
constructor(CSSNumberish... args);
readonly attribute CSSNumericArray values;
};

View file

@ -242,6 +242,7 @@ class CSSKeywordValue;
class CSSLayerBlockRule; class CSSLayerBlockRule;
class CSSLayerStatementRule; class CSSLayerStatementRule;
class CSSMarginRule; class CSSMarginRule;
class CSSMathSum;
class CSSMathValue; class CSSMathValue;
class CSSMediaRule; class CSSMediaRule;
class CSSNamespaceRule; class CSSNamespaceRule;

View file

@ -36,6 +36,7 @@ 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)
libweb_js_bindings(CSS/CSSMathSum)
libweb_js_bindings(CSS/CSSMathValue) libweb_js_bindings(CSS/CSSMathValue)
libweb_js_bindings(CSS/CSSMediaRule) libweb_js_bindings(CSS/CSSMediaRule)
libweb_js_bindings(CSS/CSS NAMESPACE) libweb_js_bindings(CSS/CSS NAMESPACE)

View file

@ -49,6 +49,7 @@ CSSKeywordValue
CSSLayerBlockRule CSSLayerBlockRule
CSSLayerStatementRule CSSLayerStatementRule
CSSMarginRule CSSMarginRule
CSSMathSum
CSSMathValue CSSMathValue
CSSMediaRule CSSMediaRule
CSSNamespaceRule CSSNamespaceRule

View file

@ -1,3 +1,30 @@
Harness status: Error Harness status: OK
Found 0 tests Found 25 tests
25 Fail
Fail Constructing a CSSPerspective with a keyword other than none (string) throws a TypeError
Fail Constructing a CSSPerspective with a keyword other than none (CSSKeywordValue) throws a TypeError
Fail Constructing a CSSPerspective with a double throws a TypeError
Fail Constructing a CSSPerspective with a unitless zero throws a TypeError
Fail Constructing a CSSPerspective with a string length throws a TypeError
Fail Constructing a CSSPerspective with a number CSSUnitValue throws a TypeError
Fail Constructing a CSSPerspective with a time dimension CSSUnitValue throws a TypeError
Fail Constructing a CSSPerspective with a CSSMathValue of angle type throws a TypeError
Fail Updating CSSPerspective.length with a keyword other than none (string) throws a TypeError
Fail Updating CSSPerspective.length with a keyword other than none (CSSKeywordValue) throws a TypeError
Fail Updating CSSPerspective.length with a double throws a TypeError
Fail Updating CSSPerspective.length with a unitless zero throws a TypeError
Fail Updating CSSPerspective.length with a string length throws a TypeError
Fail Updating CSSPerspective.length with a number CSSUnitValue throws a TypeError
Fail Updating CSSPerspective.length with a time dimension CSSUnitValue throws a TypeError
Fail Updating CSSPerspective.length with a CSSMathValue of angle type throws a TypeError
Fail CSSPerspective can be constructed from a length CSSUnitValue
Fail CSSPerspective.length can be updated to a length CSSUnitValue
Fail CSSPerspective can be constructed from a CSSMathValue of length type
Fail CSSPerspective.length can be updated to a CSSMathValue of length type
Fail CSSPerspective can be constructed from none (CSSKeywordValue)
Fail CSSPerspective.length can be updated to none (CSSKeywordValue)
Fail CSSPerspective can be constructed from none (string)
Fail CSSPerspective.length can be updated to none (string)
Fail Modifying CSSPerspective.is2D is a no-op

View file

@ -1,3 +1,32 @@
Harness status: Error Harness status: OK
Found 0 tests Found 27 tests
27 Fail
Fail Constructing a CSSRotate with a CSSUnitValue with type other than angle for the angle throws a TypeError
Fail Constructing a CSSRotate with a CSSMathValue that doesn't match <angle> for the angle throws a TypeError
Fail Constructing a CSSRotate with a CSSUnitValue with type other than number for the coordinates throws a TypeError
Fail Constructing a CSSRotate with a CSSMathValue that doesn't match <number> for the coordinates throws a TypeError
Fail Updating CSSRotate.x to a CSSUnitValue with type other than number throws a TypeError
Fail Updating CSSRotate.x to a CSSMathValue that doesn't match <number> throws a TypeError
Fail Updating CSSRotate.y to a CSSUnitValue with type other than number throws a TypeError
Fail Updating CSSRotate.y to a CSSMathValue that doesn't match <number> throws a TypeError
Fail Updating CSSRotate.z to a CSSUnitValue with type other than number throws a TypeError
Fail Updating CSSRotate.z to a CSSMathValue that doesn't match <number> throws a TypeError
Fail Updating CSSRotate.angle to a CSSUnitValue with type other than angle throws a TypeError
Fail Updating CSSRotate.angle to a CSSMathValue that doesn't match <angle> throws a TypeError
Fail CSSRotate can be constructed from a single angle
Fail CSSRotate can be constructed from numberish coordinates
Fail CSSRotate can be constructed from CSSMathValues
Fail CSSRotate.x can be updated to a double
Fail CSSRotate.x can be updated to a number CSSUnitValue
Fail CSSRotate.x can be updated to a CSSMathValue matching <number>
Fail CSSRotate.y can be updated to a double
Fail CSSRotate.y can be updated to a number CSSUnitValue
Fail CSSRotate.y can be updated to a CSSMathValue matching <number>
Fail CSSRotate.z can be updated to a double
Fail CSSRotate.z can be updated to a number CSSUnitValue
Fail CSSRotate.z can be updated to a CSSMathValue matching <number>
Fail CSSRotate.angle can be updated to a degree CSSUnitValue
Fail CSSRotate.angle can be updated to a CSSMathValue matching <angle>
Fail Modifying CSSRotate.is2D can be updated to true or false

View file

@ -1,3 +1,27 @@
Harness status: Error Harness status: OK
Found 0 tests Found 22 tests
22 Fail
Fail Constructing a CSSScale with an angle CSSUnitValue for the coordinates throws a TypeError
Fail Constructing a CSSScale with a CSSMathValue that doesn't match <number> for the coordinates throws a TypeError
Fail Updating CSSScale.x to an angle CSSUnitValue throws a TypeError
Fail Updating CSSScale.x to a CSSMathValue that doesn't match <number> throws a TypeError
Fail Updating CSSScale.y to an angle CSSUnitValue throws a TypeError
Fail Updating CSSScale.y to a CSSMathValue that doesn't match <number> throws a TypeError
Fail Updating CSSScale.z to an angle CSSUnitValue throws a TypeError
Fail Updating CSSScale.z to a CSSMathValue that doesn't match <number> throws a TypeError
Fail CSSScale can be constructed from two number coordinates
Fail CSSScale can be constructed from three number coordinates
Fail CSSScale can be constructed from CSSMathValue coordinates
Fail CSSScale can be constructed from unit canceling length value coordinates
Fail CSSScale.x can be updated to a number
Fail CSSScale.x can be updated to a numberish
Fail CSSScale.x can be updated to a CSSMathValue
Fail CSSScale.y can be updated to a number
Fail CSSScale.y can be updated to a numberish
Fail CSSScale.y can be updated to a CSSMathValue
Fail CSSScale.z can be updated to a number
Fail CSSScale.z can be updated to a numberish
Fail CSSScale.z can be updated to a CSSMathValue
Fail Modifying CSSScale.is2D can be updated to true or false

View file

@ -1,3 +1,35 @@
Harness status: Error Harness status: OK
Found 0 tests Found 30 tests
30 Fail
Fail Constructing a CSSSkew with a keyword throws a TypeError
Fail Constructing a CSSSkew with a double throws a TypeError
Fail Constructing a CSSSkew with a unitless zero throws a TypeError
Fail Constructing a CSSSkew with a string angle throws a TypeError
Fail Constructing a CSSSkew with a number CSSUnitValue throws a TypeError
Fail Constructing a CSSSkew with a time dimension CSSUnitValue throws a TypeError
Fail Constructing a CSSSkew with a CSSMathValue of length type throws a TypeError
Fail Updating CSSSkew.ax with a keyword throws a TypeError
Fail Updating CSSSkew.ax with a double throws a TypeError
Fail Updating CSSSkew.ax with a unitless zero throws a TypeError
Fail Updating CSSSkew.ax with a string angle throws a TypeError
Fail Updating CSSSkew.ax with a number CSSUnitValue throws a TypeError
Fail Updating CSSSkew.ax with a time dimension CSSUnitValue throws a TypeError
Fail Updating CSSSkew.ax with a CSSMathValue of length type throws a TypeError
Fail Updating CSSSkew.ay with a keyword throws a TypeError
Fail Updating CSSSkew.ay with a double throws a TypeError
Fail Updating CSSSkew.ay with a unitless zero throws a TypeError
Fail Updating CSSSkew.ay with a string angle throws a TypeError
Fail Updating CSSSkew.ay with a number CSSUnitValue throws a TypeError
Fail Updating CSSSkew.ay with a time dimension CSSUnitValue throws a TypeError
Fail Updating CSSSkew.ay with a CSSMathValue of length type throws a TypeError
Fail CSSSkew can be constructed from an angle CSSUnitValue and an angle CSSUnitValue
Fail CSSSkew can be constructed from an angle CSSUnitValue and a CSSMathValue of angle type
Fail CSSSkew can be constructed from a CSSMathValue of angle type and an angle CSSUnitValue
Fail CSSSkew can be constructed from a CSSMathValue of angle type and a CSSMathValue of angle type
Fail CSSSkew.ax can be updated to an angle CSSUnitValue
Fail CSSSkew.ax can be updated to a CSSMathValue of angle type
Fail CSSSkew.ay can be updated to an angle CSSUnitValue
Fail CSSSkew.ay can be updated to a CSSMathValue of angle type
Fail Modifying CSSSkew.is2D is a no-op

View file

@ -1,3 +1,24 @@
Harness status: Error Harness status: OK
Found 0 tests Found 19 tests
19 Fail
Fail Constructing a CSSSkewX with a keyword throws a TypeError
Fail Constructing a CSSSkewX with a double throws a TypeError
Fail Constructing a CSSSkewX with a unitless zero throws a TypeError
Fail Constructing a CSSSkewX with a string angle throws a TypeError
Fail Constructing a CSSSkewX with a number CSSUnitValue throws a TypeError
Fail Constructing a CSSSkewX with a time dimension CSSUnitValue throws a TypeError
Fail Constructing a CSSSkewX with a CSSMathValue of length type throws a TypeError
Fail Updating CSSSkewX.ax with a keyword throws a TypeError
Fail Updating CSSSkewX.ax with a double throws a TypeError
Fail Updating CSSSkewX.ax with a unitless zero throws a TypeError
Fail Updating CSSSkewX.ax with a string angle throws a TypeError
Fail Updating CSSSkewX.ax with a number CSSUnitValue throws a TypeError
Fail Updating CSSSkewX.ax with a time dimension CSSUnitValue throws a TypeError
Fail Updating CSSSkewX.ax with a CSSMathValue of length type throws a TypeError
Fail CSSSkewX can be constructed from an angle CSSUnitValue
Fail CSSSkewX can be constructed from a CSSMathValue of angle type
Fail CSSSkew.ax can be updated to an angle CSSUnitValue
Fail CSSSkew.ax can be updated to a CSSMathValue of angle type
Fail Modifying skewX.is2D is a no-op

View file

@ -1,3 +1,24 @@
Harness status: Error Harness status: OK
Found 0 tests Found 19 tests
19 Fail
Fail Constructing a CSSSkewY with a keyword throws a TypeError
Fail Constructing a CSSSkewY with a double throws a TypeError
Fail Constructing a CSSSkewY with a unitless zero throws a TypeError
Fail Constructing a CSSSkewY with a string angle throws a TypeError
Fail Constructing a CSSSkewY with a number CSSUnitValue throws a TypeError
Fail Constructing a CSSSkewY with a time dimension CSSUnitValue throws a TypeError
Fail Constructing a CSSSkewY with a CSSMathValue of length type throws a TypeError
Fail Updating CSSSkewY.ay with a keyword throws a TypeError
Fail Updating CSSSkewY.ay with a double throws a TypeError
Fail Updating CSSSkewY.ay with a unitless zero throws a TypeError
Fail Updating CSSSkewY.ay with a string angle throws a TypeError
Fail Updating CSSSkewY.ay with a number CSSUnitValue throws a TypeError
Fail Updating CSSSkewY.ay with a time dimension CSSUnitValue throws a TypeError
Fail Updating CSSSkewY.ay with a CSSMathValue of length type throws a TypeError
Fail CSSSkewY can be constructed from an angle CSSUnitValue
Fail CSSSkewY can be constructed from a CSSMathValue of angle type
Fail CSSSkewY.ay can be updated to an angle CSSUnitValue
Fail CSSSkewY.ay can be updated to a CSSMathValue of angle type
Fail Modifying skewY.is2D is a no-op

View file

@ -1,3 +1,27 @@
Harness status: Error Harness status: OK
Found 0 tests Found 22 tests
22 Fail
Fail Constructing a CSSTranslate with a CSSUnitValue with type other than length or percent for the coordinates throws a TypeError
Fail Constructing a CSSTranslate with a CSSMathValue that doesn't match <length-percentage> for the coordinates throws a TypeError
Fail Constructing a CSSTranslate with a percent for the Z coordinate throws a TypeError
Fail Updating CSSTranslate.x to a CSSUnitValue with type other than length or percent throws a TypeError
Fail Updating CSSTranslate.x to a CSSMathValue that doesn't match <length-percentage> throws a TypeError
Fail Updating CSSTranslate.y to a CSSUnitValue with type other than length or percent throws a TypeError
Fail Updating CSSTranslate.y to a CSSMathValue that doesn't match <length-percentage> throws a TypeError
Fail Updating CSSTranslate.z to a CSSUnitValue with type other than length or percent throws a TypeError
Fail Updating CSSTranslate.z to a CSSMathValue that doesn't match <length-percentage> throws a TypeError
Fail Updating CSSTranslate.z to a percent throws a TypeError
Fail CSSTranslate can be constructed from two length or percent coordinates
Fail CSSTranslate can be constructed from three length or percent coordinates
Fail CSSTranslate can be constructed from CSSMathValues
Fail CSSTranslate.x can be updated to a length
Fail CSSTranslate.x can be updated to a percent
Fail CSSTranslate.x can be updated to a CSSMathValue
Fail CSSTranslate.y can be updated to a length
Fail CSSTranslate.y can be updated to a percent
Fail CSSTranslate.y can be updated to a CSSMathValue
Fail CSSTranslate.z can be updated to a length
Fail CSSTranslate.z can be updated to a CSSMathValue
Fail Modifying CSSTranslate.is2D can be updated to true or false

View file

@ -2,8 +2,9 @@ Harness status: OK
Found 7 tests Found 7 tests
7 Fail 1 Pass
Fail Adding two types with different non-null percent hints throws TypeError 6 Fail
Pass Adding two types with different non-null percent hints throws TypeError
Fail Adding two types with the same nonzero values returns same type Fail Adding two types with the same nonzero values returns same type
Fail Adding two types with empty maps with returns empty map Fail Adding two types with empty maps with returns empty map
Fail Adding a type with percent returns type with percent hint Fail Adding a type with percent returns type with percent hint

View file

@ -2,14 +2,14 @@ Harness status: OK
Found 11 tests Found 11 tests
2 Pass 3 Pass
9 Fail 8 Fail
Fail Converting a CSSNumericValue to a sum with invalid units throws SyntaxError Fail Converting a CSSNumericValue to a sum with invalid units throws SyntaxError
Fail Converting a CSSNumericValue with an invalid sum value to a sum throws TypeError Fail Converting a CSSNumericValue with an invalid sum value to a sum throws TypeError
Fail Converting a CSSNumericValue with compound units to a sum throws TypeError Fail Converting a CSSNumericValue with compound units to a sum throws TypeError
Pass Converting a CSSNumericValue to a sum with an incompatible unit throws TypeError Pass Converting a CSSNumericValue to a sum with an incompatible unit throws TypeError
Pass Converting a CSSNumericValue to a sum with units that are not addable throws TypeError Pass Converting a CSSNumericValue to a sum with units that are not addable throws TypeError
Fail Converting a CSSNumericValue with leftover units to a sum throws TypeError Pass Converting a CSSNumericValue with leftover units to a sum throws TypeError
Fail Converting CSSNumericValue to a sum with its own unit returns itself Fail Converting CSSNumericValue to a sum with its own unit returns itself
Fail Converting CSSNumericValue to a sum with no arguments returns all the units in sorted order Fail Converting CSSNumericValue to a sum with no arguments returns all the units in sorted order
Fail Converting CSSNumericValue to a sum with a relative unit converts correctly Fail Converting CSSNumericValue to a sum with a relative unit converts correctly