Line data Source code
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/. */
6 :
7 : #include "SVGTransform.h"
8 :
9 : #include "mozilla/dom/SVGTransform.h"
10 : #include "mozilla/dom/SVGMatrix.h"
11 : #include "mozilla/dom/SVGTransformBinding.h"
12 : #include "nsError.h"
13 : #include "nsSVGAnimatedTransformList.h"
14 : #include "nsSVGAttrTearoffTable.h"
15 : #include "mozilla/DebugOnly.h"
16 : #include "mozilla/FloatingPoint.h"
17 :
18 : namespace {
19 : const double kRadPerDegree = 2.0 * M_PI / 360.0;
20 : } // namespace
21 :
22 : namespace mozilla {
23 : namespace dom {
24 :
25 : static nsSVGAttrTearoffTable<SVGTransform, SVGMatrix>&
26 0 : SVGMatrixTearoffTable()
27 : {
28 0 : static nsSVGAttrTearoffTable<SVGTransform, SVGMatrix> sSVGMatrixTearoffTable;
29 0 : return sSVGMatrixTearoffTable;
30 : }
31 :
32 : //----------------------------------------------------------------------
33 :
34 : // We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to
35 : // clear our list's weak ref to us to be safe. (The other option would be to
36 : // not unlink and rely on the breaking of the other edges in the cycle, as
37 : // NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
38 : NS_IMPL_CYCLE_COLLECTION_CLASS(SVGTransform)
39 :
40 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SVGTransform)
41 : // We may not belong to a list, so we must null check tmp->mList.
42 0 : if (tmp->mList) {
43 0 : tmp->mList->mItems[tmp->mListIndex] = nullptr;
44 : }
45 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mList)
46 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
47 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
48 :
49 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SVGTransform)
50 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mList)
51 : SVGMatrix* matrix =
52 0 : SVGMatrixTearoffTable().GetTearoff(tmp);
53 0 : CycleCollectionNoteChild(cb, matrix, "matrix");
54 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
55 :
56 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(SVGTransform)
57 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
58 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
59 :
60 0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SVGTransform, AddRef)
61 0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SVGTransform, Release)
62 :
63 : JSObject*
64 0 : SVGTransform::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
65 : {
66 0 : return SVGTransformBinding::Wrap(aCx, this, aGivenProto);
67 : }
68 :
69 : //----------------------------------------------------------------------
70 : // Helper class: AutoChangeTransformNotifier
71 : // Stack-based helper class to pair calls to WillChangeTransformList
72 : // and DidChangeTransformList.
73 : class MOZ_RAII AutoChangeTransformNotifier
74 : {
75 : public:
76 0 : explicit AutoChangeTransformNotifier(SVGTransform* aTransform MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
77 0 : : mTransform(aTransform)
78 : {
79 0 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
80 0 : MOZ_ASSERT(mTransform, "Expecting non-null transform");
81 0 : if (mTransform->HasOwner()) {
82 : mEmptyOrOldValue =
83 0 : mTransform->Element()->WillChangeTransformList();
84 : }
85 0 : }
86 :
87 0 : ~AutoChangeTransformNotifier()
88 0 : {
89 0 : if (mTransform->HasOwner()) {
90 0 : mTransform->Element()->DidChangeTransformList(mEmptyOrOldValue);
91 0 : if (mTransform->mList->IsAnimating()) {
92 0 : mTransform->Element()->AnimationNeedsResample();
93 : }
94 : }
95 0 : }
96 :
97 : private:
98 : SVGTransform* const mTransform;
99 : nsAttrValue mEmptyOrOldValue;
100 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
101 : };
102 :
103 : //----------------------------------------------------------------------
104 : // Ctors:
105 :
106 0 : SVGTransform::SVGTransform(DOMSVGTransformList *aList,
107 : uint32_t aListIndex,
108 0 : bool aIsAnimValItem)
109 : : mList(aList)
110 : , mListIndex(aListIndex)
111 : , mIsAnimValItem(aIsAnimValItem)
112 0 : , mTransform(nullptr)
113 : {
114 : // These shifts are in sync with the members in the header.
115 0 : MOZ_ASSERT(aList && aListIndex <= MaxListIndex(), "bad arg");
116 :
117 0 : MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGNumber!");
118 0 : }
119 :
120 0 : SVGTransform::SVGTransform()
121 : : mList(nullptr)
122 : , mListIndex(0)
123 : , mIsAnimValItem(false)
124 0 : , mTransform(new nsSVGTransform()) // Default ctor for objects not in a list
125 : // initialises to matrix type with identity
126 : // matrix
127 : {
128 0 : }
129 :
130 0 : SVGTransform::SVGTransform(const gfxMatrix &aMatrix)
131 : : mList(nullptr)
132 : , mListIndex(0)
133 : , mIsAnimValItem(false)
134 0 : , mTransform(new nsSVGTransform(aMatrix))
135 : {
136 0 : }
137 :
138 0 : SVGTransform::SVGTransform(const nsSVGTransform &aTransform)
139 : : mList(nullptr)
140 : , mListIndex(0)
141 : , mIsAnimValItem(false)
142 0 : , mTransform(new nsSVGTransform(aTransform))
143 : {
144 0 : }
145 :
146 0 : SVGTransform::~SVGTransform()
147 : {
148 0 : SVGMatrix* matrix = SVGMatrixTearoffTable().GetTearoff(this);
149 0 : if (matrix) {
150 0 : SVGMatrixTearoffTable().RemoveTearoff(this);
151 0 : NS_RELEASE(matrix);
152 : }
153 : // Our mList's weak ref to us must be nulled out when we die. If GC has
154 : // unlinked us using the cycle collector code, then that has already
155 : // happened, and mList is null.
156 0 : if (mList) {
157 0 : mList->mItems[mListIndex] = nullptr;
158 : }
159 0 : }
160 :
161 : uint16_t
162 0 : SVGTransform::Type() const
163 : {
164 0 : return Transform().Type();
165 : }
166 :
167 : SVGMatrix*
168 0 : SVGTransform::GetMatrix()
169 : {
170 : SVGMatrix* wrapper =
171 0 : SVGMatrixTearoffTable().GetTearoff(this);
172 0 : if (!wrapper) {
173 0 : NS_ADDREF(wrapper = new SVGMatrix(*this));
174 0 : SVGMatrixTearoffTable().AddTearoff(this, wrapper);
175 : }
176 0 : return wrapper;
177 : }
178 :
179 : float
180 0 : SVGTransform::Angle() const
181 : {
182 0 : return Transform().Angle();
183 : }
184 :
185 : void
186 0 : SVGTransform::SetMatrix(SVGMatrix& aMatrix, ErrorResult& rv)
187 : {
188 0 : if (mIsAnimValItem) {
189 0 : rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
190 0 : return;
191 : }
192 0 : SetMatrix(aMatrix.GetMatrix());
193 : }
194 :
195 : void
196 0 : SVGTransform::SetTranslate(float tx, float ty, ErrorResult& rv)
197 : {
198 0 : if (mIsAnimValItem) {
199 0 : rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
200 0 : return;
201 : }
202 :
203 0 : if (Transform().Type() == SVG_TRANSFORM_TRANSLATE &&
204 0 : Matrixgfx()._31 == tx && Matrixgfx()._32 == ty) {
205 0 : return;
206 : }
207 :
208 0 : AutoChangeTransformNotifier notifier(this);
209 0 : Transform().SetTranslate(tx, ty);
210 : }
211 :
212 : void
213 0 : SVGTransform::SetScale(float sx, float sy, ErrorResult& rv)
214 : {
215 0 : if (mIsAnimValItem) {
216 0 : rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
217 0 : return;
218 : }
219 :
220 0 : if (Transform().Type() == SVG_TRANSFORM_SCALE &&
221 0 : Matrixgfx()._11 == sx && Matrixgfx()._22 == sy) {
222 0 : return;
223 : }
224 0 : AutoChangeTransformNotifier notifier(this);
225 0 : Transform().SetScale(sx, sy);
226 : }
227 :
228 : void
229 0 : SVGTransform::SetRotate(float angle, float cx, float cy, ErrorResult& rv)
230 : {
231 0 : if (mIsAnimValItem) {
232 0 : rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
233 0 : return;
234 : }
235 :
236 0 : if (Transform().Type() == SVG_TRANSFORM_ROTATE) {
237 : float currentCx, currentCy;
238 0 : Transform().GetRotationOrigin(currentCx, currentCy);
239 0 : if (Transform().Angle() == angle && currentCx == cx && currentCy == cy) {
240 0 : return;
241 : }
242 : }
243 :
244 0 : AutoChangeTransformNotifier notifier(this);
245 0 : Transform().SetRotate(angle, cx, cy);
246 : }
247 :
248 : void
249 0 : SVGTransform::SetSkewX(float angle, ErrorResult& rv)
250 : {
251 0 : if (mIsAnimValItem) {
252 0 : rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
253 0 : return;
254 : }
255 :
256 0 : if (Transform().Type() == SVG_TRANSFORM_SKEWX &&
257 0 : Transform().Angle() == angle) {
258 0 : return;
259 : }
260 :
261 0 : if (!IsFinite(tan(angle * kRadPerDegree))) {
262 0 : rv.ThrowRangeError<MSG_INVALID_TRANSFORM_ANGLE_ERROR>();
263 0 : return;
264 : }
265 :
266 0 : AutoChangeTransformNotifier notifier(this);
267 0 : DebugOnly<nsresult> result = Transform().SetSkewX(angle);
268 0 : MOZ_ASSERT(NS_SUCCEEDED(result), "SetSkewX unexpectedly failed");
269 : }
270 :
271 : void
272 0 : SVGTransform::SetSkewY(float angle, ErrorResult& rv)
273 : {
274 0 : if (mIsAnimValItem) {
275 0 : rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
276 0 : return;
277 : }
278 :
279 0 : if (Transform().Type() == SVG_TRANSFORM_SKEWY &&
280 0 : Transform().Angle() == angle) {
281 0 : return;
282 : }
283 :
284 0 : if (!IsFinite(tan(angle * kRadPerDegree))) {
285 0 : rv.ThrowRangeError<MSG_INVALID_TRANSFORM_ANGLE_ERROR>();
286 0 : return;
287 : }
288 :
289 0 : AutoChangeTransformNotifier notifier(this);
290 0 : DebugOnly<nsresult> result = Transform().SetSkewY(angle);
291 0 : MOZ_ASSERT(NS_SUCCEEDED(result), "SetSkewY unexpectedly failed");
292 : }
293 :
294 : //----------------------------------------------------------------------
295 : // List management methods:
296 :
297 : void
298 0 : SVGTransform::InsertingIntoList(DOMSVGTransformList *aList,
299 : uint32_t aListIndex,
300 : bool aIsAnimValItem)
301 : {
302 0 : MOZ_ASSERT(!HasOwner(), "Inserting item that is already in a list");
303 :
304 0 : mList = aList;
305 0 : mListIndex = aListIndex;
306 0 : mIsAnimValItem = aIsAnimValItem;
307 0 : mTransform = nullptr;
308 :
309 0 : MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGLength!");
310 0 : }
311 :
312 : void
313 0 : SVGTransform::RemovingFromList()
314 : {
315 0 : MOZ_ASSERT(!mTransform,
316 : "Item in list also has another non-list value associated with it");
317 :
318 0 : mTransform = new nsSVGTransform(InternalItem());
319 0 : mList = nullptr;
320 0 : mIsAnimValItem = false;
321 0 : }
322 :
323 : nsSVGTransform&
324 0 : SVGTransform::InternalItem()
325 : {
326 0 : nsSVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList();
327 0 : return mIsAnimValItem && alist->mAnimVal ?
328 0 : (*alist->mAnimVal)[mListIndex] :
329 0 : alist->mBaseVal[mListIndex];
330 : }
331 :
332 : const nsSVGTransform&
333 0 : SVGTransform::InternalItem() const
334 : {
335 0 : return const_cast<SVGTransform*>(this)->InternalItem();
336 : }
337 :
338 : #ifdef DEBUG
339 : bool
340 0 : SVGTransform::IndexIsValid()
341 : {
342 0 : nsSVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList();
343 0 : return (mIsAnimValItem &&
344 0 : mListIndex < alist->GetAnimValue().Length()) ||
345 0 : (!mIsAnimValItem &&
346 0 : mListIndex < alist->GetBaseValue().Length());
347 : }
348 : #endif // DEBUG
349 :
350 :
351 : //----------------------------------------------------------------------
352 : // Interface for SVGMatrix's use
353 :
354 : void
355 0 : SVGTransform::SetMatrix(const gfxMatrix& aMatrix)
356 : {
357 0 : MOZ_ASSERT(!mIsAnimValItem,
358 : "Attempting to modify read-only transform");
359 :
360 0 : if (Transform().Type() == SVG_TRANSFORM_MATRIX &&
361 0 : nsSVGTransform::MatricesEqual(Matrixgfx(), aMatrix)) {
362 0 : return;
363 : }
364 :
365 0 : AutoChangeTransformNotifier notifier(this);
366 0 : Transform().SetMatrix(aMatrix);
367 : }
368 :
369 : } // namespace dom
370 : } // namespace mozilla
|