Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef nsTObserverArray_h___
7 #define nsTObserverArray_h___
9 #include "mozilla/MemoryReporting.h"
10 #include "nsTArray.h"
11 #include "nsCycleCollectionNoteChild.h"
13 /**
14 * An array of observers. Like a normal array, but supports iterators that are
15 * stable even if the array is modified during iteration.
16 * The template parameter T is the observer type the array will hold;
17 * N is the number of built-in storage slots that come with the array.
18 * NOTE: You probably want to use nsTObserverArray, unless you specifically
19 * want built-in storage. See below.
20 * @see nsTObserverArray, nsTArray
21 */
23 class NS_COM_GLUE nsTObserverArray_base {
24 public:
25 typedef uint32_t index_type;
26 typedef uint32_t size_type;
27 typedef int32_t diff_type;
29 protected:
30 class Iterator_base {
31 protected:
32 friend class nsTObserverArray_base;
34 Iterator_base(index_type aPosition, Iterator_base* aNext)
35 : mPosition(aPosition),
36 mNext(aNext) {
37 }
39 // The current position of the iterator. Its exact meaning differs
40 // depending on iterator. See nsTObserverArray<T>::ForwardIterator.
41 index_type mPosition;
43 // The next iterator currently iterating the same array
44 Iterator_base* mNext;
45 };
47 nsTObserverArray_base()
48 : mIterators(nullptr) {
49 }
51 ~nsTObserverArray_base() {
52 NS_ASSERTION(mIterators == nullptr, "iterators outlasting array");
53 }
55 /**
56 * Adjusts iterators after an element has been inserted or removed
57 * from the array.
58 * @param modPos Position where elements were added or removed.
59 * @param adjustment -1 if an element was removed, 1 if an element was
60 * added.
61 */
62 void AdjustIterators(index_type aModPos, diff_type aAdjustment);
64 /**
65 * Clears iterators when the array is destroyed.
66 */
67 void ClearIterators();
69 mutable Iterator_base* mIterators;
70 };
72 template<class T, uint32_t N>
73 class nsAutoTObserverArray : protected nsTObserverArray_base {
74 public:
75 typedef T elem_type;
76 typedef nsTArray<T> array_type;
78 nsAutoTObserverArray() {
79 }
81 //
82 // Accessor methods
83 //
85 // @return The number of elements in the array.
86 size_type Length() const {
87 return mArray.Length();
88 }
90 // @return True if the array is empty or false otherwise.
91 bool IsEmpty() const {
92 return mArray.IsEmpty();
93 }
95 // This method provides direct access to the i'th element of the array.
96 // The given index must be within the array bounds. If the underlying array
97 // may change during iteration, use an iterator instead of this function.
98 // @param i The index of an element in the array.
99 // @return A reference to the i'th element of the array.
100 elem_type& ElementAt(index_type i) {
101 return mArray.ElementAt(i);
102 }
104 // Same as above, but readonly.
105 const elem_type& ElementAt(index_type i) const {
106 return mArray.ElementAt(i);
107 }
109 // This method provides direct access to the i'th element of the array in
110 // a bounds safe manner. If the requested index is out of bounds the
111 // provided default value is returned.
112 // @param i The index of an element in the array.
113 // @param def The value to return if the index is out of bounds.
114 elem_type& SafeElementAt(index_type i, elem_type& def) {
115 return mArray.SafeElementAt(i, def);
116 }
118 // Same as above, but readonly.
119 const elem_type& SafeElementAt(index_type i, const elem_type& def) const {
120 return mArray.SafeElementAt(i, def);
121 }
123 // No operator[] is provided because the point of this class is to support
124 // allow modifying the array during iteration, and ElementAt() is not safe
125 // in those conditions.
127 //
128 // Search methods
129 //
131 // This method searches, starting from the beginning of the array,
132 // for the first element in this array that is equal to the given element.
133 // 'operator==' must be defined for elem_type.
134 // @param item The item to search for.
135 // @return true if the element was found.
136 template<class Item>
137 bool Contains(const Item& item) const {
138 return IndexOf(item) != array_type::NoIndex;
139 }
141 // This method searches for the offset of the first element in this
142 // array that is equal to the given element.
143 // 'operator==' must be defined for elem_type.
144 // @param item The item to search for.
145 // @param start The index to start from.
146 // @return The index of the found element or NoIndex if not found.
147 template<class Item>
148 index_type IndexOf(const Item& item, index_type start = 0) const {
149 return mArray.IndexOf(item, start);
150 }
152 //
153 // Mutation methods
154 //
156 // Insert a given element at the given index.
157 // @param index The index at which to insert item.
158 // @param item The item to insert,
159 // @return A pointer to the newly inserted element, or a null on DOM
160 template<class Item>
161 elem_type *InsertElementAt(index_type aIndex, const Item& aItem) {
162 elem_type* item = mArray.InsertElementAt(aIndex, aItem);
163 AdjustIterators(aIndex, 1);
164 return item;
165 }
167 // Same as above but without copy constructing.
168 // This is useful to avoid temporaries.
169 elem_type* InsertElementAt(index_type aIndex) {
170 elem_type* item = mArray.InsertElementAt(aIndex);
171 AdjustIterators(aIndex, 1);
172 return item;
173 }
175 // Prepend an element to the array unless it already exists in the array.
176 // 'operator==' must be defined for elem_type.
177 // @param item The item to prepend.
178 // @return true if the element was found, or inserted successfully.
179 template<class Item>
180 bool PrependElementUnlessExists(const Item& item) {
181 if (Contains(item)) {
182 return true;
183 }
185 bool inserted = mArray.InsertElementAt(0, item) != nullptr;
186 AdjustIterators(0, 1);
187 return inserted;
188 }
190 // Append an element to the array.
191 // @param item The item to append.
192 // @return A pointer to the newly appended element, or null on OOM.
193 template<class Item>
194 elem_type* AppendElement(const Item& item) {
195 return mArray.AppendElement(item);
196 }
198 // Same as above, but without copy-constructing. This is useful to avoid
199 // temporaries.
200 elem_type* AppendElement() {
201 return mArray.AppendElement();
202 }
204 // Append an element to the array unless it already exists in the array.
205 // 'operator==' must be defined for elem_type.
206 // @param item The item to append.
207 // @return true if the element was found, or inserted successfully.
208 template<class Item>
209 bool AppendElementUnlessExists(const Item& item) {
210 return Contains(item) || AppendElement(item) != nullptr;
211 }
213 // Remove an element from the array.
214 // @param index The index of the item to remove.
215 void RemoveElementAt(index_type index) {
216 NS_ASSERTION(index < mArray.Length(), "invalid index");
217 mArray.RemoveElementAt(index);
218 AdjustIterators(index, -1);
219 }
221 // This helper function combines IndexOf with RemoveElementAt to "search
222 // and destroy" the first element that is equal to the given element.
223 // 'operator==' must be defined for elem_type.
224 // @param item The item to search for.
225 // @return true if the element was found and removed.
226 template<class Item>
227 bool RemoveElement(const Item& item) {
228 index_type index = mArray.IndexOf(item, 0);
229 if (index == array_type::NoIndex)
230 return false;
232 mArray.RemoveElementAt(index);
233 AdjustIterators(index, -1);
234 return true;
235 }
237 // Removes all observers and collapses all iterators to the beginning of
238 // the array. The result is that forward iterators will see all elements
239 // in the array.
240 void Clear() {
241 mArray.Clear();
242 ClearIterators();
243 }
245 // Returns the number of bytes on the heap taken up by this object, not
246 // including sizeof(*this).
247 size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
248 return mArray.SizeOfExcludingThis(mallocSizeOf);
249 }
251 //
252 // Iterators
253 //
255 // Base class for iterators. Do not use this directly.
256 class Iterator : public Iterator_base {
257 protected:
258 friend class nsAutoTObserverArray;
259 typedef nsAutoTObserverArray<T, N> array_type;
261 Iterator(index_type aPosition, const array_type& aArray)
262 : Iterator_base(aPosition, aArray.mIterators),
263 mArray(const_cast<array_type&>(aArray)) {
264 aArray.mIterators = this;
265 }
267 ~Iterator() {
268 NS_ASSERTION(mArray.mIterators == this,
269 "Iterators must currently be destroyed in opposite order "
270 "from the construction order. It is suggested that you "
271 "simply put them on the stack");
272 mArray.mIterators = mNext;
273 }
275 // The array we're iterating
276 array_type& mArray;
277 };
279 // Iterates the array forward from beginning to end. mPosition points
280 // to the element that will be returned on next call to GetNext.
281 // Elements:
282 // - prepended to the array during iteration *will not* be traversed
283 // - appended during iteration *will* be traversed
284 // - removed during iteration *will not* be traversed.
285 // @see EndLimitedIterator
286 class ForwardIterator : protected Iterator {
287 public:
288 typedef nsAutoTObserverArray<T, N> array_type;
289 typedef Iterator base_type;
291 ForwardIterator(const array_type& aArray)
292 : Iterator(0, aArray) {
293 }
295 ForwardIterator(const array_type& aArray, index_type aPos)
296 : Iterator(aPos, aArray) {
297 }
299 bool operator <(const ForwardIterator& aOther) const {
300 NS_ASSERTION(&this->mArray == &aOther.mArray,
301 "not iterating the same array");
302 return base_type::mPosition < aOther.mPosition;
303 }
305 // Returns true if there are more elements to iterate.
306 // This must precede a call to GetNext(). If false is
307 // returned, GetNext() must not be called.
308 bool HasMore() const {
309 return base_type::mPosition < base_type::mArray.Length();
310 }
312 // Returns the next element and steps one step. This must
313 // be preceded by a call to HasMore().
314 // @return The next observer.
315 elem_type& GetNext() {
316 NS_ASSERTION(HasMore(), "iterating beyond end of array");
317 return base_type::mArray.ElementAt(base_type::mPosition++);
318 }
319 };
321 // EndLimitedIterator works like ForwardIterator, but will not iterate new
322 // observers appended to the array after the iterator was created.
323 class EndLimitedIterator : protected ForwardIterator {
324 public:
325 typedef nsAutoTObserverArray<T, N> array_type;
326 typedef Iterator base_type;
328 EndLimitedIterator(const array_type& aArray)
329 : ForwardIterator(aArray),
330 mEnd(aArray, aArray.Length()) {
331 }
333 // Returns true if there are more elements to iterate.
334 // This must precede a call to GetNext(). If false is
335 // returned, GetNext() must not be called.
336 bool HasMore() const {
337 return *this < mEnd;
338 }
340 // Returns the next element and steps one step. This must
341 // be preceded by a call to HasMore().
342 // @return The next observer.
343 elem_type& GetNext() {
344 NS_ASSERTION(HasMore(), "iterating beyond end of array");
345 return base_type::mArray.ElementAt(base_type::mPosition++);
346 }
348 private:
349 ForwardIterator mEnd;
350 };
352 protected:
353 nsAutoTArray<T, N> mArray;
354 };
356 template<class T>
357 class nsTObserverArray : public nsAutoTObserverArray<T, 0> {
358 public:
359 typedef nsAutoTObserverArray<T, 0> base_type;
360 typedef nsTObserverArray_base::size_type size_type;
362 //
363 // Initialization methods
364 //
366 nsTObserverArray() {}
368 // Initialize this array and pre-allocate some number of elements.
369 explicit nsTObserverArray(size_type capacity) {
370 base_type::mArray.SetCapacity(capacity);
371 }
372 };
374 template <typename T, uint32_t N>
375 inline void
376 ImplCycleCollectionUnlink(nsAutoTObserverArray<T, N>& aField)
377 {
378 aField.Clear();
379 }
381 template <typename T, uint32_t N>
382 inline void
383 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
384 nsAutoTObserverArray<T, N>& aField,
385 const char* aName,
386 uint32_t aFlags = 0)
387 {
388 aFlags |= CycleCollectionEdgeNameArrayFlag;
389 uint32_t length = aField.Length();
390 for (uint32_t i = 0; i < length; ++i) {
391 ImplCycleCollectionTraverse(aCallback, aField.ElementAt(i), aName, aFlags);
392 }
393 }
395 // XXXbz I wish I didn't have to pass in the observer type, but I
396 // don't see a way to get it out of array_.
397 // Note that this macro only works if the array holds pointers to XPCOM objects.
398 #define NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(array_, obstype_, func_, params_) \
399 PR_BEGIN_MACRO \
400 nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_); \
401 nsRefPtr<obstype_> obs_; \
402 while (iter_.HasMore()) { \
403 obs_ = iter_.GetNext(); \
404 obs_ -> func_ params_ ; \
405 } \
406 PR_END_MACRO
408 // Note that this macro only works if the array holds pointers to XPCOM objects.
409 #define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(array_, obstype_, func_, params_) \
410 PR_BEGIN_MACRO \
411 nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_); \
412 obstype_* obs_; \
413 while (iter_.HasMore()) { \
414 obs_ = iter_.GetNext(); \
415 obs_ -> func_ params_ ; \
416 } \
417 PR_END_MACRO
418 #endif // nsTObserverArray_h___