Backed out changeset a0d8fb1244c5 (bug 1313864) for causing bug 1334904.
authorAndrew McCreight <amccreight@mozilla.com>
Sun, 29 Jan 2017 10:29:27 -0800
changeset 331601 71224049c0b52ab190564d3ea0eab089a159a4cf
parent 331600 77af15f468e84d9a5ff659641737343ffa0444cf
child 331602 17a37753fc3df2aa1005d9f41dfe9498684cfd1b
child 331627 c2ebea0623f7a2b2da31c761894296fccb24cfa0
push id36616
push userphilringnalda@gmail.com
push dateSun, 29 Jan 2017 20:59:55 +0000
treeherderautoland@71224049c0b5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1313864, 1334904
milestone54.0a1
backs outa0d8fb1244c5c1d92cd328d1984a2aae3b800e55
first release with
nightly linux32
71224049c0b5 / 54.0a1 / 20170129194105 / files
nightly linux64
71224049c0b5 / 54.0a1 / 20170129194105 / files
nightly mac
71224049c0b5 / 54.0a1 / 20170129110302 / files
nightly win32
71224049c0b5 / 54.0a1 / 20170129110302 / files
nightly win64
71224049c0b5 / 54.0a1 / 20170129110302 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset a0d8fb1244c5 (bug 1313864) for causing bug 1334904.
dom/base/IdleRequest.cpp
dom/base/IdleRequest.h
dom/base/Timeout.h
dom/base/TimeoutHandler.cpp
dom/base/TimeoutHandler.h
dom/base/moz.build
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
--- a/dom/base/IdleRequest.cpp
+++ b/dom/base/IdleRequest.cpp
@@ -3,65 +3,155 @@
 /* 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/. */
 
 #include "IdleRequest.h"
 
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/IdleDeadline.h"
+#include "mozilla/dom/Performance.h"
 #include "mozilla/dom/PerformanceTiming.h"
 #include "mozilla/dom/TimeoutManager.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsComponentManagerUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsISupportsPrimitives.h"
 #include "nsPIDOMWindow.h"
 
 namespace mozilla {
 namespace dom {
 
-IdleRequest::IdleRequest(IdleRequestCallback* aCallback, uint32_t aHandle)
-  : mCallback(aCallback)
+IdleRequest::IdleRequest(JSContext* aCx, nsPIDOMWindowInner* aWindow,
+                         IdleRequestCallback& aCallback, uint32_t aHandle)
+  : mWindow(aWindow)
+  , mCallback(&aCallback)
   , mHandle(aHandle)
   , mTimeoutHandle(Nothing())
 {
-  MOZ_DIAGNOSTIC_ASSERT(mCallback);
+  MOZ_ASSERT(aWindow);
+
+  // Get the calling location.
+  nsJSUtils::GetCallingLocation(aCx, mFileName, &mLineNo, &mColumn);
 }
 
 IdleRequest::~IdleRequest()
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION(IdleRequest, mCallback)
+NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequest)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequest)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequest)
 
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequest)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequest)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequest)
+  NS_INTERFACE_MAP_ENTRY(nsIRunnable)
+  NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
+  NS_INTERFACE_MAP_ENTRY(nsIIncrementalRunnable)
+  NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimeoutHandler)
 NS_INTERFACE_MAP_END
 
-void
-IdleRequest::SetTimeoutHandle(int32_t aHandle)
+nsresult
+IdleRequest::SetTimeout(uint32_t aTimeout)
 {
-  mTimeoutHandle = Some(aHandle);
+  int32_t handle;
+  nsresult rv = mWindow->TimeoutManager().SetTimeout(
+    this, aTimeout, false, Timeout::Reason::eIdleCallbackTimeout, &handle);
+  mTimeoutHandle = Some(handle);
+
+  return rv;
+}
+
+nsresult
+IdleRequest::Run()
+{
+  if (mCallback) {
+    RunIdleRequestCallback(false);
+  }
+
+  return NS_OK;
 }
 
 nsresult
-IdleRequest::IdleRun(nsPIDOMWindowInner* aWindow,
-                     DOMHighResTimeStamp aDeadline,
-                     bool aDidTimeout)
+IdleRequest::Cancel()
+{
+  mCallback = nullptr;
+  CancelTimeout();
+  if (isInList()) {
+    remove();
+  }
+  Release();
+
+  return NS_OK;
+}
+
+void
+IdleRequest::SetDeadline(TimeStamp aDeadline)
+{
+  mozilla::dom::Performance* perf = mWindow->GetPerformance();
+  mDeadline =
+    perf ? perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline) : 0.0;
+}
+
+nsresult
+IdleRequest::RunIdleRequestCallback(bool aDidTimeout)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_DIAGNOSTIC_ASSERT(mCallback);
 
+  if (!aDidTimeout) {
+    CancelTimeout();
+  }
+
+  remove();
   ErrorResult error;
   RefPtr<IdleDeadline> deadline =
-    new IdleDeadline(aWindow, aDidTimeout, aDeadline);
+    new IdleDeadline(mWindow, aDidTimeout, mDeadline);
   mCallback->Call(*deadline, error, "requestIdleCallback handler");
+  mCallback = nullptr;
+  Release();
+
+  return error.StealNSResult();
+}
+
+void
+IdleRequest::CancelTimeout()
+{
+  if (mTimeoutHandle.isSome()) {
+    mWindow->TimeoutManager().ClearTimeout(
+      mTimeoutHandle.value(), Timeout::Reason::eIdleCallbackTimeout);
+  }
+}
 
-  mCallback = nullptr;
-  error.SuppressException();
-  return error.StealNSResult();
+nsresult
+IdleRequest::Call()
+{
+  SetDeadline(TimeStamp::Now());
+  return RunIdleRequestCallback(true);
+}
+
+void
+IdleRequest::GetLocation(const char** aFileName, uint32_t* aLineNo,
+                         uint32_t* aColumn)
+{
+  *aFileName = mFileName.get();
+  *aLineNo = mLineNo;
+  *aColumn = mColumn;
+}
+
+void
+IdleRequest::MarkForCC()
+{
+  mCallback->MarkForCC();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/IdleRequest.h
+++ b/dom/base/IdleRequest.h
@@ -10,50 +10,69 @@
 #include "mozilla/LinkedList.h"
 #include "mozilla/Maybe.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsICancelableRunnable.h"
 #include "nsIIncrementalRunnable.h"
 #include "nsIRunnable.h"
+#include "nsITimeoutHandler.h"
 #include "nsString.h"
 
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 namespace dom {
 
 class IdleRequestCallback;
 
-class IdleRequest final : public nsISupports,
-                          public LinkedListElement<IdleRequest>
+class IdleRequest final : public nsITimeoutHandler
+                        , public nsIRunnable
+                        , public nsICancelableRunnable
+                        , public nsIIncrementalRunnable
+                        , public LinkedListElement<IdleRequest>
 {
 public:
-  IdleRequest(IdleRequestCallback* aCallback, uint32_t aHandle);
+  IdleRequest(JSContext* aCx, nsPIDOMWindowInner* aWindow,
+              IdleRequestCallback& aCallback, uint32_t aHandle);
+
+  virtual nsresult Call() override;
+  virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
+                           uint32_t* aColumn) override;
+  virtual void MarkForCC() override;
 
-  nsresult IdleRun(nsPIDOMWindowInner* aWindow,
-                   DOMHighResTimeStamp aDeadline,
-                   bool aDidTimeout);
+  nsresult SetTimeout(uint32_t aTimout);
+  nsresult RunIdleRequestCallback(bool aDidTimeout);
+  void CancelTimeout();
 
-  void SetTimeoutHandle(int32_t aHandle);
-  bool HasTimeout() const { return mTimeoutHandle.isSome(); }
+  NS_DECL_NSIRUNNABLE;
+  virtual nsresult Cancel() override;
+  virtual void SetDeadline(mozilla::TimeStamp aDeadline) override;
 
   uint32_t Handle() const
   {
     return mHandle;
   }
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequest)
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequest, nsITimeoutHandler)
 
 private:
   ~IdleRequest();
 
+  // filename, line number and JS language version string of the
+  // caller of setTimeout()
+  nsCString mFileName;
+  uint32_t mLineNo;
+  uint32_t mColumn;
+
+  nsCOMPtr<nsPIDOMWindowInner> mWindow;
   RefPtr<IdleRequestCallback> mCallback;
-  const uint32_t mHandle;
+  uint32_t mHandle;
   mozilla::Maybe<int32_t> mTimeoutHandle;
+  DOMHighResTimeStamp mDeadline;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_idlerequest_h
--- a/dom/base/Timeout.h
+++ b/dom/base/Timeout.h
@@ -36,21 +36,17 @@ public:
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(Timeout)
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Timeout)
 
   // The target may be specified to use a particular event queue for the
   // resulting timer runnable.  A nullptr target will result in the
   // default main thread being used.
   nsresult InitTimer(nsIEventTarget* aTarget, uint32_t aDelay);
 
-  enum class Reason
-  {
-    eTimeoutOrInterval,
-    eIdleCallbackTimeout,
-  };
+  enum class Reason { eTimeoutOrInterval, eIdleCallbackTimeout };
 
 #ifdef DEBUG
   bool HasRefCnt(uint32_t aCount) const;
 #endif // DEBUG
 
   void SetWhenOrTimeRemaining(const TimeStamp& aBaseTime,
                               const TimeDuration& aDelay);
 
@@ -75,18 +71,16 @@ public:
   bool mRunning;
 
   // True if this is a repeating/interval timer
   bool mIsInterval;
 
   // True if this is a timeout coming from a tracking script
   bool mIsTracking;
 
-  // Used to allow several reasons for setting a timeout, where each
-  // 'Reason' value is using a possibly overlapping set of id:s.
   Reason mReason;
 
   // Returned as value of setTimeout()
   uint32_t mTimeoutId;
 
   // Interval in milliseconds
   uint32_t mInterval;
 
deleted file mode 100644
--- a/dom/base/TimeoutHandler.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/* -*- 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/. */
-
-#include "TimeoutHandler.h"
-
-namespace mozilla {
-namespace dom {
-
-TimeoutHandler::TimeoutHandler(JSContext* aCx)
-  : TimeoutHandler()
-{
-  nsJSUtils::GetCallingLocation(aCx, mFileName, &mLineNo, &mColumn);
-}
-
-nsresult
-TimeoutHandler::Call()
-{
-  return NS_OK;
-}
-
-void
-TimeoutHandler::GetLocation(const char** aFileName, uint32_t* aLineNo,
-                                    uint32_t* aColumn)
-{
-  *aFileName = mFileName.get();
-  *aLineNo = mLineNo;
-  *aColumn = mColumn;
-}
-
-NS_IMPL_CYCLE_COLLECTION_0(TimeoutHandler)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(TimeoutHandler)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(TimeoutHandler)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TimeoutHandler)
-  NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
-NS_INTERFACE_MAP_END
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/base/TimeoutHandler.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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_timeout_handler_h
-#define mozilla_dom_timeout_handler_h
-
-#include "nsCOMPtr.h"
-#include "nsISupports.h"
-#include "nsITimeoutHandler.h"
-
-namespace mozilla {
-namespace dom {
-
-/**
- * Utility class for implementing nsITimeoutHandlers, designed to be subclassed.
- */
-class TimeoutHandler : public nsITimeoutHandler
-{
-public:
-  // TimeoutHandler doesn't actually contain cycles, but subclasses
-  // probably will.
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(TimeoutHandler)
-
-  virtual nsresult Call() override;
-  virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
-                           uint32_t* aColumn) override;
-  virtual void MarkForCC() override {}
-protected:
-  TimeoutHandler() : mFileName(""), mLineNo(0), mColumn(0) {}
-  explicit TimeoutHandler(JSContext *aCx);
-
-  virtual ~TimeoutHandler() {}
-private:
-  TimeoutHandler(const TimeoutHandler&) = delete;
-  TimeoutHandler& operator=(const TimeoutHandler&) = delete;
-  TimeoutHandler& operator=(const TimeoutHandler&&) = delete;
-
-  nsCString mFileName;
-  uint32_t mLineNo;
-  uint32_t mColumn;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_timeout_handler_h
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -200,17 +200,16 @@ EXPORTS.mozilla.dom += [
     'ShadowRoot.h',
     'StructuredCloneHolder.h',
     'StructuredCloneTags.h',
     'StyleSheetList.h',
     'SubtleCrypto.h',
     'TabGroup.h',
     'Text.h',
     'Timeout.h',
-    'TimeoutHandler.h',
     'TimeoutManager.h',
     'TreeWalker.h',
     'WebKitCSSMatrix.h',
     'WebSocket.h',
     'WindowOrientationObserver.h',
 ]
 
 UNIFIED_SOURCES += [
@@ -341,17 +340,16 @@ UNIFIED_SOURCES += [
     'StructuredCloneHolder.cpp',
     'StyleSheetList.cpp',
     'SubtleCrypto.cpp',
     'TabGroup.cpp',
     'Text.cpp',
     'TextInputProcessor.cpp',
     'ThirdPartyUtil.cpp',
     'Timeout.cpp',
-    'TimeoutHandler.cpp',
     'TimeoutManager.cpp',
     'TreeWalker.cpp',
     'WebKitCSSMatrix.cpp',
     'WebSocket.cpp',
     'WindowNamedPropertiesHandler.cpp',
     'WindowOrientationObserver.cpp',
 ]
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -18,17 +18,16 @@
 #include "nsDOMNavigationTiming.h"
 #include "nsIDOMStorageManager.h"
 #include "mozilla/dom/Storage.h"
 #include "mozilla/dom/IdleRequest.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/StorageEventBinding.h"
 #include "mozilla/dom/Timeout.h"
-#include "mozilla/dom/TimeoutHandler.h"
 #include "mozilla/dom/TimeoutManager.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 #include "mozilla/dom/WindowOrientationObserver.h"
 #endif
 #include "nsDOMOfflineResourceList.h"
 #include "nsError.h"
 #include "nsIIdleService.h"
@@ -518,351 +517,124 @@ DialogValueHolder::Get(JSContext* aCx, J
   if (aSubject->Subsumes(mOrigin)) {
     aError = nsContentUtils::XPConnect()->VariantToJS(aCx, aScope,
                                                       mValue, aResult);
   } else {
     aResult.setUndefined();
   }
 }
 
-class IdleRequestExecutor final : public nsIRunnable
-                                , public nsICancelableRunnable
-                                , public nsIIncrementalRunnable
-{
-public:
-  explicit IdleRequestExecutor(nsGlobalWindow* aWindow)
-    : mDispatched(false)
-    , mDeadline(TimeStamp::Now())
-    , mWindow(aWindow)
-  {
-    MOZ_DIAGNOSTIC_ASSERT(mWindow);
-    MOZ_DIAGNOSTIC_ASSERT(mWindow->IsInnerWindow());
-  }
-
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequestExecutor, nsIRunnable)
-
-  NS_DECL_NSIRUNNABLE
-  nsresult Cancel() override;
-  void SetDeadline(TimeStamp aDeadline) override;
-
-  void MaybeDispatch();
-private:
-  ~IdleRequestExecutor() {}
-
-  bool mDispatched;
-  TimeStamp mDeadline;
-  RefPtr<nsGlobalWindow> mWindow;
-};
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequestExecutor)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutor)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequestExecutor)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequestExecutor)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor)
-  NS_INTERFACE_MAP_ENTRY(nsIRunnable)
-  NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
-  NS_INTERFACE_MAP_ENTRY(nsIIncrementalRunnable)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
-NS_INTERFACE_MAP_END
-
-NS_IMETHODIMP
-IdleRequestExecutor::Run()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  mDispatched = false;
-  if (mWindow) {
-    return mWindow->ExecuteIdleRequest(mDeadline);
-  }
-
-  return NS_OK;
-}
-
-nsresult
-IdleRequestExecutor::Cancel()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  mWindow = nullptr;
-  return NS_OK;
-}
-
-void
-IdleRequestExecutor::SetDeadline(TimeStamp aDeadline)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (!mWindow) {
-    return;
-  }
-
-  mDeadline = aDeadline;
-}
-
-void
-IdleRequestExecutor::MaybeDispatch()
-{
-  MOZ_DIAGNOSTIC_ASSERT(mWindow);
-
-  if (mDispatched) {
-    return;
-  }
-
-  mDispatched = true;
-  RefPtr<IdleRequestExecutor> request = this;
-  NS_IdleDispatchToCurrentThread(request.forget());
-}
-
-class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler
-{
-public:
-  explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor* aExecutor)
-    : mExecutor(aExecutor)
-  {
-  }
-
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IdleRequestExecutorTimeoutHandler,
-                                           TimeoutHandler)
-
-  nsresult Call() override
-  {
-    mExecutor->MaybeDispatch();
-    return NS_OK;
-  }
-private:
-  ~IdleRequestExecutorTimeoutHandler() {}
-  RefPtr<IdleRequestExecutor> mExecutor;
-};
-
-NS_IMPL_CYCLE_COLLECTION_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler, mExecutor)
-
-NS_IMPL_ADDREF_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler)
-NS_IMPL_RELEASE_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler)
-  NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
-NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler)
-
-void
-nsGlobalWindow::ScheduleIdleRequestDispatch()
+void
+nsGlobalWindow::PostThrottledIdleCallback()
 {
   AssertIsOnMainThread();
 
-  if (mIdleRequestCallbacks.isEmpty()) {
-    if (mIdleRequestExecutor) {
-      mIdleRequestExecutor->Cancel();
-      mIdleRequestExecutor = nullptr;
-    }
-
-    return;
-  }
-
-  if (!mIdleRequestExecutor) {
-    mIdleRequestExecutor = new IdleRequestExecutor(this);
-  }
-
-  nsPIDOMWindowOuter* outer = GetOuterWindow();
-  if (outer && outer->AsOuter()->IsBackground()) {
-    nsCOMPtr<nsITimeoutHandler> handler = new IdleRequestExecutorTimeoutHandler(mIdleRequestExecutor);
-    int32_t dummy;
-    // Set a timeout handler with a timeout of 0 ms to throttle idle
-    // callback requests coming from a backround window using
-    // background timeout throttling.
-    mTimeoutManager->SetTimeout(handler, 0, false,
-                                Timeout::Reason::eIdleCallbackTimeout, &dummy);
-    return;
-  }
-
-  mIdleRequestExecutor->MaybeDispatch();
-}
-
-void
-nsGlobalWindow::InsertIdleCallback(IdleRequest* aRequest)
-{
-  AssertIsOnMainThread();
-  mIdleRequestCallbacks.insertBack(aRequest);
+  if (mThrottledIdleRequestCallbacks.isEmpty())
+    return;
+
+  RefPtr<IdleRequest> request(mThrottledIdleRequestCallbacks.popFirst());
+  // ownership transferred from mThrottledIdleRequestCallbacks to
+  // mIdleRequestCallbacks
+  mIdleRequestCallbacks.insertBack(request);
+  NS_IdleDispatchToCurrentThread(request.forget());
+}
+
+/* static */ void
+nsGlobalWindow::InsertIdleCallbackIntoList(IdleRequest* aRequest,
+                                           IdleRequests& aList)
+{
+  aList.insertBack(aRequest);
   aRequest->AddRef();
 }
 
-void
-nsGlobalWindow::RemoveIdleCallback(mozilla::dom::IdleRequest* aRequest)
-{
-  AssertIsOnMainThread();
-
-  if (aRequest->HasTimeout()) {
-    mTimeoutManager->ClearTimeout(aRequest->Handle(),
-                                  Timeout::Reason::eIdleCallbackTimeout);
-  }
-
-  aRequest->removeFrom(mIdleRequestCallbacks);
-  aRequest->Release();
-}
-
-nsresult
-nsGlobalWindow::RunIdleRequest(IdleRequest* aRequest,
-                               DOMHighResTimeStamp aDeadline,
-                               bool aDidTimeout)
-{
-  AssertIsOnMainThread();
-  RefPtr<IdleRequest> request(aRequest);
-  nsresult result = request->IdleRun(AsInner(), aDeadline, aDidTimeout);
-  RemoveIdleCallback(request);
-  return result;
-}
-
-nsresult
-nsGlobalWindow::ExecuteIdleRequest(TimeStamp aDeadline)
-{
-  AssertIsOnMainThread();
-  RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
-
-  if (!request) {
-    // There are no more idle requests, so stop scheduling idle
-    // request callbacks.
-    return NS_OK;
-  }
-
-  DOMHighResTimeStamp deadline = 0.0;
-
-  if (Performance* perf = GetPerformance()) {
-    deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline);
-  }
-
-  nsresult result = RunIdleRequest(request, deadline, false);
-
-  ScheduleIdleRequestDispatch();
-  return result;
-}
-
-class IdleRequestTimeoutHandler final : public TimeoutHandler
-{
-public:
-  IdleRequestTimeoutHandler(JSContext* aCx,
-                            IdleRequest* aIdleRequest,
-                            nsPIDOMWindowInner* aWindow)
-    : TimeoutHandler(aCx)
-    , mIdleRequest(aIdleRequest)
-    , mWindow(aWindow)
-  {
-  }
-
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IdleRequestTimeoutHandler,
-                                           TimeoutHandler)
-
-  nsresult Call() override
-  {
-    return nsGlobalWindow::Cast(mWindow)->RunIdleRequest(mIdleRequest, 0.0, true);
-  }
-
-private:
-  ~IdleRequestTimeoutHandler() {}
-
-  RefPtr<IdleRequest> mIdleRequest;
-  nsCOMPtr<nsPIDOMWindowInner> mWindow;
-};
-
-NS_IMPL_CYCLE_COLLECTION_INHERITED(IdleRequestTimeoutHandler,
-                                   TimeoutHandler,
-                                   mIdleRequest,
-                                   mWindow)
-
-NS_IMPL_ADDREF_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler)
-NS_IMPL_RELEASE_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestTimeoutHandler)
-  NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
-NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler)
-
 uint32_t
 nsGlobalWindow::RequestIdleCallback(JSContext* aCx,
                                     IdleRequestCallback& aCallback,
                                     const IdleRequestOptions& aOptions,
                                     ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsInnerWindow());
   AssertIsOnMainThread();
 
-  uint32_t handle = mIdleRequestCallbackCounter++;
+  uint32_t handle = ++mIdleRequestCallbackCounter;
 
   RefPtr<IdleRequest> request =
-    new IdleRequest(&aCallback, handle);
+    new IdleRequest(aCx, AsInner(), aCallback, handle);
 
   if (aOptions.mTimeout.WasPassed()) {
-    int32_t timeoutHandle;
-    nsCOMPtr<nsITimeoutHandler> handler(new IdleRequestTimeoutHandler(aCx, request, AsInner()));
-
-    nsresult rv = mTimeoutManager->SetTimeout(
-        handler, aOptions.mTimeout.Value(), false,
-        Timeout::Reason::eIdleCallbackTimeout, &timeoutHandle);
-
-    if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError = request->SetTimeout(aOptions.mTimeout.Value());
+    if (NS_WARN_IF(aError.Failed())) {
       return 0;
     }
-
-    request->SetTimeoutHandle(timeoutHandle);
-  }
-
-  // If the list of idle callback requests is not empty it means that
-  // we've already dispatched the first idle request. It is the
-  // responsibility of that to dispatch the next.
-  bool needsScheduling = mIdleRequestCallbacks.isEmpty();
-  // mIdleRequestCallbacks now owns request
-  InsertIdleCallback(request);
-
-  if (needsScheduling) {
-    ScheduleIdleRequestDispatch();
+  }
+
+  nsGlobalWindow* outer = GetOuterWindowInternal();
+  if (outer && outer->AsOuter()->IsBackground()) {
+    // mThrottledIdleRequestCallbacks now owns request
+    InsertIdleCallbackIntoList(request, mThrottledIdleRequestCallbacks);
+
+    NS_DelayedDispatchToCurrentThread(
+      NewRunnableMethod(this, &nsGlobalWindow::PostThrottledIdleCallback),
+      10000);
+  } else {
+    MOZ_ASSERT(mThrottledIdleRequestCallbacks.isEmpty());
+
+    // mIdleRequestCallbacks now owns request
+    InsertIdleCallbackIntoList(request, mIdleRequestCallbacks);
+
+    NS_IdleDispatchToCurrentThread(request.forget());
   }
 
   return handle;
 }
 
 void
 nsGlobalWindow::CancelIdleCallback(uint32_t aHandle)
 {
   MOZ_RELEASE_ASSERT(IsInnerWindow());
 
   for (IdleRequest* r : mIdleRequestCallbacks) {
     if (r->Handle() == aHandle) {
-      RemoveIdleCallback(r);
+      r->Cancel();
       break;
     }
   }
 }
 
 void
 nsGlobalWindow::DisableIdleCallbackRequests()
 {
-  if (mIdleRequestExecutor) {
-    mIdleRequestExecutor->Cancel();
-    mIdleRequestExecutor = nullptr;
-  }
-
   while (!mIdleRequestCallbacks.isEmpty()) {
-    RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
-    RemoveIdleCallback(request);
+    RefPtr<IdleRequest> request = mIdleRequestCallbacks.popFirst();
+    request->Cancel();
+  }
+
+  while (!mThrottledIdleRequestCallbacks.isEmpty()) {
+    RefPtr<IdleRequest> request = mThrottledIdleRequestCallbacks.popFirst();
+    request->Cancel();
+  }
+}
+
+void nsGlobalWindow::UnthrottleIdleCallbackRequests()
+{
+  AssertIsOnMainThread();
+
+  while (!mThrottledIdleRequestCallbacks.isEmpty()) {
+    RefPtr<IdleRequest> request(mThrottledIdleRequestCallbacks.popFirst());
+    mIdleRequestCallbacks.insertBack(request);
+    NS_IdleDispatchToCurrentThread(request.forget());
   }
 }
 
 bool
 nsGlobalWindow::IsBackgroundInternal() const
 {
   return !mOuterWindow || mOuterWindow->IsBackground();
 }
+
 namespace mozilla {
 namespace dom {
 extern uint64_t
 NextWindowID();
 } // namespace dom
 } // namespace mozilla
 
 template<class T>
@@ -1443,17 +1215,16 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
     mHasSeenGamepadInput(false),
     mNotifiedIDDestroyed(false),
     mAllowScriptsToClose(false),
     mSuspendDepth(0),
     mFreezeDepth(0),
     mFocusMethod(0),
     mSerial(0),
     mIdleRequestCallbackCounter(1),
-    mIdleRequestExecutor(nullptr),
 #ifdef DEBUG
     mSetOpenerWindowCalled(false),
 #endif
 #ifdef MOZ_B2G
     mNetworkUploadObserverEnabled(false),
     mNetworkDownloadObserverEnabled(false),
 #endif
     mCleanedUp(false),
@@ -2155,21 +1926,24 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingStorageEvents)
 
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
   for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
     cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
   }
 
+  for (IdleRequest* request : tmp->mThrottledIdleRequestCallbacks) {
+    cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
+  }
+
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays)
 
   // Traverse stuff from nsPIDOMWindow
@@ -2264,17 +2038,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioWorklet)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMozSelfSupport)
 
   tmp->UnlinkHostObjectURIs();
 
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
   tmp->DisableIdleCallbackRequests();
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 #ifdef DEBUG
 void
 nsGlobalWindow::RiskyUnlink()
@@ -10264,16 +10037,17 @@ void nsGlobalWindow::SetIsBackground(boo
   if (!inner) {
     return;
   }
 
   if (resetTimers) {
     inner->mTimeoutManager->ResetTimersForThrottleReduction();
   }
 
+  inner->UnthrottleIdleCallbackRequests();
   inner->SyncGamepadState();
 }
 
 void nsGlobalWindow::MaybeUpdateTouchState()
 {
   FORWARD_TO_INNER_VOID(MaybeUpdateTouchState, ());
 
   if (mMayHaveTouchEventListener) {
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -97,18 +97,16 @@ class nsHistory;
 class nsGlobalWindowObserver;
 class nsGlobalWindow;
 class nsDOMWindowUtils;
 class nsIIdleService;
 struct nsRect;
 
 class nsWindowSizes;
 
-class IdleRequestExecutor;
-
 namespace mozilla {
 class AbstractThread;
 class DOMEventTargetHelper;
 class ThrottledEventQueue;
 namespace dom {
 class BarProp;
 struct ChannelPixelLayout;
 class Console;
@@ -116,17 +114,16 @@ class Crypto;
 class CustomElementRegistry;
 class DocGroup;
 class External;
 class Function;
 class Gamepad;
 enum class ImageBitmapFormat : uint8_t;
 class IdleRequest;
 class IdleRequestCallback;
-class IncrementalRunnable;
 class Location;
 class MediaQueryList;
 class MozSelfSupport;
 class Navigator;
 class OwningExternalOrWindowProxy;
 class Promise;
 class PostMessageEvent;
 struct RequestInit;
@@ -1104,16 +1101,17 @@ public:
   void CancelAnimationFrame(int32_t aHandle, mozilla::ErrorResult& aError);
 
   uint32_t RequestIdleCallback(JSContext* aCx,
                                mozilla::dom::IdleRequestCallback& aCallback,
                                const mozilla::dom::IdleRequestOptions& aOptions,
                                mozilla::ErrorResult& aError);
   void CancelIdleCallback(uint32_t aHandle);
 
+
 #ifdef MOZ_WEBSPEECH
   mozilla::dom::SpeechSynthesis*
     GetSpeechSynthesis(mozilla::ErrorResult& aError);
   bool HasActiveSpeechSynthesis();
 #endif
   already_AddRefed<nsICSSDeclaration>
     GetDefaultComputedStyle(mozilla::dom::Element& aElt,
                             const nsAString& aPseudoElt,
@@ -1783,28 +1781,16 @@ public:
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   virtual nsIEventTarget*
   EventTargetFor(mozilla::TaskCategory aCategory) const override;
 
   virtual mozilla::AbstractThread*
   AbstractMainThreadFor(mozilla::TaskCategory aCategory) override;
 
-  void DisableIdleCallbackRequests();
-  uint32_t IdleRequestHandle() const { return mIdleRequestCallbackCounter; }
-  nsresult RunIdleRequest(mozilla::dom::IdleRequest* aRequest,
-                          DOMHighResTimeStamp aDeadline, bool aDidTimeout);
-  nsresult ExecuteIdleRequest(TimeStamp aDeadline);
-  void ScheduleIdleRequestDispatch();
-
-  typedef mozilla::LinkedList<mozilla::dom::IdleRequest> IdleRequests;
-  void InsertIdleCallback(mozilla::dom::IdleRequest* aRequest);
-
-  void RemoveIdleCallback(mozilla::dom::IdleRequest* aRequest);
-
 protected:
   // These members are only used on outer window objects. Make sure
   // you never set any of these on an inner object!
   bool                          mFullScreen : 1;
   bool                          mFullscreenMode : 1;
   bool                          mIsClosed : 1;
   bool                          mInClose : 1;
   // mHavePendingClose means we've got a termination function set to
@@ -1932,20 +1918,29 @@ protected:
   uint32_t mSuspendDepth;
   uint32_t mFreezeDepth;
 
   // the method that was used to focus mFocusedNode
   uint32_t mFocusMethod;
 
   uint32_t mSerial;
 
+  void DisableIdleCallbackRequests();
+  void UnthrottleIdleCallbackRequests();
+
+  void PostThrottledIdleCallback();
+
+  typedef mozilla::LinkedList<mozilla::dom::IdleRequest> IdleRequests;
+  static void InsertIdleCallbackIntoList(mozilla::dom::IdleRequest* aRequest,
+                                         IdleRequests& aList);
+
   // The current idle request callback handle
   uint32_t mIdleRequestCallbackCounter;
   IdleRequests mIdleRequestCallbacks;
-  RefPtr<IdleRequestExecutor> mIdleRequestExecutor;
+  IdleRequests mThrottledIdleRequestCallbacks;
 
 #ifdef DEBUG
   bool mSetOpenerWindowCalled;
   nsCOMPtr<nsIURI> mLastOpenedURI;
 #endif
 
 #ifdef MOZ_B2G
   bool mNetworkUploadObserverEnabled;
@@ -2012,17 +2007,16 @@ protected:
 
   nsAutoPtr<mozilla::dom::VREventObserver> mVREventObserver;
 
   friend class nsDOMScriptableHelper;
   friend class nsDOMWindowUtils;
   friend class mozilla::dom::PostMessageEvent;
   friend class DesktopNotification;
   friend class mozilla::dom::TimeoutManager;
-  friend class IdleRequestExecutor;
 
   static WindowByIdTable* sWindowsById;
   static bool sWarnedAboutWindowInternal;
 };
 
 inline nsISupports*
 ToSupports(nsGlobalWindow *p)
 {