Line data Source code
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 16 : class MOZ_STACK_CLASS OrderedTimeoutIterator final {
20 : public:
21 : typedef TimeoutManager::Timeouts Timeouts;
22 : typedef Timeouts::TimeoutList TimeoutList;
23 :
24 16 : OrderedTimeoutIterator(Timeouts& aNormalTimeouts,
25 : Timeouts& aTrackingTimeouts)
26 16 : : mNormalTimeouts(aNormalTimeouts.mTimeoutList),
27 : mTrackingTimeouts(aTrackingTimeouts.mTimeoutList),
28 16 : mNormalIter(mNormalTimeouts.getFirst()),
29 16 : mTrackingIter(mTrackingTimeouts.getFirst()),
30 : mKind(Kind::None),
31 48 : mUpdateIteratorCalled(true)
32 : {
33 16 : }
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 37 : Timeout* Next()
39 : {
40 37 : MOZ_ASSERT(mUpdateIteratorCalled);
41 37 : MOZ_ASSERT_IF(mNormalIter, mNormalIter->isInList());
42 37 : MOZ_ASSERT_IF(mTrackingIter, mTrackingIter->isInList());
43 :
44 37 : mUpdateIteratorCalled = false;
45 37 : mKind = Kind::None;
46 37 : Timeout* timeout = nullptr;
47 37 : if (!mNormalIter) {
48 0 : if (!mTrackingIter) {
49 : // We have reached the end of both lists. Bail out!
50 0 : return nullptr;
51 : } else {
52 : // We have reached the end of the normal timeout list, select the next
53 : // tracking timeout.
54 0 : timeout = mTrackingIter;
55 0 : mKind = Kind::Tracking;
56 : }
57 37 : } else if (!mTrackingIter) {
58 : // We have reached the end of the tracking timeout list, select the next
59 : // normal timeout.
60 37 : timeout = mNormalIter;
61 37 : mKind = Kind::Normal;
62 : } else {
63 : // If we have a normal and a tracking timer, return the one with the
64 : // smaller mWhen (and prefer the timeout with a lower ID in case they are
65 : // equal.) Otherwise, return whichever iterator has an item left,
66 : // preferring a non-tracking timeout again. Note that in practice, even
67 : // if a web page calls setTimeout() twice in a row, it should get
68 : // different mWhen values, so in practice we shouldn't fall back to
69 : // 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 : }
83 : }
84 37 : if (!timeout) {
85 : // We didn't find any suitable iterator. This can happen for example
86 : // when getNext() in UpdateIterator() returns nullptr and then Next()
87 : // gets called. Bail out!
88 0 : return nullptr;
89 : }
90 :
91 37 : MOZ_ASSERT(mKind != Kind::None);
92 :
93 : // Record the current timeout we just found.
94 37 : mCurrent = timeout;
95 37 : MOZ_ASSERT(mCurrent);
96 :
97 37 : return mCurrent;
98 : }
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 32 : void UpdateIterator()
105 : {
106 32 : MOZ_ASSERT(mKind != Kind::None);
107 : // Update the winning iterator to point to the next element. Also check to
108 : // see if the other iterator is still valid, otherwise reset it to the
109 : // beginning of the list. This is needed in case a timeout handler removes
110 : // the timeout pointed to from one of our iterators.
111 32 : if (mKind == Kind::Normal) {
112 32 : mNormalIter = mCurrent->getNext();
113 32 : if (mTrackingIter && !mTrackingIter->isInList()) {
114 0 : mTrackingIter = mTrackingTimeouts.getFirst();
115 : }
116 : } else {
117 0 : mTrackingIter = mCurrent->getNext();
118 0 : if (mNormalIter && !mNormalIter->isInList()) {
119 0 : mNormalIter = mNormalTimeouts.getFirst();
120 : }
121 : }
122 :
123 32 : mUpdateIteratorCalled = true;
124 32 : }
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 0 : void Clear()
130 : {
131 : // 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 : {
145 : MOZ_ASSERT(mKind != Kind::None);
146 : return mKind == Kind::Normal;
147 : }
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 0 : bool PickedTrackingIter() const
154 : {
155 0 : MOZ_ASSERT(mKind != Kind::None);
156 0 : return mKind == Kind::Tracking;
157 : }
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
|