OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/animation/CSSLengthInterpolationType.h" | 5 #include "core/animation/CSSLengthInterpolationType.h" |
6 | 6 |
| 7 #include "core/animation/LengthInterpolationFunctions.h" |
7 #include "core/animation/LengthPropertyFunctions.h" | 8 #include "core/animation/LengthPropertyFunctions.h" |
8 #include "core/animation/css/CSSAnimatableValueFactory.h" | 9 #include "core/animation/css/CSSAnimatableValueFactory.h" |
9 #include "core/css/CSSCalculationValue.h" | 10 #include "core/css/CSSCalculationValue.h" |
10 #include "core/css/resolver/StyleBuilder.h" | 11 #include "core/css/resolver/StyleBuilder.h" |
11 #include "core/css/resolver/StyleResolverState.h" | 12 #include "core/css/resolver/StyleResolverState.h" |
12 #include "wtf/PtrUtil.h" | 13 #include "wtf/PtrUtil.h" |
13 #include <memory> | 14 #include <memory> |
14 | 15 |
15 namespace blink { | 16 namespace blink { |
16 | 17 |
17 // This class is implemented as a singleton whose instance represents the presen
ce of percentages being used in a Length value | |
18 // while nullptr represents the absence of any percentages. | |
19 class CSSLengthNonInterpolableValue : public NonInterpolableValue { | |
20 public: | |
21 ~CSSLengthNonInterpolableValue() final { NOTREACHED(); } | |
22 static PassRefPtr<CSSLengthNonInterpolableValue> create(bool hasPercentage) | |
23 { | |
24 DEFINE_STATIC_REF(CSSLengthNonInterpolableValue, singleton, adoptRef(new
CSSLengthNonInterpolableValue())); | |
25 DCHECK(singleton); | |
26 return hasPercentage ? singleton : nullptr; | |
27 } | |
28 static PassRefPtr<CSSLengthNonInterpolableValue> merge(const NonInterpolable
Value* a, const NonInterpolableValue* b) | |
29 { | |
30 return create(hasPercentage(a) || hasPercentage(b)); | |
31 } | |
32 static bool hasPercentage(const NonInterpolableValue*); | |
33 | |
34 DECLARE_NON_INTERPOLABLE_VALUE_TYPE(); | |
35 | |
36 private: | |
37 CSSLengthNonInterpolableValue() { } | |
38 }; | |
39 | |
40 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSLengthNonInterpolableValue); | |
41 DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSLengthNonInterpolableValue); | |
42 | |
43 bool CSSLengthNonInterpolableValue::hasPercentage(const NonInterpolableValue* no
nInterpolableValue) | |
44 { | |
45 DCHECK(isCSSLengthNonInterpolableValue(nonInterpolableValue)); | |
46 return static_cast<bool>(nonInterpolableValue); | |
47 } | |
48 | |
49 CSSLengthInterpolationType::CSSLengthInterpolationType(CSSPropertyID property) | 18 CSSLengthInterpolationType::CSSLengthInterpolationType(CSSPropertyID property) |
50 : CSSInterpolationType(property) | 19 : CSSInterpolationType(property) |
51 , m_valueRange(LengthPropertyFunctions::getValueRange(property)) | 20 , m_valueRange(LengthPropertyFunctions::getValueRange(property)) |
52 { } | 21 { } |
53 | 22 |
54 float CSSLengthInterpolationType::effectiveZoom(const ComputedStyle& style) cons
t | 23 float CSSLengthInterpolationType::effectiveZoom(const ComputedStyle& style) cons
t |
55 { | 24 { |
56 return LengthPropertyFunctions::isZoomedLength(cssProperty()) ? style.effect
iveZoom() : 1; | 25 return LengthPropertyFunctions::isZoomedLength(cssProperty()) ? style.effect
iveZoom() : 1; |
57 } | 26 } |
58 | 27 |
59 std::unique_ptr<InterpolableValue> CSSLengthInterpolationType::createInterpolabl
ePixels(double pixels) | |
60 { | |
61 std::unique_ptr<InterpolableList> interpolableList = createNeutralInterpolab
leValue(); | |
62 interpolableList->set(CSSPrimitiveValue::UnitTypePixels, InterpolableNumber:
:create(pixels)); | |
63 return std::move(interpolableList); | |
64 } | |
65 | |
66 InterpolationValue CSSLengthInterpolationType::createInterpolablePercent(double
percent) | |
67 { | |
68 std::unique_ptr<InterpolableList> interpolableList = createNeutralInterpolab
leValue(); | |
69 interpolableList->set(CSSPrimitiveValue::UnitTypePercentage, InterpolableNum
ber::create(percent)); | |
70 return InterpolationValue(std::move(interpolableList), CSSLengthNonInterpola
bleValue::create(true)); | |
71 } | |
72 | |
73 InterpolationValue CSSLengthInterpolationType::maybeConvertLength(const Length&
length, float zoom) | |
74 { | |
75 if (!length.isSpecified()) | |
76 return nullptr; | |
77 | |
78 PixelsAndPercent pixelsAndPercent = length.getPixelsAndPercent(); | |
79 std::unique_ptr<InterpolableList> values = createNeutralInterpolableValue(); | |
80 values->set(CSSPrimitiveValue::UnitTypePixels, InterpolableNumber::create(pi
xelsAndPercent.pixels / zoom)); | |
81 values->set(CSSPrimitiveValue::UnitTypePercentage, InterpolableNumber::creat
e(pixelsAndPercent.percent)); | |
82 | |
83 return InterpolationValue(std::move(values), CSSLengthNonInterpolableValue::
create(length.isPercentOrCalc())); | |
84 } | |
85 | |
86 std::unique_ptr<InterpolableList> CSSLengthInterpolationType::createNeutralInter
polableValue() | |
87 { | |
88 const size_t length = CSSPrimitiveValue::LengthUnitTypeCount; | |
89 std::unique_ptr<InterpolableList> values = InterpolableList::create(length); | |
90 for (size_t i = 0; i < length; i++) | |
91 values->set(i, InterpolableNumber::create(0)); | |
92 return values; | |
93 } | |
94 | |
95 PairwiseInterpolationValue CSSLengthInterpolationType::staticMergeSingleConversi
ons(InterpolationValue&& start, InterpolationValue&& end) | |
96 { | |
97 return PairwiseInterpolationValue( | |
98 std::move(start.interpolableValue), | |
99 std::move(end.interpolableValue), | |
100 CSSLengthNonInterpolableValue::merge(start.nonInterpolableValue.get(), e
nd.nonInterpolableValue.get())); | |
101 } | |
102 | |
103 bool CSSLengthInterpolationType::nonInterpolableValuesAreCompatible(const NonInt
erpolableValue* a, const NonInterpolableValue* b) | |
104 { | |
105 DCHECK(isCSSLengthNonInterpolableValue(a)); | |
106 DCHECK(isCSSLengthNonInterpolableValue(b)); | |
107 return true; | |
108 } | |
109 | |
110 void CSSLengthInterpolationType::composite( | |
111 std::unique_ptr<InterpolableValue>& underlyingInterpolableValue, | |
112 RefPtr<NonInterpolableValue>& underlyingNonInterpolableValue, | |
113 double underlyingFraction, | |
114 const InterpolableValue& interpolableValue, | |
115 const NonInterpolableValue* nonInterpolableValue) | |
116 { | |
117 underlyingInterpolableValue->scaleAndAdd(underlyingFraction, interpolableVal
ue); | |
118 underlyingNonInterpolableValue = CSSLengthNonInterpolableValue::merge(underl
yingNonInterpolableValue.get(), nonInterpolableValue); | |
119 } | |
120 | |
121 void CSSLengthInterpolationType::subtractFromOneHundredPercent(InterpolationValu
e& result) | |
122 { | |
123 InterpolableList& list = toInterpolableList(*result.interpolableValue); | |
124 for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) { | |
125 double value = -toInterpolableNumber(*list.get(i)).value(); | |
126 if (i == CSSPrimitiveValue::UnitTypePercentage) | |
127 value += 100; | |
128 toInterpolableNumber(*list.getMutable(i)).set(value); | |
129 } | |
130 result.nonInterpolableValue = CSSLengthNonInterpolableValue::create(true); | |
131 } | |
132 | |
133 InterpolationValue CSSLengthInterpolationType::maybeConvertCSSValue(const CSSVal
ue& value) | |
134 { | |
135 if (!value.isPrimitiveValue()) | |
136 return nullptr; | |
137 | |
138 const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value); | |
139 if (!primitiveValue.isLength() && !primitiveValue.isPercentage() && !primiti
veValue.isCalculatedPercentageWithLength()) | |
140 return nullptr; | |
141 | |
142 CSSLengthArray lengthArray; | |
143 primitiveValue.accumulateLengthArray(lengthArray); | |
144 | |
145 std::unique_ptr<InterpolableList> values = InterpolableList::create(CSSPrimi
tiveValue::LengthUnitTypeCount); | |
146 for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) | |
147 values->set(i, InterpolableNumber::create(lengthArray.values[i])); | |
148 | |
149 bool hasPercentage = lengthArray.typeFlags.get(CSSPrimitiveValue::UnitTypePe
rcentage); | |
150 return InterpolationValue(std::move(values), CSSLengthNonInterpolableValue::
create(hasPercentage)); | |
151 } | |
152 | |
153 class ParentLengthChecker : public InterpolationType::ConversionChecker { | 28 class ParentLengthChecker : public InterpolationType::ConversionChecker { |
154 public: | 29 public: |
155 static std::unique_ptr<ParentLengthChecker> create(CSSPropertyID property, c
onst Length& length) | 30 static std::unique_ptr<ParentLengthChecker> create(CSSPropertyID property, c
onst Length& length) |
156 { | 31 { |
157 return wrapUnique(new ParentLengthChecker(property, length)); | 32 return wrapUnique(new ParentLengthChecker(property, length)); |
158 } | 33 } |
159 | 34 |
160 private: | 35 private: |
161 ParentLengthChecker(CSSPropertyID property, const Length& length) | 36 ParentLengthChecker(CSSPropertyID property, const Length& length) |
162 : m_property(property) | 37 : m_property(property) |
163 , m_length(length) | 38 , m_length(length) |
164 { } | 39 { } |
165 | 40 |
166 bool isValid(const InterpolationEnvironment& environment, const Interpolatio
nValue& underlying) const final | 41 bool isValid(const InterpolationEnvironment& environment, const Interpolatio
nValue& underlying) const final |
167 { | 42 { |
168 Length parentLength; | 43 Length parentLength; |
169 if (!LengthPropertyFunctions::getLength(m_property, *environment.state()
.parentStyle(), parentLength)) | 44 if (!LengthPropertyFunctions::getLength(m_property, *environment.state()
.parentStyle(), parentLength)) |
170 return false; | 45 return false; |
171 return parentLength == m_length; | 46 return parentLength == m_length; |
172 } | 47 } |
173 | 48 |
174 const CSSPropertyID m_property; | 49 const CSSPropertyID m_property; |
175 const Length m_length; | 50 const Length m_length; |
176 }; | 51 }; |
177 | 52 |
178 InterpolationValue CSSLengthInterpolationType::maybeConvertNeutral(const Interpo
lationValue&, ConversionCheckers&) const | 53 InterpolationValue CSSLengthInterpolationType::maybeConvertNeutral(const Interpo
lationValue&, ConversionCheckers&) const |
179 { | 54 { |
180 return InterpolationValue(createNeutralInterpolableValue()); | 55 return InterpolationValue(LengthInterpolationFunctions::createNeutralInterpo
lableValue()); |
181 } | 56 } |
182 | 57 |
183 InterpolationValue CSSLengthInterpolationType::maybeConvertInitial(const StyleRe
solverState&, ConversionCheckers& conversionCheckers) const | 58 InterpolationValue CSSLengthInterpolationType::maybeConvertInitial(const StyleRe
solverState&, ConversionCheckers& conversionCheckers) const |
184 { | 59 { |
185 Length initialLength; | 60 Length initialLength; |
186 if (!LengthPropertyFunctions::getInitialLength(cssProperty(), initialLength)
) | 61 if (!LengthPropertyFunctions::getInitialLength(cssProperty(), initialLength)
) |
187 return nullptr; | 62 return nullptr; |
188 return maybeConvertLength(initialLength, 1); | 63 return LengthInterpolationFunctions::maybeConvertLength(initialLength, 1); |
189 } | 64 } |
190 | 65 |
191 InterpolationValue CSSLengthInterpolationType::maybeConvertInherit(const StyleRe
solverState& state, ConversionCheckers& conversionCheckers) const | 66 InterpolationValue CSSLengthInterpolationType::maybeConvertInherit(const StyleRe
solverState& state, ConversionCheckers& conversionCheckers) const |
192 { | 67 { |
193 if (!state.parentStyle()) | 68 if (!state.parentStyle()) |
194 return nullptr; | 69 return nullptr; |
195 Length inheritedLength; | 70 Length inheritedLength; |
196 if (!LengthPropertyFunctions::getLength(cssProperty(), *state.parentStyle(),
inheritedLength)) | 71 if (!LengthPropertyFunctions::getLength(cssProperty(), *state.parentStyle(),
inheritedLength)) |
197 return nullptr; | 72 return nullptr; |
198 conversionCheckers.append(ParentLengthChecker::create(cssProperty(), inherit
edLength)); | 73 conversionCheckers.append(ParentLengthChecker::create(cssProperty(), inherit
edLength)); |
199 return maybeConvertLength(inheritedLength, effectiveZoom(*state.parentStyle(
))); | 74 return LengthInterpolationFunctions::maybeConvertLength(inheritedLength, eff
ectiveZoom(*state.parentStyle())); |
200 } | 75 } |
201 | 76 |
202 InterpolationValue CSSLengthInterpolationType::maybeConvertValue(const CSSValue&
value, const StyleResolverState&, ConversionCheckers& conversionCheckers) const | 77 InterpolationValue CSSLengthInterpolationType::maybeConvertValue(const CSSValue&
value, const StyleResolverState&, ConversionCheckers& conversionCheckers) const |
203 { | 78 { |
204 if (value.isPrimitiveValue() && toCSSPrimitiveValue(value).isValueID()) { | 79 if (value.isPrimitiveValue() && toCSSPrimitiveValue(value).isValueID()) { |
205 CSSValueID valueID = toCSSPrimitiveValue(value).getValueID(); | 80 CSSValueID valueID = toCSSPrimitiveValue(value).getValueID(); |
206 double pixels; | 81 double pixels; |
207 if (!LengthPropertyFunctions::getPixelsForKeyword(cssProperty(), valueID
, pixels)) | 82 if (!LengthPropertyFunctions::getPixelsForKeyword(cssProperty(), valueID
, pixels)) |
208 return nullptr; | 83 return nullptr; |
209 return InterpolationValue(createInterpolablePixels(pixels)); | 84 return InterpolationValue(LengthInterpolationFunctions::createInterpolab
lePixels(pixels)); |
210 } | 85 } |
211 | 86 |
212 return maybeConvertCSSValue(value); | 87 return LengthInterpolationFunctions::maybeConvertCSSValue(value); |
| 88 } |
| 89 |
| 90 PairwiseInterpolationValue CSSLengthInterpolationType::maybeMergeSingles(Interpo
lationValue&& start, InterpolationValue&& end) const |
| 91 { |
| 92 return LengthInterpolationFunctions::mergeSingles(std::move(start), std::mov
e(end)); |
213 } | 93 } |
214 | 94 |
215 InterpolationValue CSSLengthInterpolationType::maybeConvertUnderlyingValue(const
InterpolationEnvironment& environment) const | 95 InterpolationValue CSSLengthInterpolationType::maybeConvertUnderlyingValue(const
InterpolationEnvironment& environment) const |
216 { | 96 { |
217 Length underlyingLength; | 97 Length underlyingLength; |
218 if (!LengthPropertyFunctions::getLength(cssProperty(), *environment.state().
style(), underlyingLength)) | 98 if (!LengthPropertyFunctions::getLength(cssProperty(), *environment.state().
style(), underlyingLength)) |
219 return nullptr; | 99 return nullptr; |
220 return maybeConvertLength(underlyingLength, effectiveZoom(*environment.state
().style())); | 100 return LengthInterpolationFunctions::maybeConvertLength(underlyingLength, ef
fectiveZoom(*environment.state().style())); |
221 } | 101 } |
222 | 102 |
223 void CSSLengthInterpolationType::composite(UnderlyingValueOwner& underlyingValue
Owner, double underlyingFraction, const InterpolationValue& value, double interp
olationFraction) const | 103 void CSSLengthInterpolationType::composite(UnderlyingValueOwner& underlyingValue
Owner, double underlyingFraction, const InterpolationValue& value, double interp
olationFraction) const |
224 { | 104 { |
225 InterpolationValue& underlying = underlyingValueOwner.mutableValue(); | 105 InterpolationValue& underlying = underlyingValueOwner.mutableValue(); |
226 composite(underlying.interpolableValue, underlying.nonInterpolableValue, | 106 LengthInterpolationFunctions::composite(underlying.interpolableValue, underl
ying.nonInterpolableValue, |
227 underlyingFraction, *value.interpolableValue, value.nonInterpolableValue
.get()); | 107 underlyingFraction, *value.interpolableValue, value.nonInterpolableValue
.get()); |
228 } | 108 } |
229 | 109 |
230 // TODO(alancutter): Move this to Length.h. | |
231 static double clampToRange(double x, ValueRange range) | |
232 { | |
233 return (range == ValueRangeNonNegative && x < 0) ? 0 : x; | |
234 } | |
235 | |
236 Length CSSLengthInterpolationType::createLength(const InterpolableValue& interpo
lableValue, const NonInterpolableValue* nonInterpolableValue, const CSSToLengthC
onversionData& conversionData, ValueRange range) | |
237 { | |
238 const InterpolableList& interpolableList = toInterpolableList(interpolableVa
lue); | |
239 bool hasPercentage = CSSLengthNonInterpolableValue::hasPercentage(nonInterpo
lableValue); | |
240 double pixels = 0; | |
241 double percentage = 0; | |
242 for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) { | |
243 double value = toInterpolableNumber(*interpolableList.get(i)).value(); | |
244 if (i == CSSPrimitiveValue::UnitTypePercentage) { | |
245 percentage = value; | |
246 } else { | |
247 CSSPrimitiveValue::UnitType type = CSSPrimitiveValue::lengthUnitType
ToUnitType(static_cast<CSSPrimitiveValue::LengthUnitType>(i)); | |
248 pixels += conversionData.zoomedComputedPixels(value, type); | |
249 } | |
250 } | |
251 if (percentage != 0) | |
252 hasPercentage = true; | |
253 if (pixels != 0 && hasPercentage) | |
254 return Length(CalculationValue::create(PixelsAndPercent(pixels, percenta
ge), range)); | |
255 if (hasPercentage) | |
256 return Length(clampToRange(percentage, range), Percent); | |
257 return Length(CSSPrimitiveValue::clampToCSSLengthRange(clampToRange(pixels,
range)), Fixed); | |
258 } | |
259 | |
260 void CSSLengthInterpolationType::apply(const InterpolableValue& interpolableValu
e, const NonInterpolableValue* nonInterpolableValue, InterpolationEnvironment& e
nvironment) const | 110 void CSSLengthInterpolationType::apply(const InterpolableValue& interpolableValu
e, const NonInterpolableValue* nonInterpolableValue, InterpolationEnvironment& e
nvironment) const |
261 { | 111 { |
262 StyleResolverState& state = environment.state(); | 112 StyleResolverState& state = environment.state(); |
263 ComputedStyle& style = *state.style(); | 113 ComputedStyle& style = *state.style(); |
264 float zoom = effectiveZoom(style); | 114 float zoom = effectiveZoom(style); |
265 Length length = createLength(interpolableValue, nonInterpolableValue, state.
cssToLengthConversionData(), m_valueRange); | 115 Length length = LengthInterpolationFunctions::createLength(interpolableValue
, nonInterpolableValue, state.cssToLengthConversionData(), m_valueRange); |
266 if (LengthPropertyFunctions::setLength(cssProperty(), style, length)) { | 116 if (LengthPropertyFunctions::setLength(cssProperty(), style, length)) { |
267 #if DCHECK_IS_ON() | 117 #if DCHECK_IS_ON() |
268 // Assert that setting the length on ComputedStyle directly is identical
to the StyleBuilder code path. | 118 // Assert that setting the length on ComputedStyle directly is identical
to the StyleBuilder code path. |
269 // This check is useful for catching differences in clamping behaviour. | 119 // This check is useful for catching differences in clamping behaviour. |
270 Length before; | 120 Length before; |
271 Length after; | 121 Length after; |
272 DCHECK(LengthPropertyFunctions::getLength(cssProperty(), style, before))
; | 122 DCHECK(LengthPropertyFunctions::getLength(cssProperty(), style, before))
; |
273 StyleBuilder::applyProperty(cssProperty(), state, *CSSPrimitiveValue::cr
eate(length, zoom)); | 123 StyleBuilder::applyProperty(cssProperty(), state, *CSSPrimitiveValue::cr
eate(length, zoom)); |
274 DCHECK(LengthPropertyFunctions::getLength(cssProperty(), style, after)); | 124 DCHECK(LengthPropertyFunctions::getLength(cssProperty(), style, after)); |
275 DCHECK(before == after); | 125 DCHECK(before == after); |
276 #endif | 126 #endif |
277 return; | 127 return; |
278 } | 128 } |
279 StyleBuilder::applyProperty(cssProperty(), state, *CSSPrimitiveValue::create
(length, zoom)); | 129 StyleBuilder::applyProperty(cssProperty(), state, *CSSPrimitiveValue::create
(length, zoom)); |
280 } | 130 } |
281 | 131 |
282 } // namespace blink | 132 } // namespace blink |
OLD | NEW |