diff options
Diffstat (limited to 'dom/base/TimeoutManager.h')
-rw-r--r-- | dom/base/TimeoutManager.h | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/dom/base/TimeoutManager.h b/dom/base/TimeoutManager.h new file mode 100644 index 0000000000..fd04a1b215 --- /dev/null +++ b/dom/base/TimeoutManager.h @@ -0,0 +1,250 @@ +/* -*- 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_TimeoutManager_h__ +#define mozilla_dom_TimeoutManager_h__ + +#include "mozilla/dom/Timeout.h" +#include "nsTArray.h" + +class nsIEventTarget; +class nsITimer; +class nsGlobalWindowInner; + +namespace mozilla { + +namespace dom { + +class TimeoutExecutor; +class TimeoutHandler; + +// This class manages the timeouts in a Window's setTimeout/setInterval pool. +class TimeoutManager final { + private: + struct Timeouts; + + public: + TimeoutManager(nsGlobalWindowInner& aWindow, uint32_t aMaxIdleDeferMS); + ~TimeoutManager(); + TimeoutManager(const TimeoutManager& rhs) = delete; + void operator=(const TimeoutManager& rhs) = delete; + + bool IsRunningTimeout() const; + + static uint32_t GetNestingLevel() { return sNestingLevel; } + static void SetNestingLevel(uint32_t aLevel) { sNestingLevel = aLevel; } + + bool HasTimeouts() const { + return !mTimeouts.IsEmpty() || !mIdleTimeouts.IsEmpty(); + } + + nsresult SetTimeout(TimeoutHandler* aHandler, int32_t interval, + bool aIsInterval, mozilla::dom::Timeout::Reason aReason, + int32_t* aReturn); + void ClearTimeout(int32_t aTimerId, mozilla::dom::Timeout::Reason aReason); + bool ClearTimeoutInternal(int32_t aTimerId, + mozilla::dom::Timeout::Reason aReason, + bool aIsIdle); + + // The timeout implementation functions. + MOZ_CAN_RUN_SCRIPT + void RunTimeout(const TimeStamp& aNow, const TimeStamp& aTargetDeadline, + bool aProcessIdle); + + void ClearAllTimeouts(); + uint32_t GetTimeoutId(mozilla::dom::Timeout::Reason aReason); + + TimeDuration CalculateDelay(Timeout* aTimeout) const; + + // aTimeout is the timeout that we're about to start running. This function + // returns the current timeout. + mozilla::dom::Timeout* BeginRunningTimeout(mozilla::dom::Timeout* aTimeout); + // aTimeout is the last running timeout. + void EndRunningTimeout(mozilla::dom::Timeout* aTimeout); + + void UnmarkGrayTimers(); + + // These four methods are intended to be called from the corresponding methods + // on nsGlobalWindow. + void Suspend(); + void Resume(); + void Freeze(); + void Thaw(); + + // This should be called by nsGlobalWindow when the window might have moved + // to the background or foreground. + void UpdateBackgroundState(); + + // The document finished loading + void OnDocumentLoaded(); + void StartThrottlingTimeouts(); + + // Run some code for each Timeout in our list. Note that this function + // doesn't guarantee that Timeouts are iterated in any particular order. + template <class Callable> + void ForEachUnorderedTimeout(Callable c) { + mIdleTimeouts.ForEach(c); + mTimeouts.ForEach(c); + } + + void BeginSyncOperation(); + void EndSyncOperation(); + + nsIEventTarget* EventTarget(); + + bool BudgetThrottlingEnabled(bool aIsBackground) const; + + static const uint32_t InvalidFiringId; + + void SetLoading(bool value); + + private: + void MaybeStartThrottleTimeout(); + + // Return true if |aTimeout| needs to be reinserted into the timeout list. + bool RescheduleTimeout(mozilla::dom::Timeout* aTimeout, + const TimeStamp& aLastCallbackTime, + const TimeStamp& aCurrentNow); + + void MoveIdleToActive(); + + bool IsBackground() const; + + bool IsActive() const; + + uint32_t CreateFiringId(); + + void DestroyFiringId(uint32_t aFiringId); + + bool IsValidFiringId(uint32_t aFiringId) const; + + bool IsInvalidFiringId(uint32_t aFiringId) const; + + TimeDuration MinSchedulingDelay() const; + + nsresult MaybeSchedule(const TimeStamp& aWhen, + const TimeStamp& aNow = TimeStamp::Now()); + + void RecordExecution(Timeout* aRunningTimeout, Timeout* aTimeout); + + void UpdateBudget(const TimeStamp& aNow, + const TimeDuration& aDuration = TimeDuration()); + + private: + struct Timeouts { + explicit Timeouts(const TimeoutManager& aManager) + : mManager(aManager), mTimeouts(new Timeout::TimeoutSet()) {} + + // Insert aTimeout into the list, before all timeouts that would + // fire after it, but no earlier than the last Timeout with a + // valid FiringId. + enum class SortBy { TimeRemaining, TimeWhen }; + void Insert(mozilla::dom::Timeout* aTimeout, SortBy aSortBy); + + const Timeout* GetFirst() const { return mTimeoutList.getFirst(); } + Timeout* GetFirst() { return mTimeoutList.getFirst(); } + const Timeout* GetLast() const { return mTimeoutList.getLast(); } + Timeout* GetLast() { return mTimeoutList.getLast(); } + bool IsEmpty() const { return mTimeoutList.isEmpty(); } + void InsertFront(Timeout* aTimeout) { + aTimeout->SetTimeoutContainer(mTimeouts); + mTimeoutList.insertFront(aTimeout); + } + void InsertBack(Timeout* aTimeout) { + aTimeout->SetTimeoutContainer(mTimeouts); + mTimeoutList.insertBack(aTimeout); + } + void Clear() { + mTimeouts->Clear(); + mTimeoutList.clear(); + } + + template <class Callable> + void ForEach(Callable c) { + for (Timeout* timeout = GetFirst(); timeout; + timeout = timeout->getNext()) { + c(timeout); + } + } + + // Returns true when a callback aborts iteration. + template <class Callable> + bool ForEachAbortable(Callable c) { + for (Timeout* timeout = GetFirst(); timeout; + timeout = timeout->getNext()) { + if (c(timeout)) { + return true; + } + } + return false; + } + + Timeout* GetTimeout(uint32_t aTimeoutId, Timeout::Reason aReason) { + Timeout::TimeoutIdAndReason key = {aTimeoutId, aReason}; + return mTimeouts->Get(key); + } + + private: + // The TimeoutManager that owns this Timeouts structure. This is + // mainly used to call state inspecting methods like IsValidFiringId(). + const TimeoutManager& mManager; + + using TimeoutList = mozilla::LinkedList<RefPtr<Timeout>>; + + // mTimeoutList is generally sorted by mWhen, but new values are always + // inserted after any Timeouts with a valid FiringId. + TimeoutList mTimeoutList; + + // mTimeouts is a set of all the timeouts in the mTimeoutList. + // It let's one to have O(1) check whether a timeout id/reason is in the + // list. + RefPtr<Timeout::TimeoutSet> mTimeouts; + }; + + // Each nsGlobalWindowInner object has a TimeoutManager member. This + // reference points to that holder object. + nsGlobalWindowInner& mWindow; + // The executor is specific to the nsGlobalWindow/TimeoutManager, but it + // can live past the destruction of the window if its scheduled. Therefore + // it must be a separate ref-counted object. + RefPtr<TimeoutExecutor> mExecutor; + // For timeouts run off the idle queue + RefPtr<TimeoutExecutor> mIdleExecutor; + // The list of timeouts coming from non-tracking scripts. + Timeouts mTimeouts; + uint32_t mTimeoutIdCounter; + uint32_t mNextFiringId; +#ifdef DEBUG + int64_t mFiringIndex; + int64_t mLastFiringIndex; +#endif + AutoTArray<uint32_t, 2> mFiringIdStack; + mozilla::dom::Timeout* mRunningTimeout; + + // Timeouts that would have fired but are being deferred until MainThread + // is idle (because we're loading) + Timeouts mIdleTimeouts; + + // The current idle request callback timeout handle + uint32_t mIdleCallbackTimeoutCounter; + + nsCOMPtr<nsITimer> mThrottleTimeoutsTimer; + mozilla::TimeStamp mLastBudgetUpdate; + mozilla::TimeDuration mExecutionBudget; + + bool mThrottleTimeouts; + bool mThrottleTrackingTimeouts; + bool mBudgetThrottleTimeouts; + + bool mIsLoading; + + static uint32_t sNestingLevel; +}; + +} // namespace dom +} // namespace mozilla + +#endif |