1/*
2 * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
3 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Copyright (C) 2012, 2013 Adobe Systems Incorporated. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer.
13 * 2. Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials
16 * provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "TransformFunctions.h"
34
35#include "CSSFunctionValue.h"
36#include "CSSPrimitiveValueMappings.h"
37#include "CSSValueList.h"
38#include "Matrix3DTransformOperation.h"
39#include "MatrixTransformOperation.h"
40#include "PerspectiveTransformOperation.h"
41#include "RotateTransformOperation.h"
42#include "ScaleTransformOperation.h"
43#include "SkewTransformOperation.h"
44#include "TranslateTransformOperation.h"
45
46namespace WebCore {
47
48static TransformOperation::OperationType transformOperationType(CSSValueID type)
49{
50 switch (type) {
51 case CSSValueScale:
52 return TransformOperation::SCALE;
53 case CSSValueScaleX:
54 return TransformOperation::SCALE_X;
55 case CSSValueScaleY:
56 return TransformOperation::SCALE_Y;
57 case CSSValueScaleZ:
58 return TransformOperation::SCALE_Z;
59 case CSSValueScale3d:
60 return TransformOperation::SCALE_3D;
61 case CSSValueTranslate:
62 return TransformOperation::TRANSLATE;
63 case CSSValueTranslateX:
64 return TransformOperation::TRANSLATE_X;
65 case CSSValueTranslateY:
66 return TransformOperation::TRANSLATE_Y;
67 case CSSValueTranslateZ:
68 return TransformOperation::TRANSLATE_Z;
69 case CSSValueTranslate3d:
70 return TransformOperation::TRANSLATE_3D;
71 case CSSValueRotate:
72 return TransformOperation::ROTATE;
73 case CSSValueRotateX:
74 return TransformOperation::ROTATE_X;
75 case CSSValueRotateY:
76 return TransformOperation::ROTATE_Y;
77 case CSSValueRotateZ:
78 return TransformOperation::ROTATE_Z;
79 case CSSValueRotate3d:
80 return TransformOperation::ROTATE_3D;
81 case CSSValueSkew:
82 return TransformOperation::SKEW;
83 case CSSValueSkewX:
84 return TransformOperation::SKEW_X;
85 case CSSValueSkewY:
86 return TransformOperation::SKEW_Y;
87 case CSSValueMatrix:
88 return TransformOperation::MATRIX;
89 case CSSValueMatrix3d:
90 return TransformOperation::MATRIX_3D;
91 case CSSValuePerspective:
92 return TransformOperation::PERSPECTIVE;
93 default:
94 break;
95 }
96 return TransformOperation::NONE;
97}
98
99Length convertToFloatLength(const CSSPrimitiveValue* primitiveValue, const CSSToLengthConversionData& conversionData)
100{
101 return primitiveValue ? primitiveValue->convertToLength<FixedFloatConversion | PercentConversion | CalculatedConversion>(conversionData) : Length(Undefined);
102}
103
104bool transformsForValue(const CSSValue& value, const CSSToLengthConversionData& conversionData, TransformOperations& outOperations)
105{
106 if (!is<CSSValueList>(value)) {
107 outOperations.clear();
108 return false;
109 }
110
111 TransformOperations operations;
112 for (auto& currentValue : downcast<CSSValueList>(value)) {
113 if (!is<CSSFunctionValue>(currentValue))
114 continue;
115
116 auto& transformValue = downcast<CSSFunctionValue>(currentValue.get());
117 if (!transformValue.length())
118 continue;
119
120 bool haveNonPrimitiveValue = false;
121 for (unsigned j = 0; j < transformValue.length(); ++j) {
122 if (!is<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(j))) {
123 haveNonPrimitiveValue = true;
124 break;
125 }
126 }
127 if (haveNonPrimitiveValue)
128 continue;
129
130 auto& firstValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(0));
131
132 switch (transformValue.name()) {
133 case CSSValueScale:
134 case CSSValueScaleX:
135 case CSSValueScaleY: {
136 double sx = 1.0;
137 double sy = 1.0;
138 if (transformValue.name() == CSSValueScaleY)
139 sy = firstValue.doubleValue();
140 else {
141 sx = firstValue.doubleValue();
142 if (transformValue.name() != CSSValueScaleX) {
143 if (transformValue.length() > 1) {
144 auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
145 sy = secondValue.doubleValue();
146 } else
147 sy = sx;
148 }
149 }
150 operations.operations().append(ScaleTransformOperation::create(sx, sy, 1.0, transformOperationType(transformValue.name())));
151 break;
152 }
153 case CSSValueScaleZ:
154 case CSSValueScale3d: {
155 double sx = 1.0;
156 double sy = 1.0;
157 double sz = 1.0;
158 if (transformValue.name() == CSSValueScaleZ)
159 sz = firstValue.doubleValue();
160 else if (transformValue.name() == CSSValueScaleY)
161 sy = firstValue.doubleValue();
162 else {
163 sx = firstValue.doubleValue();
164 if (transformValue.name() != CSSValueScaleX) {
165 if (transformValue.length() > 2) {
166 auto& thirdValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(2));
167 sz = thirdValue.doubleValue();
168 }
169 if (transformValue.length() > 1) {
170 auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
171 sy = secondValue.doubleValue();
172 } else
173 sy = sx;
174 }
175 }
176 operations.operations().append(ScaleTransformOperation::create(sx, sy, sz, transformOperationType(transformValue.name())));
177 break;
178 }
179 case CSSValueTranslate:
180 case CSSValueTranslateX:
181 case CSSValueTranslateY: {
182 Length tx = Length(0, Fixed);
183 Length ty = Length(0, Fixed);
184 if (transformValue.name() == CSSValueTranslateY)
185 ty = convertToFloatLength(&firstValue, conversionData);
186 else {
187 tx = convertToFloatLength(&firstValue, conversionData);
188 if (transformValue.name() != CSSValueTranslateX) {
189 if (transformValue.length() > 1) {
190 auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
191 ty = convertToFloatLength(&secondValue, conversionData);
192 }
193 }
194 }
195
196 if (tx.isUndefined() || ty.isUndefined())
197 return false;
198
199 operations.operations().append(TranslateTransformOperation::create(tx, ty, Length(0, Fixed), transformOperationType(transformValue.name())));
200 break;
201 }
202 case CSSValueTranslateZ:
203 case CSSValueTranslate3d: {
204 Length tx = Length(0, Fixed);
205 Length ty = Length(0, Fixed);
206 Length tz = Length(0, Fixed);
207 if (transformValue.name() == CSSValueTranslateZ)
208 tz = convertToFloatLength(&firstValue, conversionData);
209 else if (transformValue.name() == CSSValueTranslateY)
210 ty = convertToFloatLength(&firstValue, conversionData);
211 else {
212 tx = convertToFloatLength(&firstValue, conversionData);
213 if (transformValue.name() != CSSValueTranslateX) {
214 if (transformValue.length() > 2) {
215 auto& thirdValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(2));
216 tz = convertToFloatLength(&thirdValue, conversionData);
217 }
218 if (transformValue.length() > 1) {
219 auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
220 ty = convertToFloatLength(&secondValue, conversionData);
221 }
222 }
223 }
224
225 if (tx.isUndefined() || ty.isUndefined() || tz.isUndefined())
226 return false;
227
228 operations.operations().append(TranslateTransformOperation::create(tx, ty, tz, transformOperationType(transformValue.name())));
229 break;
230 }
231 case CSSValueRotate: {
232 double angle = firstValue.computeDegrees();
233 operations.operations().append(RotateTransformOperation::create(0, 0, 1, angle, transformOperationType(transformValue.name())));
234 break;
235 }
236 case CSSValueRotateX:
237 case CSSValueRotateY:
238 case CSSValueRotateZ: {
239 double x = 0;
240 double y = 0;
241 double z = 0;
242 double angle = firstValue.computeDegrees();
243
244 if (transformValue.name() == CSSValueRotateX)
245 x = 1;
246 else if (transformValue.name() == CSSValueRotateY)
247 y = 1;
248 else
249 z = 1;
250 operations.operations().append(RotateTransformOperation::create(x, y, z, angle, transformOperationType(transformValue.name())));
251 break;
252 }
253 case CSSValueRotate3d: {
254 if (transformValue.length() < 4)
255 break;
256 auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
257 auto& thirdValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(2));
258 auto& fourthValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(3));
259 double x = firstValue.doubleValue();
260 double y = secondValue.doubleValue();
261 double z = thirdValue.doubleValue();
262 double angle = fourthValue.computeDegrees();
263 operations.operations().append(RotateTransformOperation::create(x, y, z, angle, transformOperationType(transformValue.name())));
264 break;
265 }
266 case CSSValueSkew:
267 case CSSValueSkewX:
268 case CSSValueSkewY: {
269 double angleX = 0;
270 double angleY = 0;
271 double angle = firstValue.computeDegrees();
272 if (transformValue.name() == CSSValueSkewY)
273 angleY = angle;
274 else {
275 angleX = angle;
276 if (transformValue.name() == CSSValueSkew) {
277 if (transformValue.length() > 1) {
278 auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
279 angleY = secondValue.computeDegrees();
280 }
281 }
282 }
283 operations.operations().append(SkewTransformOperation::create(angleX, angleY, transformOperationType(transformValue.name())));
284 break;
285 }
286 case CSSValueMatrix: {
287 if (transformValue.length() < 6)
288 break;
289 double a = firstValue.doubleValue();
290 double b = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1)).doubleValue();
291 double c = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(2)).doubleValue();
292 double d = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(3)).doubleValue();
293 double e = conversionData.zoom() * downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(4)).doubleValue();
294 double f = conversionData.zoom() * downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(5)).doubleValue();
295 operations.operations().append(MatrixTransformOperation::create(a, b, c, d, e, f));
296 break;
297 }
298 case CSSValueMatrix3d: {
299 if (transformValue.length() < 16)
300 break;
301 TransformationMatrix matrix(downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(0)).doubleValue(),
302 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1)).doubleValue(),
303 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(2)).doubleValue(),
304 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(3)).doubleValue(),
305 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(4)).doubleValue(),
306 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(5)).doubleValue(),
307 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(6)).doubleValue(),
308 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(7)).doubleValue(),
309 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(8)).doubleValue(),
310 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(9)).doubleValue(),
311 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(10)).doubleValue(),
312 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(11)).doubleValue(),
313 conversionData.zoom() * downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(12)).doubleValue(),
314 conversionData.zoom() * downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(13)).doubleValue(),
315 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(14)).doubleValue(),
316 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(15)).doubleValue());
317 operations.operations().append(Matrix3DTransformOperation::create(matrix));
318 break;
319 }
320 case CSSValuePerspective: {
321 Length p = Length(0, Fixed);
322 if (firstValue.isLength())
323 p = convertToFloatLength(&firstValue, conversionData);
324 else {
325 // This is a quirk that should go away when 3d transforms are finalized.
326 double val = firstValue.doubleValue();
327 p = val >= 0 ? Length(clampToPositiveInteger(val), Fixed) : Length(Undefined);
328 }
329
330 if (p.isUndefined())
331 return false;
332
333 operations.operations().append(PerspectiveTransformOperation::create(p));
334 break;
335 }
336 default:
337 ASSERT_NOT_REACHED();
338 break;
339 }
340 }
341
342 outOperations = operations;
343 return true;
344}
345
346}
347