LibWeb/CSS: Implement CSSNumericType.equals()
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run

This commit is contained in:
Sam Atkins 2025-08-22 12:53:15 +01:00 committed by Andreas Kling
commit 7be645a091
Notes: github-actions[bot] 2025-08-29 09:58:23 +00:00
22 changed files with 181 additions and 16 deletions

View file

@ -102,4 +102,18 @@ GC::Ref<CSSNumericValue> CSSMathClamp::upper() const
return m_upper; return m_upper;
} }
// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value
bool CSSMathClamp::is_equal_numeric_value(GC::Ref<CSSNumericValue> 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<CSSMathClamp>(*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);
}
} }

View file

@ -29,6 +29,7 @@ public:
GC::Ref<CSSNumericValue> upper() const; GC::Ref<CSSNumericValue> upper() const;
virtual String serialize_math_value(Nested, Parens) const override; virtual String serialize_math_value(Nested, Parens) const override;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
private: private:
CSSMathClamp(JS::Realm&, NumericType, GC::Ref<CSSNumericValue> lower, GC::Ref<CSSNumericValue> value, GC::Ref<CSSNumericValue> upper); CSSMathClamp(JS::Realm&, NumericType, GC::Ref<CSSNumericValue> lower, GC::Ref<CSSNumericValue> value, GC::Ref<CSSNumericValue> upper);

View file

@ -92,4 +92,18 @@ GC::Ref<CSSNumericValue> CSSMathInvert::value() const
return m_value; return m_value;
} }
// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value
bool CSSMathInvert::is_equal_numeric_value(GC::Ref<CSSNumericValue> 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<CSSMathInvert>(*other);
if (!other_invert)
return false;
// 4. Assert: value1 and value2 are both CSSMathNegates or CSSMathInverts.
// 5. Return whether value1s value and value2s value are equal numeric values.
return m_value->is_equal_numeric_value(other_invert->m_value);
}
} }

View file

@ -27,6 +27,7 @@ public:
GC::Ref<CSSNumericValue> value() const; GC::Ref<CSSNumericValue> value() const;
virtual String serialize_math_value(Nested, Parens) const override; virtual String serialize_math_value(Nested, Parens) const override;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
private: private:
CSSMathInvert(JS::Realm&, NumericType, GC::Ref<CSSNumericValue>); CSSMathInvert(JS::Realm&, NumericType, GC::Ref<CSSNumericValue>);

View file

@ -115,4 +115,18 @@ GC::Ref<CSSNumericArray> CSSMathMax::values() const
return m_values; return m_values;
} }
// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value
bool CSSMathMax::is_equal_numeric_value(GC::Ref<CSSNumericValue> 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<CSSMathMax>(*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);
}
} }

View file

@ -27,6 +27,7 @@ public:
GC::Ref<CSSNumericArray> values() const; GC::Ref<CSSNumericArray> values() const;
virtual String serialize_math_value(Nested, Parens) const override; virtual String serialize_math_value(Nested, Parens) const override;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
private: private:
CSSMathMax(JS::Realm&, NumericType, GC::Ref<CSSNumericArray>); CSSMathMax(JS::Realm&, NumericType, GC::Ref<CSSNumericArray>);

View file

@ -116,4 +116,18 @@ GC::Ref<CSSNumericArray> CSSMathMin::values() const
return m_values; return m_values;
} }
// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value
bool CSSMathMin::is_equal_numeric_value(GC::Ref<CSSNumericValue> 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<CSSMathMin>(*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);
}
} }

View file

@ -27,6 +27,7 @@ public:
GC::Ref<CSSNumericArray> values() const; GC::Ref<CSSNumericArray> values() const;
virtual String serialize_math_value(Nested, Parens) const override; virtual String serialize_math_value(Nested, Parens) const override;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
private: private:
CSSMathMin(JS::Realm&, NumericType, GC::Ref<CSSNumericArray>); CSSMathMin(JS::Realm&, NumericType, GC::Ref<CSSNumericArray>);

View file

@ -89,4 +89,18 @@ GC::Ref<CSSNumericValue> CSSMathNegate::value() const
return m_value; return m_value;
} }
// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value
bool CSSMathNegate::is_equal_numeric_value(GC::Ref<CSSNumericValue> 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<CSSMathNegate>(*other);
if (!other_negate)
return false;
// 4. Assert: value1 and value2 are both CSSMathNegates or CSSMathInverts.
// 5. Return whether value1s value and value2s value are equal numeric values.
return m_value->is_equal_numeric_value(other_negate->m_value);
}
} }

View file

@ -27,6 +27,7 @@ public:
GC::Ref<CSSNumericValue> value() const; GC::Ref<CSSNumericValue> value() const;
virtual String serialize_math_value(Nested, Parens) const override; virtual String serialize_math_value(Nested, Parens) const override;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
private: private:
CSSMathNegate(JS::Realm&, NumericType, GC::Ref<CSSNumericValue>); CSSMathNegate(JS::Realm&, NumericType, GC::Ref<CSSNumericValue>);

View file

@ -138,4 +138,18 @@ GC::Ref<CSSNumericArray> CSSMathProduct::values() const
return m_values; return m_values;
} }
// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value
bool CSSMathProduct::is_equal_numeric_value(GC::Ref<CSSNumericValue> 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<CSSMathProduct>(*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);
}
} }

View file

@ -27,6 +27,7 @@ public:
GC::Ref<CSSNumericArray> values() const; GC::Ref<CSSNumericArray> values() const;
virtual String serialize_math_value(Nested, Parens) const override; virtual String serialize_math_value(Nested, Parens) const override;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
private: private:
CSSMathProduct(JS::Realm&, NumericType, GC::Ref<CSSNumericArray>); CSSMathProduct(JS::Realm&, NumericType, GC::Ref<CSSNumericArray>);

View file

@ -137,4 +137,18 @@ GC::Ref<CSSNumericArray> CSSMathSum::values() const
return m_values; return m_values;
} }
// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value
bool CSSMathSum::is_equal_numeric_value(GC::Ref<CSSNumericValue> 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<CSSMathSum>(*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);
}
} }

View file

@ -27,6 +27,7 @@ public:
GC::Ref<CSSNumericArray> values() const; GC::Ref<CSSNumericArray> values() const;
virtual String serialize_math_value(Nested, Parens) const override; virtual String serialize_math_value(Nested, Parens) const override;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
private: private:
CSSMathSum(JS::Realm&, NumericType, GC::Ref<CSSNumericArray>); CSSMathSum(JS::Realm&, NumericType, GC::Ref<CSSNumericArray>);

View file

@ -57,4 +57,26 @@ Optional<JS::Value> CSSNumericArray::item_value(size_t index) const
return {}; return {};
} }
// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value
bool CSSNumericArray::is_equal_numeric_values(GC::Ref<CSSNumericArray> 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 value1s 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 value1s values internal slot is not an equal numeric value to the item in value2s 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;
}
}
} }

View file

@ -27,6 +27,7 @@ public:
virtual void initialize(JS::Realm&) override; virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Visitor&) override; virtual void visit_edges(Visitor&) override;
bool is_equal_numeric_values(GC::Ref<CSSNumericArray> other) const;
private: private:
CSSNumericArray(JS::Realm&, Vector<GC::Ref<CSSNumericValue>>); CSSNumericArray(JS::Realm&, Vector<GC::Ref<CSSNumericValue>>);

View file

@ -54,6 +54,23 @@ void CSSNumericValue::initialize(JS::Realm& realm)
Base::initialize(realm); Base::initialize(realm);
} }
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-equals
bool CSSNumericValue::equals_for_bindings(Vector<CSSNumberish> 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 // https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-type
CSSNumericType CSSNumericValue::type_for_bindings() const CSSNumericType CSSNumericValue::type_for_bindings() const
{ {

View file

@ -25,6 +25,9 @@ struct CSSNumericType {
Optional<Bindings::CSSNumericBaseType> percent_hint; Optional<Bindings::CSSNumericBaseType> percent_hint;
}; };
// https://drafts.css-houdini.org/css-typed-om-1/#typedefdef-cssnumberish
using CSSNumberish = Variant<double, GC::Root<CSSNumericValue>>;
// https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue // https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue
class CSSNumericValue : public CSSStyleValue { class CSSNumericValue : public CSSStyleValue {
WEB_PLATFORM_OBJECT(CSSNumericValue, CSSStyleValue); WEB_PLATFORM_OBJECT(CSSNumericValue, CSSStyleValue);
@ -39,6 +42,9 @@ public:
}; };
virtual ~CSSNumericValue() override = default; virtual ~CSSNumericValue() override = default;
bool equals_for_bindings(Vector<CSSNumberish>) const;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const = 0;
CSSNumericType type_for_bindings() const; CSSNumericType type_for_bindings() const;
NumericType const& type() const { return m_type; } NumericType const& type() const { return m_type; }
@ -55,9 +61,6 @@ protected:
NumericType m_type; NumericType m_type;
}; };
// https://drafts.css-houdini.org/css-typed-om-1/#typedefdef-cssnumberish
using CSSNumberish = Variant<double, GC::Root<CSSNumericValue>>;
GC::Ref<CSSNumericValue> rectify_a_numberish_value(JS::Realm&, CSSNumberish const&, Optional<FlyString> unit = {}); GC::Ref<CSSNumericValue> rectify_a_numberish_value(JS::Realm&, CSSNumberish const&, Optional<FlyString> unit = {});
} }

View file

@ -33,7 +33,7 @@ interface CSSNumericValue : CSSStyleValue {
[FIXME] CSSNumericValue min(CSSNumberish... values); [FIXME] CSSNumericValue min(CSSNumberish... values);
[FIXME] CSSNumericValue max(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: CSSUnitValue to(USVString unit);
// FIXME: CSSMathSum toSum(USVString... units); // FIXME: CSSMathSum toSum(USVString... units);

View file

@ -95,4 +95,19 @@ String CSSUnitValue::serialize_unit_value(Optional<double> minimum, Optional<dou
return s.to_string_without_validation(); return s.to_string_without_validation();
} }
// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value
bool CSSUnitValue::is_equal_numeric_value(GC::Ref<CSSNumericValue> 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<CSSUnitValue>(*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;
}
} }

View file

@ -29,6 +29,8 @@ public:
String serialize_unit_value(Optional<double> minimum, Optional<double> maximum) const; String serialize_unit_value(Optional<double> minimum, Optional<double> maximum) const;
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
private: private:
explicit CSSUnitValue(JS::Realm&, double value, FlyString unit, NumericType type); explicit CSSUnitValue(JS::Realm&, double value, FlyString unit, NumericType type);

View file

@ -2,15 +2,15 @@ Harness status: OK
Found 11 tests Found 11 tests
11 Fail 11 Pass
Fail Two CSSUnitValues with same value and unit are equal Pass Two CSSUnitValues with same value and unit are equal
Fail Two CSSUnitValues with different values are not equal Pass Two CSSUnitValues with different values are not equal
Fail Two CSSUnitValues with different units are not equal Pass Two CSSUnitValues with different units are not equal
Fail Two CSSMathValues with different types are not equal Pass Two CSSMathValues with different types are not equal
Fail Two CSSMathValues with different number of values are not equal Pass Two CSSMathValues with different number of values are not equal
Fail Two CSSMathValues with different values are not equal Pass Two CSSMathValues with different values are not equal
Fail Two CSSMathValues with same structure are equal Pass Two CSSMathValues with same structure are equal
Fail Multiple CSSMathValues with same structure are equal Pass Multiple CSSMathValues with same structure are equal
Fail Multiple CSSMathValues with one different are not equal Pass Multiple CSSMathValues with one different are not equal
Fail Two CSSMathClamp with same value and unit are equal Pass Two CSSMathClamp with same value and unit are equal
Fail Two CSSMathClamp with different units are not equal Pass Two CSSMathClamp with different units are not equal