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