tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DOMSVGNumberList.cpp (11619B)


      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 "DOMSVGNumberList.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "DOMSVGNumber.h"
     12 #include "SVGAnimatedNumberList.h"
     13 #include "SVGElement.h"
     14 #include "mozilla/RefPtr.h"
     15 #include "mozilla/dom/SVGNumberListBinding.h"
     16 #include "nsError.h"
     17 
     18 // See the comment in this file's header.
     19 
     20 // local helper functions
     21 namespace {
     22 
     23 using mozilla::dom::DOMSVGNumber;
     24 
     25 void UpdateListIndicesFromIndex(FallibleTArray<DOMSVGNumber*>& aItemsArray,
     26                                uint32_t aStartingIndex) {
     27  uint32_t length = aItemsArray.Length();
     28 
     29  for (uint32_t i = aStartingIndex; i < length; ++i) {
     30    if (aItemsArray[i]) {
     31      aItemsArray[i]->UpdateListIndex(i);
     32    }
     33  }
     34 }
     35 
     36 }  // namespace
     37 
     38 namespace mozilla::dom {
     39 
     40 // We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to
     41 // clear our DOMSVGAnimatedNumberList's weak ref to us to be safe. (The other
     42 // option would be to not unlink and rely on the breaking of the other edges in
     43 // the cycle, as NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
     44 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGNumberList)
     45 
     46 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGNumberList)
     47  if (tmp->mAList) {
     48    if (tmp->IsAnimValList()) {
     49      tmp->mAList->mAnimVal = nullptr;
     50    } else {
     51      tmp->mAList->mBaseVal = nullptr;
     52    }
     53    NS_IMPL_CYCLE_COLLECTION_UNLINK(mAList)
     54  }
     55  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     56 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     57 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGNumberList)
     58  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAList)
     59 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     60 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGNumberList)
     61  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
     62 NS_IMPL_CYCLE_COLLECTION_TRACE_END
     63 
     64 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGNumberList)
     65 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGNumberList)
     66 
     67 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGNumberList)
     68  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     69  NS_INTERFACE_MAP_ENTRY(nsISupports)
     70 NS_INTERFACE_MAP_END
     71 
     72 JSObject* DOMSVGNumberList::WrapObject(JSContext* cx,
     73                                       JS::Handle<JSObject*> aGivenProto) {
     74  return mozilla::dom::SVGNumberList_Binding::Wrap(cx, this, aGivenProto);
     75 }
     76 
     77 void DOMSVGNumberList::InternalListLengthWillChange(uint32_t aNewLength) {
     78  uint32_t oldLength = mItems.Length();
     79 
     80  if (aNewLength > DOMSVGNumber::MaxListIndex()) {
     81    // It's safe to get out of sync with our internal list as long as we have
     82    // FEWER items than it does.
     83    aNewLength = DOMSVGNumber::MaxListIndex();
     84  }
     85 
     86  RefPtr<DOMSVGNumberList> kungFuDeathGrip;
     87  if (aNewLength < oldLength) {
     88    // RemovingFromList() might clear last reference to |this|.
     89    // Retain a temporary reference to keep from dying before returning.
     90    kungFuDeathGrip = this;
     91  }
     92 
     93  // If our length will decrease, notify the items that will be removed:
     94  for (uint32_t i = aNewLength; i < oldLength; ++i) {
     95    if (mItems[i]) {
     96      mItems[i]->RemovingFromList();
     97    }
     98  }
     99 
    100  if (!mItems.SetLength(aNewLength, fallible)) {
    101    // We silently ignore SetLength OOM failure since being out of sync is safe
    102    // so long as we have *fewer* items than our internal list.
    103    mItems.Clear();
    104    return;
    105  }
    106 
    107  // If our length has increased, null out the new pointers:
    108  for (uint32_t i = oldLength; i < aNewLength; ++i) {
    109    mItems[i] = nullptr;
    110  }
    111 }
    112 
    113 SVGNumberList& DOMSVGNumberList::InternalList() const {
    114  SVGAnimatedNumberList* alist = Element()->GetAnimatedNumberList(AttrEnum());
    115  return IsAnimValList() && alist->mAnimVal ? *alist->mAnimVal
    116                                            : alist->mBaseVal;
    117 }
    118 
    119 void DOMSVGNumberList::Clear(ErrorResult& aRv) {
    120  if (IsAnimValList()) {
    121    aRv.ThrowNoModificationAllowedError("Animated values cannot be set");
    122    return;
    123  }
    124 
    125  if (LengthNoFlush() > 0) {
    126    AutoChangeNumberListNotifier notifier(this);
    127    // Notify any existing DOM items of removal *before* truncating the lists
    128    // so that they can find their SVGNumber internal counterparts and copy
    129    // their values. This also notifies the animVal list:
    130    mAList->InternalBaseValListWillChangeTo(SVGNumberList());
    131 
    132    mItems.Clear();
    133    auto* alist = Element()->GetAnimatedNumberList(AttrEnum());
    134    alist->mBaseVal.Clear();
    135    alist->mIsBaseSet = false;
    136  }
    137 }
    138 
    139 already_AddRefed<DOMSVGNumber> DOMSVGNumberList::Initialize(DOMSVGNumber& aItem,
    140                                                            ErrorResult& aRv) {
    141  if (IsAnimValList()) {
    142    aRv.ThrowNoModificationAllowedError("Animated values cannot be set");
    143    return nullptr;
    144  }
    145 
    146  // If newItem is already in a list we should insert a clone of newItem, and
    147  // for consistency, this should happen even if *this* is the list that
    148  // newItem is currently in. Note that in the case of newItem being in this
    149  // list, the Clear() call before the InsertItemBefore() call would remove it
    150  // from this list, and so the InsertItemBefore() call would not insert a
    151  // clone of newItem, it would actually insert newItem. To prevent that from
    152  // happening we have to do the clone here, if necessary.
    153  RefPtr<DOMSVGNumber> domItem = aItem.HasOwner() ? aItem.Clone() : &aItem;
    154 
    155  Clear(aRv);
    156  MOZ_ASSERT(!aRv.Failed());
    157  return InsertItemBefore(*domItem, 0, aRv);
    158 }
    159 
    160 already_AddRefed<DOMSVGNumber> DOMSVGNumberList::GetItem(uint32_t index,
    161                                                         ErrorResult& aRv) {
    162  bool found;
    163  RefPtr<DOMSVGNumber> item = IndexedGetter(index, found, aRv);
    164  if (!found) {
    165    aRv.ThrowIndexSizeError("Index out of range");
    166  }
    167  return item.forget();
    168 }
    169 
    170 already_AddRefed<DOMSVGNumber> DOMSVGNumberList::IndexedGetter(
    171    uint32_t index, bool& found, ErrorResult& aRv) {
    172  if (IsAnimValList()) {
    173    Element()->FlushAnimations();
    174  }
    175  found = index < LengthNoFlush();
    176  if (found) {
    177    return GetItemAt(index);
    178  }
    179  return nullptr;
    180 }
    181 
    182 already_AddRefed<DOMSVGNumber> DOMSVGNumberList::InsertItemBefore(
    183    DOMSVGNumber& aItem, uint32_t index, ErrorResult& aRv) {
    184  if (IsAnimValList()) {
    185    aRv.ThrowNoModificationAllowedError("Animated values cannot be set");
    186    return nullptr;
    187  }
    188 
    189  index = std::min(index, LengthNoFlush());
    190  if (index >= DOMSVGNumber::MaxListIndex()) {
    191    aRv.ThrowIndexSizeError("Index out of range");
    192    return nullptr;
    193  }
    194 
    195  // must do this before changing anything!
    196  RefPtr<DOMSVGNumber> domItem = aItem.HasOwner() ? aItem.Clone() : &aItem;
    197 
    198  // Ensure we have enough memory so we can avoid complex error handling below:
    199  if (!mItems.SetCapacity(mItems.Length() + 1, fallible) ||
    200      !InternalList().SetCapacity(InternalList().Length() + 1)) {
    201    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    202    return nullptr;
    203  }
    204  if (AnimListMirrorsBaseList()) {
    205    if (!mAList->mAnimVal->mItems.SetCapacity(
    206            mAList->mAnimVal->mItems.Length() + 1, fallible)) {
    207      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    208      return nullptr;
    209    }
    210  }
    211 
    212  AutoChangeNumberListNotifier notifier(this);
    213  // Now that we know we're inserting, keep animVal list in sync as necessary.
    214  MaybeInsertNullInAnimValListAt(index);
    215 
    216  InternalList().InsertItem(index, domItem->ToSVGNumber());
    217  MOZ_ALWAYS_TRUE(mItems.InsertElementAt(index, domItem, fallible));
    218 
    219  // This MUST come after the insertion into InternalList(), or else under the
    220  // insertion into InternalList() the values read from domItem would be bad
    221  // data from InternalList() itself!:
    222  domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
    223 
    224  UpdateListIndicesFromIndex(mItems, index + 1);
    225 
    226  return domItem.forget();
    227 }
    228 
    229 already_AddRefed<DOMSVGNumber> DOMSVGNumberList::ReplaceItem(
    230    DOMSVGNumber& aItem, uint32_t index, ErrorResult& aRv) {
    231  if (IsAnimValList()) {
    232    aRv.ThrowNoModificationAllowedError("Animated values cannot be set");
    233    return nullptr;
    234  }
    235 
    236  if (index >= LengthNoFlush()) {
    237    aRv.ThrowIndexSizeError("Index out of range");
    238    return nullptr;
    239  }
    240 
    241  // must do this before changing anything!
    242  RefPtr<DOMSVGNumber> domItem = aItem.HasOwner() ? aItem.Clone() : &aItem;
    243 
    244  AutoChangeNumberListNotifier notifier(this);
    245  if (mItems[index]) {
    246    // Notify any existing DOM item of removal *before* modifying the lists so
    247    // that the DOM item can copy the *old* value at its index:
    248    mItems[index]->RemovingFromList();
    249  }
    250 
    251  InternalList()[index] = domItem->ToSVGNumber();
    252  mItems[index] = domItem;
    253 
    254  // This MUST come after the ToSVGPoint() call, otherwise that call
    255  // would end up reading bad data from InternalList()!
    256  domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
    257 
    258  return domItem.forget();
    259 }
    260 
    261 already_AddRefed<DOMSVGNumber> DOMSVGNumberList::RemoveItem(uint32_t index,
    262                                                            ErrorResult& aRv) {
    263  if (IsAnimValList()) {
    264    aRv.ThrowNoModificationAllowedError("Animated values cannot be set");
    265    return nullptr;
    266  }
    267 
    268  if (index >= LengthNoFlush()) {
    269    aRv.ThrowIndexSizeError("Index out of range");
    270    return nullptr;
    271  }
    272 
    273  // Now that we know we're removing, keep animVal list in sync as necessary.
    274  // Do this *before* touching InternalList() so the removed item can get its
    275  // internal value.
    276  MaybeRemoveItemFromAnimValListAt(index);
    277 
    278  // We have to return the removed item, so get it, creating it if necessary:
    279  RefPtr<DOMSVGNumber> result = GetItemAt(index);
    280 
    281  AutoChangeNumberListNotifier notifier(this);
    282  // Notify the DOM item of removal *before* modifying the lists so that the
    283  // DOM item can copy its *old* value:
    284  mItems[index]->RemovingFromList();
    285 
    286  InternalList().RemoveItem(index);
    287  mItems.RemoveElementAt(index);
    288 
    289  UpdateListIndicesFromIndex(mItems, index);
    290 
    291  return result.forget();
    292 }
    293 
    294 already_AddRefed<DOMSVGNumber> DOMSVGNumberList::GetItemAt(uint32_t aIndex) {
    295  MOZ_ASSERT(aIndex < mItems.Length());
    296 
    297  if (!mItems[aIndex]) {
    298    mItems[aIndex] =
    299        new DOMSVGNumber(this, AttrEnum(), aIndex, IsAnimValList());
    300  }
    301  RefPtr<DOMSVGNumber> result = mItems[aIndex];
    302  return result.forget();
    303 }
    304 
    305 void DOMSVGNumberList::MaybeInsertNullInAnimValListAt(uint32_t aIndex) {
    306  MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal");
    307 
    308  if (!AnimListMirrorsBaseList()) {
    309    return;
    310  }
    311 
    312  DOMSVGNumberList* animVal = mAList->mAnimVal;
    313 
    314  MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal");
    315  MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(),
    316             "animVal list not in sync!");
    317  MOZ_ALWAYS_TRUE(animVal->mItems.InsertElementAt(aIndex, nullptr, fallible));
    318 
    319  UpdateListIndicesFromIndex(animVal->mItems, aIndex + 1);
    320 }
    321 
    322 void DOMSVGNumberList::MaybeRemoveItemFromAnimValListAt(uint32_t aIndex) {
    323  MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal");
    324 
    325  if (!AnimListMirrorsBaseList()) {
    326    return;
    327  }
    328 
    329  // This needs to be a strong reference; otherwise, the RemovingFromList call
    330  // below might drop the last reference to animVal before we're done with it.
    331  RefPtr<DOMSVGNumberList> animVal = mAList->mAnimVal;
    332 
    333  MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal");
    334  MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(),
    335             "animVal list not in sync!");
    336 
    337  if (animVal->mItems[aIndex]) {
    338    animVal->mItems[aIndex]->RemovingFromList();
    339  }
    340  animVal->mItems.RemoveElementAt(aIndex);
    341 
    342  UpdateListIndicesFromIndex(animVal->mItems, aIndex);
    343 }
    344 
    345 }  // namespace mozilla::dom