From 7be645a091304a273ca2abbc84f0a531e2785c07 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Fri, 22 Aug 2025 12:53:15 +0100 Subject: [PATCH] LibWeb/CSS: Implement CSSNumericType.equals() --- Libraries/LibWeb/CSS/CSSMathClamp.cpp | 14 +++++++++++ Libraries/LibWeb/CSS/CSSMathClamp.h | 1 + Libraries/LibWeb/CSS/CSSMathInvert.cpp | 14 +++++++++++ Libraries/LibWeb/CSS/CSSMathInvert.h | 1 + Libraries/LibWeb/CSS/CSSMathMax.cpp | 14 +++++++++++ Libraries/LibWeb/CSS/CSSMathMax.h | 1 + Libraries/LibWeb/CSS/CSSMathMin.cpp | 14 +++++++++++ Libraries/LibWeb/CSS/CSSMathMin.h | 1 + Libraries/LibWeb/CSS/CSSMathNegate.cpp | 14 +++++++++++ Libraries/LibWeb/CSS/CSSMathNegate.h | 1 + Libraries/LibWeb/CSS/CSSMathProduct.cpp | 14 +++++++++++ Libraries/LibWeb/CSS/CSSMathProduct.h | 1 + Libraries/LibWeb/CSS/CSSMathSum.cpp | 14 +++++++++++ Libraries/LibWeb/CSS/CSSMathSum.h | 1 + Libraries/LibWeb/CSS/CSSNumericArray.cpp | 22 +++++++++++++++++ Libraries/LibWeb/CSS/CSSNumericArray.h | 1 + Libraries/LibWeb/CSS/CSSNumericValue.cpp | 17 +++++++++++++ Libraries/LibWeb/CSS/CSSNumericValue.h | 9 ++++--- Libraries/LibWeb/CSS/CSSNumericValue.idl | 2 +- Libraries/LibWeb/CSS/CSSUnitValue.cpp | 15 ++++++++++++ Libraries/LibWeb/CSS/CSSUnitValue.h | 2 ++ .../numeric-objects/equals.tentative.txt | 24 +++++++++---------- 22 files changed, 181 insertions(+), 16 deletions(-) diff --git a/Libraries/LibWeb/CSS/CSSMathClamp.cpp b/Libraries/LibWeb/CSS/CSSMathClamp.cpp index 58532a0771b..ea6b11a0c3e 100644 --- a/Libraries/LibWeb/CSS/CSSMathClamp.cpp +++ b/Libraries/LibWeb/CSS/CSSMathClamp.cpp @@ -102,4 +102,18 @@ GC::Ref CSSMathClamp::upper() const return m_upper; } +// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value +bool CSSMathClamp::is_equal_numeric_value(GC::Ref other) const +{ + // AD-HOC: Spec doesn't handle clamp() + // 1. If value1 and value2 are not members of the same interface, return false. + auto* other_clamp = as_if(*other); + if (!other_clamp) + return false; + + return m_lower->is_equal_numeric_value(other_clamp->m_lower) + && m_value->is_equal_numeric_value(other_clamp->m_value) + && m_upper->is_equal_numeric_value(other_clamp->m_upper); +} + } diff --git a/Libraries/LibWeb/CSS/CSSMathClamp.h b/Libraries/LibWeb/CSS/CSSMathClamp.h index 6826a85a96d..17237cecb21 100644 --- a/Libraries/LibWeb/CSS/CSSMathClamp.h +++ b/Libraries/LibWeb/CSS/CSSMathClamp.h @@ -29,6 +29,7 @@ public: GC::Ref upper() const; virtual String serialize_math_value(Nested, Parens) const override; + virtual bool is_equal_numeric_value(GC::Ref other) const override; private: CSSMathClamp(JS::Realm&, NumericType, GC::Ref lower, GC::Ref value, GC::Ref upper); diff --git a/Libraries/LibWeb/CSS/CSSMathInvert.cpp b/Libraries/LibWeb/CSS/CSSMathInvert.cpp index 62493796ed0..af31199665d 100644 --- a/Libraries/LibWeb/CSS/CSSMathInvert.cpp +++ b/Libraries/LibWeb/CSS/CSSMathInvert.cpp @@ -92,4 +92,18 @@ GC::Ref CSSMathInvert::value() const return m_value; } +// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value +bool CSSMathInvert::is_equal_numeric_value(GC::Ref other) const +{ + // NB: Only steps 1, 4 and 5 are relevant. + // 1. If value1 and value2 are not members of the same interface, return false. + auto* other_invert = as_if(*other); + if (!other_invert) + return false; + + // 4. Assert: value1 and value2 are both CSSMathNegates or CSSMathInverts. + // 5. Return whether value1’s value and value2’s value are equal numeric values. + return m_value->is_equal_numeric_value(other_invert->m_value); +} + } diff --git a/Libraries/LibWeb/CSS/CSSMathInvert.h b/Libraries/LibWeb/CSS/CSSMathInvert.h index 231bd14af19..f34ea7efa75 100644 --- a/Libraries/LibWeb/CSS/CSSMathInvert.h +++ b/Libraries/LibWeb/CSS/CSSMathInvert.h @@ -27,6 +27,7 @@ public: GC::Ref value() const; virtual String serialize_math_value(Nested, Parens) const override; + virtual bool is_equal_numeric_value(GC::Ref other) const override; private: CSSMathInvert(JS::Realm&, NumericType, GC::Ref); diff --git a/Libraries/LibWeb/CSS/CSSMathMax.cpp b/Libraries/LibWeb/CSS/CSSMathMax.cpp index fff7319d60e..0e01ee03055 100644 --- a/Libraries/LibWeb/CSS/CSSMathMax.cpp +++ b/Libraries/LibWeb/CSS/CSSMathMax.cpp @@ -115,4 +115,18 @@ GC::Ref CSSMathMax::values() const return m_values; } +// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value +bool CSSMathMax::is_equal_numeric_value(GC::Ref other) const +{ + // NB: Only steps 1 and 3 are relevant. + // 1. If value1 and value2 are not members of the same interface, return false. + auto* other_max = as_if(*other); + if (!other_max) + return false; + + // 3. If value1 and value2 are both CSSMathSums, CSSMathProducts, CSSMathMins, or CSSMathMaxs: + // NB: Substeps are implemented in CSSNumericArray. + return m_values->is_equal_numeric_values(other_max->m_values); +} + } diff --git a/Libraries/LibWeb/CSS/CSSMathMax.h b/Libraries/LibWeb/CSS/CSSMathMax.h index 387c5d9b6ee..52748956002 100644 --- a/Libraries/LibWeb/CSS/CSSMathMax.h +++ b/Libraries/LibWeb/CSS/CSSMathMax.h @@ -27,6 +27,7 @@ public: GC::Ref values() const; virtual String serialize_math_value(Nested, Parens) const override; + virtual bool is_equal_numeric_value(GC::Ref other) const override; private: CSSMathMax(JS::Realm&, NumericType, GC::Ref); diff --git a/Libraries/LibWeb/CSS/CSSMathMin.cpp b/Libraries/LibWeb/CSS/CSSMathMin.cpp index ad9e0792b5d..87aa0bbf10a 100644 --- a/Libraries/LibWeb/CSS/CSSMathMin.cpp +++ b/Libraries/LibWeb/CSS/CSSMathMin.cpp @@ -116,4 +116,18 @@ GC::Ref CSSMathMin::values() const return m_values; } +// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value +bool CSSMathMin::is_equal_numeric_value(GC::Ref other) const +{ + // NB: Only steps 1 and 3 are relevant. + // 1. If value1 and value2 are not members of the same interface, return false. + auto* other_min = as_if(*other); + if (!other_min) + return false; + + // 3. If value1 and value2 are both CSSMathSums, CSSMathProducts, CSSMathMins, or CSSMathMaxs: + // NB: Substeps are implemented in CSSNumericArray. + return m_values->is_equal_numeric_values(other_min->m_values); +} + } diff --git a/Libraries/LibWeb/CSS/CSSMathMin.h b/Libraries/LibWeb/CSS/CSSMathMin.h index 611b2f0f59c..e163215db23 100644 --- a/Libraries/LibWeb/CSS/CSSMathMin.h +++ b/Libraries/LibWeb/CSS/CSSMathMin.h @@ -27,6 +27,7 @@ public: GC::Ref values() const; virtual String serialize_math_value(Nested, Parens) const override; + virtual bool is_equal_numeric_value(GC::Ref other) const override; private: CSSMathMin(JS::Realm&, NumericType, GC::Ref); diff --git a/Libraries/LibWeb/CSS/CSSMathNegate.cpp b/Libraries/LibWeb/CSS/CSSMathNegate.cpp index fd214b510b6..dce50176542 100644 --- a/Libraries/LibWeb/CSS/CSSMathNegate.cpp +++ b/Libraries/LibWeb/CSS/CSSMathNegate.cpp @@ -89,4 +89,18 @@ GC::Ref CSSMathNegate::value() const return m_value; } +// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value +bool CSSMathNegate::is_equal_numeric_value(GC::Ref other) const +{ + // NB: Only steps 1, 4 and 5 are relevant. + // 1. If value1 and value2 are not members of the same interface, return false. + auto* other_negate = as_if(*other); + if (!other_negate) + return false; + + // 4. Assert: value1 and value2 are both CSSMathNegates or CSSMathInverts. + // 5. Return whether value1’s value and value2’s value are equal numeric values. + return m_value->is_equal_numeric_value(other_negate->m_value); +} + } diff --git a/Libraries/LibWeb/CSS/CSSMathNegate.h b/Libraries/LibWeb/CSS/CSSMathNegate.h index 4470f74a5df..4cbcbf611c2 100644 --- a/Libraries/LibWeb/CSS/CSSMathNegate.h +++ b/Libraries/LibWeb/CSS/CSSMathNegate.h @@ -27,6 +27,7 @@ public: GC::Ref value() const; virtual String serialize_math_value(Nested, Parens) const override; + virtual bool is_equal_numeric_value(GC::Ref other) const override; private: CSSMathNegate(JS::Realm&, NumericType, GC::Ref); diff --git a/Libraries/LibWeb/CSS/CSSMathProduct.cpp b/Libraries/LibWeb/CSS/CSSMathProduct.cpp index 120db0ed6bd..0e4d1848893 100644 --- a/Libraries/LibWeb/CSS/CSSMathProduct.cpp +++ b/Libraries/LibWeb/CSS/CSSMathProduct.cpp @@ -138,4 +138,18 @@ GC::Ref CSSMathProduct::values() const return m_values; } +// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value +bool CSSMathProduct::is_equal_numeric_value(GC::Ref other) const +{ + // NB: Only steps 1 and 3 are relevant. + // 1. If value1 and value2 are not members of the same interface, return false. + auto* other_product = as_if(*other); + if (!other_product) + return false; + + // 3. If value1 and value2 are both CSSMathSums, CSSMathProducts, CSSMathMins, or CSSMathMaxs: + // NB: Substeps are implemented in CSSNumericArray. + return m_values->is_equal_numeric_values(other_product->m_values); +} + } diff --git a/Libraries/LibWeb/CSS/CSSMathProduct.h b/Libraries/LibWeb/CSS/CSSMathProduct.h index 4b057e8a772..8dc47079c2c 100644 --- a/Libraries/LibWeb/CSS/CSSMathProduct.h +++ b/Libraries/LibWeb/CSS/CSSMathProduct.h @@ -27,6 +27,7 @@ public: GC::Ref values() const; virtual String serialize_math_value(Nested, Parens) const override; + virtual bool is_equal_numeric_value(GC::Ref other) const override; private: CSSMathProduct(JS::Realm&, NumericType, GC::Ref); diff --git a/Libraries/LibWeb/CSS/CSSMathSum.cpp b/Libraries/LibWeb/CSS/CSSMathSum.cpp index bd60e175ade..626972644b9 100644 --- a/Libraries/LibWeb/CSS/CSSMathSum.cpp +++ b/Libraries/LibWeb/CSS/CSSMathSum.cpp @@ -137,4 +137,18 @@ GC::Ref CSSMathSum::values() const return m_values; } +// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value +bool CSSMathSum::is_equal_numeric_value(GC::Ref other) const +{ + // NB: Only steps 1 and 3 are relevant. + // 1. If value1 and value2 are not members of the same interface, return false. + auto* other_sum = as_if(*other); + if (!other_sum) + return false; + + // 3. If value1 and value2 are both CSSMathSums, CSSMathProducts, CSSMathMins, or CSSMathMaxs: + // NB: Substeps are implemented in CSSNumericArray. + return m_values->is_equal_numeric_values(other_sum->m_values); +} + } diff --git a/Libraries/LibWeb/CSS/CSSMathSum.h b/Libraries/LibWeb/CSS/CSSMathSum.h index c28ebee9b6b..190872b0081 100644 --- a/Libraries/LibWeb/CSS/CSSMathSum.h +++ b/Libraries/LibWeb/CSS/CSSMathSum.h @@ -27,6 +27,7 @@ public: GC::Ref values() const; virtual String serialize_math_value(Nested, Parens) const override; + virtual bool is_equal_numeric_value(GC::Ref other) const override; private: CSSMathSum(JS::Realm&, NumericType, GC::Ref); diff --git a/Libraries/LibWeb/CSS/CSSNumericArray.cpp b/Libraries/LibWeb/CSS/CSSNumericArray.cpp index 7dd5043e89e..9dcbfd55aae 100644 --- a/Libraries/LibWeb/CSS/CSSNumericArray.cpp +++ b/Libraries/LibWeb/CSS/CSSNumericArray.cpp @@ -57,4 +57,26 @@ Optional CSSNumericArray::item_value(size_t index) const return {}; } +// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value +bool CSSNumericArray::is_equal_numeric_values(GC::Ref other) const +{ + // NB: This is just step 3, moved here to reduce repetition. + // 3. If value1 and value2 are both CSSMathSums, CSSMathProducts, CSSMathMins, or CSSMathMaxs: + { + // 1. If value1’s values and value2s values internal slots have different sizes, return false. + if (m_values.size() != other->m_values.size()) + return false; + + // 2. If any item in value1’s values internal slot is not an equal numeric value to the item in value2’s values + // internal slot at the same index, return false. + for (auto index = 0u; index < m_values.size(); ++index) { + if (!m_values[index]->is_equal_numeric_value(other->m_values[index])) + return false; + } + + // 3. Return true. + return true; + } +} + } diff --git a/Libraries/LibWeb/CSS/CSSNumericArray.h b/Libraries/LibWeb/CSS/CSSNumericArray.h index 4d3396bd09b..3ae71ff0e01 100644 --- a/Libraries/LibWeb/CSS/CSSNumericArray.h +++ b/Libraries/LibWeb/CSS/CSSNumericArray.h @@ -27,6 +27,7 @@ public: virtual void initialize(JS::Realm&) override; virtual void visit_edges(Visitor&) override; + bool is_equal_numeric_values(GC::Ref other) const; private: CSSNumericArray(JS::Realm&, Vector>); diff --git a/Libraries/LibWeb/CSS/CSSNumericValue.cpp b/Libraries/LibWeb/CSS/CSSNumericValue.cpp index d839aea47f1..4829579d767 100644 --- a/Libraries/LibWeb/CSS/CSSNumericValue.cpp +++ b/Libraries/LibWeb/CSS/CSSNumericValue.cpp @@ -54,6 +54,23 @@ void CSSNumericValue::initialize(JS::Realm& realm) Base::initialize(realm); } +// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-equals +bool CSSNumericValue::equals_for_bindings(Vector values) const +{ + // The equals(...values) method, when called on a CSSNumericValue this, must perform the following steps: + + // 1. Replace each item of values with the result of rectifying a numberish value for the item. + // 2. For each item in values, if the item is not an equal numeric value to this, return false. + for (auto const& value : values) { + auto rectified_value = rectify_a_numberish_value(realm(), value); + if (!is_equal_numeric_value(rectified_value)) + return false; + } + + // 3. Return true. + return true; +} + // https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-type CSSNumericType CSSNumericValue::type_for_bindings() const { diff --git a/Libraries/LibWeb/CSS/CSSNumericValue.h b/Libraries/LibWeb/CSS/CSSNumericValue.h index f23c54d028b..be3b455367a 100644 --- a/Libraries/LibWeb/CSS/CSSNumericValue.h +++ b/Libraries/LibWeb/CSS/CSSNumericValue.h @@ -25,6 +25,9 @@ struct CSSNumericType { Optional percent_hint; }; +// https://drafts.css-houdini.org/css-typed-om-1/#typedefdef-cssnumberish +using CSSNumberish = Variant>; + // https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue class CSSNumericValue : public CSSStyleValue { WEB_PLATFORM_OBJECT(CSSNumericValue, CSSStyleValue); @@ -39,6 +42,9 @@ public: }; virtual ~CSSNumericValue() override = default; + bool equals_for_bindings(Vector) const; + virtual bool is_equal_numeric_value(GC::Ref other) const = 0; + CSSNumericType type_for_bindings() const; NumericType const& type() const { return m_type; } @@ -55,9 +61,6 @@ protected: NumericType m_type; }; -// https://drafts.css-houdini.org/css-typed-om-1/#typedefdef-cssnumberish -using CSSNumberish = Variant>; - GC::Ref rectify_a_numberish_value(JS::Realm&, CSSNumberish const&, Optional unit = {}); } diff --git a/Libraries/LibWeb/CSS/CSSNumericValue.idl b/Libraries/LibWeb/CSS/CSSNumericValue.idl index b8b1742d7af..a955ae35ad9 100644 --- a/Libraries/LibWeb/CSS/CSSNumericValue.idl +++ b/Libraries/LibWeb/CSS/CSSNumericValue.idl @@ -33,7 +33,7 @@ interface CSSNumericValue : CSSStyleValue { [FIXME] CSSNumericValue min(CSSNumberish... values); [FIXME] CSSNumericValue max(CSSNumberish... values); - [FIXME] boolean equals(CSSNumberish... value); + [ImplementedAs=equals_for_bindings] boolean equals(CSSNumberish... value); // FIXME: CSSUnitValue to(USVString unit); // FIXME: CSSMathSum toSum(USVString... units); diff --git a/Libraries/LibWeb/CSS/CSSUnitValue.cpp b/Libraries/LibWeb/CSS/CSSUnitValue.cpp index 92ded0998a9..709a925f03d 100644 --- a/Libraries/LibWeb/CSS/CSSUnitValue.cpp +++ b/Libraries/LibWeb/CSS/CSSUnitValue.cpp @@ -95,4 +95,19 @@ String CSSUnitValue::serialize_unit_value(Optional minimum, Optional other) const +{ + // NB: Only steps 1 and 2 are relevant. + // 1. If value1 and value2 are not members of the same interface, return false. + auto* other_unit_value = as_if(*other); + if (!other_unit_value) + return false; + + // 2. If value1 and value2 are both CSSUnitValues, return true if they have equal unit and value internal slots, + // or false otherwise. + return m_unit == other_unit_value->m_unit + && m_value == other_unit_value->m_value; +} + } diff --git a/Libraries/LibWeb/CSS/CSSUnitValue.h b/Libraries/LibWeb/CSS/CSSUnitValue.h index 557af0dffe6..5be2b53c782 100644 --- a/Libraries/LibWeb/CSS/CSSUnitValue.h +++ b/Libraries/LibWeb/CSS/CSSUnitValue.h @@ -29,6 +29,8 @@ public: String serialize_unit_value(Optional minimum, Optional maximum) const; + virtual bool is_equal_numeric_value(GC::Ref other) const override; + private: explicit CSSUnitValue(JS::Realm&, double value, FlyString unit, NumericType type); diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/equals.tentative.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/equals.tentative.txt index 422f5edc6cb..2202cfa5228 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/equals.tentative.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/equals.tentative.txt @@ -2,15 +2,15 @@ Harness status: OK Found 11 tests -11 Fail -Fail Two CSSUnitValues with same value and unit are equal -Fail Two CSSUnitValues with different values are not equal -Fail Two CSSUnitValues with different units are not equal -Fail Two CSSMathValues with different types are not equal -Fail Two CSSMathValues with different number of values are not equal -Fail Two CSSMathValues with different values are not equal -Fail Two CSSMathValues with same structure are equal -Fail Multiple CSSMathValues with same structure are equal -Fail Multiple CSSMathValues with one different are not equal -Fail Two CSSMathClamp with same value and unit are equal -Fail Two CSSMathClamp with different units are not equal \ No newline at end of file +11 Pass +Pass Two CSSUnitValues with same value and unit are equal +Pass Two CSSUnitValues with different values are not equal +Pass Two CSSUnitValues with different units are not equal +Pass Two CSSMathValues with different types are not equal +Pass Two CSSMathValues with different number of values are not equal +Pass Two CSSMathValues with different values are not equal +Pass Two CSSMathValues with same structure are equal +Pass Multiple CSSMathValues with same structure are equal +Pass Multiple CSSMathValues with one different are not equal +Pass Two CSSMathClamp with same value and unit are equal +Pass Two CSSMathClamp with different units are not equal \ No newline at end of file