SVGTransform.cpp 9.6 KB

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