Bug 1700051: part 46) Const-qualify `mozInlineSpellStatus::mAnchorRange`. r=smaug
[gecko.git] / dom / svg / DOMSVGNumberList.cpp
blobddd909fdb1a7ca7358d644f5a7d7e3984d8225af
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/. */
7 #include "DOMSVGNumberList.h"
9 #include "SVGElement.h"
10 #include "DOMSVGNumber.h"
11 #include "nsError.h"
12 #include "SVGAnimatedNumberList.h"
13 #include "mozilla/dom/SVGNumberListBinding.h"
14 #include "mozilla/RefPtr.h"
15 #include <algorithm>
17 // See the comment in this file's header.
19 // local helper functions
20 namespace {
22 using mozilla::dom::DOMSVGNumber;
24 void UpdateListIndicesFromIndex(FallibleTArray<DOMSVGNumber*>& aItemsArray,
25 uint32_t aStartingIndex) {
26 uint32_t length = aItemsArray.Length();
28 for (uint32_t i = aStartingIndex; i < length; ++i) {
29 if (aItemsArray[i]) {
30 aItemsArray[i]->UpdateListIndex(i);
35 } // namespace
37 namespace mozilla {
38 namespace dom {
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)
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;
53 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAList)
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
64 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGNumberList)
65 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGNumberList)
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
72 JSObject* DOMSVGNumberList::WrapObject(JSContext* cx,
73 JS::Handle<JSObject*> aGivenProto) {
74 return mozilla::dom::SVGNumberList_Binding::Wrap(cx, this, aGivenProto);
77 void DOMSVGNumberList::InternalListLengthWillChange(uint32_t aNewLength) {
78 uint32_t oldLength = mItems.Length();
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();
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;
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();
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;
107 // If our length has increased, null out the new pointers:
108 for (uint32_t i = oldLength; i < aNewLength; ++i) {
109 mItems[i] = nullptr;
113 SVGNumberList& DOMSVGNumberList::InternalList() const {
114 SVGAnimatedNumberList* alist = Element()->GetAnimatedNumberList(AttrEnum());
115 return IsAnimValList() && alist->mAnimVal ? *alist->mAnimVal
116 : alist->mBaseVal;
119 void DOMSVGNumberList::Clear(ErrorResult& error) {
120 if (IsAnimValList()) {
121 error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
122 return;
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());
132 mItems.Clear();
133 InternalList().Clear();
137 already_AddRefed<DOMSVGNumber> DOMSVGNumberList::Initialize(
138 DOMSVGNumber& aItem, ErrorResult& error) {
139 if (IsAnimValList()) {
140 error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
141 return nullptr;
144 // If newItem is already in a list we should insert a clone of newItem, and
145 // for consistency, this should happen even if *this* is the list that
146 // newItem is currently in. Note that in the case of newItem being in this
147 // list, the Clear() call before the InsertItemBefore() call would remove it
148 // from this list, and so the InsertItemBefore() call would not insert a
149 // clone of newItem, it would actually insert newItem. To prevent that from
150 // happening we have to do the clone here, if necessary.
151 RefPtr<DOMSVGNumber> domItem = aItem.HasOwner() ? aItem.Clone() : &aItem;
153 Clear(error);
154 MOZ_ASSERT(!error.Failed());
155 return InsertItemBefore(*domItem, 0, error);
158 already_AddRefed<DOMSVGNumber> DOMSVGNumberList::GetItem(uint32_t index,
159 ErrorResult& error) {
160 bool found;
161 RefPtr<DOMSVGNumber> item = IndexedGetter(index, found, error);
162 if (!found) {
163 error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
165 return item.forget();
168 already_AddRefed<DOMSVGNumber> DOMSVGNumberList::IndexedGetter(
169 uint32_t index, bool& found, ErrorResult& error) {
170 if (IsAnimValList()) {
171 Element()->FlushAnimations();
173 found = index < LengthNoFlush();
174 if (found) {
175 return GetItemAt(index);
177 return nullptr;
180 already_AddRefed<DOMSVGNumber> DOMSVGNumberList::InsertItemBefore(
181 DOMSVGNumber& aItem, uint32_t index, ErrorResult& error) {
182 if (IsAnimValList()) {
183 error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
184 return nullptr;
187 index = std::min(index, LengthNoFlush());
188 if (index >= DOMSVGNumber::MaxListIndex()) {
189 error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
190 return nullptr;
193 // must do this before changing anything!
194 RefPtr<DOMSVGNumber> domItem = aItem.HasOwner() ? aItem.Clone() : &aItem;
196 // Ensure we have enough memory so we can avoid complex error handling below:
197 if (!mItems.SetCapacity(mItems.Length() + 1, fallible) ||
198 !InternalList().SetCapacity(InternalList().Length() + 1)) {
199 error.Throw(NS_ERROR_OUT_OF_MEMORY);
200 return nullptr;
202 if (AnimListMirrorsBaseList()) {
203 if (!mAList->mAnimVal->mItems.SetCapacity(
204 mAList->mAnimVal->mItems.Length() + 1, fallible)) {
205 error.Throw(NS_ERROR_OUT_OF_MEMORY);
206 return nullptr;
210 AutoChangeNumberListNotifier notifier(this);
211 // Now that we know we're inserting, keep animVal list in sync as necessary.
212 MaybeInsertNullInAnimValListAt(index);
214 InternalList().InsertItem(index, domItem->ToSVGNumber());
215 MOZ_ALWAYS_TRUE(mItems.InsertElementAt(index, domItem, fallible));
217 // This MUST come after the insertion into InternalList(), or else under the
218 // insertion into InternalList() the values read from domItem would be bad
219 // data from InternalList() itself!:
220 domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
222 UpdateListIndicesFromIndex(mItems, index + 1);
224 return domItem.forget();
227 already_AddRefed<DOMSVGNumber> DOMSVGNumberList::ReplaceItem(
228 DOMSVGNumber& aItem, uint32_t index, ErrorResult& error) {
229 if (IsAnimValList()) {
230 error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
231 return nullptr;
234 if (index >= LengthNoFlush()) {
235 error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
236 return nullptr;
239 // must do this before changing anything!
240 RefPtr<DOMSVGNumber> domItem = aItem.HasOwner() ? aItem.Clone() : &aItem;
242 AutoChangeNumberListNotifier notifier(this);
243 if (mItems[index]) {
244 // Notify any existing DOM item of removal *before* modifying the lists so
245 // that the DOM item can copy the *old* value at its index:
246 mItems[index]->RemovingFromList();
249 InternalList()[index] = domItem->ToSVGNumber();
250 mItems[index] = domItem;
252 // This MUST come after the ToSVGPoint() call, otherwise that call
253 // would end up reading bad data from InternalList()!
254 domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
256 return domItem.forget();
259 already_AddRefed<DOMSVGNumber> DOMSVGNumberList::RemoveItem(
260 uint32_t index, ErrorResult& error) {
261 if (IsAnimValList()) {
262 error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
263 return nullptr;
266 if (index >= LengthNoFlush()) {
267 error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
268 return nullptr;
271 // Now that we know we're removing, keep animVal list in sync as necessary.
272 // Do this *before* touching InternalList() so the removed item can get its
273 // internal value.
274 MaybeRemoveItemFromAnimValListAt(index);
276 // We have to return the removed item, so get it, creating it if necessary:
277 RefPtr<DOMSVGNumber> result = GetItemAt(index);
279 AutoChangeNumberListNotifier notifier(this);
280 // Notify the DOM item of removal *before* modifying the lists so that the
281 // DOM item can copy its *old* value:
282 mItems[index]->RemovingFromList();
284 InternalList().RemoveItem(index);
285 mItems.RemoveElementAt(index);
287 UpdateListIndicesFromIndex(mItems, index);
289 return result.forget();
292 already_AddRefed<DOMSVGNumber> DOMSVGNumberList::GetItemAt(uint32_t aIndex) {
293 MOZ_ASSERT(aIndex < mItems.Length());
295 if (!mItems[aIndex]) {
296 mItems[aIndex] =
297 new DOMSVGNumber(this, AttrEnum(), aIndex, IsAnimValList());
299 RefPtr<DOMSVGNumber> result = mItems[aIndex];
300 return result.forget();
303 void DOMSVGNumberList::MaybeInsertNullInAnimValListAt(uint32_t aIndex) {
304 MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal");
306 if (!AnimListMirrorsBaseList()) {
307 return;
310 DOMSVGNumberList* animVal = mAList->mAnimVal;
312 MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal");
313 MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(),
314 "animVal list not in sync!");
315 MOZ_ALWAYS_TRUE(animVal->mItems.InsertElementAt(aIndex, nullptr, fallible));
317 UpdateListIndicesFromIndex(animVal->mItems, aIndex + 1);
320 void DOMSVGNumberList::MaybeRemoveItemFromAnimValListAt(uint32_t aIndex) {
321 MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal");
323 if (!AnimListMirrorsBaseList()) {
324 return;
327 // This needs to be a strong reference; otherwise, the RemovingFromList call
328 // below might drop the last reference to animVal before we're done with it.
329 RefPtr<DOMSVGNumberList> animVal = mAList->mAnimVal;
331 MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal");
332 MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(),
333 "animVal list not in sync!");
335 if (animVal->mItems[aIndex]) {
336 animVal->mItems[aIndex]->RemovingFromList();
338 animVal->mItems.RemoveElementAt(aIndex);
340 UpdateListIndicesFromIndex(animVal->mItems, aIndex);
343 } // namespace dom
344 } // namespace mozilla