Line data Source code
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 0 : NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGPathSegList)
58 :
59 : virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
60 :
61 0 : nsISupports* GetParentObject()
62 : {
63 0 : return static_cast<nsIContent*>(mElement);
64 : }
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 : mItems.Length() == InternalList().CountItems(),
103 : "DOM wrapper's list length is out of sync");
104 0 : return mItems.Length();
105 : }
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 0 : uint32_t NumberOfItems() const
136 : {
137 0 : if (IsAnimValList()) {
138 0 : Element()->FlushAnimations();
139 : }
140 0 : return LengthNoFlush();
141 : }
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 0 : already_AddRefed<DOMSVGPathSeg> AppendItem(DOMSVGPathSeg& aNewItem,
158 : ErrorResult& aError)
159 : {
160 0 : return InsertItemBefore(aNewItem, LengthNoFlush(), aError);
161 : }
162 0 : uint32_t Length() const
163 : {
164 0 : return NumberOfItems();
165 : }
166 :
167 : private:
168 :
169 : /**
170 : * Only our static GetDOMWrapper() factory method may create objects of our
171 : * type.
172 : */
173 0 : DOMSVGPathSegList(nsSVGElement *aElement, bool aIsAnimValList)
174 0 : : mElement(aElement)
175 0 : , mIsAnimValList(aIsAnimValList)
176 : {
177 0 : InternalListWillChangeTo(InternalList()); // Sync mItems
178 0 : }
179 :
180 : ~DOMSVGPathSegList();
181 :
182 0 : nsSVGElement* Element() const {
183 0 : return mElement.get();
184 : }
185 :
186 : /// Used to determine if this list is the baseVal or animVal list.
187 0 : bool IsAnimValList() const {
188 0 : return mIsAnimValList;
189 : }
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 returs 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 : }
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 0 : ItemProxy(DOMSVGPathSeg *aItem, uint32_t aInternalDataIndex)
235 0 : : mItem(aItem)
236 0 : , mInternalDataIndex(aInternalDataIndex)
237 0 : {}
238 :
239 : DOMSVGPathSeg *mItem;
240 : uint32_t mInternalDataIndex;
241 : };
242 :
243 : // Weak refs to our DOMSVGPathSeg items. The items are friends and take care
244 : // of clearing our pointer to them when they die.
245 : FallibleTArray<ItemProxy> mItems;
246 :
247 : // Strong ref to our element to keep it alive. We hold this not only for
248 : // ourself, but also for our DOMSVGPathSeg items too.
249 : RefPtr<nsSVGElement> mElement;
250 :
251 : bool mIsAnimValList;
252 : };
253 :
254 : } // namespace mozilla
255 :
256 : #endif // MOZILLA_DOMSVGPATHSEGLIST_H__
|