Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / svg / DOMSVGTransform.cpp
blob2ff8b994943c1ac7abb38e107d473ea0a2989216
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"
17 #include "nsError.h"
18 #include "SVGAnimatedTransformList.h"
19 #include "SVGAttrTearoffTable.h"
21 namespace {
22 const double kRadPerDegree = 2.0 * M_PI / 360.0;
23 } // namespace
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.
45 if (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 //----------------------------------------------------------------------
68 // Ctors:
70 DOMSVGTransform::DOMSVGTransform(DOMSVGTransformList* aList,
71 uint32_t aListIndex, bool aIsAnimValItem)
72 : mList(aList),
73 mListIndex(aListIndex),
74 mIsAnimValItem(aIsAnimValItem),
75 mTransform(nullptr) {
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()
83 : mList(nullptr),
84 mListIndex(0),
85 mIsAnimValItem(false),
86 mTransform(new SVGTransform()) // Default ctor for objects not in a
87 // list initialises to matrix type with
88 // identity matrix
91 DOMSVGTransform::DOMSVGTransform(const gfxMatrix& aMatrix)
92 : mList(nullptr),
93 mListIndex(0),
94 mIsAnimValItem(false),
95 mTransform(new SVGTransform(aMatrix)) {}
97 DOMSVGTransform::DOMSVGTransform(const DOMMatrix2DInit& aMatrix,
98 ErrorResult& rv)
99 : mList(nullptr),
100 mListIndex(0),
101 mIsAnimValItem(false),
102 mTransform(new SVGTransform()) {
103 SetMatrix(aMatrix, rv);
106 DOMSVGTransform::DOMSVGTransform(const SVGTransform& aTransform)
107 : mList(nullptr),
108 mListIndex(0),
109 mIsAnimValItem(false),
110 mTransform(new SVGTransform(aTransform)) {}
112 DOMSVGTransform::~DOMSVGTransform() {
113 SVGMatrix* matrix = SVGMatrixTearoffTable().GetTearoff(this);
114 if (matrix) {
115 SVGMatrixTearoffTable().RemoveTearoff(this);
116 NS_RELEASE(matrix);
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.
121 if (mList) {
122 mList->mItems[mListIndex] = nullptr;
126 uint16_t DOMSVGTransform::Type() const { return Transform().Type(); }
128 SVGMatrix* DOMSVGTransform::GetMatrix() {
129 SVGMatrix* wrapper = SVGMatrixTearoffTable().GetTearoff(this);
130 if (!wrapper) {
131 NS_ADDREF(wrapper = new SVGMatrix(*this));
132 SVGMatrixTearoffTable().AddTearoff(this, wrapper);
134 return wrapper;
137 float DOMSVGTransform::Angle() const { return Transform().Angle(); }
139 void DOMSVGTransform::SetMatrix(const DOMMatrix2DInit& aMatrix,
140 ErrorResult& aRv) {
141 if (mIsAnimValItem) {
142 aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
143 return;
145 RefPtr<DOMMatrixReadOnly> matrix =
146 DOMMatrixReadOnly::FromMatrix(GetParentObject(), aMatrix, aRv);
147 if (aRv.Failed()) {
148 return;
150 const gfxMatrix* matrix2D = matrix->GetInternal2D();
151 if (!matrix2D->IsFinite()) {
152 aRv.ThrowTypeError<MSG_NOT_FINITE>("Matrix setter");
153 return;
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);
161 return;
164 if (Transform().Type() == SVG_TRANSFORM_TRANSLATE && Matrixgfx()._31 == tx &&
165 Matrixgfx()._32 == ty) {
166 return;
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);
176 return;
179 if (Transform().Type() == SVG_TRANSFORM_SCALE && Matrixgfx()._11 == sx &&
180 Matrixgfx()._22 == sy) {
181 return;
183 AutoChangeTransformListNotifier notifier(this);
184 Transform().SetScale(sx, sy);
187 void DOMSVGTransform::SetRotate(float angle, float cx, float cy,
188 ErrorResult& rv) {
189 if (mIsAnimValItem) {
190 rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
191 return;
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) {
198 return;
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);
209 return;
212 if (Transform().Type() == SVG_TRANSFORM_SKEWX &&
213 Transform().Angle() == angle) {
214 return;
217 if (!std::isfinite(tan(angle * kRadPerDegree))) {
218 rv.ThrowRangeError<MSG_INVALID_TRANSFORM_ANGLE_ERROR>();
219 return;
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);
230 return;
233 if (Transform().Type() == SVG_TRANSFORM_SKEWY &&
234 Transform().Angle() == angle) {
235 return;
238 if (!std::isfinite(tan(angle * kRadPerDegree))) {
239 rv.ThrowRangeError<MSG_INVALID_TRANSFORM_ANGLE_ERROR>();
240 return;
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,
252 uint32_t aListIndex,
253 bool aIsAnimValItem) {
254 MOZ_ASSERT(!HasOwner(), "Inserting item that is already in a list");
256 mList = aList;
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());
269 mList = nullptr;
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();
283 #ifdef DEBUG
284 bool DOMSVGTransform::IndexIsValid() {
285 SVGAnimatedTransformList* alist = Element()->GetAnimatedTransformList();
286 return (mIsAnimValItem && mListIndex < alist->GetAnimValue().Length()) ||
287 (!mIsAnimValItem && mListIndex < alist->GetBaseValue().Length());
289 #endif // DEBUG
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)) {
299 return;
302 AutoChangeTransformListNotifier notifier(this);
303 Transform().SetMatrix(aMatrix);
306 } // namespace mozilla::dom