diff --git a/Libraries/LibWeb/CSS/CSSMathClamp.cpp b/Libraries/LibWeb/CSS/CSSMathClamp.cpp index ea6b11a0c3e..e06cc6ff070 100644 --- a/Libraries/LibWeb/CSS/CSSMathClamp.cpp +++ b/Libraries/LibWeb/CSS/CSSMathClamp.cpp @@ -105,7 +105,7 @@ GC::Ref CSSMathClamp::upper() const // 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() + // AD-HOC: Spec doesn't handle clamp(). https://github.com/w3c/css-houdini-drafts/issues/1152 // 1. If value1 and value2 are not members of the same interface, return false. auto* other_clamp = as_if(*other); if (!other_clamp) @@ -116,4 +116,34 @@ bool CSSMathClamp::is_equal_numeric_value(GC::Ref other) const && m_upper->is_equal_numeric_value(other_clamp->m_upper); } +// https://drafts.css-houdini.org/css-typed-om-1/#create-a-sum-value +Optional CSSMathClamp::create_a_sum_value() const +{ + // AD-HOC: There is no spec for this. https://github.com/w3c/css-houdini-drafts/issues/1152 + // So, basing it on the spec for CSSMathMin. + + // Create sum values from lower, value, and upper. + auto lower = m_lower->create_a_sum_value(); + auto value = m_value->create_a_sum_value(); + auto upper = m_upper->create_a_sum_value(); + + // If any of those are failure, or has a length greater than one, return failure. + if (!lower.has_value() || lower->size() > 1 + || !value.has_value() || value->size() > 1 + || !upper.has_value() || upper->size() > 1) + return {}; + + // If not all their unit maps are identical, return failure. + if (lower->first().unit_map != value->first().unit_map || value->first().unit_map != upper->first().unit_map) + return {}; + + // Return value clamped between lower and upper. + return SumValue { + SumValueItem { + clamp(value->first().value, lower->first().value, upper->first().value), + value->first().unit_map, + } + }; +} + } diff --git a/Libraries/LibWeb/CSS/CSSMathClamp.h b/Libraries/LibWeb/CSS/CSSMathClamp.h index 562bc65d24f..26ab60274fc 100644 --- a/Libraries/LibWeb/CSS/CSSMathClamp.h +++ b/Libraries/LibWeb/CSS/CSSMathClamp.h @@ -30,6 +30,7 @@ public: virtual String serialize_math_value(Nested, Parens) const override; virtual bool is_equal_numeric_value(GC::Ref other) const override; + virtual Optional create_a_sum_value() 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 af31199665d..6f8a2887dcc 100644 --- a/Libraries/LibWeb/CSS/CSSMathInvert.cpp +++ b/Libraries/LibWeb/CSS/CSSMathInvert.cpp @@ -106,4 +106,29 @@ bool CSSMathInvert::is_equal_numeric_value(GC::Ref other) const return m_value->is_equal_numeric_value(other_invert->m_value); } +// https://drafts.css-houdini.org/css-typed-om-1/#create-a-sum-value +Optional CSSMathInvert::create_a_sum_value() const +{ + // 1. Let values be the result of creating a sum value from this’s value internal slot. + auto values = m_value->create_a_sum_value(); + + // 2. If values is failure, return failure. + if (!values.has_value()) + return {}; + + // 3. If the length of values is more than one, return failure. + if (values->size() > 1) + return {}; + + // 4. Invert (find the reciprocal of) the value of the item in values, and negate the value of each entry in its unit map. + for (auto& [value, unit_map] : *values) { + value = 1.0 / value; + for (auto& [_, power] : unit_map) + power = -power; + } + + // 5. Return values. + return values; +} + } diff --git a/Libraries/LibWeb/CSS/CSSMathInvert.h b/Libraries/LibWeb/CSS/CSSMathInvert.h index 9798b29eb3d..996a1738b13 100644 --- a/Libraries/LibWeb/CSS/CSSMathInvert.h +++ b/Libraries/LibWeb/CSS/CSSMathInvert.h @@ -28,6 +28,7 @@ public: virtual String serialize_math_value(Nested, Parens) const override; virtual bool is_equal_numeric_value(GC::Ref other) const override; + virtual Optional create_a_sum_value() const override; private: CSSMathInvert(JS::Realm&, NumericType, GC::Ref); diff --git a/Libraries/LibWeb/CSS/CSSMathMax.cpp b/Libraries/LibWeb/CSS/CSSMathMax.cpp index 0e01ee03055..7db0edd7d77 100644 --- a/Libraries/LibWeb/CSS/CSSMathMax.cpp +++ b/Libraries/LibWeb/CSS/CSSMathMax.cpp @@ -129,4 +129,32 @@ bool CSSMathMax::is_equal_numeric_value(GC::Ref other) const return m_values->is_equal_numeric_values(other_max->m_values); } +// https://drafts.css-houdini.org/css-typed-om-1/#create-a-sum-value +Optional CSSMathMax::create_a_sum_value() const +{ + // 1. Let args be the result of creating a sum value for each item in this’s values internal slot. + Vector> args; + args.ensure_capacity(m_values->length()); + for (auto const& value : m_values->values()) { + args.unchecked_append(value->create_a_sum_value()); + } + + Optional item_with_largest_value = args.first(); + for (auto const& item : args) { + // 2. If any item of args is failure, or has a length greater than one, return failure. + if (!item.has_value() || item->size() > 1) + return {}; + + // 3. If not all of the unit maps among the items of args are identical, return failure. + if (item->first().unit_map != item_with_largest_value->first().unit_map) + return {}; + + if (item->first().value > item_with_largest_value->first().value) + item_with_largest_value = item; + } + + // 4. Return the item of args whose sole item has the largest value. + return item_with_largest_value; +} + } diff --git a/Libraries/LibWeb/CSS/CSSMathMax.h b/Libraries/LibWeb/CSS/CSSMathMax.h index ed63ca7d337..9fe7d98dae5 100644 --- a/Libraries/LibWeb/CSS/CSSMathMax.h +++ b/Libraries/LibWeb/CSS/CSSMathMax.h @@ -28,6 +28,7 @@ public: virtual String serialize_math_value(Nested, Parens) const override; virtual bool is_equal_numeric_value(GC::Ref other) const override; + virtual Optional create_a_sum_value() const override; private: CSSMathMax(JS::Realm&, NumericType, GC::Ref); diff --git a/Libraries/LibWeb/CSS/CSSMathMin.cpp b/Libraries/LibWeb/CSS/CSSMathMin.cpp index 87aa0bbf10a..733ac0eb862 100644 --- a/Libraries/LibWeb/CSS/CSSMathMin.cpp +++ b/Libraries/LibWeb/CSS/CSSMathMin.cpp @@ -130,4 +130,32 @@ bool CSSMathMin::is_equal_numeric_value(GC::Ref other) const return m_values->is_equal_numeric_values(other_min->m_values); } +// https://drafts.css-houdini.org/css-typed-om-1/#create-a-sum-value +Optional CSSMathMin::create_a_sum_value() const +{ + // 1. Let args be the result of creating a sum value for each item in this’s values internal slot. + Vector> args; + args.ensure_capacity(m_values->length()); + for (auto const& value : m_values->values()) { + args.unchecked_append(value->create_a_sum_value()); + } + + Optional item_with_smallest_value = args.first(); + for (auto const& item : args) { + // 2. If any item of args is failure, or has a length greater than one, return failure. + if (!item.has_value() || item->size() > 1) + return {}; + + // 3. If not all of the unit maps among the items of args are identical, return failure. + if (item->first().unit_map != item_with_smallest_value->first().unit_map) + return {}; + + if (item->first().value < item_with_smallest_value->first().value) + item_with_smallest_value = item; + } + + // 4. Return the item of args whose sole item has the smallest value. + return item_with_smallest_value; +} + } diff --git a/Libraries/LibWeb/CSS/CSSMathMin.h b/Libraries/LibWeb/CSS/CSSMathMin.h index 5ac75c00de0..2e271c1dc4f 100644 --- a/Libraries/LibWeb/CSS/CSSMathMin.h +++ b/Libraries/LibWeb/CSS/CSSMathMin.h @@ -28,6 +28,7 @@ public: virtual String serialize_math_value(Nested, Parens) const override; virtual bool is_equal_numeric_value(GC::Ref other) const override; + virtual Optional create_a_sum_value() const override; private: CSSMathMin(JS::Realm&, NumericType, GC::Ref); diff --git a/Libraries/LibWeb/CSS/CSSMathNegate.cpp b/Libraries/LibWeb/CSS/CSSMathNegate.cpp index dce50176542..706375887b3 100644 --- a/Libraries/LibWeb/CSS/CSSMathNegate.cpp +++ b/Libraries/LibWeb/CSS/CSSMathNegate.cpp @@ -103,4 +103,22 @@ bool CSSMathNegate::is_equal_numeric_value(GC::Ref other) const return m_value->is_equal_numeric_value(other_negate->m_value); } +// https://drafts.css-houdini.org/css-typed-om-1/#create-a-sum-value +Optional CSSMathNegate::create_a_sum_value() const +{ + // 1. Let values be the result of creating a sum value from this’s value internal slot. + auto values = m_value->create_a_sum_value(); + + // 2. If values is failure, return failure. + if (!values.has_value()) + return {}; + + // 3. Negate the value of each item of values. + for (auto& value : *values) + value.value = -value.value; + + // 4. Return values. + return values; +} + } diff --git a/Libraries/LibWeb/CSS/CSSMathNegate.h b/Libraries/LibWeb/CSS/CSSMathNegate.h index 595d600e5b9..3b63b52834c 100644 --- a/Libraries/LibWeb/CSS/CSSMathNegate.h +++ b/Libraries/LibWeb/CSS/CSSMathNegate.h @@ -28,6 +28,7 @@ public: virtual String serialize_math_value(Nested, Parens) const override; virtual bool is_equal_numeric_value(GC::Ref other) const override; + virtual Optional create_a_sum_value() const override; private: CSSMathNegate(JS::Realm&, NumericType, GC::Ref); diff --git a/Libraries/LibWeb/CSS/CSSMathProduct.cpp b/Libraries/LibWeb/CSS/CSSMathProduct.cpp index 0e4d1848893..219cf120e53 100644 --- a/Libraries/LibWeb/CSS/CSSMathProduct.cpp +++ b/Libraries/LibWeb/CSS/CSSMathProduct.cpp @@ -152,4 +152,46 @@ bool CSSMathProduct::is_equal_numeric_value(GC::Ref other) cons return m_values->is_equal_numeric_values(other_product->m_values); } +// https://drafts.css-houdini.org/css-typed-om-1/#create-a-sum-value +Optional CSSMathProduct::create_a_sum_value() const +{ + // 1. Let values initially be the sum value «(1, «[ ]»)». (I.e. what you’d get from 1.) + SumValue values { + SumValueItem { 1, {} } + }; + + // 2. For each item in this’s values internal slot: + for (auto const& item : m_values->values()) { + // 1. Let new values be the result of creating a sum value from item. + // Let temp initially be an empty list. + auto new_values = item->create_a_sum_value(); + SumValue temp; + + // 2. If new values is failure, return failure. + if (!new_values.has_value()) + return {}; + + // 3. For each item1 in values: + for (auto const& item1 : values) { + // 1. For each item2 in new values: + for (auto const& item2 : *new_values) { + // 1. Let item be a tuple with its value set to the product of the values of item1 and item2, and its + // unit map set to the product of the unit maps of item1 and item2, with all entries with a zero + // value removed. + auto unit_map = product_of_two_unit_maps(item1.unit_map, item2.unit_map); + unit_map.remove_all_matching([](auto&, auto& value) { return value == 0; }); + + // 2. Append item to temp. + temp.empend(item1.value * item2.value, move(unit_map)); + } + } + + // 4. Set values to temp. + values = move(temp); + } + + // 3. Return values. + return values; +} + } diff --git a/Libraries/LibWeb/CSS/CSSMathProduct.h b/Libraries/LibWeb/CSS/CSSMathProduct.h index 459b958c5bc..c3326b7ef44 100644 --- a/Libraries/LibWeb/CSS/CSSMathProduct.h +++ b/Libraries/LibWeb/CSS/CSSMathProduct.h @@ -28,6 +28,7 @@ public: virtual String serialize_math_value(Nested, Parens) const override; virtual bool is_equal_numeric_value(GC::Ref other) const override; + virtual Optional create_a_sum_value() const override; private: CSSMathProduct(JS::Realm&, NumericType, GC::Ref); diff --git a/Libraries/LibWeb/CSS/CSSMathSum.cpp b/Libraries/LibWeb/CSS/CSSMathSum.cpp index 626972644b9..9ae6a713eac 100644 --- a/Libraries/LibWeb/CSS/CSSMathSum.cpp +++ b/Libraries/LibWeb/CSS/CSSMathSum.cpp @@ -151,4 +151,59 @@ bool CSSMathSum::is_equal_numeric_value(GC::Ref other) const return m_values->is_equal_numeric_values(other_sum->m_values); } +// https://drafts.css-houdini.org/css-typed-om-1/#create-a-sum-value +Optional CSSMathSum::create_a_sum_value() const +{ + // 1. Let values initially be an empty list. + SumValue values; + + // 2. For each item in this’s values internal slot: + for (auto const item : m_values->values()) { + // 1. Let value be the result of creating a sum value from item. If value is failure, return failure. + auto maybe_value = item->create_a_sum_value(); + if (!maybe_value.has_value()) + return {}; + auto const& value = maybe_value.value(); + + // 2. For each subvalue of value: + for (auto const& subvalue : value) { + // 1. If values already contains an item with the same unit map as subvalue, increment that item’s value by + // the value of subvalue. + auto existing_item = values.find_if([&subvalue](SumValueItem const& other) { + return subvalue.unit_map == other.unit_map; + }); + if (existing_item != values.end()) { + existing_item->value += subvalue.value; + } + // 2. Otherwise, append subvalue to values. + else { + values.append(subvalue); + } + } + } + + // 3. Create a type from the unit map of each item of values, and add all the types together. + // If the result is failure, return failure. + auto added_type = NumericType::create_from_unit_map(values.first().unit_map); + if (!added_type.has_value()) + return {}; + bool first = true; + for (auto const& [_, unit_map] : values) { + if (first) { + first = false; + continue; + } + auto type = NumericType::create_from_unit_map(unit_map); + if (!type.has_value()) + return {}; + + added_type = added_type->added_to(type.value()); + if (!added_type.has_value()) + return {}; + } + + // 4. Return values. + return values; +} + } diff --git a/Libraries/LibWeb/CSS/CSSMathSum.h b/Libraries/LibWeb/CSS/CSSMathSum.h index 81059c22f02..02751646990 100644 --- a/Libraries/LibWeb/CSS/CSSMathSum.h +++ b/Libraries/LibWeb/CSS/CSSMathSum.h @@ -28,6 +28,7 @@ public: virtual String serialize_math_value(Nested, Parens) const override; virtual bool is_equal_numeric_value(GC::Ref other) const override; + virtual Optional create_a_sum_value() const override; private: CSSMathSum(JS::Realm&, NumericType, GC::Ref); diff --git a/Libraries/LibWeb/CSS/CSSNumericValue.h b/Libraries/LibWeb/CSS/CSSNumericValue.h index be3b455367a..ddeecc746f2 100644 --- a/Libraries/LibWeb/CSS/CSSNumericValue.h +++ b/Libraries/LibWeb/CSS/CSSNumericValue.h @@ -28,6 +28,13 @@ struct CSSNumericType { // https://drafts.css-houdini.org/css-typed-om-1/#typedefdef-cssnumberish using CSSNumberish = Variant>; +// https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-sum-value +struct SumValueItem { + double value; + UnitMap unit_map; +}; +using SumValue = Vector; + // https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue class CSSNumericValue : public CSSStyleValue { WEB_PLATFORM_OBJECT(CSSNumericValue, CSSStyleValue); @@ -45,6 +52,8 @@ public: bool equals_for_bindings(Vector) const; virtual bool is_equal_numeric_value(GC::Ref other) const = 0; + virtual Optional create_a_sum_value() const = 0; + CSSNumericType type_for_bindings() const; NumericType const& type() const { return m_type; } diff --git a/Libraries/LibWeb/CSS/CSSUnitValue.cpp b/Libraries/LibWeb/CSS/CSSUnitValue.cpp index 44caed59b28..4230434ba5b 100644 --- a/Libraries/LibWeb/CSS/CSSUnitValue.cpp +++ b/Libraries/LibWeb/CSS/CSSUnitValue.cpp @@ -186,4 +186,80 @@ bool CSSUnitValue::is_equal_numeric_value(GC::Ref other) const && m_value == other_unit_value->m_value; } +// https://drafts.css-houdini.org/css-typed-om-1/#create-a-sum-value +Optional CSSUnitValue::create_a_sum_value() const +{ + // 1. Let unit be the value of this’s unit internal slot, and value be the value of this’s value internal slot. + auto unit = m_unit; + auto value = m_value; + + // 2. If unit is a member of a set of compatible units, and is not the set’s canonical unit, multiply value + // by the conversion ratio between unit and the canonical unit, and change unit to the canonical unit. + if (auto dimension_type = dimension_for_unit(unit); dimension_type.has_value()) { + switch (*dimension_type) { + case DimensionType::Angle: { + auto angle_unit = string_to_angle_unit(unit).release_value(); + auto canonical_unit = canonical_angle_unit(); + if (angle_unit != canonical_unit && units_are_compatible(angle_unit, canonical_unit)) { + value *= ratio_between_units(angle_unit, canonical_unit); + unit = CSS::to_string(canonical_unit); + } + break; + } + case DimensionType::Flex: { + auto flex_unit = string_to_flex_unit(unit).release_value(); + auto canonical_unit = canonical_flex_unit(); + if (flex_unit != canonical_unit && units_are_compatible(flex_unit, canonical_unit)) { + value *= ratio_between_units(flex_unit, canonical_unit); + unit = CSS::to_string(canonical_unit); + } + break; + } + case DimensionType::Frequency: { + auto frequency_unit = string_to_frequency_unit(unit).release_value(); + auto canonical_unit = canonical_frequency_unit(); + if (frequency_unit != canonical_unit && units_are_compatible(frequency_unit, canonical_unit)) { + value *= ratio_between_units(frequency_unit, canonical_unit); + unit = CSS::to_string(canonical_unit); + } + break; + } + case DimensionType::Length: { + auto length_unit = string_to_length_unit(unit).release_value(); + auto canonical_unit = canonical_length_unit(); + if (length_unit != canonical_unit && units_are_compatible(length_unit, canonical_unit)) { + value *= ratio_between_units(length_unit, canonical_unit); + unit = CSS::to_string(canonical_unit); + } + break; + } + case DimensionType::Resolution: { + auto resolution_unit = string_to_resolution_unit(unit).release_value(); + auto canonical_unit = canonical_resolution_unit(); + if (resolution_unit != canonical_unit && units_are_compatible(resolution_unit, canonical_unit)) { + value *= ratio_between_units(resolution_unit, canonical_unit); + unit = CSS::to_string(canonical_unit); + } + break; + } + case DimensionType::Time: { + auto time_unit = string_to_time_unit(unit).release_value(); + auto canonical_unit = canonical_time_unit(); + if (time_unit != canonical_unit && units_are_compatible(time_unit, canonical_unit)) { + value *= ratio_between_units(time_unit, canonical_unit); + unit = CSS::to_string(canonical_unit); + } + break; + } + } + } + + // 3. If unit is "number", return «(value, «[ ]»)». + if (unit == "number"_fly_string) + return SumValue { SumValueItem { value, {} } }; + + // 4. Otherwise, return «(value, «[unit → 1]»)». + return SumValue { SumValueItem { value, { { unit, 1 } } } }; +} + } diff --git a/Libraries/LibWeb/CSS/CSSUnitValue.h b/Libraries/LibWeb/CSS/CSSUnitValue.h index 873a914f48f..aea29ee0a76 100644 --- a/Libraries/LibWeb/CSS/CSSUnitValue.h +++ b/Libraries/LibWeb/CSS/CSSUnitValue.h @@ -32,6 +32,7 @@ public: GC::Ptr converted_to_unit(FlyString const& unit) const; virtual bool is_equal_numeric_value(GC::Ref other) const override; + virtual Optional create_a_sum_value() const override; private: explicit CSSUnitValue(JS::Realm&, double value, FlyString unit, NumericType type);