DOMSVGPointList.h (9836B)
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 #ifndef DOM_SVG_DOMSVGPOINTLIST_H_ 8 #define DOM_SVG_DOMSVGPOINTLIST_H_ 9 10 #include "SVGPointList.h" // IWYU pragma: keep 11 #include "mozAutoDocUpdate.h" 12 #include "mozilla/Attributes.h" 13 #include "mozilla/RefPtr.h" 14 #include "nsCycleCollectionParticipant.h" 15 #include "nsDebug.h" 16 #include "nsTArray.h" 17 18 // {61812ad1-c078-4cd1-87e6-bc1c1b8d7284} 19 #define MOZILLA_DOMSVGPOINTLIST_IID \ 20 {0x61812ad1, 0xc078, 0x4cd1, {0x87, 0xe6, 0xbc, 0x1c, 0x1b, 0x8d, 0x72, 0x84}} 21 22 namespace mozilla { 23 24 class ErrorResult; 25 class SVGAnimatedPointList; 26 27 namespace dom { 28 29 class DOMSVGPoint; 30 class SVGElement; 31 class SVGPolyElement; 32 33 //---------------------------------------------------------------------- 34 // Helper class: AutoChangePointListNotifier 35 // Stack-based helper class to pair calls to WillChangePointList and 36 // DidChangePointList. Used by DOMSVGPoint and DOMSVGPointList. 37 template <class T> 38 class MOZ_RAII AutoChangePointListNotifier { 39 public: 40 explicit AutoChangePointListNotifier(T* aValue) : mValue(aValue) { 41 MOZ_ASSERT(mValue, "Expecting non-null value"); 42 if (mValue->IsInList()) { 43 mUpdateBatch.emplace(mValue->Element()->GetComposedDoc(), true); 44 mValue->Element()->WillChangePointList(mUpdateBatch.ref()); 45 } 46 } 47 48 ~AutoChangePointListNotifier() { 49 if (mValue->IsInList()) { 50 mValue->Element()->DidChangePointList(mUpdateBatch.ref()); 51 if (mValue->AttrIsAnimating()) { 52 mValue->Element()->AnimationNeedsResample(); 53 } 54 } 55 } 56 57 private: 58 Maybe<mozAutoDocUpdate> mUpdateBatch; 59 T* const mValue; 60 }; 61 62 /** 63 * Class DOMSVGPointList 64 * 65 * This class is used to create the DOM tearoff objects that wrap internal 66 * SVGPointList objects. 67 * 68 * See the architecture comment in DOMSVGAnimatedLengthList.h first (that's 69 * LENGTH list), then continue reading the remainder of this comment. 70 * 71 * The architecture of this class is very similar to that of DOMSVGLengthList 72 * except that, since there is no nsIDOMSVGAnimatedPointList interface 73 * in SVG, we have no parent DOMSVGAnimatedPointList (unlike DOMSVGLengthList 74 * which has a parent DOMSVGAnimatedLengthList class). (There is an 75 * SVGAnimatedPoints interface, but that is quite different to 76 * DOMSVGAnimatedLengthList, since it is inherited by elements rather than 77 * elements having members of that type.) As a consequence, much of the logic 78 * that would otherwise be in DOMSVGAnimatedPointList (and is in 79 * DOMSVGAnimatedLengthList) is contained in this class. 80 * 81 * This class is strongly intertwined with DOMSVGPoint. Our DOMSVGPoint 82 * items are friends of us and responsible for nulling out our pointers to 83 * them when they die. 84 * 85 * Our DOM items are created lazily on demand as and when script requests them. 86 */ 87 class DOMSVGPointList final : public nsISupports, public nsWrapperCache { 88 template <class T> 89 friend class AutoChangePointListNotifier; 90 friend class DOMSVGPoint; 91 92 public: 93 NS_INLINE_DECL_STATIC_IID(MOZILLA_DOMSVGPOINTLIST_IID) 94 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 95 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGPointList) 96 97 JSObject* WrapObject(JSContext* cx, 98 JS::Handle<JSObject*> aGivenProto) override; 99 100 nsISupports* GetParentObject() { return static_cast<nsIContent*>(mElement); } 101 102 /** 103 * Factory method to create and return a DOMSVGPointList wrapper 104 * for a given internal SVGPointList object. The factory takes care 105 * of caching the object that it returns so that the same object can be 106 * returned for the given SVGPointList each time it is requested. 107 * The cached object is only removed from the cache when it is destroyed due 108 * to there being no more references to it or to any of its descendant 109 * objects. If that happens, any subsequent call requesting the DOM wrapper 110 * for the SVGPointList will naturally result in a new 111 * DOMSVGPointList being returned. 112 * 113 * It's unfortunate that aList is a void* instead of a typed argument. This 114 * is because the mBaseVal and mAnimVal members of SVGAnimatedPointList are 115 * of different types - a plain SVGPointList, and a SVGPointList*. We 116 * use the addresses of these members as the key for the hash table, and 117 * clearly SVGPointList* and a SVGPointList** are not the same type. 118 */ 119 static already_AddRefed<DOMSVGPointList> GetDOMWrapper( 120 void* aList, dom::SVGPolyElement* aElement); 121 122 /** 123 * This method returns the DOMSVGPointList wrapper for an internal 124 * SVGPointList object if it currently has a wrapper. If it does 125 * not, then nullptr is returned. 126 */ 127 static DOMSVGPointList* GetDOMWrapperIfExists(void* aList); 128 129 /** 130 * This will normally be the same as InternalList().Length(), except if 131 * we've hit OOM, in which case our length will be zero. 132 */ 133 uint32_t LengthNoFlush() const { 134 MOZ_ASSERT( 135 mItems.Length() == 0 || mItems.Length() == InternalList().Length(), 136 "DOM wrapper's list length is out of sync"); 137 return mItems.Length(); 138 } 139 140 /** 141 * WATCH OUT! If you add code to call this on a baseVal wrapper, then you 142 * must also call it on the animVal wrapper too if necessary!! See other 143 * callers! 144 * 145 * Called by internal code to notify us when we need to sync the length of 146 * this DOM list with its internal list. This is called immediately prior to 147 * the length of the internal list being changed so that any DOM list items 148 * that need to be removed from the DOM list can first copy their values from 149 * their internal counterpart. 150 * 151 * The only time this method could fail is on OOM when trying to increase the 152 * length of the DOM list. If that happens then this method simply clears the 153 * list and returns. Callers just proceed as normal, and we simply accept 154 * that the DOM list will be empty (until successfully set to a new value). 155 */ 156 void InternalListWillChangeTo(const SVGPointList& aNewValue); 157 158 /* 159 * We need this so that templates that work on lists and elements can check 160 * ownership where elements may be not be in a list. 161 */ 162 bool IsInList() const { return true; } 163 164 /** 165 * Returns true if our attribute is animating (in which case our animVal is 166 * not simply a mirror of our baseVal). 167 */ 168 bool AttrIsAnimating() const; 169 170 /** 171 * Returns true if there is an animated list mirroring the base list. 172 */ 173 bool AnimListMirrorsBaseList() const; 174 175 uint32_t NumberOfItems() const { 176 if (IsAnimValList()) { 177 Element()->FlushAnimations(); 178 } 179 return LengthNoFlush(); 180 } 181 void Clear(ErrorResult& aRv); 182 already_AddRefed<DOMSVGPoint> Initialize(DOMSVGPoint& aNewItem, 183 ErrorResult& aRv); 184 already_AddRefed<DOMSVGPoint> GetItem(uint32_t index, ErrorResult& error); 185 already_AddRefed<DOMSVGPoint> IndexedGetter(uint32_t index, bool& found, 186 ErrorResult& error); 187 already_AddRefed<DOMSVGPoint> InsertItemBefore(DOMSVGPoint& aNewItem, 188 uint32_t aIndex, 189 ErrorResult& aRv); 190 already_AddRefed<DOMSVGPoint> ReplaceItem(DOMSVGPoint& aNewItem, 191 uint32_t aIndex, ErrorResult& aRv); 192 already_AddRefed<DOMSVGPoint> RemoveItem(uint32_t aIndex, ErrorResult& aRv); 193 already_AddRefed<DOMSVGPoint> AppendItem(DOMSVGPoint& aNewItem, 194 ErrorResult& aRv) { 195 return InsertItemBefore(aNewItem, LengthNoFlush(), aRv); 196 } 197 uint32_t Length() const { return NumberOfItems(); } 198 199 private: 200 /** 201 * Only our static GetDOMWrapper() factory method may create objects of our 202 * type. 203 */ 204 DOMSVGPointList(dom::SVGElement* aElement, bool aIsAnimValList) 205 : mElement(aElement), mIsAnimValList(aIsAnimValList) { 206 InternalListWillChangeTo(InternalList()); // Sync mItems 207 } 208 209 ~DOMSVGPointList(); 210 211 dom::SVGElement* Element() const { return mElement.get(); } 212 213 /// Used to determine if this list is the baseVal or animVal list. 214 bool IsAnimValList() const { return mIsAnimValList; } 215 216 /** 217 * Get a reference to this object's corresponding internal SVGPointList. 218 * 219 * To simplify the code we just have this one method for obtaining both 220 * base val and anim val internal lists. This means that anim val lists don't 221 * get const protection, but our setter methods guard against changing 222 * anim val lists. 223 */ 224 SVGPointList& InternalList() const; 225 226 SVGAnimatedPointList& InternalAList() const; 227 228 /// Returns the DOMSVGPoint at aIndex, creating it if necessary. 229 already_AddRefed<DOMSVGPoint> GetItemAt(uint32_t aIndex); 230 231 void MaybeInsertNullInAnimValListAt(uint32_t aIndex); 232 void MaybeRemoveItemFromAnimValListAt(uint32_t aIndex); 233 234 void RemoveFromTearoffTable(); 235 236 // Weak refs to our DOMSVGPoint items. The items are friends and take care 237 // of clearing our pointer to them when they die. 238 FallibleTArray<DOMSVGPoint*> mItems; 239 240 // Strong ref to our element to keep it alive. We hold this not only for 241 // ourself, but also for our DOMSVGPoint items too. 242 RefPtr<dom::SVGElement> mElement; 243 244 bool mIsAnimValList; 245 246 // Tracks whether we're in the tearoff table. Initialized to true, since all 247 // new instances are added to the table right after construction. Updated to 248 // false when we're removed from the table (at which point we're being 249 // destructed or soon-to-be destructed). 250 bool mIsInTearoffTable = true; 251 }; 252 253 } // namespace dom 254 } // namespace mozilla 255 256 #endif // DOM_SVG_DOMSVGPOINTLIST_H_