diff --git a/Libraries/LibWeb/Animations/AnimationEffect.cpp b/Libraries/LibWeb/Animations/AnimationEffect.cpp index 1850fdbfdba..819233627e6 100644 --- a/Libraries/LibWeb/Animations/AnimationEffect.cpp +++ b/Libraries/LibWeb/Animations/AnimationEffect.cpp @@ -607,6 +607,7 @@ Optional AnimationEffect::parse_easing_string(StringView va { if (auto style_value = parse_css_value(CSS::Parser::ParsingParams(), value, CSS::PropertyID::AnimationTimingFunction)) { if (style_value->is_easing()) + // FIXME: We should absolutize style_value to resolve relative lengths within calcs return CSS::EasingFunction::from_style_value(*style_value); } diff --git a/Libraries/LibWeb/CSS/EasingFunction.cpp b/Libraries/LibWeb/CSS/EasingFunction.cpp index b2bf2d8dc85..2e930f64f8b 100644 --- a/Libraries/LibWeb/CSS/EasingFunction.cpp +++ b/Libraries/LibWeb/CSS/EasingFunction.cpp @@ -7,6 +7,8 @@ #include "EasingFunction.h" #include #include +#include +#include namespace Web::CSS { @@ -267,13 +269,40 @@ static Vector canonicalize_linear_easing_fun EasingFunction EasingFunction::from_style_value(StyleValue const& style_value) { + auto const resolve_number = [](StyleValue const& style_value) { + if (style_value.is_number()) + return style_value.as_number().number(); + + if (style_value.is_calculated()) + return style_value.as_calculated().resolve_number({}).value(); + + VERIFY_NOT_REACHED(); + }; + + auto const resolve_percentage = [](StyleValue const& style_value) { + if (style_value.is_percentage()) + return style_value.as_percentage().percentage().as_fraction(); + + if (style_value.is_calculated()) + return style_value.as_calculated().resolve_percentage({})->as_fraction(); + + VERIFY_NOT_REACHED(); + }; + if (style_value.is_easing()) { return style_value.as_easing().function().visit( - [](EasingStyleValue::Linear const& linear) -> EasingFunction { + [&](EasingStyleValue::Linear const& linear) -> EasingFunction { Vector resolved_control_points; - for (auto const& control_point : linear.stops) - resolved_control_points.append({ control_point.input, control_point.output }); + for (auto const& control_point : linear.stops) { + double output = resolve_number(control_point.output); + + Optional input; + if (control_point.input) + input = resolve_percentage(*control_point.input); + + resolved_control_points.append({ input, output }); + } // https://drafts.csswg.org/css-easing-2/#funcdef-linear // If an argument lacks a , its input progress value is initially empty. This is corrected diff --git a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp index 451ef7ff5d4..d1e3d5362ca 100644 --- a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp @@ -2908,32 +2908,32 @@ RefPtr Parser::parse_easing_value(TokenStream& for (auto const& argument : comma_separated_arguments) { TokenStream argument_tokens { argument }; - Optional output; - Optional first_input; - Optional second_input; + RefPtr output; + RefPtr first_input; + RefPtr second_input; - if (argument_tokens.next_token().is(Token::Type::Number)) - output = argument_tokens.consume_a_token().token().number_value(); + if (auto maybe_output = parse_number_value(argument_tokens)) + output = maybe_output; - if (argument_tokens.next_token().is(Token::Type::Percentage)) { - first_input = argument_tokens.consume_a_token().token().percentage() / 100; - if (argument_tokens.next_token().is(Token::Type::Percentage)) { - second_input = argument_tokens.consume_a_token().token().percentage() / 100; + if (auto maybe_first_input = parse_percentage_value(argument_tokens)) { + first_input = maybe_first_input; + if (auto maybe_second_input = parse_percentage_value(argument_tokens)) { + second_input = maybe_second_input; } } - if (argument_tokens.next_token().is(Token::Type::Number)) { - if (output.has_value()) + if (auto maybe_output = parse_number_value(argument_tokens)) { + if (output) return nullptr; - output = argument_tokens.consume_a_token().token().number_value(); + output = maybe_output; } - if (argument_tokens.has_next_token() || !output.has_value()) + if (argument_tokens.has_next_token() || !output) return nullptr; - stops.append({ output.value(), first_input }); - if (second_input.has_value()) - stops.append({ output.value(), second_input }); + stops.append({ *output, first_input }); + if (second_input) + stops.append({ *output, second_input }); } if (stops.is_empty()) diff --git a/Libraries/LibWeb/CSS/StyleValues/EasingStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/EasingStyleValue.cpp index c084910de63..d89314e83c3 100644 --- a/Libraries/LibWeb/CSS/StyleValues/EasingStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/EasingStyleValue.cpp @@ -12,13 +12,14 @@ #include #include #include +#include namespace Web::CSS { // https://drafts.csswg.org/css-easing-1/#valdef-easing-function-linear EasingStyleValue::Linear EasingStyleValue::Linear::identity() { - static Linear linear { { { 0, {} }, { 1, {} } } }; + static Linear linear { { { NumberStyleValue::create(0), {} }, { NumberStyleValue::create(1), {} } } }; return linear; } @@ -61,7 +62,7 @@ EasingStyleValue::Steps EasingStyleValue::Steps::step_end() } // https://drafts.csswg.org/css-easing/#linear-easing-function-serializing -String EasingStyleValue::Linear::to_string(SerializationMode) const +String EasingStyleValue::Linear::to_string(SerializationMode mode) const { // The linear keyword is serialized as itself. if (*this == identity()) @@ -85,13 +86,13 @@ String EasingStyleValue::Linear::to_string(SerializationMode) const // To serialize a linear() control point: // 1. Let s be the serialization, as a , of the control point’s output progress value. - builder.appendff("{}", stop.output); + builder.appendff(stop.output->to_string(mode)); // 2. If the control point originally lacked an input progress value, return s. // 3. Otherwise, append " " (U+0020 SPACE) to s, // then serialize the control point’s input progress value as a and append it to s. - if (stop.input.has_value()) { - builder.appendff(" {}%", stop.input.value() * 100); + if (stop.input) { + builder.appendff(" {}", stop.input->to_string(mode)); } // 4. Return s. @@ -169,4 +170,31 @@ String EasingStyleValue::Function::to_string(SerializationMode mode) const }); } +ValueComparingNonnullRefPtr EasingStyleValue::absolutized(ComputationContext const& computation_context) const +{ + auto const& absolutized_function = m_function.visit( + [&](Linear const& linear) -> Function { + Vector absolutized_stops; + + for (auto stop : linear.stops) { + RefPtr absolutized_input; + + if (stop.input) + absolutized_input = stop.input->absolutized(computation_context); + + absolutized_stops.append({ stop.output->absolutized(computation_context), absolutized_input }); + } + + return Linear { absolutized_stops }; + }, + [&](CubicBezier const& cubic_bezier) -> Function { + return cubic_bezier; + }, + [&](Steps const& steps) -> Function { + return steps; + }); + + return EasingStyleValue::create(absolutized_function); +} + } diff --git a/Libraries/LibWeb/CSS/StyleValues/EasingStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/EasingStyleValue.h index 9910f364a8d..90d4bdb8959 100644 --- a/Libraries/LibWeb/CSS/StyleValues/EasingStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/EasingStyleValue.h @@ -22,8 +22,8 @@ public: static Linear identity(); struct Stop { - double output; - Optional input; + ValueComparingNonnullRefPtr output; + ValueComparingRefPtr input; bool operator==(Stop const&) const = default; }; @@ -90,6 +90,8 @@ public: virtual String to_string(SerializationMode mode) const override { return m_function.to_string(mode); } + virtual ValueComparingNonnullRefPtr absolutized(ComputationContext const&) const override; + bool properties_equal(EasingStyleValue const& other) const { return m_function == other.m_function; } private: diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-easing/linear-timing-functions-syntax.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-easing/linear-timing-functions-syntax.txt index b2e7e713534..6b32d27d202 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-easing/linear-timing-functions-syntax.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-easing/linear-timing-functions-syntax.txt @@ -2,16 +2,16 @@ Harness status: OK Found 35 tests -13 Pass -22 Fail +19 Pass +16 Fail Pass e.style['animation-timing-function'] = "linear(0 0%, 1 100%)" should set the property value Pass e.style['animation-timing-function'] = "linear( 0 0%, 1 100% )" should set the property value Fail e.style['animation-timing-function'] = "linear(0, 1)" should set the property value Pass e.style['animation-timing-function'] = "linear(-10, -5, 0, 5, 10)" should set the property value Pass e.style['animation-timing-function'] = "linear(-10 -10%, -5 -5%, 0, 5, 10)" should set the property value -Fail e.style['animation-timing-function'] = "linear(0 calc(0%), 0 calc(100%))" should set the property value -Fail e.style['animation-timing-function'] = "linear(0 calc(50% - 50%), 0 calc(50% + 50%))" should set the property value -Fail e.style['animation-timing-function'] = "linear(0 calc(50%), 0 100%)" should set the property value +Pass e.style['animation-timing-function'] = "linear(0 calc(0%), 0 calc(100%))" should set the property value +Pass e.style['animation-timing-function'] = "linear(0 calc(50% - 50%), 0 calc(50% + 50%))" should set the property value +Pass e.style['animation-timing-function'] = "linear(0 calc(50%), 0 100%)" should set the property value Fail e.style['animation-timing-function'] = "linear(0 0% 50%, 1 50% 100%)" should set the property value Fail e.style['animation-timing-function'] = "linear(0, 0.5 25% 75%, 1 100% 100%)" should set the property value Fail e.style['animation-timing-function'] = "linear(0, 1.3, 1, 0.92, 1, 0.99, 1, 0.998, 1 100% 100%)" should set the property value @@ -31,9 +31,9 @@ Pass Property animation-timing-function value 'linear( 0 0%, 1 100% )' Fail Property animation-timing-function value 'linear(0, 1)' Fail Property animation-timing-function value 'linear(-10, -5, 0, 5, 10)' Fail Property animation-timing-function value 'linear(-10 -10%, -5 -5%, 0, 5, 10)' -Fail Property animation-timing-function value 'linear(0 calc(0%), 0 calc(100%))' -Fail Property animation-timing-function value 'linear(0 calc(50% - 50%), 0 calc(50% + 50%))' -Fail Property animation-timing-function value 'linear(0 calc(min(50%, 60%)), 0 100%)' +Pass Property animation-timing-function value 'linear(0 calc(0%), 0 calc(100%))' +Pass Property animation-timing-function value 'linear(0 calc(50% - 50%), 0 calc(50% + 50%))' +Pass Property animation-timing-function value 'linear(0 calc(min(50%, 60%)), 0 100%)' Pass Property animation-timing-function value 'linear(0 0% 50%, 1 50% 100%)' Fail Property animation-timing-function value 'linear(0, 0.5 25% 75%, 1 100% 100%)' Fail Property animation-timing-function value 'linear(0, 1.3, 1, 0.92, 1, 0.99, 1, 0.998, 1 100% 100%)'