LibWeb/CSS: Implement CSSMathClamp

As the final CSSMathFoo class, this makes the serialization test finally
run instead of crashing.
This commit is contained in:
Sam Atkins 2025-08-21 12:47:26 +01:00 committed by Andreas Kling
commit 177395155a
Notes: github-actions[bot] 2025-08-29 09:59:11 +00:00
11 changed files with 200 additions and 9 deletions

View file

@ -116,6 +116,7 @@ set(SOURCES
CSS/CSSLayerBlockRule.cpp
CSS/CSSLayerStatementRule.cpp
CSS/CSSMarginRule.cpp
CSS/CSSMathClamp.cpp
CSS/CSSMathInvert.cpp
CSS/CSSMathMax.cpp
CSS/CSSMathMin.cpp

View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "CSSMathClamp.h"
#include <LibWeb/Bindings/CSSMathClampPrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSMathNegate.h>
#include <LibWeb/CSS/CSSNumericArray.h>
#include <LibWeb/WebIDL/DOMException.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::CSS {
GC_DEFINE_ALLOCATOR(CSSMathClamp);
GC::Ref<CSSMathClamp> CSSMathClamp::create(JS::Realm& realm, NumericType type, GC::Ref<CSSNumericValue> lower, GC::Ref<CSSNumericValue> value, GC::Ref<CSSNumericValue> upper)
{
return realm.create<CSSMathClamp>(realm, move(type), move(lower), move(value), move(upper));
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathclamp-cssmathclamp
WebIDL::ExceptionOr<GC::Ref<CSSMathClamp>> CSSMathClamp::construct_impl(JS::Realm& realm, CSSNumberish lower, CSSNumberish value, CSSNumberish upper)
{
// The CSSMathClamp(lower, value, upper) constructor must, when called, perform the following steps:
// 1. Replace lower, value, and upper with the result of rectifying a numberish value for each.
auto lower_rectified = rectify_a_numberish_value(realm, lower);
auto value_rectified = rectify_a_numberish_value(realm, value);
auto upper_rectified = rectify_a_numberish_value(realm, upper);
// 2. Let type be the result of adding the types of lower, value, and upper. If type is failure, throw a TypeError.
auto type = lower_rectified->type()
.added_to(value_rectified->type())
.map([&](auto& type) { return type.added_to(upper_rectified->type()); });
if (!type.has_value()) {
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot create a CSSMathClamp with values of incompatible types"sv };
}
// 3. Return a new CSSMathClamp whose lower, value, and upper internal slots are set to lower, value, and upper, respectively.
return CSSMathClamp::create(realm, type->release_value(), move(lower_rectified), move(value_rectified), move(upper_rectified));
}
CSSMathClamp::CSSMathClamp(JS::Realm& realm, NumericType type, GC::Ref<CSSNumericValue> lower, GC::Ref<CSSNumericValue> value, GC::Ref<CSSNumericValue> upper)
: CSSMathValue(realm, Bindings::CSSMathOperator::Clamp, move(type))
, m_lower(move(lower))
, m_value(move(value))
, m_upper(move(upper))
{
}
CSSMathClamp::~CSSMathClamp() = default;
void CSSMathClamp::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSMathClamp);
Base::initialize(realm);
}
void CSSMathClamp::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_lower);
visitor.visit(m_value);
visitor.visit(m_upper);
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-cssmathvalue
String CSSMathClamp::serialize_math_value(Nested, Parens) const
{
// AD-HOC: The spec is missing serialization rules for CSSMathClamp: https://github.com/w3c/css-houdini-drafts/issues/1152
StringBuilder s;
s.append("clamp("sv);
s.append(m_lower->to_string({ .nested = true, .parenless = true }));
s.append(", "sv);
s.append(m_value->to_string({ .nested = true, .parenless = true }));
s.append(", "sv);
s.append(m_upper->to_string({ .nested = true, .parenless = true }));
s.append(")"sv);
return s.to_string_without_validation();
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathclamp-lower
GC::Ref<CSSNumericValue> CSSMathClamp::lower() const
{
// AD-HOC: No spec definition.
return m_lower;
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathclamp-value
GC::Ref<CSSNumericValue> CSSMathClamp::value() const
{
// AD-HOC: No spec definition.
return m_value;
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathclamp-upper
GC::Ref<CSSNumericValue> CSSMathClamp::upper() const
{
// AD-HOC: No spec definition.
return m_upper;
}
}

View file

@ -0,0 +1,40 @@
/*
* 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/#cssmathclamp
class CSSMathClamp : public CSSMathValue {
WEB_PLATFORM_OBJECT(CSSMathClamp, CSSMathValue);
GC_DECLARE_ALLOCATOR(CSSMathClamp);
public:
[[nodiscard]] static GC::Ref<CSSMathClamp> create(JS::Realm&, NumericType, GC::Ref<CSSNumericValue> lower, GC::Ref<CSSNumericValue> value, GC::Ref<CSSNumericValue> upper);
static WebIDL::ExceptionOr<GC::Ref<CSSMathClamp>> construct_impl(JS::Realm&, CSSNumberish lower, CSSNumberish value, CSSNumberish upper);
virtual ~CSSMathClamp() override;
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Visitor&) override;
GC::Ref<CSSNumericValue> lower() const;
GC::Ref<CSSNumericValue> value() const;
GC::Ref<CSSNumericValue> upper() const;
virtual String serialize_math_value(Nested, Parens) const override;
private:
CSSMathClamp(JS::Realm&, NumericType, GC::Ref<CSSNumericValue> lower, GC::Ref<CSSNumericValue> value, GC::Ref<CSSNumericValue> upper);
GC::Ref<CSSNumericValue> m_lower;
GC::Ref<CSSNumericValue> m_value;
GC::Ref<CSSNumericValue> m_upper;
};
}

View file

@ -0,0 +1,11 @@
#import <CSS/CSSMathValue.idl>
#import <CSS/CSSNumericValue.idl>
// https://drafts.css-houdini.org/css-typed-om-1/#cssmathclamp
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSMathClamp : CSSMathValue {
constructor(CSSNumberish lower, CSSNumberish value, CSSNumberish upper);
readonly attribute CSSNumericValue lower;
readonly attribute CSSNumericValue value;
readonly attribute CSSNumericValue upper;
};

View file

@ -9,6 +9,7 @@
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSMathNegate.h>
#include <LibWeb/CSS/CSSNumericArray.h>
#include <LibWeb/CSS/CSSNumericValue.h>
#include <LibWeb/WebIDL/DOMException.h>
#include <LibWeb/WebIDL/ExceptionOr.h>

View file

@ -242,6 +242,7 @@ class CSSKeywordValue;
class CSSLayerBlockRule;
class CSSLayerStatementRule;
class CSSMarginRule;
class CSSMathClamp;
class CSSMathInvert;
class CSSMathMax;
class CSSMathMin;

View file

@ -36,6 +36,7 @@ libweb_js_bindings(CSS/CSSKeywordValue)
libweb_js_bindings(CSS/CSSLayerBlockRule)
libweb_js_bindings(CSS/CSSLayerStatementRule)
libweb_js_bindings(CSS/CSSMarginRule)
libweb_js_bindings(CSS/CSSMathClamp)
libweb_js_bindings(CSS/CSSMathInvert)
libweb_js_bindings(CSS/CSSMathMax)
libweb_js_bindings(CSS/CSSMathMin)

View file

@ -49,6 +49,7 @@ CSSKeywordValue
CSSLayerBlockRule
CSSLayerStatementRule
CSSMarginRule
CSSMathClamp
CSSMathInvert
CSSMathMax
CSSMathMin

View file

@ -1,3 +1,30 @@
Harness status: Error
Harness status: OK
Found 0 tests
Found 25 tests
25 Pass
Pass CSSMathMax with one argument serializes correctly
Pass CSSMathMax with more than one argument serializes correctly
Pass CSSMathMax with pixel arguments serializes correctly
Pass CSSMathMax containing nested CSSMathValues serializes correctly
Pass CSSMathMin with one argument serializes correctly
Pass CSSMathMin with more than one argument serializes correctly
Pass CSSMathMin with pixel arguments serializes correctly
Pass CSSMathMin containing nested CSSMathValues serializes correctly
Pass CSSMathClamp with lower, value and upper arguments serializes correctly
Pass CSSMathClamp with pixel arguments serializes correctly
Pass CSSMathClamp containing nested CSSMathValues serializes correctly
Pass CSSMathSum with one argument serializes correctly
Pass CSSMathSum with more than one argument serializes correctly
Pass CSSMathSum with a CSSMathNegate as first value serializes correctly
Pass CSSMathSum containing a CSSMathNegate after first value serializes correctly
Pass CSSMathSum nested inside a CSSMathValue serializes correctly
Pass CSSMathNegate serializes correctly
Pass CSSMathNegate nested inside a CSSMathValue serializes correctly
Pass CSSMathProduct with one argument serializes correctly
Pass CSSMathProduct with more than one argument serializes correctly
Pass CSSMathProduct with a CSSMathInvert as first value serializes correctly
Pass CSSMathProduct containing a CSSMathInvert after first value serializes correctly
Pass CSSMathProduct nested inside a CSSMathValue serializes correctly
Pass CSSMathInvert serializes correctly
Pass CSSMathInvert nested inside a CSSMathValue serializes correctly

View file

@ -1,8 +1,8 @@
Harness status: Error
Harness status: OK
Found 20 tests
Found 23 tests
20 Pass
23 Pass
Pass Constructing a CSSMathSum with no arguments throws a SyntaxError
Pass CSSMathSum can be constructed from a single number CSSUnitValue
Pass CSSMathSum can be constructed from more than one number CSSUnitValue
@ -22,4 +22,7 @@ Pass CSSMathMax.operator is readonly
Pass CSSMathNegate can be constructed from a single numberish value
Pass CSSMathNegate.operator is readonly
Pass CSSMathInvert can be constructed from a single numberish value
Pass CSSMathInvert.operator is readonly
Pass CSSMathInvert.operator is readonly
Pass Constructing a CSSMathClamp with less than 3 arguments throws a SyntaxError
Pass Constructing a CSSMathClamp with different unit arguments throws a SyntaxError
Pass CSSMathClamp.operator is readonly

View file

@ -2,8 +2,8 @@ Harness status: OK
Found 17 tests
5 Pass
12 Fail
6 Pass
11 Fail
Fail Converting a CSSUnitValue to an invalid unit throws SyntaxError
Pass Converting a CSSNumericValue with invalid sum value throws TypeError
Pass Converting a CSSNumericValue with sum value containing more than one value throws TypeError
@ -17,7 +17,7 @@ Pass Converting a CSSMathMin to a single unit with different units throws a Type
Fail Converting a CSSMathMax to a single unit finds the max value
Pass Converting a CSSMathMax to a single unit with different units throws a TypeError
Fail Converting a CSSMathClamp to a single unit returns the clamped value
Fail Converting a CSSMathClamp to a single unit with different units throws a TypeError
Pass Converting a CSSMathClamp to a single unit with different units throws a TypeError
Fail Converting a CSSMathNegate to a single unit negates its value
Fail Converting a CSSMathInvert to a single unit inverts its value and units
Fail Converting a complex expression to a single unit