5af5c8de3c2e1e4b6d029e5403afb0a13298e2e7
[WebKit-https.git] / Source / WebCore / css / DOMMatrixReadOnly.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "DOMMatrixReadOnly.h"
28
29 #include "CSSParser.h"
30 #include "CSSToLengthConversionData.h"
31 #include "DOMMatrix.h"
32 #include "DOMPoint.h"
33 #include "StyleProperties.h"
34 #include "TransformFunctions.h"
35 #include <JavaScriptCore/GenericTypedArrayViewInlines.h>
36 #include <JavaScriptCore/JSGenericTypedArrayViewInlines.h>
37 #include <heap/HeapInlines.h>
38 #include <wtf/text/StringBuilder.h>
39
40 namespace WebCore {
41
42 DOMMatrixReadOnly::DOMMatrixReadOnly(const TransformationMatrix& matrix, Is2D is2D)
43     : m_matrix(matrix)
44     , m_is2D(is2D == Is2D::Yes)
45 {
46     ASSERT(!m_is2D || m_matrix.isAffine());
47 }
48
49 DOMMatrixReadOnly::DOMMatrixReadOnly(TransformationMatrix&& matrix, Is2D is2D)
50     : m_matrix(WTFMove(matrix))
51     , m_is2D(is2D == Is2D::Yes)
52 {
53     ASSERT(!m_is2D || m_matrix.isAffine());
54 }
55
56 inline Ref<DOMMatrix> DOMMatrixReadOnly::cloneAsDOMMatrix() const
57 {
58     return DOMMatrix::create(m_matrix, m_is2D ? Is2D::Yes : Is2D::No);
59 }
60
61 // https://drafts.fxtf.org/geometry/#matrix-validate-and-fixup
62 ExceptionOr<void> DOMMatrixReadOnly::validateAndFixup(DOMMatrix2DInit& init)
63 {
64     // FIXME: Should be using SameValueZero rather than c-equality.
65     if (init.a && init.m11 && init.a.value() != init.m11.value())
66         return Exception { TypeError, ASCIILiteral("init.a and init.m11 do not match") };
67     if (init.b && init.m12 && init.b.value() != init.m12.value())
68         return Exception { TypeError, ASCIILiteral("init.b and init.m12 do not match") };
69     if (init.c && init.m21 && init.c.value() != init.m21.value())
70         return Exception { TypeError, ASCIILiteral("init.c and init.m21 do not match") };
71     if (init.d && init.m22 && init.d.value() != init.m22.value())
72         return Exception { TypeError, ASCIILiteral("init.d and init.m22 do not match") };
73     if (init.e && init.m41 && init.e.value() != init.m41.value())
74         return Exception { TypeError, ASCIILiteral("init.e and init.m41 do not match") };
75     if (init.f && init.m42 && init.f.value() != init.m42.value())
76         return Exception { TypeError, ASCIILiteral("init.f and init.m42 do not match") };
77
78     if (!init.m11)
79         init.m11 = init.a.value_or(1);
80     if (!init.m12)
81         init.m12 = init.b.value_or(0);
82     if (!init.m21)
83         init.m21 = init.c.value_or(0);
84     if (!init.m22)
85         init.m22 = init.d.value_or(1);
86     if (!init.m41)
87         init.m41 = init.e.value_or(0);
88     if (!init.m42)
89         init.m42 = init.f.value_or(0);
90
91     return { };
92 }
93
94 ExceptionOr<void> DOMMatrixReadOnly::validateAndFixup(DOMMatrixInit& init)
95 {
96     auto validate2D = validateAndFixup(static_cast<DOMMatrix2DInit&>(init));
97     if (validate2D.hasException())
98         return validate2D.releaseException();
99
100     if (init.is2D && init.is2D.value()) {
101         if (init.m13)
102             return Exception { TypeError, ASCIILiteral("m13 should be 0 for a 2D matrix") };
103         if (init.m14)
104             return Exception { TypeError, ASCIILiteral("m14 should be 0 for a 2D matrix") };
105         if (init.m23)
106             return Exception { TypeError, ASCIILiteral("m23 should be 0 for a 2D matrix") };
107         if (init.m24)
108             return Exception { TypeError, ASCIILiteral("m24 should be 0 for a 2D matrix") };
109         if (init.m31)
110             return Exception { TypeError, ASCIILiteral("m31 should be 0 for a 2D matrix") };
111         if (init.m32)
112             return Exception { TypeError, ASCIILiteral("m32 should be 0 for a 2D matrix") };
113         if (init.m34)
114             return Exception { TypeError, ASCIILiteral("m34 should be 0 for a 2D matrix") };
115         if (init.m43)
116             return Exception { TypeError, ASCIILiteral("m43 should be 0 for a 2D matrix") };
117         if (init.m33 != 1)
118             return Exception { TypeError, ASCIILiteral("m33 should be 1 for a 2D matrix") };
119         if (init.m44 != 1)
120             return Exception { TypeError, ASCIILiteral("m44 should be 1 for a 2D matrix") };
121     }
122
123     if (!init.is2D) {
124         if (init.m13 || init.m14 || init.m23 || init.m24 || init.m31 || init.m32 || init.m34 || init.m43 || init.m33 != 1 || init.m44 != 1)
125             init.is2D = false;
126         else
127             init.is2D = true;
128     }
129     return { };
130 }
131
132 ExceptionOr<Ref<DOMMatrixReadOnly>> DOMMatrixReadOnly::fromMatrix(DOMMatrixInit&& init)
133 {
134     return fromMatrixHelper<DOMMatrixReadOnly>(WTFMove(init));
135 }
136
137 ExceptionOr<Ref<DOMMatrixReadOnly>> DOMMatrixReadOnly::fromFloat32Array(Ref<Float32Array>&& array32)
138 {
139     if (array32->length() == 6)
140         return DOMMatrixReadOnly::create(TransformationMatrix(array32->item(0), array32->item(1), array32->item(2), array32->item(3), array32->item(4), array32->item(5)), Is2D::Yes);
141
142     if (array32->length() == 16) {
143         return DOMMatrixReadOnly::create(TransformationMatrix(
144             array32->item(0), array32->item(1), array32->item(2), array32->item(3),
145             array32->item(4), array32->item(5), array32->item(6), array32->item(7),
146             array32->item(8), array32->item(9), array32->item(10), array32->item(11),
147             array32->item(12), array32->item(13), array32->item(14), array32->item(15)
148         ), Is2D::No);
149     }
150
151     return Exception { TypeError };
152 }
153
154 ExceptionOr<Ref<DOMMatrixReadOnly>> DOMMatrixReadOnly::fromFloat64Array(Ref<Float64Array>&& array64)
155 {
156     if (array64->length() == 6)
157         return DOMMatrixReadOnly::create(TransformationMatrix(array64->item(0), array64->item(1), array64->item(2), array64->item(3), array64->item(4), array64->item(5)), Is2D::Yes);
158
159     if (array64->length() == 16) {
160         return DOMMatrixReadOnly::create(TransformationMatrix(
161             array64->item(0), array64->item(1), array64->item(2), array64->item(3),
162             array64->item(4), array64->item(5), array64->item(6), array64->item(7),
163             array64->item(8), array64->item(9), array64->item(10), array64->item(11),
164             array64->item(12), array64->item(13), array64->item(14), array64->item(15)
165         ), Is2D::No);
166     }
167
168     return Exception { TypeError };
169 }
170
171 bool DOMMatrixReadOnly::isIdentity() const
172 {
173     return m_matrix.isIdentity();
174 }
175
176 // https://drafts.fxtf.org/geometry/#dom-dommatrix-setmatrixvalue
177 ExceptionOr<void> DOMMatrixReadOnly::setMatrixValue(const String& string)
178 {
179     if (string.isEmpty())
180         return { };
181
182     auto styleDeclaration = MutableStyleProperties::create();
183     if (CSSParser::parseValue(styleDeclaration, CSSPropertyTransform, string, true, HTMLStandardMode) == CSSParser::ParseResult::Error)
184         return Exception { SyntaxError };
185
186     // Convert to TransformOperations. This can fail if a property requires style (i.e., param uses 'ems' or 'exs')
187     auto value = styleDeclaration->getPropertyCSSValue(CSSPropertyTransform);
188
189     // Check for a "none" or empty transform. In these cases we can use the default identity matrix.
190     if (!value || (is<CSSPrimitiveValue>(*value) && downcast<CSSPrimitiveValue>(*value).valueID() == CSSValueNone))
191         return { };
192
193     TransformOperations operations;
194     if (!transformsForValue(*value, CSSToLengthConversionData(), operations))
195         return Exception { SyntaxError };
196
197     m_is2D = true;
198     // Convert transform operations to a TransformationMatrix. This can fail if a parameter has a percentage ('%').
199     TransformationMatrix matrix;
200     for (auto& operation : operations.operations()) {
201         if (operation->apply(matrix, IntSize(0, 0)))
202             return Exception { SyntaxError };
203         if (operation->is3DOperation())
204             m_is2D = false;
205     }
206     m_matrix = matrix;
207     return { };
208 }
209
210 ExceptionOr<void> DOMMatrixReadOnly::setMatrixValue(const Vector<double>& init)
211 {
212     if (init.size() == 6) {
213         m_matrix = TransformationMatrix { init[0], init[1], init[2], init[3], init[4], init[5] };
214         return { };
215     }
216     if (init.size() == 16) {
217         m_matrix = TransformationMatrix {
218             init[0], init[1], init[2], init[3],
219             init[4], init[5], init[6], init[7],
220             init[8], init[9], init[10], init[11],
221             init[12], init[13], init[14], init[15]
222         };
223         m_is2D = false;
224         return { };
225     }
226     return Exception { TypeError };
227 }
228
229 Ref<DOMMatrix> DOMMatrixReadOnly::translate(double tx, double ty, double tz)
230 {
231     auto matrix = cloneAsDOMMatrix();
232     return matrix->translateSelf(tx, ty, tz);
233 }
234
235 // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-flipx
236 Ref<DOMMatrix> DOMMatrixReadOnly::flipX()
237 {
238     auto matrix = cloneAsDOMMatrix();
239     matrix->m_matrix.flipX();
240     return matrix;
241 }
242
243 // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-flipy
244 Ref<DOMMatrix> DOMMatrixReadOnly::flipY()
245 {
246     auto matrix = cloneAsDOMMatrix();
247     matrix->m_matrix.flipY();
248     return matrix;
249 }
250
251 ExceptionOr<Ref<DOMMatrix>> DOMMatrixReadOnly::multiply(DOMMatrixInit&& other) const
252 {
253     auto matrix = cloneAsDOMMatrix();
254     return matrix->multiplySelf(WTFMove(other));
255 }
256
257 Ref<DOMMatrix> DOMMatrixReadOnly::scale(double scaleX, std::optional<double> scaleY, double scaleZ, double originX, double originY, double originZ)
258 {
259     auto matrix = cloneAsDOMMatrix();
260     return matrix->scaleSelf(scaleX, scaleY, scaleZ, originX, originY, originZ);
261 }
262
263 Ref<DOMMatrix> DOMMatrixReadOnly::scale3d(double scale, double originX, double originY, double originZ)
264 {
265     auto matrix = cloneAsDOMMatrix();
266     return matrix->scale3dSelf(scale, originX, originY, originZ);
267 }
268
269 Ref<DOMMatrix> DOMMatrixReadOnly::rotate(double rotX, std::optional<double> rotY, std::optional<double> rotZ)
270 {
271     auto matrix = cloneAsDOMMatrix();
272     return matrix->rotateSelf(rotX, rotY, rotZ);
273 }
274
275 Ref<DOMMatrix> DOMMatrixReadOnly::rotateFromVector(double x, double y)
276 {
277     auto matrix = cloneAsDOMMatrix();
278     return matrix->rotateFromVectorSelf(x, y);
279 }
280
281 Ref<DOMMatrix> DOMMatrixReadOnly::rotateAxisAngle(double x, double y, double z, double angle)
282 {
283     auto matrix = cloneAsDOMMatrix();
284     return matrix->rotateAxisAngleSelf(x, y, z, angle);
285 }
286
287 Ref<DOMMatrix> DOMMatrixReadOnly::skewX(double sx)
288 {
289     auto matrix = cloneAsDOMMatrix();
290     return matrix->skewXSelf(sx);
291 }
292
293 Ref<DOMMatrix> DOMMatrixReadOnly::skewY(double sy)
294 {
295     auto matrix = cloneAsDOMMatrix();
296     return matrix->skewYSelf(sy);
297 }
298
299 Ref<DOMMatrix> DOMMatrixReadOnly::inverse() const
300 {
301     auto matrix = cloneAsDOMMatrix();
302     return matrix->invertSelf();
303 }
304
305 // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-transformpoint
306 Ref<DOMPoint> DOMMatrixReadOnly::transformPoint(DOMPointInit&& pointInit)
307 {
308     m_matrix.map4ComponentPoint(pointInit.x, pointInit.y, pointInit.z, pointInit.w);
309     return DOMPoint::create(pointInit.x, pointInit.y, pointInit.z, pointInit.w);
310 }
311
312 ExceptionOr<Ref<Float32Array>> DOMMatrixReadOnly::toFloat32Array() const
313 {
314     auto array32 = Float32Array::createUninitialized(16);
315     if (!array32)
316         return Exception { UnknownError, ASCIILiteral("Out of memory") };
317
318     unsigned index = 0;
319     array32->set(index++, m_matrix.m11());
320     array32->set(index++, m_matrix.m12());
321     array32->set(index++, m_matrix.m13());
322     array32->set(index++, m_matrix.m14());
323     array32->set(index++, m_matrix.m21());
324     array32->set(index++, m_matrix.m22());
325     array32->set(index++, m_matrix.m23());
326     array32->set(index++, m_matrix.m24());
327     array32->set(index++, m_matrix.m31());
328     array32->set(index++, m_matrix.m32());
329     array32->set(index++, m_matrix.m33());
330     array32->set(index++, m_matrix.m34());
331     array32->set(index++, m_matrix.m41());
332     array32->set(index++, m_matrix.m42());
333     array32->set(index++, m_matrix.m43());
334     array32->set(index, m_matrix.m44());
335     return array32.releaseNonNull();
336 }
337
338 ExceptionOr<Ref<Float64Array>> DOMMatrixReadOnly::toFloat64Array() const
339 {
340     auto array64 = Float64Array::createUninitialized(16);
341     if (!array64)
342         return Exception { UnknownError, ASCIILiteral("Out of memory") };
343
344     unsigned index = 0;
345     array64->set(index++, m_matrix.m11());
346     array64->set(index++, m_matrix.m12());
347     array64->set(index++, m_matrix.m13());
348     array64->set(index++, m_matrix.m14());
349     array64->set(index++, m_matrix.m21());
350     array64->set(index++, m_matrix.m22());
351     array64->set(index++, m_matrix.m23());
352     array64->set(index++, m_matrix.m24());
353     array64->set(index++, m_matrix.m31());
354     array64->set(index++, m_matrix.m32());
355     array64->set(index++, m_matrix.m33());
356     array64->set(index++, m_matrix.m34());
357     array64->set(index++, m_matrix.m41());
358     array64->set(index++, m_matrix.m42());
359     array64->set(index++, m_matrix.m43());
360     array64->set(index, m_matrix.m44());
361     return array64.releaseNonNull();
362 }
363
364 // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-stringifier
365 ExceptionOr<String> DOMMatrixReadOnly::toString() const
366 {
367     if (!m_matrix.containsOnlyFiniteValues())
368         return Exception { InvalidStateError, ASCIILiteral("Matrix contains non-finite values") };
369
370     StringBuilder builder;
371     if (is2D()) {
372         builder.appendLiteral("matrix(");
373         builder.append(String::numberToStringECMAScript(m_matrix.a()));
374         builder.appendLiteral(", ");
375         builder.append(String::numberToStringECMAScript(m_matrix.b()));
376         builder.appendLiteral(", ");
377         builder.append(String::numberToStringECMAScript(m_matrix.c()));
378         builder.appendLiteral(", ");
379         builder.append(String::numberToStringECMAScript(m_matrix.d()));
380         builder.appendLiteral(", ");
381         builder.append(String::numberToStringECMAScript(m_matrix.e()));
382         builder.appendLiteral(", ");
383         builder.append(String::numberToStringECMAScript(m_matrix.f()));
384     } else {
385         builder.appendLiteral("matrix3d(");
386         builder.append(String::numberToStringECMAScript(m_matrix.m11()));
387         builder.appendLiteral(", ");
388         builder.append(String::numberToStringECMAScript(m_matrix.m12()));
389         builder.appendLiteral(", ");
390         builder.append(String::numberToStringECMAScript(m_matrix.m13()));
391         builder.appendLiteral(", ");
392         builder.append(String::numberToStringECMAScript(m_matrix.m14()));
393         builder.appendLiteral(", ");
394         builder.append(String::numberToStringECMAScript(m_matrix.m21()));
395         builder.appendLiteral(", ");
396         builder.append(String::numberToStringECMAScript(m_matrix.m22()));
397         builder.appendLiteral(", ");
398         builder.append(String::numberToStringECMAScript(m_matrix.m23()));
399         builder.appendLiteral(", ");
400         builder.append(String::numberToStringECMAScript(m_matrix.m24()));
401         builder.appendLiteral(", ");
402         builder.append(String::numberToStringECMAScript(m_matrix.m31()));
403         builder.appendLiteral(", ");
404         builder.append(String::numberToStringECMAScript(m_matrix.m32()));
405         builder.appendLiteral(", ");
406         builder.append(String::numberToStringECMAScript(m_matrix.m33()));
407         builder.appendLiteral(", ");
408         builder.append(String::numberToStringECMAScript(m_matrix.m34()));
409         builder.appendLiteral(", ");
410         builder.append(String::numberToStringECMAScript(m_matrix.m41()));
411         builder.appendLiteral(", ");
412         builder.append(String::numberToStringECMAScript(m_matrix.m42()));
413         builder.appendLiteral(", ");
414         builder.append(String::numberToStringECMAScript(m_matrix.m43()));
415         builder.appendLiteral(", ");
416         builder.append(String::numberToStringECMAScript(m_matrix.m44()));
417     }
418     builder.append(')');
419     return builder.toString();
420 }
421
422 } // namespace WebCore