/src/mozilla-central/dom/svg/DOMSVGPathSegList.h
Line | Count | Source (jump to first uncovered line) |
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 MOZILLA_DOMSVGPATHSEGLIST_H__ |
8 | | #define MOZILLA_DOMSVGPATHSEGLIST_H__ |
9 | | |
10 | | #include "nsCOMPtr.h" |
11 | | #include "nsCycleCollectionParticipant.h" |
12 | | #include "nsDebug.h" |
13 | | #include "nsSVGElement.h" |
14 | | #include "nsTArray.h" |
15 | | #include "SVGPathData.h" // IWYU pragma: keep |
16 | | #include "mozilla/Attributes.h" |
17 | | #include "mozilla/ErrorResult.h" |
18 | | |
19 | | namespace mozilla { |
20 | | |
21 | | class DOMSVGPathSeg; |
22 | | class SVGAnimatedPathSegList; |
23 | | |
24 | | /** |
25 | | * Class DOMSVGPathSegList |
26 | | * |
27 | | * This class is used to create the DOM tearoff objects that wrap internal |
28 | | * SVGPathData objects. |
29 | | * |
30 | | * See the architecture comment in DOMSVGAnimatedLengthList.h first (that's |
31 | | * LENGTH list), then continue reading the remainder of this comment. |
32 | | * |
33 | | * The architecture of this class is very similar to that of DOMSVGLengthList |
34 | | * except that, since there is no nsIDOMSVGAnimatedPathSegList interface |
35 | | * in SVG, we have no parent DOMSVGAnimatedPathSegList (unlike DOMSVGLengthList |
36 | | * which has a parent DOMSVGAnimatedLengthList class). (There is an |
37 | | * SVGAnimatedPathData interface, but that is quite different to |
38 | | * DOMSVGAnimatedLengthList, since it is inherited by elements rather than |
39 | | * elements having members of that type.) As a consequence, much of the logic |
40 | | * that would otherwise be in DOMSVGAnimatedPathSegList (and is in |
41 | | * DOMSVGAnimatedLengthList) is contained in this class. |
42 | | * |
43 | | * This class is strongly intertwined with DOMSVGPathSeg. Our DOMSVGPathSeg |
44 | | * items are friends of us and responsible for nulling out our pointers to |
45 | | * them when they die. |
46 | | * |
47 | | * Our DOM items are created lazily on demand as and when script requests them. |
48 | | */ |
49 | | class DOMSVGPathSegList final : public nsISupports, |
50 | | public nsWrapperCache |
51 | | { |
52 | | friend class AutoChangePathSegListNotifier; |
53 | | friend class DOMSVGPathSeg; |
54 | | |
55 | | public: |
56 | | NS_DECL_CYCLE_COLLECTING_ISUPPORTS |
57 | | NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGPathSegList) |
58 | | |
59 | | virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; |
60 | | |
61 | | nsISupports* GetParentObject() |
62 | 0 | { |
63 | 0 | return static_cast<nsIContent*>(mElement); |
64 | 0 | } |
65 | | |
66 | | /** |
67 | | * Factory method to create and return a DOMSVGPathSegList wrapper |
68 | | * for a given internal SVGPathData object. The factory takes care |
69 | | * of caching the object that it returns so that the same object can be |
70 | | * returned for the given SVGPathData each time it is requested. |
71 | | * The cached object is only removed from the cache when it is destroyed due |
72 | | * to there being no more references to it or to any of its descendant |
73 | | * objects. If that happens, any subsequent call requesting the DOM wrapper |
74 | | * for the SVGPathData will naturally result in a new |
75 | | * DOMSVGPathSegList being returned. |
76 | | * |
77 | | * It's unfortunate that aList is a void* instead of a typed argument. This |
78 | | * is because the mBaseVal and mAnimVal members of SVGAnimatedPathSegList are |
79 | | * of different types - a plain SVGPathData, and a SVGPathData*. We |
80 | | * use the addresses of these members as the key for the hash table, and |
81 | | * clearly SVGPathData* and a SVGPathData** are not the same type. |
82 | | */ |
83 | | static already_AddRefed<DOMSVGPathSegList> |
84 | | GetDOMWrapper(void *aList, |
85 | | nsSVGElement *aElement, |
86 | | bool aIsAnimValList); |
87 | | |
88 | | /** |
89 | | * This method returns the DOMSVGPathSegList wrapper for an internal |
90 | | * SVGPathData object if it currently has a wrapper. If it does |
91 | | * not, then nullptr is returned. |
92 | | */ |
93 | | static DOMSVGPathSegList* |
94 | | GetDOMWrapperIfExists(void *aList); |
95 | | |
96 | | /** |
97 | | * This will normally be the same as InternalList().CountItems(), except if |
98 | | * we've hit OOM, in which case our length will be zero. |
99 | | */ |
100 | 0 | uint32_t LengthNoFlush() const { |
101 | 0 | MOZ_ASSERT(mItems.Length() == 0 || |
102 | 0 | mItems.Length() == InternalList().CountItems(), |
103 | 0 | "DOM wrapper's list length is out of sync"); |
104 | 0 | return mItems.Length(); |
105 | 0 | } |
106 | | |
107 | | /** |
108 | | * WATCH OUT! If you add code to call this on a baseVal wrapper, then you |
109 | | * must also call it on the animVal wrapper too if necessary!! See other |
110 | | * callers! |
111 | | * |
112 | | * Called by internal code to notify us when we need to sync the length of |
113 | | * this DOM list with its internal list. This is called immediately prior to |
114 | | * the length of the internal list being changed so that any DOM list items |
115 | | * that need to be removed from the DOM list can first copy their values from |
116 | | * their internal counterpart. |
117 | | * |
118 | | * The only time this method could fail is on OOM when trying to increase the |
119 | | * length of the DOM list. If that happens then this method simply clears the |
120 | | * list and returns. Callers just proceed as normal, and we simply accept |
121 | | * that the DOM list will be empty (until successfully set to a new value). |
122 | | */ |
123 | | void InternalListWillChangeTo(const SVGPathData& aNewValue); |
124 | | |
125 | | /** |
126 | | * Returns true if our attribute is animating (in which case our animVal is |
127 | | * not simply a mirror of our baseVal). |
128 | | */ |
129 | | bool AttrIsAnimating() const; |
130 | | /** |
131 | | * Returns true if there is an animated list mirroring the base list. |
132 | | */ |
133 | | bool AnimListMirrorsBaseList() const; |
134 | | |
135 | | uint32_t NumberOfItems() const |
136 | 0 | { |
137 | 0 | if (IsAnimValList()) { |
138 | 0 | Element()->FlushAnimations(); |
139 | 0 | } |
140 | 0 | return LengthNoFlush(); |
141 | 0 | } |
142 | | void Clear(ErrorResult& aError); |
143 | | already_AddRefed<DOMSVGPathSeg> Initialize(DOMSVGPathSeg& aNewItem, |
144 | | ErrorResult& aError); |
145 | | already_AddRefed<DOMSVGPathSeg> GetItem(uint32_t index, |
146 | | ErrorResult& error); |
147 | | already_AddRefed<DOMSVGPathSeg> IndexedGetter(uint32_t index, bool& found, |
148 | | ErrorResult& error); |
149 | | already_AddRefed<DOMSVGPathSeg> InsertItemBefore(DOMSVGPathSeg& aNewItem, |
150 | | uint32_t aIndex, |
151 | | ErrorResult& aError); |
152 | | already_AddRefed<DOMSVGPathSeg> ReplaceItem(DOMSVGPathSeg& aNewItem, |
153 | | uint32_t aIndex, |
154 | | ErrorResult& aError); |
155 | | already_AddRefed<DOMSVGPathSeg> RemoveItem(uint32_t aIndex, |
156 | | ErrorResult& aError); |
157 | | already_AddRefed<DOMSVGPathSeg> AppendItem(DOMSVGPathSeg& aNewItem, |
158 | | ErrorResult& aError) |
159 | 0 | { |
160 | 0 | return InsertItemBefore(aNewItem, LengthNoFlush(), aError); |
161 | 0 | } |
162 | | uint32_t Length() const |
163 | 0 | { |
164 | 0 | return NumberOfItems(); |
165 | 0 | } |
166 | | |
167 | | private: |
168 | | |
169 | | /** |
170 | | * Only our static GetDOMWrapper() factory method may create objects of our |
171 | | * type. |
172 | | */ |
173 | | DOMSVGPathSegList(nsSVGElement *aElement, bool aIsAnimValList) |
174 | | : mElement(aElement) |
175 | | , mIsAnimValList(aIsAnimValList) |
176 | 0 | { |
177 | 0 | InternalListWillChangeTo(InternalList()); // Sync mItems |
178 | 0 | } |
179 | | |
180 | | ~DOMSVGPathSegList(); |
181 | | |
182 | 0 | nsSVGElement* Element() const { |
183 | 0 | return mElement.get(); |
184 | 0 | } |
185 | | |
186 | | /// Used to determine if this list is the baseVal or animVal list. |
187 | 0 | bool IsAnimValList() const { |
188 | 0 | return mIsAnimValList; |
189 | 0 | } |
190 | | |
191 | | /** |
192 | | * Get a reference to this object's corresponding internal SVGPathData. |
193 | | * |
194 | | * To simplify the code we just have this one method for obtaining both |
195 | | * base val and anim val internal lists. This means that anim val lists don't |
196 | | * get const protection, but our setter methods guard against changing |
197 | | * anim val lists. |
198 | | */ |
199 | | SVGPathData& InternalList() const; |
200 | | |
201 | | SVGAnimatedPathSegList& InternalAList() const; |
202 | | |
203 | | /// Creates an instance of the appropriate DOMSVGPathSeg sub-class for |
204 | | // aIndex, if it doesn't already exist, and then returns it. |
205 | | already_AddRefed<DOMSVGPathSeg> GetItemAt(uint32_t aIndex); |
206 | | |
207 | | void MaybeInsertNullInAnimValListAt(uint32_t aIndex, |
208 | | uint32_t aInternalIndex, |
209 | | uint32_t aArgCountForItem); |
210 | | void MaybeRemoveItemFromAnimValListAt(uint32_t aIndex, |
211 | | int32_t aArgCountForItem); |
212 | | |
213 | | // Calls UpdateListIndex on all elements in |mItems| that satisfy ItemAt(), |
214 | | // from |aStartingIndex| to the end of |mItems|. Also adjusts |
215 | | // |mItems.mInternalDataIndex| by the requested amount. |
216 | | void UpdateListIndicesFromIndex(uint32_t aStartingIndex, |
217 | | int32_t aInternalDataIndexDelta); |
218 | | |
219 | 0 | DOMSVGPathSeg*& ItemAt(uint32_t aIndex) { |
220 | 0 | return mItems[aIndex].mItem; |
221 | 0 | } |
222 | | |
223 | | /** |
224 | | * This struct is used in our array of mItems to provide us with somewhere to |
225 | | * store the indexes into the internal SVGPathData of the internal seg data |
226 | | * that our DOMSVGPathSeg items wrap (the internal segment data is or varying |
227 | | * length, so we can't just use the index of our DOMSVGPathSeg items |
228 | | * themselves). The reason that we have this separate struct rather than |
229 | | * just storing the internal indexes in the DOMSVGPathSeg items is because we |
230 | | * want to create the DOMSVGPathSeg items lazily on demand. |
231 | | */ |
232 | | struct ItemProxy { |
233 | | ItemProxy() |
234 | | : mItem(nullptr) |
235 | | , mInternalDataIndex(0) |
236 | 0 | {} |
237 | | ItemProxy(DOMSVGPathSeg *aItem, uint32_t aInternalDataIndex) |
238 | | : mItem(aItem) |
239 | | , mInternalDataIndex(aInternalDataIndex) |
240 | 0 | {} |
241 | | |
242 | | DOMSVGPathSeg *mItem; |
243 | | uint32_t mInternalDataIndex; |
244 | | }; |
245 | | |
246 | | // Weak refs to our DOMSVGPathSeg items. The items are friends and take care |
247 | | // of clearing our pointer to them when they die. |
248 | | FallibleTArray<ItemProxy> mItems; |
249 | | |
250 | | // Strong ref to our element to keep it alive. We hold this not only for |
251 | | // ourself, but also for our DOMSVGPathSeg items too. |
252 | | RefPtr<nsSVGElement> mElement; |
253 | | |
254 | | bool mIsAnimValList; |
255 | | }; |
256 | | |
257 | | } // namespace mozilla |
258 | | |
259 | | #endif // MOZILLA_DOMSVGPATHSEGLIST_H__ |