1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 20:37:35 +00:00

LibWeb: Deduplicate calc-parsing code

We had `parse_calculated_value()` which parsed the contents of `calc()`,
and `parse_dynamic_value()` which parsed any math function, both of
which produce a CalculatedStyleValue, but return a plain StyleValue.
This was confusing, so let's combine them together, and return a
CalculatedStyleValue.

This also makes the other math functions work in
`StyleComputer::expand_unresolved_values()`.
This commit is contained in:
Sam Atkins 2023-08-17 14:43:36 +01:00 committed by Andreas Kling
parent 7bc7f376fa
commit 68dae8ab46
3 changed files with 40 additions and 89 deletions

View file

@ -3467,54 +3467,22 @@ ErrorOr<RefPtr<StyleValue>> Parser::parse_builtin_value(ComponentValue const& co
return nullptr; return nullptr;
} }
ErrorOr<RefPtr<CalculatedStyleValue>> Parser::parse_calculated_value(Vector<ComponentValue> const& component_values) ErrorOr<RefPtr<CalculatedStyleValue>> Parser::parse_calculated_value(ComponentValue const& component_value)
{ {
auto calculation_tree = TRY(parse_a_calculation(component_values)); if (!component_value.is_function())
if (calculation_tree == nullptr) {
dbgln_if(CSS_PARSER_DEBUG, "Failed to parse calculation tree");
return nullptr; return nullptr;
} else {
if constexpr (CSS_PARSER_DEBUG) {
dbgln("Parsed calculation tree:");
StringBuilder builder;
TRY(calculation_tree->dump(builder, 0));
dbgln(builder.string_view());
}
}
auto calc_type = calculation_tree->determine_type(m_context.current_property_id()); auto const& function = component_value.function();
if (!calc_type.has_value()) {
dbgln_if(CSS_PARSER_DEBUG, "calc() resolved as invalid!!!"); auto function_node = TRY(parse_a_calc_function_node(function));
if (!function_node)
return nullptr; return nullptr;
}
dbgln_if(CSS_PARSER_DEBUG, "Deduced calc() resolved type as: {}", calc_type->dump());
return CalculatedStyleValue::create(calculation_tree.release_nonnull(), calc_type.release_value()); auto function_type = function_node->determine_type(m_context.current_property_id());
} if (!function_type.has_value())
return nullptr;
ErrorOr<RefPtr<StyleValue>> Parser::parse_dynamic_value(ComponentValue const& component_value) return CalculatedStyleValue::create(function_node.release_nonnull(), function_type.release_value());
{
if (component_value.is_function()) {
auto const& function = component_value.function();
if (function.name().equals_ignoring_ascii_case("var"sv)) {
// Declarations using `var()` should already be parsed as an UnresolvedStyleValue before this point.
VERIFY_NOT_REACHED();
}
auto function_node = TRY(parse_a_calc_function_node(function));
if (!function_node)
return nullptr;
auto function_type = function_node->determine_type(m_context.current_property_id());
if (!function_type.has_value())
return nullptr;
return CalculatedStyleValue::create(function_node.release_nonnull(), function_type.release_value());
}
return nullptr;
} }
ErrorOr<OwnPtr<CalculationNode>> Parser::parse_a_calc_function_node(Function const& function) ErrorOr<OwnPtr<CalculationNode>> Parser::parse_a_calc_function_node(Function const& function)
@ -3575,8 +3543,8 @@ Optional<LengthOrCalculated> Parser::parse_source_size_value(ComponentValue cons
return LengthOrCalculated { Length::make_auto() }; return LengthOrCalculated { Length::make_auto() };
} }
if (auto dynamic_value = parse_dynamic_value(component_value); !dynamic_value.is_error() && dynamic_value.value()) { if (auto calculated_value = parse_calculated_value(component_value); !calculated_value.is_error() && calculated_value.value()) {
return LengthOrCalculated { dynamic_value.value()->as_calculated().as_calculated() }; return LengthOrCalculated { calculated_value.value().release_nonnull() };
} }
if (auto length = parse_length(component_value); length.has_value()) { if (auto length = parse_length(component_value); length.has_value()) {
@ -3607,10 +3575,10 @@ Optional<Ratio> Parser::parse_ratio(TokenStream<ComponentValue>& tokens)
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();
} else if (component_value.is_function()) { } else if (component_value.is_function()) {
auto maybe_calc = parse_dynamic_value(component_value).release_value_but_fixme_should_propagate_errors(); auto maybe_calc = parse_calculated_value(component_value).release_value_but_fixme_should_propagate_errors();
if (!maybe_calc || !maybe_calc->is_calculated() || !maybe_calc->as_calculated().resolves_to_number()) if (!maybe_calc || !maybe_calc->resolves_to_number())
return {}; return {};
if (auto resolved_number = maybe_calc->as_calculated().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();
} }
} }
@ -5212,11 +5180,8 @@ ErrorOr<RefPtr<StyleValue>> Parser::parse_single_shadow_value(TokenStream<Compon
Optional<ShadowPlacement> placement; Optional<ShadowPlacement> placement;
auto possibly_dynamic_length = [&](ComponentValue const& token) -> ErrorOr<RefPtr<StyleValue>> { auto possibly_dynamic_length = [&](ComponentValue const& token) -> ErrorOr<RefPtr<StyleValue>> {
if (auto maybe_dynamic_value = TRY(parse_dynamic_value(token))) { if (auto calculated_value = TRY(parse_calculated_value(token))) {
if (!maybe_dynamic_value->is_calculated()) if (!calculated_value->resolves_to_length())
return nullptr;
auto const& calculated_value = maybe_dynamic_value->as_calculated();
if (!calculated_value.resolves_to_length())
return nullptr; return nullptr;
return calculated_value; return calculated_value;
} }
@ -6631,14 +6596,7 @@ ErrorOr<RefPtr<StyleValue>> Parser::parse_transform_value(Vector<ComponentValue>
} }
auto const& value = argument_tokens.next_token(); auto const& value = argument_tokens.next_token();
RefPtr<CalculatedStyleValue> maybe_calc_value; RefPtr<CalculatedStyleValue> maybe_calc_value = TRY(parse_calculated_value(value));
if (auto maybe_dynamic_value = TRY(parse_dynamic_value(value))) {
// TODO: calc() is the only dynamic value we support for now, but more will come later.
// FIXME: Actually, calc() should probably be parsed inside parse_dimension_value() etc,
// so that it affects every use instead of us having to manually implement it.
VERIFY(maybe_dynamic_value->is_calculated());
maybe_calc_value = maybe_dynamic_value->as_calculated();
}
switch (function_metadata.parameters[argument_index].type) { switch (function_metadata.parameters[argument_index].type) {
case TransformFunctionParameterType::Angle: { case TransformFunctionParameterType::Angle: {
@ -6851,8 +6809,8 @@ ErrorOr<RefPtr<StyleValue>> Parser::parse_as_css_value(PropertyID property_id)
Optional<CSS::GridSize> Parser::parse_grid_size(ComponentValue const& component_value) Optional<CSS::GridSize> Parser::parse_grid_size(ComponentValue const& component_value)
{ {
if (component_value.is_function()) { if (component_value.is_function()) {
if (auto maybe_dynamic = parse_dynamic_value(component_value); !maybe_dynamic.is_error() && maybe_dynamic.value()) if (auto maybe_calculated = parse_calculated_value(component_value); !maybe_calculated.is_error() && maybe_calculated.value())
return GridSize(LengthPercentage(maybe_dynamic.release_value()->as_calculated())); return GridSize(LengthPercentage(maybe_calculated.release_value().release_nonnull()));
return {}; return {};
} }
@ -7032,8 +6990,8 @@ Optional<CSS::ExplicitGridTrack> Parser::parse_track_sizing_function(ComponentVa
return CSS::ExplicitGridTrack(maybe_min_max_value.value()); return CSS::ExplicitGridTrack(maybe_min_max_value.value());
else else
return {}; return {};
} else if (auto maybe_dynamic = parse_dynamic_value(token); !maybe_dynamic.is_error() && maybe_dynamic.value()) { } else if (auto maybe_calculated = parse_calculated_value(token); !maybe_calculated.is_error() && maybe_calculated.value()) {
return CSS::ExplicitGridTrack(GridSize(LengthPercentage(maybe_dynamic.release_value()->as_calculated()))); return CSS::ExplicitGridTrack(GridSize(LengthPercentage(maybe_calculated.release_value().release_nonnull())));
} }
return {}; return {};
} else if (token.is(Token::Type::Ident) && token.token().ident().equals_ignoring_ascii_case("auto"sv)) { } else if (token.is(Token::Type::Ident) && token.token().ident().equals_ignoring_ascii_case("auto"sv)) {
@ -7925,9 +7883,9 @@ ErrorOr<Parser::PropertyAndValue> Parser::parse_css_value_for_properties(Readonl
// In order to not end up parsing `calc()` and other math expressions multiple times, // In order to not end up parsing `calc()` and other math expressions multiple times,
// we parse it once, and then see if its resolved type matches what the property accepts. // we parse it once, and then see if its resolved type matches what the property accepts.
if (peek_token.is_function() && (property_accepts_dimension || property_accepts_numeric)) { if (peek_token.is_function() && (property_accepts_dimension || property_accepts_numeric)) {
if (auto maybe_dynamic = TRY(parse_dynamic_value(peek_token)); maybe_dynamic && maybe_dynamic->is_calculated()) { if (auto maybe_calculated = TRY(parse_calculated_value(peek_token)); maybe_calculated) {
(void)tokens.next_token(); (void)tokens.next_token();
auto& calculated = maybe_dynamic->as_calculated(); auto& calculated = *maybe_calculated;
// This is a bit sensitive to ordering: `<foo>` and `<percentage>` have to be checked before `<foo-percentage>`. // This is a bit sensitive to ordering: `<foo>` and `<percentage>` have to be checked before `<foo-percentage>`.
if (calculated.resolves_to_percentage()) { if (calculated.resolves_to_percentage()) {
if (auto property = any_property_accepts_type(property_ids, ValueType::Percentage); property.has_value()) if (auto property = any_property_accepts_type(property_ids, ValueType::Percentage); property.has_value())
@ -8594,13 +8552,10 @@ bool Parser::is_builtin(StringView name)
|| name.equals_ignoring_ascii_case("unset"sv); || name.equals_ignoring_ascii_case("unset"sv);
} }
ErrorOr<RefPtr<CalculatedStyleValue>> Parser::parse_calculated_value(Badge<StyleComputer>, ParsingContext const& context, Vector<ComponentValue> const& tokens) ErrorOr<RefPtr<CalculatedStyleValue>> Parser::parse_calculated_value(Badge<StyleComputer>, ParsingContext const& context, ComponentValue const& token)
{ {
if (tokens.is_empty())
return nullptr;
auto parser = TRY(Parser::create(context, ""sv)); auto parser = TRY(Parser::create(context, ""sv));
return parser.parse_calculated_value(tokens); return parser.parse_calculated_value(token);
} }
ErrorOr<RefPtr<StyleValue>> Parser::parse_css_value(Badge<StyleComputer>, ParsingContext const& context, PropertyID property_id, Vector<ComponentValue> const& tokens) ErrorOr<RefPtr<StyleValue>> Parser::parse_css_value(Badge<StyleComputer>, ParsingContext const& context, PropertyID property_id, Vector<ComponentValue> const& tokens)

View file

@ -89,7 +89,7 @@ public:
ErrorOr<RefPtr<StyleValue>> parse_as_css_value(PropertyID); ErrorOr<RefPtr<StyleValue>> parse_as_css_value(PropertyID);
static ErrorOr<RefPtr<StyleValue>> parse_css_value(Badge<StyleComputer>, ParsingContext const&, PropertyID, Vector<ComponentValue> const&); static ErrorOr<RefPtr<StyleValue>> parse_css_value(Badge<StyleComputer>, ParsingContext const&, PropertyID, Vector<ComponentValue> const&);
static ErrorOr<RefPtr<CalculatedStyleValue>> parse_calculated_value(Badge<StyleComputer>, ParsingContext const&, Vector<ComponentValue> const&); static ErrorOr<RefPtr<CalculatedStyleValue>> parse_calculated_value(Badge<StyleComputer>, ParsingContext const&, ComponentValue const&);
[[nodiscard]] LengthOrCalculated parse_as_sizes_attribute(); [[nodiscard]] LengthOrCalculated parse_as_sizes_attribute();
@ -284,8 +284,7 @@ private:
}; };
ErrorOr<PropertyAndValue> parse_css_value_for_properties(ReadonlySpan<PropertyID>, TokenStream<ComponentValue>&); ErrorOr<PropertyAndValue> parse_css_value_for_properties(ReadonlySpan<PropertyID>, TokenStream<ComponentValue>&);
ErrorOr<RefPtr<StyleValue>> parse_builtin_value(ComponentValue const&); ErrorOr<RefPtr<StyleValue>> parse_builtin_value(ComponentValue const&);
ErrorOr<RefPtr<StyleValue>> parse_dynamic_value(ComponentValue const&); ErrorOr<RefPtr<CalculatedStyleValue>> parse_calculated_value(ComponentValue const&);
ErrorOr<RefPtr<CalculatedStyleValue>> parse_calculated_value(Vector<ComponentValue> const&);
// NOTE: Implemented in generated code. (GenerateCSSMathFunctions.cpp) // NOTE: Implemented in generated code. (GenerateCSSMathFunctions.cpp)
ErrorOr<OwnPtr<CalculationNode>> parse_math_function(PropertyID, Function const&); ErrorOr<OwnPtr<CalculationNode>> parse_math_function(PropertyID, Function const&);
ErrorOr<OwnPtr<CalculationNode>> parse_a_calc_function_node(Function const&); ErrorOr<OwnPtr<CalculationNode>> parse_a_calc_function_node(Function const&);

View file

@ -1074,21 +1074,18 @@ bool StyleComputer::expand_unresolved_values(DOM::Element& element, StringView p
return false; return false;
} }
// FIXME: Handle all math functions. if (auto maybe_calc_value = Parser::Parser::parse_calculated_value({}, Parser::ParsingContext { document() }, value).release_value_but_fixme_should_propagate_errors(); maybe_calc_value && maybe_calc_value->is_calculated()) {
if (value.function().name().equals_ignoring_ascii_case("calc"sv)) { auto& calc_value = maybe_calc_value->as_calculated();
auto const& calc_function = value.function(); if (calc_value.resolves_to_number()) {
if (auto calc_value = Parser::Parser::parse_calculated_value({}, Parser::ParsingContext { document() }, calc_function.values()).release_value_but_fixme_should_propagate_errors()) { auto resolved_value = calc_value.resolve_number();
if (calc_value->resolves_to_number()) { dest.empend(Parser::Token::create_number(resolved_value.value()));
auto resolved_value = calc_value->resolve_number(); continue;
dest.empend(Parser::Token::create_number(resolved_value.value())); } else if (calc_value.resolves_to_percentage()) {
continue; auto resolved_value = calc_value.resolve_percentage();
} else if (calc_value->resolves_to_percentage()) { dest.empend(Parser::Token::create_percentage(resolved_value.value().value()));
auto resolved_value = calc_value->resolve_percentage(); continue;
dest.empend(Parser::Token::create_percentage(resolved_value.value().value())); } else {
continue; dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Unimplemented calc() expansion: {}", calc_value.to_string());
} else {
dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Unimplemented calc() expansion: {}", calc_value->to_string());
}
} }
} }