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_DOMSVGLENGTHLIST_H_ 8 #define DOM_SVG_DOMSVGLENGTHLIST_H_ 9 10 #include "DOMSVGAnimatedLengthList.h" 11 #include "mozAutoDocUpdate.h" 12 #include "nsCycleCollectionParticipant.h" 13 #include "nsDebug.h" 14 #include "nsTArray.h" 15 #include "SVGLengthList.h" 16 #include "mozilla/Attributes.h" 17 #include "mozilla/Unused.h" 18 19 // {cbecb7a4-d6f3-47b5-b5a3-3e5bdbf5b2f9} 20 #define MOZILLA_DOMSVGLENGTHLIST_IID \ 21 { \ 22 0xcbecb7a4, 0xd6f3, 0xd6f3, { \ 23 0xb5, 0xa3, 0x3e, 0x5b, 0xdb, 0xf5, 0xb2, 0xf9 \ 24 } \ 25 } 26 27 namespace mozilla { 28 class ErrorResult; 29 30 namespace dom { 31 class DOMSVGLength; 32 class SVGElement; 33 34 //---------------------------------------------------------------------- 35 // Helper class: AutoChangeLengthListNotifier 36 // Stack-based helper class to pair calls to WillChangeLengthList and 37 // DidChangeLengthList. Used by DOMSVGLength and DOMSVGLengthList. 38 template <class T> 39 class MOZ_RAII AutoChangeLengthListNotifier : public mozAutoDocUpdate { 40 public: AutoChangeLengthListNotifier(T * aValue)41 explicit AutoChangeLengthListNotifier(T* aValue) 42 : mozAutoDocUpdate(aValue->Element()->GetComposedDoc(), true), 43 mValue(aValue) { 44 MOZ_ASSERT(aValue, "Expecting non-null value"); 45 mEmptyOrOldValue = 46 mValue->Element()->WillChangeLengthList(mValue->AttrEnum(), *this); 47 } 48 ~AutoChangeLengthListNotifier()49 ~AutoChangeLengthListNotifier() { 50 mValue->Element()->DidChangeLengthList(mValue->AttrEnum(), mEmptyOrOldValue, 51 *this); 52 if (mValue->IsAnimating()) { 53 mValue->Element()->AnimationNeedsResample(); 54 } 55 } 56 57 private: 58 T* const mValue; 59 nsAttrValue mEmptyOrOldValue; 60 }; 61 62 /** 63 * Class DOMSVGLengthList 64 * 65 * This class is used to create the DOM tearoff objects that wrap internal 66 * SVGLengthList objects. 67 * 68 * See the architecture comment in DOMSVGAnimatedLengthList.h. 69 * 70 * This class is strongly intertwined with DOMSVGAnimatedLengthList and 71 * DOMSVGLength. We are a friend of DOMSVGAnimatedLengthList, and are 72 * responsible for nulling out our DOMSVGAnimatedLengthList's pointer to us 73 * when we die, essentially making its pointer to us a weak pointer. Similarly, 74 * our DOMSVGLength items are friends of us and responsible for nulling out our 75 * pointers to them. 76 * 77 * Our DOM items are created lazily on demand as and when script requests them. 78 */ 79 class DOMSVGLengthList final : public nsISupports, public nsWrapperCache { 80 template <class T> 81 friend class AutoChangeLengthListNotifier; 82 friend class DOMSVGLength; 83 ~DOMSVGLengthList()84 ~DOMSVGLengthList() { 85 // Our mAList's weak ref to us must be nulled out when we die. If GC has 86 // unlinked us using the cycle collector code, then that has already 87 // happened, and mAList is null. 88 if (mAList) { 89 (IsAnimValList() ? mAList->mAnimVal : mAList->mBaseVal) = nullptr; 90 } 91 } 92 93 public: NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOMSVGLENGTHLIST_IID)94 NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOMSVGLENGTHLIST_IID) 95 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 96 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGLengthList) 97 98 DOMSVGLengthList(DOMSVGAnimatedLengthList* aAList, 99 const SVGLengthList& aInternalList) 100 : mAList(aAList) { 101 // aInternalList must be passed in explicitly because we can't use 102 // InternalList() here. (Because it depends on IsAnimValList, which depends 103 // on this object having been assigned to aAList's mBaseVal or mAnimVal, 104 // which hasn't happened yet.) 105 106 InternalListLengthWillChange(aInternalList.Length()); // Sync mItems 107 } 108 109 virtual JSObject* WrapObject(JSContext* cx, 110 JS::Handle<JSObject*> aGivenProto) override; 111 GetParentObject()112 nsISupports* GetParentObject() { return static_cast<nsIContent*>(Element()); } 113 114 /** 115 * This will normally be the same as InternalList().Length(), except if we've 116 * hit OOM in which case our length will be zero. 117 */ LengthNoFlush()118 uint32_t LengthNoFlush() const { 119 MOZ_ASSERT( 120 mItems.Length() == 0 || mItems.Length() == InternalList().Length(), 121 "DOM wrapper's list length is out of sync"); 122 return mItems.Length(); 123 } 124 125 /// Called to notify us to synchronize our length and detach excess items. 126 void InternalListLengthWillChange(uint32_t aNewLength); 127 128 /** 129 * Returns true if our attribute is animating (in which case our animVal is 130 * not simply a mirror of our baseVal). 131 */ IsAnimating()132 bool IsAnimating() const { return mAList->IsAnimating(); } 133 /** 134 * Returns true if there is an animated list mirroring the base list. 135 */ AnimListMirrorsBaseList()136 bool AnimListMirrorsBaseList() const { 137 return mAList->mAnimVal && !mAList->IsAnimating(); 138 } 139 NumberOfItems()140 uint32_t NumberOfItems() const { 141 if (IsAnimValList()) { 142 Element()->FlushAnimations(); 143 } 144 return LengthNoFlush(); 145 } 146 void Clear(ErrorResult& aError); 147 already_AddRefed<DOMSVGLength> Initialize(DOMSVGLength& newItem, 148 ErrorResult& error); 149 already_AddRefed<DOMSVGLength> GetItem(uint32_t index, ErrorResult& error); 150 already_AddRefed<DOMSVGLength> IndexedGetter(uint32_t index, bool& found, 151 ErrorResult& error); 152 already_AddRefed<DOMSVGLength> InsertItemBefore(DOMSVGLength& newItem, 153 uint32_t index, 154 ErrorResult& error); 155 already_AddRefed<DOMSVGLength> ReplaceItem(DOMSVGLength& newItem, 156 uint32_t index, 157 ErrorResult& error); 158 already_AddRefed<DOMSVGLength> RemoveItem(uint32_t index, ErrorResult& error); AppendItem(DOMSVGLength & newItem,ErrorResult & error)159 already_AddRefed<DOMSVGLength> AppendItem(DOMSVGLength& newItem, 160 ErrorResult& error) { 161 return InsertItemBefore(newItem, LengthNoFlush(), error); 162 } 163 void IndexedSetter(uint32_t index, DOMSVGLength& newValue, 164 ErrorResult& error); Length()165 uint32_t Length() const { return NumberOfItems(); } 166 167 private: Element()168 dom::SVGElement* Element() const { return mAList->mElement; } 169 AttrEnum()170 uint8_t AttrEnum() const { return mAList->mAttrEnum; } 171 Axis()172 uint8_t Axis() const { return mAList->mAxis; } 173 174 /// Used to determine if this list is the baseVal or animVal list. IsAnimValList()175 bool IsAnimValList() const { 176 MOZ_ASSERT(this == mAList->mBaseVal || this == mAList->mAnimVal, 177 "Calling IsAnimValList() too early?!"); 178 return this == mAList->mAnimVal; 179 } 180 181 /** 182 * Get a reference to this object's corresponding internal SVGLengthList. 183 * 184 * To simplify the code we just have this one method for obtaining both 185 * baseVal and animVal internal lists. This means that animVal lists don't 186 * get const protection, but our setter methods guard against changing 187 * animVal lists. 188 */ 189 SVGLengthList& InternalList() const; 190 191 /// Returns the DOMSVGLength at aIndex, creating it if necessary. 192 already_AddRefed<DOMSVGLength> GetItemAt(uint32_t aIndex); 193 194 void MaybeInsertNullInAnimValListAt(uint32_t aIndex); 195 void MaybeRemoveItemFromAnimValListAt(uint32_t aIndex); 196 197 // Weak refs to our DOMSVGLength items. The items are friends and take care 198 // of clearing our pointer to them when they die. 199 FallibleTArray<DOMSVGLength*> mItems; 200 201 RefPtr<DOMSVGAnimatedLengthList> mAList; 202 }; 203 204 NS_DEFINE_STATIC_IID_ACCESSOR(DOMSVGLengthList, MOZILLA_DOMSVGLENGTHLIST_IID) 205 206 } // namespace dom 207 } // namespace mozilla 208 209 #endif // DOM_SVG_DOMSVGLENGTHLIST_H_ 210