DOMSVGPathSegList.cpp 22 KB

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