1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "core/animation/AnimatableLength.h"
33
34 #include "core/css/CSSPrimitiveValueMappings.h"
35 #include "platform/CalculationValue.h"
36 #include "platform/animation/AnimationUtilities.h"
37
38 namespace WebCore {
39
create(CSSValue * value)40 PassRefPtr<AnimatableLength> AnimatableLength::create(CSSValue* value)
41 {
42 ASSERT(canCreateFrom(value));
43 if (value->isPrimitiveValue()) {
44 CSSPrimitiveValue* primitiveValue = WebCore::toCSSPrimitiveValue(value);
45 const CSSCalcValue* calcValue = primitiveValue->cssCalcValue();
46 if (calcValue)
47 return create(calcValue->expressionNode(), primitiveValue);
48 NumberUnitType unitType;
49 bool isPrimitiveLength = primitiveUnitToNumberType(primitiveValue->primitiveType(), unitType);
50 ASSERT_UNUSED(isPrimitiveLength, isPrimitiveLength);
51 const double scale = CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(primitiveValue->primitiveType());
52 return create(primitiveValue->getDoubleValue() * scale, unitType, primitiveValue);
53 }
54
55 if (value->isCalcValue())
56 return create(toCSSCalcValue(value)->expressionNode());
57
58 ASSERT_NOT_REACHED();
59 return 0;
60 }
61
canCreateFrom(const CSSValue * value)62 bool AnimatableLength::canCreateFrom(const CSSValue* value)
63 {
64 ASSERT(value);
65 if (value->isPrimitiveValue()) {
66 const CSSPrimitiveValue* primitiveValue = WebCore::toCSSPrimitiveValue(value);
67 if (primitiveValue->cssCalcValue())
68 return true;
69
70 NumberUnitType unitType;
71 // Only returns true if the type is a primitive length unit.
72 return primitiveUnitToNumberType(primitiveValue->primitiveType(), unitType);
73 }
74 return value->isCalcValue();
75 }
76
toCSSValue(NumberRange range) const77 PassRefPtr<CSSValue> AnimatableLength::toCSSValue(NumberRange range) const
78 {
79 return toCSSPrimitiveValue(range);
80 }
81
toLength(const CSSToLengthConversionData & conversionData,NumberRange range) const82 Length AnimatableLength::toLength(const CSSToLengthConversionData& conversionData, NumberRange range) const
83 {
84 // Avoid creating a CSSValue in the common cases
85 if (m_unitType == UnitTypePixels)
86 return Length(clampedNumber(range) * conversionData.zoom(), Fixed);
87 if (m_unitType == UnitTypePercentage)
88 return Length(clampedNumber(range), Percent);
89
90 return toCSSPrimitiveValue(range)->convertToLength<AnyConversion>(conversionData);
91 }
92
interpolateTo(const AnimatableValue * value,double fraction) const93 PassRefPtr<AnimatableValue> AnimatableLength::interpolateTo(const AnimatableValue* value, double fraction) const
94 {
95 const AnimatableLength* length = toAnimatableLength(value);
96 NumberUnitType type = commonUnitType(length);
97 if (type != UnitTypeCalc)
98 return AnimatableLength::create(blend(m_number, length->m_number, fraction), type);
99
100 // FIXME(crbug.com/168840): Support for viewport units in calc needs to be added before we can blend them with other units.
101 if (isViewportUnit() || length->isViewportUnit())
102 return defaultInterpolateTo(this, value, fraction);
103
104 return AnimatableLength::create(scale(1 - fraction).get(), length->scale(fraction).get());
105 }
106
addWith(const AnimatableValue * value) const107 PassRefPtr<AnimatableValue> AnimatableLength::addWith(const AnimatableValue* value) const
108 {
109 // Optimization for adding with 0.
110 if (isUnitlessZero())
111 return takeConstRef(value);
112
113 const AnimatableLength* length = toAnimatableLength(value);
114 if (length->isUnitlessZero())
115 return takeConstRef(this);
116
117 NumberUnitType type = commonUnitType(length);
118 if (type != UnitTypeCalc)
119 return AnimatableLength::create(m_number + length->m_number, type);
120
121 return AnimatableLength::create(this, length);
122 }
123
equalTo(const AnimatableValue * value) const124 bool AnimatableLength::equalTo(const AnimatableValue* value) const
125 {
126 const AnimatableLength* length = toAnimatableLength(value);
127 if (m_unitType != length->m_unitType)
128 return false;
129 if (isCalc())
130 return m_calcExpression == length->m_calcExpression || m_calcExpression->equals(*length->m_calcExpression);
131 return m_number == length->m_number;
132 }
133
toCSSCalcExpressionNode() const134 PassRefPtr<CSSCalcExpressionNode> AnimatableLength::toCSSCalcExpressionNode() const
135 {
136 if (isCalc())
137 return m_calcExpression;
138 return CSSCalcValue::createExpressionNode(toCSSPrimitiveValue(AllValues), m_number == trunc(m_number));
139 }
140
isCompatibleWithRange(const CSSPrimitiveValue * primitiveValue,NumberRange range)141 static bool isCompatibleWithRange(const CSSPrimitiveValue* primitiveValue, NumberRange range)
142 {
143 ASSERT(primitiveValue);
144 if (range == AllValues)
145 return true;
146 if (primitiveValue->isCalculated())
147 return primitiveValue->cssCalcValue()->permittedValueRange() == ValueRangeNonNegative;
148 return primitiveValue->getDoubleValue() >= 0;
149 }
150
toCSSPrimitiveValue(NumberRange range) const151 PassRefPtr<CSSPrimitiveValue> AnimatableLength::toCSSPrimitiveValue(NumberRange range) const
152 {
153 if (!m_cachedCSSPrimitiveValue || !isCompatibleWithRange(m_cachedCSSPrimitiveValue.get(), range)) {
154 if (isCalc())
155 m_cachedCSSPrimitiveValue = CSSPrimitiveValue::create(CSSCalcValue::create(m_calcExpression, range == AllValues ? ValueRangeAll : ValueRangeNonNegative));
156 else
157 m_cachedCSSPrimitiveValue = CSSPrimitiveValue::create(clampedNumber(range), static_cast<CSSPrimitiveValue::UnitTypes>(numberTypeToPrimitiveUnit(m_unitType)));
158 }
159 return m_cachedCSSPrimitiveValue;
160 }
161
scale(double factor) const162 PassRefPtr<AnimatableLength> AnimatableLength::scale(double factor) const
163 {
164 if (isCalc()) {
165 return AnimatableLength::create(CSSCalcValue::createExpressionNode(
166 m_calcExpression,
167 CSSCalcValue::createExpressionNode(CSSPrimitiveValue::create(factor, CSSPrimitiveValue::CSS_NUMBER)),
168 CalcMultiply));
169 }
170 return AnimatableLength::create(m_number * factor, m_unitType);
171 }
172
primitiveUnitToNumberType(unsigned short primitiveUnit,NumberUnitType & numberType)173 bool AnimatableLength::primitiveUnitToNumberType(unsigned short primitiveUnit, NumberUnitType& numberType)
174 {
175 switch (primitiveUnit) {
176 case CSSPrimitiveValue::CSS_PX:
177 case CSSPrimitiveValue::CSS_CM:
178 case CSSPrimitiveValue::CSS_MM:
179 case CSSPrimitiveValue::CSS_IN:
180 case CSSPrimitiveValue::CSS_PT:
181 case CSSPrimitiveValue::CSS_PC:
182 numberType = UnitTypePixels;
183 return true;
184 case CSSPrimitiveValue::CSS_EMS:
185 numberType = UnitTypeFontSize;
186 return true;
187 case CSSPrimitiveValue::CSS_EXS:
188 numberType = UnitTypeFontXSize;
189 return true;
190 case CSSPrimitiveValue::CSS_REMS:
191 numberType = UnitTypeRootFontSize;
192 return true;
193 case CSSPrimitiveValue::CSS_PERCENTAGE:
194 numberType = UnitTypePercentage;
195 return true;
196 case CSSPrimitiveValue::CSS_VW:
197 numberType = UnitTypeViewportWidth;
198 return true;
199 case CSSPrimitiveValue::CSS_VH:
200 numberType = UnitTypeViewportHeight;
201 return true;
202 case CSSPrimitiveValue::CSS_VMIN:
203 numberType = UnitTypeViewportMin;
204 return true;
205 case CSSPrimitiveValue::CSS_VMAX:
206 numberType = UnitTypeViewportMax;
207 return true;
208 default:
209 return false;
210 }
211 }
212
numberTypeToPrimitiveUnit(NumberUnitType numberType)213 unsigned short AnimatableLength::numberTypeToPrimitiveUnit(NumberUnitType numberType)
214 {
215 switch (numberType) {
216 case UnitTypePixels:
217 return CSSPrimitiveValue::CSS_PX;
218 case UnitTypeFontSize:
219 return CSSPrimitiveValue::CSS_EMS;
220 case UnitTypeFontXSize:
221 return CSSPrimitiveValue::CSS_EXS;
222 case UnitTypeRootFontSize:
223 return CSSPrimitiveValue::CSS_REMS;
224 case UnitTypePercentage:
225 return CSSPrimitiveValue::CSS_PERCENTAGE;
226 case UnitTypeViewportWidth:
227 return CSSPrimitiveValue::CSS_VW;
228 case UnitTypeViewportHeight:
229 return CSSPrimitiveValue::CSS_VH;
230 case UnitTypeViewportMin:
231 return CSSPrimitiveValue::CSS_VMIN;
232 case UnitTypeViewportMax:
233 return CSSPrimitiveValue::CSS_VMAX;
234 case UnitTypeCalc:
235 return CSSPrimitiveValue::CSS_UNKNOWN;
236 }
237 ASSERT_NOT_REACHED();
238 return CSSPrimitiveValue::CSS_UNKNOWN;
239 }
240
241 } // namespace WebCore
242