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