1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 *           (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
7 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
10 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
11 * Copyright (C) 2012 Google Inc. All rights reserved.
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Library General Public
15 * License as published by the Free Software Foundation; either
16 * version 2 of the License, or (at your option) any later version.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 * Library General Public License for more details.
22 *
23 * You should have received a copy of the GNU Library General Public License
24 * along with this library; see the file COPYING.LIB.  If not, write to
25 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 * Boston, MA 02110-1301, USA.
27 */
28
29#include "config.h"
30#include "core/css/resolver/TransformBuilder.h"
31
32#include "core/css/CSSPrimitiveValueMappings.h"
33#include "core/css/CSSTransformValue.h"
34#include "core/rendering/style/RenderStyle.h"
35#include "platform/heap/Handle.h"
36#include "platform/transforms/Matrix3DTransformOperation.h"
37#include "platform/transforms/MatrixTransformOperation.h"
38#include "platform/transforms/PerspectiveTransformOperation.h"
39#include "platform/transforms/RotateTransformOperation.h"
40#include "platform/transforms/ScaleTransformOperation.h"
41#include "platform/transforms/SkewTransformOperation.h"
42#include "platform/transforms/TransformationMatrix.h"
43#include "platform/transforms/TranslateTransformOperation.h"
44
45namespace blink {
46
47static Length convertToFloatLength(CSSPrimitiveValue* primitiveValue, const CSSToLengthConversionData& conversionData)
48{
49    ASSERT(primitiveValue);
50    return primitiveValue->convertToLength<FixedConversion | PercentConversion>(conversionData);
51}
52
53static TransformOperation::OperationType getTransformOperationType(CSSTransformValue::TransformOperationType type)
54{
55    switch (type) {
56    case CSSTransformValue::ScaleTransformOperation: return TransformOperation::Scale;
57    case CSSTransformValue::ScaleXTransformOperation: return TransformOperation::ScaleX;
58    case CSSTransformValue::ScaleYTransformOperation: return TransformOperation::ScaleY;
59    case CSSTransformValue::ScaleZTransformOperation: return TransformOperation::ScaleZ;
60    case CSSTransformValue::Scale3DTransformOperation: return TransformOperation::Scale3D;
61    case CSSTransformValue::TranslateTransformOperation: return TransformOperation::Translate;
62    case CSSTransformValue::TranslateXTransformOperation: return TransformOperation::TranslateX;
63    case CSSTransformValue::TranslateYTransformOperation: return TransformOperation::TranslateY;
64    case CSSTransformValue::TranslateZTransformOperation: return TransformOperation::TranslateZ;
65    case CSSTransformValue::Translate3DTransformOperation: return TransformOperation::Translate3D;
66    case CSSTransformValue::RotateTransformOperation: return TransformOperation::Rotate;
67    case CSSTransformValue::RotateXTransformOperation: return TransformOperation::RotateX;
68    case CSSTransformValue::RotateYTransformOperation: return TransformOperation::RotateY;
69    case CSSTransformValue::RotateZTransformOperation: return TransformOperation::RotateZ;
70    case CSSTransformValue::Rotate3DTransformOperation: return TransformOperation::Rotate3D;
71    case CSSTransformValue::SkewTransformOperation: return TransformOperation::Skew;
72    case CSSTransformValue::SkewXTransformOperation: return TransformOperation::SkewX;
73    case CSSTransformValue::SkewYTransformOperation: return TransformOperation::SkewY;
74    case CSSTransformValue::MatrixTransformOperation: return TransformOperation::Matrix;
75    case CSSTransformValue::Matrix3DTransformOperation: return TransformOperation::Matrix3D;
76    case CSSTransformValue::PerspectiveTransformOperation: return TransformOperation::Perspective;
77    case CSSTransformValue::UnknownTransformOperation: return TransformOperation::None;
78    }
79    return TransformOperation::None;
80}
81
82bool TransformBuilder::createTransformOperations(CSSValue* inValue, const CSSToLengthConversionData& conversionData, TransformOperations& outOperations)
83{
84    if (!inValue || !inValue->isValueList()) {
85        outOperations.clear();
86        return false;
87    }
88
89    float zoomFactor = conversionData.zoom();
90    TransformOperations operations;
91    for (CSSValueListIterator i = inValue; i.hasMore(); i.advance()) {
92        CSSValue* currValue = i.value();
93
94        if (!currValue->isTransformValue())
95            continue;
96
97        CSSTransformValue* transformValue = toCSSTransformValue(i.value());
98        if (!transformValue->length())
99            continue;
100
101        bool haveNonPrimitiveValue = false;
102        for (unsigned j = 0; j < transformValue->length(); ++j) {
103            if (!transformValue->item(j)->isPrimitiveValue()) {
104                haveNonPrimitiveValue = true;
105                break;
106            }
107        }
108        if (haveNonPrimitiveValue)
109            continue;
110
111        CSSPrimitiveValue* firstValue = toCSSPrimitiveValue(transformValue->item(0));
112
113        switch (transformValue->operationType()) {
114        case CSSTransformValue::ScaleTransformOperation:
115        case CSSTransformValue::ScaleXTransformOperation:
116        case CSSTransformValue::ScaleYTransformOperation: {
117            double sx = 1.0;
118            double sy = 1.0;
119            if (transformValue->operationType() == CSSTransformValue::ScaleYTransformOperation)
120                sy = firstValue->getDoubleValue();
121            else {
122                sx = firstValue->getDoubleValue();
123                if (transformValue->operationType() != CSSTransformValue::ScaleXTransformOperation) {
124                    if (transformValue->length() > 1) {
125                        CSSPrimitiveValue* secondValue = toCSSPrimitiveValue(transformValue->item(1));
126                        sy = secondValue->getDoubleValue();
127                    } else
128                        sy = sx;
129                }
130            }
131            operations.operations().append(ScaleTransformOperation::create(sx, sy, 1.0, getTransformOperationType(transformValue->operationType())));
132            break;
133        }
134        case CSSTransformValue::ScaleZTransformOperation:
135        case CSSTransformValue::Scale3DTransformOperation: {
136            double sx = 1.0;
137            double sy = 1.0;
138            double sz = 1.0;
139            if (transformValue->operationType() == CSSTransformValue::ScaleZTransformOperation)
140                sz = firstValue->getDoubleValue();
141            else if (transformValue->operationType() == CSSTransformValue::ScaleYTransformOperation)
142                sy = firstValue->getDoubleValue();
143            else {
144                sx = firstValue->getDoubleValue();
145                if (transformValue->operationType() != CSSTransformValue::ScaleXTransformOperation) {
146                    if (transformValue->length() > 2) {
147                        CSSPrimitiveValue* thirdValue = toCSSPrimitiveValue(transformValue->item(2));
148                        sz = thirdValue->getDoubleValue();
149                    }
150                    if (transformValue->length() > 1) {
151                        CSSPrimitiveValue* secondValue = toCSSPrimitiveValue(transformValue->item(1));
152                        sy = secondValue->getDoubleValue();
153                    } else
154                        sy = sx;
155                }
156            }
157            operations.operations().append(ScaleTransformOperation::create(sx, sy, sz, getTransformOperationType(transformValue->operationType())));
158            break;
159        }
160        case CSSTransformValue::TranslateTransformOperation:
161        case CSSTransformValue::TranslateXTransformOperation:
162        case CSSTransformValue::TranslateYTransformOperation: {
163            Length tx = Length(0, Fixed);
164            Length ty = Length(0, Fixed);
165            if (transformValue->operationType() == CSSTransformValue::TranslateYTransformOperation)
166                ty = convertToFloatLength(firstValue, conversionData);
167            else {
168                tx = convertToFloatLength(firstValue, conversionData);
169                if (transformValue->operationType() != CSSTransformValue::TranslateXTransformOperation) {
170                    if (transformValue->length() > 1) {
171                        CSSPrimitiveValue* secondValue = toCSSPrimitiveValue(transformValue->item(1));
172                        ty = convertToFloatLength(secondValue, conversionData);
173                    }
174                }
175            }
176
177            operations.operations().append(TranslateTransformOperation::create(tx, ty, 0, getTransformOperationType(transformValue->operationType())));
178            break;
179        }
180        case CSSTransformValue::TranslateZTransformOperation:
181        case CSSTransformValue::Translate3DTransformOperation: {
182            Length tx = Length(0, Fixed);
183            Length ty = Length(0, Fixed);
184            double tz = 0;
185            if (transformValue->operationType() == CSSTransformValue::TranslateZTransformOperation)
186                tz = firstValue->computeLength<double>(conversionData);
187            else if (transformValue->operationType() == CSSTransformValue::TranslateYTransformOperation)
188                ty = convertToFloatLength(firstValue, conversionData);
189            else {
190                tx = convertToFloatLength(firstValue, conversionData);
191                if (transformValue->operationType() != CSSTransformValue::TranslateXTransformOperation) {
192                    if (transformValue->length() > 2) {
193                        CSSPrimitiveValue* thirdValue = toCSSPrimitiveValue(transformValue->item(2));
194                        tz = thirdValue->computeLength<double>(conversionData);
195                    }
196                    if (transformValue->length() > 1) {
197                        CSSPrimitiveValue* secondValue = toCSSPrimitiveValue(transformValue->item(1));
198                        ty = convertToFloatLength(secondValue, conversionData);
199                    }
200                }
201            }
202
203            operations.operations().append(TranslateTransformOperation::create(tx, ty, tz, getTransformOperationType(transformValue->operationType())));
204            break;
205        }
206        case CSSTransformValue::RotateTransformOperation: {
207            double angle = firstValue->computeDegrees();
208            operations.operations().append(RotateTransformOperation::create(0, 0, 1, angle, getTransformOperationType(transformValue->operationType())));
209            break;
210        }
211        case CSSTransformValue::RotateXTransformOperation:
212        case CSSTransformValue::RotateYTransformOperation:
213        case CSSTransformValue::RotateZTransformOperation: {
214            double x = 0;
215            double y = 0;
216            double z = 0;
217            double angle = firstValue->computeDegrees();
218
219            if (transformValue->operationType() == CSSTransformValue::RotateXTransformOperation)
220                x = 1;
221            else if (transformValue->operationType() == CSSTransformValue::RotateYTransformOperation)
222                y = 1;
223            else
224                z = 1;
225            operations.operations().append(RotateTransformOperation::create(x, y, z, angle, getTransformOperationType(transformValue->operationType())));
226            break;
227        }
228        case CSSTransformValue::Rotate3DTransformOperation: {
229            if (transformValue->length() < 4)
230                break;
231            CSSPrimitiveValue* secondValue = toCSSPrimitiveValue(transformValue->item(1));
232            CSSPrimitiveValue* thirdValue = toCSSPrimitiveValue(transformValue->item(2));
233            CSSPrimitiveValue* fourthValue = toCSSPrimitiveValue(transformValue->item(3));
234            double x = firstValue->getDoubleValue();
235            double y = secondValue->getDoubleValue();
236            double z = thirdValue->getDoubleValue();
237            double angle = fourthValue->computeDegrees();
238            operations.operations().append(RotateTransformOperation::create(x, y, z, angle, getTransformOperationType(transformValue->operationType())));
239            break;
240        }
241        case CSSTransformValue::SkewTransformOperation:
242        case CSSTransformValue::SkewXTransformOperation:
243        case CSSTransformValue::SkewYTransformOperation: {
244            double angleX = 0;
245            double angleY = 0;
246            double angle = firstValue->computeDegrees();
247            if (transformValue->operationType() == CSSTransformValue::SkewYTransformOperation)
248                angleY = angle;
249            else {
250                angleX = angle;
251                if (transformValue->operationType() == CSSTransformValue::SkewTransformOperation) {
252                    if (transformValue->length() > 1) {
253                        CSSPrimitiveValue* secondValue = toCSSPrimitiveValue(transformValue->item(1));
254                        angleY = secondValue->computeDegrees();
255                    }
256                }
257            }
258            operations.operations().append(SkewTransformOperation::create(angleX, angleY, getTransformOperationType(transformValue->operationType())));
259            break;
260        }
261        case CSSTransformValue::MatrixTransformOperation: {
262            if (transformValue->length() < 6)
263                break;
264            double a = firstValue->getDoubleValue();
265            double b = toCSSPrimitiveValue(transformValue->item(1))->getDoubleValue();
266            double c = toCSSPrimitiveValue(transformValue->item(2))->getDoubleValue();
267            double d = toCSSPrimitiveValue(transformValue->item(3))->getDoubleValue();
268            double e = zoomFactor * toCSSPrimitiveValue(transformValue->item(4))->getDoubleValue();
269            double f = zoomFactor * toCSSPrimitiveValue(transformValue->item(5))->getDoubleValue();
270            operations.operations().append(MatrixTransformOperation::create(a, b, c, d, e, f));
271            break;
272        }
273        case CSSTransformValue::Matrix3DTransformOperation: {
274            if (transformValue->length() < 16)
275                break;
276            TransformationMatrix matrix(toCSSPrimitiveValue(transformValue->item(0))->getDoubleValue(),
277                toCSSPrimitiveValue(transformValue->item(1))->getDoubleValue(),
278                toCSSPrimitiveValue(transformValue->item(2))->getDoubleValue(),
279                toCSSPrimitiveValue(transformValue->item(3))->getDoubleValue(),
280                toCSSPrimitiveValue(transformValue->item(4))->getDoubleValue(),
281                toCSSPrimitiveValue(transformValue->item(5))->getDoubleValue(),
282                toCSSPrimitiveValue(transformValue->item(6))->getDoubleValue(),
283                toCSSPrimitiveValue(transformValue->item(7))->getDoubleValue(),
284                toCSSPrimitiveValue(transformValue->item(8))->getDoubleValue(),
285                toCSSPrimitiveValue(transformValue->item(9))->getDoubleValue(),
286                toCSSPrimitiveValue(transformValue->item(10))->getDoubleValue(),
287                toCSSPrimitiveValue(transformValue->item(11))->getDoubleValue(),
288                zoomFactor * toCSSPrimitiveValue(transformValue->item(12))->getDoubleValue(),
289                zoomFactor * toCSSPrimitiveValue(transformValue->item(13))->getDoubleValue(),
290                toCSSPrimitiveValue(transformValue->item(14))->getDoubleValue(),
291                toCSSPrimitiveValue(transformValue->item(15))->getDoubleValue());
292            operations.operations().append(Matrix3DTransformOperation::create(matrix));
293            break;
294        }
295        case CSSTransformValue::PerspectiveTransformOperation: {
296            double p;
297            if (firstValue->isLength())
298                p = firstValue->computeLength<double>(conversionData);
299            else {
300                // This is a quirk that should go away when 3d transforms are finalized.
301                double val = firstValue->getDoubleValue();
302                if (val < 0)
303                    return false;
304                p = clampToPositiveInteger(val);
305            }
306
307            operations.operations().append(PerspectiveTransformOperation::create(p));
308            break;
309        }
310        case CSSTransformValue::UnknownTransformOperation:
311            ASSERT_NOT_REACHED();
312            break;
313        }
314    }
315
316    outOperations = operations;
317    return true;
318}
319
320} // namespace blink
321