mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 11:36:10 +00:00
LibWeb/CSS: Give calc() a CalculationContext for resolving percentages
This is passed in at construction, meaning we will be able to refer to it later, when we're no longer inside the Parser.
This commit is contained in:
parent
bc00ef8314
commit
4efdb76857
Notes:
github-actions[bot]
2025-01-13 11:00:31 +00:00
Author: https://github.com/AtkinsSJ Commit: https://github.com/LadybirdBrowser/ladybird/commit/4efdb768576 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3198
8 changed files with 111 additions and 84 deletions
|
@ -12,6 +12,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/AngleStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CSSColorValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/FrequencyStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/IntegerStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
|
||||
|
@ -60,10 +61,14 @@ ValueComparingRefPtr<CSSStyleValue const> interpolate_property(DOM::Element& ele
|
|||
auto from = with_keyword_values_resolved(element, property_id, a_from);
|
||||
auto to = with_keyword_values_resolved(element, property_id, a_to);
|
||||
|
||||
CalculationContext calculation_context {
|
||||
.percentages_resolve_as = property_resolves_percentages_relative_to(property_id),
|
||||
};
|
||||
|
||||
auto animation_type = animation_type_from_longhand_property(property_id);
|
||||
switch (animation_type) {
|
||||
case AnimationType::ByComputedValue:
|
||||
return interpolate_value(element, from, to, delta);
|
||||
return interpolate_value(element, calculation_context, from, to, delta);
|
||||
case AnimationType::None:
|
||||
return to;
|
||||
case AnimationType::Custom: {
|
||||
|
@ -78,7 +83,7 @@ ValueComparingRefPtr<CSSStyleValue const> interpolate_property(DOM::Element& ele
|
|||
return {};
|
||||
}
|
||||
if (property_id == PropertyID::BoxShadow)
|
||||
return interpolate_box_shadow(element, from, to, delta);
|
||||
return interpolate_box_shadow(element, calculation_context, from, to, delta);
|
||||
|
||||
// FIXME: Handle all custom animatable properties
|
||||
[[fallthrough]];
|
||||
|
@ -422,7 +427,7 @@ Color interpolate_color(Color from, Color to, float delta)
|
|||
return color;
|
||||
}
|
||||
|
||||
NonnullRefPtr<CSSStyleValue const> interpolate_box_shadow(DOM::Element& element, CSSStyleValue const& from, CSSStyleValue const& to, float delta)
|
||||
NonnullRefPtr<CSSStyleValue const> interpolate_box_shadow(DOM::Element& element, CalculationContext const& calculation_context, CSSStyleValue const& from, CSSStyleValue const& to, float delta)
|
||||
{
|
||||
// https://drafts.csswg.org/css-backgrounds/#box-shadow
|
||||
// Animation type: by computed value, treating none as a zero-item list and appending blank shadows
|
||||
|
@ -472,10 +477,10 @@ NonnullRefPtr<CSSStyleValue const> interpolate_box_shadow(DOM::Element& element,
|
|||
auto const& to_shadow = to_shadows[i]->as_shadow();
|
||||
auto result_shadow = ShadowStyleValue::create(
|
||||
CSSColorValue::create_from_color(interpolate_color(from_shadow.color()->to_color({}), to_shadow.color()->to_color({}), delta)),
|
||||
interpolate_value(element, from_shadow.offset_x(), to_shadow.offset_x(), delta),
|
||||
interpolate_value(element, from_shadow.offset_y(), to_shadow.offset_y(), delta),
|
||||
interpolate_value(element, from_shadow.blur_radius(), to_shadow.blur_radius(), delta),
|
||||
interpolate_value(element, from_shadow.spread_distance(), to_shadow.spread_distance(), delta),
|
||||
interpolate_value(element, calculation_context, from_shadow.offset_x(), to_shadow.offset_x(), delta),
|
||||
interpolate_value(element, calculation_context, from_shadow.offset_y(), to_shadow.offset_y(), delta),
|
||||
interpolate_value(element, calculation_context, from_shadow.blur_radius(), to_shadow.blur_radius(), delta),
|
||||
interpolate_value(element, calculation_context, from_shadow.spread_distance(), to_shadow.spread_distance(), delta),
|
||||
delta >= 0.5f ? to_shadow.placement() : from_shadow.placement());
|
||||
result_shadows.unchecked_append(result_shadow);
|
||||
}
|
||||
|
@ -483,7 +488,7 @@ NonnullRefPtr<CSSStyleValue const> interpolate_box_shadow(DOM::Element& element,
|
|||
return StyleValueList::create(move(result_shadows), StyleValueList::Separator::Comma);
|
||||
}
|
||||
|
||||
NonnullRefPtr<CSSStyleValue const> interpolate_value(DOM::Element& element, CSSStyleValue const& from, CSSStyleValue const& to, float delta)
|
||||
NonnullRefPtr<CSSStyleValue const> interpolate_value(DOM::Element& element, CalculationContext const& calculation_context, CSSStyleValue const& from, CSSStyleValue const& to, float delta)
|
||||
{
|
||||
if (from.type() != to.type()) {
|
||||
// Handle mixed percentage and dimension types
|
||||
|
@ -520,18 +525,18 @@ NonnullRefPtr<CSSStyleValue const> interpolate_value(DOM::Element& element, CSSS
|
|||
}
|
||||
};
|
||||
|
||||
static constexpr auto to_calculation_node = [](CSSStyleValue const& value) -> NonnullOwnPtr<CalculationNode> {
|
||||
static auto to_calculation_node = [calculation_context](CSSStyleValue const& value) -> NonnullOwnPtr<CalculationNode> {
|
||||
switch (value.type()) {
|
||||
case CSSStyleValue::Type::Angle:
|
||||
return NumericCalculationNode::create(value.as_angle().angle());
|
||||
return NumericCalculationNode::create(value.as_angle().angle(), calculation_context);
|
||||
case CSSStyleValue::Type::Frequency:
|
||||
return NumericCalculationNode::create(value.as_frequency().frequency());
|
||||
return NumericCalculationNode::create(value.as_frequency().frequency(), calculation_context);
|
||||
case CSSStyleValue::Type::Length:
|
||||
return NumericCalculationNode::create(value.as_length().length());
|
||||
return NumericCalculationNode::create(value.as_length().length(), calculation_context);
|
||||
case CSSStyleValue::Type::Percentage:
|
||||
return NumericCalculationNode::create(value.as_percentage().percentage());
|
||||
return NumericCalculationNode::create(value.as_percentage().percentage(), calculation_context);
|
||||
case CSSStyleValue::Type::Time:
|
||||
return NumericCalculationNode::create(value.as_time().time());
|
||||
return NumericCalculationNode::create(value.as_time().time(), calculation_context);
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
@ -546,15 +551,15 @@ NonnullRefPtr<CSSStyleValue const> interpolate_value(DOM::Element& element, CSSS
|
|||
// hard to understand how this interpolation works, but if instead we rewrite the values as "30px + 0%" and
|
||||
// "0px + 80%", then it is very simple to understand; we just interpolate each component separately.
|
||||
|
||||
auto interpolated_from = interpolate_value(element, from, from_base_type_and_default->default_value, delta);
|
||||
auto interpolated_to = interpolate_value(element, to_base_type_and_default->default_value, to, delta);
|
||||
auto interpolated_from = interpolate_value(element, calculation_context, from, from_base_type_and_default->default_value, delta);
|
||||
auto interpolated_to = interpolate_value(element, calculation_context, to_base_type_and_default->default_value, to, delta);
|
||||
|
||||
Vector<NonnullOwnPtr<CalculationNode>> values;
|
||||
values.ensure_capacity(2);
|
||||
values.unchecked_append(to_calculation_node(interpolated_from));
|
||||
values.unchecked_append(to_calculation_node(interpolated_to));
|
||||
auto calc_node = SumCalculationNode::create(move(values));
|
||||
return CalculatedStyleValue::create(move(calc_node), CSSNumericType { to_base_type_and_default->base_type, 1 });
|
||||
return CalculatedStyleValue::create(move(calc_node), CSSNumericType { to_base_type_and_default->base_type, 1 }, calculation_context);
|
||||
}
|
||||
|
||||
return delta >= 0.5f ? to : from;
|
||||
|
@ -587,8 +592,8 @@ NonnullRefPtr<CSSStyleValue const> interpolate_value(DOM::Element& element, CSSS
|
|||
auto const& from_position = from.as_position();
|
||||
auto const& to_position = to.as_position();
|
||||
return PositionStyleValue::create(
|
||||
interpolate_value(element, from_position.edge_x(), to_position.edge_x(), delta)->as_edge(),
|
||||
interpolate_value(element, from_position.edge_y(), to_position.edge_y(), delta)->as_edge());
|
||||
interpolate_value(element, calculation_context, from_position.edge_x(), to_position.edge_x(), delta)->as_edge(),
|
||||
interpolate_value(element, calculation_context, from_position.edge_y(), to_position.edge_y(), delta)->as_edge());
|
||||
}
|
||||
case CSSStyleValue::Type::Ratio: {
|
||||
auto from_ratio = from.as_ratio().ratio();
|
||||
|
@ -634,7 +639,7 @@ NonnullRefPtr<CSSStyleValue const> interpolate_value(DOM::Element& element, CSSS
|
|||
StyleValueVector interpolated_values;
|
||||
interpolated_values.ensure_capacity(from_list.size());
|
||||
for (size_t i = 0; i < from_list.size(); ++i)
|
||||
interpolated_values.append(interpolate_value(element, from_list.values()[i], to_list.values()[i], delta));
|
||||
interpolated_values.append(interpolate_value(element, calculation_context, from_list.values()[i], to_list.values()[i], delta));
|
||||
|
||||
return StyleValueList::create(move(interpolated_values), from_list.separator());
|
||||
}
|
||||
|
|
|
@ -11,13 +11,15 @@
|
|||
|
||||
namespace Web::CSS {
|
||||
|
||||
struct CalculationContext;
|
||||
|
||||
ValueComparingRefPtr<CSSStyleValue const> interpolate_property(DOM::Element&, PropertyID, CSSStyleValue const& from, CSSStyleValue const& to, float delta);
|
||||
|
||||
// https://drafts.csswg.org/css-transitions/#transitionable
|
||||
bool property_values_are_transitionable(PropertyID, CSSStyleValue const& old_value, CSSStyleValue const& new_value);
|
||||
|
||||
NonnullRefPtr<CSSStyleValue const> interpolate_value(DOM::Element&, CSSStyleValue const& from, CSSStyleValue const& to, float delta);
|
||||
NonnullRefPtr<CSSStyleValue const> interpolate_box_shadow(DOM::Element&, CSSStyleValue const& from, CSSStyleValue const& to, float delta);
|
||||
NonnullRefPtr<CSSStyleValue const> interpolate_value(DOM::Element&, CalculationContext const&, CSSStyleValue const& from, CSSStyleValue const& to, float delta);
|
||||
NonnullRefPtr<CSSStyleValue const> interpolate_box_shadow(DOM::Element&, CalculationContext const&, CSSStyleValue const& from, CSSStyleValue const& to, float delta);
|
||||
RefPtr<CSSStyleValue const> interpolate_transform(DOM::Element&, CSSStyleValue const& from, CSSStyleValue const& to, float delta);
|
||||
|
||||
Color interpolate_color(Color from, Color to, float delta);
|
||||
|
|
|
@ -1922,7 +1922,30 @@ RefPtr<CalculatedStyleValue> Parser::parse_calculated_value(ComponentValue const
|
|||
|
||||
auto const& function = component_value.function();
|
||||
|
||||
auto function_node = parse_a_calc_function_node(function);
|
||||
CalculationContext context {};
|
||||
for (auto const& value_context : m_value_context.in_reverse()) {
|
||||
auto percentages_resolve_as = value_context.visit(
|
||||
[](PropertyID property_id) -> Optional<ValueType> {
|
||||
return property_resolves_percentages_relative_to(property_id);
|
||||
},
|
||||
[](FunctionContext const& function) -> Optional<ValueType> {
|
||||
// Gradients resolve percentages as lengths relative to the gradient-box.
|
||||
if (function.name.is_one_of_ignoring_ascii_case(
|
||||
"linear-gradient"sv, "repeating-linear-gradient"sv,
|
||||
"radial-gradient"sv, "repeating-radial-gradient"sv,
|
||||
"conic-gradient"sv, "repeating-conic-gradient"sv)) {
|
||||
return ValueType::Length;
|
||||
}
|
||||
// FIXME: Add other functions that provide a context for resolving percentages
|
||||
return {};
|
||||
});
|
||||
if (percentages_resolve_as.has_value()) {
|
||||
context.percentages_resolve_as = move(percentages_resolve_as);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto function_node = parse_a_calc_function_node(function, context);
|
||||
if (!function_node)
|
||||
return nullptr;
|
||||
|
||||
|
@ -1930,17 +1953,17 @@ RefPtr<CalculatedStyleValue> Parser::parse_calculated_value(ComponentValue const
|
|||
if (!function_type.has_value())
|
||||
return nullptr;
|
||||
|
||||
return CalculatedStyleValue::create(function_node.release_nonnull(), function_type.release_value());
|
||||
return CalculatedStyleValue::create(function_node.release_nonnull(), function_type.release_value(), context);
|
||||
}
|
||||
|
||||
OwnPtr<CalculationNode> Parser::parse_a_calc_function_node(Function const& function)
|
||||
OwnPtr<CalculationNode> Parser::parse_a_calc_function_node(Function const& function, CalculationContext const& context)
|
||||
{
|
||||
auto context_guard = push_temporary_value_parsing_context(FunctionContext { function.name });
|
||||
|
||||
if (function.name.equals_ignoring_ascii_case("calc"sv))
|
||||
return parse_a_calculation(function.value);
|
||||
return parse_a_calculation(function.value, context);
|
||||
|
||||
if (auto maybe_function = parse_math_function(function))
|
||||
if (auto maybe_function = parse_math_function(function, context))
|
||||
return maybe_function;
|
||||
|
||||
return nullptr;
|
||||
|
@ -9231,15 +9254,15 @@ LengthOrCalculated Parser::Parser::parse_as_sizes_attribute(DOM::Element const&
|
|||
return Length(100, Length::Type::Vw);
|
||||
}
|
||||
|
||||
OwnPtr<CalculationNode> Parser::convert_to_calculation_node(CalcParsing::Node const& node)
|
||||
OwnPtr<CalculationNode> Parser::convert_to_calculation_node(CalcParsing::Node const& node, CalculationContext const& context)
|
||||
{
|
||||
return node.visit(
|
||||
[this](NonnullOwnPtr<CalcParsing::ProductNode> const& product_node) -> OwnPtr<CalculationNode> {
|
||||
[this, &context](NonnullOwnPtr<CalcParsing::ProductNode> const& product_node) -> OwnPtr<CalculationNode> {
|
||||
Vector<NonnullOwnPtr<CalculationNode>> children;
|
||||
children.ensure_capacity(product_node->children.size());
|
||||
|
||||
for (auto const& child : product_node->children) {
|
||||
if (auto child_as_node = convert_to_calculation_node(child)) {
|
||||
if (auto child_as_node = convert_to_calculation_node(child, context)) {
|
||||
children.append(child_as_node.release_nonnull());
|
||||
} else {
|
||||
return nullptr;
|
||||
|
@ -9248,12 +9271,12 @@ OwnPtr<CalculationNode> Parser::convert_to_calculation_node(CalcParsing::Node co
|
|||
|
||||
return ProductCalculationNode::create(move(children));
|
||||
},
|
||||
[this](NonnullOwnPtr<CalcParsing::SumNode> const& sum_node) -> OwnPtr<CalculationNode> {
|
||||
[this, &context](NonnullOwnPtr<CalcParsing::SumNode> const& sum_node) -> OwnPtr<CalculationNode> {
|
||||
Vector<NonnullOwnPtr<CalculationNode>> children;
|
||||
children.ensure_capacity(sum_node->children.size());
|
||||
|
||||
for (auto const& child : sum_node->children) {
|
||||
if (auto child_as_node = convert_to_calculation_node(child)) {
|
||||
if (auto child_as_node = convert_to_calculation_node(child, context)) {
|
||||
children.append(child_as_node.release_nonnull());
|
||||
} else {
|
||||
return nullptr;
|
||||
|
@ -9262,24 +9285,24 @@ OwnPtr<CalculationNode> Parser::convert_to_calculation_node(CalcParsing::Node co
|
|||
|
||||
return SumCalculationNode::create(move(children));
|
||||
},
|
||||
[this](NonnullOwnPtr<CalcParsing::InvertNode> const& invert_node) -> OwnPtr<CalculationNode> {
|
||||
if (auto child_as_node = convert_to_calculation_node(invert_node->child))
|
||||
[this, &context](NonnullOwnPtr<CalcParsing::InvertNode> const& invert_node) -> OwnPtr<CalculationNode> {
|
||||
if (auto child_as_node = convert_to_calculation_node(invert_node->child, context))
|
||||
return InvertCalculationNode::create(child_as_node.release_nonnull());
|
||||
return nullptr;
|
||||
},
|
||||
[this](NonnullOwnPtr<CalcParsing::NegateNode> const& negate_node) -> OwnPtr<CalculationNode> {
|
||||
if (auto child_as_node = convert_to_calculation_node(negate_node->child))
|
||||
[this, &context](NonnullOwnPtr<CalcParsing::NegateNode> const& negate_node) -> OwnPtr<CalculationNode> {
|
||||
if (auto child_as_node = convert_to_calculation_node(negate_node->child, context))
|
||||
return NegateCalculationNode::create(child_as_node.release_nonnull());
|
||||
return nullptr;
|
||||
},
|
||||
[this](NonnullRawPtr<ComponentValue const> const& component_value) -> OwnPtr<CalculationNode> {
|
||||
[this, &context](NonnullRawPtr<ComponentValue const> const& component_value) -> OwnPtr<CalculationNode> {
|
||||
// NOTE: This is the "process the leaf nodes" part of step 5 of https://drafts.csswg.org/css-values-4/#parse-a-calculation
|
||||
// We divert a little from the spec: Rather than modify an existing tree of values, we construct a new one from that source tree.
|
||||
// This lets us make CalculationNodes immutable.
|
||||
|
||||
// 1. If leaf is a parenthesized simple block, replace leaf with the result of parsing a calculation from leaf’s contents.
|
||||
if (component_value->is_block() && component_value->block().is_paren()) {
|
||||
auto leaf_calculation = parse_a_calculation(component_value->block().value);
|
||||
auto leaf_calculation = parse_a_calculation(component_value->block().value, context);
|
||||
if (!leaf_calculation)
|
||||
return nullptr;
|
||||
|
||||
|
@ -9290,7 +9313,7 @@ OwnPtr<CalculationNode> Parser::convert_to_calculation_node(CalcParsing::Node co
|
|||
// NOTE: All function tokens at this point should be math functions.
|
||||
if (component_value->is_function()) {
|
||||
auto const& function = component_value->function();
|
||||
auto leaf_calculation = parse_a_calc_function_node(function);
|
||||
auto leaf_calculation = parse_a_calc_function_node(function, context);
|
||||
if (!leaf_calculation)
|
||||
return nullptr;
|
||||
|
||||
|
@ -9307,17 +9330,17 @@ OwnPtr<CalculationNode> Parser::convert_to_calculation_node(CalcParsing::Node co
|
|||
}
|
||||
|
||||
if (component_value->is(Token::Type::Number))
|
||||
return NumericCalculationNode::create(component_value->token().number());
|
||||
return NumericCalculationNode::create(component_value->token().number(), context);
|
||||
|
||||
if (component_value->is(Token::Type::Dimension)) {
|
||||
auto numeric_value = component_value->token().dimension_value();
|
||||
auto unit_string = component_value->token().dimension_unit();
|
||||
|
||||
if (auto length_type = Length::unit_from_name(unit_string); length_type.has_value())
|
||||
return NumericCalculationNode::create(Length { numeric_value, length_type.release_value() });
|
||||
return NumericCalculationNode::create(Length { numeric_value, length_type.release_value() }, context);
|
||||
|
||||
if (auto angle_type = Angle::unit_from_name(unit_string); angle_type.has_value())
|
||||
return NumericCalculationNode::create(Angle { numeric_value, angle_type.release_value() });
|
||||
return NumericCalculationNode::create(Angle { numeric_value, angle_type.release_value() }, context);
|
||||
|
||||
if (auto flex_type = Flex::unit_from_name(unit_string); flex_type.has_value()) {
|
||||
// https://www.w3.org/TR/css3-grid-layout/#fr-unit
|
||||
|
@ -9329,34 +9352,20 @@ OwnPtr<CalculationNode> Parser::convert_to_calculation_node(CalcParsing::Node co
|
|||
}
|
||||
|
||||
if (auto frequency_type = Frequency::unit_from_name(unit_string); frequency_type.has_value())
|
||||
return NumericCalculationNode::create(Frequency { numeric_value, frequency_type.release_value() });
|
||||
return NumericCalculationNode::create(Frequency { numeric_value, frequency_type.release_value() }, context);
|
||||
|
||||
if (auto resolution_type = Resolution::unit_from_name(unit_string); resolution_type.has_value())
|
||||
return NumericCalculationNode::create(Resolution { numeric_value, resolution_type.release_value() });
|
||||
return NumericCalculationNode::create(Resolution { numeric_value, resolution_type.release_value() }, context);
|
||||
|
||||
if (auto time_type = Time::unit_from_name(unit_string); time_type.has_value())
|
||||
return NumericCalculationNode::create(Time { numeric_value, time_type.release_value() });
|
||||
return NumericCalculationNode::create(Time { numeric_value, time_type.release_value() }, context);
|
||||
|
||||
dbgln_if(CSS_PARSER_DEBUG, "Unrecognized dimension type in calc() expression: {}", component_value->to_string());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (component_value->is(Token::Type::Percentage)) {
|
||||
Optional<ValueType> percentage_resolved_type;
|
||||
for (auto const& value_context : m_value_context.in_reverse()) {
|
||||
percentage_resolved_type = value_context.visit(
|
||||
[](PropertyID property_id) -> Optional<ValueType> {
|
||||
return property_resolves_percentages_relative_to(property_id);
|
||||
},
|
||||
[](FunctionContext const&) -> Optional<ValueType> {
|
||||
// FIXME: Some functions provide this. The spec mentions `media-progress()` as an example.
|
||||
return {};
|
||||
});
|
||||
if (percentage_resolved_type.has_value())
|
||||
break;
|
||||
}
|
||||
return NumericCalculationNode::create(Percentage { component_value->token().percentage() }, percentage_resolved_type);
|
||||
}
|
||||
if (component_value->is(Token::Type::Percentage))
|
||||
return NumericCalculationNode::create(Percentage { component_value->token().percentage() }, context);
|
||||
|
||||
// NOTE: If we get here, then we have a ComponentValue that didn't get replaced with something else,
|
||||
// so the calc() is invalid.
|
||||
|
@ -9370,7 +9379,7 @@ OwnPtr<CalculationNode> Parser::convert_to_calculation_node(CalcParsing::Node co
|
|||
}
|
||||
|
||||
// https://drafts.csswg.org/css-values-4/#parse-a-calculation
|
||||
OwnPtr<CalculationNode> Parser::parse_a_calculation(Vector<ComponentValue> const& original_values)
|
||||
OwnPtr<CalculationNode> Parser::parse_a_calculation(Vector<ComponentValue> const& original_values, CalculationContext const& context)
|
||||
{
|
||||
// 1. Discard any <whitespace-token>s from values.
|
||||
// 2. An item in values is an “operator” if it’s a <delim-token> with the value "+", "-", "*", or "/". Otherwise, it’s a “value”.
|
||||
|
@ -9483,7 +9492,7 @@ OwnPtr<CalculationNode> Parser::parse_a_calculation(Vector<ComponentValue> const
|
|||
|
||||
// 5. At this point values is a tree of Sum, Product, Negate, and Invert nodes, with other types of values at the leaf nodes. Process the leaf nodes.
|
||||
// NOTE: We process leaf nodes as part of this conversion.
|
||||
auto calculation_tree = convert_to_calculation_node(*single_value);
|
||||
auto calculation_tree = convert_to_calculation_node(*single_value, context);
|
||||
if (!calculation_tree)
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -276,8 +276,8 @@ private:
|
|||
RefPtr<CalculatedStyleValue> parse_calculated_value(ComponentValue const&);
|
||||
RefPtr<CustomIdentStyleValue> parse_custom_ident_value(TokenStream<ComponentValue>&, std::initializer_list<StringView> blacklist);
|
||||
// NOTE: Implemented in generated code. (GenerateCSSMathFunctions.cpp)
|
||||
OwnPtr<CalculationNode> parse_math_function(Function const&);
|
||||
OwnPtr<CalculationNode> parse_a_calc_function_node(Function const&);
|
||||
OwnPtr<CalculationNode> parse_math_function(Function const&, CalculationContext const&);
|
||||
OwnPtr<CalculationNode> parse_a_calc_function_node(Function const&, CalculationContext const&);
|
||||
RefPtr<CSSStyleValue> parse_keyword_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<CSSStyleValue> parse_hue_none_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<CSSStyleValue> parse_solidus_and_alpha_value(TokenStream<ComponentValue>&);
|
||||
|
@ -396,8 +396,8 @@ private:
|
|||
RefPtr<CSSStyleValue> parse_grid_area_shorthand_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<CSSStyleValue> parse_grid_shorthand_value(TokenStream<ComponentValue>&);
|
||||
|
||||
OwnPtr<CalculationNode> convert_to_calculation_node(CalcParsing::Node const&);
|
||||
OwnPtr<CalculationNode> parse_a_calculation(Vector<ComponentValue> const&);
|
||||
OwnPtr<CalculationNode> convert_to_calculation_node(CalcParsing::Node const&, CalculationContext const&);
|
||||
OwnPtr<CalculationNode> parse_a_calculation(Vector<ComponentValue> const&, CalculationContext const&);
|
||||
|
||||
ParseErrorOr<NonnullRefPtr<Selector>> parse_complex_selector(TokenStream<ComponentValue>&, SelectorType);
|
||||
ParseErrorOr<Optional<Selector::CompoundSelector>> parse_compound_selector(TokenStream<ComponentValue>&);
|
||||
|
|
|
@ -109,7 +109,7 @@ CalculationNode::CalculationNode(Type type, Optional<CSSNumericType> numeric_typ
|
|||
|
||||
CalculationNode::~CalculationNode() = default;
|
||||
|
||||
static CSSNumericType numeric_type_from_calculated_style_value(CalculatedStyleValue::CalculationResult::Value const& value, Optional<ValueType> percentage_resolved_type)
|
||||
static CSSNumericType numeric_type_from_calculated_style_value(CalculatedStyleValue::CalculationResult::Value const& value, CalculationContext const& context)
|
||||
{
|
||||
// https://drafts.csswg.org/css-values-4/#determine-the-type-of-a-calculation
|
||||
// Anything else is a terminal value, whose type is determined based on its CSS type.
|
||||
|
@ -152,14 +152,14 @@ static CSSNumericType numeric_type_from_calculated_style_value(CalculatedStyleVa
|
|||
return CSSNumericType { CSSNumericType::BaseType::Flex, 1 };
|
||||
},
|
||||
// NOTE: <calc-constant> is a separate node type. (FIXME: Should it be?)
|
||||
[&percentage_resolved_type](Percentage const&) {
|
||||
[&context](Percentage const&) {
|
||||
// -> <percentage>
|
||||
// If, in the context in which the math function containing this calculation is placed,
|
||||
// <percentage>s are resolved relative to another type of value (such as in width,
|
||||
// where <percentage> is resolved against a <length>), and that other type is not <number>,
|
||||
// the type is determined as the other type, but with a percent hint set to that other type.
|
||||
if (percentage_resolved_type.has_value() && percentage_resolved_type != ValueType::Number && percentage_resolved_type != ValueType::Percentage) {
|
||||
auto base_type = CSSNumericType::base_type_from_value_type(*percentage_resolved_type);
|
||||
if (context.percentages_resolve_as.has_value() && context.percentages_resolve_as != ValueType::Number && context.percentages_resolve_as != ValueType::Percentage) {
|
||||
auto base_type = CSSNumericType::base_type_from_value_type(*context.percentages_resolve_as);
|
||||
VERIFY(base_type.has_value());
|
||||
auto result = CSSNumericType { base_type.value(), 1 };
|
||||
result.set_percent_hint(base_type);
|
||||
|
@ -174,9 +174,9 @@ static CSSNumericType numeric_type_from_calculated_style_value(CalculatedStyleVa
|
|||
});
|
||||
}
|
||||
|
||||
NonnullOwnPtr<NumericCalculationNode> NumericCalculationNode::create(NumericValue value, Optional<ValueType> percentage_resolved_type)
|
||||
NonnullOwnPtr<NumericCalculationNode> NumericCalculationNode::create(NumericValue value, CalculationContext const& context)
|
||||
{
|
||||
auto numeric_type = numeric_type_from_calculated_style_value(value, percentage_resolved_type);
|
||||
auto numeric_type = numeric_type_from_calculated_style_value(value, context);
|
||||
return adopt_own(*new (nothrow) NumericCalculationNode(move(value), numeric_type));
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,11 @@ namespace Web::CSS {
|
|||
|
||||
class CalculationNode;
|
||||
|
||||
// https://drafts.csswg.org/css-values-4/#ref-for-calc-calculation%E2%91%A2%E2%91%A7
|
||||
struct CalculationContext {
|
||||
Optional<ValueType> percentages_resolve_as {};
|
||||
};
|
||||
|
||||
class CalculatedStyleValue : public CSSStyleValue {
|
||||
public:
|
||||
using PercentageBasis = Variant<Empty, Angle, Flex, Frequency, Length, Time>;
|
||||
|
@ -56,9 +61,9 @@ public:
|
|||
Optional<CSSNumericType> m_type;
|
||||
};
|
||||
|
||||
static ValueComparingNonnullRefPtr<CalculatedStyleValue> create(NonnullOwnPtr<CalculationNode> calculation, CSSNumericType resolved_type)
|
||||
static ValueComparingNonnullRefPtr<CalculatedStyleValue> create(NonnullOwnPtr<CalculationNode> calculation, CSSNumericType resolved_type, CalculationContext context)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) CalculatedStyleValue(move(calculation), resolved_type));
|
||||
return adopt_ref(*new (nothrow) CalculatedStyleValue(move(calculation), move(resolved_type), move(context)));
|
||||
}
|
||||
|
||||
virtual String to_string(SerializationMode) const override;
|
||||
|
@ -113,15 +118,17 @@ public:
|
|||
String dump() const;
|
||||
|
||||
private:
|
||||
explicit CalculatedStyleValue(NonnullOwnPtr<CalculationNode> calculation, CSSNumericType resolved_type)
|
||||
explicit CalculatedStyleValue(NonnullOwnPtr<CalculationNode> calculation, CSSNumericType resolved_type, CalculationContext context)
|
||||
: CSSStyleValue(Type::Calculated)
|
||||
, m_resolved_type(resolved_type)
|
||||
, m_resolved_type(move(resolved_type))
|
||||
, m_calculation(move(calculation))
|
||||
, m_context(move(context))
|
||||
{
|
||||
}
|
||||
|
||||
CSSNumericType m_resolved_type;
|
||||
NonnullOwnPtr<CalculationNode> m_calculation;
|
||||
CalculationContext m_context;
|
||||
};
|
||||
|
||||
// https://www.w3.org/TR/css-values-4/#calculation-tree
|
||||
|
@ -258,7 +265,7 @@ private:
|
|||
|
||||
class NumericCalculationNode final : public CalculationNode {
|
||||
public:
|
||||
static NonnullOwnPtr<NumericCalculationNode> create(NumericValue, Optional<ValueType> percentage_resolved_type = {});
|
||||
static NonnullOwnPtr<NumericCalculationNode> create(NumericValue, CalculationContext const&);
|
||||
~NumericCalculationNode();
|
||||
|
||||
virtual String to_string() const override;
|
||||
|
|
|
@ -16,15 +16,19 @@ String EdgeStyleValue::to_string(SerializationMode mode) const
|
|||
auto flipped_percentage = 100 - offset().percentage().value();
|
||||
return Percentage(flipped_percentage).to_string();
|
||||
}
|
||||
|
||||
// FIXME: Figure out how to get the proper calculation context here
|
||||
CalculationContext context = {};
|
||||
|
||||
Vector<NonnullOwnPtr<CalculationNode>> sum_parts;
|
||||
sum_parts.append(NumericCalculationNode::create(Percentage(100)));
|
||||
sum_parts.append(NumericCalculationNode::create(Percentage(100), context));
|
||||
if (offset().is_length()) {
|
||||
sum_parts.append(NegateCalculationNode::create(NumericCalculationNode::create(offset().length())));
|
||||
sum_parts.append(NegateCalculationNode::create(NumericCalculationNode::create(offset().length(), context)));
|
||||
} else {
|
||||
// FIXME: Flip calculated offsets (convert CalculatedStyleValue to CalculationNode, then negate and append)
|
||||
return to_string(CSSStyleValue::SerializationMode::Normal);
|
||||
}
|
||||
auto flipped_absolute = CalculatedStyleValue::create(SumCalculationNode::create(move(sum_parts)), CSSNumericType(CSSNumericType::BaseType::Length, 1));
|
||||
auto flipped_absolute = CalculatedStyleValue::create(SumCalculationNode::create(move(sum_parts)), CSSNumericType(CSSNumericType::BaseType::Length, 1), context);
|
||||
return flipped_absolute->to_string(mode);
|
||||
}
|
||||
return offset().to_string();
|
||||
|
|
|
@ -116,7 +116,7 @@ static Optional<RoundingStrategy> parse_rounding_strategy(Vector<ComponentValue>
|
|||
return keyword_to_rounding_strategy(maybe_keyword.value());
|
||||
}
|
||||
|
||||
OwnPtr<CalculationNode> Parser::parse_math_function(Function const& function)
|
||||
OwnPtr<CalculationNode> Parser::parse_math_function(Function const& function, CalculationContext const& context)
|
||||
{
|
||||
TokenStream stream { function.value };
|
||||
auto arguments = parse_a_comma_separated_list_of_component_values(stream);
|
||||
|
@ -140,7 +140,7 @@ OwnPtr<CalculationNode> Parser::parse_math_function(Function const& function)
|
|||
parsed_arguments.ensure_capacity(arguments.size());
|
||||
|
||||
for (auto& argument : arguments) {
|
||||
auto calculation_node = parse_a_calculation(argument);
|
||||
auto calculation_node = parse_a_calculation(argument, context);
|
||||
if (!calculation_node) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "@name:lowercase@() argument #{} is not a valid calculation", parsed_arguments.size());
|
||||
return nullptr;
|
||||
|
@ -243,7 +243,7 @@ OwnPtr<CalculationNode> Parser::parse_math_function(Function const& function)
|
|||
// NOTE: This assumes everything not handled above is a calculation node of some kind.
|
||||
parameter_is_calculation = true;
|
||||
parameter_generator.set("parameter_type", "OwnPtr<CalculationNode>"_string);
|
||||
parameter_generator.set("parse_function", "parse_a_calculation(arguments[argument_index])"_string);
|
||||
parameter_generator.set("parse_function", "parse_a_calculation(arguments[argument_index], context)"_string);
|
||||
parameter_generator.set("check_function", " != nullptr"_string);
|
||||
parameter_generator.set("release_function", ".release_nonnull()"_string);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue