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_clip_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_length.h"
12 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
13 #include "third_party/blink/renderer/core/css/css_quad_value.h"
14 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
15 #include "third_party/blink/renderer/core/style/computed_style.h"
16
17 namespace blink {
18
19 struct ClipAutos {
ClipAutosblink::ClipAutos20 ClipAutos()
21 : is_auto(true),
22 is_top_auto(false),
23 is_right_auto(false),
24 is_bottom_auto(false),
25 is_left_auto(false) {}
ClipAutosblink::ClipAutos26 ClipAutos(bool is_top_auto,
27 bool is_right_auto,
28 bool is_bottom_auto,
29 bool is_left_auto)
30 : is_auto(false),
31 is_top_auto(is_top_auto),
32 is_right_auto(is_right_auto),
33 is_bottom_auto(is_bottom_auto),
34 is_left_auto(is_left_auto) {}
ClipAutosblink::ClipAutos35 explicit ClipAutos(const LengthBox& clip)
36 : is_auto(false),
37 is_top_auto(clip.Top().IsAuto()),
38 is_right_auto(clip.Right().IsAuto()),
39 is_bottom_auto(clip.Bottom().IsAuto()),
40 is_left_auto(clip.Left().IsAuto()) {}
41
operator ==blink::ClipAutos42 bool operator==(const ClipAutos& other) const {
43 return is_auto == other.is_auto && is_top_auto == other.is_top_auto &&
44 is_right_auto == other.is_right_auto &&
45 is_bottom_auto == other.is_bottom_auto &&
46 is_left_auto == other.is_left_auto;
47 }
operator !=blink::ClipAutos48 bool operator!=(const ClipAutos& other) const { return !(*this == other); }
49
50 bool is_auto;
51 bool is_top_auto;
52 bool is_right_auto;
53 bool is_bottom_auto;
54 bool is_left_auto;
55 };
56
57 class InheritedClipChecker : public CSSInterpolationType::CSSConversionChecker {
58 public:
Create(const ComputedStyle & parent_style)59 static std::unique_ptr<InheritedClipChecker> Create(
60 const ComputedStyle& parent_style) {
61 Vector<Length> inherited_length_list;
62 GetClipLengthList(parent_style, inherited_length_list);
63 return base::WrapUnique(
64 new InheritedClipChecker(std::move(inherited_length_list)));
65 }
66
67 private:
InheritedClipChecker(const Vector<Length> && inherited_length_list)68 InheritedClipChecker(const Vector<Length>&& inherited_length_list)
69 : inherited_length_list_(std::move(inherited_length_list)) {}
70
IsValid(const StyleResolverState & state,const InterpolationValue & underlying) const71 bool IsValid(const StyleResolverState& state,
72 const InterpolationValue& underlying) const final {
73 Vector<Length> inherited_length_list;
74 GetClipLengthList(*state.ParentStyle(), inherited_length_list);
75 return inherited_length_list_ == inherited_length_list;
76 }
77
GetClipLengthList(const ComputedStyle & style,Vector<Length> & length_list)78 static void GetClipLengthList(const ComputedStyle& style,
79 Vector<Length>& length_list) {
80 if (style.HasAutoClip())
81 return;
82 length_list.push_back(style.ClipTop());
83 length_list.push_back(style.ClipRight());
84 length_list.push_back(style.ClipBottom());
85 length_list.push_back(style.ClipLeft());
86 }
87
88 const Vector<Length> inherited_length_list_;
89 };
90
91 class CSSClipNonInterpolableValue : public NonInterpolableValue {
92 public:
93 ~CSSClipNonInterpolableValue() final = default;
94
Create(const ClipAutos & clip_autos)95 static scoped_refptr<CSSClipNonInterpolableValue> Create(
96 const ClipAutos& clip_autos) {
97 return base::AdoptRef(new CSSClipNonInterpolableValue(clip_autos));
98 }
99
GetClipAutos() const100 const ClipAutos& GetClipAutos() const { return clip_autos_; }
101
102 DECLARE_NON_INTERPOLABLE_VALUE_TYPE();
103
104 private:
CSSClipNonInterpolableValue(const ClipAutos & clip_autos)105 CSSClipNonInterpolableValue(const ClipAutos& clip_autos)
106 : clip_autos_(clip_autos) {
107 DCHECK(!clip_autos_.is_auto);
108 }
109
110 const ClipAutos clip_autos_;
111 };
112
113 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSClipNonInterpolableValue);
114 template <>
115 struct DowncastTraits<CSSClipNonInterpolableValue> {
AllowFromblink::DowncastTraits116 static bool AllowFrom(const NonInterpolableValue* value) {
117 return value && AllowFrom(*value);
118 }
AllowFromblink::DowncastTraits119 static bool AllowFrom(const NonInterpolableValue& value) {
120 return value.GetType() == CSSClipNonInterpolableValue::static_type_;
121 }
122 };
123
124 class UnderlyingAutosChecker
125 : public CSSInterpolationType::CSSConversionChecker {
126 public:
UnderlyingAutosChecker(const ClipAutos & underlying_autos)127 explicit UnderlyingAutosChecker(const ClipAutos& underlying_autos)
128 : underlying_autos_(underlying_autos) {}
129 ~UnderlyingAutosChecker() final = default;
130
GetUnderlyingAutos(const InterpolationValue & underlying)131 static ClipAutos GetUnderlyingAutos(const InterpolationValue& underlying) {
132 if (!underlying)
133 return ClipAutos();
134 return To<CSSClipNonInterpolableValue>(*underlying.non_interpolable_value)
135 .GetClipAutos();
136 }
137
138 private:
IsValid(const StyleResolverState &,const InterpolationValue & underlying) const139 bool IsValid(const StyleResolverState&,
140 const InterpolationValue& underlying) const final {
141 return underlying_autos_ == GetUnderlyingAutos(underlying);
142 }
143
144 const ClipAutos underlying_autos_;
145 };
146
147 enum ClipComponentIndex : unsigned {
148 kClipTop,
149 kClipRight,
150 kClipBottom,
151 kClipLeft,
152 kClipComponentIndexCount,
153 };
154
ConvertClipComponent(const Length & length,double zoom)155 static std::unique_ptr<InterpolableValue> ConvertClipComponent(
156 const Length& length,
157 double zoom) {
158 if (length.IsAuto())
159 return std::make_unique<InterpolableList>(0);
160 return InterpolableLength::MaybeConvertLength(length, zoom);
161 }
162
CreateClipValue(const LengthBox & clip,double zoom)163 static InterpolationValue CreateClipValue(const LengthBox& clip, double zoom) {
164 auto list = std::make_unique<InterpolableList>(kClipComponentIndexCount);
165 list->Set(kClipTop, ConvertClipComponent(clip.Top(), zoom));
166 list->Set(kClipRight, ConvertClipComponent(clip.Right(), zoom));
167 list->Set(kClipBottom, ConvertClipComponent(clip.Bottom(), zoom));
168 list->Set(kClipLeft, ConvertClipComponent(clip.Left(), zoom));
169 return InterpolationValue(
170 std::move(list), CSSClipNonInterpolableValue::Create(ClipAutos(clip)));
171 }
172
MaybeConvertNeutral(const InterpolationValue & underlying,ConversionCheckers & conversion_checkers) const173 InterpolationValue CSSClipInterpolationType::MaybeConvertNeutral(
174 const InterpolationValue& underlying,
175 ConversionCheckers& conversion_checkers) const {
176 ClipAutos underlying_autos =
177 UnderlyingAutosChecker::GetUnderlyingAutos(underlying);
178 conversion_checkers.push_back(
179 std::make_unique<UnderlyingAutosChecker>(underlying_autos));
180 if (underlying_autos.is_auto)
181 return nullptr;
182 LengthBox neutral_box(
183 underlying_autos.is_top_auto ? Length::Auto() : Length::Fixed(0),
184 underlying_autos.is_right_auto ? Length::Auto() : Length::Fixed(0),
185 underlying_autos.is_bottom_auto ? Length::Auto() : Length::Fixed(0),
186 underlying_autos.is_left_auto ? Length::Auto() : Length::Fixed(0));
187 return CreateClipValue(neutral_box, 1);
188 }
189
MaybeConvertInitial(const StyleResolverState &,ConversionCheckers &) const190 InterpolationValue CSSClipInterpolationType::MaybeConvertInitial(
191 const StyleResolverState&,
192 ConversionCheckers&) const {
193 return nullptr;
194 }
195
MaybeConvertInherit(const StyleResolverState & state,ConversionCheckers & conversion_checkers) const196 InterpolationValue CSSClipInterpolationType::MaybeConvertInherit(
197 const StyleResolverState& state,
198 ConversionCheckers& conversion_checkers) const {
199 conversion_checkers.push_back(
200 InheritedClipChecker::Create(*state.ParentStyle()));
201 if (state.ParentStyle()->HasAutoClip())
202 return nullptr;
203 return CreateClipValue(state.ParentStyle()->Clip(),
204 state.ParentStyle()->EffectiveZoom());
205 }
206
IsCSSAuto(const CSSValue & value)207 static bool IsCSSAuto(const CSSValue& value) {
208 auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
209 return identifier_value &&
210 identifier_value->GetValueID() == CSSValueID::kAuto;
211 }
212
ConvertClipComponent(const CSSValue & length)213 static std::unique_ptr<InterpolableValue> ConvertClipComponent(
214 const CSSValue& length) {
215 if (IsCSSAuto(length))
216 return std::make_unique<InterpolableList>(0);
217 return InterpolableLength::MaybeConvertCSSValue(length);
218 }
219
MaybeConvertValue(const CSSValue & value,const StyleResolverState *,ConversionCheckers &) const220 InterpolationValue CSSClipInterpolationType::MaybeConvertValue(
221 const CSSValue& value,
222 const StyleResolverState*,
223 ConversionCheckers&) const {
224 const auto* quad = DynamicTo<CSSQuadValue>(value);
225 if (!quad)
226 return nullptr;
227 auto list = std::make_unique<InterpolableList>(kClipComponentIndexCount);
228 list->Set(kClipTop, ConvertClipComponent(*quad->Top()));
229 list->Set(kClipRight, ConvertClipComponent(*quad->Right()));
230 list->Set(kClipBottom, ConvertClipComponent(*quad->Bottom()));
231 list->Set(kClipLeft, ConvertClipComponent(*quad->Left()));
232 ClipAutos autos(IsCSSAuto(*quad->Top()), IsCSSAuto(*quad->Right()),
233 IsCSSAuto(*quad->Bottom()), IsCSSAuto(*quad->Left()));
234 return InterpolationValue(std::move(list),
235 CSSClipNonInterpolableValue::Create(autos));
236 }
237
238 InterpolationValue
MaybeConvertStandardPropertyUnderlyingValue(const ComputedStyle & style) const239 CSSClipInterpolationType::MaybeConvertStandardPropertyUnderlyingValue(
240 const ComputedStyle& style) const {
241 if (style.HasAutoClip())
242 return nullptr;
243 return CreateClipValue(style.Clip(), style.EffectiveZoom());
244 }
245
MaybeMergeSingles(InterpolationValue && start,InterpolationValue && end) const246 PairwiseInterpolationValue CSSClipInterpolationType::MaybeMergeSingles(
247 InterpolationValue&& start,
248 InterpolationValue&& end) const {
249 const auto& start_autos =
250 To<CSSClipNonInterpolableValue>(*start.non_interpolable_value)
251 .GetClipAutos();
252 const auto& end_autos =
253 To<CSSClipNonInterpolableValue>(*end.non_interpolable_value)
254 .GetClipAutos();
255 if (start_autos != end_autos)
256 return nullptr;
257 return PairwiseInterpolationValue(std::move(start.interpolable_value),
258 std::move(end.interpolable_value),
259 std::move(start.non_interpolable_value));
260 }
261
Composite(UnderlyingValueOwner & underlying_value_owner,double underlying_fraction,const InterpolationValue & value,double interpolation_fraction) const262 void CSSClipInterpolationType::Composite(
263 UnderlyingValueOwner& underlying_value_owner,
264 double underlying_fraction,
265 const InterpolationValue& value,
266 double interpolation_fraction) const {
267 const auto& underlying_autos =
268 To<CSSClipNonInterpolableValue>(
269 *underlying_value_owner.Value().non_interpolable_value)
270 .GetClipAutos();
271 const auto& autos =
272 To<CSSClipNonInterpolableValue>(*value.non_interpolable_value)
273 .GetClipAutos();
274 if (underlying_autos == autos)
275 underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd(
276 underlying_fraction, *value.interpolable_value);
277 else
278 underlying_value_owner.Set(*this, value);
279 }
280
ApplyStandardPropertyValue(const InterpolableValue & interpolable_value,const NonInterpolableValue * non_interpolable_value,StyleResolverState & state) const281 void CSSClipInterpolationType::ApplyStandardPropertyValue(
282 const InterpolableValue& interpolable_value,
283 const NonInterpolableValue* non_interpolable_value,
284 StyleResolverState& state) const {
285 const auto& autos =
286 To<CSSClipNonInterpolableValue>(non_interpolable_value)->GetClipAutos();
287 const auto& list = To<InterpolableList>(interpolable_value);
288 const auto& convert_index = [&list, &state](bool is_auto, wtf_size_t index) {
289 if (is_auto)
290 return Length::Auto();
291 return To<InterpolableLength>(*list.Get(index))
292 .CreateLength(state.CssToLengthConversionData(), kValueRangeAll);
293 };
294 state.Style()->SetClip(
295 LengthBox(convert_index(autos.is_top_auto, kClipTop),
296 convert_index(autos.is_right_auto, kClipRight),
297 convert_index(autos.is_bottom_auto, kClipBottom),
298 convert_index(autos.is_left_auto, kClipLeft)));
299 }
300
301 } // namespace blink
302