diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 5628ae9a08e..e7d287d6f42 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -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 diff --git a/Libraries/LibWeb/CSS/CSSMathMin.cpp b/Libraries/LibWeb/CSS/CSSMathMin.cpp new file mode 100644 index 00000000000..f918a8951ad --- /dev/null +++ b/Libraries/LibWeb/CSS/CSSMathMin.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2025, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "CSSMathMin.h" +#include +#include +#include +#include +#include +#include + +namespace Web::CSS { + +GC_DEFINE_ALLOCATOR(CSSMathMin); + +GC::Ref CSSMathMin::create(JS::Realm& realm, NumericType type, GC::Ref values) +{ + return realm.create(realm, move(type), move(values)); +} + +// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathmin-cssmathmin +WebIDL::ExceptionOr> CSSMathMin::construct_impl(JS::Realm& realm, Vector 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> 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 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 this’s 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 CSSMathMin::values() const +{ + return m_values; +} + +} diff --git a/Libraries/LibWeb/CSS/CSSMathMin.h b/Libraries/LibWeb/CSS/CSSMathMin.h new file mode 100644 index 00000000000..611b2f0f59c --- /dev/null +++ b/Libraries/LibWeb/CSS/CSSMathMin.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +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 create(JS::Realm&, NumericType, GC::Ref); + static WebIDL::ExceptionOr> construct_impl(JS::Realm&, Vector); + + virtual ~CSSMathMin() override; + + virtual void initialize(JS::Realm&) override; + virtual void visit_edges(Visitor&) override; + + GC::Ref values() const; + + virtual String serialize_math_value(Nested, Parens) const override; + +private: + CSSMathMin(JS::Realm&, NumericType, GC::Ref); + GC::Ref m_values; +}; + +} diff --git a/Libraries/LibWeb/CSS/CSSMathMin.idl b/Libraries/LibWeb/CSS/CSSMathMin.idl new file mode 100644 index 00000000000..24282b6c812 --- /dev/null +++ b/Libraries/LibWeb/CSS/CSSMathMin.idl @@ -0,0 +1,9 @@ +#import +#import + +// 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; +}; diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 1f90a407d04..741580d4ad8 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -243,6 +243,7 @@ class CSSLayerBlockRule; class CSSLayerStatementRule; class CSSMarginRule; class CSSMathInvert; +class CSSMathMin; class CSSMathNegate; class CSSMathProduct; class CSSMathSum; diff --git a/Libraries/LibWeb/idl_files.cmake b/Libraries/LibWeb/idl_files.cmake index 65bcfeaed12..563169728bf 100644 --- a/Libraries/LibWeb/idl_files.cmake +++ b/Libraries/LibWeb/idl_files.cmake @@ -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) diff --git a/Tests/LibWeb/Text/expected/all-window-properties.txt b/Tests/LibWeb/Text/expected/all-window-properties.txt index 7c8aaae834a..6b291c16212 100644 --- a/Tests/LibWeb/Text/expected/all-window-properties.txt +++ b/Tests/LibWeb/Text/expected/all-window-properties.txt @@ -50,6 +50,7 @@ CSSLayerBlockRule CSSLayerStatementRule CSSMarginRule CSSMathInvert +CSSMathMin CSSMathNegate CSSMathProduct CSSMathSum diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/parse.tentative.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/parse.tentative.txt index fc16f83771d..734e1ec2fcd 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/parse.tentative.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/parse.tentative.txt @@ -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 \ No newline at end of file +Pass Parsing sum of multiple min() is successful +Pass Parsing product of multiple min() is successful \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.txt index e050accc9b2..1492f8e5d31 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.txt @@ -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