Bug 1198381 - Extend setTimeout handling in nsGlobalWindow, r=smaug
authorAndreas Farre <farre@mozilla.com>
Mon, 22 Aug 2016 15:07:50 +0200
changeset 346865 ed2eac576e47abe0b3ffa7a7e9cfab2fb5df7959
parent 346864 030e1cc3b1c1feede3182d67adc12a1d6f80c746
child 346866 17b19eb241db07356004d211c4d23a7646fd8b09
push id10298
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:33:03 +0000
treeherdermozilla-aurora@7e29173b1641 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1198381
milestone52.0a1
Bug 1198381 - Extend setTimeout handling in nsGlobalWindow, r=smaug The requestIdleCallback feature behaves in many ways as setTimeout since it takes an optional timout when the idle callback will be called regardless of the user agent being idle or not. This means that the same mechanisms controlling setTimeout are needed for requestIdleCallback. MozReview-Commit-ID: 9mESsJnUexf
dom/base/Timeout.cpp
dom/base/Timeout.h
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
--- a/dom/base/Timeout.cpp
+++ b/dom/base/Timeout.cpp
@@ -13,17 +13,18 @@
 
 namespace mozilla {
 namespace dom {
 
 Timeout::Timeout()
   : mCleared(false),
     mRunning(false),
     mIsInterval(false),
-    mPublicId(0),
+    mReason(Reason::eTimeoutOrInterval),
+    mTimeoutId(0),
     mInterval(0),
     mFiringDepth(0),
     mNestingLevel(0),
     mPopupState(openAllowed)
 {
   MOZ_COUNT_CTOR(Timeout);
 }
 
--- a/dom/base/Timeout.h
+++ b/dom/base/Timeout.h
@@ -31,16 +31,18 @@ class Timeout final
 public:
   Timeout();
 
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(Timeout)
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Timeout)
 
   nsresult InitTimer(uint32_t aDelay);
 
+  enum class Reason { eTimeoutOrInterval, eIdleCallbackTimeout };
+
   static void TimerNameCallback(nsITimer* aTimer, void* aClosure, char* aBuf,
                                 size_t aLen);
 
 #ifdef DEBUG
   bool HasRefCntOne() const;
 #endif // DEBUG
 
   // Window for which this timeout fires
@@ -53,18 +55,20 @@ public:
   bool mCleared;
 
   // True if this is one of the timeouts that are currently running
   bool mRunning;
 
   // True if this is a repeating/interval timer
   bool mIsInterval;
 
+  Reason mReason;
+
   // Returned as value of setTimeout()
-  uint32_t mPublicId;
+  uint32_t mTimeoutId;
 
   // Interval in milliseconds
   uint32_t mInterval;
 
   // mWhen and mTimeRemaining can't be in a union, sadly, because they
   // have constructors.
   // Nominal time to run this timeout.  Use only when timeouts are not
   // suspended.
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1143,22 +1143,23 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
     mHasGamepad(false),
     mHasVREvents(false),
 #ifdef MOZ_GAMEPAD
     mHasSeenGamepadInput(false),
 #endif
     mNotifiedIDDestroyed(false),
     mAllowScriptsToClose(false),
     mTimeoutInsertionPoint(nullptr),
-    mTimeoutPublicIdCounter(1),
+    mTimeoutIdCounter(1),
     mTimeoutFiringDepth(0),
     mSuspendDepth(0),
     mFreezeDepth(0),
     mFocusMethod(0),
     mSerial(0),
+    mIdleCallbackTimeoutCounter(1),
 #ifdef DEBUG
     mSetOpenerWindowCalled(false),
 #endif
 #ifdef MOZ_B2G
     mNetworkUploadObserverEnabled(false),
     mNetworkDownloadObserverEnabled(false),
 #endif
     mCleanedUp(false),
@@ -7887,25 +7888,25 @@ nsGlobalWindow::MozRequestOverfill(Overf
 }
 
 void
 nsGlobalWindow::ClearTimeout(int32_t aHandle)
 {
   MOZ_RELEASE_ASSERT(IsInnerWindow());
 
   if (aHandle > 0) {
-    ClearTimeoutOrInterval(aHandle);
+    ClearTimeoutOrInterval(aHandle, Timeout::Reason::eTimeoutOrInterval);
   }
 }
 
 void
 nsGlobalWindow::ClearInterval(int32_t aHandle)
 {
   if (aHandle > 0) {
-    ClearTimeoutOrInterval(aHandle);
+    ClearTimeoutOrInterval(aHandle, Timeout::Reason::eTimeoutOrInterval);
   }
 }
 
 void
 nsGlobalWindow::SetResizable(bool aResizable) const
 {
   // nop
 }
@@ -12257,16 +12258,28 @@ nsGlobalWindow::OpenInternal(const nsASt
 }
 
 //*****************************************************************************
 // nsGlobalWindow: Timeout Functions
 //*****************************************************************************
 
 uint32_t sNestingLevel;
 
+uint32_t
+nsGlobalWindow::GetTimeoutId(Timeout::Reason aReason)
+{
+  switch (aReason) {
+    case Timeout::Reason::eIdleCallbackTimeout:
+      return ++mIdleCallbackTimeoutCounter;
+    case Timeout::Reason::eTimeoutOrInterval:
+    default:
+      return ++mTimeoutIdCounter;
+  }
+}
+
 nsGlobalWindow*
 nsGlobalWindow::InnerForSetTimeoutOrInterval(ErrorResult& aError)
 {
   nsGlobalWindow* currentInner;
   nsGlobalWindow* forwardTo;
   if (IsInnerWindow()) {
     nsGlobalWindow* outer = GetOuterWindowInternal();
     currentInner = outer ? outer->GetCurrentInnerWindowInternal() : this;
@@ -12366,17 +12379,17 @@ nsGlobalWindow::SetInterval(JSContext* a
   int32_t timeout;
   bool isInterval = IsInterval(aTimeout, timeout);
   return SetTimeoutOrInterval(aCx, aHandler, timeout, isInterval, aError);
 }
 
 nsresult
 nsGlobalWindow::SetTimeoutOrInterval(nsITimeoutHandler* aHandler,
                                      int32_t interval, bool aIsInterval,
-                                     int32_t* aReturn)
+                                     Timeout::Reason aReason, int32_t* aReturn)
 {
   MOZ_ASSERT(IsInnerWindow());
 
   // If we don't have a document (we could have been unloaded since
   // the call to setTimeout was made), do nothing.
   if (!mDoc) {
     return NS_OK;
   }
@@ -12392,16 +12405,17 @@ nsGlobalWindow::SetTimeoutOrInterval(nsI
   if (static_cast<uint32_t>(interval) > maxTimeoutMs) {
     interval = maxTimeoutMs;
   }
 
   RefPtr<Timeout> timeout = new Timeout();
   timeout->mIsInterval = aIsInterval;
   timeout->mInterval = interval;
   timeout->mScriptHandler = aHandler;
+  timeout->mReason = aReason;
 
   // Now clamp the actual interval we will use for the timer based on
   uint32_t nestingLevel = sNestingLevel + 1;
   uint32_t realInterval = interval;
   if (aIsInterval || nestingLevel >= DOM_CLAMP_TIMEOUT_NESTING_LEVEL) {
     // Don't allow timeouts less than DOMMinTimeoutValue() from
     // now...
     realInterval = std::max(realInterval, uint32_t(DOMMinTimeoutValue()));
@@ -12466,18 +12480,18 @@ nsGlobalWindow::SetTimeoutOrInterval(nsI
     // in some cases.
     if (interval <= delay) {
       timeout->mPopupState = gPopupControlState;
     }
   }
 
   InsertTimeoutIntoList(timeout);
 
-  timeout->mPublicId = ++mTimeoutPublicIdCounter;
-  *aReturn = timeout->mPublicId;
+  timeout->mTimeoutId = GetTimeoutId(aReason);
+  *aReturn = timeout->mTimeoutId;
 
   return NS_OK;
 }
 
 int32_t
 nsGlobalWindow::SetTimeoutOrInterval(JSContext *aCx, Function& aFunction,
                                      int32_t aTimeout,
                                      const Sequence<JS::Value>& aArguments,
@@ -12495,17 +12509,18 @@ nsGlobalWindow::SetTimeoutOrInterval(JSC
 
   nsCOMPtr<nsIScriptTimeoutHandler> handler =
     NS_CreateJSTimeoutHandler(aCx, this, aFunction, aArguments, aError);
   if (!handler) {
     return 0;
   }
 
   int32_t result;
-  aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval, &result);
+  aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval,
+                                Timeout::Reason::eTimeoutOrInterval, &result);
   return result;
 }
 
 int32_t
 nsGlobalWindow::SetTimeoutOrInterval(JSContext* aCx, const nsAString& aHandler,
                                      int32_t aTimeout, bool aIsInterval,
                                      ErrorResult& aError)
 {
@@ -12521,17 +12536,18 @@ nsGlobalWindow::SetTimeoutOrInterval(JSC
 
   nsCOMPtr<nsIScriptTimeoutHandler> handler =
     NS_CreateJSTimeoutHandler(aCx, this, aHandler, aError);
   if (!handler) {
     return 0;
   }
 
   int32_t result;
-  aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval, &result);
+  aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval,
+                                Timeout::Reason::eTimeoutOrInterval, &result);
   return result;
 }
 
 bool
 nsGlobalWindow::RunTimeoutHandler(Timeout* aTimeout,
                                   nsIScriptContext* aScx)
 {
   // Hold on to the timeout in case mExpr or mFunObj releases its
@@ -12884,25 +12900,25 @@ nsGlobalWindow::RunTimeout(Timeout* aTim
   dummy_timeout->remove();
   timeoutExtraRef = nullptr;
   MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak");
 
   mTimeoutInsertionPoint = last_insertion_point;
 }
 
 void
-nsGlobalWindow::ClearTimeoutOrInterval(int32_t aTimerId)
+nsGlobalWindow::ClearTimeoutOrInterval(int32_t aTimerId, Timeout::Reason aReason)
 {
   MOZ_RELEASE_ASSERT(IsInnerWindow());
 
   uint32_t timerId = (uint32_t)aTimerId;
   Timeout* timeout;
 
   for (timeout = mTimeouts.getFirst(); timeout; timeout = timeout->getNext()) {
-    if (timeout->mPublicId == timerId) {
+    if (timeout->mTimeoutId == timerId && timeout->mReason == aReason) {
       if (timeout->mRunning) {
         /* We're running from inside the timeout. Mark this
            timeout for deferred deletion by the code in
            RunTimeout() */
         timeout->mIsInterval = false;
       }
       else {
         /* Delete the timeout from the pending timeout list */
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1446,26 +1446,28 @@ private:
   void ThawInternal();
 
 public:
   // Timeout Functions
   // Language agnostic timeout function (all args passed).
   // |interval| is in milliseconds.
   nsresult SetTimeoutOrInterval(nsITimeoutHandler* aHandler,
                                 int32_t interval, bool aIsInterval,
+                                mozilla::dom::Timeout::Reason aReason,
                                 int32_t* aReturn);
   int32_t SetTimeoutOrInterval(JSContext* aCx,
                                mozilla::dom::Function& aFunction,
                                int32_t aTimeout,
                                const mozilla::dom::Sequence<JS::Value>& aArguments,
                                bool aIsInterval, mozilla::ErrorResult& aError);
   int32_t SetTimeoutOrInterval(JSContext* aCx, const nsAString& aHandler,
                                int32_t aTimeout, bool aIsInterval,
                                mozilla::ErrorResult& aError);
-  void ClearTimeoutOrInterval(int32_t aTimerId);
+  void ClearTimeoutOrInterval(int32_t aTimerId,
+                              mozilla::dom::Timeout::Reason aReason);
 
   // JS specific timeout functions (JS args grabbed from context).
   nsresult ResetTimersForNonBackgroundWindow();
 
   // The timeout implementation functions.
   void RunTimeout(mozilla::dom::Timeout* aTimeout);
   void RunTimeout() { RunTimeout(nullptr); }
   // Return true if |aTimeout| was cleared while its handler ran.
@@ -1474,16 +1476,17 @@ public:
   bool RescheduleTimeout(mozilla::dom::Timeout* aTimeout, const TimeStamp& now,
                          bool aRunningPendingTimeouts);
 
   void ClearAllTimeouts();
   // Insert aTimeout into the list, before all timeouts that would
   // fire after it, but no earlier than mTimeoutInsertionPoint, if any.
   void InsertTimeoutIntoList(mozilla::dom::Timeout* aTimeout);
   static void TimerCallback(nsITimer *aTimer, void *aClosure);
+  uint32_t GetTimeoutId(mozilla::dom::Timeout::Reason aReason);
 
   // Helper Functions
   already_AddRefed<nsIDocShellTreeOwner> GetTreeOwner();
   already_AddRefed<nsIBaseWindow> GetTreeOwnerWindow();
   already_AddRefed<nsIWebBrowserChrome> GetWebBrowserChrome();
   nsresult SecurityCheckURL(const char *aURL);
   bool IsPrivateBrowsing();
 
@@ -1813,17 +1816,17 @@ protected:
   // non-null.  In that case, the dummy timeout pointed to by
   // mTimeoutInsertionPoint may have a later mWhen than some of the timeouts
   // that come after it.
   mozilla::LinkedList<mozilla::dom::Timeout> mTimeouts;
   // If mTimeoutInsertionPoint is non-null, insertions should happen after it.
   // This is a dummy timeout at the moment; if that ever changes, the logic in
   // ResetTimersForNonBackgroundWindow needs to change.
   mozilla::dom::Timeout*      mTimeoutInsertionPoint;
-  uint32_t                    mTimeoutPublicIdCounter;
+  uint32_t                    mTimeoutIdCounter;
   uint32_t                    mTimeoutFiringDepth;
   RefPtr<mozilla::dom::Location> mLocation;
   RefPtr<nsHistory>           mHistory;
   RefPtr<mozilla::dom::CustomElementRegistry> mCustomElements;
 
   // These member variables are used on both inner and the outer windows.
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
 
@@ -1833,16 +1836,18 @@ protected:
   uint32_t mSuspendDepth;
   uint32_t mFreezeDepth;
 
   // the method that was used to focus mFocusedNode
   uint32_t mFocusMethod;
 
   uint32_t mSerial;
 
+   // The current idle request callback timeout handle
+  uint32_t mIdleCallbackTimeoutCounter;
 #ifdef DEBUG
   bool mSetOpenerWindowCalled;
   nsCOMPtr<nsIURI> mLastOpenedURI;
 #endif
 
 #ifdef MOZ_B2G
   bool mNetworkUploadObserverEnabled;
   bool mNetworkDownloadObserverEnabled;