Skip to content

Commit

Permalink
Implement linear timing function.
Browse files Browse the repository at this point in the history
https://w3c.github.io/csswg-drafts/css-easing/#funcdef-linear

Integration with DevTools editor will be in future CLs.

Change-Id: I6e0e8314248de92ff5de6ddae1e858d8ecb730fb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4245137
Reviewed-by: Philip Jägenstedt <foolip@chromium.org>
Commit-Queue: Daniil Sakhapov <sakhapov@chromium.org>
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1107373}
  • Loading branch information
danielsakhapov authored and Chromium LUCI CQ committed Feb 20, 2023
1 parent 99e88df commit ae1218c
Show file tree
Hide file tree
Showing 22 changed files with 475 additions and 60 deletions.
1 change: 1 addition & 0 deletions third_party/blink/renderer/core/DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ include_rules = [
"+ui/events/types/scroll_types.h",
"+ui/gfx/animation/keyframe/animation_curve.h",
"+ui/gfx/animation/keyframe/keyframed_animation_curve.h",
"+ui/gfx/animation/keyframe/timing_function.h",
"+ui/gfx/geometry",
"+ui/gfx/range/range.h",
"-web",
Expand Down
37 changes: 32 additions & 5 deletions third_party/blink/renderer/core/css/css_timing_function_value.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,39 @@
*/

#include "third_party/blink/renderer/core/css/css_timing_function_value.h"

#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"

namespace blink {
namespace cssvalue {
namespace blink::cssvalue {

String CSSLinearTimingFunctionValue::CustomCSSText() const {
WTF::StringBuilder builder;
builder.Append("linear(");
for (wtf_size_t i = 0; i < points_.size(); ++i) {
if (i != 0) {
builder.Append(", ");
}
builder.AppendNumber(points_[i].output);
builder.Append(" ");
builder.AppendNumber(points_[i].input);
builder.Append("%");
}
builder.Append(")");
return builder.ReleaseString();
}

bool CSSLinearTimingFunctionValue::Equals(
const CSSLinearTimingFunctionValue& other) const {
if (points_.size() != other.points_.size()) {
return false;
}
for (wtf_size_t i = 0; i < points_.size(); ++i) {
if (points_[i] != other.points_[i]) {
return false;
}
}
return true;
}

String CSSCubicBezierTimingFunctionValue::CustomCSSText() const {
return "cubic-bezier(" + String::Number(x1_) + ", " + String::Number(y1_) +
Expand Down Expand Up @@ -83,5 +111,4 @@ bool CSSStepsTimingFunctionValue::Equals(
return steps_ == other.steps_ && step_position_ == other.step_position_;
}

} // namespace cssvalue
} // namespace blink
} // namespace blink::cssvalue
36 changes: 36 additions & 0 deletions third_party/blink/renderer/core/css/css_timing_function_value.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,42 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_TIMING_FUNCTION_VALUE_H_

#include "base/memory/scoped_refptr.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/css/css_value.h"
#include "third_party/blink/renderer/platform/animation/timing_function.h"
#include "third_party/blink/renderer/platform/wtf/casting.h"
#include "ui/gfx/animation/keyframe/timing_function.h"

namespace blink {
namespace cssvalue {

struct CSSLinearStop {
double number;
absl::optional<double> length_a;
absl::optional<double> length_b;
};

class CSSLinearTimingFunctionValue : public CSSValue {
public:
explicit CSSLinearTimingFunctionValue(Vector<gfx::LinearEasingPoint> points)
: CSSValue(kLinearTimingFunctionClass), points_(std::move(points)) {}
explicit CSSLinearTimingFunctionValue(
const std::vector<gfx::LinearEasingPoint>& points)
: CSSValue(kLinearTimingFunctionClass), points_(points) {}

String CustomCSSText() const;
const Vector<gfx::LinearEasingPoint>& Points() const { return points_; }

bool Equals(const CSSLinearTimingFunctionValue&) const;

void TraceAfterDispatch(blink::Visitor* visitor) const {
CSSValue::TraceAfterDispatch(visitor);
}

private:
Vector<gfx::LinearEasingPoint> points_;
};

class CSSCubicBezierTimingFunctionValue : public CSSValue {
public:
CSSCubicBezierTimingFunctionValue(double x1, double y1, double x2, double y2)
Expand Down Expand Up @@ -91,6 +120,13 @@ class CSSStepsTimingFunctionValue : public CSSValue {

} // namespace cssvalue

template <>
struct DowncastTraits<cssvalue::CSSLinearTimingFunctionValue> {
static bool AllowFrom(const CSSValue& value) {
return value.IsLinearTimingFunctionValue();
}
};

template <>
struct DowncastTraits<cssvalue::CSSCubicBezierTimingFunctionValue> {
static bool AllowFrom(const CSSValue& value) {
Expand Down
9 changes: 9 additions & 0 deletions third_party/blink/renderer/core/css/css_value.cc
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,9 @@ bool CSSValue::operator==(const CSSValue& other) const {
return CompareCSSValues<CSSShadowValue>(*this, other);
case kStringClass:
return CompareCSSValues<CSSStringValue>(*this, other);
case kLinearTimingFunctionClass:
return CompareCSSValues<cssvalue::CSSLinearTimingFunctionValue>(*this,
other);
case kCubicBezierTimingFunctionClass:
return CompareCSSValues<cssvalue::CSSCubicBezierTimingFunctionValue>(
*this, other);
Expand Down Expand Up @@ -417,6 +420,8 @@ String CSSValue::CssText() const {
return To<CSSShadowValue>(this)->CustomCSSText();
case kStringClass:
return To<CSSStringValue>(this)->CustomCSSText();
case kLinearTimingFunctionClass:
return To<cssvalue::CSSLinearTimingFunctionValue>(this)->CustomCSSText();
case kCubicBezierTimingFunctionClass:
return To<cssvalue::CSSCubicBezierTimingFunctionValue>(this)
->CustomCSSText();
Expand Down Expand Up @@ -624,6 +629,10 @@ void CSSValue::Trace(Visitor* visitor) const {
case kStringClass:
To<CSSStringValue>(this)->TraceAfterDispatch(visitor);
return;
case kLinearTimingFunctionClass:
To<cssvalue::CSSLinearTimingFunctionValue>(this)->TraceAfterDispatch(
visitor);
return;
case kCubicBezierTimingFunctionClass:
To<cssvalue::CSSCubicBezierTimingFunctionValue>(this)->TraceAfterDispatch(
visitor);
Expand Down
4 changes: 4 additions & 0 deletions third_party/blink/renderer/core/css/css_value.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ class CORE_EXPORT CSSValue : public GarbageCollected<CSSValue> {
bool IsShadowValue() const { return class_type_ == kShadowClass; }
bool IsStringValue() const { return class_type_ == kStringClass; }
bool IsURIValue() const { return class_type_ == kURIClass; }
bool IsLinearTimingFunctionValue() const {
return class_type_ == kLinearTimingFunctionClass;
}
bool IsCubicBezierTimingFunctionValue() const {
return class_type_ == kCubicBezierTimingFunctionClass;
}
Expand Down Expand Up @@ -248,6 +251,7 @@ class CORE_EXPORT CSSValue : public GarbageCollected<CSSValue> {
kConicGradientClass,

// Timing function classes.
kLinearTimingFunctionClass,
kCubicBezierTimingFunctionClass,
kStepsTimingFunctionClass,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
#include "third_party/blink/renderer/core/style/style_svg_resource.h"
#include "third_party/blink/renderer/core/style_property_shorthand.h"
#include "third_party/blink/renderer/core/svg_element_type_helpers.h"
#include "third_party/blink/renderer/platform/animation/timing_function.h"
#include "third_party/blink/renderer/platform/fonts/font_optical_sizing.h"
#include "third_party/blink/renderer/platform/fonts/opentype/font_settings.h"
#include "third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h"
Expand Down Expand Up @@ -2225,7 +2226,13 @@ CSSValue* ComputedStyleUtils::ValueForAnimationTimingFunction(
}

default:
return CSSIdentifierValue::Create(CSSValueID::kLinear);
const auto* linear_timing_function =
To<LinearTimingFunction>(timing_function.get());
if (linear_timing_function->IsTrivial()) {
return CSSIdentifierValue::Create(CSSValueID::kLinear);
}
return MakeGarbageCollected<cssvalue::CSSLinearTimingFunctionValue>(
linear_timing_function->Points());
}
}

Expand Down
131 changes: 131 additions & 0 deletions third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "ui/gfx/animation/keyframe/timing_function.h"
#include "ui/gfx/color_utils.h"

namespace blink {
Expand Down Expand Up @@ -155,6 +156,132 @@ CSSValue* ConsumeBaseline(CSSParserTokenRange& range) {
return baseline;
}

absl::optional<cssvalue::CSSLinearStop> ConsumeLinearStop(
CSSParserTokenRange& range,
const CSSParserContext& context) {
absl::optional<double> number;
absl::optional<double> length_a;
absl::optional<double> length_b;
while (!range.AtEnd()) {
if (range.Peek().GetType() == kCommaToken) {
break;
}
CSSPrimitiveValue* value =
ConsumeNumber(range, context, CSSPrimitiveValue::ValueRange::kAll);
if (!number.has_value() && value && value->IsNumber()) {
number = value->GetDoubleValue();
continue;
}
value = ConsumePercent(range, context, CSSPrimitiveValue::ValueRange::kAll);
if (!length_a.has_value() && value && value->IsPercentage()) {
length_a = value->GetDoubleValue();
value =
ConsumePercent(range, context, CSSPrimitiveValue::ValueRange::kAll);
if (value && value->IsPercentage()) {
length_b = value->GetDoubleValue();
}
continue;
}
return {};
}
if (!number.has_value()) {
return {};
}
return {{number.value(), length_a, length_b}};
}

CSSValue* ConsumeLinear(CSSParserTokenRange& range,
const CSSParserContext& context) {
// https://w3c.github.io/csswg-drafts/css-easing/#linear-easing-function-parsing
DCHECK_EQ(range.Peek().FunctionId(), CSSValueID::kLinear);
CSSParserTokenRange range_copy = range;
CSSParserTokenRange args = ConsumeFunction(range_copy);
Vector<cssvalue::CSSLinearStop> stop_list{};
absl::optional<cssvalue::CSSLinearStop> linear_stop;
do {
linear_stop = ConsumeLinearStop(args, context);
if (!linear_stop.has_value()) {
return nullptr;
}
stop_list.emplace_back(linear_stop.value());
} while (ConsumeCommaIncludingWhitespace(args));
if (!args.AtEnd()) {
return nullptr;
}
// 1. Let function be a new linear easing function.
// 2. Let largestInput be negative infinity.
// 3. If there are less than two items in stopList, then return failure.
if (stop_list.size() < 2) {
return nullptr;
}
// 4. For each stop in stopList:
double largest_input = std::numeric_limits<double>::lowest();
Vector<gfx::LinearEasingPoint> points{};
for (wtf_size_t i = 0; i < stop_list.size(); ++i) {
const auto& stop = stop_list[i];
// 4.1. Let point be a new linear easing point with its output set
// to stop’s <number> as a number.
gfx::LinearEasingPoint point{std::numeric_limits<double>::quiet_NaN(),
stop.number};
// 4.2. Append point to function’s points.
points.emplace_back(point);
// 4.3. If stop has a <linear-stop-length>, then:
if (stop.length_a.has_value()) {
// 4.3.1. Set point’s input to whichever is greater:
// stop’s <linear-stop-length>'s first <percentage> as a number,
// or largestInput.
points.back().input = std::max(largest_input, stop.length_a.value());
// 4.3.2. Set largestInput to point’s input.
largest_input = points.back().input;
// 4.3.3. If stop’s <linear-stop-length> has a second <percentage>, then:
if (stop.length_b.has_value()) {
// 4.3.3.1. Let extraPoint be a new linear easing point with its output
// set to stop’s <number> as a number.
gfx::LinearEasingPoint extra_point{
// 4.3.3.3. Set extraPoint’s input to whichever is greater:
// stop’s <linear-stop-length>'s second <percentage>
// as a number, or largestInput.
std::max(largest_input, stop.length_b.value()), stop.number};
// 4.3.3.2. Append extraPoint to function’s points.
points.emplace_back(extra_point);
// 4.3.3.4. Set largestInput to extraPoint’s input.
largest_input = extra_point.input;
}
// 4.4. Otherwise, if stop is the first item in stopList, then:
} else if (i == 0) {
// 4.4.1. Set point’s input to 0.
points.back().input = 0;
// 4.4.2. Set largestInput to 0.
largest_input = 0;
// 4.5. Otherwise, if stop is the last item in stopList,
// then set point’s input to whichever is greater: 1 or largestInput.
} else if (i == stop_list.size() - 1) {
points.back().input = std::max(100., largest_input);
}
}
// 5. For runs of items in function’s points that have a null input, assign a
// number to the input by linearly interpolating between the closest previous
// and next points that have a non-null input.
wtf_size_t upper_index = 0;
for (wtf_size_t i = 1; i < points.size(); ++i) {
if (std::isnan(points[i].input)) {
if (i > upper_index) {
const auto* it = std::find_if(
std::next(points.begin(), i + 1), points.end(),
[](const auto& point) { return !std::isnan(point.input); });
upper_index = static_cast<wtf_size_t>(it - points.begin());
}
points[i].input = points[i - 1].input +
(points[upper_index].input - points[i - 1].input) /
(upper_index - (i - 1));
}
}
range = range_copy;
// 6. Return function.
return MakeGarbageCollected<cssvalue::CSSLinearTimingFunctionValue>(
std::move(points));
}

CSSValue* ConsumeSteps(CSSParserTokenRange& range,
const CSSParserContext& context) {
DCHECK_EQ(range.Peek().FunctionId(), CSSValueID::kSteps);
Expand Down Expand Up @@ -4077,6 +4204,10 @@ CSSValue* ConsumeAnimationTimingFunction(CSSParserTokenRange& range,
}

CSSValueID function = range.Peek().FunctionId();
if (function == CSSValueID::kLinear &&
RuntimeEnabledFeatures::CSSLinearTimingFunctionEnabled()) {
return ConsumeLinear(range, context);
}
if (function == CSSValueID::kSteps) {
return ConsumeSteps(range, context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "third_party/blink/renderer/core/style/border_image_length_box.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/style/fill_layer.h"
#include "third_party/blink/renderer/platform/animation/timing_function.h"

namespace blink {

Expand Down Expand Up @@ -571,6 +572,11 @@ scoped_refptr<TimingFunction> CSSToStyleMap::MapAnimationTimingFunction(
}
}

if (const auto* linear_timing_function =
DynamicTo<cssvalue::CSSLinearTimingFunctionValue>(value)) {
return LinearTimingFunction::Create(linear_timing_function->Points());
}

if (const auto* cubic_timing_function =
DynamicTo<cssvalue::CSSCubicBezierTimingFunctionValue>(value)) {
return CubicBezierTimingFunction::Create(
Expand Down

0 comments on commit ae1218c

Please sign in to comment.