LibWeb: Compute dimension percentage mix as percentage where applicable

When we have a `calc` which is a mix of a dimension and a percentage, we
should use the percentage alone for the computed value if the dimension
component is 0 e.g. `calc(50% + 0px)` should use `50%` as it's computed
value.
This commit is contained in:
Callum Law 2025-09-12 21:21:24 +12:00 committed by Sam Atkins
commit a805635e40
Notes: github-actions[bot] 2025-09-12 14:05:24 +00:00
5 changed files with 67 additions and 31 deletions

View file

@ -106,6 +106,7 @@ public:
bool operator==(NumericType const& other) const = default;
String dump() const;
Optional<BaseType> entry_with_value_1_while_all_others_are_0() const;
private:
bool contains_all_the_non_zero_entries_of_other_with_the_same_value(NumericType const& other) const;
@ -116,7 +117,6 @@ private:
};
void copy_all_entries_from(NumericType const& other, SkipIfAlreadyPresent);
Optional<BaseType> entry_with_value_1_while_all_others_are_0() const;
bool matches_dimension(BaseType, Optional<ValueType> percentages_resolve_as) const;
bool matches_dimension_percentage(BaseType, Optional<ValueType> percentages_resolve_as) const;

View file

@ -2877,7 +2877,46 @@ ValueComparingNonnullRefPtr<StyleValue const> CalculatedStyleValue::absolutized(
.root_font_metrics = root_font_metrics
};
return CalculatedStyleValue::create(simplify_a_calculation_tree(m_calculation, m_context, { .length_resolution_context = length_resolution_context }), m_resolved_type, m_context);
auto simplified_calculation_tree = simplify_a_calculation_tree(m_calculation, m_context, { .length_resolution_context = length_resolution_context });
auto const simplified_percentage_dimension_mix = [&]() -> Optional<ValueComparingNonnullRefPtr<StyleValue const>> {
// NOTE: A percentage dimension mix is a SumCalculationNode with two NumericCalculationNode children which have
// matching base types - only the first of which has a percent hint.
if (simplified_calculation_tree->type() != CalculationNode::Type::Sum)
return {};
auto const& sum_node = as<SumCalculationNode>(*simplified_calculation_tree);
if (sum_node.children()[0]->type() != CalculationNode::Type::Numeric || sum_node.children()[1]->type() != CalculationNode::Type::Numeric)
return {};
auto const& first_node = as<NumericCalculationNode>(*sum_node.children()[0]);
auto const& second_node = as<NumericCalculationNode>(*sum_node.children()[1]);
auto first_base_type = first_node.numeric_type()->entry_with_value_1_while_all_others_are_0();
auto second_base_type = second_node.numeric_type()->entry_with_value_1_while_all_others_are_0();
if (!first_base_type.has_value() || first_base_type != second_base_type)
return {};
if (!first_node.numeric_type()->percent_hint().has_value() || second_node.numeric_type()->percent_hint().has_value())
return {};
auto dimension_component = try_get_value_with_canonical_unit(second_node, m_context, {});
// https://drafts.csswg.org/css-values-4/#combine-mixed
// The computed value of a percentage-dimension mix is defined as
// - a computed percentage if the dimension component is zero
if (dimension_component->value() == 0)
return PercentageStyleValue::create(first_node.value().get<Percentage>());
return {};
}();
if (simplified_percentage_dimension_mix.has_value())
return simplified_percentage_dimension_mix.value();
return CalculatedStyleValue::create(simplified_calculation_tree, m_resolved_type, m_context);
}
bool CalculatedStyleValue::equals(StyleValue const& other) const

View file

@ -2,8 +2,7 @@ Harness status: OK
Found 12 tests
11 Pass
1 Fail
12 Pass
Pass Property flex-basis value '1px'
Pass Property flex-basis value '400%'
Pass Property flex-basis value 'auto'
@ -15,4 +14,4 @@ Pass Property flex-basis value 'calc(10px + 0.5em)'
Pass Property flex-basis value 'calc(10px - 0.5em)'
Pass Property flex-basis value 'calc(10%)'
Pass Property flex-basis value 'calc(0% + 10px)'
Fail Property flex-basis value 'calc(10% + 0px)'
Pass Property flex-basis value 'calc(10% + 0px)'

View file

@ -2,8 +2,7 @@ Harness status: OK
Found 320 tests
300 Pass
20 Fail
320 Pass
Pass CSS Transitions: property <text-underline-offset> from [15px] to [0px] at (0) should be [15px]
Pass CSS Transitions: property <text-underline-offset> from [15px] to [0px] at (0.3) should be [10.5px]
Pass CSS Transitions: property <text-underline-offset> from [15px] to [0px] at (0.6) should be [6px]
@ -260,38 +259,38 @@ Pass Web Animations: property <text-underline-offset> from [100%] to [0px] at (0
Pass Web Animations: property <text-underline-offset> from [100%] to [0px] at (0.3) should be [70%]
Pass Web Animations: property <text-underline-offset> from [100%] to [0px] at (0.6) should be [40%]
Pass Web Animations: property <text-underline-offset> from [100%] to [0px] at (1) should be [0%]
Fail CSS Transitions: property <text-underline-offset> from [100%] to [32px] at (0) should be [calc(100% + 0px)]
Pass CSS Transitions: property <text-underline-offset> from [100%] to [32px] at (0) should be [calc(100% + 0px)]
Pass CSS Transitions: property <text-underline-offset> from [100%] to [32px] at (0.3) should be [calc(70% + 9.6px)]
Pass CSS Transitions: property <text-underline-offset> from [100%] to [32px] at (0.6) should be [calc(40% + 19.2px)]
Pass CSS Transitions: property <text-underline-offset> from [100%] to [32px] at (1) should be [calc(0% + 32px)]
Fail CSS Transitions with transition: all: property <text-underline-offset> from [100%] to [32px] at (0) should be [calc(100% + 0px)]
Pass CSS Transitions with transition: all: property <text-underline-offset> from [100%] to [32px] at (0) should be [calc(100% + 0px)]
Pass CSS Transitions with transition: all: property <text-underline-offset> from [100%] to [32px] at (0.3) should be [calc(70% + 9.6px)]
Pass CSS Transitions with transition: all: property <text-underline-offset> from [100%] to [32px] at (0.6) should be [calc(40% + 19.2px)]
Pass CSS Transitions with transition: all: property <text-underline-offset> from [100%] to [32px] at (1) should be [calc(0% + 32px)]
Fail CSS Animations: property <text-underline-offset> from [100%] to [32px] at (0) should be [calc(100% + 0px)]
Pass CSS Animations: property <text-underline-offset> from [100%] to [32px] at (0) should be [calc(100% + 0px)]
Pass CSS Animations: property <text-underline-offset> from [100%] to [32px] at (0.3) should be [calc(70% + 9.6px)]
Pass CSS Animations: property <text-underline-offset> from [100%] to [32px] at (0.6) should be [calc(40% + 19.2px)]
Pass CSS Animations: property <text-underline-offset> from [100%] to [32px] at (1) should be [calc(0% + 32px)]
Fail Web Animations: property <text-underline-offset> from [100%] to [32px] at (0) should be [calc(100% + 0px)]
Pass Web Animations: property <text-underline-offset> from [100%] to [32px] at (0) should be [calc(100% + 0px)]
Pass Web Animations: property <text-underline-offset> from [100%] to [32px] at (0.3) should be [calc(70% + 9.6px)]
Pass Web Animations: property <text-underline-offset> from [100%] to [32px] at (0.6) should be [calc(40% + 19.2px)]
Pass Web Animations: property <text-underline-offset> from [100%] to [32px] at (1) should be [calc(0% + 32px)]
Fail CSS Transitions: property <text-underline-offset> from [100%] to [0em] at (0) should be [calc(100% + 0em)]
Fail CSS Transitions: property <text-underline-offset> from [100%] to [0em] at (0.3) should be [calc(70% + 0em)]
Fail CSS Transitions: property <text-underline-offset> from [100%] to [0em] at (0.6) should be [calc(40% + 0em)]
Fail CSS Transitions: property <text-underline-offset> from [100%] to [0em] at (1) should be [calc(0% + 0em)]
Fail CSS Transitions with transition: all: property <text-underline-offset> from [100%] to [0em] at (0) should be [calc(100% + 0em)]
Fail CSS Transitions with transition: all: property <text-underline-offset> from [100%] to [0em] at (0.3) should be [calc(70% + 0em)]
Fail CSS Transitions with transition: all: property <text-underline-offset> from [100%] to [0em] at (0.6) should be [calc(40% + 0em)]
Fail CSS Transitions with transition: all: property <text-underline-offset> from [100%] to [0em] at (1) should be [calc(0% + 0em)]
Fail CSS Animations: property <text-underline-offset> from [100%] to [0em] at (0) should be [calc(100% + 0em)]
Fail CSS Animations: property <text-underline-offset> from [100%] to [0em] at (0.3) should be [calc(70% + 0em)]
Fail CSS Animations: property <text-underline-offset> from [100%] to [0em] at (0.6) should be [calc(40% + 0em)]
Fail CSS Animations: property <text-underline-offset> from [100%] to [0em] at (1) should be [calc(0% + 0em)]
Fail Web Animations: property <text-underline-offset> from [100%] to [0em] at (0) should be [calc(100% + 0em)]
Fail Web Animations: property <text-underline-offset> from [100%] to [0em] at (0.3) should be [calc(70% + 0em)]
Fail Web Animations: property <text-underline-offset> from [100%] to [0em] at (0.6) should be [calc(40% + 0em)]
Fail Web Animations: property <text-underline-offset> from [100%] to [0em] at (1) should be [calc(0% + 0em)]
Pass CSS Transitions: property <text-underline-offset> from [100%] to [0em] at (0) should be [calc(100% + 0em)]
Pass CSS Transitions: property <text-underline-offset> from [100%] to [0em] at (0.3) should be [calc(70% + 0em)]
Pass CSS Transitions: property <text-underline-offset> from [100%] to [0em] at (0.6) should be [calc(40% + 0em)]
Pass CSS Transitions: property <text-underline-offset> from [100%] to [0em] at (1) should be [calc(0% + 0em)]
Pass CSS Transitions with transition: all: property <text-underline-offset> from [100%] to [0em] at (0) should be [calc(100% + 0em)]
Pass CSS Transitions with transition: all: property <text-underline-offset> from [100%] to [0em] at (0.3) should be [calc(70% + 0em)]
Pass CSS Transitions with transition: all: property <text-underline-offset> from [100%] to [0em] at (0.6) should be [calc(40% + 0em)]
Pass CSS Transitions with transition: all: property <text-underline-offset> from [100%] to [0em] at (1) should be [calc(0% + 0em)]
Pass CSS Animations: property <text-underline-offset> from [100%] to [0em] at (0) should be [calc(100% + 0em)]
Pass CSS Animations: property <text-underline-offset> from [100%] to [0em] at (0.3) should be [calc(70% + 0em)]
Pass CSS Animations: property <text-underline-offset> from [100%] to [0em] at (0.6) should be [calc(40% + 0em)]
Pass CSS Animations: property <text-underline-offset> from [100%] to [0em] at (1) should be [calc(0% + 0em)]
Pass Web Animations: property <text-underline-offset> from [100%] to [0em] at (0) should be [calc(100% + 0em)]
Pass Web Animations: property <text-underline-offset> from [100%] to [0em] at (0.3) should be [calc(70% + 0em)]
Pass Web Animations: property <text-underline-offset> from [100%] to [0em] at (0.6) should be [calc(40% + 0em)]
Pass Web Animations: property <text-underline-offset> from [100%] to [0em] at (1) should be [calc(0% + 0em)]
Pass CSS Transitions: property <text-underline-offset> from [100%] to [2em] at (0) should be [100%]
Pass CSS Transitions: property <text-underline-offset> from [100%] to [2em] at (0.3) should be [calc(70% + 9.6px)]
Pass CSS Transitions: property <text-underline-offset> from [100%] to [2em] at (0.6) should be [calc(40% + 19.2px)]

View file

@ -2,17 +2,16 @@ Harness status: OK
Found 15 tests
13 Pass
2 Fail
15 Pass
Pass Property text-underline-offset value 'auto'
Pass Property text-underline-offset value 'calc(10px - 8px)'
Pass Property text-underline-offset value '32px'
Pass Property text-underline-offset value '2em'
Pass Property text-underline-offset value '200%'
Pass Property text-underline-offset value 'calc(2em - 0px)'
Fail Property text-underline-offset value 'calc(200% - 0px)'
Pass Property text-underline-offset value 'calc(200% - 0px)'
Pass Property text-underline-offset value 'calc(0.5em - 0px)'
Fail Property text-underline-offset value 'calc(50% - 0px)'
Pass Property text-underline-offset value 'calc(50% - 0px)'
Pass Property text-underline-offset value 'calc(2em - 8px)'
Pass Property text-underline-offset value 'calc(2em - 0.5em)'
Pass Property text-underline-offset value 'calc(2em - 50%)'