1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/DOMMatrix.h"
12 #include "ErrorList.h"
13 #include "js/Conversions.h"
14 #include "js/Equality.h"
15 #include "js/StructuredClone.h"
17 #include "mozilla/Casting.h"
18 #include "mozilla/ErrorResult.h"
19 #include "mozilla/FloatingPoint.h"
20 #include "mozilla/MacroForEach.h"
21 #include "mozilla/RefPtr.h"
22 #include "mozilla/ServoCSSParser.h"
23 #include "mozilla/dom/BindingDeclarations.h"
24 #include "mozilla/dom/DOMMatrixBinding.h"
25 #include "mozilla/dom/DOMPoint.h"
26 #include "mozilla/dom/DOMPointBinding.h"
27 #include "mozilla/dom/ToJSValue.h"
28 #include "mozilla/gfx/BasePoint.h"
29 #include "mozilla/gfx/BasePoint4D.h"
30 #include "mozilla/gfx/Point.h"
31 #include "nsIGlobalObject.h"
32 #include "nsPIDOMWindow.h"
34 #include "nsStringFlags.h"
36 #include "nsTLiteralString.h"
38 namespace mozilla::dom
{
41 static void SetDataInMatrix(DOMMatrixReadOnly
* aMatrix
, const T
* aData
,
42 int aLength
, ErrorResult
& aRv
);
44 static const double radPerDegree
= 2.0 * M_PI
/ 360.0;
46 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMMatrixReadOnly
, mParent
)
48 JSObject
* DOMMatrixReadOnly::WrapObject(JSContext
* aCx
,
49 JS::Handle
<JSObject
*> aGivenProto
) {
50 return DOMMatrixReadOnly_Binding::Wrap(aCx
, this, aGivenProto
);
53 // https://drafts.fxtf.org/geometry/#matrix-validate-and-fixup-2d
54 static bool ValidateAndFixupMatrix2DInit(DOMMatrix2DInit
& aMatrixInit
,
56 #define ValidateAliases(field, alias, fieldName, aliasName) \
57 if ((field).WasPassed() && (alias).WasPassed() && \
58 !JS::SameValueZero((field).Value(), (alias).Value())) { \
59 aRv.ThrowTypeError<MSG_MATRIX_INIT_CONFLICTING_VALUE>((fieldName), \
63 #define SetFromAliasOrDefault(field, alias, defaultValue) \
64 if (!(field).WasPassed()) { \
65 if ((alias).WasPassed()) { \
66 (field).Construct((alias).Value()); \
68 (field).Construct(defaultValue); \
71 #define ValidateAndSet(field, alias, fieldName, aliasName, defaultValue) \
72 ValidateAliases((field), (alias), fieldName, aliasName); \
73 SetFromAliasOrDefault((field), (alias), (defaultValue));
75 ValidateAndSet(aMatrixInit
.mM11
, aMatrixInit
.mA
, "m11", "a", 1);
76 ValidateAndSet(aMatrixInit
.mM12
, aMatrixInit
.mB
, "m12", "b", 0);
77 ValidateAndSet(aMatrixInit
.mM21
, aMatrixInit
.mC
, "m21", "c", 0);
78 ValidateAndSet(aMatrixInit
.mM22
, aMatrixInit
.mD
, "m22", "d", 1);
79 ValidateAndSet(aMatrixInit
.mM41
, aMatrixInit
.mE
, "m41", "e", 0);
80 ValidateAndSet(aMatrixInit
.mM42
, aMatrixInit
.mF
, "m42", "f", 0);
84 #undef ValidateAliases
85 #undef SetFromAliasOrDefault
89 // https://drafts.fxtf.org/geometry/#matrix-validate-and-fixup
90 static bool ValidateAndFixupMatrixInit(DOMMatrixInit
& aMatrixInit
,
92 #define Check3DField(field, fieldName, defaultValue) \
93 if ((field) != (defaultValue)) { \
94 if (!aMatrixInit.mIs2D.WasPassed()) { \
95 aMatrixInit.mIs2D.Construct(false); \
98 if (aMatrixInit.mIs2D.Value()) { \
99 aRv.ThrowTypeError<MSG_MATRIX_INIT_EXCEEDS_2D>(fieldName); \
104 if (!ValidateAndFixupMatrix2DInit(aMatrixInit
, aRv
)) {
108 Check3DField(aMatrixInit
.mM13
, "m13", 0);
109 Check3DField(aMatrixInit
.mM14
, "m14", 0);
110 Check3DField(aMatrixInit
.mM23
, "m23", 0);
111 Check3DField(aMatrixInit
.mM24
, "m24", 0);
112 Check3DField(aMatrixInit
.mM31
, "m31", 0);
113 Check3DField(aMatrixInit
.mM32
, "m32", 0);
114 Check3DField(aMatrixInit
.mM34
, "m34", 0);
115 Check3DField(aMatrixInit
.mM43
, "m43", 0);
116 Check3DField(aMatrixInit
.mM33
, "m33", 1);
117 Check3DField(aMatrixInit
.mM44
, "m44", 1);
119 if (!aMatrixInit
.mIs2D
.WasPassed()) {
120 aMatrixInit
.mIs2D
.Construct(true);
127 void DOMMatrixReadOnly::SetDataFromMatrix2DInit(
128 const DOMMatrix2DInit
& aMatrixInit
) {
130 mMatrix2D
->_11
= aMatrixInit
.mM11
.Value();
131 mMatrix2D
->_12
= aMatrixInit
.mM12
.Value();
132 mMatrix2D
->_21
= aMatrixInit
.mM21
.Value();
133 mMatrix2D
->_22
= aMatrixInit
.mM22
.Value();
134 mMatrix2D
->_31
= aMatrixInit
.mM41
.Value();
135 mMatrix2D
->_32
= aMatrixInit
.mM42
.Value();
138 void DOMMatrixReadOnly::SetDataFromMatrixInit(
139 const DOMMatrixInit
& aMatrixInit
) {
140 const bool is2D
= aMatrixInit
.mIs2D
.Value();
141 MOZ_ASSERT(is2D
== Is2D());
143 SetDataFromMatrix2DInit(aMatrixInit
);
145 mMatrix3D
->_11
= aMatrixInit
.mM11
.Value();
146 mMatrix3D
->_12
= aMatrixInit
.mM12
.Value();
147 mMatrix3D
->_13
= aMatrixInit
.mM13
;
148 mMatrix3D
->_14
= aMatrixInit
.mM14
;
149 mMatrix3D
->_21
= aMatrixInit
.mM21
.Value();
150 mMatrix3D
->_22
= aMatrixInit
.mM22
.Value();
151 mMatrix3D
->_23
= aMatrixInit
.mM23
;
152 mMatrix3D
->_24
= aMatrixInit
.mM24
;
153 mMatrix3D
->_31
= aMatrixInit
.mM31
;
154 mMatrix3D
->_32
= aMatrixInit
.mM32
;
155 mMatrix3D
->_33
= aMatrixInit
.mM33
;
156 mMatrix3D
->_34
= aMatrixInit
.mM34
;
157 mMatrix3D
->_41
= aMatrixInit
.mM41
.Value();
158 mMatrix3D
->_42
= aMatrixInit
.mM42
.Value();
159 mMatrix3D
->_43
= aMatrixInit
.mM43
;
160 mMatrix3D
->_44
= aMatrixInit
.mM44
;
164 already_AddRefed
<DOMMatrixReadOnly
> DOMMatrixReadOnly::FromMatrix(
165 nsISupports
* aParent
, const DOMMatrix2DInit
& aMatrixInit
,
167 DOMMatrix2DInit
matrixInit(aMatrixInit
);
168 if (!ValidateAndFixupMatrix2DInit(matrixInit
, aRv
)) {
172 RefPtr
<DOMMatrixReadOnly
> matrix
=
173 new DOMMatrixReadOnly(aParent
, /* is2D */ true);
174 matrix
->SetDataFromMatrix2DInit(matrixInit
);
175 return matrix
.forget();
178 already_AddRefed
<DOMMatrixReadOnly
> DOMMatrixReadOnly::FromMatrix(
179 nsISupports
* aParent
, const DOMMatrixInit
& aMatrixInit
, ErrorResult
& aRv
) {
180 DOMMatrixInit
matrixInit(aMatrixInit
);
181 if (!ValidateAndFixupMatrixInit(matrixInit
, aRv
)) {
185 RefPtr
<DOMMatrixReadOnly
> rval
=
186 new DOMMatrixReadOnly(aParent
, matrixInit
.mIs2D
.Value());
187 rval
->SetDataFromMatrixInit(matrixInit
);
188 return rval
.forget();
191 already_AddRefed
<DOMMatrixReadOnly
> DOMMatrixReadOnly::FromMatrix(
192 const GlobalObject
& aGlobal
, const DOMMatrixInit
& aMatrixInit
,
194 RefPtr
<DOMMatrixReadOnly
> matrix
=
195 FromMatrix(aGlobal
.GetAsSupports(), aMatrixInit
, aRv
);
196 return matrix
.forget();
199 already_AddRefed
<DOMMatrixReadOnly
> DOMMatrixReadOnly::FromFloat32Array(
200 const GlobalObject
& aGlobal
, const Float32Array
& aArray32
,
202 nsCOMPtr
<nsISupports
> global
= aGlobal
.GetAsSupports();
203 return aArray32
.ProcessData(
204 [&](const Span
<float>& aData
, JS::AutoCheckCannotGC
&& nogc
) {
205 const int length
= aData
.Length();
206 const bool is2D
= length
== 6;
207 RefPtr
<DOMMatrixReadOnly
> obj
;
209 JS::AutoSuppressGCAnalysis suppress
;
210 obj
= new DOMMatrixReadOnly(global
.forget(), is2D
);
212 SetDataInMatrix(obj
, aData
.Elements(), length
, aRv
);
213 nogc
.reset(); // Done with aData
218 already_AddRefed
<DOMMatrixReadOnly
> DOMMatrixReadOnly::FromFloat64Array(
219 const GlobalObject
& aGlobal
, const Float64Array
& aArray64
,
221 nsCOMPtr
<nsISupports
> global
= aGlobal
.GetAsSupports();
222 return aArray64
.ProcessData(
223 [&](const Span
<double>& aData
, JS::AutoCheckCannotGC
&& nogc
) {
224 const int length
= aData
.Length();
225 const bool is2D
= length
== 6;
226 RefPtr
<DOMMatrixReadOnly
> obj
;
228 JS::AutoSuppressGCAnalysis suppress
;
229 obj
= new DOMMatrixReadOnly(global
.forget(), is2D
);
231 SetDataInMatrix(obj
, aData
.Elements(), length
, aRv
);
232 nogc
.reset(); // Done with aData
237 already_AddRefed
<DOMMatrixReadOnly
> DOMMatrixReadOnly::Constructor(
238 const GlobalObject
& aGlobal
,
239 const Optional
<UTF8StringOrUnrestrictedDoubleSequenceOrDOMMatrixReadOnly
>&
242 if (!aArg
.WasPassed()) {
243 RefPtr
<DOMMatrixReadOnly
> rval
=
244 new DOMMatrixReadOnly(aGlobal
.GetAsSupports());
245 return rval
.forget();
248 const auto& arg
= aArg
.Value();
249 if (arg
.IsUTF8String()) {
250 nsCOMPtr
<nsPIDOMWindowInner
> win
=
251 do_QueryInterface(aGlobal
.GetAsSupports());
253 aRv
.ThrowTypeError
<MSG_ILLEGAL_CONSTRUCTOR
>();
256 RefPtr
<DOMMatrixReadOnly
> rval
=
257 new DOMMatrixReadOnly(aGlobal
.GetAsSupports());
258 rval
->SetMatrixValue(arg
.GetAsUTF8String(), aRv
);
259 return rval
.forget();
261 if (arg
.IsDOMMatrixReadOnly()) {
262 RefPtr
<DOMMatrixReadOnly
> obj
= new DOMMatrixReadOnly(
263 aGlobal
.GetAsSupports(), arg
.GetAsDOMMatrixReadOnly());
267 const auto& sequence
= arg
.GetAsUnrestrictedDoubleSequence();
268 const int length
= sequence
.Length();
269 const bool is2D
= length
== 6;
270 RefPtr
<DOMMatrixReadOnly
> rval
=
271 new DOMMatrixReadOnly(aGlobal
.GetAsSupports(), is2D
);
272 SetDataInMatrix(rval
, sequence
.Elements(), length
, aRv
);
273 return rval
.forget();
276 already_AddRefed
<DOMMatrixReadOnly
> DOMMatrixReadOnly::ReadStructuredClone(
277 JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
278 JSStructuredCloneReader
* aReader
) {
281 if (!JS_ReadBytes(aReader
, &is2D
, 1)) {
285 RefPtr
<DOMMatrixReadOnly
> rval
= new DOMMatrixReadOnly(aGlobal
, is2D
);
287 if (!ReadStructuredCloneElements(aReader
, rval
)) {
291 return rval
.forget();
294 already_AddRefed
<DOMMatrix
> DOMMatrixReadOnly::Translate(double aTx
, double aTy
,
296 RefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
297 retval
->TranslateSelf(aTx
, aTy
, aTz
);
299 return retval
.forget();
302 already_AddRefed
<DOMMatrix
> DOMMatrixReadOnly::Scale(
303 double aScaleX
, const Optional
<double>& aScaleY
, double aScaleZ
,
304 double aOriginX
, double aOriginY
, double aOriginZ
) const {
305 RefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
306 retval
->ScaleSelf(aScaleX
, aScaleY
, aScaleZ
, aOriginX
, aOriginY
, aOriginZ
);
308 return retval
.forget();
311 already_AddRefed
<DOMMatrix
> DOMMatrixReadOnly::Scale3d(double aScale
,
314 double aOriginZ
) const {
315 RefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
316 retval
->Scale3dSelf(aScale
, aOriginX
, aOriginY
, aOriginZ
);
318 return retval
.forget();
321 already_AddRefed
<DOMMatrix
> DOMMatrixReadOnly::ScaleNonUniform(
322 double aScaleX
, double aScaleY
) const {
323 RefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
324 retval
->ScaleSelf(aScaleX
, Optional
<double>(aScaleY
), 1, 0, 0, 0);
326 return retval
.forget();
329 already_AddRefed
<DOMMatrix
> DOMMatrixReadOnly::Rotate(
330 double aRotX
, const Optional
<double>& aRotY
,
331 const Optional
<double>& aRotZ
) const {
332 RefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
333 retval
->RotateSelf(aRotX
, aRotY
, aRotZ
);
335 return retval
.forget();
338 already_AddRefed
<DOMMatrix
> DOMMatrixReadOnly::RotateFromVector(
339 double x
, double y
) const {
340 RefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
341 retval
->RotateFromVectorSelf(x
, y
);
343 return retval
.forget();
346 already_AddRefed
<DOMMatrix
> DOMMatrixReadOnly::RotateAxisAngle(
347 double aX
, double aY
, double aZ
, double aAngle
) const {
348 RefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
349 retval
->RotateAxisAngleSelf(aX
, aY
, aZ
, aAngle
);
351 return retval
.forget();
354 already_AddRefed
<DOMMatrix
> DOMMatrixReadOnly::SkewX(double aSx
) const {
355 RefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
356 retval
->SkewXSelf(aSx
);
358 return retval
.forget();
361 already_AddRefed
<DOMMatrix
> DOMMatrixReadOnly::SkewY(double aSy
) const {
362 RefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
363 retval
->SkewYSelf(aSy
);
365 return retval
.forget();
368 already_AddRefed
<DOMMatrix
> DOMMatrixReadOnly::Multiply(
369 const DOMMatrixInit
& other
, ErrorResult
& aRv
) const {
370 RefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
371 retval
->MultiplySelf(other
, aRv
);
373 return retval
.forget();
376 already_AddRefed
<DOMMatrix
> DOMMatrixReadOnly::FlipX() const {
377 RefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
379 gfx::Matrix4x4Double m
;
381 retval
->mMatrix3D
= MakeUnique
<gfx::Matrix4x4Double
>(m
* *mMatrix3D
);
386 MakeUnique
<gfx::MatrixDouble
>(mMatrix2D
? m
* *mMatrix2D
: m
);
389 return retval
.forget();
392 already_AddRefed
<DOMMatrix
> DOMMatrixReadOnly::FlipY() const {
393 RefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
395 gfx::Matrix4x4Double m
;
397 retval
->mMatrix3D
= MakeUnique
<gfx::Matrix4x4Double
>(m
* *mMatrix3D
);
402 MakeUnique
<gfx::MatrixDouble
>(mMatrix2D
? m
* *mMatrix2D
: m
);
405 return retval
.forget();
408 already_AddRefed
<DOMMatrix
> DOMMatrixReadOnly::Inverse() const {
409 RefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
410 retval
->InvertSelf();
412 return retval
.forget();
415 bool DOMMatrixReadOnly::Is2D() const { return !mMatrix3D
; }
417 bool DOMMatrixReadOnly::IsIdentity() const {
419 return mMatrix3D
->IsIdentity();
422 return mMatrix2D
->IsIdentity();
425 already_AddRefed
<DOMPoint
> DOMMatrixReadOnly::TransformPoint(
426 const DOMPointInit
& point
) const {
427 RefPtr
<DOMPoint
> retval
= new DOMPoint(mParent
);
430 gfx::Point4D transformedPoint
;
431 transformedPoint
.x
= point
.mX
;
432 transformedPoint
.y
= point
.mY
;
433 transformedPoint
.z
= point
.mZ
;
434 transformedPoint
.w
= point
.mW
;
436 transformedPoint
= mMatrix3D
->TransformPoint(transformedPoint
);
438 retval
->SetX(transformedPoint
.x
);
439 retval
->SetY(transformedPoint
.y
);
440 retval
->SetZ(transformedPoint
.z
);
441 retval
->SetW(transformedPoint
.w
);
442 } else if (point
.mZ
!= 0 || point
.mW
!= 1.0) {
443 gfx::Matrix4x4Double
tempMatrix(gfx::Matrix4x4Double::From2D(*mMatrix2D
));
445 gfx::PointDouble4D transformedPoint
;
446 transformedPoint
.x
= point
.mX
;
447 transformedPoint
.y
= point
.mY
;
448 transformedPoint
.z
= point
.mZ
;
449 transformedPoint
.w
= point
.mW
;
451 transformedPoint
= tempMatrix
.TransformPoint(transformedPoint
);
453 retval
->SetX(transformedPoint
.x
);
454 retval
->SetY(transformedPoint
.y
);
455 retval
->SetZ(transformedPoint
.z
);
456 retval
->SetW(transformedPoint
.w
);
458 gfx::PointDouble transformedPoint
;
459 transformedPoint
.x
= point
.mX
;
460 transformedPoint
.y
= point
.mY
;
462 transformedPoint
= mMatrix2D
->TransformPoint(transformedPoint
);
464 retval
->SetX(transformedPoint
.x
);
465 retval
->SetY(transformedPoint
.y
);
466 retval
->SetZ(point
.mZ
);
467 retval
->SetW(point
.mW
);
469 return retval
.forget();
472 template <typename T
>
473 void GetDataFromMatrix(const DOMMatrixReadOnly
* aMatrix
, T
* aData
) {
474 aData
[0] = static_cast<T
>(aMatrix
->M11());
475 aData
[1] = static_cast<T
>(aMatrix
->M12());
476 aData
[2] = static_cast<T
>(aMatrix
->M13());
477 aData
[3] = static_cast<T
>(aMatrix
->M14());
478 aData
[4] = static_cast<T
>(aMatrix
->M21());
479 aData
[5] = static_cast<T
>(aMatrix
->M22());
480 aData
[6] = static_cast<T
>(aMatrix
->M23());
481 aData
[7] = static_cast<T
>(aMatrix
->M24());
482 aData
[8] = static_cast<T
>(aMatrix
->M31());
483 aData
[9] = static_cast<T
>(aMatrix
->M32());
484 aData
[10] = static_cast<T
>(aMatrix
->M33());
485 aData
[11] = static_cast<T
>(aMatrix
->M34());
486 aData
[12] = static_cast<T
>(aMatrix
->M41());
487 aData
[13] = static_cast<T
>(aMatrix
->M42());
488 aData
[14] = static_cast<T
>(aMatrix
->M43());
489 aData
[15] = static_cast<T
>(aMatrix
->M44());
492 void DOMMatrixReadOnly::ToFloat32Array(JSContext
* aCx
,
493 JS::MutableHandle
<JSObject
*> aResult
,
494 ErrorResult
& aRv
) const {
495 AutoTArray
<float, 16> arr
;
497 GetDataFromMatrix(this, arr
.Elements());
498 JS::Rooted
<JS::Value
> value(aCx
);
499 if (!ToJSValue(aCx
, TypedArrayCreator
<Float32Array
>(arr
), &value
)) {
500 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
503 aResult
.set(&value
.toObject());
506 void DOMMatrixReadOnly::ToFloat64Array(JSContext
* aCx
,
507 JS::MutableHandle
<JSObject
*> aResult
,
508 ErrorResult
& aRv
) const {
509 AutoTArray
<double, 16> arr
;
511 GetDataFromMatrix(this, arr
.Elements());
512 JS::Rooted
<JS::Value
> value(aCx
);
513 if (!ToJSValue(aCx
, TypedArrayCreator
<Float64Array
>(arr
), &value
)) {
514 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
517 aResult
.set(&value
.toObject());
520 void DOMMatrixReadOnly::Stringify(nsAString
& aResult
, ErrorResult
& aRv
) {
521 char cbuf
[JS::MaximumNumberToStringLength
];
522 nsAutoString matrixStr
;
523 auto AppendDouble
= [&aRv
, &cbuf
, &matrixStr
](double d
,
524 bool isLastItem
= false) {
525 if (!std::isfinite(d
)) {
526 aRv
.ThrowInvalidStateError(
527 "Matrix with a non-finite element cannot be stringified.");
530 JS::NumberToString(d
, cbuf
);
531 matrixStr
.AppendASCII(cbuf
);
533 matrixStr
.AppendLiteral(", ");
539 // We can't use AppendPrintf here, because it does locale-specific
540 // formatting of floating-point values.
541 matrixStr
.AssignLiteral("matrix3d(");
542 if (!AppendDouble(M11()) || !AppendDouble(M12()) || !AppendDouble(M13()) ||
543 !AppendDouble(M14()) || !AppendDouble(M21()) || !AppendDouble(M22()) ||
544 !AppendDouble(M23()) || !AppendDouble(M24()) || !AppendDouble(M31()) ||
545 !AppendDouble(M32()) || !AppendDouble(M33()) || !AppendDouble(M34()) ||
546 !AppendDouble(M41()) || !AppendDouble(M42()) || !AppendDouble(M43()) ||
547 !AppendDouble(M44(), true)) {
550 matrixStr
.AppendLiteral(")");
552 // We can't use AppendPrintf here, because it does locale-specific
553 // formatting of floating-point values.
554 matrixStr
.AssignLiteral("matrix(");
555 if (!AppendDouble(A()) || !AppendDouble(B()) || !AppendDouble(C()) ||
556 !AppendDouble(D()) || !AppendDouble(E()) || !AppendDouble(F(), true)) {
559 matrixStr
.AppendLiteral(")");
565 // https://drafts.fxtf.org/geometry/#structured-serialization
566 bool DOMMatrixReadOnly::WriteStructuredClone(
567 JSContext
* aCx
, JSStructuredCloneWriter
* aWriter
) const {
568 const uint8_t is2D
= Is2D();
570 if (!JS_WriteBytes(aWriter
, &is2D
, 1)) {
575 return JS_WriteDouble(aWriter
, mMatrix2D
->_11
) &&
576 JS_WriteDouble(aWriter
, mMatrix2D
->_12
) &&
577 JS_WriteDouble(aWriter
, mMatrix2D
->_21
) &&
578 JS_WriteDouble(aWriter
, mMatrix2D
->_22
) &&
579 JS_WriteDouble(aWriter
, mMatrix2D
->_31
) &&
580 JS_WriteDouble(aWriter
, mMatrix2D
->_32
);
583 return JS_WriteDouble(aWriter
, mMatrix3D
->_11
) &&
584 JS_WriteDouble(aWriter
, mMatrix3D
->_12
) &&
585 JS_WriteDouble(aWriter
, mMatrix3D
->_13
) &&
586 JS_WriteDouble(aWriter
, mMatrix3D
->_14
) &&
587 JS_WriteDouble(aWriter
, mMatrix3D
->_21
) &&
588 JS_WriteDouble(aWriter
, mMatrix3D
->_22
) &&
589 JS_WriteDouble(aWriter
, mMatrix3D
->_23
) &&
590 JS_WriteDouble(aWriter
, mMatrix3D
->_24
) &&
591 JS_WriteDouble(aWriter
, mMatrix3D
->_31
) &&
592 JS_WriteDouble(aWriter
, mMatrix3D
->_32
) &&
593 JS_WriteDouble(aWriter
, mMatrix3D
->_33
) &&
594 JS_WriteDouble(aWriter
, mMatrix3D
->_34
) &&
595 JS_WriteDouble(aWriter
, mMatrix3D
->_41
) &&
596 JS_WriteDouble(aWriter
, mMatrix3D
->_42
) &&
597 JS_WriteDouble(aWriter
, mMatrix3D
->_43
) &&
598 JS_WriteDouble(aWriter
, mMatrix3D
->_44
);
601 bool DOMMatrixReadOnly::ReadStructuredCloneElements(
602 JSStructuredCloneReader
* aReader
, DOMMatrixReadOnly
* matrix
) {
603 if (matrix
->Is2D() == 1) {
604 JS_ReadDouble(aReader
, &(matrix
->mMatrix2D
->_11
));
605 JS_ReadDouble(aReader
, &(matrix
->mMatrix2D
->_12
));
606 JS_ReadDouble(aReader
, &(matrix
->mMatrix2D
->_21
));
607 JS_ReadDouble(aReader
, &(matrix
->mMatrix2D
->_22
));
608 JS_ReadDouble(aReader
, &(matrix
->mMatrix2D
->_31
));
609 JS_ReadDouble(aReader
, &(matrix
->mMatrix2D
->_32
));
611 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_11
));
612 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_12
));
613 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_13
));
614 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_14
));
615 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_21
));
616 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_22
));
617 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_23
));
618 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_24
));
619 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_31
));
620 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_32
));
621 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_33
));
622 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_34
));
623 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_41
));
624 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_42
));
625 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_43
));
626 JS_ReadDouble(aReader
, &(matrix
->mMatrix3D
->_44
));
632 already_AddRefed
<DOMMatrix
> DOMMatrix::FromMatrix(
633 nsISupports
* aParent
, const DOMMatrixInit
& aMatrixInit
, ErrorResult
& aRv
) {
634 DOMMatrixInit
matrixInit(aMatrixInit
);
635 if (!ValidateAndFixupMatrixInit(matrixInit
, aRv
)) {
639 RefPtr
<DOMMatrix
> matrix
= new DOMMatrix(aParent
, matrixInit
.mIs2D
.Value());
640 matrix
->SetDataFromMatrixInit(matrixInit
);
641 return matrix
.forget();
644 already_AddRefed
<DOMMatrix
> DOMMatrix::FromMatrix(
645 const GlobalObject
& aGlobal
, const DOMMatrixInit
& aMatrixInit
,
647 RefPtr
<DOMMatrix
> matrix
=
648 FromMatrix(aGlobal
.GetAsSupports(), aMatrixInit
, aRv
);
649 return matrix
.forget();
652 already_AddRefed
<DOMMatrix
> DOMMatrix::FromFloat32Array(
653 const GlobalObject
& aGlobal
, const Float32Array
& aArray32
,
655 nsCOMPtr
<nsISupports
> global
= aGlobal
.GetAsSupports();
656 return aArray32
.ProcessData(
657 [&](const Span
<float>& aData
, JS::AutoCheckCannotGC
&& nogc
) {
658 const int length
= aData
.Length();
659 const bool is2D
= length
== 6;
660 RefPtr
<DOMMatrix
> obj
;
662 JS::AutoSuppressGCAnalysis suppress
;
663 obj
= new DOMMatrix(global
.forget(), is2D
);
665 SetDataInMatrix(obj
, aData
.Elements(), length
, aRv
);
666 nogc
.reset(); // Done with aData
671 already_AddRefed
<DOMMatrix
> DOMMatrix::FromFloat64Array(
672 const GlobalObject
& aGlobal
, const Float64Array
& aArray64
,
674 nsCOMPtr
<nsISupports
> global
= aGlobal
.GetAsSupports();
675 return aArray64
.ProcessData(
676 [&](const Span
<double>& aData
, JS::AutoCheckCannotGC
&& nogc
) {
677 const int length
= aData
.Length();
678 const bool is2D
= length
== 6;
679 RefPtr
<DOMMatrix
> obj
;
681 JS::AutoSuppressGCAnalysis suppress
;
682 obj
= new DOMMatrix(global
.forget(), is2D
);
684 SetDataInMatrix(obj
, aData
.Elements(), length
, aRv
);
685 nogc
.reset(); // Done with aData
690 already_AddRefed
<DOMMatrix
> DOMMatrix::Constructor(
691 const GlobalObject
& aGlobal
,
692 const Optional
<UTF8StringOrUnrestrictedDoubleSequenceOrDOMMatrixReadOnly
>&
695 if (!aArg
.WasPassed()) {
696 RefPtr
<DOMMatrix
> rval
= new DOMMatrix(aGlobal
.GetAsSupports());
697 return rval
.forget();
700 const auto& arg
= aArg
.Value();
701 if (arg
.IsUTF8String()) {
702 nsCOMPtr
<nsPIDOMWindowInner
> win
=
703 do_QueryInterface(aGlobal
.GetAsSupports());
705 aRv
.ThrowTypeError
<MSG_ILLEGAL_CONSTRUCTOR
>();
708 RefPtr
<DOMMatrix
> rval
= new DOMMatrix(aGlobal
.GetAsSupports());
709 rval
->SetMatrixValue(arg
.GetAsUTF8String(), aRv
);
710 return rval
.forget();
712 if (arg
.IsDOMMatrixReadOnly()) {
713 RefPtr
<DOMMatrix
> obj
=
714 new DOMMatrix(aGlobal
.GetAsSupports(), arg
.GetAsDOMMatrixReadOnly());
718 const auto& sequence
= arg
.GetAsUnrestrictedDoubleSequence();
719 const int length
= sequence
.Length();
720 const bool is2D
= length
== 6;
721 RefPtr
<DOMMatrix
> rval
= new DOMMatrix(aGlobal
.GetAsSupports(), is2D
);
722 SetDataInMatrix(rval
, sequence
.Elements(), length
, aRv
);
723 return rval
.forget();
726 template <typename T
>
727 static void SetDataInMatrix(DOMMatrixReadOnly
* aMatrix
, const T
* aData
,
728 int aLength
, ErrorResult
& aRv
) {
730 aMatrix
->SetM11(aData
[0]);
731 aMatrix
->SetM12(aData
[1]);
732 aMatrix
->SetM13(aData
[2]);
733 aMatrix
->SetM14(aData
[3]);
734 aMatrix
->SetM21(aData
[4]);
735 aMatrix
->SetM22(aData
[5]);
736 aMatrix
->SetM23(aData
[6]);
737 aMatrix
->SetM24(aData
[7]);
738 aMatrix
->SetM31(aData
[8]);
739 aMatrix
->SetM32(aData
[9]);
740 aMatrix
->SetM33(aData
[10]);
741 aMatrix
->SetM34(aData
[11]);
742 aMatrix
->SetM41(aData
[12]);
743 aMatrix
->SetM42(aData
[13]);
744 aMatrix
->SetM43(aData
[14]);
745 aMatrix
->SetM44(aData
[15]);
746 } else if (aLength
== 6) {
747 aMatrix
->SetA(aData
[0]);
748 aMatrix
->SetB(aData
[1]);
749 aMatrix
->SetC(aData
[2]);
750 aMatrix
->SetD(aData
[3]);
751 aMatrix
->SetE(aData
[4]);
752 aMatrix
->SetF(aData
[5]);
754 nsAutoCString lengthStr
;
755 lengthStr
.AppendInt(aLength
);
756 aRv
.ThrowTypeError
<MSG_MATRIX_INIT_LENGTH_WRONG
>(lengthStr
);
760 already_AddRefed
<DOMMatrix
> DOMMatrix::ReadStructuredClone(
761 JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
762 JSStructuredCloneReader
* aReader
) {
765 if (!JS_ReadBytes(aReader
, &is2D
, 1)) {
769 RefPtr
<DOMMatrix
> rval
= new DOMMatrix(aGlobal
, is2D
);
771 if (!ReadStructuredCloneElements(aReader
, rval
)) {
775 return rval
.forget();
778 void DOMMatrixReadOnly::Ensure3DMatrix() {
780 mMatrix3D
= MakeUnique
<gfx::Matrix4x4Double
>(
781 gfx::Matrix4x4Double::From2D(*mMatrix2D
));
786 DOMMatrix
* DOMMatrix::MultiplySelf(const DOMMatrixInit
& aOtherInit
,
788 RefPtr
<DOMMatrix
> other
= FromMatrix(mParent
, aOtherInit
, aRv
);
793 if (other
->IsIdentity()) {
799 *mMatrix3D
= gfx::Matrix4x4Double::From2D(*other
->mMatrix2D
) * *mMatrix3D
;
801 *mMatrix2D
= *other
->mMatrix2D
* *mMatrix2D
;
805 *mMatrix3D
= *other
->mMatrix3D
* *mMatrix3D
;
811 DOMMatrix
* DOMMatrix::PreMultiplySelf(const DOMMatrixInit
& aOtherInit
,
813 RefPtr
<DOMMatrix
> other
= FromMatrix(mParent
, aOtherInit
, aRv
);
818 if (other
->IsIdentity()) {
824 *mMatrix3D
= *mMatrix3D
* gfx::Matrix4x4Double::From2D(*other
->mMatrix2D
);
826 *mMatrix2D
= *mMatrix2D
* *other
->mMatrix2D
;
830 *mMatrix3D
= *mMatrix3D
* *other
->mMatrix3D
;
836 DOMMatrix
* DOMMatrix::TranslateSelf(double aTx
, double aTy
, double aTz
) {
837 if (aTx
== 0 && aTy
== 0 && aTz
== 0) {
841 if (mMatrix3D
|| aTz
!= 0) {
843 mMatrix3D
->PreTranslate(aTx
, aTy
, aTz
);
845 mMatrix2D
->PreTranslate(aTx
, aTy
);
851 DOMMatrix
* DOMMatrix::ScaleSelf(double aScaleX
, const Optional
<double>& aScaleY
,
852 double aScaleZ
, double aOriginX
,
853 double aOriginY
, double aOriginZ
) {
854 const double scaleY
= aScaleY
.WasPassed() ? aScaleY
.Value() : aScaleX
;
856 TranslateSelf(aOriginX
, aOriginY
, aOriginZ
);
858 if (mMatrix3D
|| aScaleZ
!= 1.0) {
860 gfx::Matrix4x4Double m
;
864 *mMatrix3D
= m
* *mMatrix3D
;
869 *mMatrix2D
= m
* *mMatrix2D
;
872 TranslateSelf(-aOriginX
, -aOriginY
, -aOriginZ
);
877 DOMMatrix
* DOMMatrix::Scale3dSelf(double aScale
, double aOriginX
,
878 double aOriginY
, double aOriginZ
) {
879 ScaleSelf(aScale
, Optional
<double>(aScale
), aScale
, aOriginX
, aOriginY
,
885 DOMMatrix
* DOMMatrix::RotateFromVectorSelf(double aX
, double aY
) {
886 const double angle
= (aX
== 0.0 && aY
== 0.0) ? 0 : atan2(aY
, aX
);
888 if (fmod(angle
, 2 * M_PI
) == 0) {
893 RotateAxisAngleSelf(0, 0, 1, angle
/ radPerDegree
);
895 *mMatrix2D
= mMatrix2D
->PreRotate(angle
);
901 DOMMatrix
* DOMMatrix::RotateSelf(double aRotX
, const Optional
<double>& aRotY
,
902 const Optional
<double>& aRotZ
) {
905 if (!aRotY
.WasPassed() && !aRotZ
.WasPassed()) {
910 rotY
= aRotY
.WasPassed() ? aRotY
.Value() : 0;
911 rotZ
= aRotZ
.WasPassed() ? aRotZ
.Value() : 0;
914 if (aRotX
!= 0 || rotY
!= 0) {
919 if (fmod(rotZ
, 360) != 0) {
920 mMatrix3D
->RotateZ(rotZ
* radPerDegree
);
922 if (fmod(rotY
, 360) != 0) {
923 mMatrix3D
->RotateY(rotY
* radPerDegree
);
925 if (fmod(aRotX
, 360) != 0) {
926 mMatrix3D
->RotateX(aRotX
* radPerDegree
);
928 } else if (fmod(rotZ
, 360) != 0) {
929 *mMatrix2D
= mMatrix2D
->PreRotate(rotZ
* radPerDegree
);
935 DOMMatrix
* DOMMatrix::RotateAxisAngleSelf(double aX
, double aY
, double aZ
,
937 if (fmod(aAngle
, 360) == 0) {
941 aAngle
*= radPerDegree
;
944 gfx::Matrix4x4Double m
;
945 m
.SetRotateAxisAngle(aX
, aY
, aZ
, aAngle
);
947 *mMatrix3D
= m
* *mMatrix3D
;
952 DOMMatrix
* DOMMatrix::SkewXSelf(double aSx
) {
953 if (fmod(aSx
, 360) == 0) {
958 gfx::Matrix4x4Double m
;
959 m
._21
= tan(aSx
* radPerDegree
);
960 *mMatrix3D
= m
* *mMatrix3D
;
963 m
._21
= tan(aSx
* radPerDegree
);
964 *mMatrix2D
= m
* *mMatrix2D
;
970 DOMMatrix
* DOMMatrix::SkewYSelf(double aSy
) {
971 if (fmod(aSy
, 360) == 0) {
976 gfx::Matrix4x4Double m
;
977 m
._12
= tan(aSy
* radPerDegree
);
978 *mMatrix3D
= m
* *mMatrix3D
;
981 m
._12
= tan(aSy
* radPerDegree
);
982 *mMatrix2D
= m
* *mMatrix2D
;
988 DOMMatrix
* DOMMatrix::InvertSelf() {
990 if (!mMatrix3D
->Invert()) {
993 } else if (!mMatrix2D
->Invert()) {
996 mMatrix3D
= MakeUnique
<gfx::Matrix4x4Double
>();
1003 DOMMatrixReadOnly
* DOMMatrixReadOnly::SetMatrixValue(
1004 const nsACString
& aTransformList
, ErrorResult
& aRv
) {
1005 // An empty string is a no-op.
1006 if (aTransformList
.IsEmpty()) {
1010 gfx::Matrix4x4 transform
;
1011 bool contains3dTransform
= false;
1012 if (!ServoCSSParser::ParseTransformIntoMatrix(
1013 aTransformList
, contains3dTransform
, transform
)) {
1014 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
1018 if (!contains3dTransform
) {
1019 mMatrix3D
= nullptr;
1021 mMatrix2D
= MakeUnique
<gfx::MatrixDouble
>();
1024 SetA(transform
._11
);
1025 SetB(transform
._12
);
1026 SetC(transform
._21
);
1027 SetD(transform
._22
);
1028 SetE(transform
._41
);
1029 SetF(transform
._42
);
1031 mMatrix3D
= MakeUnique
<gfx::Matrix4x4Double
>(transform
);
1032 mMatrix2D
= nullptr;
1038 DOMMatrix
* DOMMatrix::SetMatrixValue(const nsACString
& aTransformList
,
1040 DOMMatrixReadOnly::SetMatrixValue(aTransformList
, aRv
);
1044 JSObject
* DOMMatrix::WrapObject(JSContext
* aCx
,
1045 JS::Handle
<JSObject
*> aGivenProto
) {
1046 return DOMMatrix_Binding::Wrap(aCx
, this, aGivenProto
);
1049 } // namespace mozilla::dom