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