LibWeb/CSS: Recalculate calc() numeric type when resolving percentages

Previously, `percentage_of` would be called on the previous value,
potentially changing its numeric type, yet this potential change
was never reflected as the old numeric type was always used. Now,
the numeric type will be re-calculated every time after the
percentage is resolved. As well, VERIFY checks have been placed to
uphold the requirements for the numeric types to match what the
actual values are.
This commit is contained in:
Jaycadox 2024-12-22 23:27:36 +10:00 committed by Sam Atkins
parent 938ffe183e
commit db58986e5f
Notes: github-actions[bot] 2025-01-04 18:48:41 +00:00
3 changed files with 28 additions and 4 deletions

View file

@ -109,12 +109,12 @@ CalculationNode::CalculationNode(Type type, Optional<CSSNumericType> numeric_typ
CalculationNode::~CalculationNode() = default;
NonnullOwnPtr<NumericCalculationNode> NumericCalculationNode::create(NumericValue value, Optional<ValueType> percentage_resolved_type)
static CSSNumericType numeric_type_from_calculated_style_value(CalculatedStyleValue::CalculationResult::Value const& value, Optional<ValueType> percentage_resolved_type)
{
// https://drafts.csswg.org/css-values-4/#determine-the-type-of-a-calculation
// Anything else is a terminal value, whose type is determined based on its CSS type.
// (Unless otherwise specified, the types associated percent hint is null.)
auto numeric_type = value.visit(
return value.visit(
[](Number const&) {
// -> <number>
// -> <integer>
@ -172,7 +172,11 @@ NonnullOwnPtr<NumericCalculationNode> NumericCalculationNode::create(NumericValu
// result.set_percent_hint(CSSNumericType::BaseType::Percent);
return result;
});
}
NonnullOwnPtr<NumericCalculationNode> NumericCalculationNode::create(NumericValue value, Optional<ValueType> percentage_resolved_type)
{
auto numeric_type = numeric_type_from_calculated_style_value(value, percentage_resolved_type);
return adopt_own(*new (nothrow) NumericCalculationNode(move(value), numeric_type));
}
@ -200,11 +204,13 @@ CalculatedStyleValue::CalculationResult NumericCalculationNode::resolve(Optional
// NOTE: Depending on whether percentage_basis is set, the caller of resolve() is expecting a raw percentage or
// resolved type.
return percentage_basis.visit(
[&](Empty const&) -> CalculatedStyleValue::CalculationResult {
[&](Empty const&) {
VERIFY(numeric_type_from_calculated_style_value(m_value, {}) == numeric_type());
return CalculatedStyleValue::CalculationResult::from_value(m_value, resolution_context, numeric_type());
},
[&](auto const& value) {
return CalculatedStyleValue::CalculationResult::from_value(value.percentage_of(m_value.get<Percentage>()), resolution_context, numeric_type());
auto const calculated_value = value.percentage_of(m_value.get<Percentage>());
return CalculatedStyleValue::CalculationResult::from_value(calculated_value, resolution_context, numeric_type_from_calculated_style_value(calculated_value, {}));
});
}
@ -1730,6 +1736,11 @@ bool RemCalculationNode::equals(CalculationNode const& other) const
CalculatedStyleValue::CalculationResult CalculatedStyleValue::CalculationResult::from_value(Value const& value, Optional<Length::ResolutionContext const&> context, Optional<CSSNumericType> numeric_type)
{
auto const expected_numeric_type = numeric_type_from_calculated_style_value(value, {});
if (numeric_type.has_value()) {
VERIFY(numeric_type.value() == expected_numeric_type);
}
auto number = value.visit(
[](Number const& number) { return number.value(); },
[](Angle const& angle) { return angle.to_degrees(); },

View file

@ -0,0 +1 @@
PASS! (Didn't crash)

View file

@ -0,0 +1,12 @@
<script src="../include.js"></script>
<style>
div {
transform: translateX(calc(10%))
}
</style>
<div></div>
<script>
test(() => {
println(`PASS! (Didn't crash)`);
});
</script>