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"
12 #include "SVGAnimatedNumberList.h"
13 #include "mozilla/dom/SVGNumberListBinding.h"
14 #include "mozilla/RefPtr.h"
17 // See the comment in this file's header.
19 // local helper functions
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
) {
30 aItemsArray
[i
]->UpdateListIndex(i
);
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
)
48 if (tmp
->IsAnimValList()) {
49 tmp
->mAList
->mAnimVal
= nullptr;
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
)
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
) {
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.
107 // If our length has increased, null out the new pointers:
108 for (uint32_t i
= oldLength
; i
< aNewLength
; ++i
) {
113 SVGNumberList
& DOMSVGNumberList::InternalList() const {
114 SVGAnimatedNumberList
* alist
= Element()->GetAnimatedNumberList(AttrEnum());
115 return IsAnimValList() && alist
->mAnimVal
? *alist
->mAnimVal
119 void DOMSVGNumberList::Clear(ErrorResult
& error
) {
120 if (IsAnimValList()) {
121 error
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
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());
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
);
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
;
154 MOZ_ASSERT(!error
.Failed());
155 return InsertItemBefore(*domItem
, 0, error
);
158 already_AddRefed
<DOMSVGNumber
> DOMSVGNumberList::GetItem(uint32_t index
,
159 ErrorResult
& error
) {
161 RefPtr
<DOMSVGNumber
> item
= IndexedGetter(index
, found
, error
);
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();
175 return GetItemAt(index
);
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
);
187 index
= std::min(index
, LengthNoFlush());
188 if (index
>= DOMSVGNumber::MaxListIndex()) {
189 error
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
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
);
202 if (AnimListMirrorsBaseList()) {
203 if (!mAList
->mAnimVal
->mItems
.SetCapacity(
204 mAList
->mAnimVal
->mItems
.Length() + 1, fallible
)) {
205 error
.Throw(NS_ERROR_OUT_OF_MEMORY
);
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
);
234 if (index
>= LengthNoFlush()) {
235 error
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
239 // must do this before changing anything!
240 RefPtr
<DOMSVGNumber
> domItem
= aItem
.HasOwner() ? aItem
.Clone() : &aItem
;
242 AutoChangeNumberListNotifier
notifier(this);
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
);
266 if (index
>= LengthNoFlush()) {
267 error
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
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
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
]) {
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()) {
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()) {
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
);
344 } // namespace mozilla