source: webkit/trunk/Source/WebCore/css/TransformFunctions.cpp

Last change on this file was 295425, checked in by [email protected], 3 years ago

convertingToLengthRequiresNonNullStyle needs to consider calc values
https://bugs.webkit.org/show_bug.cgi?id=241452

Reviewed by Antti Koivisto.

  • LayoutTests/fast/css/matrix-translate-calc-units.html: Added.
  • Source/WebCore/css/CSSPrimitiveValueMappings.h:

(WebCore::CSSPrimitiveValue::convertingToLengthRequiresNonNullStyle const):
(WebCore::CSSPrimitiveValue::convertToLength const):

  • Source/WebCore/css/TransformFunctions.cpp:
  • Source/WebCore/css/calc/CSSCalcExpressionNode.h:
  • Source/WebCore/css/calc/CSSCalcInvertNode.h:
  • Source/WebCore/css/calc/CSSCalcNegateNode.h:
  • Source/WebCore/css/calc/CSSCalcOperationNode.cpp:

(WebCore::CSSCalcOperationNode::convertingToLengthRequiresNonNullStyle const):

  • Source/WebCore/css/calc/CSSCalcOperationNode.h:
  • Source/WebCore/css/calc/CSSCalcPrimitiveValueNode.cpp:

(WebCore::CSSCalcPrimitiveValueNode::convertingToLengthRequiresNonNullStyle const):

  • Source/WebCore/css/calc/CSSCalcPrimitiveValueNode.h:
  • Source/WebCore/css/calc/CSSCalcValue.cpp:

(WebCore::CSSCalcValue::convertingToLengthRequiresNonNullStyle const):

  • Source/WebCore/css/calc/CSSCalcValue.h:

Canonical link: https://commits.webkit.org/251431@main

File size: 20.7 KB
Line 
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#include <wtf/text/StringConcatenateNumbers.h>
46
47namespace WebCore {
48
49static TransformOperation::OperationType transformOperationType(CSSValueID type)
50{
51 switch (type) {
52 case CSSValueScale:
53 return TransformOperation::SCALE;
54 case CSSValueScaleX:
55 return TransformOperation::SCALE_X;
56 case CSSValueScaleY:
57 return TransformOperation::SCALE_Y;
58 case CSSValueScaleZ:
59 return TransformOperation::SCALE_Z;
60 case CSSValueScale3d:
61 return TransformOperation::SCALE_3D;
62 case CSSValueTranslate:
63 return TransformOperation::TRANSLATE;
64 case CSSValueTranslateX:
65 return TransformOperation::TRANSLATE_X;
66 case CSSValueTranslateY:
67 return TransformOperation::TRANSLATE_Y;
68 case CSSValueTranslateZ:
69 return TransformOperation::TRANSLATE_Z;
70 case CSSValueTranslate3d:
71 return TransformOperation::TRANSLATE_3D;
72 case CSSValueRotate:
73 return TransformOperation::ROTATE;
74 case CSSValueRotateX:
75 return TransformOperation::ROTATE_X;
76 case CSSValueRotateY:
77 return TransformOperation::ROTATE_Y;
78 case CSSValueRotateZ:
79 return TransformOperation::ROTATE_Z;
80 case CSSValueRotate3d:
81 return TransformOperation::ROTATE_3D;
82 case CSSValueSkew:
83 return TransformOperation::SKEW;
84 case CSSValueSkewX:
85 return TransformOperation::SKEW_X;
86 case CSSValueSkewY:
87 return TransformOperation::SKEW_Y;
88 case CSSValueMatrix:
89 return TransformOperation::MATRIX;
90 case CSSValueMatrix3d:
91 return TransformOperation::MATRIX_3D;
92 case CSSValuePerspective:
93 return TransformOperation::PERSPECTIVE;
94 default:
95 break;
96 }
97 return TransformOperation::NONE;
98}
99
100Length convertToFloatLength(const CSSPrimitiveValue* primitiveValue, const CSSToLengthConversionData& conversionData)
101{
102 return primitiveValue ? primitiveValue->convertToLength<FixedFloatConversion | PercentConversion | CalculatedConversion>(conversionData) : Length(LengthType::Undefined);
103}
104
105// FIXME: This should return std::optional<TransformOperations>
106bool transformsForValue(const CSSValue& value, const CSSToLengthConversionData& conversionData, TransformOperations& outOperations)
107{
108 ASSERT(!outOperations.size());
109 if (!is<CSSValueList>(value))
110 return false;
111
112 auto& operations = outOperations.operations();
113 for (auto& currentValue : downcast<CSSValueList>(value)) {
114 if (!is<CSSFunctionValue>(currentValue))
115 continue;
116
117 auto& transformValue = downcast<CSSFunctionValue>(currentValue.get());
118 if (!transformValue.length())
119 continue;
120
121 bool haveNonPrimitiveValue = false;
122 for (unsigned j = 0; j < transformValue.length(); ++j) {
123 if (!is<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(j))) {
124 haveNonPrimitiveValue = true;
125 break;
126 }
127 }
128 if (haveNonPrimitiveValue)
129 continue;
130
131 auto& firstValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(0));
132
133 switch (transformValue.name()) {
134 case CSSValueScale:
135 case CSSValueScaleX:
136 case CSSValueScaleY: {
137 double sx = 1.0;
138 double sy = 1.0;
139 if (transformValue.name() == CSSValueScaleY)
140 sy = firstValue.doubleValueDividingBy100IfPercentage();
141 else {
142 sx = firstValue.doubleValueDividingBy100IfPercentage();
143 if (transformValue.name() != CSSValueScaleX) {
144 if (transformValue.length() > 1) {
145 auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
146 sy = secondValue.doubleValueDividingBy100IfPercentage();
147 } else
148 sy = sx;
149 }
150 }
151 operations.append(ScaleTransformOperation::create(sx, sy, 1.0, transformOperationType(transformValue.name())));
152 break;
153 }
154 case CSSValueScaleZ:
155 case CSSValueScale3d: {
156 double sx = 1.0;
157 double sy = 1.0;
158 double sz = 1.0;
159 if (transformValue.name() == CSSValueScaleZ)
160 sz = firstValue.doubleValueDividingBy100IfPercentage();
161 else if (transformValue.name() == CSSValueScaleY)
162 sy = firstValue.doubleValueDividingBy100IfPercentage();
163 else {
164 sx = firstValue.doubleValueDividingBy100IfPercentage();
165 if (transformValue.name() != CSSValueScaleX) {
166 if (transformValue.length() > 2) {
167 auto& thirdValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(2));
168 sz = thirdValue.doubleValueDividingBy100IfPercentage();
169 }
170 if (transformValue.length() > 1) {
171 auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
172 sy = secondValue.doubleValueDividingBy100IfPercentage();
173 } else
174 sy = sx;
175 }
176 }
177 operations.append(ScaleTransformOperation::create(sx, sy, sz, transformOperationType(transformValue.name())));
178 break;
179 }
180 case CSSValueTranslate:
181 case CSSValueTranslateX:
182 case CSSValueTranslateY: {
183 Length tx = Length(0, LengthType::Fixed);
184 Length ty = Length(0, LengthType::Fixed);
185 if (transformValue.name() == CSSValueTranslateY)
186 ty = convertToFloatLength(&firstValue, conversionData);
187 else {
188 tx = convertToFloatLength(&firstValue, conversionData);
189 if (transformValue.name() != CSSValueTranslateX) {
190 if (transformValue.length() > 1) {
191 auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
192 ty = convertToFloatLength(&secondValue, conversionData);
193 }
194 }
195 }
196
197 if (tx.isUndefined() || ty.isUndefined()) {
198 operations.clear();
199 return false;
200 }
201
202 operations.append(TranslateTransformOperation::create(tx, ty, Length(0, LengthType::Fixed), transformOperationType(transformValue.name())));
203 break;
204 }
205 case CSSValueTranslateZ:
206 case CSSValueTranslate3d: {
207 Length tx = Length(0, LengthType::Fixed);
208 Length ty = Length(0, LengthType::Fixed);
209 Length tz = Length(0, LengthType::Fixed);
210 if (transformValue.name() == CSSValueTranslateZ)
211 tz = convertToFloatLength(&firstValue, conversionData);
212 else if (transformValue.name() == CSSValueTranslateY)
213 ty = convertToFloatLength(&firstValue, conversionData);
214 else {
215 tx = convertToFloatLength(&firstValue, conversionData);
216 if (transformValue.name() != CSSValueTranslateX) {
217 if (transformValue.length() > 2) {
218 auto& thirdValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(2));
219 tz = convertToFloatLength(&thirdValue, conversionData);
220 }
221 if (transformValue.length() > 1) {
222 auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
223 ty = convertToFloatLength(&secondValue, conversionData);
224 }
225 }
226 }
227
228 if (tx.isUndefined() || ty.isUndefined() || tz.isUndefined()) {
229 operations.clear();
230 return false;
231 }
232
233 operations.append(TranslateTransformOperation::create(tx, ty, tz, transformOperationType(transformValue.name())));
234 break;
235 }
236 case CSSValueRotate: {
237 double angle = firstValue.computeDegrees();
238 operations.append(RotateTransformOperation::create(0, 0, 1, angle, transformOperationType(transformValue.name())));
239 break;
240 }
241 case CSSValueRotateX:
242 case CSSValueRotateY:
243 case CSSValueRotateZ: {
244 double x = 0;
245 double y = 0;
246 double z = 0;
247 double angle = firstValue.computeDegrees();
248
249 if (transformValue.name() == CSSValueRotateX)
250 x = 1;
251 else if (transformValue.name() == CSSValueRotateY)
252 y = 1;
253 else
254 z = 1;
255 operations.append(RotateTransformOperation::create(x, y, z, angle, transformOperationType(transformValue.name())));
256 break;
257 }
258 case CSSValueRotate3d: {
259 if (transformValue.length() < 4)
260 break;
261 auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
262 auto& thirdValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(2));
263 auto& fourthValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(3));
264 double x = firstValue.doubleValue();
265 double y = secondValue.doubleValue();
266 double z = thirdValue.doubleValue();
267 double angle = fourthValue.computeDegrees();
268 operations.append(RotateTransformOperation::create(x, y, z, angle, transformOperationType(transformValue.name())));
269 break;
270 }
271 case CSSValueSkew:
272 case CSSValueSkewX:
273 case CSSValueSkewY: {
274 double angleX = 0;
275 double angleY = 0;
276 double angle = firstValue.computeDegrees();
277 if (transformValue.name() == CSSValueSkewY)
278 angleY = angle;
279 else {
280 angleX = angle;
281 if (transformValue.name() == CSSValueSkew) {
282 if (transformValue.length() > 1) {
283 auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
284 angleY = secondValue.computeDegrees();
285 }
286 }
287 }
288 operations.append(SkewTransformOperation::create(angleX, angleY, transformOperationType(transformValue.name())));
289 break;
290 }
291 case CSSValueMatrix: {
292 if (transformValue.length() < 6)
293 break;
294 double a = firstValue.doubleValue();
295 double b = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1)).doubleValue();
296 double c = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(2)).doubleValue();
297 double d = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(3)).doubleValue();
298 double e = conversionData.zoom() * downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(4)).doubleValue();
299 double f = conversionData.zoom() * downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(5)).doubleValue();
300 operations.append(MatrixTransformOperation::create(a, b, c, d, e, f));
301 break;
302 }
303 case CSSValueMatrix3d: {
304 if (transformValue.length() < 16)
305 break;
306 TransformationMatrix matrix(downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(0)).doubleValue(),
307 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1)).doubleValue(),
308 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(2)).doubleValue(),
309 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(3)).doubleValue(),
310 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(4)).doubleValue(),
311 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(5)).doubleValue(),
312 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(6)).doubleValue(),
313 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(7)).doubleValue(),
314 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(8)).doubleValue(),
315 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(9)).doubleValue(),
316 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(10)).doubleValue(),
317 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(11)).doubleValue(),
318 conversionData.zoom() * downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(12)).doubleValue(),
319 conversionData.zoom() * downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(13)).doubleValue(),
320 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(14)).doubleValue(),
321 downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(15)).doubleValue());
322 operations.append(Matrix3DTransformOperation::create(matrix));
323 break;
324 }
325 case CSSValuePerspective: {
326 std::optional<Length> perspectiveLength;
327 if (!firstValue.isValueID()) {
328 if (firstValue.isLength())
329 perspectiveLength = convertToFloatLength(&firstValue, conversionData);
330 else {
331 // This is a quirk that should go away when 3d transforms are finalized.
332 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=232669
333 // This does not deal properly with calc(), because we aren't passing conversionData here.
334 double doubleValue = firstValue.doubleValue();
335 if (doubleValue < 0) {
336 operations.clear();
337 return false;
338 }
339 perspectiveLength = Length(clampToPositiveInteger(doubleValue), LengthType::Fixed);
340 }
341 } else
342 ASSERT(firstValue.valueID() == CSSValueNone);
343
344 operations.append(PerspectiveTransformOperation::create(perspectiveLength));
345 break;
346 }
347 default:
348 ASSERT_NOT_REACHED();
349 break;
350 }
351 }
352
353 return true;
354}
355
356RefPtr<TranslateTransformOperation> translateForValue(const CSSValue& value, const CSSToLengthConversionData& conversionData)
357{
358 if (!is<CSSValueList>(value))
359 return nullptr;
360
361 auto& valueList = downcast<CSSValueList>(value);
362 if (!valueList.length())
363 return nullptr;
364
365 auto type = TransformOperation::TRANSLATE;
366 Length tx = Length(0, LengthType::Fixed);
367 Length ty = Length(0, LengthType::Fixed);
368 Length tz = Length(0, LengthType::Fixed);
369 for (unsigned i = 0; i < valueList.length(); ++i) {
370 auto* valueItem = valueList.itemWithoutBoundsCheck(i);
371 if (!is<CSSPrimitiveValue>(valueItem))
372 return nullptr;
373 if (!i)
374 tx = convertToFloatLength(downcast<CSSPrimitiveValue>(valueItem), conversionData);
375 else if (i == 1)
376 ty = convertToFloatLength(downcast<CSSPrimitiveValue>(valueItem), conversionData);
377 else if (i == 2) {
378 type = TransformOperation::TRANSLATE_3D;
379 tz = convertToFloatLength(downcast<CSSPrimitiveValue>(valueItem), conversionData);
380 }
381 }
382
383 return TranslateTransformOperation::create(tx, ty, tz, type);
384}
385
386RefPtr<ScaleTransformOperation> scaleForValue(const CSSValue& value)
387{
388 if (!is<CSSValueList>(value))
389 return nullptr;
390
391 auto& valueList = downcast<CSSValueList>(value);
392 if (!valueList.length())
393 return nullptr;
394
395 auto type = TransformOperation::SCALE;
396 double sx = 1.0;
397 double sy = 1.0;
398 double sz = 1.0;
399 for (unsigned i = 0; i < valueList.length(); ++i) {
400 auto* valueItem = valueList.itemWithoutBoundsCheck(i);
401 if (!is<CSSPrimitiveValue>(valueItem))
402 return nullptr;
403 if (!i) {
404 sx = downcast<CSSPrimitiveValue>(*valueItem).doubleValueDividingBy100IfPercentage();
405 sy = sx;
406 } else if (i == 1)
407 sy = downcast<CSSPrimitiveValue>(*valueItem).doubleValueDividingBy100IfPercentage();
408 else if (i == 2) {
409 type = TransformOperation::SCALE_3D;
410 sz = downcast<CSSPrimitiveValue>(*valueItem).doubleValueDividingBy100IfPercentage();
411 }
412 }
413
414 return ScaleTransformOperation::create(sx, sy, sz, type);
415}
416
417RefPtr<RotateTransformOperation> rotateForValue(const CSSValue& value)
418{
419 if (!is<CSSValueList>(value))
420 return nullptr;
421
422 auto& valueList = downcast<CSSValueList>(value);
423 auto numberOfItems = valueList.length();
424
425 // There are three scenarios here since the rotation axis is defined either as:
426 // - no value: implicit 2d rotation
427 // - 1 value: an axis identifier (x/y/z)
428 // - 3 values: three numbers defining an x/y/z vector
429 // The angle is specified as the last value.
430 if (numberOfItems != 1 && numberOfItems != 2 && numberOfItems != 4)
431 return nullptr;
432
433 auto* lastValue = valueList.itemWithoutBoundsCheck(numberOfItems - 1);
434 if (!is<CSSPrimitiveValue>(lastValue))
435 return nullptr;
436 auto angle = downcast<CSSPrimitiveValue>(*lastValue).computeDegrees();
437
438 if (numberOfItems == 1)
439 return RotateTransformOperation::create(angle, TransformOperation::ROTATE);
440
441 double x = 0.0;
442 double y = 0.0;
443 double z = 0.0;
444 auto type = TransformOperation::ROTATE;
445
446 if (numberOfItems == 2) {
447 // An axis identifier was specified.
448 auto* axisIdentifierItem = valueList.itemWithoutBoundsCheck(0);
449 if (!is<CSSPrimitiveValue>(axisIdentifierItem))
450 return nullptr;
451 auto axisIdentifier = downcast<CSSPrimitiveValue>(*axisIdentifierItem).valueID();
452 if (axisIdentifier == CSSValueX) {
453 type = TransformOperation::ROTATE_X;
454 x = 1.0;
455 } else if (axisIdentifier == CSSValueY) {
456 type = TransformOperation::ROTATE_Y;
457 y = 1.0;
458 } else if (axisIdentifier == CSSValueZ) {
459 type = TransformOperation::ROTATE_3D;
460 z = 1.0;
461 } else
462 return nullptr;
463 } else if (numberOfItems == 4) {
464 // The axis was specified using a vector.
465 type = TransformOperation::ROTATE;
466 for (unsigned i = 0; i < 3; ++i) {
467 auto* valueItem = valueList.itemWithoutBoundsCheck(i);
468 if (!is<CSSPrimitiveValue>(valueItem))
469 return nullptr;
470 if (!i)
471 x = downcast<CSSPrimitiveValue>(*valueItem).doubleValue();
472 else if (i == 1)
473 y = downcast<CSSPrimitiveValue>(*valueItem).doubleValue();
474 else if (i == 2) {
475 type = TransformOperation::ROTATE_3D;
476 z = downcast<CSSPrimitiveValue>(*valueItem).doubleValue();
477 }
478 }
479 }
480
481 return RotateTransformOperation::create(x, y, z, angle, type);
482}
483
484}
Note: See TracBrowser for help on using the repository browser.