OLD | NEW |
---|---|
(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 | |
OLD | NEW |