Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/OrderedTimeoutIterator.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 mozilla_dom_OrderedTimeoutIterator_h__
8
#define mozilla_dom_OrderedTimeoutIterator_h__
9
10
#include "mozilla/RefPtr.h"
11
#include "mozilla/dom/Timeout.h"
12
#include "mozilla/dom/TimeoutManager.h"
13
14
namespace mozilla {
15
namespace dom {
16
17
// This class implements and iterator which iterates the normal and tracking
18
// timeouts lists simultaneously in the mWhen order.
19
class MOZ_STACK_CLASS OrderedTimeoutIterator final {
20
public:
21
  typedef TimeoutManager::Timeouts Timeouts;
22
  typedef Timeouts::TimeoutList    TimeoutList;
23
24
  OrderedTimeoutIterator(Timeouts& aNormalTimeouts,
25
                         Timeouts& aTrackingTimeouts)
26
    : mNormalTimeouts(aNormalTimeouts.mTimeoutList),
27
      mTrackingTimeouts(aTrackingTimeouts.mTimeoutList),
28
      mNormalIter(mNormalTimeouts.getFirst()),
29
      mTrackingIter(mTrackingTimeouts.getFirst()),
30
      mKind(Kind::None),
31
      mUpdateIteratorCalled(true)
32
0
  {
33
0
  }
34
35
  // Return the current timeout and move to the next one.
36
  // Unless this is the first time calling Next(), you must call
37
  // UpdateIterator() before calling this method.
38
  Timeout* Next()
39
0
  {
40
0
    MOZ_ASSERT(mUpdateIteratorCalled);
41
0
    MOZ_ASSERT_IF(mNormalIter, mNormalIter->isInList());
42
0
    MOZ_ASSERT_IF(mTrackingIter, mTrackingIter->isInList());
43
0
44
0
    mUpdateIteratorCalled = false;
45
0
    mKind = Kind::None;
46
0
    Timeout* timeout = nullptr;
47
0
    if (!mNormalIter) {
48
0
      if (!mTrackingIter) {
49
0
        // We have reached the end of both lists.  Bail out!
50
0
        return nullptr;
51
0
      } else {
52
0
        // We have reached the end of the normal timeout list, select the next
53
0
        // tracking timeout.
54
0
        timeout = mTrackingIter;
55
0
        mKind = Kind::Tracking;
56
0
      }
57
0
    } else if (!mTrackingIter) {
58
0
      // We have reached the end of the tracking timeout list, select the next
59
0
      // normal timeout.
60
0
      timeout = mNormalIter;
61
0
      mKind = Kind::Normal;
62
0
    } else {
63
0
      // If we have a normal and a tracking timer, return the one with the
64
0
      // smaller mWhen (and prefer the timeout with a lower ID in case they are
65
0
      // equal.) Otherwise, return whichever iterator has an item left,
66
0
      // preferring a non-tracking timeout again.  Note that in practice, even
67
0
      // if a web page calls setTimeout() twice in a row, it should get
68
0
      // different mWhen values, so in practice we shouldn't fall back to
69
0
      // comparing timeout IDs.
70
0
      if (mNormalIter && mTrackingIter &&
71
0
          (mTrackingIter->When() < mNormalIter->When() ||
72
0
           (mTrackingIter->When() == mNormalIter->When() &&
73
0
            mTrackingIter->mTimeoutId < mNormalIter->mTimeoutId))) {
74
0
        timeout = mTrackingIter;
75
0
        mKind = Kind::Tracking;
76
0
      } else if (mNormalIter) {
77
0
        timeout = mNormalIter;
78
0
        mKind = Kind::Normal;
79
0
      } else if (mTrackingIter) {
80
0
        timeout = mTrackingIter;
81
0
        mKind = Kind::Tracking;
82
0
      }
83
0
    }
84
0
    if (!timeout) {
85
0
      // We didn't find any suitable iterator.  This can happen for example
86
0
      // when getNext() in UpdateIterator() returns nullptr and then Next()
87
0
      // gets called.  Bail out!
88
0
      return nullptr;
89
0
    }
90
0
91
0
    MOZ_ASSERT(mKind != Kind::None);
92
0
93
0
    // Record the current timeout we just found.
94
0
    mCurrent = timeout;
95
0
    MOZ_ASSERT(mCurrent);
96
0
97
0
    return mCurrent;
98
0
  }
99
100
  // Prepare the iterator for the next call to Next().
101
  // This method can be called as many times as needed.  Calling this more than
102
  // once is helpful in cases where we expect the timeouts list has been
103
  // modified before we got a chance to call Next().
104
  void UpdateIterator()
105
0
  {
106
0
    MOZ_ASSERT(mKind != Kind::None);
107
0
    // Update the winning iterator to point to the next element.  Also check to
108
0
    // see if the other iterator is still valid, otherwise reset it to the
109
0
    // beginning of the list.  This is needed in case a timeout handler removes
110
0
    // the timeout pointed to from one of our iterators.
111
0
    if (mKind == Kind::Normal) {
112
0
      mNormalIter = mCurrent->getNext();
113
0
      if (mTrackingIter && !mTrackingIter->isInList()) {
114
0
        mTrackingIter = mTrackingTimeouts.getFirst();
115
0
      }
116
0
    } else {
117
0
      mTrackingIter = mCurrent->getNext();
118
0
      if (mNormalIter && !mNormalIter->isInList()) {
119
0
        mNormalIter = mNormalTimeouts.getFirst();
120
0
      }
121
0
    }
122
0
123
0
    mUpdateIteratorCalled = true;
124
0
  }
125
126
  // This function resets the iterator to a defunct state.  It should only be
127
  // used when we want to forcefully sever all of the strong references this
128
  // class holds.
129
  void Clear()
130
0
  {
131
0
    // Release all strong references.
132
0
    mNormalIter = nullptr;
133
0
    mTrackingIter = nullptr;
134
0
    mCurrent = nullptr;
135
0
    mKind = Kind::None;
136
0
    mUpdateIteratorCalled = true;
137
0
  }
138
139
  // Returns true if the previous call to Next() picked a normal timeout.
140
  // Cannot be called before Next() has been called.  Note that the result of
141
  // this method is only affected by Next() and not UpdateIterator(), so calling
142
  // UpdateIterator() before calling this is allowed.
143
  bool PickedNormalIter() const
144
0
  {
145
0
    MOZ_ASSERT(mKind != Kind::None);
146
0
    return mKind == Kind::Normal;
147
0
  }
148
149
  // Returns true if the previous call to Next() picked a tracking timeout.
150
  // Cannot be called before Next() has been called.  Note that the result of
151
  // this method is only affected by Next() and not UpdateIterator(), so calling
152
  // UpdateIterator() before calling this is allowed.
153
  bool PickedTrackingIter() const
154
0
  {
155
0
    MOZ_ASSERT(mKind != Kind::None);
156
0
    return mKind == Kind::Tracking;
157
0
  }
158
159
private:
160
  TimeoutList& mNormalTimeouts;          // The list of normal timeouts.
161
  TimeoutList& mTrackingTimeouts;        // The list of tracking timeouts.
162
  RefPtr<Timeout> mNormalIter;           // The iterator over the normal timeout list.
163
  RefPtr<Timeout> mTrackingIter;         // The iterator over the tracking timeout list.
164
  RefPtr<Timeout> mCurrent;              // The current timeout that Next() just found.
165
  enum class Kind { Normal, Tracking, None };
166
  Kind mKind;                            // The kind of iterator picked the last time.
167
  DebugOnly<bool> mUpdateIteratorCalled; // Whether we have called UpdateIterator() before calling Next().
168
};
169
170
}
171
}
172
173
#endif