[Pointer Events] touch-action set to pan-x or pan-y alone should disable scrolling...
[WebKit-https.git] / Source / WebCore / css / TransformFunctions.cpp
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
46 namespace WebCore {
47
48 static 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
99 Length convertToFloatLength(const CSSPrimitiveValue* primitiveValue, const CSSToLengthConversionData& conversionData)
100 {
101     return primitiveValue ? primitiveValue->convertToLength<FixedFloatConversion | PercentConversion | CalculatedConversion>(conversionData) : Length(Undefined);
102 }
103
104 bool 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 }