LibWeb: Add new CalculatedStyleValue::resolve_* methods

These new methods are built on top of the spec's
`simplify_a_calculation_tree` algorithm where the old methods were
ad-hoc.

These methods are not used anywhere yet as callers will need to be
migrated over from the deprecated methods one-by-one to account for
differences in behaviour.

No functionality changes.
This commit is contained in:
Callum Law 2025-07-02 19:50:16 +12:00 committed by Sam Atkins
commit e66332c07a
Notes: github-actions[bot] 2025-07-16 12:06:52 +00:00
2 changed files with 132 additions and 0 deletions

View file

@ -2669,6 +2669,30 @@ bool CalculatedStyleValue::equals(CSSStyleValue const& other) const
return m_calculation->equals(*other.as_calculated().m_calculation);
}
// https://drafts.csswg.org/css-values-4/#calc-computed-value
Optional<CalculatedStyleValue::CalculationResult> CalculatedStyleValue::resolve_value(CalculationResolutionContext const& resolution_context) const
{
// The calculation tree is again simplified at used value time; with used value time information.
auto simplified_tree = simplify_a_calculation_tree(m_calculation, m_context, resolution_context);
if (!is<NumericCalculationNode>(*simplified_tree))
return {};
auto value = try_get_value_with_canonical_unit(simplified_tree, m_context, resolution_context);
VERIFY(value.has_value());
// https://drafts.csswg.org/css-values/#calc-ieee
// FIXME: NaN does not escape a top-level calculation; its censored into a zero value.
// https://drafts.csswg.org/css-values/#calc-range
// FIXME: the value resulting from a top-level calculation must be clamped to the range allowed in the target
// context. Clamping is performed on computed values to the extent possible, and also on used values if
// computation was unable to sufficiently simplify the expression to allow range-checking.
return value;
}
Optional<Angle> CalculatedStyleValue::resolve_angle_deprecated(CalculationResolutionContext const& context) const
{
auto result = m_calculation->resolve(context);
@ -2677,6 +2701,16 @@ Optional<Angle> CalculatedStyleValue::resolve_angle_deprecated(CalculationResolu
return {};
}
Optional<Angle> CalculatedStyleValue::resolve_angle(CalculationResolutionContext const& context) const
{
auto result = resolve_value(context);
if (result.has_value() && result.value().type().has_value() && result.value().type()->matches_angle(m_context.percentages_resolve_as))
return Angle::make_degrees(result.value().value());
return {};
}
Optional<Flex> CalculatedStyleValue::resolve_flex_deprecated(CalculationResolutionContext const& context) const
{
auto result = m_calculation->resolve(context);
@ -2685,6 +2719,16 @@ Optional<Flex> CalculatedStyleValue::resolve_flex_deprecated(CalculationResoluti
return {};
}
Optional<Flex> CalculatedStyleValue::resolve_flex(CalculationResolutionContext const& context) const
{
auto result = resolve_value(context);
if (result.has_value() && result.value().type().has_value() && result.value().type()->matches_flex(m_context.percentages_resolve_as))
return Flex::make_fr(result.value().value());
return {};
}
Optional<Frequency> CalculatedStyleValue::resolve_frequency_deprecated(CalculationResolutionContext const& context) const
{
auto result = m_calculation->resolve(context);
@ -2693,6 +2737,16 @@ Optional<Frequency> CalculatedStyleValue::resolve_frequency_deprecated(Calculati
return {};
}
Optional<Frequency> CalculatedStyleValue::resolve_frequency(CalculationResolutionContext const& context) const
{
auto result = resolve_value(context);
if (result.has_value() && result.value().type().has_value() && result.value().type()->matches_frequency(m_context.percentages_resolve_as))
return Frequency::make_hertz(result.value().value());
return {};
}
Optional<Length> CalculatedStyleValue::resolve_length_deprecated(CalculationResolutionContext const& context) const
{
auto result = m_calculation->resolve(context);
@ -2701,6 +2755,16 @@ Optional<Length> CalculatedStyleValue::resolve_length_deprecated(CalculationReso
return {};
}
Optional<Length> CalculatedStyleValue::resolve_length(CalculationResolutionContext const& context) const
{
auto result = resolve_value(context);
if (result.has_value() && result.value().type().has_value() && result.value().type()->matches_length(m_context.percentages_resolve_as))
return Length::make_px(result.value().value());
return {};
}
Optional<Percentage> CalculatedStyleValue::resolve_percentage_deprecated(CalculationResolutionContext const& context) const
{
auto result = m_calculation->resolve(context);
@ -2709,6 +2773,16 @@ Optional<Percentage> CalculatedStyleValue::resolve_percentage_deprecated(Calcula
return {};
}
Optional<Percentage> CalculatedStyleValue::resolve_percentage(CalculationResolutionContext const& context) const
{
auto result = resolve_value(context);
if (result.has_value() && result.value().type().has_value() && result.value().type()->matches_percentage())
return Percentage { result.value().value() };
return {};
}
Optional<Resolution> CalculatedStyleValue::resolve_resolution_deprecated(CalculationResolutionContext const& context) const
{
auto result = m_calculation->resolve(context);
@ -2717,6 +2791,16 @@ Optional<Resolution> CalculatedStyleValue::resolve_resolution_deprecated(Calcula
return {};
}
Optional<Resolution> CalculatedStyleValue::resolve_resolution(CalculationResolutionContext const& context) const
{
auto result = resolve_value(context);
if (result.has_value() && result.value().type().has_value() && result.value().type()->matches_resolution(m_context.percentages_resolve_as))
return Resolution::make_dots_per_pixel(result.value().value());
return {};
}
Optional<Time> CalculatedStyleValue::resolve_time_deprecated(CalculationResolutionContext const& context) const
{
auto result = m_calculation->resolve(context);
@ -2725,6 +2809,16 @@ Optional<Time> CalculatedStyleValue::resolve_time_deprecated(CalculationResoluti
return {};
}
Optional<Time> CalculatedStyleValue::resolve_time(CalculationResolutionContext const& context) const
{
auto result = resolve_value(context);
if (result.has_value() && result.value().type().has_value() && result.value().type()->matches_time(m_context.percentages_resolve_as))
return Time::make_seconds(result.value().value());
return {};
}
Optional<double> CalculatedStyleValue::resolve_number_deprecated(CalculationResolutionContext const& context) const
{
auto result = m_calculation->resolve(context);
@ -2740,6 +2834,23 @@ Optional<double> CalculatedStyleValue::resolve_number_deprecated(CalculationReso
return value;
}
Optional<double> CalculatedStyleValue::resolve_number(CalculationResolutionContext const& context) const
{
auto result = resolve_value(context);
if (result.has_value() && result.value().type().has_value() && result.value().type()->matches_number(m_context.percentages_resolve_as)) {
auto value = result.value().value();
// FIXME: This can be removed once it is upstreamed to `resolve_value`
if (isnan(value))
return 0.;
return result.value().value();
}
return {};
}
Optional<i64> CalculatedStyleValue::resolve_integer_deprecated(CalculationResolutionContext const& context) const
{
auto result = m_calculation->resolve(context);
@ -2748,6 +2859,16 @@ Optional<i64> CalculatedStyleValue::resolve_integer_deprecated(CalculationResolu
return {};
}
Optional<i64> CalculatedStyleValue::resolve_integer(CalculationResolutionContext const& context) const
{
auto result = resolve_value(context);
if (result.has_value() && result.value().type().has_value() && result.value().type()->matches_number(m_context.percentages_resolve_as))
return llround(result.value().value());
return {};
}
bool CalculatedStyleValue::contains_percentage() const
{
return m_calculation->contains_percentage();

View file

@ -75,34 +75,45 @@ public:
virtual String to_string(SerializationMode) const override;
virtual bool equals(CSSStyleValue const& other) const override;
Optional<CalculationResult> resolve_value(CalculationResolutionContext const&) const;
bool resolves_to_angle() const { return m_resolved_type.matches_angle(m_context.percentages_resolve_as); }
bool resolves_to_angle_percentage() const { return m_resolved_type.matches_angle_percentage(m_context.percentages_resolve_as); }
Optional<Angle> resolve_angle_deprecated(CalculationResolutionContext const&) const;
Optional<Angle> resolve_angle(CalculationResolutionContext const&) const;
bool resolves_to_flex() const { return m_resolved_type.matches_flex(m_context.percentages_resolve_as); }
Optional<Flex> resolve_flex_deprecated(CalculationResolutionContext const&) const;
Optional<Flex> resolve_flex(CalculationResolutionContext const&) const;
bool resolves_to_frequency() const { return m_resolved_type.matches_frequency(m_context.percentages_resolve_as); }
bool resolves_to_frequency_percentage() const { return m_resolved_type.matches_frequency_percentage(m_context.percentages_resolve_as); }
Optional<Frequency> resolve_frequency_deprecated(CalculationResolutionContext const&) const;
Optional<Frequency> resolve_frequency(CalculationResolutionContext const&) const;
bool resolves_to_length() const { return m_resolved_type.matches_length(m_context.percentages_resolve_as); }
bool resolves_to_length_percentage() const { return m_resolved_type.matches_length_percentage(m_context.percentages_resolve_as); }
Optional<Length> resolve_length_deprecated(CalculationResolutionContext const&) const;
Optional<Length> resolve_length(CalculationResolutionContext const&) const;
bool resolves_to_percentage() const { return m_resolved_type.matches_percentage(); }
Optional<Percentage> resolve_percentage_deprecated(CalculationResolutionContext const&) const;
Optional<Percentage> resolve_percentage(CalculationResolutionContext const&) const;
bool resolves_to_resolution() const { return m_resolved_type.matches_resolution(m_context.percentages_resolve_as); }
Optional<Resolution> resolve_resolution_deprecated(CalculationResolutionContext const&) const;
Optional<Resolution> resolve_resolution(CalculationResolutionContext const&) const;
bool resolves_to_time() const { return m_resolved_type.matches_time(m_context.percentages_resolve_as); }
bool resolves_to_time_percentage() const { return m_resolved_type.matches_time_percentage(m_context.percentages_resolve_as); }
Optional<Time> resolve_time_deprecated(CalculationResolutionContext const&) const;
Optional<Time> resolve_time(CalculationResolutionContext const&) const;
bool resolves_to_number() const { return m_resolved_type.matches_number(m_context.percentages_resolve_as); }
Optional<double> resolve_number_deprecated(CalculationResolutionContext const&) const;
Optional<double> resolve_number(CalculationResolutionContext const&) const;
Optional<i64> resolve_integer_deprecated(CalculationResolutionContext const&) const;
Optional<i64> resolve_integer(CalculationResolutionContext const&) const;
bool resolves_to_dimension() const { return m_resolved_type.matches_dimension(); }