DOMSVGPoint.cpp (6879B)
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 "DOMSVGPoint.h" 8 9 #include "DOMSVGPointList.h" 10 #include "gfx2DGlue.h" 11 #include "mozilla/dom/DOMMatrix.h" 12 #include "mozilla/dom/SVGPointBinding.h" 13 #include "nsError.h" 14 15 // See the architecture comment in DOMSVGPointList.h. 16 17 using namespace mozilla::gfx; 18 19 namespace mozilla::dom { 20 21 //---------------------------------------------------------------------- 22 // Helper class: AutoChangePointNotifier 23 // 24 class MOZ_RAII AutoChangePointNotifier { 25 public: 26 explicit AutoChangePointNotifier(DOMSVGPoint* aValue) : mValue(aValue) { 27 MOZ_ASSERT(mValue, "Expecting non-null value"); 28 } 29 30 ~AutoChangePointNotifier() { 31 if (mValue->IsTranslatePoint()) { 32 mValue->DidChangeTranslate(); 33 } 34 } 35 36 private: 37 DOMSVGPoint* const mValue; 38 }; 39 40 constinit static SVGAttrTearoffTable<SVGPoint, DOMSVGPoint> 41 sSVGTranslateTearOffTable; 42 43 // We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to 44 // clear our list's weak ref to us to be safe. (The other option would be to 45 // not unlink and rely on the breaking of the other edges in the cycle, as 46 // NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.) 47 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGPoint) 48 49 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGPoint) 50 tmp->CleanupWeakRefs(); 51 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner) 52 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 53 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 54 55 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGPoint) 56 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) 57 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 58 59 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGPoint) 60 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER 61 NS_IMPL_CYCLE_COLLECTION_TRACE_END 62 63 JSObject* DOMSVGPoint::WrapObject(JSContext* aCx, 64 JS::Handle<JSObject*> aGivenProto) { 65 return SVGPoint_Binding::Wrap(aCx, this, aGivenProto); 66 } 67 68 float DOMSVGPoint::X() { 69 if (mIsAnimValItem && IsInList()) { 70 Element()->FlushAnimations(); // May make IsInList() == false 71 } 72 return InternalItem().mX; 73 } 74 75 void DOMSVGPoint::SetX(float aX, ErrorResult& aRv) { 76 if (mIsAnimValItem) { 77 aRv.ThrowNoModificationAllowedError("Animated values cannot be set"); 78 return; 79 } 80 81 auto& val = InternalItem(); 82 83 if (val.mX == aX) { 84 return; 85 } 86 87 AutoChangePointListNotifier listNotifier(this); 88 AutoChangePointNotifier translateNotifier(this); 89 90 val.mX = aX; 91 } 92 93 float DOMSVGPoint::Y() { 94 if (mIsAnimValItem && IsInList()) { 95 Element()->FlushAnimations(); // May make IsInList() == false 96 } 97 return InternalItem().mY; 98 } 99 100 void DOMSVGPoint::SetY(float aY, ErrorResult& aRv) { 101 if (mIsAnimValItem) { 102 aRv.ThrowNoModificationAllowedError("Animated values cannot be set"); 103 return; 104 } 105 auto& val = InternalItem(); 106 107 if (val.mY == aY) { 108 return; 109 } 110 111 AutoChangePointListNotifier listNotifier(this); 112 AutoChangePointNotifier translateNotifier(this); 113 114 val.mY = aY; 115 } 116 117 already_AddRefed<DOMSVGPoint> DOMSVGPoint::MatrixTransform( 118 const DOMMatrix2DInit& aMatrix, ErrorResult& aRv) { 119 auto matrix2D = DOMMatrixReadOnly::ToValidatedMatrixDouble(aMatrix, aRv); 120 if (aRv.Failed()) { 121 return nullptr; 122 } 123 if (!matrix2D.IsFinite()) { 124 aRv.ThrowTypeError<MSG_NOT_FINITE>("MatrixTransform matrix"); 125 return nullptr; 126 } 127 auto pt = matrix2D.TransformPoint(InternalItem()); 128 return do_AddRef(new DOMSVGPoint(ToPoint(pt))); 129 } 130 131 void DOMSVGPoint::InsertingIntoList(DOMSVGPointList* aList, uint32_t aListIndex, 132 bool aIsAnimValItem) { 133 MOZ_RELEASE_ASSERT(!IsInList(), "Inserting item that is already in a list"); 134 MOZ_RELEASE_ASSERT(!mIsTranslatePoint, 135 "Inserting item that is a currentTranslate"); 136 137 delete mVal; 138 mVal = nullptr; 139 140 mOwner = aList; 141 mListIndex = aListIndex; 142 mIsAnimValItem = aIsAnimValItem; 143 144 MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGPoint!"); 145 } 146 147 void DOMSVGPoint::RemovingFromList() { 148 MOZ_ASSERT( 149 IsInList(), 150 "We should start in a list if we're going to be removed from one."); 151 mVal = new SVGPoint(InternalItem()); 152 mOwner = nullptr; 153 mIsAnimValItem = false; 154 } 155 156 SVGPoint& DOMSVGPoint::InternalItem() { 157 if (nsCOMPtr<DOMSVGPointList> pointList = do_QueryInterface(mOwner)) { 158 return pointList->InternalList().mItems[mListIndex]; 159 } 160 return *mVal; 161 } 162 163 already_AddRefed<DOMSVGPoint> DOMSVGPoint::GetTranslateTearOff( 164 SVGPoint* aVal, SVGSVGElement* aSVGSVGElement) { 165 RefPtr<DOMSVGPoint> domPoint = sSVGTranslateTearOffTable.GetTearoff(aVal); 166 if (!domPoint) { 167 domPoint = new DOMSVGPoint(aVal, aSVGSVGElement); 168 sSVGTranslateTearOffTable.AddTearoff(aVal, domPoint); 169 domPoint->mIsInTearoffTable = true; 170 } 171 172 return domPoint.forget(); 173 } 174 175 bool DOMSVGPoint::AttrIsAnimating() const { 176 nsCOMPtr<DOMSVGPointList> pointList = do_QueryInterface(mOwner); 177 return pointList && pointList->AttrIsAnimating(); 178 } 179 180 void DOMSVGPoint::DidChangeTranslate() { 181 nsCOMPtr<SVGSVGElement> svg = do_QueryInterface(mOwner); 182 MOZ_ASSERT(svg); 183 nsContentUtils::AddScriptRunner( 184 NewRunnableMethod("dom::SVGSVGElement::DidChangeTranslate", svg, 185 &SVGSVGElement::DidChangeTranslate)); 186 } 187 188 SVGElement* DOMSVGPoint::Element() { 189 if (nsCOMPtr<DOMSVGPointList> pointList = do_QueryInterface(mOwner)) { 190 return pointList->Element(); 191 } 192 nsCOMPtr<SVGSVGElement> svg = do_QueryInterface(mOwner); 193 return svg; 194 } 195 196 void DOMSVGPoint::CleanupWeakRefs() { 197 // Our mList's weak ref to us must be nulled out when we die (or when we're 198 // cycle collected), so we that don't leave behind a pointer to 199 // free / soon-to-be-free memory. 200 if (nsCOMPtr<DOMSVGPointList> pointList = do_QueryInterface(mOwner)) { 201 MOZ_ASSERT(pointList->mItems[mListIndex] == this, 202 "Clearing out the wrong list index...?"); 203 pointList->mItems[mListIndex] = nullptr; 204 } 205 206 if (mIsInTearoffTable) { 207 // Similarly, we must update the tearoff table to remove its (non-owning) 208 // pointer to mVal. 209 MOZ_ASSERT(mVal && mIsTranslatePoint, 210 "Tearoff table should only be used for translate-point objects " 211 "with non-null mVal (see GetTranslateTearOff and its callers)"); 212 sSVGTranslateTearOffTable.RemoveTearoff(mVal); 213 mIsInTearoffTable = false; 214 } 215 216 if (mVal) { 217 if (!mIsTranslatePoint) { 218 // In this case we own mVal 219 delete mVal; 220 } 221 mVal = nullptr; 222 } 223 } 224 225 #ifdef DEBUG 226 bool DOMSVGPoint::IndexIsValid() { 227 nsCOMPtr<DOMSVGPointList> pointList = do_QueryInterface(mOwner); 228 return mListIndex < pointList->InternalList().Length(); 229 } 230 #endif 231 232 } // namespace mozilla::dom