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 "DOMSVGTransform.h"
9 #include "mozAutoDocUpdate.h"
10 #include "mozilla/dom/DOMMatrix.h"
11 #include "mozilla/dom/DOMMatrixBinding.h"
12 #include "mozilla/dom/SVGMatrix.h"
13 #include "mozilla/dom/SVGTransformBinding.h"
14 #include "mozilla/DebugOnly.h"
15 #include "mozilla/FloatingPoint.h"
16 #include "mozilla/Maybe.h"
18 #include "SVGAnimatedTransformList.h"
19 #include "SVGAttrTearoffTable.h"
22 const double kRadPerDegree
= 2.0 * M_PI
/ 360.0;
25 namespace mozilla::dom
{
27 using namespace SVGTransform_Binding
;
29 static SVGAttrTearoffTable
<DOMSVGTransform
, SVGMatrix
>&
30 SVGMatrixTearoffTable() {
31 static SVGAttrTearoffTable
<DOMSVGTransform
, SVGMatrix
> sSVGMatrixTearoffTable
;
32 return sSVGMatrixTearoffTable
;
35 //----------------------------------------------------------------------
37 // We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to
38 // clear our list's weak ref to us to be safe. (The other option would be to
39 // not unlink and rely on the breaking of the other edges in the cycle, as
40 // NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
41 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGTransform
)
43 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGTransform
)
44 // We may not belong to a list, so we must null check tmp->mList.
46 tmp
->mList
->mItems
[tmp
->mListIndex
] = nullptr;
48 NS_IMPL_CYCLE_COLLECTION_UNLINK(mList
)
49 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
50 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
52 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGTransform
)
53 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mList
)
54 SVGMatrix
* matrix
= SVGMatrixTearoffTable().GetTearoff(tmp
);
55 CycleCollectionNoteChild(cb
, matrix
, "matrix");
56 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
58 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGTransform
)
59 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
60 NS_IMPL_CYCLE_COLLECTION_TRACE_END
62 JSObject
* DOMSVGTransform::WrapObject(JSContext
* aCx
,
63 JS::Handle
<JSObject
*> aGivenProto
) {
64 return SVGTransform_Binding::Wrap(aCx
, this, aGivenProto
);
67 //----------------------------------------------------------------------
70 DOMSVGTransform::DOMSVGTransform(DOMSVGTransformList
* aList
,
71 uint32_t aListIndex
, bool aIsAnimValItem
)
73 mListIndex(aListIndex
),
74 mIsAnimValItem(aIsAnimValItem
),
76 // These shifts are in sync with the members in the header.
77 MOZ_ASSERT(aList
&& aListIndex
<= MaxListIndex(), "bad arg");
79 MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGNumber!");
82 DOMSVGTransform::DOMSVGTransform()
85 mIsAnimValItem(false),
86 mTransform(new SVGTransform()) // Default ctor for objects not in a
87 // list initialises to matrix type with
91 DOMSVGTransform::DOMSVGTransform(const gfxMatrix
& aMatrix
)
94 mIsAnimValItem(false),
95 mTransform(new SVGTransform(aMatrix
)) {}
97 DOMSVGTransform::DOMSVGTransform(const DOMMatrix2DInit
& aMatrix
,
101 mIsAnimValItem(false),
102 mTransform(new SVGTransform()) {
103 SetMatrix(aMatrix
, rv
);
106 DOMSVGTransform::DOMSVGTransform(const SVGTransform
& aTransform
)
109 mIsAnimValItem(false),
110 mTransform(new SVGTransform(aTransform
)) {}
112 DOMSVGTransform::~DOMSVGTransform() {
113 SVGMatrix
* matrix
= SVGMatrixTearoffTable().GetTearoff(this);
115 SVGMatrixTearoffTable().RemoveTearoff(this);
118 // Our mList's weak ref to us must be nulled out when we die. If GC has
119 // unlinked us using the cycle collector code, then that has already
120 // happened, and mList is null.
122 mList
->mItems
[mListIndex
] = nullptr;
126 uint16_t DOMSVGTransform::Type() const { return Transform().Type(); }
128 SVGMatrix
* DOMSVGTransform::GetMatrix() {
129 SVGMatrix
* wrapper
= SVGMatrixTearoffTable().GetTearoff(this);
131 NS_ADDREF(wrapper
= new SVGMatrix(*this));
132 SVGMatrixTearoffTable().AddTearoff(this, wrapper
);
137 float DOMSVGTransform::Angle() const { return Transform().Angle(); }
139 void DOMSVGTransform::SetMatrix(const DOMMatrix2DInit
& aMatrix
,
141 if (mIsAnimValItem
) {
142 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
145 RefPtr
<DOMMatrixReadOnly
> matrix
=
146 DOMMatrixReadOnly::FromMatrix(GetParentObject(), aMatrix
, aRv
);
150 const gfxMatrix
* matrix2D
= matrix
->GetInternal2D();
151 if (!matrix2D
->IsFinite()) {
152 aRv
.ThrowTypeError
<MSG_NOT_FINITE
>("Matrix setter");
155 SetMatrix(*matrix2D
);
158 void DOMSVGTransform::SetTranslate(float tx
, float ty
, ErrorResult
& rv
) {
159 if (mIsAnimValItem
) {
160 rv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
164 if (Transform().Type() == SVG_TRANSFORM_TRANSLATE
&& Matrixgfx()._31
== tx
&&
165 Matrixgfx()._32
== ty
) {
169 AutoChangeTransformListNotifier
notifier(this);
170 Transform().SetTranslate(tx
, ty
);
173 void DOMSVGTransform::SetScale(float sx
, float sy
, ErrorResult
& rv
) {
174 if (mIsAnimValItem
) {
175 rv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
179 if (Transform().Type() == SVG_TRANSFORM_SCALE
&& Matrixgfx()._11
== sx
&&
180 Matrixgfx()._22
== sy
) {
183 AutoChangeTransformListNotifier
notifier(this);
184 Transform().SetScale(sx
, sy
);
187 void DOMSVGTransform::SetRotate(float angle
, float cx
, float cy
,
189 if (mIsAnimValItem
) {
190 rv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
194 if (Transform().Type() == SVG_TRANSFORM_ROTATE
) {
195 float currentCx
, currentCy
;
196 Transform().GetRotationOrigin(currentCx
, currentCy
);
197 if (Transform().Angle() == angle
&& currentCx
== cx
&& currentCy
== cy
) {
202 AutoChangeTransformListNotifier
notifier(this);
203 Transform().SetRotate(angle
, cx
, cy
);
206 void DOMSVGTransform::SetSkewX(float angle
, ErrorResult
& rv
) {
207 if (mIsAnimValItem
) {
208 rv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
212 if (Transform().Type() == SVG_TRANSFORM_SKEWX
&&
213 Transform().Angle() == angle
) {
217 if (!std::isfinite(tan(angle
* kRadPerDegree
))) {
218 rv
.ThrowRangeError
<MSG_INVALID_TRANSFORM_ANGLE_ERROR
>();
222 AutoChangeTransformListNotifier
notifier(this);
223 DebugOnly
<nsresult
> result
= Transform().SetSkewX(angle
);
224 MOZ_ASSERT(NS_SUCCEEDED(result
), "SetSkewX unexpectedly failed");
227 void DOMSVGTransform::SetSkewY(float angle
, ErrorResult
& rv
) {
228 if (mIsAnimValItem
) {
229 rv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
233 if (Transform().Type() == SVG_TRANSFORM_SKEWY
&&
234 Transform().Angle() == angle
) {
238 if (!std::isfinite(tan(angle
* kRadPerDegree
))) {
239 rv
.ThrowRangeError
<MSG_INVALID_TRANSFORM_ANGLE_ERROR
>();
243 AutoChangeTransformListNotifier
notifier(this);
244 DebugOnly
<nsresult
> result
= Transform().SetSkewY(angle
);
245 MOZ_ASSERT(NS_SUCCEEDED(result
), "SetSkewY unexpectedly failed");
248 //----------------------------------------------------------------------
249 // List management methods:
251 void DOMSVGTransform::InsertingIntoList(DOMSVGTransformList
* aList
,
253 bool aIsAnimValItem
) {
254 MOZ_ASSERT(!HasOwner(), "Inserting item that is already in a list");
257 mListIndex
= aListIndex
;
258 mIsAnimValItem
= aIsAnimValItem
;
259 mTransform
= nullptr;
261 MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGLength!");
264 void DOMSVGTransform::RemovingFromList() {
265 MOZ_ASSERT(!mTransform
,
266 "Item in list also has another non-list value associated with it");
268 mTransform
= MakeUnique
<SVGTransform
>(InternalItem());
270 mIsAnimValItem
= false;
273 SVGTransform
& DOMSVGTransform::InternalItem() {
274 SVGAnimatedTransformList
* alist
= Element()->GetAnimatedTransformList();
275 return mIsAnimValItem
&& alist
->mAnimVal
? (*alist
->mAnimVal
)[mListIndex
]
276 : alist
->mBaseVal
[mListIndex
];
279 const SVGTransform
& DOMSVGTransform::InternalItem() const {
280 return const_cast<DOMSVGTransform
*>(this)->InternalItem();
284 bool DOMSVGTransform::IndexIsValid() {
285 SVGAnimatedTransformList
* alist
= Element()->GetAnimatedTransformList();
286 return (mIsAnimValItem
&& mListIndex
< alist
->GetAnimValue().Length()) ||
287 (!mIsAnimValItem
&& mListIndex
< alist
->GetBaseValue().Length());
291 //----------------------------------------------------------------------
292 // Interface for SVGMatrix's use
294 void DOMSVGTransform::SetMatrix(const gfxMatrix
& aMatrix
) {
295 MOZ_ASSERT(!mIsAnimValItem
, "Attempting to modify read-only transform");
297 if (Transform().Type() == SVG_TRANSFORM_MATRIX
&&
298 SVGTransform::MatricesEqual(Matrixgfx(), aMatrix
)) {
302 AutoChangeTransformListNotifier
notifier(this);
303 Transform().SetMatrix(aMatrix
);
306 } // namespace mozilla::dom