Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(174)

Side by Side Diff: third_party/WebKit/Source/core/animation/CSSSizeListInterpolationType.cpp

Issue 2280553002: Allow interpolation of background-size values with keywords in CSS Animations (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 "core/animation/CSSSizeListInterpolationType.h"
6
7 #include "core/animation/CSSLengthInterpolationType.h"
8 #include "core/animation/ListInterpolationFunctions.h"
9 #include "core/animation/SizeListPropertyFunctions.h"
10 #include "core/css/CSSValueList.h"
11 #include "core/css/CSSValuePair.h"
12 #include "core/css/resolver/StyleResolverState.h"
suzyh_UTC10 (ex-contributor) 2016/08/26 04:14:34 Consider also explicitly including the headers wer
alancutter (OOO until 2018) 2016/08/26 13:31:48 I think the policy is only to not include things t
13
14 namespace blink {
15
16 class CSSSizeNonInterpolableValue : public NonInterpolableValue {
17 public:
18 static PassRefPtr<CSSSizeNonInterpolableValue> create(CSSValueID keyword)
19 {
20 return adoptRef(new CSSSizeNonInterpolableValue(keyword));
21 }
22
23 static PassRefPtr<CSSSizeNonInterpolableValue> create(PassRefPtr<NonInterpol ableValue> lengthNonInterpolableValue)
24 {
25 return adoptRef(new CSSSizeNonInterpolableValue(lengthNonInterpolableVal ue));
26 }
27
28 bool isKeyword() const { return m_keyword != CSSValueInvalid; }
29 CSSValueID keyword() const { DCHECK(isKeyword()); return m_keyword; }
30
31 const NonInterpolableValue* lengthNonInterpolableValue() const { DCHECK(!isK eyword()); return m_lengthNonInterpolableValue.get(); }
32 RefPtr<NonInterpolableValue>& lengthNonInterpolableValue() { DCHECK(!isKeywo rd()); return m_lengthNonInterpolableValue; }
33
34 DECLARE_NON_INTERPOLABLE_VALUE_TYPE();
35
36 private:
37 CSSSizeNonInterpolableValue(CSSValueID keyword)
suzyh_UTC10 (ex-contributor) 2016/08/26 04:14:34 Is there a reason to have two constructors mirrori
alancutter (OOO until 2018) 2016/08/26 13:31:48 I think it's better for assertions to be in constr
suzyh_UTC10 (ex-contributor) 2016/08/30 08:01:07 I think this is a style thing, and I am undecided.
alancutter (OOO until 2018) 2016/09/05 07:44:27 There is precedence for checking in the constructo
38 : m_keyword(keyword)
39 , m_lengthNonInterpolableValue(nullptr)
40 {
41 DCHECK_NE(keyword, CSSValueInvalid);
42 }
43
44 CSSSizeNonInterpolableValue(PassRefPtr<NonInterpolableValue> lengthNonInterp olableValue)
45 : m_keyword(CSSValueInvalid)
46 , m_lengthNonInterpolableValue(lengthNonInterpolableValue)
47 { }
48
49 CSSValueID m_keyword;
50 RefPtr<NonInterpolableValue> m_lengthNonInterpolableValue;
51 };
52
53 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSSizeNonInterpolableValue);
54 DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSSizeNonInterpolableValue);
55
56 static bool sizesAreCompatible(const NonInterpolableValue* a, const NonInterpola bleValue* b)
suzyh_UTC10 (ex-contributor) 2016/08/26 04:14:34 Does this function belong in a separate helper lib
alancutter (OOO until 2018) 2016/08/26 13:31:48 Moved into SizeInterpolationFunctions.
57 {
58 const auto& sizeA = toCSSSizeNonInterpolableValue(*a);
59 const auto& sizeB = toCSSSizeNonInterpolableValue(*b);
60 if (sizeA.isKeyword() != sizeB.isKeyword())
61 return false;
62 if (sizeA.isKeyword())
63 return sizeA.keyword() == sizeB.keyword();
64 return true;
65 }
66
67 class UnderlyingSizeListChecker : public InterpolationType::ConversionChecker {
68 public:
69 ~UnderlyingSizeListChecker() final {}
70
71 static std::unique_ptr<UnderlyingSizeListChecker> create(const NonInterpolab leList& underlyingList)
72 {
73 return wrapUnique(new UnderlyingSizeListChecker(underlyingList));
74 }
75
76 private:
77 UnderlyingSizeListChecker(const NonInterpolableList& underlyingList)
78 : m_underlyingList(&underlyingList)
79 { }
80
81 bool isValid(const InterpolationEnvironment&, const InterpolationValue& unde rlying) const final
82 {
83 const auto& underlyingList = toNonInterpolableList(*underlying.nonInterp olableValue);
84 size_t underlyingLength = underlyingList.length();
85 if (underlyingLength != m_underlyingList->length())
86 return false;
87 for (size_t i = 0; i < underlyingLength; i++) {
88 if (!sizesAreCompatible(underlyingList.get(i), m_underlyingList->get (i)))
89 return false;
90 }
91 return true;
92 }
93
94 RefPtr<const NonInterpolableList> m_underlyingList;
95 };
96
97 class InheritedSizeListChecker : public InterpolationType::ConversionChecker {
98 public:
99 ~InheritedSizeListChecker() final {}
100
101 static std::unique_ptr<InheritedSizeListChecker> create(CSSPropertyID proper ty, const SizeList& inheritedSizeList)
102 {
103 return wrapUnique(new InheritedSizeListChecker(property, inheritedSizeLi st));
104 }
105
106 private:
107 InheritedSizeListChecker(CSSPropertyID property, const SizeList& inheritedSi zeList)
108 : m_property(property)
109 , m_inheritedSizeList(inheritedSizeList)
110 { }
111
112 bool isValid(const InterpolationEnvironment& environment, const Interpolatio nValue&) const final
113 {
114 return m_inheritedSizeList == SizeListPropertyFunctions::getSizeList(m_p roperty, *environment.state().parentStyle());
115 }
116
117 CSSPropertyID m_property;
118 SizeList m_inheritedSizeList;
119 };
120
121 InterpolationValue convertKeyword(CSSValueID keyword)
122 {
123 return InterpolationValue(InterpolableList::create(0), CSSSizeNonInterpolabl eValue::create(keyword));
124 }
125
126 InterpolationValue wrapConvertedLength(InterpolationValue&& convertedLength)
127 {
128 if (!convertedLength)
129 return nullptr;
130 return InterpolationValue(
131 std::move(convertedLength.interpolableValue),
132 CSSSizeNonInterpolableValue::create(convertedLength.nonInterpolableValue .release()));
133 }
134
135 InterpolationValue convertSizeList(const SizeList& sizeList, float zoom)
136 {
137 // Flatten pairs of width/height into individual items, even for contain and cover.
138 return ListInterpolationFunctions::createList(sizeList.size() * 2, [&sizeLis t, zoom](size_t index) -> InterpolationValue {
139 const FillSize& size = sizeList[index / 2];
140 switch (size.type) {
141 case SizeLength: {
142 const Length& side = index % 2 == 0 ? size.size.width() : size.size. height();
143 if (side.isAuto())
144 return convertKeyword(CSSValueAuto);
145 return wrapConvertedLength(CSSLengthInterpolationType::maybeConvertL ength(side, zoom));
146 }
147 case Contain:
148 return convertKeyword(CSSValueContain);
149 case Cover:
150 return convertKeyword(CSSValueCover);
151 case SizeNone:
152 default:
153 NOTREACHED();
154 return nullptr;
155 }
156 });
157 }
158
159 InterpolationValue CSSSizeListInterpolationType::maybeConvertNeutral(const Inter polationValue& underlying, ConversionCheckers& conversionCheckers) const
160 {
161 const auto& underlyingList = toNonInterpolableList(*underlying.nonInterpolab leValue);
162 conversionCheckers.append(UnderlyingSizeListChecker::create(underlyingList)) ;
163 return ListInterpolationFunctions::createList(underlyingList.length(), [&und erlyingList](size_t index) {
164 // const_cast to take a ref.
165 auto& item = toCSSSizeNonInterpolableValue(const_cast<NonInterpolableVal ue&>(*underlyingList.get(index)));
166 if (item.isKeyword())
167 return InterpolationValue(InterpolableList::create(0), &item);
168 return InterpolationValue(CSSLengthInterpolationType::createNeutralInter polableValue(), &item);
169 });
170 }
171
172 InterpolationValue CSSSizeListInterpolationType::maybeConvertInitial(const Style ResolverState&, ConversionCheckers&) const
173 {
174 return convertSizeList(SizeListPropertyFunctions::getInitialSizeList(cssProp erty()), 1);
175 }
176
177 InterpolationValue CSSSizeListInterpolationType::maybeConvertInherit(const Style ResolverState& state, ConversionCheckers& conversionCheckers) const
178 {
179 SizeList inheritedSizeList = SizeListPropertyFunctions::getSizeList(cssPrope rty(), *state.parentStyle());
180 conversionCheckers.append(InheritedSizeListChecker::create(cssProperty(), in heritedSizeList));
181 return convertSizeList(inheritedSizeList, state.style()->effectiveZoom());
182 }
183
184 InterpolationValue CSSSizeListInterpolationType::maybeConvertValue(const CSSValu e& value, const StyleResolverState& state, ConversionCheckers&) const
185 {
186 // CSSPropertyParser doesn't put single values in lists so wrap it up in a t emporary list.
187 const CSSValueList* list = nullptr;
188 if (!value.isBaseValueList()) {
189 CSSValueList* tempList = CSSValueList::createCommaSeparated();
190 tempList->append(value);
191 list = tempList;
192 } else {
193 list = toCSSValueList(&value);
194 }
195
196 // Flatten pairs of width/height into individual items, even for contain and cover.
197 return ListInterpolationFunctions::createList(list->length() * 2, [list](siz e_t index) -> InterpolationValue {
suzyh_UTC10 (ex-contributor) 2016/08/26 04:14:34 I think I would prefer to see a separate library o
alancutter (OOO until 2018) 2016/08/26 13:31:48 This is a lambda due to the closure of list (and t
198 const CSSValue& item = list->item(index / 2);
199 if (item.isValuePair()) {
200 const CSSValuePair& pair = toCSSValuePair(item);
201 const CSSValue& side = index % 2 == 0 ? pair.first() : pair.second() ;
202 if (side.isPrimitiveValue() && toCSSPrimitiveValue(side).getValueID( ) == CSSValueAuto)
203 return convertKeyword(CSSValueAuto);
204 return wrapConvertedLength(CSSLengthInterpolationType::maybeConvertC SSValue(side));
205 }
206 if (!item.isPrimitiveValue())
207 return nullptr;
208 CSSValueID keyword = toCSSPrimitiveValue(item).getValueID();
209 if (keyword)
210 return convertKeyword(keyword);
211 // A single length is equivalent to "<length> auto".
212 if (index % 2 == 0)
213 return wrapConvertedLength(CSSLengthInterpolationType::maybeConvertC SSValue(item));
214 return convertKeyword(CSSValueAuto);
215 });
216 }
217
218 static PairwiseInterpolationValue maybeMergeSingleSizes(InterpolationValue&& sta rt, InterpolationValue&& end)
219 {
220 if (!sizesAreCompatible(start.nonInterpolableValue.get(), end.nonInterpolabl eValue.get()))
221 return nullptr;
222 return PairwiseInterpolationValue(
223 std::move(start.interpolableValue),
224 std::move(end.interpolableValue),
225 start.nonInterpolableValue.release());
226 }
227
228 PairwiseInterpolationValue CSSSizeListInterpolationType::maybeMergeSingles(Inter polationValue&& start, InterpolationValue&& end) const
229 {
230 return ListInterpolationFunctions::maybeMergeSingles(std::move(start), std:: move(end), maybeMergeSingleSizes);
231 }
232
233 InterpolationValue CSSSizeListInterpolationType::maybeConvertUnderlyingValue(con st InterpolationEnvironment& environment) const
234 {
235 const ComputedStyle& style = *environment.state().style();
236 return convertSizeList(SizeListPropertyFunctions::getSizeList(cssProperty(), style), style.effectiveZoom());
237 }
238
239 void compositeSizes(std::unique_ptr<InterpolableValue>& underlyingInterpolableVa lue, RefPtr<NonInterpolableValue>& underlyingNonInterpolableValue, double underl yingFraction, const InterpolableValue& interpolableValue, const NonInterpolableV alue* nonInterpolableValue)
240 {
241 const auto& sizeNonInterpolableValue = toCSSSizeNonInterpolableValue(*nonInt erpolableValue);
242 if (sizeNonInterpolableValue.isKeyword())
243 return;
244 auto& underlyingSizeNonInterpolableValue = toCSSSizeNonInterpolableValue(*un derlyingNonInterpolableValue);
245 CSSLengthInterpolationType::composite(
246 underlyingInterpolableValue,
247 underlyingSizeNonInterpolableValue.lengthNonInterpolableValue(),
248 underlyingFraction,
249 interpolableValue,
250 sizeNonInterpolableValue.lengthNonInterpolableValue());
251 }
252
253 void CSSSizeListInterpolationType::composite(UnderlyingValueOwner& underlyingVal ueOwner, double underlyingFraction, const InterpolationValue& value, double inte rpolationFraction) const
254 {
255 ListInterpolationFunctions::composite(underlyingValueOwner, underlyingFracti on, *this, value, sizesAreCompatible, compositeSizes);
256 }
257
258 Length createLength(const InterpolableValue& interpolableValue, const CSSSizeNon InterpolableValue& nonInterpolableValue, const CSSToLengthConversionData& conver sionData)
259 {
260 if (nonInterpolableValue.isKeyword()) {
261 DCHECK_EQ(nonInterpolableValue.keyword(), CSSValueAuto);
262 return Length(Auto);
263 }
264 return CSSLengthInterpolationType::resolveInterpolableLength(interpolableVal ue, nonInterpolableValue.lengthNonInterpolableValue(), conversionData, ValueRang eNonNegative);
suzyh_UTC10 (ex-contributor) 2016/08/26 04:14:34 As a reader of this code it is surprising to have
alancutter (OOO until 2018) 2016/08/26 13:31:48 Agreed, CSSLengthInterpolationType should have its
265 }
266
267 FillSize createFillSize(const InterpolableValue& interpolableValueA, const NonIn terpolableValue* nonInterpolableValueA, const InterpolableValue& interpolableVal ueB, const NonInterpolableValue* nonInterpolableValueB, const CSSToLengthConvers ionData& conversionData)
268 {
269 const auto& sideA = toCSSSizeNonInterpolableValue(*nonInterpolableValueA);
270 const auto& sideB = toCSSSizeNonInterpolableValue(*nonInterpolableValueB);
271 if (sideA.isKeyword()) {
272 switch (sideA.keyword()) {
273 case CSSValueCover:
274 DCHECK_EQ(sideA.keyword(), sideB.keyword());
275 return FillSize(Cover, LengthSize());
276 case CSSValueContain:
277 DCHECK_EQ(sideA.keyword(), sideB.keyword());
278 return FillSize(Contain, LengthSize());
279 case CSSValueAuto:
280 break;
281 default:
282 NOTREACHED();
283 break;
284 }
285 }
286 return FillSize(SizeLength, LengthSize(
287 createLength(interpolableValueA, sideA, conversionData),
288 createLength(interpolableValueB, sideB, conversionData)));
289 }
290
291 void CSSSizeListInterpolationType::apply(const InterpolableValue& interpolableVa lue, const NonInterpolableValue* nonInterpolableValue, InterpolationEnvironment& environment) const
292 {
293 const auto& interpolableList = toInterpolableList(interpolableValue);
294 const auto& nonInterpolableList = toNonInterpolableList(*nonInterpolableValu e);
295 size_t length = interpolableList.length();
296 DCHECK_EQ(length, nonInterpolableList.length());
297 DCHECK_EQ(length % 2, 0ul);
298 size_t sizeListLength = length / 2;
299 SizeList sizeList(sizeListLength);
300 for (size_t i = 0; i < sizeListLength; i++) {
301 sizeList[i] = createFillSize(
302 *interpolableList.get(i * 2),
303 nonInterpolableList.get(i * 2),
304 *interpolableList.get(i * 2 + 1),
305 nonInterpolableList.get(i * 2 + 1),
306 environment.state().cssToLengthConversionData());
307 }
308 SizeListPropertyFunctions::setSizeList(cssProperty(), *environment.state().s tyle(), sizeList);
309 }
310
311 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698