LibWeb/CSS: Wrap calc()-resolution data in a struct

Initially I added this to the existing CalculationContext, but in
reality, we have some data at parse-time and different data at
resolve-time, so it made more sense to keep those separate.

Instead of needing a variety of methods for resolving a Foo, depending
on whether we have a Layout::Node available, or a percentage basis, or
a length resolution context... put those in a
CalculationResolutionContext, and just pass that one thing to these
methods. This also removes the need for separate resolve_*_percentage()
methods, because we can just pass the percentage basis in to the regular
resolve_foo() method.

This also corrects the issue that *any* calculation may need to resolve
lengths, but we previously only passed a length resolution context to
specific types in some situations. Now, they can all have one available,
though it's up to the caller to provide it.
This commit is contained in:
Sam Atkins 2025-01-22 16:05:32 +00:00 committed by Andreas Kling
commit 1d71662f31
Notes: github-actions[bot] 2025-01-30 18:33:58 +00:00
18 changed files with 256 additions and 309 deletions

View file

@ -84,9 +84,14 @@ Optional<Angle::Type> Angle::unit_from_name(StringView name)
return {}; return {};
} }
Angle Angle::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const&, Angle const& reference_value) Angle Angle::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node, Angle const& reference_value)
{ {
return calculated->resolve_angle_percentage(reference_value).value(); return calculated->resolve_angle(
{
.percentage_basis = reference_value,
.length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node),
})
.value();
} }
} }

View file

@ -386,7 +386,7 @@ int CSSStyleValue::to_font_weight() const
return round_to<int>(as_number().number()); return round_to<int>(as_number().number());
} }
if (is_calculated()) { if (is_calculated()) {
auto maybe_weight = as_calculated().resolve_integer(); auto maybe_weight = as_calculated().resolve_integer({});
if (maybe_weight.has_value()) if (maybe_weight.has_value())
return maybe_weight.value(); return maybe_weight.value();
} }

View file

@ -17,9 +17,9 @@
namespace Web::CSS { namespace Web::CSS {
Angle AngleOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const&) const Angle AngleOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node) const
{ {
return calculated->resolve_angle().value(); return calculated->resolve_angle({ .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) }).value();
} }
NonnullRefPtr<CSSStyleValue> AngleOrCalculated::create_style_value() const NonnullRefPtr<CSSStyleValue> AngleOrCalculated::create_style_value() const
@ -27,9 +27,9 @@ NonnullRefPtr<CSSStyleValue> AngleOrCalculated::create_style_value() const
return AngleStyleValue::create(value()); return AngleStyleValue::create(value());
} }
Flex FlexOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const&) const Flex FlexOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node) const
{ {
return calculated->resolve_flex().value(); return calculated->resolve_flex({ .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) }).value();
} }
NonnullRefPtr<CSSStyleValue> FlexOrCalculated::create_style_value() const NonnullRefPtr<CSSStyleValue> FlexOrCalculated::create_style_value() const
@ -37,9 +37,9 @@ NonnullRefPtr<CSSStyleValue> FlexOrCalculated::create_style_value() const
return FlexStyleValue::create(value()); return FlexStyleValue::create(value());
} }
Frequency FrequencyOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const&) const Frequency FrequencyOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node) const
{ {
return calculated->resolve_frequency().value(); return calculated->resolve_frequency({ .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) }).value();
} }
NonnullRefPtr<CSSStyleValue> FrequencyOrCalculated::create_style_value() const NonnullRefPtr<CSSStyleValue> FrequencyOrCalculated::create_style_value() const
@ -49,13 +49,13 @@ NonnullRefPtr<CSSStyleValue> FrequencyOrCalculated::create_style_value() const
i64 IntegerOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node) const i64 IntegerOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node) const
{ {
return calculated->resolve_integer(layout_node).value(); return calculated->resolve_integer({ .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) }).value();
} }
i64 IntegerOrCalculated::resolved(Length::ResolutionContext const& context) const i64 IntegerOrCalculated::resolved(Length::ResolutionContext const& context) const
{ {
if (is_calculated()) if (is_calculated())
return calculated()->resolve_integer(context).value(); return calculated()->resolve_integer({ .length_resolution_context = context }).value();
return value(); return value();
} }
@ -66,13 +66,13 @@ NonnullRefPtr<CSSStyleValue> IntegerOrCalculated::create_style_value() const
Length LengthOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node) const Length LengthOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node) const
{ {
return calculated->resolve_length(layout_node).value(); return calculated->resolve_length({ .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) }).value();
} }
Length LengthOrCalculated::resolved(Length::ResolutionContext const& context) const Length LengthOrCalculated::resolved(Length::ResolutionContext const& context) const
{ {
if (is_calculated()) if (is_calculated())
return calculated()->resolve_length(context).value(); return calculated()->resolve_length({ .length_resolution_context = context }).value();
return value(); return value();
} }
@ -83,7 +83,7 @@ NonnullRefPtr<CSSStyleValue> LengthOrCalculated::create_style_value() const
double NumberOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node) const double NumberOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node) const
{ {
return calculated->resolve_number(layout_node).value(); return calculated->resolve_number({ .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) }).value();
} }
NonnullRefPtr<CSSStyleValue> NumberOrCalculated::create_style_value() const NonnullRefPtr<CSSStyleValue> NumberOrCalculated::create_style_value() const
@ -91,9 +91,9 @@ NonnullRefPtr<CSSStyleValue> NumberOrCalculated::create_style_value() const
return NumberStyleValue::create(value()); return NumberStyleValue::create(value());
} }
Percentage PercentageOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const&) const Percentage PercentageOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node) const
{ {
return calculated->resolve_percentage().value(); return calculated->resolve_percentage({ .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) }).value();
} }
NonnullRefPtr<CSSStyleValue> PercentageOrCalculated::create_style_value() const NonnullRefPtr<CSSStyleValue> PercentageOrCalculated::create_style_value() const
@ -101,15 +101,15 @@ NonnullRefPtr<CSSStyleValue> PercentageOrCalculated::create_style_value() const
return PercentageStyleValue::create(value()); return PercentageStyleValue::create(value());
} }
Resolution ResolutionOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const&) const Resolution ResolutionOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node) const
{ {
return calculated->resolve_resolution().value(); return calculated->resolve_resolution({ .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) }).value();
} }
Resolution ResolutionOrCalculated::resolved() const Resolution ResolutionOrCalculated::resolved() const
{ {
if (is_calculated()) if (is_calculated())
return calculated()->resolve_resolution().value(); return calculated()->resolve_resolution({}).value();
return value(); return value();
} }
@ -118,9 +118,9 @@ NonnullRefPtr<CSSStyleValue> ResolutionOrCalculated::create_style_value() const
return ResolutionStyleValue::create(value()); return ResolutionStyleValue::create(value());
} }
Time TimeOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const&) const Time TimeOrCalculated::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node) const
{ {
return calculated->resolve_time().value(); return calculated->resolve_time({ .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) }).value();
} }
NonnullRefPtr<CSSStyleValue> TimeOrCalculated::create_style_value() const NonnullRefPtr<CSSStyleValue> TimeOrCalculated::create_style_value() const

View file

@ -301,8 +301,12 @@ CSSPixels ComputedProperties::compute_line_height(CSSPixelRect const& viewport_r
} }
if (line_height.is_calculated()) { if (line_height.is_calculated()) {
CalculationResolutionContext context {
.percentage_basis = Length(1, Length::Type::Em),
.length_resolution_context = Length::ResolutionContext { viewport_rect, font_metrics, root_font_metrics },
};
if (line_height.as_calculated().resolves_to_number()) { if (line_height.as_calculated().resolves_to_number()) {
auto resolved = line_height.as_calculated().resolve_number(); auto resolved = line_height.as_calculated().resolve_number(context);
if (!resolved.has_value()) { if (!resolved.has_value()) {
dbgln("FIXME: Failed to resolve calc() line-height (number): {}", line_height.as_calculated().to_string(CSSStyleValue::SerializationMode::Normal)); dbgln("FIXME: Failed to resolve calc() line-height (number): {}", line_height.as_calculated().to_string(CSSStyleValue::SerializationMode::Normal));
return CSSPixels::nearest_value_for(m_font_list->first().pixel_metrics().line_spacing()); return CSSPixels::nearest_value_for(m_font_list->first().pixel_metrics().line_spacing());
@ -310,7 +314,7 @@ CSSPixels ComputedProperties::compute_line_height(CSSPixelRect const& viewport_r
return Length(resolved.value(), Length::Type::Em).to_px(viewport_rect, font_metrics, root_font_metrics); return Length(resolved.value(), Length::Type::Em).to_px(viewport_rect, font_metrics, root_font_metrics);
} }
auto resolved = line_height.as_calculated().resolve_length(Length::ResolutionContext { viewport_rect, font_metrics, root_font_metrics }); auto resolved = line_height.as_calculated().resolve_length(context);
if (!resolved.has_value()) { if (!resolved.has_value()) {
dbgln("FIXME: Failed to resolve calc() line-height: {}", line_height.as_calculated().to_string(CSSStyleValue::SerializationMode::Normal)); dbgln("FIXME: Failed to resolve calc() line-height: {}", line_height.as_calculated().to_string(CSSStyleValue::SerializationMode::Normal));
return CSSPixels::nearest_value_for(m_font_list->first().pixel_metrics().line_spacing()); return CSSPixels::nearest_value_for(m_font_list->first().pixel_metrics().line_spacing());
@ -346,14 +350,15 @@ float ComputedProperties::resolve_opacity_value(CSSStyleValue const& value)
unclamped_opacity = value.as_number().number(); unclamped_opacity = value.as_number().number();
} else if (value.is_calculated()) { } else if (value.is_calculated()) {
auto const& calculated = value.as_calculated(); auto const& calculated = value.as_calculated();
CalculationResolutionContext context {};
if (calculated.resolves_to_percentage()) { if (calculated.resolves_to_percentage()) {
auto maybe_percentage = value.as_calculated().resolve_percentage(); auto maybe_percentage = value.as_calculated().resolve_percentage(context);
if (maybe_percentage.has_value()) if (maybe_percentage.has_value())
unclamped_opacity = maybe_percentage->as_fraction(); unclamped_opacity = maybe_percentage->as_fraction();
else else
dbgln("Unable to resolve calc() as opacity (percentage): {}", value.to_string(CSSStyleValue::SerializationMode::Normal)); dbgln("Unable to resolve calc() as opacity (percentage): {}", value.to_string(CSSStyleValue::SerializationMode::Normal));
} else if (calculated.resolves_to_number()) { } else if (calculated.resolves_to_number()) {
auto maybe_number = value.as_calculated().resolve_number(); auto maybe_number = value.as_calculated().resolve_number(context);
if (maybe_number.has_value()) if (maybe_number.has_value())
unclamped_opacity = maybe_number.value(); unclamped_opacity = maybe_number.value();
else else
@ -485,7 +490,7 @@ CSS::Length ComputedProperties::border_spacing_horizontal(Layout::Node const& la
if (value.is_length()) if (value.is_length())
return value.as_length().length(); return value.as_length().length();
if (value.is_calculated()) if (value.is_calculated())
return value.as_calculated().resolve_length(layout_node).value_or(CSS::Length(0, CSS::Length::Type::Px)); return value.as_calculated().resolve_length({ .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) }).value_or(CSS::Length(0, CSS::Length::Type::Px));
auto const& list = value.as_value_list(); auto const& list = value.as_value_list();
return list.value_at(0, false)->as_length().length(); return list.value_at(0, false)->as_length().length();
} }
@ -496,7 +501,7 @@ CSS::Length ComputedProperties::border_spacing_vertical(Layout::Node const& layo
if (value.is_length()) if (value.is_length())
return value.as_length().length(); return value.as_length().length();
if (value.is_calculated()) if (value.is_calculated())
return value.as_calculated().resolve_length(layout_node).value_or(CSS::Length(0, CSS::Length::Type::Px)); return value.as_calculated().resolve_length({ .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) }).value_or(CSS::Length(0, CSS::Length::Type::Px));
auto const& list = value.as_value_list(); auto const& list = value.as_value_list();
return list.value_at(1, false)->as_length().length(); return list.value_at(1, false)->as_length().length();
} }
@ -1038,7 +1043,7 @@ Vector<ShadowData> ComputedProperties::shadow(PropertyID property_id, Layout::No
if (value->is_length()) if (value->is_length())
return value->as_length().length(); return value->as_length().length();
if (value->is_calculated()) if (value->is_calculated())
return value->as_calculated().resolve_length(layout_node); return value->as_calculated().resolve_length({ .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) });
return {}; return {};
}; };
@ -1503,7 +1508,7 @@ Vector<CounterData> ComputedProperties::counter_data(PropertyID property_id) con
if (counter.value->is_integer()) { if (counter.value->is_integer()) {
data.value = AK::clamp_to<i32>(counter.value->as_integer().integer()); data.value = AK::clamp_to<i32>(counter.value->as_integer().integer());
} else if (counter.value->is_calculated()) { } else if (counter.value->is_calculated()) {
auto maybe_int = counter.value->as_calculated().resolve_integer(); auto maybe_int = counter.value->as_calculated().resolve_integer({});
if (maybe_int.has_value()) if (maybe_int.has_value())
data.value = AK::clamp_to<i32>(*maybe_int); data.value = AK::clamp_to<i32>(*maybe_int);
} else { } else {

View file

@ -63,9 +63,14 @@ Optional<Frequency::Type> Frequency::unit_from_name(StringView name)
return {}; return {};
} }
Frequency Frequency::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const&, Frequency const& reference_value) Frequency Frequency::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node, Frequency const& reference_value)
{ {
return calculated->resolve_frequency_percentage(reference_value).value(); return calculated->resolve_frequency(
{
.percentage_basis = reference_value,
.length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node),
})
.value();
} }
} }

View file

@ -411,12 +411,22 @@ Length Length::absolutized(CSSPixelRect const& viewport_rect, FontMetrics const&
Length Length::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node, Length const& reference_value) Length Length::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node, Length const& reference_value)
{ {
return calculated->resolve_length_percentage(layout_node, reference_value).value(); return calculated->resolve_length(
{
.percentage_basis = reference_value,
.length_resolution_context = ResolutionContext::for_layout_node(layout_node),
})
.value();
} }
Length Length::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node, CSSPixels reference_value) Length Length::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node, CSSPixels reference_value)
{ {
return calculated->resolve_length_percentage(layout_node, reference_value).value(); return calculated->resolve_length(
{
.percentage_basis = make_px(reference_value),
.length_resolution_context = ResolutionContext::for_layout_node(layout_node),
})
.value();
} }
} }

View file

@ -2174,6 +2174,10 @@ Optional<Ratio> Parser::parse_ratio(TokenStream<ComponentValue>& tokens)
auto transaction = tokens.begin_transaction(); auto transaction = tokens.begin_transaction();
tokens.discard_whitespace(); tokens.discard_whitespace();
// FIXME: It seems like `calc(...) / calc(...)` is a valid <ratio>, but this case is neither mentioned in a spec,
// nor tested in WPT, as far as I can tell.
// Still, we should probably support it. That means not assuming we can resolve the calculation immediately.
auto read_number_value = [this](ComponentValue const& component_value) -> Optional<double> { auto read_number_value = [this](ComponentValue const& component_value) -> Optional<double> {
if (component_value.is(Token::Type::Number)) { if (component_value.is(Token::Type::Number)) {
return component_value.token().number_value(); return component_value.token().number_value();
@ -2181,7 +2185,7 @@ Optional<Ratio> Parser::parse_ratio(TokenStream<ComponentValue>& tokens)
auto maybe_calc = parse_calculated_value(component_value); auto maybe_calc = parse_calculated_value(component_value);
if (!maybe_calc || !maybe_calc->resolves_to_number()) if (!maybe_calc || !maybe_calc->resolves_to_number())
return {}; return {};
if (auto resolved_number = maybe_calc->resolve_number(); resolved_number.has_value() && resolved_number.value() >= 0) { if (auto resolved_number = maybe_calc->resolve_number({}); resolved_number.has_value() && resolved_number.value() >= 0) {
return resolved_number.value(); return resolved_number.value();
} }
} }
@ -8129,7 +8133,7 @@ RefPtr<GridTrackPlacementStyleValue> Parser::parse_grid_track_placement(TokenStr
auto const& token = tokens.consume_a_token(); auto const& token = tokens.consume_a_token();
if (auto maybe_calculated = parse_calculated_value(token); maybe_calculated && maybe_calculated->resolves_to_number()) { if (auto maybe_calculated = parse_calculated_value(token); maybe_calculated && maybe_calculated->resolves_to_number()) {
transaction.commit(); transaction.commit();
return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_line(static_cast<int>(maybe_calculated->resolve_integer().value()), {})); return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_line(static_cast<int>(maybe_calculated->resolve_integer({}).value()), {}));
} }
if (token.is_ident("auto"sv)) { if (token.is_ident("auto"sv)) {
transaction.commit(); transaction.commit();
@ -9710,17 +9714,19 @@ bool Parser::expand_unresolved_values(DOM::Element& element, FlyString const& pr
} }
if (property.has_value()) { if (property.has_value()) {
// FIXME: I think we don't need any of this once simplification is implemented. It runs inside parse_calculation_node() already.
// So, this is just a temporary hack to not change behaviour until that's done.
if (auto maybe_calc_value = parse_calculated_value(value); maybe_calc_value && maybe_calc_value->is_calculated()) { if (auto maybe_calc_value = parse_calculated_value(value); maybe_calc_value && maybe_calc_value->is_calculated()) {
// FIXME: Run the actual simplification algorithm
auto& calc_value = maybe_calc_value->as_calculated(); auto& calc_value = maybe_calc_value->as_calculated();
CalculationResolutionContext context {};
if (property_accepts_type(*property, ValueType::Angle) && calc_value.resolves_to_angle()) { if (property_accepts_type(*property, ValueType::Angle) && calc_value.resolves_to_angle()) {
auto resolved_value = calc_value.resolve_angle(); if (auto resolved_value = calc_value.resolve_angle(context); resolved_value.has_value())
dest.empend(Token::create_dimension(resolved_value->to_degrees(), "deg"_fly_string)); dest.empend(Token::create_dimension(resolved_value->to_degrees(), "deg"_fly_string));
continue; continue;
} }
if (property_accepts_type(*property, ValueType::Frequency) && calc_value.resolves_to_frequency()) { if (property_accepts_type(*property, ValueType::Frequency) && calc_value.resolves_to_frequency()) {
auto resolved_value = calc_value.resolve_frequency(); if (auto resolved_value = calc_value.resolve_frequency(context); resolved_value.has_value())
dest.empend(Token::create_dimension(resolved_value->to_hertz(), "hz"_fly_string)); dest.empend(Token::create_dimension(resolved_value->to_hertz(), "hz"_fly_string));
continue; continue;
} }
if (property_accepts_type(*property, ValueType::Length) && calc_value.resolves_to_length()) { if (property_accepts_type(*property, ValueType::Length) && calc_value.resolves_to_length()) {
@ -9729,23 +9735,23 @@ bool Parser::expand_unresolved_values(DOM::Element& element, FlyString const& pr
// This might be easier once we have calc-simplification implemented. // This might be easier once we have calc-simplification implemented.
} }
if (property_accepts_type(*property, ValueType::Percentage) && calc_value.resolves_to_percentage()) { if (property_accepts_type(*property, ValueType::Percentage) && calc_value.resolves_to_percentage()) {
auto resolved_value = calc_value.resolve_percentage(); if (auto resolved_value = calc_value.resolve_percentage(context); resolved_value.has_value())
dest.empend(Token::create_percentage(resolved_value.value().value())); dest.empend(Token::create_percentage(resolved_value.value().value()));
continue; continue;
} }
if (property_accepts_type(*property, ValueType::Time) && calc_value.resolves_to_time()) { if (property_accepts_type(*property, ValueType::Time) && calc_value.resolves_to_time()) {
auto resolved_value = calc_value.resolve_time(); if (auto resolved_value = calc_value.resolve_time(context); resolved_value.has_value())
dest.empend(Token::create_dimension(resolved_value->to_seconds(), "s"_fly_string)); dest.empend(Token::create_dimension(resolved_value->to_seconds(), "s"_fly_string));
continue; continue;
} }
if (property_accepts_type(*property, ValueType::Number) && calc_value.resolves_to_number()) { if (property_accepts_type(*property, ValueType::Number) && calc_value.resolves_to_number()) {
auto resolved_value = calc_value.resolve_number(); if (auto resolved_value = calc_value.resolve_number(context); resolved_value.has_value())
dest.empend(Token::create_number(resolved_value.value(), Number::Type::Number)); dest.empend(Token::create_number(resolved_value.value(), Number::Type::Number));
continue; continue;
} }
if (property_accepts_type(*property, ValueType::Integer) && calc_value.resolves_to_number()) { if (property_accepts_type(*property, ValueType::Integer) && calc_value.resolves_to_number()) {
auto resolved_value = calc_value.resolve_integer(); if (auto resolved_value = calc_value.resolve_integer(context); resolved_value.has_value())
dest.empend(Token::create_number(resolved_value.value(), Number::Type::Integer)); dest.empend(Token::create_number(resolved_value.value(), Number::Type::Integer));
continue; continue;
} }
} }

View file

@ -621,7 +621,7 @@ GC::Ptr<CSSFontFaceRule> Parser::convert_to_font_face_rule(AtRule const& rule)
// TODO: Once we implement calc-simplification in the parser, we should no longer see math values here, // TODO: Once we implement calc-simplification in the parser, we should no longer see math values here,
// unless they're impossible to resolve and thus invalid. // unless they're impossible to resolve and thus invalid.
if (percentage_value->is_calculated()) { if (percentage_value->is_calculated()) {
if (auto result = percentage_value->as_calculated().resolve_percentage(); result.has_value()) if (auto result = percentage_value->as_calculated().resolve_percentage({}); result.has_value())
return result.value(); return result.value();
} }
@ -736,7 +736,7 @@ GC::Ptr<CSSFontFaceRule> Parser::convert_to_font_face_rule(AtRule const& rule)
if (setting_value->is_integer()) { if (setting_value->is_integer()) {
settings.set(feature_tag->as_open_type_tagged().tag(), setting_value->as_integer().integer()); settings.set(feature_tag->as_open_type_tagged().tag(), setting_value->as_integer().integer());
} else if (setting_value->is_calculated() && setting_value->as_calculated().resolves_to_number()) { } else if (setting_value->is_calculated() && setting_value->as_calculated().resolves_to_number()) {
if (auto integer = setting_value->as_calculated().resolve_integer(); integer.has_value()) { if (auto integer = setting_value->as_calculated().resolve_integer({}); integer.has_value()) {
settings.set(feature_tag->as_open_type_tagged().tag(), *integer); settings.set(feature_tag->as_open_type_tagged().tag(), *integer);
} else { } else {
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Calculated value in font-feature-settings descriptor cannot be resolved at parse time; skipping"); dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Calculated value in font-feature-settings descriptor cannot be resolved at parse time; skipping");
@ -810,7 +810,7 @@ GC::Ptr<CSSFontFaceRule> Parser::convert_to_font_face_rule(AtRule const& rule)
if (setting_value->is_number()) { if (setting_value->is_number()) {
settings.set(variation_tag->as_open_type_tagged().tag(), setting_value->as_number().number()); settings.set(variation_tag->as_open_type_tagged().tag(), setting_value->as_number().number());
} else if (setting_value->is_calculated() && setting_value->as_calculated().resolves_to_number()) { } else if (setting_value->is_calculated() && setting_value->as_calculated().resolves_to_number()) {
if (auto number = setting_value->as_calculated().resolve_number(); number.has_value()) { if (auto number = setting_value->as_calculated().resolve_number({}); number.has_value()) {
settings.set(variation_tag->as_open_type_tagged().tag(), *number); settings.set(variation_tag->as_open_type_tagged().tag(), *number);
} else { } else {
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Calculated value in font-variation-settings descriptor cannot be resolved at parse time; skipping"); dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Calculated value in font-variation-settings descriptor cannot be resolved at parse time; skipping");

View file

@ -1963,11 +1963,10 @@ RefPtr<Gfx::FontCascadeList const> StyleComputer::compute_font_for_style_values(
} else if (font_size.is_length()) { } else if (font_size.is_length()) {
maybe_length = font_size.as_length().length(); maybe_length = font_size.as_length().length();
} else if (font_size.is_calculated()) { } else if (font_size.is_calculated()) {
if (font_size.as_calculated().contains_percentage()) { maybe_length = font_size.as_calculated().resolve_length({
maybe_length = font_size.as_calculated().resolve_length_percentage(length_resolution_context, Length::make_px(parent_font_size())); .percentage_basis = Length::make_px(parent_font_size()),
} else { .length_resolution_context = length_resolution_context,
maybe_length = font_size.as_calculated().resolve_length(length_resolution_context); });
}
} }
if (maybe_length.has_value()) { if (maybe_length.has_value()) {
font_size_in_px = maybe_length.value().to_px(length_resolution_context); font_size_in_px = maybe_length.value().to_px(length_resolution_context);
@ -3034,7 +3033,7 @@ void StyleComputer::compute_math_depth(ComputedProperties& style, DOM::Element c
if (integer_value.is_integer()) if (integer_value.is_integer())
return integer_value.as_integer().integer(); return integer_value.as_integer().integer();
if (integer_value.is_calculated()) if (integer_value.is_calculated())
return integer_value.as_calculated().resolve_integer().value(); return integer_value.as_calculated().resolve_integer({}).value();
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
}; };

View file

@ -42,7 +42,7 @@ Optional<double> CSSColorValue::resolve_hue(CSSStyleValue const& style_value)
return normalized(style_value.as_angle().angle().to_degrees()); return normalized(style_value.as_angle().angle().to_degrees());
if (style_value.is_calculated() && style_value.as_calculated().resolves_to_angle()) if (style_value.is_calculated() && style_value.as_calculated().resolves_to_angle())
return normalized(style_value.as_calculated().resolve_angle().value().to_degrees()); return normalized(style_value.as_calculated().resolve_angle({}).value().to_degrees());
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None) if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)
return 0; return 0;
@ -65,10 +65,11 @@ Optional<double> CSSColorValue::resolve_with_reference_value(CSSStyleValue const
if (style_value.is_calculated()) { if (style_value.is_calculated()) {
auto const& calculated = style_value.as_calculated(); auto const& calculated = style_value.as_calculated();
CalculationResolutionContext context {};
if (calculated.resolves_to_number()) if (calculated.resolves_to_number())
return calculated.resolve_number().value(); return calculated.resolve_number(context).value();
if (calculated.resolves_to_percentage()) if (calculated.resolves_to_percentage())
return normalize_percentage(calculated.resolve_percentage().value()); return normalize_percentage(calculated.resolve_percentage(context).value());
} }
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None) if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)
@ -94,10 +95,11 @@ Optional<double> CSSColorValue::resolve_alpha(CSSStyleValue const& style_value)
if (style_value.is_calculated()) { if (style_value.is_calculated()) {
auto const& calculated = style_value.as_calculated(); auto const& calculated = style_value.as_calculated();
CalculationResolutionContext context {};
if (calculated.resolves_to_number()) if (calculated.resolves_to_number())
return normalized(calculated.resolve_number().value()); return normalized(calculated.resolve_number(context).value());
if (calculated.resolves_to_percentage()) if (calculated.resolves_to_percentage())
return normalized(calculated.resolve_percentage().value().as_fraction()); return normalized(calculated.resolve_percentage(context).value().as_fraction());
} }
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None) if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)

View file

@ -32,10 +32,11 @@ Color CSSRGB::to_color(Optional<Layout::NodeWithStyle const&>) const
if (style_value.is_calculated()) { if (style_value.is_calculated()) {
auto const& calculated = style_value.as_calculated(); auto const& calculated = style_value.as_calculated();
CalculationResolutionContext context {};
if (calculated.resolves_to_number()) if (calculated.resolves_to_number())
return normalized(calculated.resolve_number().value()); return normalized(calculated.resolve_number(context).value());
if (calculated.resolves_to_percentage()) if (calculated.resolves_to_percentage())
return normalized(calculated.resolve_percentage().value().value() * 255 / 100); return normalized(calculated.resolve_percentage(context).value().value() * 255 / 100);
} }
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None) if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)

View file

@ -1,7 +1,7 @@
/* /*
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org> * Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org> * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
* Copyright (c) 2021-2024, Sam Atkins <sam@ladybird.org> * Copyright (c) 2021-2025, Sam Atkins <sam@ladybird.org>
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech> * Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
@ -198,23 +198,23 @@ bool NumericCalculationNode::contains_percentage() const
return m_value.has<Percentage>(); return m_value.has<Percentage>();
} }
CalculatedStyleValue::CalculationResult NumericCalculationNode::resolve(Optional<Length::ResolutionContext const&> resolution_context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult NumericCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
if (m_value.has<Percentage>()) { if (m_value.has<Percentage>()) {
// NOTE: Depending on whether percentage_basis is set, the caller of resolve() is expecting a raw percentage or // NOTE: Depending on whether percentage_basis is set, the caller of resolve() is expecting a raw percentage or
// resolved type. // resolved type.
return percentage_basis.visit( return context.percentage_basis.visit(
[&](Empty const&) { [&](Empty const&) {
VERIFY(numeric_type_from_calculated_style_value(m_value, {}) == numeric_type()); VERIFY(numeric_type_from_calculated_style_value(m_value, {}) == numeric_type());
return CalculatedStyleValue::CalculationResult::from_value(m_value, resolution_context, numeric_type()); return CalculatedStyleValue::CalculationResult::from_value(m_value, context, numeric_type());
}, },
[&](auto const& value) { [&](auto const& value) {
auto const calculated_value = value.percentage_of(m_value.get<Percentage>()); 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, {})); return CalculatedStyleValue::CalculationResult::from_value(calculated_value, context, numeric_type_from_calculated_style_value(calculated_value, {}));
}); });
} }
return CalculatedStyleValue::CalculationResult::from_value(m_value, resolution_context, numeric_type()); return CalculatedStyleValue::CalculationResult::from_value(m_value, context, numeric_type());
} }
void NumericCalculationNode::dump(StringBuilder& builder, int indent) const void NumericCalculationNode::dump(StringBuilder& builder, int indent) const
@ -272,17 +272,17 @@ bool SumCalculationNode::contains_percentage() const
return false; return false;
} }
CalculatedStyleValue::CalculationResult SumCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult SumCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
Optional<CalculatedStyleValue::CalculationResult> total; Optional<CalculatedStyleValue::CalculationResult> total;
for (auto& additional_product : m_values) { for (auto& additional_product : m_values) {
auto additional_value = additional_product->resolve(context, percentage_basis); auto additional_value = additional_product->resolve(context);
if (!total.has_value()) { if (!total.has_value()) {
total = additional_value; total = additional_value;
continue; continue;
} }
total->add(additional_value, context, percentage_basis); total->add(additional_value);
} }
return total.value(); return total.value();
@ -350,17 +350,17 @@ bool ProductCalculationNode::contains_percentage() const
return false; return false;
} }
CalculatedStyleValue::CalculationResult ProductCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult ProductCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
Optional<CalculatedStyleValue::CalculationResult> total; Optional<CalculatedStyleValue::CalculationResult> total;
for (auto& additional_product : m_values) { for (auto& additional_product : m_values) {
auto additional_value = additional_product->resolve(context, percentage_basis); auto additional_value = additional_product->resolve(context);
if (!total.has_value()) { if (!total.has_value()) {
total = additional_value; total = additional_value;
continue; continue;
} }
total->multiply_by(additional_value, context); total->multiply_by(additional_value);
} }
return total.value(); return total.value();
@ -412,9 +412,9 @@ bool NegateCalculationNode::contains_percentage() const
return m_value->contains_percentage(); return m_value->contains_percentage();
} }
CalculatedStyleValue::CalculationResult NegateCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult NegateCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto child_value = m_value->resolve(context, percentage_basis); auto child_value = m_value->resolve(context);
child_value.negate(); child_value.negate();
return child_value; return child_value;
} }
@ -464,9 +464,9 @@ bool InvertCalculationNode::contains_percentage() const
return m_value->contains_percentage(); return m_value->contains_percentage();
} }
CalculatedStyleValue::CalculationResult InvertCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult InvertCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto child_value = m_value->resolve(context, percentage_basis); auto child_value = m_value->resolve(context);
child_value.invert(); child_value.invert();
return child_value; return child_value;
} }
@ -525,13 +525,13 @@ bool MinCalculationNode::contains_percentage() const
return false; return false;
} }
CalculatedStyleValue::CalculationResult MinCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult MinCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
CalculatedStyleValue::CalculationResult smallest_node = m_values.first()->resolve(context, percentage_basis); CalculatedStyleValue::CalculationResult smallest_node = m_values.first()->resolve(context);
auto smallest_value = smallest_node.value(); auto smallest_value = smallest_node.value();
for (size_t i = 1; i < m_values.size(); i++) { for (size_t i = 1; i < m_values.size(); i++) {
auto child_resolved = m_values[i]->resolve(context, percentage_basis); auto child_resolved = m_values[i]->resolve(context);
auto child_value = child_resolved.value(); auto child_value = child_resolved.value();
if (child_value < smallest_value) { if (child_value < smallest_value) {
@ -604,13 +604,13 @@ bool MaxCalculationNode::contains_percentage() const
return false; return false;
} }
CalculatedStyleValue::CalculationResult MaxCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult MaxCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
CalculatedStyleValue::CalculationResult largest_node = m_values.first()->resolve(context, percentage_basis); CalculatedStyleValue::CalculationResult largest_node = m_values.first()->resolve(context);
auto largest_value = largest_node.value(); auto largest_value = largest_node.value();
for (size_t i = 1; i < m_values.size(); i++) { for (size_t i = 1; i < m_values.size(); i++) {
auto child_resolved = m_values[i]->resolve(context, percentage_basis); auto child_resolved = m_values[i]->resolve(context);
auto child_value = child_resolved.value(); auto child_value = child_resolved.value();
if (child_value > largest_value) { if (child_value > largest_value) {
@ -680,11 +680,11 @@ bool ClampCalculationNode::contains_percentage() const
return m_min_value->contains_percentage() || m_center_value->contains_percentage() || m_max_value->contains_percentage(); return m_min_value->contains_percentage() || m_center_value->contains_percentage() || m_max_value->contains_percentage();
} }
CalculatedStyleValue::CalculationResult ClampCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult ClampCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto min_node = m_min_value->resolve(context, percentage_basis); auto min_node = m_min_value->resolve(context);
auto center_node = m_center_value->resolve(context, percentage_basis); auto center_node = m_center_value->resolve(context);
auto max_node = m_max_value->resolve(context, percentage_basis); auto max_node = m_max_value->resolve(context);
auto min_value = min_node.value(); auto min_value = min_node.value();
auto center_value = center_node.value(); auto center_value = center_node.value();
@ -750,9 +750,9 @@ bool AbsCalculationNode::contains_percentage() const
return m_value->contains_percentage(); return m_value->contains_percentage();
} }
CalculatedStyleValue::CalculationResult AbsCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult AbsCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_value->resolve(context, percentage_basis); auto node_a = m_value->resolve(context);
if (node_a.value() < 0) if (node_a.value() < 0)
node_a.negate(); node_a.negate();
return node_a; return node_a;
@ -801,9 +801,9 @@ bool SignCalculationNode::contains_percentage() const
return m_value->contains_percentage(); return m_value->contains_percentage();
} }
CalculatedStyleValue::CalculationResult SignCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult SignCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_value->resolve(context, percentage_basis); auto node_a = m_value->resolve(context);
auto node_a_value = node_a.value(); auto node_a_value = node_a.value();
if (node_a_value < 0) if (node_a_value < 0)
@ -864,7 +864,7 @@ String ConstantCalculationNode::to_string() const
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
CalculatedStyleValue::CalculationResult ConstantCalculationNode::resolve([[maybe_unused]] Optional<Length::ResolutionContext const&> context, [[maybe_unused]] CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult ConstantCalculationNode::resolve(CalculationResolutionContext const&) const
{ {
switch (m_constant) { switch (m_constant) {
case ConstantType::E: case ConstantType::E:
@ -925,9 +925,9 @@ bool SinCalculationNode::contains_percentage() const
return m_value->contains_percentage(); return m_value->contains_percentage();
} }
CalculatedStyleValue::CalculationResult SinCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult SinCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_value->resolve(context, percentage_basis); auto node_a = m_value->resolve(context);
auto node_a_value = AK::to_radians(node_a.value()); auto node_a_value = AK::to_radians(node_a.value());
auto result = sin(node_a_value); auto result = sin(node_a_value);
@ -977,9 +977,9 @@ bool CosCalculationNode::contains_percentage() const
return m_value->contains_percentage(); return m_value->contains_percentage();
} }
CalculatedStyleValue::CalculationResult CosCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult CosCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_value->resolve(context, percentage_basis); auto node_a = m_value->resolve(context);
auto node_a_value = AK::to_radians(node_a.value()); auto node_a_value = AK::to_radians(node_a.value());
auto result = cos(node_a_value); auto result = cos(node_a_value);
@ -1029,9 +1029,9 @@ bool TanCalculationNode::contains_percentage() const
return m_value->contains_percentage(); return m_value->contains_percentage();
} }
CalculatedStyleValue::CalculationResult TanCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult TanCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_value->resolve(context, percentage_basis); auto node_a = m_value->resolve(context);
auto node_a_value = AK::to_radians(node_a.value()); auto node_a_value = AK::to_radians(node_a.value());
auto result = tan(node_a_value); auto result = tan(node_a_value);
@ -1081,9 +1081,9 @@ bool AsinCalculationNode::contains_percentage() const
return m_value->contains_percentage(); return m_value->contains_percentage();
} }
CalculatedStyleValue::CalculationResult AsinCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult AsinCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_value->resolve(context, percentage_basis); auto node_a = m_value->resolve(context);
auto result = AK::to_degrees(asin(node_a.value())); auto result = AK::to_degrees(asin(node_a.value()));
return { result, CSSNumericType { CSSNumericType::BaseType::Angle, 1 } }; return { result, CSSNumericType { CSSNumericType::BaseType::Angle, 1 } };
} }
@ -1131,9 +1131,9 @@ bool AcosCalculationNode::contains_percentage() const
return m_value->contains_percentage(); return m_value->contains_percentage();
} }
CalculatedStyleValue::CalculationResult AcosCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult AcosCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_value->resolve(context, percentage_basis); auto node_a = m_value->resolve(context);
auto result = AK::to_degrees(acos(node_a.value())); auto result = AK::to_degrees(acos(node_a.value()));
return { result, CSSNumericType { CSSNumericType::BaseType::Angle, 1 } }; return { result, CSSNumericType { CSSNumericType::BaseType::Angle, 1 } };
} }
@ -1181,9 +1181,9 @@ bool AtanCalculationNode::contains_percentage() const
return m_value->contains_percentage(); return m_value->contains_percentage();
} }
CalculatedStyleValue::CalculationResult AtanCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult AtanCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_value->resolve(context, percentage_basis); auto node_a = m_value->resolve(context);
auto result = AK::to_degrees(atan(node_a.value())); auto result = AK::to_degrees(atan(node_a.value()));
return { result, CSSNumericType { CSSNumericType::BaseType::Angle, 1 } }; return { result, CSSNumericType { CSSNumericType::BaseType::Angle, 1 } };
} }
@ -1234,10 +1234,10 @@ bool Atan2CalculationNode::contains_percentage() const
return m_y->contains_percentage() || m_x->contains_percentage(); return m_y->contains_percentage() || m_x->contains_percentage();
} }
CalculatedStyleValue::CalculationResult Atan2CalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult Atan2CalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_y->resolve(context, percentage_basis); auto node_a = m_y->resolve(context);
auto node_b = m_x->resolve(context, percentage_basis); auto node_b = m_x->resolve(context);
auto result = AK::to_degrees(atan2(node_a.value(), node_b.value())); auto result = AK::to_degrees(atan2(node_a.value(), node_b.value()));
return { result, CSSNumericType { CSSNumericType::BaseType::Angle, 1 } }; return { result, CSSNumericType { CSSNumericType::BaseType::Angle, 1 } };
} }
@ -1284,10 +1284,10 @@ String PowCalculationNode::to_string() const
return MUST(builder.to_string()); return MUST(builder.to_string());
} }
CalculatedStyleValue::CalculationResult PowCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult PowCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_x->resolve(context, percentage_basis); auto node_a = m_x->resolve(context);
auto node_b = m_y->resolve(context, percentage_basis); auto node_b = m_y->resolve(context);
auto result = pow(node_a.value(), node_b.value()); auto result = pow(node_a.value(), node_b.value());
return { result, CSSNumericType {} }; return { result, CSSNumericType {} };
} }
@ -1331,9 +1331,9 @@ String SqrtCalculationNode::to_string() const
return MUST(builder.to_string()); return MUST(builder.to_string());
} }
CalculatedStyleValue::CalculationResult SqrtCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult SqrtCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_value->resolve(context, percentage_basis); auto node_a = m_value->resolve(context);
auto result = sqrt(node_a.value()); auto result = sqrt(node_a.value());
return { result, CSSNumericType {} }; return { result, CSSNumericType {} };
} }
@ -1391,13 +1391,13 @@ bool HypotCalculationNode::contains_percentage() const
return false; return false;
} }
CalculatedStyleValue::CalculationResult HypotCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult HypotCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
double square_sum = 0.0; double square_sum = 0.0;
Optional<CSSNumericType> result_type; Optional<CSSNumericType> result_type;
for (auto const& value : m_values) { for (auto const& value : m_values) {
auto child_resolved = value->resolve(context, percentage_basis); auto child_resolved = value->resolve(context);
auto child_value = child_resolved.value(); auto child_value = child_resolved.value();
square_sum += child_value * child_value; square_sum += child_value * child_value;
@ -1459,10 +1459,10 @@ String LogCalculationNode::to_string() const
return MUST(builder.to_string()); return MUST(builder.to_string());
} }
CalculatedStyleValue::CalculationResult LogCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult LogCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_x->resolve(context, percentage_basis); auto node_a = m_x->resolve(context);
auto node_b = m_y->resolve(context, percentage_basis); auto node_b = m_y->resolve(context);
auto result = log2(node_a.value()) / log2(node_b.value()); auto result = log2(node_a.value()) / log2(node_b.value());
return { result, CSSNumericType {} }; return { result, CSSNumericType {} };
} }
@ -1506,9 +1506,9 @@ String ExpCalculationNode::to_string() const
return MUST(builder.to_string()); return MUST(builder.to_string());
} }
CalculatedStyleValue::CalculationResult ExpCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult ExpCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_value->resolve(context, percentage_basis); auto node_a = m_value->resolve(context);
auto result = exp(node_a.value()); auto result = exp(node_a.value());
return { result, CSSNumericType {} }; return { result, CSSNumericType {} };
} }
@ -1563,10 +1563,10 @@ bool RoundCalculationNode::contains_percentage() const
return m_x->contains_percentage() || m_y->contains_percentage(); return m_x->contains_percentage() || m_y->contains_percentage();
} }
CalculatedStyleValue::CalculationResult RoundCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult RoundCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_x->resolve(context, percentage_basis); auto node_a = m_x->resolve(context);
auto node_b = m_y->resolve(context, percentage_basis); auto node_b = m_y->resolve(context);
auto node_a_value = node_a.value(); auto node_a_value = node_a.value();
auto node_b_value = node_b.value(); auto node_b_value = node_b.value();
@ -1650,10 +1650,10 @@ bool ModCalculationNode::contains_percentage() const
return m_x->contains_percentage() || m_y->contains_percentage(); return m_x->contains_percentage() || m_y->contains_percentage();
} }
CalculatedStyleValue::CalculationResult ModCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult ModCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_x->resolve(context, percentage_basis); auto node_a = m_x->resolve(context);
auto node_b = m_y->resolve(context, percentage_basis); auto node_b = m_y->resolve(context);
auto node_a_value = node_a.value(); auto node_a_value = node_a.value();
auto node_b_value = node_b.value(); auto node_b_value = node_b.value();
@ -1711,10 +1711,10 @@ bool RemCalculationNode::contains_percentage() const
return m_x->contains_percentage() || m_y->contains_percentage(); return m_x->contains_percentage() || m_y->contains_percentage();
} }
CalculatedStyleValue::CalculationResult RemCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const& percentage_basis) const CalculatedStyleValue::CalculationResult RemCalculationNode::resolve(CalculationResolutionContext const& context) const
{ {
auto node_a = m_x->resolve(context, percentage_basis); auto node_a = m_x->resolve(context);
auto node_b = m_y->resolve(context, percentage_basis); auto node_b = m_y->resolve(context);
auto value = fmod(node_a.value(), node_b.value()); auto value = fmod(node_a.value(), node_b.value());
return { value, node_a.type() }; return { value, node_a.type() };
} }
@ -1734,7 +1734,7 @@ bool RemCalculationNode::equals(CalculationNode const& other) const
&& m_y->equals(*static_cast<RemCalculationNode const&>(other).m_y); && m_y->equals(*static_cast<RemCalculationNode const&>(other).m_y);
} }
CalculatedStyleValue::CalculationResult CalculatedStyleValue::CalculationResult::from_value(Value const& value, Optional<Length::ResolutionContext const&> context, Optional<CSSNumericType> numeric_type) CalculatedStyleValue::CalculationResult CalculatedStyleValue::CalculationResult::from_value(Value const& value, CalculationResolutionContext const& context, Optional<CSSNumericType> numeric_type)
{ {
auto const expected_numeric_type = numeric_type_from_calculated_style_value(value, {}); auto const expected_numeric_type = numeric_type_from_calculated_style_value(value, {});
if (numeric_type.has_value()) { if (numeric_type.has_value()) {
@ -1755,12 +1755,12 @@ CalculatedStyleValue::CalculationResult CalculatedStyleValue::CalculationResult:
return length.absolute_length_to_px().to_double(); return length.absolute_length_to_px().to_double();
// If we don't have a context, we cant resolve the length, so return NAN // If we don't have a context, we cant resolve the length, so return NAN
if (!context.has_value()) { if (!context.length_resolution_context.has_value()) {
dbgln("Failed to resolve length, likely due to calc() being used with relative units and a property not taking it into account"); dbgln("Failed to resolve length, likely due to calc() being used with relative units and a property not taking it into account");
return AK::NaN<double>; return AK::NaN<double>;
} }
return length.to_px(*context).to_double(); return length.to_px(context.length_resolution_context.value()).to_double();
}, },
[](Resolution const& resolution) { return resolution.to_dots_per_pixel(); }, [](Resolution const& resolution) { return resolution.to_dots_per_pixel(); },
[](Time const& time) { return time.to_seconds(); }, [](Time const& time) { return time.to_seconds(); },
@ -1769,25 +1769,25 @@ CalculatedStyleValue::CalculationResult CalculatedStyleValue::CalculationResult:
return CalculationResult { number, numeric_type }; return CalculationResult { number, numeric_type };
} }
void CalculatedStyleValue::CalculationResult::add(CalculationResult const& other, Optional<Length::ResolutionContext const&>, PercentageBasis const&) void CalculatedStyleValue::CalculationResult::add(CalculationResult const& other)
{ {
m_value = m_value + other.m_value; m_value = m_value + other.m_value;
m_type = m_type.has_value() && other.m_type.has_value() ? m_type->added_to(*other.m_type) : OptionalNone {}; m_type = m_type.has_value() && other.m_type.has_value() ? m_type->added_to(*other.m_type) : OptionalNone {};
} }
void CalculatedStyleValue::CalculationResult::subtract(CalculationResult const& other, Optional<Length::ResolutionContext const&>, PercentageBasis const&) void CalculatedStyleValue::CalculationResult::subtract(CalculationResult const& other)
{ {
m_value = m_value - other.m_value; m_value = m_value - other.m_value;
m_type = m_type.has_value() && other.m_type.has_value() ? m_type->added_to(*other.m_type) : OptionalNone {}; m_type = m_type.has_value() && other.m_type.has_value() ? m_type->added_to(*other.m_type) : OptionalNone {};
} }
void CalculatedStyleValue::CalculationResult::multiply_by(CalculationResult const& other, Optional<Length::ResolutionContext const&>) void CalculatedStyleValue::CalculationResult::multiply_by(CalculationResult const& other)
{ {
m_value = m_value * other.m_value; m_value = m_value * other.m_value;
m_type = m_type.has_value() && other.m_type.has_value() ? m_type->multiplied_by(*other.m_type) : OptionalNone {}; m_type = m_type.has_value() && other.m_type.has_value() ? m_type->multiplied_by(*other.m_type) : OptionalNone {};
} }
void CalculatedStyleValue::CalculationResult::divide_by(CalculationResult const& other, Optional<Length::ResolutionContext const&>) void CalculatedStyleValue::CalculationResult::divide_by(CalculationResult const& other)
{ {
auto other_copy = other; auto other_copy = other;
other_copy.invert(); other_copy.invert();
@ -1822,164 +1822,78 @@ bool CalculatedStyleValue::equals(CSSStyleValue const& other) const
return m_calculation->equals(*other.as_calculated().m_calculation); return m_calculation->equals(*other.as_calculated().m_calculation);
} }
Optional<Angle> CalculatedStyleValue::resolve_angle() const Optional<Angle> CalculatedStyleValue::resolve_angle(CalculationResolutionContext const& context) const
{ {
auto result = m_calculation->resolve({}, {}); auto result = m_calculation->resolve(context);
if (result.type().has_value() && result.type()->matches_angle(m_context.percentages_resolve_as)) if (result.type().has_value() && result.type()->matches_angle(m_context.percentages_resolve_as))
return Angle::make_degrees(result.value()); return Angle::make_degrees(result.value());
return {}; return {};
} }
Optional<Angle> CalculatedStyleValue::resolve_angle(Layout::Node const& layout_node) const Optional<Flex> CalculatedStyleValue::resolve_flex(CalculationResolutionContext const& context) const
{ {
return resolve_angle(Length::ResolutionContext::for_layout_node(layout_node)); auto result = m_calculation->resolve(context);
}
Optional<Angle> CalculatedStyleValue::resolve_angle(Length::ResolutionContext const& context) const
{
auto result = m_calculation->resolve(context, {});
if (result.type().has_value() && result.type()->matches_angle(m_context.percentages_resolve_as))
return Angle::make_degrees(result.value());
return {};
}
Optional<Angle> CalculatedStyleValue::resolve_angle_percentage(Angle const& percentage_basis) const
{
auto result = m_calculation->resolve({}, percentage_basis);
if (result.type().has_value() && result.type()->matches_angle(m_context.percentages_resolve_as))
return Angle::make_degrees(result.value());
return {};
}
Optional<Flex> CalculatedStyleValue::resolve_flex() const
{
auto result = m_calculation->resolve({}, {});
if (result.type().has_value() && result.type()->matches_flex(m_context.percentages_resolve_as)) if (result.type().has_value() && result.type()->matches_flex(m_context.percentages_resolve_as))
return Flex::make_fr(result.value()); return Flex::make_fr(result.value());
return {}; return {};
} }
Optional<Frequency> CalculatedStyleValue::resolve_frequency() const Optional<Frequency> CalculatedStyleValue::resolve_frequency(CalculationResolutionContext const& context) const
{ {
auto result = m_calculation->resolve({}, {}); auto result = m_calculation->resolve(context);
if (result.type().has_value() && result.type()->matches_frequency(m_context.percentages_resolve_as)) if (result.type().has_value() && result.type()->matches_frequency(m_context.percentages_resolve_as))
return Frequency::make_hertz(result.value()); return Frequency::make_hertz(result.value());
return {}; return {};
} }
Optional<Frequency> CalculatedStyleValue::resolve_frequency_percentage(Frequency const& percentage_basis) const Optional<Length> CalculatedStyleValue::resolve_length(CalculationResolutionContext const& context) const
{ {
auto result = m_calculation->resolve({}, percentage_basis); auto result = m_calculation->resolve(context);
if (result.type().has_value() && result.type()->matches_frequency(m_context.percentages_resolve_as))
return Frequency::make_hertz(result.value());
return {};
}
Optional<Length> CalculatedStyleValue::resolve_length(Length::ResolutionContext const& context) const
{
auto result = m_calculation->resolve(context, {});
if (result.type().has_value() && result.type()->matches_length(m_context.percentages_resolve_as)) if (result.type().has_value() && result.type()->matches_length(m_context.percentages_resolve_as))
return Length::make_px(CSSPixels { result.value() }); return Length::make_px(CSSPixels { result.value() });
return {}; return {};
} }
Optional<Length> CalculatedStyleValue::resolve_length(Layout::Node const& layout_node) const Optional<Percentage> CalculatedStyleValue::resolve_percentage(CalculationResolutionContext const& context) const
{ {
return resolve_length(Length::ResolutionContext::for_layout_node(layout_node)); auto result = m_calculation->resolve(context);
}
Optional<Length> CalculatedStyleValue::resolve_length_percentage(Layout::Node const& layout_node, Length const& percentage_basis) const
{
return resolve_length_percentage(Length::ResolutionContext::for_layout_node(layout_node), percentage_basis);
}
Optional<Length> CalculatedStyleValue::resolve_length_percentage(Layout::Node const& layout_node, CSSPixels percentage_basis) const
{
return resolve_length_percentage(Length::ResolutionContext::for_layout_node(layout_node), Length::make_px(percentage_basis));
}
Optional<Length> CalculatedStyleValue::resolve_length_percentage(Length::ResolutionContext const& resolution_context, Length const& percentage_basis) const
{
auto result = m_calculation->resolve(resolution_context, percentage_basis);
if (result.type().has_value() && result.type()->matches_length(m_context.percentages_resolve_as))
return Length::make_px(CSSPixels { result.value() });
return {};
}
Optional<Percentage> CalculatedStyleValue::resolve_percentage() const
{
auto result = m_calculation->resolve({}, {});
if (result.type().has_value() && result.type()->matches_percentage()) if (result.type().has_value() && result.type()->matches_percentage())
return Percentage { result.value() }; return Percentage { result.value() };
return {}; return {};
} }
Optional<Resolution> CalculatedStyleValue::resolve_resolution() const Optional<Resolution> CalculatedStyleValue::resolve_resolution(CalculationResolutionContext const& context) const
{ {
auto result = m_calculation->resolve({}, {}); auto result = m_calculation->resolve(context);
if (result.type().has_value() && result.type()->matches_resolution(m_context.percentages_resolve_as)) if (result.type().has_value() && result.type()->matches_resolution(m_context.percentages_resolve_as))
return Resolution::make_dots_per_pixel(result.value()); return Resolution::make_dots_per_pixel(result.value());
return {}; return {};
} }
Optional<Time> CalculatedStyleValue::resolve_time() const Optional<Time> CalculatedStyleValue::resolve_time(CalculationResolutionContext const& context) const
{ {
auto result = m_calculation->resolve({}, {}); auto result = m_calculation->resolve(context);
if (result.type().has_value() && result.type()->matches_time(m_context.percentages_resolve_as)) if (result.type().has_value() && result.type()->matches_time(m_context.percentages_resolve_as))
return Time::make_seconds(result.value()); return Time::make_seconds(result.value());
return {}; return {};
} }
Optional<Time> CalculatedStyleValue::resolve_time_percentage(Time const& percentage_basis) const Optional<double> CalculatedStyleValue::resolve_number(CalculationResolutionContext const& context) const
{ {
auto result = m_calculation->resolve({}, percentage_basis); auto result = m_calculation->resolve(context);
if (result.type().has_value() && result.type()->matches_time(m_context.percentages_resolve_as))
return Time::make_seconds(result.value());
return {};
}
Optional<double> CalculatedStyleValue::resolve_number() const
{
auto result = m_calculation->resolve({}, {});
if (result.type().has_value() && result.type()->matches_number(m_context.percentages_resolve_as)) if (result.type().has_value() && result.type()->matches_number(m_context.percentages_resolve_as))
return result.value(); return result.value();
return {}; return {};
} }
Optional<double> CalculatedStyleValue::resolve_number(Length::ResolutionContext const& context) const Optional<i64> CalculatedStyleValue::resolve_integer(CalculationResolutionContext const& context) const
{ {
auto result = m_calculation->resolve(context, {}); auto result = m_calculation->resolve(context);
if (result.type().has_value() && result.type()->matches_number(m_context.percentages_resolve_as))
return result.value();
return {};
}
Optional<double> CalculatedStyleValue::resolve_number(Layout::Node const& layout_node) const
{
return resolve_number(Length::ResolutionContext::for_layout_node(layout_node));
}
Optional<i64> CalculatedStyleValue::resolve_integer() const
{
auto result = m_calculation->resolve({}, {});
if (result.type().has_value() && result.type()->matches_number(m_context.percentages_resolve_as)) if (result.type().has_value() && result.type()->matches_number(m_context.percentages_resolve_as))
return llround(result.value()); return llround(result.value());
return {}; return {};
} }
Optional<i64> CalculatedStyleValue::resolve_integer(Length::ResolutionContext const& context) const
{
auto result = m_calculation->resolve(context, {});
if (result.type().has_value() && result.type()->matches_number(m_context.percentages_resolve_as))
return llround(result.value());
return {};
}
Optional<i64> CalculatedStyleValue::resolve_integer(Layout::Node const& layout_node) const
{
return resolve_integer(Length::ResolutionContext::for_layout_node(layout_node));
}
bool CalculatedStyleValue::contains_percentage() const bool CalculatedStyleValue::contains_percentage() const
{ {
return m_calculation->contains_percentage(); return m_calculation->contains_percentage();

View file

@ -1,7 +1,7 @@
/* /*
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org> * Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org> * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
* Copyright (c) 2021-2024, Sam Atkins <sam@ladybird.org> * Copyright (c) 2021-2025, Sam Atkins <sam@ladybird.org>
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech> * Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
@ -25,18 +25,22 @@ namespace Web::CSS {
class CalculationNode; class CalculationNode;
// https://drafts.csswg.org/css-values-4/#ref-for-calc-calculation%E2%91%A2%E2%91%A7 // https://drafts.csswg.org/css-values-4/#ref-for-calc-calculation%E2%91%A2%E2%91%A7
// Contains the context available at parse-time.
struct CalculationContext { struct CalculationContext {
Optional<ValueType> percentages_resolve_as {}; Optional<ValueType> percentages_resolve_as {};
}; };
// Contains the context for resolving the calculation.
struct CalculationResolutionContext {
Variant<Empty, Angle, Frequency, Length, Time> percentage_basis {};
Optional<Length::ResolutionContext> length_resolution_context;
};
class CalculatedStyleValue : public CSSStyleValue { class CalculatedStyleValue : public CSSStyleValue {
public: public:
using PercentageBasis = Variant<Empty, Angle, Frequency, Length, Time>;
class CalculationResult { class CalculationResult {
public: public:
using Value = Variant<Number, Angle, Flex, Frequency, Length, Percentage, Resolution, Time>; using Value = Variant<Number, Angle, Flex, Frequency, Length, Percentage, Resolution, Time>;
static CalculationResult from_value(Value const&, Optional<Length::ResolutionContext const&>, Optional<CSSNumericType>); static CalculationResult from_value(Value const&, CalculationResolutionContext const&, Optional<CSSNumericType>);
CalculationResult(double value, Optional<CSSNumericType> type) CalculationResult(double value, Optional<CSSNumericType> type)
: m_value(value) : m_value(value)
@ -44,10 +48,10 @@ public:
{ {
} }
void add(CalculationResult const& other, Optional<Length::ResolutionContext const&>, PercentageBasis const& percentage_basis); void add(CalculationResult const& other);
void subtract(CalculationResult const& other, Optional<Length::ResolutionContext const&>, PercentageBasis const& percentage_basis); void subtract(CalculationResult const& other);
void multiply_by(CalculationResult const& other, Optional<Length::ResolutionContext const&>); void multiply_by(CalculationResult const& other);
void divide_by(CalculationResult const& other, Optional<Length::ResolutionContext const&>); void divide_by(CalculationResult const& other);
void negate(); void negate();
void invert(); void invert();
@ -71,45 +75,32 @@ public:
bool resolves_to_angle() const { return m_resolved_type.matches_angle(m_context.percentages_resolve_as); } 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); } bool resolves_to_angle_percentage() const { return m_resolved_type.matches_angle_percentage(m_context.percentages_resolve_as); }
Optional<Angle> resolve_angle() const; Optional<Angle> resolve_angle(CalculationResolutionContext const&) const;
Optional<Angle> resolve_angle(Layout::Node const& layout_node) const;
Optional<Angle> resolve_angle(Length::ResolutionContext const& context) const;
Optional<Angle> resolve_angle_percentage(Angle const& percentage_basis) const;
bool resolves_to_flex() const { return m_resolved_type.matches_flex(m_context.percentages_resolve_as); } bool resolves_to_flex() const { return m_resolved_type.matches_flex(m_context.percentages_resolve_as); }
Optional<Flex> resolve_flex() 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() 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); } bool resolves_to_frequency_percentage() const { return m_resolved_type.matches_frequency_percentage(m_context.percentages_resolve_as); }
Optional<Frequency> resolve_frequency() const; Optional<Frequency> resolve_frequency(CalculationResolutionContext const&) const;
Optional<Frequency> resolve_frequency_percentage(Frequency const& percentage_basis) const;
bool resolves_to_length() const { return m_resolved_type.matches_length(m_context.percentages_resolve_as); } 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); } bool resolves_to_length_percentage() const { return m_resolved_type.matches_length_percentage(m_context.percentages_resolve_as); }
Optional<Length> resolve_length(Length::ResolutionContext const&) const; Optional<Length> resolve_length(CalculationResolutionContext const&) const;
Optional<Length> resolve_length(Layout::Node const& layout_node) const;
Optional<Length> resolve_length_percentage(Layout::Node const&, Length const& percentage_basis) const;
Optional<Length> resolve_length_percentage(Layout::Node const&, CSSPixels percentage_basis) const;
Optional<Length> resolve_length_percentage(Length::ResolutionContext const&, Length const& percentage_basis) const;
bool resolves_to_percentage() const { return m_resolved_type.matches_percentage(); } bool resolves_to_percentage() const { return m_resolved_type.matches_percentage(); }
Optional<Percentage> resolve_percentage() const; Optional<Percentage> resolve_percentage(CalculationResolutionContext const&) const;
bool resolves_to_resolution() const { return m_resolved_type.matches_resolution(m_context.percentages_resolve_as); } bool resolves_to_resolution() const { return m_resolved_type.matches_resolution(m_context.percentages_resolve_as); }
Optional<Resolution> resolve_resolution() 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() 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); } bool resolves_to_time_percentage() const { return m_resolved_type.matches_time_percentage(m_context.percentages_resolve_as); }
Optional<Time> resolve_time() const; Optional<Time> resolve_time(CalculationResolutionContext const&) const;
Optional<Time> resolve_time_percentage(Time const& percentage_basis) const;
bool resolves_to_number() const { return m_resolved_type.matches_number(m_context.percentages_resolve_as); } bool resolves_to_number() const { return m_resolved_type.matches_number(m_context.percentages_resolve_as); }
Optional<double> resolve_number() const; Optional<double> resolve_number(CalculationResolutionContext const&) const;
Optional<double> resolve_number(Length::ResolutionContext const&) const; Optional<i64> resolve_integer(CalculationResolutionContext const&) const;
Optional<double> resolve_number(Layout::Node const& layout_node) const;
Optional<i64> resolve_integer() const;
Optional<i64> resolve_integer(Length::ResolutionContext const&) const;
Optional<i64> resolve_integer(Layout::Node const& layout_node) const;
bool resolves_to_dimension() const { return m_resolved_type.matches_dimension(); } bool resolves_to_dimension() const { return m_resolved_type.matches_dimension(); }
@ -249,7 +240,7 @@ public:
virtual String to_string() const = 0; virtual String to_string() const = 0;
Optional<CSSNumericType> const& numeric_type() const { return m_numeric_type; } Optional<CSSNumericType> const& numeric_type() const { return m_numeric_type; }
virtual bool contains_percentage() const = 0; virtual bool contains_percentage() const = 0;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const = 0; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const = 0;
virtual void dump(StringBuilder&, int indent) const = 0; virtual void dump(StringBuilder&, int indent) const = 0;
virtual bool equals(CalculationNode const&) const = 0; virtual bool equals(CalculationNode const&) const = 0;
@ -269,7 +260,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -286,7 +277,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -303,7 +294,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -320,7 +311,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -337,7 +328,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -354,7 +345,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -371,7 +362,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -388,7 +379,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -407,7 +398,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -424,7 +415,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -441,7 +432,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override { return false; } virtual bool contains_percentage() const override { return false; }
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&> context, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -458,7 +449,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -475,7 +466,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -492,7 +483,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -509,7 +500,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -526,7 +517,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -543,7 +534,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -560,7 +551,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -578,7 +569,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override { return false; } virtual bool contains_percentage() const override { return false; }
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -596,7 +587,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override { return false; } virtual bool contains_percentage() const override { return false; }
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -613,7 +604,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -630,7 +621,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override { return false; } virtual bool contains_percentage() const override { return false; }
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -648,7 +639,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override { return false; } virtual bool contains_percentage() const override { return false; }
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -665,7 +656,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -684,7 +675,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;
@ -702,7 +693,7 @@ public:
virtual String to_string() const override; virtual String to_string() const override;
virtual bool contains_percentage() const override; virtual bool contains_percentage() const override;
virtual CalculatedStyleValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CalculatedStyleValue::PercentageBasis const&) const override; virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override;
virtual void dump(StringBuilder&, int indent) const override; virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override; virtual bool equals(CalculationNode const&) const override;

View file

@ -43,11 +43,12 @@ float FilterOperation::Color::resolved_amount() const
return amount->percentage().as_fraction(); return amount->percentage().as_fraction();
if (amount->is_calculated()) { if (amount->is_calculated()) {
CalculationResolutionContext context {};
if (amount->calculated()->resolves_to_number()) if (amount->calculated()->resolves_to_number())
return amount->calculated()->resolve_number().value(); return amount->calculated()->resolve_number(context).value();
if (amount->calculated()->resolves_to_percentage()) if (amount->calculated()->resolves_to_percentage())
return amount->calculated()->resolve_percentage()->as_fraction(); return amount->calculated()->resolve_percentage(context)->as_fraction();
} }
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();

View file

@ -70,7 +70,7 @@ String TransformationStyleValue::to_string(SerializationMode mode) const
if (value->is_number()) if (value->is_number())
return value->as_number().number(); return value->as_number().number();
if (value->is_calculated() && value->as_calculated().resolves_to_number()) if (value->is_calculated() && value->as_calculated().resolves_to_number())
return value->as_calculated().resolve_number(); return value->as_calculated().resolve_number({});
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
}; };

View file

@ -74,9 +74,14 @@ Optional<Time::Type> Time::unit_from_name(StringView name)
return {}; return {};
} }
Time Time::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const&, Time const& reference_value) Time Time::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node, Time const& reference_value)
{ {
return calculated->resolve_time_percentage(reference_value).value(); return calculated->resolve_time(
{
.percentage_basis = reference_value,
.length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node),
})
.value();
} }
} }

View file

@ -550,14 +550,16 @@ void LayoutState::UsedValues::set_node(NodeWithStyle& node, UsedValues const* co
} }
if (size.is_calculated()) { if (size.is_calculated()) {
CSS::CalculationResolutionContext context {
.length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(node),
};
if (size.calculated().contains_percentage()) { if (size.calculated().contains_percentage()) {
if (!containing_block_has_definite_size) if (!containing_block_has_definite_size)
return false; return false;
auto containing_block_size_as_length = width ? containing_block_used_values->content_width() : containing_block_used_values->content_height(); auto containing_block_size_as_length = width ? containing_block_used_values->content_width() : containing_block_used_values->content_height();
resolved_definite_size = adjust_for_box_sizing(size.calculated().resolve_length_percentage(node, containing_block_size_as_length).value_or(CSS::Length::make_auto()).to_px(node), size, width); context.percentage_basis = CSS::Length::make_px(containing_block_size_as_length);
return true;
} }
resolved_definite_size = adjust_for_box_sizing(size.calculated().resolve_length(node)->to_px(node), size, width); resolved_definite_size = adjust_for_box_sizing(size.calculated().resolve_length(context)->to_px(node), size, width);
return true; return true;
} }

View file

@ -787,7 +787,7 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
computed_values.set_transition_delay(transition_delay.time()); computed_values.set_transition_delay(transition_delay.time());
} else if (transition_delay_property.is_calculated()) { } else if (transition_delay_property.is_calculated()) {
auto const& transition_delay = transition_delay_property.as_calculated(); auto const& transition_delay = transition_delay_property.as_calculated();
computed_values.set_transition_delay(transition_delay.resolve_time().value()); computed_values.set_transition_delay(transition_delay.resolve_time({ .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*this) }).value());
} }
auto do_border_style = [&](CSS::BorderData& border, CSS::PropertyID width_property, CSS::PropertyID color_property, CSS::PropertyID style_property) { auto do_border_style = [&](CSS::BorderData& border, CSS::PropertyID width_property, CSS::PropertyID color_property, CSS::PropertyID style_property) {
@ -808,7 +808,8 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
auto resolve_border_width = [&]() -> CSSPixels { auto resolve_border_width = [&]() -> CSSPixels {
auto const& value = computed_style.property(width_property); auto const& value = computed_style.property(width_property);
if (value.is_calculated()) if (value.is_calculated())
return max(CSSPixels { 0 }, value.as_calculated().resolve_length(*this)->to_px(*this)); return max(CSSPixels { 0 },
value.as_calculated().resolve_length({ .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*this) })->to_px(*this));
if (value.is_length()) if (value.is_length())
return value.as_length().length().to_px(*this); return value.as_length().length().to_px(*this);
if (value.is_keyword()) { if (value.is_keyword()) {