LibWeb/CSS: Implement CSSMathMin

This commit is contained in:
Sam Atkins 2025-08-19 17:07:05 +01:00 committed by Andreas Kling
commit 1a35795f47
Notes: github-actions[bot] 2025-08-29 09:59:24 +00:00
9 changed files with 174 additions and 6 deletions

View file

@ -117,6 +117,7 @@ set(SOURCES
CSS/CSSLayerStatementRule.cpp
CSS/CSSMarginRule.cpp
CSS/CSSMathInvert.cpp
CSS/CSSMathMin.cpp
CSS/CSSMathNegate.cpp
CSS/CSSMathProduct.cpp
CSS/CSSMathSum.cpp

View file

@ -0,0 +1,118 @@
/*
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "CSSMathMin.h"
#include <LibWeb/Bindings/CSSMathMinPrototype.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(CSSMathMin);
GC::Ref<CSSMathMin> CSSMathMin::create(JS::Realm& realm, NumericType type, GC::Ref<CSSNumericArray> values)
{
return realm.create<CSSMathMin>(realm, move(type), move(values));
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathmin-cssmathmin
WebIDL::ExceptionOr<GC::Ref<CSSMathMin>> CSSMathMin::construct_impl(JS::Realm& realm, Vector<CSSNumberish> values)
{
// The CSSMathMin(...args) and CSSMathMax(...args) constructors are defined identically to the above, except that
// in the last step they return a new CSSMathMin or CSSMathMax object, respectively.
// NB: So, the steps below are a modification of the CSSMathSum 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 CSSMathMin"_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 CSSMathMin with values of incompatible types"sv };
}
}
// 4. Return a new CSSMathMin whose values internal slot is set to args.
auto values_array = CSSNumericArray::create(realm, { converted_values });
return CSSMathMin::create(realm, move(type), move(values_array));
}
CSSMathMin::CSSMathMin(JS::Realm& realm, NumericType type, GC::Ref<CSSNumericArray> values)
: CSSMathValue(realm, Bindings::CSSMathOperator::Min, move(type))
, m_values(move(values))
{
}
CSSMathMin::~CSSMathMin() = default;
void CSSMathMin::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSMathMin);
Base::initialize(realm);
}
void CSSMathMin::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 CSSMathMin::serialize_math_value(Nested, Parens) const
{
// NB: Only steps 1 and 2 apply here.
// 1. Let s initially be the empty string.
StringBuilder s;
// 2. If this is a CSSMathMin or CSSMathMax:
{
// 1. Append "min(" or "max(" to s, as appropriate.
s.append("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.
bool first = true;
for (auto const& arg : m_values->values()) {
if (first) {
first = false;
} else {
s.append(", "sv);
}
s.append(arg->to_string({ .nested = true, .parenless = true }));
}
// 3. Append ")" to s and return s.
s.append(")"sv);
return s.to_string_without_validation();
}
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathmin-values
GC::Ref<CSSNumericArray> CSSMathMin::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/#cssmathmin
class CSSMathMin : public CSSMathValue {
WEB_PLATFORM_OBJECT(CSSMathMin, CSSMathValue);
GC_DECLARE_ALLOCATOR(CSSMathMin);
public:
[[nodiscard]] static GC::Ref<CSSMathMin> create(JS::Realm&, NumericType, GC::Ref<CSSNumericArray>);
static WebIDL::ExceptionOr<GC::Ref<CSSMathMin>> construct_impl(JS::Realm&, Vector<CSSNumberish>);
virtual ~CSSMathMin() 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:
CSSMathMin(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/#cssmathmin
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSMathMin : CSSMathValue {
constructor(CSSNumberish... args);
readonly attribute CSSNumericArray values;
};

View file

@ -243,6 +243,7 @@ class CSSLayerBlockRule;
class CSSLayerStatementRule;
class CSSMarginRule;
class CSSMathInvert;
class CSSMathMin;
class CSSMathNegate;
class CSSMathProduct;
class CSSMathSum;

View file

@ -37,6 +37,7 @@ libweb_js_bindings(CSS/CSSLayerBlockRule)
libweb_js_bindings(CSS/CSSLayerStatementRule)
libweb_js_bindings(CSS/CSSMarginRule)
libweb_js_bindings(CSS/CSSMathInvert)
libweb_js_bindings(CSS/CSSMathMin)
libweb_js_bindings(CSS/CSSMathNegate)
libweb_js_bindings(CSS/CSSMathProduct)
libweb_js_bindings(CSS/CSSMathSum)

View file

@ -50,6 +50,7 @@ CSSLayerBlockRule
CSSLayerStatementRule
CSSMarginRule
CSSMathInvert
CSSMathMin
CSSMathNegate
CSSMathProduct
CSSMathSum

View file

@ -2,7 +2,8 @@ Harness status: OK
Found 11 tests
11 Fail
2 Pass
9 Fail
Fail Parsing an invalid string throws SyntaxError
Fail Parsing a string with a non numeric token throws SyntaxError
Fail Parsing a string with left over numeric tokens throws SyntaxError
@ -12,5 +13,5 @@ Fail Parsing ignores surrounding spaces
Fail Parsing min() is successful
Fail Parsing max() is successful
Fail Parsing clamp() is successful
Fail Parsing sum of multiple min() is successful
Fail Parsing product of multiple min() is successful
Pass Parsing sum of multiple min() is successful
Pass Parsing product of multiple min() is successful

View file

@ -2,8 +2,8 @@ Harness status: OK
Found 17 tests
2 Pass
15 Fail
3 Pass
14 Fail
Fail Converting a CSSUnitValue to an invalid unit throws SyntaxError
Fail Converting a CSSNumericValue with invalid sum value throws TypeError
Pass Converting a CSSNumericValue with sum value containing more than one value throws TypeError
@ -13,7 +13,7 @@ Fail Converting a CSSUnitValue to its canonical unit returns correct value
Fail Converting a CSSMathSum to a single unit adds the values
Fail Converting a CSSMathProduct to a single unit multiplies the values
Fail Converting a CSSMathMin to a single unit finds the min value
Fail Converting a CSSMathMin to a single unit with different units throws a TypeError
Pass Converting a CSSMathMin to a single unit with different units throws a TypeError
Fail Converting a CSSMathMax to a single unit finds the max value
Fail 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