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 "CSSPrimitiveValueMappings.h"
36#include "CSSValueList.h"
37#include "CSSValuePool.h"
38#include "Matrix3DTransformOperation.h"
39#include "MatrixTransformOperation.h"
40#include "PerspectiveTransformOperation.h"
41#include "RenderStyle.h"
42#include "RotateTransformOperation.h"
43#include "ScaleTransformOperation.h"
44#include "SkewTransformOperation.h"
45#include "TranslateTransformOperation.h"
46#include "WebKitCSSTransformValue.h"
47
48namespace WebCore {
49
50static TransformOperation::OperationType transformOperationType(WebKitCSSTransformValue::TransformOperationType type)
51{
52    switch (type) {
53    case WebKitCSSTransformValue::ScaleTransformOperation: return TransformOperation::SCALE;
54    case WebKitCSSTransformValue::ScaleXTransformOperation: return TransformOperation::SCALE_X;
55    case WebKitCSSTransformValue::ScaleYTransformOperation: return TransformOperation::SCALE_Y;
56    case WebKitCSSTransformValue::ScaleZTransformOperation: return TransformOperation::SCALE_Z;
57    case WebKitCSSTransformValue::Scale3DTransformOperation: return TransformOperation::SCALE_3D;
58    case WebKitCSSTransformValue::TranslateTransformOperation: return TransformOperation::TRANSLATE;
59    case WebKitCSSTransformValue::TranslateXTransformOperation: return TransformOperation::TRANSLATE_X;
60    case WebKitCSSTransformValue::TranslateYTransformOperation: return TransformOperation::TRANSLATE_Y;
61    case WebKitCSSTransformValue::TranslateZTransformOperation: return TransformOperation::TRANSLATE_Z;
62    case WebKitCSSTransformValue::Translate3DTransformOperation: return TransformOperation::TRANSLATE_3D;
63    case WebKitCSSTransformValue::RotateTransformOperation: return TransformOperation::ROTATE;
64    case WebKitCSSTransformValue::RotateXTransformOperation: return TransformOperation::ROTATE_X;
65    case WebKitCSSTransformValue::RotateYTransformOperation: return TransformOperation::ROTATE_Y;
66    case WebKitCSSTransformValue::RotateZTransformOperation: return TransformOperation::ROTATE_Z;
67    case WebKitCSSTransformValue::Rotate3DTransformOperation: return TransformOperation::ROTATE_3D;
68    case WebKitCSSTransformValue::SkewTransformOperation: return TransformOperation::SKEW;
69    case WebKitCSSTransformValue::SkewXTransformOperation: return TransformOperation::SKEW_X;
70    case WebKitCSSTransformValue::SkewYTransformOperation: return TransformOperation::SKEW_Y;
71    case WebKitCSSTransformValue::MatrixTransformOperation: return TransformOperation::MATRIX;
72    case WebKitCSSTransformValue::Matrix3DTransformOperation: return TransformOperation::MATRIX_3D;
73    case WebKitCSSTransformValue::PerspectiveTransformOperation: return TransformOperation::PERSPECTIVE;
74    case WebKitCSSTransformValue::UnknownTransformOperation: return TransformOperation::NONE;
75    }
76    return TransformOperation::NONE;
77}
78
79static Length convertToFloatLength(const CSSPrimitiveValue* primitiveValue, const RenderStyle* style, const RenderStyle* rootStyle, double multiplier)
80{
81    return primitiveValue ? primitiveValue->convertToLength<FixedFloatConversion | PercentConversion | CalculatedConversion | FractionConversion | ViewportPercentageConversion>(style, rootStyle, multiplier) : Length(Undefined);
82}
83
84bool transformsForValue(const RenderStyle* style, const RenderStyle* rootStyle, CSSValue* value, TransformOperations& outOperations)
85{
86    if (!value || !value->isValueList()) {
87        outOperations.clear();
88        return false;
89    }
90
91    float zoomFactor = style ? style->effectiveZoom() : 1;
92    TransformOperations operations;
93    for (CSSValueListIterator i = value; i.hasMore(); i.advance()) {
94        CSSValue* currValue = i.value();
95
96        if (!currValue->isWebKitCSSTransformValue())
97            continue;
98
99        WebKitCSSTransformValue* transformValue = static_cast<WebKitCSSTransformValue*>(i.value());
100        if (!transformValue->length())
101            continue;
102
103        bool haveNonPrimitiveValue = false;
104        for (unsigned j = 0; j < transformValue->length(); ++j) {
105            if (!transformValue->itemWithoutBoundsCheck(j)->isPrimitiveValue()) {
106                haveNonPrimitiveValue = true;
107                break;
108            }
109        }
110        if (haveNonPrimitiveValue)
111            continue;
112
113        CSSPrimitiveValue* firstValue = static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(0));
114
115        switch (transformValue->operationType()) {
116        case WebKitCSSTransformValue::ScaleTransformOperation:
117        case WebKitCSSTransformValue::ScaleXTransformOperation:
118        case WebKitCSSTransformValue::ScaleYTransformOperation: {
119            double sx = 1.0;
120            double sy = 1.0;
121            if (transformValue->operationType() == WebKitCSSTransformValue::ScaleYTransformOperation)
122                sy = firstValue->getDoubleValue();
123            else {
124                sx = firstValue->getDoubleValue();
125                if (transformValue->operationType() != WebKitCSSTransformValue::ScaleXTransformOperation) {
126                    if (transformValue->length() > 1) {
127                        CSSPrimitiveValue* secondValue = static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(1));
128                        sy = secondValue->getDoubleValue();
129                    } else
130                        sy = sx;
131                }
132            }
133            operations.operations().append(ScaleTransformOperation::create(sx, sy, 1.0, transformOperationType(transformValue->operationType())));
134            break;
135        }
136        case WebKitCSSTransformValue::ScaleZTransformOperation:
137        case WebKitCSSTransformValue::Scale3DTransformOperation: {
138            double sx = 1.0;
139            double sy = 1.0;
140            double sz = 1.0;
141            if (transformValue->operationType() == WebKitCSSTransformValue::ScaleZTransformOperation)
142                sz = firstValue->getDoubleValue();
143            else if (transformValue->operationType() == WebKitCSSTransformValue::ScaleYTransformOperation)
144                sy = firstValue->getDoubleValue();
145            else {
146                sx = firstValue->getDoubleValue();
147                if (transformValue->operationType() != WebKitCSSTransformValue::ScaleXTransformOperation) {
148                    if (transformValue->length() > 2) {
149                        CSSPrimitiveValue* thirdValue = static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(2));
150                        sz = thirdValue->getDoubleValue();
151                    }
152                    if (transformValue->length() > 1) {
153                        CSSPrimitiveValue* secondValue = static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(1));
154                        sy = secondValue->getDoubleValue();
155                    } else
156                        sy = sx;
157                }
158            }
159            operations.operations().append(ScaleTransformOperation::create(sx, sy, sz, transformOperationType(transformValue->operationType())));
160            break;
161        }
162        case WebKitCSSTransformValue::TranslateTransformOperation:
163        case WebKitCSSTransformValue::TranslateXTransformOperation:
164        case WebKitCSSTransformValue::TranslateYTransformOperation: {
165            Length tx = Length(0, Fixed);
166            Length ty = Length(0, Fixed);
167            if (transformValue->operationType() == WebKitCSSTransformValue::TranslateYTransformOperation)
168                ty = convertToFloatLength(firstValue, style, rootStyle, zoomFactor);
169            else {
170                tx = convertToFloatLength(firstValue, style, rootStyle, zoomFactor);
171                if (transformValue->operationType() != WebKitCSSTransformValue::TranslateXTransformOperation) {
172                    if (transformValue->length() > 1) {
173                        CSSPrimitiveValue* secondValue = static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(1));
174                        ty = convertToFloatLength(secondValue, style, rootStyle, zoomFactor);
175                    }
176                }
177            }
178
179            if (tx.isUndefined() || ty.isUndefined())
180                return false;
181
182            operations.operations().append(TranslateTransformOperation::create(tx, ty, Length(0, Fixed), transformOperationType(transformValue->operationType())));
183            break;
184        }
185        case WebKitCSSTransformValue::TranslateZTransformOperation:
186        case WebKitCSSTransformValue::Translate3DTransformOperation: {
187            Length tx = Length(0, Fixed);
188            Length ty = Length(0, Fixed);
189            Length tz = Length(0, Fixed);
190            if (transformValue->operationType() == WebKitCSSTransformValue::TranslateZTransformOperation)
191                tz = convertToFloatLength(firstValue, style, rootStyle, zoomFactor);
192            else if (transformValue->operationType() == WebKitCSSTransformValue::TranslateYTransformOperation)
193                ty = convertToFloatLength(firstValue, style, rootStyle, zoomFactor);
194            else {
195                tx = convertToFloatLength(firstValue, style, rootStyle, zoomFactor);
196                if (transformValue->operationType() != WebKitCSSTransformValue::TranslateXTransformOperation) {
197                    if (transformValue->length() > 2) {
198                        CSSPrimitiveValue* thirdValue = static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(2));
199                        tz = convertToFloatLength(thirdValue, style, rootStyle, zoomFactor);
200                    }
201                    if (transformValue->length() > 1) {
202                        CSSPrimitiveValue* secondValue = static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(1));
203                        ty = convertToFloatLength(secondValue, style, rootStyle, zoomFactor);
204                    }
205                }
206            }
207
208            if (tx.isUndefined() || ty.isUndefined() || tz.isUndefined())
209                return false;
210
211            operations.operations().append(TranslateTransformOperation::create(tx, ty, tz, transformOperationType(transformValue->operationType())));
212            break;
213        }
214        case WebKitCSSTransformValue::RotateTransformOperation: {
215            double angle = firstValue->computeDegrees();
216            operations.operations().append(RotateTransformOperation::create(0, 0, 1, angle, transformOperationType(transformValue->operationType())));
217            break;
218        }
219        case WebKitCSSTransformValue::RotateXTransformOperation:
220        case WebKitCSSTransformValue::RotateYTransformOperation:
221        case WebKitCSSTransformValue::RotateZTransformOperation: {
222            double x = 0;
223            double y = 0;
224            double z = 0;
225            double angle = firstValue->computeDegrees();
226
227            if (transformValue->operationType() == WebKitCSSTransformValue::RotateXTransformOperation)
228                x = 1;
229            else if (transformValue->operationType() == WebKitCSSTransformValue::RotateYTransformOperation)
230                y = 1;
231            else
232                z = 1;
233            operations.operations().append(RotateTransformOperation::create(x, y, z, angle, transformOperationType(transformValue->operationType())));
234            break;
235        }
236        case WebKitCSSTransformValue::Rotate3DTransformOperation: {
237            if (transformValue->length() < 4)
238                break;
239            CSSPrimitiveValue* secondValue = static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(1));
240            CSSPrimitiveValue* thirdValue = static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(2));
241            CSSPrimitiveValue* fourthValue = static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(3));
242            double x = firstValue->getDoubleValue();
243            double y = secondValue->getDoubleValue();
244            double z = thirdValue->getDoubleValue();
245            double angle = fourthValue->computeDegrees();
246            operations.operations().append(RotateTransformOperation::create(x, y, z, angle, transformOperationType(transformValue->operationType())));
247            break;
248        }
249        case WebKitCSSTransformValue::SkewTransformOperation:
250        case WebKitCSSTransformValue::SkewXTransformOperation:
251        case WebKitCSSTransformValue::SkewYTransformOperation: {
252            double angleX = 0;
253            double angleY = 0;
254            double angle = firstValue->computeDegrees();
255            if (transformValue->operationType() == WebKitCSSTransformValue::SkewYTransformOperation)
256                angleY = angle;
257            else {
258                angleX = angle;
259                if (transformValue->operationType() == WebKitCSSTransformValue::SkewTransformOperation) {
260                    if (transformValue->length() > 1) {
261                        CSSPrimitiveValue* secondValue = static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(1));
262                        angleY = secondValue->computeDegrees();
263                    }
264                }
265            }
266            operations.operations().append(SkewTransformOperation::create(angleX, angleY, transformOperationType(transformValue->operationType())));
267            break;
268        }
269        case WebKitCSSTransformValue::MatrixTransformOperation: {
270            if (transformValue->length() < 6)
271                break;
272            double a = firstValue->getDoubleValue();
273            double b = static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(1))->getDoubleValue();
274            double c = static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(2))->getDoubleValue();
275            double d = static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(3))->getDoubleValue();
276            double e = zoomFactor * static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(4))->getDoubleValue();
277            double f = zoomFactor * static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(5))->getDoubleValue();
278            operations.operations().append(MatrixTransformOperation::create(a, b, c, d, e, f));
279            break;
280        }
281        case WebKitCSSTransformValue::Matrix3DTransformOperation: {
282            if (transformValue->length() < 16)
283                break;
284            TransformationMatrix matrix(static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(0))->getDoubleValue(),
285                                static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(1))->getDoubleValue(),
286                                static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(2))->getDoubleValue(),
287                                static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(3))->getDoubleValue(),
288                                static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(4))->getDoubleValue(),
289                                static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(5))->getDoubleValue(),
290                                static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(6))->getDoubleValue(),
291                                static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(7))->getDoubleValue(),
292                                static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(8))->getDoubleValue(),
293                                static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(9))->getDoubleValue(),
294                                static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(10))->getDoubleValue(),
295                                static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(11))->getDoubleValue(),
296                                zoomFactor * static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(12))->getDoubleValue(),
297                                zoomFactor * static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(13))->getDoubleValue(),
298                                static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(14))->getDoubleValue(),
299                                static_cast<CSSPrimitiveValue*>(transformValue->itemWithoutBoundsCheck(15))->getDoubleValue());
300            operations.operations().append(Matrix3DTransformOperation::create(matrix));
301            break;
302        }
303        case WebKitCSSTransformValue::PerspectiveTransformOperation: {
304            Length p = Length(0, Fixed);
305            if (firstValue->isLength())
306                p = convertToFloatLength(firstValue, style, rootStyle, zoomFactor);
307            else {
308                // This is a quirk that should go away when 3d transforms are finalized.
309                double val = firstValue->getDoubleValue();
310                p = val >= 0 ? Length(clampToPositiveInteger(val), Fixed) : Length(Undefined);
311            }
312
313            if (p.isUndefined())
314                return false;
315
316            operations.operations().append(PerspectiveTransformOperation::create(p));
317            break;
318        }
319        case WebKitCSSTransformValue::UnknownTransformOperation:
320            ASSERT_NOT_REACHED();
321            break;
322        }
323    }
324
325    outOperations = operations;
326    return true;
327}
328
329}
330