Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/ds/nsTObserverArray.h
Line
Count
Source (jump to first uncovered line)
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
#ifndef nsTObserverArray_h___
8
#define nsTObserverArray_h___
9
10
#include "mozilla/MemoryReporting.h"
11
#include "nsTArray.h"
12
#include "nsCycleCollectionNoteChild.h"
13
14
#include <functional>
15
16
/**
17
 * An array of observers. Like a normal array, but supports iterators that are
18
 * stable even if the array is modified during iteration.
19
 * The template parameter T is the observer type the array will hold;
20
 * N is the number of built-in storage slots that come with the array.
21
 * NOTE: You probably want to use nsTObserverArray, unless you specifically
22
 * want built-in storage. See below.
23
 * @see nsTObserverArray, nsTArray
24
 */
25
26
class nsTObserverArray_base
27
{
28
public:
29
  typedef size_t index_type;
30
  typedef size_t size_type;
31
  typedef ptrdiff_t diff_type;
32
33
protected:
34
  class Iterator_base
35
  {
36
  protected:
37
    friend class nsTObserverArray_base;
38
39
    Iterator_base(index_type aPosition, Iterator_base* aNext)
40
      : mPosition(aPosition)
41
      , mNext(aNext)
42
    {
43
    }
44
45
    // The current position of the iterator. Its exact meaning differs
46
    // depending on iterator. See nsTObserverArray<T>::ForwardIterator.
47
    index_type mPosition;
48
49
    // The next iterator currently iterating the same array
50
    Iterator_base* mNext;
51
  };
52
53
  nsTObserverArray_base() : mIterators(nullptr) {}
54
55
  ~nsTObserverArray_base()
56
  {
57
    NS_ASSERTION(mIterators == nullptr, "iterators outlasting array");
58
  }
59
60
  /**
61
   * Adjusts iterators after an element has been inserted or removed
62
   * from the array.
63
   * @param aModPos     Position where elements were added or removed.
64
   * @param aAdjustment -1 if an element was removed, 1 if an element was
65
   *                    added.
66
   */
67
  void AdjustIterators(index_type aModPos, diff_type aAdjustment);
68
69
  /**
70
   * Clears iterators when the array is destroyed.
71
   */
72
  void ClearIterators();
73
74
  mutable Iterator_base* mIterators;
75
};
76
77
template<class T, size_t N>
78
class nsAutoTObserverArray : protected nsTObserverArray_base
79
{
80
public:
81
  typedef T           elem_type;
82
  typedef nsTArray<T> array_type;
83
84
0
  nsAutoTObserverArray() {}
85
86
  //
87
  // Accessor methods
88
  //
89
90
  // @return The number of elements in the array.
91
0
  size_type Length() const { return mArray.Length(); }
Unexecuted instantiation: nsAutoTObserverArray<nsIMutationObserver*, 1ul>::Length() const
Unexecuted instantiation: nsAutoTObserverArray<nsMessageListenerInfo, 1ul>::Length() const
92
93
  // @return True if the array is empty or false otherwise.
94
0
  bool IsEmpty() const { return mArray.IsEmpty(); }
95
96
  // This method provides direct, readonly access to the array elements.
97
  // @return A pointer to the first element of the array.  If the array is
98
  // empty, then this pointer must not be dereferenced.
99
  const elem_type* Elements() const
100
  {
101
    return mArray.Elements();
102
  }
103
  elem_type* Elements()
104
0
  {
105
0
    return mArray.Elements();
106
0
  }
Unexecuted instantiation: nsAutoTObserverArray<nsIMutationObserver*, 1ul>::Elements()
Unexecuted instantiation: nsAutoTObserverArray<nsMessageListenerInfo, 1ul>::Elements()
107
108
  // This method provides direct access to an element of the array. The given
109
  // index must be within the array bounds. If the underlying array may change
110
  // during iteration, use an iterator instead of this function.
111
  // @param aIndex The index of an element in the array.
112
  // @return A reference to the i'th element of the array.
113
  elem_type& ElementAt(index_type aIndex)
114
0
  {
115
0
    return mArray.ElementAt(aIndex);
116
0
  }
117
118
  // Same as above, but readonly.
119
  const elem_type& ElementAt(index_type aIndex) const
120
  {
121
    return mArray.ElementAt(aIndex);
122
  }
123
124
  // This method provides direct access to an element of the array in a bounds
125
  // safe manner. If the requested index is out of bounds the provided default
126
  // value is returned.
127
  // @param aIndex The index of an element in the array.
128
  // @param aDef   The value to return if the index is out of bounds.
129
  elem_type& SafeElementAt(index_type aIndex, elem_type& aDef)
130
  {
131
    return mArray.SafeElementAt(aIndex, aDef);
132
  }
133
134
  // Same as above, but readonly.
135
  const elem_type& SafeElementAt(index_type aIndex, const elem_type& aDef) const
136
  {
137
    return mArray.SafeElementAt(aIndex, aDef);
138
  }
139
140
  // No operator[] is provided because the point of this class is to support
141
  // allow modifying the array during iteration, and ElementAt() is not safe
142
  // in those conditions.
143
144
  //
145
  // Search methods
146
  //
147
148
  // This method searches, starting from the beginning of the array,
149
  // for the first element in this array that is equal to the given element.
150
  // 'operator==' must be defined for elem_type.
151
  // @param aItem The item to search for.
152
  // @return      true if the element was found.
153
  template<class Item>
154
  bool Contains(const Item& aItem) const
155
  {
156
    return IndexOf(aItem) != array_type::NoIndex;
157
  }
158
159
  // This method searches for the offset of the first element in this
160
  // array that is equal to the given element.
161
  // 'operator==' must be defined for elem_type.
162
  // @param aItem  The item to search for.
163
  // @param aStart The index to start from.
164
  // @return The index of the found element or NoIndex if not found.
165
  template<class Item>
166
  index_type IndexOf(const Item& aItem, index_type aStart = 0) const
167
  {
168
    return mArray.IndexOf(aItem, aStart);
169
  }
170
171
  //
172
  // Mutation methods
173
  //
174
175
  // Insert a given element at the given index.
176
  // @param aIndex The index at which to insert item.
177
  // @param aItem  The item to insert,
178
  // @return A pointer to the newly inserted element, or a null on DOM
179
  template<class Item>
180
  elem_type* InsertElementAt(index_type aIndex, const Item& aItem)
181
  {
182
    elem_type* item = mArray.InsertElementAt(aIndex, aItem);
183
    AdjustIterators(aIndex, 1);
184
    return item;
185
  }
186
187
  // Same as above but without copy constructing.
188
  // This is useful to avoid temporaries.
189
  elem_type* InsertElementAt(index_type aIndex)
190
  {
191
    elem_type* item = mArray.InsertElementAt(aIndex);
192
    AdjustIterators(aIndex, 1);
193
    return item;
194
  }
195
196
  // Prepend an element to the array unless it already exists in the array.
197
  // 'operator==' must be defined for elem_type.
198
  // @param aItem The item to prepend.
199
  // @return true if the element was found, or inserted successfully.
200
  template<class Item>
201
  bool PrependElementUnlessExists(const Item& aItem)
202
  {
203
    if (Contains(aItem)) {
204
      return true;
205
    }
206
207
    bool inserted = mArray.InsertElementAt(0, aItem) != nullptr;
208
    AdjustIterators(0, 1);
209
    return inserted;
210
  }
211
212
  // Append an element to the array.
213
  // @param aItem The item to append.
214
  // @return A pointer to the newly appended element, or null on OOM.
215
  template<class Item>
216
  elem_type* AppendElement(const Item& aItem)
217
0
  {
218
0
    return mArray.AppendElement(aItem);
219
0
  }
220
221
  // Same as above, but without copy-constructing. This is useful to avoid
222
  // temporaries.
223
  elem_type* AppendElement()
224
0
  {
225
0
    return mArray.AppendElement();
226
0
  }
227
228
  // Append an element to the array unless it already exists in the array.
229
  // 'operator==' must be defined for elem_type.
230
  // @param aItem The item to append.
231
  // @return true if the element was found, or inserted successfully.
232
  template<class Item>
233
  bool AppendElementUnlessExists(const Item& aItem)
234
  {
235
    return Contains(aItem) || AppendElement(aItem) != nullptr;
236
  }
237
238
  // Remove an element from the array.
239
  // @param aIndex The index of the item to remove.
240
  void RemoveElementAt(index_type aIndex)
241
0
  {
242
0
    NS_ASSERTION(aIndex < mArray.Length(), "invalid index");
243
0
    mArray.RemoveElementAt(aIndex);
244
0
    AdjustIterators(aIndex, -1);
245
0
  }
246
247
  // This helper function combines IndexOf with RemoveElementAt to "search
248
  // and destroy" the first element that is equal to the given element.
249
  // 'operator==' must be defined for elem_type.
250
  // @param aItem The item to search for.
251
  // @return true if the element was found and removed.
252
  template<class Item>
253
  bool RemoveElement(const Item& aItem)
254
0
  {
255
0
    index_type index = mArray.IndexOf(aItem, 0);
256
0
    if (index == array_type::NoIndex) {
257
0
      return false;
258
0
    }
259
0
260
0
    mArray.RemoveElementAt(index);
261
0
    AdjustIterators(index, -1);
262
0
    return true;
263
0
  }
Unexecuted instantiation: bool nsAutoTObserverArray<nsIMutationObserver*, 1ul>::RemoveElement<nsIMutationObserver*>(nsIMutationObserver* const&)
Unexecuted instantiation: bool nsAutoTObserverArray<nsMessageListenerInfo, 1ul>::RemoveElement<nsMessageListenerInfo>(nsMessageListenerInfo const&)
264
265
  // See nsTArray::RemoveElementsBy.
266
  template <typename Predicate>
267
  void RemoveElementsBy(Predicate aPredicate)
268
  {
269
    index_type i = 0;
270
    mArray.RemoveElementsBy([&](const elem_type& aItem) {
271
      if (aPredicate(aItem)) {
272
        // This element is going to be removed.
273
        AdjustIterators(i, -1);
274
        return true;
275
      }
276
      ++i;
277
      return false;
278
    });
279
  }
280
281
  // Removes all observers and collapses all iterators to the beginning of
282
  // the array. The result is that forward iterators will see all elements
283
  // in the array.
284
  void Clear()
285
  {
286
    mArray.Clear();
287
    ClearIterators();
288
  }
289
290
  // Compact the array to minimize the memory it uses
291
  void Compact() { mArray.Compact(); }
292
293
  // Returns the number of bytes on the heap taken up by this object, not
294
  // including sizeof(*this). If you want to measure anything hanging off the
295
  // array, you must iterate over the elements and measure them individually;
296
  // hence the "Shallow" prefix.
297
  size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
298
  {
299
    return mArray.ShallowSizeOfExcludingThis(aMallocSizeOf);
300
  }
301
302
  //
303
  // Iterators
304
  //
305
306
  // Base class for iterators. Do not use this directly.
307
  class Iterator : public Iterator_base
308
  {
309
  protected:
310
    friend class nsAutoTObserverArray;
311
    typedef nsAutoTObserverArray<T, N> array_type;
312
313
    Iterator(index_type aPosition, const array_type& aArray)
314
      : Iterator_base(aPosition, aArray.mIterators)
315
      , mArray(const_cast<array_type&>(aArray))
316
0
    {
317
0
      aArray.mIterators = this;
318
0
    }
Unexecuted instantiation: nsAutoTObserverArray<nsMessageListenerInfo, 1ul>::Iterator::Iterator(unsigned long, nsAutoTObserverArray<nsMessageListenerInfo, 1ul> const&)
Unexecuted instantiation: nsAutoTObserverArray<nsIMutationObserver*, 1ul>::Iterator::Iterator(unsigned long, nsAutoTObserverArray<nsIMutationObserver*, 1ul> const&)
319
320
    ~Iterator()
321
0
    {
322
0
      NS_ASSERTION(mArray.mIterators == this,
323
0
                   "Iterators must currently be destroyed in opposite order "
324
0
                   "from the construction order. It is suggested that you "
325
0
                   "simply put them on the stack");
326
0
      mArray.mIterators = mNext;
327
0
    }
Unexecuted instantiation: nsAutoTObserverArray<nsMessageListenerInfo, 1ul>::Iterator::~Iterator()
Unexecuted instantiation: nsAutoTObserverArray<nsIMutationObserver*, 1ul>::Iterator::~Iterator()
328
329
    // The array we're iterating
330
    array_type& mArray;
331
  };
332
333
  // Iterates the array forward from beginning to end. mPosition points
334
  // to the element that will be returned on next call to GetNext.
335
  // Elements:
336
  // - prepended to the array during iteration *will not* be traversed
337
  // - appended during iteration *will* be traversed
338
  // - removed during iteration *will not* be traversed.
339
  // @see EndLimitedIterator
340
  class ForwardIterator : protected Iterator
341
  {
342
  public:
343
    typedef nsAutoTObserverArray<T, N> array_type;
344
    typedef Iterator                   base_type;
345
346
    explicit ForwardIterator(const array_type& aArray)
347
      : Iterator(0, aArray)
348
0
    {
349
0
    }
Unexecuted instantiation: nsAutoTObserverArray<nsMessageListenerInfo, 1ul>::ForwardIterator::ForwardIterator(nsAutoTObserverArray<nsMessageListenerInfo, 1ul> const&)
Unexecuted instantiation: nsAutoTObserverArray<nsIMutationObserver*, 1ul>::ForwardIterator::ForwardIterator(nsAutoTObserverArray<nsIMutationObserver*, 1ul> const&)
350
351
    ForwardIterator(const array_type& aArray, index_type aPos)
352
      : Iterator(aPos, aArray)
353
0
    {
354
0
    }
355
356
    bool operator<(const ForwardIterator& aOther) const
357
0
    {
358
0
      NS_ASSERTION(&this->mArray == &aOther.mArray,
359
0
                   "not iterating the same array");
360
0
      return base_type::mPosition < aOther.mPosition;
361
0
    }
362
363
    // Returns true if there are more elements to iterate.
364
    // This must precede a call to GetNext(). If false is
365
    // returned, GetNext() must not be called.
366
    bool HasMore() const
367
0
    {
368
0
      return base_type::mPosition < base_type::mArray.Length();
369
0
    }
370
371
    // Returns the next element and steps one step. This must
372
    // be preceded by a call to HasMore().
373
    // @return The next observer.
374
    elem_type& GetNext()
375
0
    {
376
0
      NS_ASSERTION(HasMore(), "iterating beyond end of array");
377
0
      return base_type::mArray.Elements()[base_type::mPosition++];
378
0
    }
379
  };
380
381
  // EndLimitedIterator works like ForwardIterator, but will not iterate new
382
  // observers appended to the array after the iterator was created.
383
  class EndLimitedIterator : protected ForwardIterator
384
  {
385
  public:
386
    typedef nsAutoTObserverArray<T, N> array_type;
387
    typedef Iterator                   base_type;
388
389
    explicit EndLimitedIterator(const array_type& aArray)
390
      : ForwardIterator(aArray)
391
      , mEnd(aArray, aArray.Length())
392
0
    {
393
0
    }
394
395
    // Returns true if there are more elements to iterate.
396
    // This must precede a call to GetNext(). If false is
397
    // returned, GetNext() must not be called.
398
0
    bool HasMore() const { return *this < mEnd; }
399
400
    // Returns the next element and steps one step. This must
401
    // be preceded by a call to HasMore().
402
    // @return The next observer.
403
    elem_type& GetNext()
404
0
    {
405
0
      NS_ASSERTION(HasMore(), "iterating beyond end of array");
406
0
      return base_type::mArray.Elements()[base_type::mPosition++];
407
0
    }
408
409
  private:
410
    ForwardIterator mEnd;
411
  };
412
413
  // Iterates the array backward from end to start. mPosition points
414
  // to the element that was returned last.
415
  // Elements:
416
  // - prepended to the array during iteration *will* be traversed,
417
  //   unless the iteration already arrived at the first element
418
  // - appended during iteration *will not* be traversed
419
  // - removed during iteration *will not* be traversed.
420
  class BackwardIterator : protected Iterator
421
  {
422
  public:
423
    typedef nsAutoTObserverArray<T, N> array_type;
424
    typedef Iterator                   base_type;
425
426
    explicit BackwardIterator(const array_type& aArray)
427
      : Iterator(aArray.Length(), aArray)
428
    {
429
    }
430
431
    // Returns true if there are more elements to iterate.
432
    // This must precede a call to GetNext(). If false is
433
    // returned, GetNext() must not be called.
434
    bool HasMore() const { return base_type::mPosition > 0; }
435
436
    // Returns the next element and steps one step. This must
437
    // be preceded by a call to HasMore().
438
    // @return The next observer.
439
    elem_type& GetNext()
440
    {
441
      NS_ASSERTION(HasMore(), "iterating beyond start of array");
442
      return base_type::mArray.Elements()[--base_type::mPosition];
443
    }
444
445
    // Removes the element at the current iterator position.
446
    // (the last element returned from |GetNext()|)
447
    // This will not affect the next call to |GetNext()|
448
    void Remove()
449
    {
450
      return base_type::mArray.RemoveElementAt(base_type::mPosition);
451
    }
452
  };
453
454
protected:
455
  AutoTArray<T, N> mArray;
456
};
457
458
template<class T>
459
class nsTObserverArray : public nsAutoTObserverArray<T, 0>
460
{
461
public:
462
  typedef nsAutoTObserverArray<T, 0>       base_type;
463
  typedef nsTObserverArray_base::size_type size_type;
464
465
  //
466
  // Initialization methods
467
  //
468
469
  nsTObserverArray() {}
470
471
  // Initialize this array and pre-allocate some number of elements.
472
  explicit nsTObserverArray(size_type aCapacity)
473
  {
474
    base_type::mArray.SetCapacity(aCapacity);
475
  }
476
};
477
478
template<typename T, size_t N>
479
inline void
480
ImplCycleCollectionUnlink(nsAutoTObserverArray<T, N>& aField)
481
{
482
  aField.Clear();
483
}
484
485
template<typename T, size_t N>
486
inline void
487
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
488
                            nsAutoTObserverArray<T, N>& aField,
489
                            const char* aName,
490
                            uint32_t aFlags = 0)
491
0
{
492
0
  aFlags |= CycleCollectionEdgeNameArrayFlag;
493
0
  size_t length = aField.Length();
494
0
  for (size_t i = 0; i < length; ++i) {
495
0
    ImplCycleCollectionTraverse(aCallback, aField.ElementAt(i), aName, aFlags);
496
0
  }
497
0
}
498
499
// XXXbz I wish I didn't have to pass in the observer type, but I
500
// don't see a way to get it out of array_.
501
// Note that this macro only works if the array holds pointers to XPCOM objects.
502
#define NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(array_, obstype_, func_, params_) \
503
  do {                                                                       \
504
    nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_);             \
505
    RefPtr<obstype_> obs_;                                                 \
506
    while (iter_.HasMore()) {                                                 \
507
      obs_ = iter_.GetNext();                                                \
508
      obs_ -> func_ params_ ;                                                \
509
    }                                                                        \
510
  } while(0)
511
512
// Note that this macro only works if the array holds pointers to XPCOM objects.
513
#define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(array_, obstype_, func_, params_) \
514
  do {                                                                       \
515
    nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_);             \
516
    obstype_* obs_;                                                          \
517
    while (iter_.HasMore()) {                                                \
518
      obs_ = iter_.GetNext();                                                \
519
      obs_ -> func_ params_ ;                                                \
520
    }                                                                        \
521
  } while(0)
522
523
// Note that this macro only works if the array holds pointers to XPCOM objects.
524
#define NS_OBSERVER_AUTO_ARRAY_NOTIFY_OBSERVERS(array_, obstype_, num_, func_, params_) \
525
0
  do {                                                                       \
526
0
    nsAutoTObserverArray<obstype_ *, num_>::ForwardIterator iter_(array_);   \
527
0
    obstype_* obs_;                                                          \
528
0
    while (iter_.HasMore()) {                                                \
529
0
      obs_ = iter_.GetNext();                                                \
530
0
      obs_ -> func_ params_ ;                                                \
531
0
    }                                                                        \
532
0
  } while(0)
533
534
#define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS_WITH_QI(array_, basetype_, obstype_, func_, params_) \
535
  do {                                                                       \
536
    nsTObserverArray<basetype_ *>::ForwardIterator iter_(array_);            \
537
    basetype_* obsbase_;                                                     \
538
    while (iter_.HasMore()) {                                                \
539
      obsbase_ = iter_.GetNext();                                            \
540
      nsCOMPtr<obstype_> obs_ = do_QueryInterface(obsbase_);                 \
541
      if (obs_) {                                                            \
542
        obs_ -> func_ params_ ;                                              \
543
      }                                                                      \
544
    }                                                                        \
545
  } while(0)
546
547
#define NS_OBSERVER_AUTO_ARRAY_NOTIFY_OBSERVERS_WITH_QI(array_, basetype_, num_, obstype_, func_, params_) \
548
  do {                                                                       \
549
    nsAutoTObserverArray<basetype_ *, num_>::ForwardIterator iter_(array_);  \
550
    basetype_* obsbase_;                                                     \
551
    while (iter_.HasMore()) {                                                \
552
      obsbase_ = iter_.GetNext();                                            \
553
      nsCOMPtr<obstype_> obs_ = do_QueryInterface(obsbase_);                 \
554
      if (obs_) {                                                            \
555
        obs_ -> func_ params_ ;                                              \
556
      }                                                                      \
557
    }                                                                        \
558
  } while(0)
559
#endif // nsTObserverArray_h___