tor-browser

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

DOMSVGLengthList.cpp (12002B)


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