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
Notes: sideshowbarker 2024-07-17 03:59:29 +09:00
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;
}
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 (calculation_tree == nullptr) {
dbgln_if(CSS_PARSER_DEBUG, "Failed to parse calculation tree");
if (!component_value.is_function())
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());
if (!calc_type.has_value()) {
dbgln_if(CSS_PARSER_DEBUG, "calc() resolved as invalid!!!");
auto const& function = component_value.function();
auto function_node = TRY(parse_a_calc_function_node(function));
if (!function_node)
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)
{
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;
return CalculatedStyleValue::create(function_node.release_nonnull(), function_type.release_value());
}
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() };
}
if (auto dynamic_value = parse_dynamic_value(component_value); !dynamic_value.is_error() && dynamic_value.value()) {
return LengthOrCalculated { dynamic_value.value()->as_calculated().as_calculated() };
if (auto calculated_value = parse_calculated_value(component_value); !calculated_value.is_error() && calculated_value.value()) {
return LengthOrCalculated { calculated_value.value().release_nonnull() };
}
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)) {
return component_value.token().number_value();
} else if (component_value.is_function()) {
auto maybe_calc = parse_dynamic_value(component_value).release_value_but_fixme_should_propagate_errors();
if (!maybe_calc || !maybe_calc->is_calculated() || !maybe_calc->as_calculated().resolves_to_number())
auto maybe_calc = parse_calculated_value(component_value).release_value_but_fixme_should_propagate_errors();
if (!maybe_calc || !maybe_calc->resolves_to_number())
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();
}
}
@ -5212,11 +5180,8 @@ ErrorOr<RefPtr<StyleValue>> Parser::parse_single_shadow_value(TokenStream<Compon
Optional<ShadowPlacement> placement;
auto possibly_dynamic_length = [&](ComponentValue const& token) -> ErrorOr<RefPtr<StyleValue>> {
if (auto maybe_dynamic_value = TRY(parse_dynamic_value(token))) {
if (!maybe_dynamic_value->is_calculated())
return nullptr;
auto const& calculated_value = maybe_dynamic_value->as_calculated();
if (!calculated_value.resolves_to_length())
if (auto calculated_value = TRY(parse_calculated_value(token))) {
if (!calculated_value->resolves_to_length())
return nullptr;
return calculated_value;
}
@ -6631,14 +6596,7 @@ ErrorOr<RefPtr<StyleValue>> Parser::parse_transform_value(Vector<ComponentValue>
}
auto const& value = argument_tokens.next_token();
RefPtr<CalculatedStyleValue> maybe_calc_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();
}
RefPtr<CalculatedStyleValue> maybe_calc_value = TRY(parse_calculated_value(value));
switch (function_metadata.parameters[argument_index].type) {
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)
{
if (component_value.is_function()) {
if (auto maybe_dynamic = parse_dynamic_value(component_value); !maybe_dynamic.is_error() && maybe_dynamic.value())
return GridSize(LengthPercentage(maybe_dynamic.release_value()->as_calculated()));
if (auto maybe_calculated = parse_calculated_value(component_value); !maybe_calculated.is_error() && maybe_calculated.value())
return GridSize(LengthPercentage(maybe_calculated.release_value().release_nonnull()));
return {};
}
@ -7032,8 +6990,8 @@ Optional<CSS::ExplicitGridTrack> Parser::parse_track_sizing_function(ComponentVa
return CSS::ExplicitGridTrack(maybe_min_max_value.value());
else
return {};
} else if (auto maybe_dynamic = parse_dynamic_value(token); !maybe_dynamic.is_error() && maybe_dynamic.value()) {
return CSS::ExplicitGridTrack(GridSize(LengthPercentage(maybe_dynamic.release_value()->as_calculated())));
} else if (auto maybe_calculated = parse_calculated_value(token); !maybe_calculated.is_error() && maybe_calculated.value()) {
return CSS::ExplicitGridTrack(GridSize(LengthPercentage(maybe_calculated.release_value().release_nonnull())));
}
return {};
} 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,
// 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 (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();
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>`.
if (calculated.resolves_to_percentage()) {
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);
}
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));
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)

View file

@ -89,7 +89,7 @@ public:
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<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();
@ -284,8 +284,7 @@ private:
};
ErrorOr<PropertyAndValue> parse_css_value_for_properties(ReadonlySpan<PropertyID>, TokenStream<ComponentValue>&);
ErrorOr<RefPtr<StyleValue>> parse_builtin_value(ComponentValue const&);
ErrorOr<RefPtr<StyleValue>> parse_dynamic_value(ComponentValue const&);
ErrorOr<RefPtr<CalculatedStyleValue>> parse_calculated_value(Vector<ComponentValue> const&);
ErrorOr<RefPtr<CalculatedStyleValue>> parse_calculated_value(ComponentValue const&);
// NOTE: Implemented in generated code. (GenerateCSSMathFunctions.cpp)
ErrorOr<OwnPtr<CalculationNode>> parse_math_function(PropertyID, 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;
}
// FIXME: Handle all math functions.
if (value.function().name().equals_ignoring_ascii_case("calc"sv)) {
auto const& calc_function = value.function();
if (auto calc_value = Parser::Parser::parse_calculated_value({}, Parser::ParsingContext { document() }, calc_function.values()).release_value_but_fixme_should_propagate_errors()) {
if (calc_value->resolves_to_number()) {
auto resolved_value = calc_value->resolve_number();
dest.empend(Parser::Token::create_number(resolved_value.value()));
continue;
} else if (calc_value->resolves_to_percentage()) {
auto resolved_value = calc_value->resolve_percentage();
dest.empend(Parser::Token::create_percentage(resolved_value.value().value()));
continue;
} else {
dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Unimplemented calc() expansion: {}", calc_value->to_string());
}
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()) {
auto& calc_value = maybe_calc_value->as_calculated();
if (calc_value.resolves_to_number()) {
auto resolved_value = calc_value.resolve_number();
dest.empend(Parser::Token::create_number(resolved_value.value()));
continue;
} else if (calc_value.resolves_to_percentage()) {
auto resolved_value = calc_value.resolve_percentage();
dest.empend(Parser::Token::create_percentage(resolved_value.value().value()));
continue;
} else {
dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Unimplemented calc() expansion: {}", calc_value.to_string());
}
}