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 : #include "nsSVGElement.h"
8 : #include "DOMSVGPathSegList.h"
9 : #include "DOMSVGPathSeg.h"
10 : #include "nsError.h"
11 : #include "SVGAnimatedPathSegList.h"
12 : #include "nsCOMPtr.h"
13 : #include "nsSVGAttrTearoffTable.h"
14 : #include "SVGPathSegUtils.h"
15 : #include "mozilla/dom/SVGPathSegListBinding.h"
16 :
17 : // See the comment in this file's header.
18 :
19 : namespace mozilla {
20 :
21 : static inline
22 : nsSVGAttrTearoffTable<void, DOMSVGPathSegList>&
23 88 : SVGPathSegListTearoffTable()
24 : {
25 : static nsSVGAttrTearoffTable<void, DOMSVGPathSegList>
26 88 : sSVGPathSegListTearoffTable;
27 88 : return sSVGPathSegListTearoffTable;
28 : }
29 :
30 : NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGPathSegList)
31 :
32 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGPathSegList)
33 : // No unlinking of mElement, we'd need to null out the value pointer (the
34 : // object it points to is held by the element) and null-check it everywhere.
35 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
36 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
37 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGPathSegList)
38 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement)
39 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
40 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGPathSegList)
41 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
42 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
43 :
44 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGPathSegList)
45 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGPathSegList)
46 :
47 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGPathSegList)
48 0 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
49 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
50 0 : NS_INTERFACE_MAP_END
51 :
52 :
53 : //----------------------------------------------------------------------
54 : // Helper class: AutoChangePathSegListNotifier
55 : // Stack-based helper class to pair calls to WillChangePathSegList and
56 : // DidChangePathSegList.
57 : class MOZ_RAII AutoChangePathSegListNotifier
58 : {
59 : public:
60 0 : explicit AutoChangePathSegListNotifier(DOMSVGPathSegList* aPathSegList MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
61 0 : : mPathSegList(aPathSegList)
62 : {
63 0 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
64 0 : MOZ_ASSERT(mPathSegList, "Expecting non-null pathSegList");
65 : mEmptyOrOldValue =
66 0 : mPathSegList->Element()->WillChangePathSegList();
67 0 : }
68 :
69 0 : ~AutoChangePathSegListNotifier()
70 0 : {
71 0 : mPathSegList->Element()->DidChangePathSegList(mEmptyOrOldValue);
72 0 : if (mPathSegList->AttrIsAnimating()) {
73 0 : mPathSegList->Element()->AnimationNeedsResample();
74 : }
75 0 : }
76 :
77 : private:
78 : DOMSVGPathSegList* const mPathSegList;
79 : nsAttrValue mEmptyOrOldValue;
80 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
81 : };
82 :
83 : /* static */ already_AddRefed<DOMSVGPathSegList>
84 0 : DOMSVGPathSegList::GetDOMWrapper(void *aList,
85 : nsSVGElement *aElement,
86 : bool aIsAnimValList)
87 : {
88 : RefPtr<DOMSVGPathSegList> wrapper =
89 0 : SVGPathSegListTearoffTable().GetTearoff(aList);
90 0 : if (!wrapper) {
91 0 : wrapper = new DOMSVGPathSegList(aElement, aIsAnimValList);
92 0 : SVGPathSegListTearoffTable().AddTearoff(aList, wrapper);
93 : }
94 0 : return wrapper.forget();
95 : }
96 :
97 : /* static */ DOMSVGPathSegList*
98 88 : DOMSVGPathSegList::GetDOMWrapperIfExists(void *aList)
99 : {
100 88 : return SVGPathSegListTearoffTable().GetTearoff(aList);
101 : }
102 :
103 0 : DOMSVGPathSegList::~DOMSVGPathSegList()
104 : {
105 : // There are now no longer any references to us held by script or list items.
106 : // Note we must use GetAnimValKey/GetBaseValKey here, NOT InternalList()!
107 0 : void *key = mIsAnimValList ?
108 0 : InternalAList().GetAnimValKey() :
109 0 : InternalAList().GetBaseValKey();
110 0 : SVGPathSegListTearoffTable().RemoveTearoff(key);
111 0 : }
112 :
113 : JSObject*
114 0 : DOMSVGPathSegList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
115 : {
116 0 : return mozilla::dom::SVGPathSegListBinding::Wrap(cx, this, aGivenProto);
117 : }
118 :
119 : void
120 0 : DOMSVGPathSegList::InternalListWillChangeTo(const SVGPathData& aNewValue)
121 : {
122 : // When the number of items in our internal counterpart changes, we MUST stay
123 : // in sync. Everything in the scary comment in
124 : // DOMSVGLengthList::InternalBaseValListWillChangeTo applies here just as
125 : // much, but we have the additional issue that failing to stay in sync would
126 : // mean that - assuming we aren't reading bad memory - we would likely end up
127 : // decoding command types from argument floats when looking in our
128 : // SVGPathData's data array! Either way, we'll likely then go down
129 : // NS_NOTREACHED code paths, or end up reading/setting more bad memory!!
130 :
131 : // The only time that our other DOM list type implementations remove items is
132 : // if those items become surplus items due to an attribute change or SMIL
133 : // animation sample shortening the list. In general though, they try to keep
134 : // their existing DOM items, even when things change. To be consistent, we'd
135 : // really like to do the same thing. However, because different types of path
136 : // segment correspond to different DOMSVGPathSeg subclasses, the type of
137 : // items in our list are generally not the same, which makes this harder for
138 : // us. We have to remove DOM segments if their type is not the same as the
139 : // type of the new internal segment at their index.
140 : //
141 : // We also need to sync up mInternalDataIndex, but since we need to loop over
142 : // all the items in the new list checking types anyway, that's almost
143 : // insignificant in terms of overhead.
144 : //
145 : // Note that this method is called on every single SMIL animation resample
146 : // and we have no way to short circuit the overhead since we don't have a
147 : // way to tell if the call is due to a new animation, or a resample of an
148 : // existing animation (when the number and type of items would be the same).
149 : // (Note that a new animation could start overriding an existing animation at
150 : // any time, so checking IsAnimating() wouldn't work.) Because we get called
151 : // on every sample, it would not be acceptable alternative to throw away all
152 : // our items and let them be recreated lazily, since that would break what
153 : // script sees!
154 :
155 0 : uint32_t length = mItems.Length();
156 0 : uint32_t index = 0;
157 :
158 0 : uint32_t dataLength = aNewValue.mData.Length();
159 0 : uint32_t dataIndex = 0; // index into aNewValue's raw data array
160 :
161 : uint32_t newSegType;
162 :
163 0 : RefPtr<DOMSVGPathSegList> kungFuDeathGrip;
164 0 : if (length) {
165 : // RemovingFromList() might clear last reference to |this|.
166 : // Retain a temporary reference to keep from dying before returning.
167 : //
168 : // NOTE: For path-seg lists (unlike other list types), we have to do this
169 : // *whenever our list is nonempty* (even if we're growing in length).
170 : // That's because the path-seg-type of any segment could differ between old
171 : // list vs. new list, which will make us destroy & recreate that segment,
172 : // which could remove the last reference to us.
173 : //
174 : // (We explicitly *don't* want to create a kungFuDeathGrip in the length=0
175 : // case, though, because we do hit this code inside our constructor before
176 : // any other owning references have been added, and at that point, the
177 : // deathgrip-removal would make us die before we exit our constructor.)
178 0 : kungFuDeathGrip = this;
179 : }
180 :
181 0 : while (index < length && dataIndex < dataLength) {
182 0 : newSegType = SVGPathSegUtils::DecodeType(aNewValue.mData[dataIndex]);
183 0 : if (ItemAt(index) && ItemAt(index)->Type() != newSegType) {
184 0 : ItemAt(index)->RemovingFromList();
185 0 : ItemAt(index) = nullptr;
186 : }
187 : // Only after the RemovingFromList() can we touch mInternalDataIndex!
188 0 : mItems[index].mInternalDataIndex = dataIndex;
189 0 : ++index;
190 0 : dataIndex += 1 + SVGPathSegUtils::ArgCountForType(newSegType);
191 : }
192 :
193 0 : MOZ_ASSERT((index == length && dataIndex <= dataLength) ||
194 : (index <= length && dataIndex == dataLength),
195 : "very bad - list corruption?");
196 :
197 0 : if (index < length) {
198 : // aNewValue has fewer items than our previous internal counterpart
199 :
200 0 : uint32_t newLength = index;
201 :
202 : // Remove excess items from the list:
203 0 : for (; index < length; ++index) {
204 0 : if (ItemAt(index)) {
205 0 : ItemAt(index)->RemovingFromList();
206 0 : ItemAt(index) = nullptr;
207 : }
208 : }
209 :
210 : // Only now may we truncate mItems
211 0 : mItems.TruncateLength(newLength);
212 0 : } else if (dataIndex < dataLength) {
213 : // aNewValue has more items than our previous internal counterpart
214 :
215 : // Sync mItems:
216 0 : while (dataIndex < dataLength) {
217 0 : if (mItems.Length() &&
218 0 : mItems.Length() - 1 > DOMSVGPathSeg::MaxListIndex()) {
219 : // It's safe to get out of sync with our internal list as long as we
220 : // have FEWER items than it does.
221 0 : return;
222 : }
223 0 : if (!mItems.AppendElement(ItemProxy(nullptr, dataIndex), fallible)) {
224 : // OOM
225 0 : ErrorResult rv;
226 0 : Clear(rv);
227 0 : MOZ_ASSERT(!rv.Failed());
228 0 : return;
229 : }
230 0 : dataIndex += 1 + SVGPathSegUtils::ArgCountForType(SVGPathSegUtils::DecodeType(aNewValue.mData[dataIndex]));
231 : }
232 : }
233 :
234 0 : MOZ_ASSERT(dataIndex == dataLength, "Serious processing error");
235 0 : MOZ_ASSERT(index == length, "Serious counting error");
236 : }
237 :
238 : bool
239 0 : DOMSVGPathSegList::AttrIsAnimating() const
240 : {
241 0 : return InternalAList().IsAnimating();
242 : }
243 :
244 : bool
245 0 : DOMSVGPathSegList::AnimListMirrorsBaseList() const
246 : {
247 0 : return GetDOMWrapperIfExists(InternalAList().GetAnimValKey()) &&
248 0 : !AttrIsAnimating();
249 : }
250 :
251 : SVGPathData&
252 0 : DOMSVGPathSegList::InternalList() const
253 : {
254 0 : SVGAnimatedPathSegList *alist = mElement->GetAnimPathSegList();
255 0 : return mIsAnimValList && alist->IsAnimating() ? *alist->mAnimVal : alist->mBaseVal;
256 : }
257 :
258 : SVGAnimatedPathSegList&
259 0 : DOMSVGPathSegList::InternalAList() const
260 : {
261 0 : MOZ_ASSERT(mElement->GetAnimPathSegList(), "Internal error");
262 0 : return *mElement->GetAnimPathSegList();
263 : }
264 :
265 : // ----------------------------------------------------------------------------
266 : // nsIDOMSVGPathSegList implementation:
267 :
268 : void
269 0 : DOMSVGPathSegList::Clear(ErrorResult& aError)
270 : {
271 0 : if (IsAnimValList()) {
272 0 : aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
273 0 : return;
274 : }
275 :
276 0 : if (LengthNoFlush() > 0) {
277 0 : AutoChangePathSegListNotifier notifier(this);
278 : // DOM list items that are to be removed must be removed before we change
279 : // the internal list, otherwise they wouldn't be able to copy their
280 : // internal counterparts' values!
281 :
282 0 : InternalListWillChangeTo(SVGPathData()); // clears mItems
283 :
284 0 : if (!AttrIsAnimating()) {
285 : // The anim val list is in sync with the base val list
286 : DOMSVGPathSegList *animList =
287 0 : GetDOMWrapperIfExists(InternalAList().GetAnimValKey());
288 0 : if (animList) {
289 0 : animList->InternalListWillChangeTo(SVGPathData()); // clears its mItems
290 : }
291 : }
292 :
293 0 : InternalList().Clear();
294 : }
295 : }
296 :
297 : already_AddRefed<DOMSVGPathSeg>
298 0 : DOMSVGPathSegList::Initialize(DOMSVGPathSeg& aNewItem, ErrorResult& aError)
299 : {
300 0 : if (IsAnimValList()) {
301 0 : aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
302 0 : return nullptr;
303 : }
304 :
305 : // If aNewItem is already in a list we should insert a clone of aNewItem,
306 : // and for consistency, this should happen even if *this* is the list that
307 : // aNewItem is currently in. Note that in the case of aNewItem being in this
308 : // list, the Clear() call before the InsertItemBefore() call would remove it
309 : // from this list, and so the InsertItemBefore() call would not insert a
310 : // clone of aNewItem, it would actually insert aNewItem. To prevent that
311 : // from happening we have to do the clone here, if necessary.
312 :
313 0 : RefPtr<DOMSVGPathSeg> domItem = &aNewItem;
314 0 : if (aNewItem.HasOwner()) {
315 0 : domItem = aNewItem.Clone();
316 : }
317 :
318 0 : Clear(aError);
319 0 : MOZ_ASSERT(!aError.Failed(), "How could this fail?");
320 0 : return InsertItemBefore(*domItem, 0, aError);
321 : }
322 :
323 : already_AddRefed<DOMSVGPathSeg>
324 0 : DOMSVGPathSegList::GetItem(uint32_t index, ErrorResult& error)
325 : {
326 : bool found;
327 0 : RefPtr<DOMSVGPathSeg> item = IndexedGetter(index, found, error);
328 0 : if (!found) {
329 0 : error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
330 : }
331 0 : return item.forget();
332 : }
333 :
334 : already_AddRefed<DOMSVGPathSeg>
335 0 : DOMSVGPathSegList::IndexedGetter(uint32_t aIndex, bool& aFound,
336 : ErrorResult& aError)
337 : {
338 0 : if (IsAnimValList()) {
339 0 : Element()->FlushAnimations();
340 : }
341 0 : aFound = aIndex < LengthNoFlush();
342 0 : if (aFound) {
343 0 : return GetItemAt(aIndex);
344 : }
345 0 : return nullptr;
346 : }
347 :
348 : already_AddRefed<DOMSVGPathSeg>
349 0 : DOMSVGPathSegList::InsertItemBefore(DOMSVGPathSeg& aNewItem,
350 : uint32_t aIndex,
351 : ErrorResult& aError)
352 : {
353 0 : if (IsAnimValList()) {
354 0 : aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
355 0 : return nullptr;
356 : }
357 :
358 : uint32_t internalIndex;
359 0 : if (aIndex < LengthNoFlush()) {
360 0 : internalIndex = mItems[aIndex].mInternalDataIndex;
361 : } else {
362 0 : aIndex = LengthNoFlush();
363 0 : internalIndex = InternalList().mData.Length();
364 : }
365 0 : if (aIndex >= DOMSVGPathSeg::MaxListIndex()) {
366 0 : aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
367 0 : return nullptr;
368 : }
369 :
370 0 : RefPtr<DOMSVGPathSeg> domItem = &aNewItem;
371 0 : if (domItem->HasOwner()) {
372 0 : domItem = domItem->Clone(); // must do this before changing anything!
373 : }
374 :
375 0 : uint32_t argCount = SVGPathSegUtils::ArgCountForType(domItem->Type());
376 :
377 : // Ensure we have enough memory so we can avoid complex error handling below:
378 0 : if (!mItems.SetCapacity(mItems.Length() + 1, fallible) ||
379 0 : !InternalList().mData.SetCapacity(InternalList().mData.Length() + 1 + argCount,
380 : fallible)) {
381 0 : aError.Throw(NS_ERROR_OUT_OF_MEMORY);
382 0 : return nullptr;
383 : }
384 0 : if (AnimListMirrorsBaseList()) {
385 : DOMSVGPathSegList *animVal =
386 0 : GetDOMWrapperIfExists(InternalAList().GetAnimValKey());
387 0 : MOZ_ASSERT(animVal, "animVal should be a valid pointer");
388 0 : if (!animVal->mItems.SetCapacity(
389 0 : animVal->mItems.Length() + 1, fallible)) {
390 0 : aError.Throw(NS_ERROR_OUT_OF_MEMORY);
391 0 : return nullptr;
392 : }
393 : }
394 :
395 0 : AutoChangePathSegListNotifier notifier(this);
396 : // Now that we know we're inserting, keep animVal list in sync as necessary.
397 0 : MaybeInsertNullInAnimValListAt(aIndex, internalIndex, argCount);
398 :
399 : float segAsRaw[1 + NS_SVG_PATH_SEG_MAX_ARGS];
400 0 : domItem->ToSVGPathSegEncodedData(segAsRaw);
401 :
402 0 : MOZ_ALWAYS_TRUE(InternalList().mData.InsertElementsAt(internalIndex,
403 : segAsRaw,
404 : 1 + argCount,
405 : fallible));
406 0 : MOZ_ALWAYS_TRUE(mItems.InsertElementAt(aIndex,
407 : ItemProxy(domItem.get(),
408 : internalIndex),
409 : fallible));
410 :
411 : // This MUST come after the insertion into InternalList(), or else under the
412 : // insertion into InternalList() the values read from domItem would be bad
413 : // data from InternalList() itself!:
414 0 : domItem->InsertingIntoList(this, aIndex, IsAnimValList());
415 :
416 0 : UpdateListIndicesFromIndex(aIndex + 1, argCount + 1);
417 :
418 0 : return domItem.forget();
419 : }
420 :
421 : already_AddRefed<DOMSVGPathSeg>
422 0 : DOMSVGPathSegList::ReplaceItem(DOMSVGPathSeg& aNewItem,
423 : uint32_t aIndex,
424 : ErrorResult& aError)
425 : {
426 0 : if (IsAnimValList()) {
427 0 : aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
428 0 : return nullptr;
429 : }
430 :
431 0 : if (aIndex >= LengthNoFlush()) {
432 0 : aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
433 0 : return nullptr;
434 : }
435 :
436 0 : RefPtr<DOMSVGPathSeg> domItem = &aNewItem;
437 0 : if (domItem->HasOwner()) {
438 0 : domItem = domItem->Clone(); // must do this before changing anything!
439 : }
440 :
441 0 : AutoChangePathSegListNotifier notifier(this);
442 0 : if (ItemAt(aIndex)) {
443 : // Notify any existing DOM item of removal *before* modifying the lists so
444 : // that the DOM item can copy the *old* value at its index:
445 0 : ItemAt(aIndex)->RemovingFromList();
446 : }
447 :
448 0 : uint32_t internalIndex = mItems[aIndex].mInternalDataIndex;
449 : // We use InternalList() to get oldArgCount since we may not have a DOM
450 : // wrapper at the index being replaced.
451 0 : uint32_t oldType = SVGPathSegUtils::DecodeType(InternalList().mData[internalIndex]);
452 :
453 : // NOTE: ArgCountForType returns a (small) unsigned value, but we're
454 : // intentionally putting it in a signed variable, because we're going to
455 : // subtract these values and might produce something negative.
456 0 : int32_t oldArgCount = SVGPathSegUtils::ArgCountForType(oldType);
457 0 : int32_t newArgCount = SVGPathSegUtils::ArgCountForType(domItem->Type());
458 :
459 : float segAsRaw[1 + NS_SVG_PATH_SEG_MAX_ARGS];
460 0 : domItem->ToSVGPathSegEncodedData(segAsRaw);
461 :
462 0 : if (!InternalList().mData.ReplaceElementsAt(internalIndex, 1 + oldArgCount,
463 0 : segAsRaw, 1 + newArgCount,
464 : fallible)) {
465 0 : aError.Throw(NS_ERROR_OUT_OF_MEMORY);
466 0 : return nullptr;
467 : }
468 0 : ItemAt(aIndex) = domItem;
469 :
470 : // This MUST come after the ToSVGPathSegEncodedData call, otherwise that call
471 : // would end up reading bad data from InternalList()!
472 0 : domItem->InsertingIntoList(this, aIndex, IsAnimValList());
473 :
474 0 : int32_t delta = newArgCount - oldArgCount;
475 0 : if (delta != 0) {
476 0 : for (uint32_t i = aIndex + 1; i < LengthNoFlush(); ++i) {
477 0 : mItems[i].mInternalDataIndex += delta;
478 : }
479 : }
480 :
481 0 : return domItem.forget();
482 : }
483 :
484 : already_AddRefed<DOMSVGPathSeg>
485 0 : DOMSVGPathSegList::RemoveItem(uint32_t aIndex,
486 : ErrorResult& aError)
487 : {
488 0 : if (IsAnimValList()) {
489 0 : aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
490 0 : return nullptr;
491 : }
492 :
493 0 : if (aIndex >= LengthNoFlush()) {
494 0 : aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
495 0 : return nullptr;
496 : }
497 : // We have to return the removed item, so get it, creating it if necessary:
498 0 : RefPtr<DOMSVGPathSeg> result = GetItemAt(aIndex);
499 :
500 0 : AutoChangePathSegListNotifier notifier(this);
501 : // Notify the DOM item of removal *before* modifying the lists so that the
502 : // DOM item can copy its *old* value:
503 0 : ItemAt(aIndex)->RemovingFromList();
504 :
505 0 : uint32_t internalIndex = mItems[aIndex].mInternalDataIndex;
506 0 : uint32_t segType = SVGPathSegUtils::DecodeType(InternalList().mData[internalIndex]);
507 : // NOTE: ArgCountForType returns a (small) unsigned value, but we're
508 : // intentionally putting it in a signed value, because we're going to
509 : // negate it, and you can't negate an unsigned value.
510 0 : int32_t argCount = SVGPathSegUtils::ArgCountForType(segType);
511 :
512 : // Now that we know we're removing, keep animVal list in sync as necessary.
513 : // Do this *before* touching InternalList() so the removed item can get its
514 : // internal value.
515 0 : MaybeRemoveItemFromAnimValListAt(aIndex, argCount);
516 :
517 0 : InternalList().mData.RemoveElementsAt(internalIndex, 1 + argCount);
518 0 : mItems.RemoveElementAt(aIndex);
519 :
520 0 : UpdateListIndicesFromIndex(aIndex, -(argCount + 1));
521 :
522 0 : return result.forget();
523 : }
524 :
525 : already_AddRefed<DOMSVGPathSeg>
526 0 : DOMSVGPathSegList::GetItemAt(uint32_t aIndex)
527 : {
528 0 : MOZ_ASSERT(aIndex < mItems.Length());
529 :
530 0 : if (!ItemAt(aIndex)) {
531 0 : ItemAt(aIndex) = DOMSVGPathSeg::CreateFor(this, aIndex, IsAnimValList());
532 : }
533 0 : RefPtr<DOMSVGPathSeg> result = ItemAt(aIndex);
534 0 : return result.forget();
535 : }
536 :
537 : void
538 0 : DOMSVGPathSegList::
539 : MaybeInsertNullInAnimValListAt(uint32_t aIndex,
540 : uint32_t aInternalIndex,
541 : uint32_t aArgCountForItem)
542 : {
543 0 : MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal");
544 :
545 0 : if (!AnimListMirrorsBaseList()) {
546 0 : return;
547 : }
548 :
549 : // The anim val list is in sync with the base val list
550 : DOMSVGPathSegList *animVal =
551 0 : GetDOMWrapperIfExists(InternalAList().GetAnimValKey());
552 :
553 0 : MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal");
554 0 : MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(),
555 : "animVal list not in sync!");
556 0 : MOZ_ALWAYS_TRUE(animVal->mItems.InsertElementAt(aIndex,
557 : ItemProxy(nullptr,
558 : aInternalIndex),
559 : fallible));
560 :
561 0 : animVal->UpdateListIndicesFromIndex(aIndex + 1, 1 + aArgCountForItem);
562 : }
563 :
564 : void
565 0 : DOMSVGPathSegList::
566 : MaybeRemoveItemFromAnimValListAt(uint32_t aIndex,
567 : int32_t aArgCountForItem)
568 : {
569 0 : MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal");
570 :
571 0 : if (!AnimListMirrorsBaseList()) {
572 0 : return;
573 : }
574 :
575 : // This needs to be a strong reference; otherwise, the RemovingFromList call
576 : // below might drop the last reference to animVal before we're done with it.
577 : RefPtr<DOMSVGPathSegList> animVal =
578 0 : GetDOMWrapperIfExists(InternalAList().GetAnimValKey());
579 :
580 0 : MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal");
581 0 : MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(),
582 : "animVal list not in sync!");
583 :
584 0 : if (animVal->ItemAt(aIndex)) {
585 0 : animVal->ItemAt(aIndex)->RemovingFromList();
586 : }
587 0 : animVal->mItems.RemoveElementAt(aIndex);
588 :
589 0 : animVal->UpdateListIndicesFromIndex(aIndex, -(1 + aArgCountForItem));
590 : }
591 :
592 : void
593 0 : DOMSVGPathSegList::UpdateListIndicesFromIndex(uint32_t aStartingIndex,
594 : int32_t aInternalDataIndexDelta)
595 : {
596 0 : uint32_t length = mItems.Length();
597 :
598 0 : for (uint32_t i = aStartingIndex; i < length; ++i) {
599 0 : mItems[i].mInternalDataIndex += aInternalDataIndexDelta;
600 0 : if (ItemAt(i)) {
601 0 : ItemAt(i)->UpdateListIndex(i);
602 : }
603 : }
604 0 : }
605 :
606 : } // namespace mozilla
|