DOMSVGStringList.cpp (6989B)
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 <algorithm> 10 11 #include "SVGAttrTearoffTable.h" 12 #include "mozAutoDocUpdate.h" 13 #include "mozilla/dom/SVGStringListBinding.h" 14 #include "mozilla/dom/SVGTests.h" 15 #include "nsCOMPtr.h" 16 #include "nsError.h" 17 #include "nsQueryObject.h" 18 19 // See the architecture comment in this file's header. 20 21 namespace mozilla::dom { 22 23 static inline SVGAttrTearoffTable<SVGStringList, DOMSVGStringList>& 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: 59 explicit AutoChangeStringListNotifier(DOMSVGStringList* aStringList) 60 : mozAutoDocUpdate(aStringList->mElement->GetComposedDoc(), true), 61 mStringList(aStringList) { 62 MOZ_ASSERT(mStringList, "Expecting non-null stringList"); 63 mStringList->mElement->WillChangeStringList( 64 mStringList->mIsConditionalProcessingAttribute, mStringList->mAttrEnum, 65 *this); 66 } 67 68 ~AutoChangeStringListNotifier() { 69 mStringList->mElement->DidChangeStringList( 70 mStringList->mIsConditionalProcessingAttribute, mStringList->mAttrEnum, 71 *this); 72 } 73 74 private: 75 DOMSVGStringList* const mStringList; 76 }; 77 78 /* static */ 79 already_AddRefed<DOMSVGStringList> DOMSVGStringList::GetDOMWrapper( 80 SVGStringList* aList, SVGElement* aElement, 81 bool aIsConditionalProcessingAttribute, uint8_t aAttrEnum) { 82 RefPtr<DOMSVGStringList> wrapper = 83 SVGStringListTearoffTable().GetTearoff(aList); 84 if (!wrapper) { 85 wrapper = new DOMSVGStringList(aElement, aIsConditionalProcessingAttribute, 86 aAttrEnum); 87 SVGStringListTearoffTable().AddTearoff(aList, wrapper); 88 } 89 return wrapper.forget(); 90 } 91 92 void DOMSVGStringList::RemoveFromTearoffTable() { 93 // Script no longer has any references to us. 94 if (mIsInTearoffTable) { 95 SVGStringListTearoffTable().RemoveTearoff(&InternalList()); 96 mIsInTearoffTable = false; 97 } 98 } 99 100 DOMSVGStringList::~DOMSVGStringList() { RemoveFromTearoffTable(); } 101 102 /* virtual */ 103 JSObject* DOMSVGStringList::WrapObject(JSContext* aCx, 104 JS::Handle<JSObject*> aGivenProto) { 105 return SVGStringList_Binding::Wrap(aCx, this, aGivenProto); 106 } 107 108 // ---------------------------------------------------------------------------- 109 // SVGStringList implementation: 110 111 uint32_t DOMSVGStringList::NumberOfItems() const { 112 return InternalList().Length(); 113 } 114 115 uint32_t DOMSVGStringList::Length() const { return NumberOfItems(); } 116 117 void DOMSVGStringList::Clear() { 118 if (InternalList().IsExplicitlySet()) { 119 AutoChangeStringListNotifier notifier(this); 120 InternalList().Clear(); 121 } 122 } 123 124 void DOMSVGStringList::Initialize(const nsAString& aNewItem, nsAString& aRetval, 125 ErrorResult& aRv) { 126 if (InternalList().IsExplicitlySet()) { 127 InternalList().Clear(); 128 } 129 InsertItemBefore(aNewItem, 0, aRetval, aRv); 130 } 131 132 void DOMSVGStringList::GetItem(uint32_t aIndex, nsAString& aRetval, 133 ErrorResult& aRv) { 134 bool found; 135 IndexedGetter(aIndex, found, aRetval); 136 if (!found) { 137 aRv.ThrowIndexSizeError("Index out of range"); 138 } 139 } 140 141 void DOMSVGStringList::IndexedGetter(uint32_t aIndex, bool& aFound, 142 nsAString& aRetval) { 143 aFound = aIndex < InternalList().Length(); 144 if (aFound) { 145 aRetval = InternalList()[aIndex]; 146 } 147 } 148 149 void DOMSVGStringList::InsertItemBefore(const nsAString& aNewItem, 150 uint32_t aIndex, nsAString& aRetval, 151 ErrorResult& aRv) { 152 if (aNewItem.IsEmpty()) { 153 aRv.ThrowSyntaxError("Cannot insert empty string"); 154 return; 155 } 156 aIndex = std::min(aIndex, InternalList().Length()); 157 158 // Ensure we have enough memory so we can avoid complex error handling below: 159 if (!InternalList().SetCapacity(InternalList().Length() + 1)) { 160 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 161 return; 162 } 163 164 AutoChangeStringListNotifier notifier(this); 165 InternalList().InsertItem(aIndex, aNewItem); 166 aRetval = aNewItem; 167 } 168 169 void DOMSVGStringList::ReplaceItem(const nsAString& aNewItem, uint32_t aIndex, 170 nsAString& aRetval, ErrorResult& aRv) { 171 if (aNewItem.IsEmpty()) { 172 aRv.ThrowSyntaxError("Cannot replace with empty string"); 173 return; 174 } 175 if (aIndex >= InternalList().Length()) { 176 aRv.ThrowIndexSizeError("Index out of range"); 177 return; 178 } 179 180 aRetval = InternalList()[aIndex]; 181 AutoChangeStringListNotifier notifier(this); 182 InternalList().ReplaceItem(aIndex, aNewItem); 183 } 184 185 void DOMSVGStringList::RemoveItem(uint32_t aIndex, nsAString& aRetval, 186 ErrorResult& aRv) { 187 if (aIndex >= InternalList().Length()) { 188 aRv.ThrowIndexSizeError("Index out of range"); 189 return; 190 } 191 192 AutoChangeStringListNotifier notifier(this); 193 InternalList().RemoveItem(aIndex); 194 } 195 196 void DOMSVGStringList::AppendItem(const nsAString& aNewItem, nsAString& aRetval, 197 ErrorResult& aRv) { 198 InsertItemBefore(aNewItem, InternalList().Length(), aRetval, aRv); 199 } 200 201 SVGStringList& DOMSVGStringList::InternalList() const { 202 if (mIsConditionalProcessingAttribute) { 203 nsCOMPtr<dom::SVGTests> tests = do_QueryObject(mElement); 204 return tests->mStringListAttributes[mAttrEnum]; 205 } 206 return mElement->GetStringListInfo().mValues[mAttrEnum]; 207 } 208 209 } // namespace mozilla::dom