DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (0bb0f14672fd)

VCS Links

Kind

OrderedTimeoutIterator

Macros

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_dom_OrderedTimeoutIterator_h__
#define mozilla_dom_OrderedTimeoutIterator_h__

#include "mozilla/RefPtr.h"
#include "mozilla/dom/Timeout.h"
#include "mozilla/dom/TimeoutManager.h"

namespace mozilla {
namespace dom {

// This class implements and iterator which iterates the normal and tracking
// timeouts lists simultaneously in the mWhen order.
class MOZ_STACK_CLASS OrderedTimeoutIterator final {
public:
  typedef TimeoutManager::Timeouts Timeouts;
  typedef Timeouts::TimeoutList    TimeoutList;

  OrderedTimeoutIterator(Timeouts& aNormalTimeouts,
                         Timeouts& aTrackingTimeouts)
    : mNormalTimeouts(aNormalTimeouts.mTimeoutList),
      mTrackingTimeouts(aTrackingTimeouts.mTimeoutList),
      mNormalIter(mNormalTimeouts.getFirst()),
      mTrackingIter(mTrackingTimeouts.getFirst()),
      mKind(Kind::None),
      mUpdateIteratorCalled(true)
  {
  }

  // Return the current timeout and move to the next one.
  // Unless this is the first time calling Next(), you must call
  // UpdateIterator() before calling this method.
  Timeout* Next()
  {
    MOZ_ASSERT(mUpdateIteratorCalled);
    MOZ_ASSERT_IF(mNormalIter, mNormalIter->isInList());
    MOZ_ASSERT_IF(mTrackingIter, mTrackingIter->isInList());

    mUpdateIteratorCalled = false;
    mKind = Kind::None;
    Timeout* timeout = nullptr;
    if (!mNormalIter) {
      if (!mTrackingIter) {
        // We have reached the end of both lists.  Bail out!
        return nullptr;
      } else {
        // We have reached the end of the normal timeout list, select the next
        // tracking timeout.
        timeout = mTrackingIter;
        mKind = Kind::Tracking;
      }
    } else if (!mTrackingIter) {
      // We have reached the end of the tracking timeout list, select the next
      // normal timeout.
      timeout = mNormalIter;
      mKind = Kind::Normal;
    } else {
      // If we have a normal and a tracking timer, return the one with the
      // smaller mWhen (and prefer the timeout with a lower ID in case they are
      // equal.) Otherwise, return whichever iterator has an item left,
      // preferring a non-tracking timeout again.  Note that in practice, even
      // if a web page calls setTimeout() twice in a row, it should get
      // different mWhen values, so in practice we shouldn't fall back to
      // comparing timeout IDs.
      if (mNormalIter && mTrackingIter &&
          (mTrackingIter->When() < mNormalIter->When() ||
           (mTrackingIter->When() == mNormalIter->When() &&
            mTrackingIter->mTimeoutId < mNormalIter->mTimeoutId))) {
        timeout = mTrackingIter;
        mKind = Kind::Tracking;
      } else if (mNormalIter) {
        timeout = mNormalIter;
        mKind = Kind::Normal;
      } else if (mTrackingIter) {
        timeout = mTrackingIter;
        mKind = Kind::Tracking;
      }
    }
    if (!timeout) {
      // We didn't find any suitable iterator.  This can happen for example
      // when getNext() in UpdateIterator() returns nullptr and then Next()
      // gets called.  Bail out!
      return nullptr;
    }

    MOZ_ASSERT(mKind != Kind::None);

    // Record the current timeout we just found.
    mCurrent = timeout;
    MOZ_ASSERT(mCurrent);

    return mCurrent;
  }

  // Prepare the iterator for the next call to Next().
  // This method can be called as many times as needed.  Calling this more than
  // once is helpful in cases where we expect the timeouts list has been
  // modified before we got a chance to call Next().
  void UpdateIterator()
  {
    MOZ_ASSERT(mKind != Kind::None);
    // Update the winning iterator to point to the next element.  Also check to
    // see if the other iterator is still valid, otherwise reset it to the
    // beginning of the list.  This is needed in case a timeout handler removes
    // the timeout pointed to from one of our iterators.
    if (mKind == Kind::Normal) {
      mNormalIter = mCurrent->getNext();
      if (mTrackingIter && !mTrackingIter->isInList()) {
        mTrackingIter = mTrackingTimeouts.getFirst();
      }
    } else {
      mTrackingIter = mCurrent->getNext();
      if (mNormalIter && !mNormalIter->isInList()) {
        mNormalIter = mNormalTimeouts.getFirst();
      }
    }

    mUpdateIteratorCalled = true;
  }

  // This function resets the iterator to a defunct state.  It should only be
  // used when we want to forcefully sever all of the strong references this
  // class holds.
  void Clear()
  {
    // Release all strong references.
    mNormalIter = nullptr;
    mTrackingIter = nullptr;
    mCurrent = nullptr;
    mKind = Kind::None;
    mUpdateIteratorCalled = true;
  }

  // Returns true if the previous call to Next() picked a normal timeout.
  // Cannot be called before Next() has been called.  Note that the result of
  // this method is only affected by Next() and not UpdateIterator(), so calling
  // UpdateIterator() before calling this is allowed.
  bool PickedNormalIter() const
  {
    MOZ_ASSERT(mKind != Kind::None);
    return mKind == Kind::Normal;
  }

  // Returns true if the previous call to Next() picked a tracking timeout.
  // Cannot be called before Next() has been called.  Note that the result of
  // this method is only affected by Next() and not UpdateIterator(), so calling
  // UpdateIterator() before calling this is allowed.
  bool PickedTrackingIter() const
  {
    MOZ_ASSERT(mKind != Kind::None);
    return mKind == Kind::Tracking;
  }

private:
  TimeoutList& mNormalTimeouts;          // The list of normal timeouts.
  TimeoutList& mTrackingTimeouts;        // The list of tracking timeouts.
  RefPtr<Timeout> mNormalIter;           // The iterator over the normal timeout list.
  RefPtr<Timeout> mTrackingIter;         // The iterator over the tracking timeout list.
  RefPtr<Timeout> mCurrent;              // The current timeout that Next() just found.
  enum class Kind { Normal, Tracking, None };
  Kind mKind;                            // The kind of iterator picked the last time.
  DebugOnly<bool> mUpdateIteratorCalled; // Whether we have called UpdateIterator() before calling Next().
};

}
}

#endif