Stop storing raw pointers to Documents
[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 "ScriptExecutionContext.h"
34 #include "StyleProperties.h"
35 #include "TransformFunctions.h"
36 #include <JavaScriptCore/GenericTypedArrayViewInlines.h>
37 #include <JavaScriptCore/HeapInlines.h>
38 #include <JavaScriptCore/JSGenericTypedArrayViewInlines.h>
39 #include <wtf/text/StringBuilder.h>
40
41 namespace WebCore {
42
43 // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-dommatrixreadonly
44 ExceptionOr<Ref<DOMMatrixReadOnly>> DOMMatrixReadOnly::create(ScriptExecutionContext& scriptExecutionContext, Optional<Variant<String, Vector<double>>>&& init)
45 {
46     if (!init)
47         return adoptRef(*new DOMMatrixReadOnly);
48
49     return WTF::switchOn(init.value(),
50         [&scriptExecutionContext](const String& init) -> ExceptionOr<Ref<DOMMatrixReadOnly>> {
51             if (!scriptExecutionContext.isDocument())
52                 return Exception { TypeError };
53
54             auto parseResult = parseStringIntoAbstractMatrix(init);
55             if (parseResult.hasException())
56                 return parseResult.releaseException();
57             
58             return adoptRef(*new DOMMatrixReadOnly(parseResult.returnValue().matrix, parseResult.returnValue().is2D ? Is2D::Yes : Is2D::No));
59         },
60         [](const Vector<double>& init) -> ExceptionOr<Ref<DOMMatrixReadOnly>> {
61             if (init.size() == 6) {
62                 return adoptRef(*new DOMMatrixReadOnly(TransformationMatrix {
63                     init[0], init[1], init[2], init[3], init[4], init[5]
64                 }, Is2D::Yes));
65             }
66             if (init.size() == 16) {
67                 return adoptRef(*new DOMMatrixReadOnly(TransformationMatrix {
68                     init[0], init[1], init[2], init[3],
69                     init[4], init[5], init[6], init[7],
70                     init[8], init[9], init[10], init[11],
71                     init[12], init[13], init[14], init[15]
72                 }, Is2D::No));
73             }
74             return Exception { TypeError };
75         }
76     );
77 }
78
79 DOMMatrixReadOnly::DOMMatrixReadOnly(const TransformationMatrix& matrix, Is2D is2D)
80     : m_matrix(matrix)
81     , m_is2D(is2D == Is2D::Yes)
82 {
83     ASSERT(!m_is2D || m_matrix.isAffine());
84 }
85
86 DOMMatrixReadOnly::DOMMatrixReadOnly(TransformationMatrix&& matrix, Is2D is2D)
87     : m_matrix(WTFMove(matrix))
88     , m_is2D(is2D == Is2D::Yes)
89 {
90     ASSERT(!m_is2D || m_matrix.isAffine());
91 }
92
93 inline Ref<DOMMatrix> DOMMatrixReadOnly::cloneAsDOMMatrix() const
94 {
95     return DOMMatrix::create(m_matrix, m_is2D ? Is2D::Yes : Is2D::No);
96 }
97
98 // https://tc39.github.io/ecma262/#sec-samevaluezero
99 static bool sameValueZero(double a, double b)
100 {
101     if (std::isnan(a) && std::isnan(b))
102         return true;
103     return a == b;
104 }
105
106 // https://drafts.fxtf.org/geometry/#matrix-validate-and-fixup
107 ExceptionOr<void> DOMMatrixReadOnly::validateAndFixup(DOMMatrix2DInit& init)
108 {
109     if (init.a && init.m11 && !sameValueZero(init.a.value(), init.m11.value()))
110         return Exception { TypeError, "init.a and init.m11 do not match"_s };
111     if (init.b && init.m12 && !sameValueZero(init.b.value(), init.m12.value()))
112         return Exception { TypeError, "init.b and init.m12 do not match"_s };
113     if (init.c && init.m21 && !sameValueZero(init.c.value(), init.m21.value()))
114         return Exception { TypeError, "init.c and init.m21 do not match"_s };
115     if (init.d && init.m22 && !sameValueZero(init.d.value(), init.m22.value()))
116         return Exception { TypeError, "init.d and init.m22 do not match"_s };
117     if (init.e && init.m41 && !sameValueZero(init.e.value(), init.m41.value()))
118         return Exception { TypeError, "init.e and init.m41 do not match"_s };
119     if (init.f && init.m42 && !sameValueZero(init.f.value(), init.m42.value()))
120         return Exception { TypeError, "init.f and init.m42 do not match"_s };
121
122     if (!init.m11)
123         init.m11 = init.a.valueOr(1);
124     if (!init.m12)
125         init.m12 = init.b.valueOr(0);
126     if (!init.m21)
127         init.m21 = init.c.valueOr(0);
128     if (!init.m22)
129         init.m22 = init.d.valueOr(1);
130     if (!init.m41)
131         init.m41 = init.e.valueOr(0);
132     if (!init.m42)
133         init.m42 = init.f.valueOr(0);
134
135     return { };
136 }
137
138 ExceptionOr<void> DOMMatrixReadOnly::validateAndFixup(DOMMatrixInit& init)
139 {
140     auto validate2D = validateAndFixup(static_cast<DOMMatrix2DInit&>(init));
141     if (validate2D.hasException())
142         return validate2D.releaseException();
143
144     if (init.is2D && init.is2D.value()) {
145         if (init.m13)
146             return Exception { TypeError, "m13 should be 0 for a 2D matrix"_s };
147         if (init.m14)
148             return Exception { TypeError, "m14 should be 0 for a 2D matrix"_s };
149         if (init.m23)
150             return Exception { TypeError, "m23 should be 0 for a 2D matrix"_s };
151         if (init.m24)
152             return Exception { TypeError, "m24 should be 0 for a 2D matrix"_s };
153         if (init.m31)
154             return Exception { TypeError, "m31 should be 0 for a 2D matrix"_s };
155         if (init.m32)
156             return Exception { TypeError, "m32 should be 0 for a 2D matrix"_s };
157         if (init.m34)
158             return Exception { TypeError, "m34 should be 0 for a 2D matrix"_s };
159         if (init.m43)
160             return Exception { TypeError, "m43 should be 0 for a 2D matrix"_s };
161         if (init.m33 != 1)
162             return Exception { TypeError, "m33 should be 1 for a 2D matrix"_s };
163         if (init.m44 != 1)
164             return Exception { TypeError, "m44 should be 1 for a 2D matrix"_s };
165     }
166
167     if (!init.is2D) {
168         if (init.m13 || init.m14 || init.m23 || init.m24 || init.m31 || init.m32 || init.m34 || init.m43 || init.m33 != 1 || init.m44 != 1)
169             init.is2D = false;
170         else
171             init.is2D = true;
172     }
173     return { };
174 }
175
176 ExceptionOr<Ref<DOMMatrixReadOnly>> DOMMatrixReadOnly::fromMatrix(DOMMatrixInit&& init)
177 {
178     return fromMatrixHelper<DOMMatrixReadOnly>(WTFMove(init));
179 }
180
181 ExceptionOr<Ref<DOMMatrixReadOnly>> DOMMatrixReadOnly::fromFloat32Array(Ref<Float32Array>&& array32)
182 {
183     if (array32->length() == 6)
184         return DOMMatrixReadOnly::create(TransformationMatrix(array32->item(0), array32->item(1), array32->item(2), array32->item(3), array32->item(4), array32->item(5)), Is2D::Yes);
185
186     if (array32->length() == 16) {
187         return DOMMatrixReadOnly::create(TransformationMatrix(
188             array32->item(0), array32->item(1), array32->item(2), array32->item(3),
189             array32->item(4), array32->item(5), array32->item(6), array32->item(7),
190             array32->item(8), array32->item(9), array32->item(10), array32->item(11),
191             array32->item(12), array32->item(13), array32->item(14), array32->item(15)
192         ), Is2D::No);
193     }
194
195     return Exception { TypeError };
196 }
197
198 ExceptionOr<Ref<DOMMatrixReadOnly>> DOMMatrixReadOnly::fromFloat64Array(Ref<Float64Array>&& array64)
199 {
200     if (array64->length() == 6)
201         return DOMMatrixReadOnly::create(TransformationMatrix(array64->item(0), array64->item(1), array64->item(2), array64->item(3), array64->item(4), array64->item(5)), Is2D::Yes);
202
203     if (array64->length() == 16) {
204         return DOMMatrixReadOnly::create(TransformationMatrix(
205             array64->item(0), array64->item(1), array64->item(2), array64->item(3),
206             array64->item(4), array64->item(5), array64->item(6), array64->item(7),
207             array64->item(8), array64->item(9), array64->item(10), array64->item(11),
208             array64->item(12), array64->item(13), array64->item(14), array64->item(15)
209         ), Is2D::No);
210     }
211
212     return Exception { TypeError };
213 }
214
215 bool DOMMatrixReadOnly::isIdentity() const
216 {
217     return m_matrix.isIdentity();
218 }
219
220 ExceptionOr<DOMMatrixReadOnly::AbstractMatrix> DOMMatrixReadOnly::parseStringIntoAbstractMatrix(const String& string)
221 {
222     if (string.isEmpty())
223         return AbstractMatrix { };
224
225     auto styleDeclaration = MutableStyleProperties::create();
226     if (CSSParser::parseValue(styleDeclaration, CSSPropertyTransform, string, true, HTMLStandardMode) == CSSParser::ParseResult::Error)
227         return Exception { SyntaxError };
228
229     // Convert to TransformOperations. This can fail if a property requires style (i.e., param uses 'ems' or 'exs')
230     auto value = styleDeclaration->getPropertyCSSValue(CSSPropertyTransform);
231
232     // Check for a "none" or empty transform. In these cases we can use the default identity matrix.
233     if (!value || (is<CSSPrimitiveValue>(*value) && downcast<CSSPrimitiveValue>(*value).valueID() == CSSValueNone))
234         return AbstractMatrix { };
235
236     TransformOperations operations;
237     if (!transformsForValue(*value, CSSToLengthConversionData(), operations))
238         return Exception { SyntaxError };
239
240     AbstractMatrix matrix;
241     for (auto& operation : operations.operations()) {
242         if (operation->apply(matrix.matrix, { 0, 0 }))
243             return Exception { SyntaxError };
244         if (operation->is3DOperation())
245             matrix.is2D = false;
246     }
247
248     return matrix;
249 }
250
251 // https://drafts.fxtf.org/geometry/#dom-dommatrix-setmatrixvalue
252 ExceptionOr<void> DOMMatrixReadOnly::setMatrixValue(const String& string)
253 {
254     auto parseResult = parseStringIntoAbstractMatrix(string);
255     if (parseResult.hasException())
256         return parseResult.releaseException();
257
258     m_is2D = parseResult.returnValue().is2D;
259     m_matrix = parseResult.returnValue().matrix;
260     return { };
261 }
262
263 Ref<DOMMatrix> DOMMatrixReadOnly::translate(double tx, double ty, double tz)
264 {
265     auto matrix = cloneAsDOMMatrix();
266     return matrix->translateSelf(tx, ty, tz);
267 }
268
269 // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-flipx
270 Ref<DOMMatrix> DOMMatrixReadOnly::flipX()
271 {
272     auto matrix = cloneAsDOMMatrix();
273     matrix->m_matrix.flipX();
274     return matrix;
275 }
276
277 // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-flipy
278 Ref<DOMMatrix> DOMMatrixReadOnly::flipY()
279 {
280     auto matrix = cloneAsDOMMatrix();
281     matrix->m_matrix.flipY();
282     return matrix;
283 }
284
285 ExceptionOr<Ref<DOMMatrix>> DOMMatrixReadOnly::multiply(DOMMatrixInit&& other) const
286 {
287     auto matrix = cloneAsDOMMatrix();
288     return matrix->multiplySelf(WTFMove(other));
289 }
290
291 Ref<DOMMatrix> DOMMatrixReadOnly::scale(double scaleX, Optional<double> scaleY, double scaleZ, double originX, double originY, double originZ)
292 {
293     auto matrix = cloneAsDOMMatrix();
294     return matrix->scaleSelf(scaleX, scaleY, scaleZ, originX, originY, originZ);
295 }
296
297 Ref<DOMMatrix> DOMMatrixReadOnly::scale3d(double scale, double originX, double originY, double originZ)
298 {
299     auto matrix = cloneAsDOMMatrix();
300     return matrix->scale3dSelf(scale, originX, originY, originZ);
301 }
302
303 Ref<DOMMatrix> DOMMatrixReadOnly::rotate(double rotX, Optional<double> rotY, Optional<double> rotZ)
304 {
305     auto matrix = cloneAsDOMMatrix();
306     return matrix->rotateSelf(rotX, rotY, rotZ);
307 }
308
309 Ref<DOMMatrix> DOMMatrixReadOnly::rotateFromVector(double x, double y)
310 {
311     auto matrix = cloneAsDOMMatrix();
312     return matrix->rotateFromVectorSelf(x, y);
313 }
314
315 Ref<DOMMatrix> DOMMatrixReadOnly::rotateAxisAngle(double x, double y, double z, double angle)
316 {
317     auto matrix = cloneAsDOMMatrix();
318     return matrix->rotateAxisAngleSelf(x, y, z, angle);
319 }
320
321 Ref<DOMMatrix> DOMMatrixReadOnly::skewX(double sx)
322 {
323     auto matrix = cloneAsDOMMatrix();
324     return matrix->skewXSelf(sx);
325 }
326
327 Ref<DOMMatrix> DOMMatrixReadOnly::skewY(double sy)
328 {
329     auto matrix = cloneAsDOMMatrix();
330     return matrix->skewYSelf(sy);
331 }
332
333 Ref<DOMMatrix> DOMMatrixReadOnly::inverse() const
334 {
335     auto matrix = cloneAsDOMMatrix();
336     return matrix->invertSelf();
337 }
338
339 // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-transformpoint
340 Ref<DOMPoint> DOMMatrixReadOnly::transformPoint(DOMPointInit&& pointInit)
341 {
342     m_matrix.map4ComponentPoint(pointInit.x, pointInit.y, pointInit.z, pointInit.w);
343     return DOMPoint::create(pointInit.x, pointInit.y, pointInit.z, pointInit.w);
344 }
345
346 ExceptionOr<Ref<Float32Array>> DOMMatrixReadOnly::toFloat32Array() const
347 {
348     auto array32 = Float32Array::tryCreateUninitialized(16);
349     if (!array32)
350         return Exception { UnknownError, "Out of memory"_s };
351
352     unsigned index = 0;
353     array32->set(index++, m_matrix.m11());
354     array32->set(index++, m_matrix.m12());
355     array32->set(index++, m_matrix.m13());
356     array32->set(index++, m_matrix.m14());
357     array32->set(index++, m_matrix.m21());
358     array32->set(index++, m_matrix.m22());
359     array32->set(index++, m_matrix.m23());
360     array32->set(index++, m_matrix.m24());
361     array32->set(index++, m_matrix.m31());
362     array32->set(index++, m_matrix.m32());
363     array32->set(index++, m_matrix.m33());
364     array32->set(index++, m_matrix.m34());
365     array32->set(index++, m_matrix.m41());
366     array32->set(index++, m_matrix.m42());
367     array32->set(index++, m_matrix.m43());
368     array32->set(index, m_matrix.m44());
369     return array32.releaseNonNull();
370 }
371
372 ExceptionOr<Ref<Float64Array>> DOMMatrixReadOnly::toFloat64Array() const
373 {
374     auto array64 = Float64Array::tryCreateUninitialized(16);
375     if (!array64)
376         return Exception { UnknownError, "Out of memory"_s };
377
378     unsigned index = 0;
379     array64->set(index++, m_matrix.m11());
380     array64->set(index++, m_matrix.m12());
381     array64->set(index++, m_matrix.m13());
382     array64->set(index++, m_matrix.m14());
383     array64->set(index++, m_matrix.m21());
384     array64->set(index++, m_matrix.m22());
385     array64->set(index++, m_matrix.m23());
386     array64->set(index++, m_matrix.m24());
387     array64->set(index++, m_matrix.m31());
388     array64->set(index++, m_matrix.m32());
389     array64->set(index++, m_matrix.m33());
390     array64->set(index++, m_matrix.m34());
391     array64->set(index++, m_matrix.m41());
392     array64->set(index++, m_matrix.m42());
393     array64->set(index++, m_matrix.m43());
394     array64->set(index, m_matrix.m44());
395     return array64.releaseNonNull();
396 }
397
398 // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-stringifier
399 ExceptionOr<String> DOMMatrixReadOnly::toString() const
400 {
401     if (!m_matrix.containsOnlyFiniteValues())
402         return Exception { InvalidStateError, "Matrix contains non-finite values"_s };
403
404     StringBuilder builder;
405     if (is2D()) {
406         builder.appendLiteral("matrix(");
407         builder.appendECMAScriptNumber(m_matrix.a());
408         builder.appendLiteral(", ");
409         builder.appendECMAScriptNumber(m_matrix.b());
410         builder.appendLiteral(", ");
411         builder.appendECMAScriptNumber(m_matrix.c());
412         builder.appendLiteral(", ");
413         builder.appendECMAScriptNumber(m_matrix.d());
414         builder.appendLiteral(", ");
415         builder.appendECMAScriptNumber(m_matrix.e());
416         builder.appendLiteral(", ");
417         builder.appendECMAScriptNumber(m_matrix.f());
418     } else {
419         builder.appendLiteral("matrix3d(");
420         builder.appendECMAScriptNumber(m_matrix.m11());
421         builder.appendLiteral(", ");
422         builder.appendECMAScriptNumber(m_matrix.m12());
423         builder.appendLiteral(", ");
424         builder.appendECMAScriptNumber(m_matrix.m13());
425         builder.appendLiteral(", ");
426         builder.appendECMAScriptNumber(m_matrix.m14());
427         builder.appendLiteral(", ");
428         builder.appendECMAScriptNumber(m_matrix.m21());
429         builder.appendLiteral(", ");
430         builder.appendECMAScriptNumber(m_matrix.m22());
431         builder.appendLiteral(", ");
432         builder.appendECMAScriptNumber(m_matrix.m23());
433         builder.appendLiteral(", ");
434         builder.appendECMAScriptNumber(m_matrix.m24());
435         builder.appendLiteral(", ");
436         builder.appendECMAScriptNumber(m_matrix.m31());
437         builder.appendLiteral(", ");
438         builder.appendECMAScriptNumber(m_matrix.m32());
439         builder.appendLiteral(", ");
440         builder.appendECMAScriptNumber(m_matrix.m33());
441         builder.appendLiteral(", ");
442         builder.appendECMAScriptNumber(m_matrix.m34());
443         builder.appendLiteral(", ");
444         builder.appendECMAScriptNumber(m_matrix.m41());
445         builder.appendLiteral(", ");
446         builder.appendECMAScriptNumber(m_matrix.m42());
447         builder.appendLiteral(", ");
448         builder.appendECMAScriptNumber(m_matrix.m43());
449         builder.appendLiteral(", ");
450         builder.appendECMAScriptNumber(m_matrix.m44());
451     }
452     builder.append(')');
453     return builder.toString();
454 }
455
456 } // namespace WebCore