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