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