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 "DOMSVGStringList.h"
8
9 #include "mozAutoDocUpdate.h"
10 #include "mozilla/dom/SVGStringListBinding.h"
11 #include "mozilla/dom/SVGTests.h"
12 #include "nsCOMPtr.h"
13 #include "nsError.h"
14 #include "nsQueryObject.h"
15 #include "SVGAttrTearoffTable.h"
16 #include <algorithm>
17
18 // See the architecture comment in this file's header.
19
20 namespace mozilla {
21 namespace dom {
22
23 static inline SVGAttrTearoffTable<SVGStringList, DOMSVGStringList>&
SVGStringListTearoffTable()24 SVGStringListTearoffTable() {
25 static SVGAttrTearoffTable<SVGStringList, DOMSVGStringList>
26 sSVGStringListTearoffTable;
27 return sSVGStringListTearoffTable;
28 }
29
30 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGStringList)
31
32 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGStringList)
33 // No unlinking of mElement, we'd need to null out the value pointer (the
34 // object it points to is held by the element) and null-check it everywhere.
35 tmp->RemoveFromTearoffTable();
36 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
37 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
38 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGStringList)
39 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement)
40 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
41 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGStringList)
42 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
43 NS_IMPL_CYCLE_COLLECTION_TRACE_END
44
45 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGStringList)
46 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGStringList)
47
48 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGStringList)
49 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
50 NS_INTERFACE_MAP_ENTRY(nsISupports)
51 NS_INTERFACE_MAP_END
52
53 //----------------------------------------------------------------------
54 // Helper class: AutoChangeStringListNotifier
55 // Stack-based helper class to pair calls to WillChangeStringListList and
56 // DidChangeStringListList.
57 class MOZ_RAII AutoChangeStringListNotifier : public mozAutoDocUpdate {
58 public:
AutoChangeStringListNotifier(DOMSVGStringList * aStringList)59 explicit AutoChangeStringListNotifier(DOMSVGStringList* aStringList)
60 : mozAutoDocUpdate(aStringList->mElement->GetComposedDoc(), true),
61 mStringList(aStringList) {
62 MOZ_ASSERT(mStringList, "Expecting non-null stringList");
63 mEmptyOrOldValue = mStringList->mElement->WillChangeStringList(
64 mStringList->mIsConditionalProcessingAttribute, mStringList->mAttrEnum,
65 *this);
66 }
67
~AutoChangeStringListNotifier()68 ~AutoChangeStringListNotifier() {
69 mStringList->mElement->DidChangeStringList(
70 mStringList->mIsConditionalProcessingAttribute, mStringList->mAttrEnum,
71 mEmptyOrOldValue, *this);
72 }
73
74 private:
75 DOMSVGStringList* const mStringList;
76 nsAttrValue mEmptyOrOldValue;
77 };
78
79 /* static */
GetDOMWrapper(SVGStringList * aList,SVGElement * aElement,bool aIsConditionalProcessingAttribute,uint8_t aAttrEnum)80 already_AddRefed<DOMSVGStringList> DOMSVGStringList::GetDOMWrapper(
81 SVGStringList* aList, SVGElement* aElement,
82 bool aIsConditionalProcessingAttribute, uint8_t aAttrEnum) {
83 RefPtr<DOMSVGStringList> wrapper =
84 SVGStringListTearoffTable().GetTearoff(aList);
85 if (!wrapper) {
86 wrapper = new DOMSVGStringList(aElement, aIsConditionalProcessingAttribute,
87 aAttrEnum);
88 SVGStringListTearoffTable().AddTearoff(aList, wrapper);
89 }
90 return wrapper.forget();
91 }
92
RemoveFromTearoffTable()93 void DOMSVGStringList::RemoveFromTearoffTable() {
94 // Script no longer has any references to us.
95 SVGStringListTearoffTable().RemoveTearoff(&InternalList());
96 }
97
~DOMSVGStringList()98 DOMSVGStringList::~DOMSVGStringList() { RemoveFromTearoffTable(); }
99
100 /* virtual */
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)101 JSObject* DOMSVGStringList::WrapObject(JSContext* aCx,
102 JS::Handle<JSObject*> aGivenProto) {
103 return SVGStringList_Binding::Wrap(aCx, this, aGivenProto);
104 }
105
106 // ----------------------------------------------------------------------------
107 // SVGStringList implementation:
108
NumberOfItems() const109 uint32_t DOMSVGStringList::NumberOfItems() const {
110 return InternalList().Length();
111 }
112
Length() const113 uint32_t DOMSVGStringList::Length() const { return NumberOfItems(); }
114
Clear()115 void DOMSVGStringList::Clear() {
116 if (InternalList().IsExplicitlySet()) {
117 AutoChangeStringListNotifier notifier(this);
118 InternalList().Clear();
119 }
120 }
121
Initialize(const nsAString & aNewItem,nsAString & aRetval,ErrorResult & aRv)122 void DOMSVGStringList::Initialize(const nsAString& aNewItem, nsAString& aRetval,
123 ErrorResult& aRv) {
124 if (InternalList().IsExplicitlySet()) {
125 InternalList().Clear();
126 }
127 InsertItemBefore(aNewItem, 0, aRetval, aRv);
128 }
129
GetItem(uint32_t aIndex,nsAString & aRetval,ErrorResult & aRv)130 void DOMSVGStringList::GetItem(uint32_t aIndex, nsAString& aRetval,
131 ErrorResult& aRv) {
132 bool found;
133 IndexedGetter(aIndex, found, aRetval);
134 if (!found) {
135 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
136 }
137 }
138
IndexedGetter(uint32_t aIndex,bool & aFound,nsAString & aRetval)139 void DOMSVGStringList::IndexedGetter(uint32_t aIndex, bool& aFound,
140 nsAString& aRetval) {
141 aFound = aIndex < InternalList().Length();
142 if (aFound) {
143 aRetval = InternalList()[aIndex];
144 }
145 }
146
InsertItemBefore(const nsAString & aNewItem,uint32_t aIndex,nsAString & aRetval,ErrorResult & aRv)147 void DOMSVGStringList::InsertItemBefore(const nsAString& aNewItem,
148 uint32_t aIndex, nsAString& aRetval,
149 ErrorResult& aRv) {
150 if (aNewItem.IsEmpty()) {
151 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
152 return;
153 }
154 aIndex = std::min(aIndex, InternalList().Length());
155
156 // Ensure we have enough memory so we can avoid complex error handling below:
157 if (!InternalList().SetCapacity(InternalList().Length() + 1)) {
158 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
159 return;
160 }
161
162 AutoChangeStringListNotifier notifier(this);
163 InternalList().InsertItem(aIndex, aNewItem);
164 aRetval = aNewItem;
165 }
166
ReplaceItem(const nsAString & aNewItem,uint32_t aIndex,nsAString & aRetval,ErrorResult & aRv)167 void DOMSVGStringList::ReplaceItem(const nsAString& aNewItem, uint32_t aIndex,
168 nsAString& aRetval, ErrorResult& aRv) {
169 if (aNewItem.IsEmpty()) {
170 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
171 return;
172 }
173 if (aIndex >= InternalList().Length()) {
174 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
175 return;
176 }
177
178 aRetval = InternalList()[aIndex];
179 AutoChangeStringListNotifier notifier(this);
180 InternalList().ReplaceItem(aIndex, aNewItem);
181 }
182
RemoveItem(uint32_t aIndex,nsAString & aRetval,ErrorResult & aRv)183 void DOMSVGStringList::RemoveItem(uint32_t aIndex, nsAString& aRetval,
184 ErrorResult& aRv) {
185 if (aIndex >= InternalList().Length()) {
186 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
187 return;
188 }
189
190 AutoChangeStringListNotifier notifier(this);
191 InternalList().RemoveItem(aIndex);
192 }
193
AppendItem(const nsAString & aNewItem,nsAString & aRetval,ErrorResult & aRv)194 void DOMSVGStringList::AppendItem(const nsAString& aNewItem, nsAString& aRetval,
195 ErrorResult& aRv) {
196 InsertItemBefore(aNewItem, InternalList().Length(), aRetval, aRv);
197 }
198
InternalList() const199 SVGStringList& DOMSVGStringList::InternalList() const {
200 if (mIsConditionalProcessingAttribute) {
201 nsCOMPtr<dom::SVGTests> tests = do_QueryObject(mElement);
202 return tests->mStringListAttributes[mAttrEnum];
203 }
204 return mElement->GetStringListInfo().mValues[mAttrEnum];
205 }
206
207 } // namespace dom
208 } // namespace mozilla
209