mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
LibWeb: Support relative lengths within easing function calc()s
This commit is contained in:
parent
ad41f053b8
commit
755a576013
Notes:
github-actions[bot]
2025-10-20 10:29:08 +00:00
Author: https://github.com/Calme1709
Commit: 755a576013
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6459
Reviewed-by: https://github.com/AtkinsSJ ✅
6 changed files with 63 additions and 47 deletions
|
|
@ -7,6 +7,7 @@
|
|||
#include "EasingFunction.h"
|
||||
#include <AK/BinarySearch.h>
|
||||
#include <LibWeb/CSS/StyleValues/EasingStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/IntegerStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
|
||||
|
||||
|
|
@ -289,6 +290,16 @@ EasingFunction EasingFunction::from_style_value(StyleValue const& style_value)
|
|||
VERIFY_NOT_REACHED();
|
||||
};
|
||||
|
||||
auto const resolve_integer = [](StyleValue const& style_value) {
|
||||
if (style_value.is_integer())
|
||||
return style_value.as_integer().integer();
|
||||
|
||||
if (style_value.is_calculated())
|
||||
return style_value.as_calculated().resolve_integer({}).value();
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
};
|
||||
|
||||
if (style_value.is_easing()) {
|
||||
return style_value.as_easing().function().visit(
|
||||
[&](EasingStyleValue::Linear const& linear) -> EasingFunction {
|
||||
|
|
@ -311,18 +322,16 @@ EasingFunction EasingFunction::from_style_value(StyleValue const& style_value)
|
|||
|
||||
return LinearEasingFunction { resolved_control_points, linear.to_string(SerializationMode::ResolvedValue) };
|
||||
},
|
||||
[](EasingStyleValue::CubicBezier const& cubic_bezier) -> EasingFunction {
|
||||
auto resolved_x1 = cubic_bezier.x1.resolved({}).value_or(0.0);
|
||||
auto resolved_y1 = cubic_bezier.y1.resolved({}).value_or(0.0);
|
||||
auto resolved_x2 = cubic_bezier.x2.resolved({}).value_or(0.0);
|
||||
auto resolved_y2 = cubic_bezier.y2.resolved({}).value_or(0.0);
|
||||
[&](EasingStyleValue::CubicBezier const& cubic_bezier) -> EasingFunction {
|
||||
auto resolved_x1 = resolve_number(cubic_bezier.x1);
|
||||
auto resolved_y1 = resolve_number(cubic_bezier.y1);
|
||||
auto resolved_x2 = resolve_number(cubic_bezier.x2);
|
||||
auto resolved_y2 = resolve_number(cubic_bezier.y2);
|
||||
|
||||
return CubicBezierEasingFunction { resolved_x1, resolved_y1, resolved_x2, resolved_y2, cubic_bezier.to_string(SerializationMode::Normal) };
|
||||
},
|
||||
[](EasingStyleValue::Steps const& steps) -> EasingFunction {
|
||||
auto resolved_interval_count = steps.number_of_intervals.resolved({}).value_or(1);
|
||||
|
||||
return StepsEasingFunction { resolved_interval_count, steps.position, steps.to_string(SerializationMode::ResolvedValue) };
|
||||
[&](EasingStyleValue::Steps const& steps) -> EasingFunction {
|
||||
return StepsEasingFunction { resolve_integer(steps.number_of_intervals), steps.position, steps.to_string(SerializationMode::ResolvedValue) };
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2954,7 +2954,7 @@ RefPtr<StyleValue const> Parser::parse_easing_value(TokenStream<ComponentValue>&
|
|||
|
||||
auto parse_argument = [this, &comma_separated_arguments](auto index) {
|
||||
TokenStream<ComponentValue> argument_tokens { comma_separated_arguments[index] };
|
||||
return parse_number(argument_tokens);
|
||||
return parse_number_value(argument_tokens);
|
||||
};
|
||||
|
||||
m_value_context.append(SpecialContext::CubicBezierFunctionXCoordinate);
|
||||
|
|
@ -2964,18 +2964,18 @@ RefPtr<StyleValue const> Parser::parse_easing_value(TokenStream<ComponentValue>&
|
|||
|
||||
auto y1 = parse_argument(1);
|
||||
auto y2 = parse_argument(3);
|
||||
if (!x1.has_value() || !y1.has_value() || !x2.has_value() || !y2.has_value())
|
||||
if (!x1 || !y1 || !x2 || !y2)
|
||||
return nullptr;
|
||||
if (!x1->is_calculated() && (x1->value() < 0.0 || x1->value() > 1.0))
|
||||
if (x1->is_number() && (x1->as_number().number() < 0.0 || x1->as_number().number() > 1.0))
|
||||
return nullptr;
|
||||
if (!x2->is_calculated() && (x2->value() < 0.0 || x2->value() > 1.0))
|
||||
if (x2->is_number() && (x2->as_number().number() < 0.0 || x2->as_number().number() > 1.0))
|
||||
return nullptr;
|
||||
|
||||
EasingStyleValue::CubicBezier bezier {
|
||||
x1.release_value(),
|
||||
y1.release_value(),
|
||||
x2.release_value(),
|
||||
y2.release_value(),
|
||||
x1.release_nonnull(),
|
||||
y1.release_nonnull(),
|
||||
x2.release_nonnull(),
|
||||
y2.release_nonnull(),
|
||||
};
|
||||
|
||||
transaction.commit();
|
||||
|
|
@ -3018,26 +3018,26 @@ RefPtr<StyleValue const> Parser::parse_easing_value(TokenStream<ComponentValue>&
|
|||
auto const& intervals_argument = comma_separated_arguments[0][0];
|
||||
auto intervals_token = TokenStream<ComponentValue>::of_single_token(intervals_argument);
|
||||
m_value_context.append(position == StepPosition::JumpNone ? SpecialContext::StepsIntervalsJumpNone : SpecialContext::StepsIntervalsNormal);
|
||||
auto intervals = parse_integer(intervals_token);
|
||||
auto intervals = parse_integer_value(intervals_token);
|
||||
m_value_context.take_last();
|
||||
if (!intervals.has_value())
|
||||
if (!intervals)
|
||||
return nullptr;
|
||||
|
||||
// Perform extra validation
|
||||
// https://drafts.csswg.org/css-easing/#step-easing-functions
|
||||
// If the <step-position> is jump-none, the <integer> must be at least 2, or the function is invalid.
|
||||
// Otherwise, the <integer> must be at least 1, or the function is invalid.
|
||||
if (!intervals->is_calculated()) {
|
||||
if (intervals->is_integer()) {
|
||||
if (position == StepPosition::JumpNone) {
|
||||
if (intervals->value() <= 1)
|
||||
if (intervals->as_integer().integer() <= 1)
|
||||
return nullptr;
|
||||
} else if (intervals->value() <= 0) {
|
||||
} else if (intervals->as_integer().integer() <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
return EasingStyleValue::create(EasingStyleValue::Steps { *intervals, position });
|
||||
return EasingStyleValue::create(EasingStyleValue::Steps { intervals.release_nonnull(), position });
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
|
|
|||
|
|
@ -27,37 +27,37 @@ EasingStyleValue::Linear EasingStyleValue::Linear::identity()
|
|||
|
||||
EasingStyleValue::CubicBezier EasingStyleValue::CubicBezier::ease()
|
||||
{
|
||||
static CubicBezier bezier { 0.25, 0.1, 0.25, 1.0 };
|
||||
static CubicBezier bezier { NumberStyleValue::create(0.25), NumberStyleValue::create(0.1), NumberStyleValue::create(0.25), NumberStyleValue::create(1.0) };
|
||||
return bezier;
|
||||
}
|
||||
|
||||
EasingStyleValue::CubicBezier EasingStyleValue::CubicBezier::ease_in()
|
||||
{
|
||||
static CubicBezier bezier { 0.42, 0.0, 1.0, 1.0 };
|
||||
static CubicBezier bezier { NumberStyleValue::create(0.42), NumberStyleValue::create(0.0), NumberStyleValue::create(1.0), NumberStyleValue::create(1.0) };
|
||||
return bezier;
|
||||
}
|
||||
|
||||
EasingStyleValue::CubicBezier EasingStyleValue::CubicBezier::ease_out()
|
||||
{
|
||||
static CubicBezier bezier { 0.0, 0.0, 0.58, 1.0 };
|
||||
static CubicBezier bezier { NumberStyleValue::create(0.0), NumberStyleValue::create(0.0), NumberStyleValue::create(0.58), NumberStyleValue::create(1.0) };
|
||||
return bezier;
|
||||
}
|
||||
|
||||
EasingStyleValue::CubicBezier EasingStyleValue::CubicBezier::ease_in_out()
|
||||
{
|
||||
static CubicBezier bezier { 0.42, 0.0, 0.58, 1.0 };
|
||||
static CubicBezier bezier { NumberStyleValue::create(0.42), NumberStyleValue::create(0.0), NumberStyleValue::create(0.58), NumberStyleValue::create(1.0) };
|
||||
return bezier;
|
||||
}
|
||||
|
||||
EasingStyleValue::Steps EasingStyleValue::Steps::step_start()
|
||||
{
|
||||
static Steps steps { 1, StepPosition::Start };
|
||||
static Steps steps { IntegerStyleValue::create(1), StepPosition::Start };
|
||||
return steps;
|
||||
}
|
||||
|
||||
EasingStyleValue::Steps EasingStyleValue::Steps::step_end()
|
||||
{
|
||||
static Steps steps { 1, StepPosition::End };
|
||||
static Steps steps { IntegerStyleValue::create(1), StepPosition::End };
|
||||
return steps;
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ String EasingStyleValue::CubicBezier::to_string(SerializationMode mode) const
|
|||
} else if (*this == CubicBezier::ease_in_out()) {
|
||||
builder.append("ease-in-out"sv);
|
||||
} else {
|
||||
builder.appendff("cubic-bezier({}, {}, {}, {})", x1.to_string(mode), y1.to_string(mode), x2.to_string(mode), y2.to_string(mode));
|
||||
builder.appendff("cubic-bezier({}, {}, {}, {})", x1->to_string(mode), y1->to_string(mode), x2->to_string(mode), y2->to_string(mode));
|
||||
}
|
||||
return MUST(builder.to_string());
|
||||
}
|
||||
|
|
@ -138,9 +138,9 @@ String EasingStyleValue::Steps::to_string(SerializationMode mode) const
|
|||
return CSS::to_string(this->position);
|
||||
}();
|
||||
if (position.has_value()) {
|
||||
builder.appendff("steps({}, {})", number_of_intervals.to_string(mode), position.value());
|
||||
builder.appendff("steps({}, {})", number_of_intervals->to_string(mode), position.value());
|
||||
} else {
|
||||
builder.appendff("steps({})", number_of_intervals.to_string(mode));
|
||||
builder.appendff("steps({})", number_of_intervals->to_string(mode));
|
||||
}
|
||||
}
|
||||
return MUST(builder.to_string());
|
||||
|
|
@ -172,10 +172,18 @@ ValueComparingNonnullRefPtr<StyleValue const> EasingStyleValue::absolutized(Comp
|
|||
return Linear { absolutized_stops };
|
||||
},
|
||||
[&](CubicBezier const& cubic_bezier) -> Function {
|
||||
return cubic_bezier;
|
||||
return CubicBezier {
|
||||
cubic_bezier.x1->absolutized(computation_context),
|
||||
cubic_bezier.y1->absolutized(computation_context),
|
||||
cubic_bezier.x2->absolutized(computation_context),
|
||||
cubic_bezier.y2->absolutized(computation_context)
|
||||
};
|
||||
},
|
||||
[&](Steps const& steps) -> Function {
|
||||
return steps;
|
||||
return Steps {
|
||||
steps.number_of_intervals->absolutized(computation_context),
|
||||
steps.position
|
||||
};
|
||||
});
|
||||
|
||||
return EasingStyleValue::create(absolutized_function);
|
||||
|
|
|
|||
|
|
@ -41,10 +41,10 @@ public:
|
|||
static CubicBezier ease_out();
|
||||
static CubicBezier ease_in_out();
|
||||
|
||||
NumberOrCalculated x1 { 0 };
|
||||
NumberOrCalculated y1 { 0 };
|
||||
NumberOrCalculated x2 { 0 };
|
||||
NumberOrCalculated y2 { 0 };
|
||||
ValueComparingNonnullRefPtr<StyleValue const> x1;
|
||||
ValueComparingNonnullRefPtr<StyleValue const> y1;
|
||||
ValueComparingNonnullRefPtr<StyleValue const> x2;
|
||||
ValueComparingNonnullRefPtr<StyleValue const> y2;
|
||||
|
||||
struct CachedSample {
|
||||
double x;
|
||||
|
|
@ -66,8 +66,8 @@ public:
|
|||
static Steps step_start();
|
||||
static Steps step_end();
|
||||
|
||||
IntegerOrCalculated number_of_intervals { 1 };
|
||||
StepPosition position { StepPosition::End };
|
||||
ValueComparingNonnullRefPtr<StyleValue const> number_of_intervals;
|
||||
StepPosition position;
|
||||
|
||||
bool operator==(Steps const&) const = default;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ Harness status: OK
|
|||
|
||||
Found 21 tests
|
||||
|
||||
20 Pass
|
||||
1 Fail
|
||||
21 Pass
|
||||
Pass Property animation-timing-function value 'linear'
|
||||
Pass Property animation-timing-function value 'ease'
|
||||
Pass Property animation-timing-function value 'ease-in'
|
||||
|
|
@ -24,4 +23,4 @@ Pass Property animation-timing-function value 'steps(calc(-10), start)'
|
|||
Pass Property animation-timing-function value 'steps(calc(5 / 2), start)'
|
||||
Pass Property animation-timing-function value 'steps(calc(1), jump-none)'
|
||||
Pass Property animation-timing-function value 'linear, ease, linear'
|
||||
Fail Property animation-timing-function value 'steps(calc(2 + sign(100em - 1px)), end)'
|
||||
Pass Property animation-timing-function value 'steps(calc(2 + sign(100em - 1px)), end)'
|
||||
|
|
@ -2,8 +2,8 @@ Harness status: OK
|
|||
|
||||
Found 22 tests
|
||||
|
||||
18 Pass
|
||||
4 Fail
|
||||
20 Pass
|
||||
2 Fail
|
||||
Pass Property transition-timing-function value 'linear'
|
||||
Pass Property transition-timing-function value 'ease'
|
||||
Pass Property transition-timing-function value 'ease-in'
|
||||
|
|
@ -23,6 +23,6 @@ Pass Property transition-timing-function value 'steps(2, jump-both)'
|
|||
Pass Property transition-timing-function value 'steps(2, jump-none)'
|
||||
Fail Property transition-timing-function value 'steps(calc(2 * sibling-index()), jump-none)'
|
||||
Fail Property transition-timing-function value 'steps(sibling-index(), jump-none)'
|
||||
Fail Property transition-timing-function value 'steps(calc(2 * sign(1em - 1000px)), jump-none)'
|
||||
Fail Property transition-timing-function value 'steps(calc(2 * sign(1em - 1000px)), start)'
|
||||
Pass Property transition-timing-function value 'steps(calc(2 * sign(1em - 1000px)), jump-none)'
|
||||
Pass Property transition-timing-function value 'steps(calc(2 * sign(1em - 1000px)), start)'
|
||||
Pass Property transition-timing-function value 'linear, ease, linear'
|
||||
Loading…
Add table
Add a link
Reference in a new issue