/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 |