Coverage Report

Created: 2018-09-25 14:53

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