1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/animation/css_transform_interpolation_type.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/memory/ptr_util.h"
11 #include "third_party/blink/renderer/core/animation/interpolable_transform_list.h"
12 #include "third_party/blink/renderer/core/animation/length_units_checker.h"
13 #include "third_party/blink/renderer/core/css/css_function_value.h"
14 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
15 #include "third_party/blink/renderer/core/css/css_value_list.h"
16 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
17 #include "third_party/blink/renderer/core/css/resolver/transform_builder.h"
18 #include "third_party/blink/renderer/core/style/computed_style.h"
19 #include "third_party/blink/renderer/platform/transforms/transform_operations.h"
20 #include "third_party/blink/renderer/platform/transforms/translate_transform_operation.h"
21
22 namespace blink {
23 namespace {
ConvertTransform(TransformOperations && transform)24 InterpolationValue ConvertTransform(TransformOperations&& transform) {
25 return InterpolationValue(
26 InterpolableTransformList::Create(std::move(transform)));
27 }
28
ConvertTransform(const TransformOperations & transform)29 InterpolationValue ConvertTransform(const TransformOperations& transform) {
30 return ConvertTransform(TransformOperations(transform));
31 }
32
33 class InheritedTransformChecker
34 : public CSSInterpolationType::CSSConversionChecker {
35 public:
InheritedTransformChecker(const TransformOperations & inherited_transform)36 InheritedTransformChecker(const TransformOperations& inherited_transform)
37 : inherited_transform_(inherited_transform) {}
38
IsValid(const StyleResolverState & state,const InterpolationValue & underlying) const39 bool IsValid(const StyleResolverState& state,
40 const InterpolationValue& underlying) const final {
41 return inherited_transform_ == state.ParentStyle()->Transform();
42 }
43
44 private:
45 const TransformOperations inherited_transform_;
46 };
47
48 class AlwaysInvalidateChecker
49 : public CSSInterpolationType::CSSConversionChecker {
50 public:
IsValid(const StyleResolverState & state,const InterpolationValue & underlying) const51 bool IsValid(const StyleResolverState& state,
52 const InterpolationValue& underlying) const final {
53 return false;
54 }
55 };
56 } // namespace
57
MaybeConvertNeutral(const InterpolationValue & underlying,ConversionCheckers &) const58 InterpolationValue CSSTransformInterpolationType::MaybeConvertNeutral(
59 const InterpolationValue& underlying,
60 ConversionCheckers&) const {
61 return ConvertTransform(EmptyTransformOperations());
62 }
63
MaybeConvertInitial(const StyleResolverState &,ConversionCheckers &) const64 InterpolationValue CSSTransformInterpolationType::MaybeConvertInitial(
65 const StyleResolverState&,
66 ConversionCheckers&) const {
67 return ConvertTransform(ComputedStyle::InitialStyle().Transform());
68 }
69
MaybeConvertInherit(const StyleResolverState & state,ConversionCheckers & conversion_checkers) const70 InterpolationValue CSSTransformInterpolationType::MaybeConvertInherit(
71 const StyleResolverState& state,
72 ConversionCheckers& conversion_checkers) const {
73 const TransformOperations& inherited_transform =
74 state.ParentStyle()->Transform();
75 conversion_checkers.push_back(
76 std::make_unique<InheritedTransformChecker>(inherited_transform));
77 return ConvertTransform(inherited_transform);
78 }
79
MaybeConvertValue(const CSSValue & value,const StyleResolverState * state,ConversionCheckers & conversion_checkers) const80 InterpolationValue CSSTransformInterpolationType::MaybeConvertValue(
81 const CSSValue& value,
82 const StyleResolverState* state,
83 ConversionCheckers& conversion_checkers) const {
84 DCHECK(state);
85 if (auto* list_value = DynamicTo<CSSValueList>(value)) {
86 CSSPrimitiveValue::LengthTypeFlags types;
87 for (const CSSValue* item : *list_value) {
88 const auto& transform_function = To<CSSFunctionValue>(*item);
89 if (transform_function.FunctionType() == CSSValueID::kMatrix ||
90 transform_function.FunctionType() == CSSValueID::kMatrix3d) {
91 types.set(CSSPrimitiveValue::kUnitTypePixels);
92 continue;
93 }
94 for (const CSSValue* argument : transform_function) {
95 const auto& primitive_value = To<CSSPrimitiveValue>(*argument);
96 if (!primitive_value.IsLength() &&
97 !primitive_value.IsCalculatedPercentageWithLength()) {
98 continue;
99 }
100 primitive_value.AccumulateLengthUnitTypes(types);
101 }
102 }
103 std::unique_ptr<InterpolationType::ConversionChecker> length_units_checker =
104 LengthUnitsChecker::MaybeCreate(types, *state);
105
106 if (length_units_checker)
107 conversion_checkers.push_back(std::move(length_units_checker));
108 }
109
110 return InterpolationValue(
111 InterpolableTransformList::ConvertCSSValue(value, state));
112 }
113
114 InterpolationValue
PreInterpolationCompositeIfNeeded(InterpolationValue value,const InterpolationValue & underlying,EffectModel::CompositeOperation composite,ConversionCheckers & conversion_checkers) const115 CSSTransformInterpolationType::PreInterpolationCompositeIfNeeded(
116 InterpolationValue value,
117 const InterpolationValue& underlying,
118 EffectModel::CompositeOperation composite,
119 ConversionCheckers& conversion_checkers) const {
120 // Due to the post-interpolation composite optimization, the interpolation
121 // stack aggressively caches interpolated values. When we are doing
122 // pre-interpolation compositing, this can cause us to bake-in the composited
123 // result even when the underlying value is changing. This checker is a hack
124 // to disable that caching in this case.
125 // TODO(crbug.com/1009230): Remove this once our interpolation code isn't
126 // caching composited values.
127 conversion_checkers.push_back(std::make_unique<AlwaysInvalidateChecker>());
128
129 InterpolableTransformList& transform_list =
130 To<InterpolableTransformList>(*value.interpolable_value);
131 const InterpolableTransformList& underlying_transform_list =
132 To<InterpolableTransformList>(*underlying.interpolable_value);
133
134 // Addition of transform lists uses concatenation, whilst accumulation
135 // performs a similar matching to interpolation but then adds the components.
136 // See https://drafts.csswg.org/css-transforms-2/#combining-transform-lists
137 if (composite == EffectModel::CompositeOperation::kCompositeAdd) {
138 transform_list.PreConcat(underlying_transform_list);
139 } else {
140 DCHECK_EQ(composite, EffectModel::CompositeOperation::kCompositeAccumulate);
141 transform_list.AccumulateOnto(underlying_transform_list);
142 }
143 return value;
144 }
145
MaybeMergeSingles(InterpolationValue && start,InterpolationValue && end) const146 PairwiseInterpolationValue CSSTransformInterpolationType::MaybeMergeSingles(
147 InterpolationValue&& start,
148 InterpolationValue&& end) const {
149 // We don't do any checking here; InterpolableTransformList::Interpolate will
150 // handle discrete animation for us if needed.
151 return PairwiseInterpolationValue(std::move(start.interpolable_value),
152 std::move(end.interpolable_value));
153 }
154
155 InterpolationValue
MaybeConvertStandardPropertyUnderlyingValue(const ComputedStyle & style) const156 CSSTransformInterpolationType::MaybeConvertStandardPropertyUnderlyingValue(
157 const ComputedStyle& style) const {
158 return ConvertTransform(style.Transform());
159 }
160
Composite(UnderlyingValueOwner & underlying_value_owner,double underlying_fraction,const InterpolationValue & value,double interpolation_fraction) const161 void CSSTransformInterpolationType::Composite(
162 UnderlyingValueOwner& underlying_value_owner,
163 double underlying_fraction,
164 const InterpolationValue& value,
165 double interpolation_fraction) const {
166 // We do our compositing behavior in |PreInterpolationCompositeIfNeeded|; see
167 // the documentation on that method.
168 underlying_value_owner.Set(*this, value);
169 }
170
ApplyStandardPropertyValue(const InterpolableValue & interpolable_value,const NonInterpolableValue * untyped_non_interpolable_value,StyleResolverState & state) const171 void CSSTransformInterpolationType::ApplyStandardPropertyValue(
172 const InterpolableValue& interpolable_value,
173 const NonInterpolableValue* untyped_non_interpolable_value,
174 StyleResolverState& state) const {
175 state.Style()->SetTransform(
176 To<InterpolableTransformList>(interpolable_value).operations());
177 }
178
179 } // namespace blink
180