Bug 1372733 - EventTargetFor should return an nsISerialEventTarget (r=bkelly)
authorBill McCloskey <billm@mozilla.com>
Tue, 13 Jun 2017 13:40:00 -0700
changeset 364318 8408c88a471ca55dc914098ded86e9ffaacff7a3
parent 364317 c9fb476f66a2cac062d990a5017d4db0513946a3
child 364319 871e57ecd232a8aeb1feb47e6aff647ae867588a
push id32037
push userarchaeopteryx@coole-files.de
push dateFri, 16 Jun 2017 07:50:18 +0000
treeherdermozilla-central@fe809f57bf22 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
bugs1372733
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1372733 - EventTargetFor should return an nsISerialEventTarget (r=bkelly) MozReview-Commit-ID: Kchg4kqBERt
dom/base/DispatcherTrait.cpp
dom/base/DispatcherTrait.h
dom/base/DocGroup.cpp
dom/base/DocGroup.h
dom/base/TabGroup.cpp
dom/base/TabGroup.h
dom/base/nsDocument.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsIDocument.h
dom/base/nsPIDOMWindow.h
dom/workers/WorkerPrivate.cpp
xpcom/tests/gtest/TestEventTargetQI.cpp
xpcom/threads/SchedulerGroup.cpp
xpcom/threads/SchedulerGroup.h
xpcom/threads/SystemGroup.cpp
xpcom/threads/SystemGroup.h
xpcom/threads/ThrottledEventQueue.cpp
xpcom/threads/ThrottledEventQueue.h
xpcom/threads/nsISerialEventTarget.idl
--- a/dom/base/DispatcherTrait.cpp
+++ b/dom/base/DispatcherTrait.cpp
@@ -16,20 +16,20 @@ using namespace mozilla::dom;
 nsresult
 DispatcherTrait::Dispatch(const char* aName,
                           TaskCategory aCategory,
                           already_AddRefed<nsIRunnable>&& aRunnable)
 {
   return SchedulerGroup::UnlabeledDispatch(aName, aCategory, Move(aRunnable));
 }
 
-nsIEventTarget*
+nsISerialEventTarget*
 DispatcherTrait::EventTargetFor(TaskCategory aCategory) const
 {
-  return GetMainThreadEventTarget();
+  return GetMainThreadSerialEventTarget();
 }
 
 AbstractThread*
 DispatcherTrait::AbstractMainThreadFor(TaskCategory aCategory)
 {
   // Return non DocGroup version by default.
   return AbstractThread::MainThread();
 }
--- a/dom/base/DispatcherTrait.h
+++ b/dom/base/DispatcherTrait.h
@@ -5,18 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_DispatcherTrait_h
 #define mozilla_dom_DispatcherTrait_h
 
 #include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/TaskCategory.h"
 
-class nsIEventTarget;
 class nsIRunnable;
+class nsISerialEventTarget;
 
 namespace mozilla {
 class AbstractThread;
 namespace dom {
 class TabGroup;
 
 // This trait should be attached to classes like nsIGlobalObject and nsIDocument
 // that have a DocGroup or TabGroup attached to them. The methods here should
@@ -26,19 +26,19 @@ class DispatcherTrait {
 public:
   // This method may or may not be safe off of the main thread. For nsIDocument
   // it is safe. For nsIGlobalWindow it is not safe.
   virtual nsresult Dispatch(const char* aName,
                             TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable);
 
   // This method may or may not be safe off of the main thread. For nsIDocument
-  // it is safe. For nsIGlobalWindow it is not safe. The nsIEventTarget can
+  // it is safe. For nsIGlobalWindow it is not safe. The nsISerialEventTarget can
   // always be used off the main thread.
-  virtual nsIEventTarget* EventTargetFor(TaskCategory aCategory) const;
+  virtual nsISerialEventTarget* EventTargetFor(TaskCategory aCategory) const;
 
   // Must be called on the main thread. The AbstractThread can always be used
   // off the main thread.
   virtual AbstractThread* AbstractMainThreadFor(TaskCategory aCategory);
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/DocGroup.cpp
+++ b/dom/base/DocGroup.cpp
@@ -58,17 +58,17 @@ DocGroup::~DocGroup()
 nsresult
 DocGroup::Dispatch(const char* aName,
                    TaskCategory aCategory,
                    already_AddRefed<nsIRunnable>&& aRunnable)
 {
   return mTabGroup->Dispatch(aName, aCategory, Move(aRunnable));
 }
 
-nsIEventTarget*
+nsISerialEventTarget*
 DocGroup::EventTargetFor(TaskCategory aCategory) const
 {
   return mTabGroup->EventTargetFor(aCategory);
 }
 
 AbstractThread*
 DocGroup::AbstractMainThreadFor(TaskCategory aCategory)
 {
--- a/dom/base/DocGroup.h
+++ b/dom/base/DocGroup.h
@@ -79,17 +79,17 @@ public:
     MOZ_ASSERT(NS_IsMainThread());
     return mDocuments.end();
   }
 
   nsresult Dispatch(const char* aName,
                     TaskCategory aCategory,
                     already_AddRefed<nsIRunnable>&& aRunnable);
 
-  nsIEventTarget* EventTargetFor(TaskCategory aCategory) const;
+  nsISerialEventTarget* EventTargetFor(TaskCategory aCategory) const;
 
   AbstractThread*
   AbstractMainThreadFor(TaskCategory aCategory);
 
   // Ensure that it's valid to access the DocGroup at this time.
   void ValidateAccess() const
   {
     mTabGroup->ValidateAccess();
--- a/dom/base/TabGroup.cpp
+++ b/dom/base/TabGroup.cpp
@@ -61,17 +61,17 @@ TabGroup::EnsureThrottledEventQueues()
     return;
   }
 
   mThrottledQueuesInitialized = true;
 
   for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
     TaskCategory category = static_cast<TaskCategory>(i);
     if (category == TaskCategory::Worker || category == TaskCategory::Timer) {
-      nsCOMPtr<nsIEventTarget> target = ThrottledEventQueue::Create(mEventTargets[i]);
+      nsCOMPtr<nsISerialEventTarget> target = ThrottledEventQueue::Create(mEventTargets[i]);
       if (target) {
         // This may return nullptr during xpcom shutdown.  This is ok as we
         // do not guarantee a ThrottledEventQueue will be present.
         mEventTargets[i] = target;
       }
     }
   }
 }
@@ -245,17 +245,17 @@ TabGroup::GetTopLevelWindows() const
 
   return array;
 }
 
 TabGroup::HashEntry::HashEntry(const nsACString* aKey)
   : nsCStringHashKey(aKey), mDocGroup(nullptr)
 {}
 
-nsIEventTarget*
+nsISerialEventTarget*
 TabGroup::EventTargetFor(TaskCategory aCategory) const
 {
   if (aCategory == TaskCategory::Worker || aCategory == TaskCategory::Timer) {
     MOZ_RELEASE_ASSERT(mThrottledQueuesInitialized || mIsChrome);
   }
   return SchedulerGroup::EventTargetFor(aCategory);
 }
 
--- a/dom/base/TabGroup.h
+++ b/dom/base/TabGroup.h
@@ -114,17 +114,17 @@ public:
                    nsIDocShellTreeItem* aOriginalRequestor,
                    nsIDocShellTreeItem** aFoundItem);
 
   nsTArray<nsPIDOMWindowOuter*> GetTopLevelWindows() const;
   const nsTArray<nsPIDOMWindowOuter*>& GetWindows() { return mWindows; }
 
   // This method is always safe to call off the main thread. The nsIEventTarget
   // can always be used off the main thread.
-  nsIEventTarget* EventTargetFor(TaskCategory aCategory) const override;
+  nsISerialEventTarget* EventTargetFor(TaskCategory aCategory) const override;
 
   void WindowChangedBackgroundStatus(bool aIsNowBackground);
 
   // Returns true if all of the TabGroup's top-level windows are in
   // the background.
   bool IsBackground() const override;
 
 private:
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3040,17 +3040,17 @@ nsIDocument::Dispatch(const char* aName,
 {
   // Note that this method may be called off the main thread.
   if (mDocGroup) {
     return mDocGroup->Dispatch(aName, aCategory, Move(aRunnable));
   }
   return DispatcherTrait::Dispatch(aName, aCategory, Move(aRunnable));
 }
 
-nsIEventTarget*
+nsISerialEventTarget*
 nsIDocument::EventTargetFor(TaskCategory aCategory) const
 {
   if (mDocGroup) {
     return mDocGroup->EventTargetFor(aCategory);
   }
   return DispatcherTrait::EventTargetFor(aCategory);
 }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -15312,17 +15312,17 @@ nsGlobalWindow::Dispatch(const char* aNa
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   if (GetDocGroup()) {
     return GetDocGroup()->Dispatch(aName, aCategory, Move(aRunnable));
   }
   return DispatcherTrait::Dispatch(aName, aCategory, Move(aRunnable));
 }
 
-nsIEventTarget*
+nsISerialEventTarget*
 nsGlobalWindow::EventTargetFor(TaskCategory aCategory) const
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   if (GetDocGroup()) {
     return GetDocGroup()->EventTargetFor(aCategory);
   }
   return DispatcherTrait::EventTargetFor(aCategory);
 }
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1823,17 +1823,17 @@ private:
   void SetIsBackgroundInternal(bool aIsBackground);
 
 public:
   // Dispatch a runnable related to the global.
   virtual nsresult Dispatch(const char* aName,
                             mozilla::TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
-  virtual nsIEventTarget*
+  virtual nsISerialEventTarget*
   EventTargetFor(mozilla::TaskCategory aCategory) const override;
 
   virtual mozilla::AbstractThread*
   AbstractMainThreadFor(mozilla::TaskCategory aCategory) override;
 
   void DisableIdleCallbackRequests();
   uint32_t LastIdleRequestHandle() const { return mIdleRequestCallbackCounter - 1; }
   nsresult RunIdleRequest(mozilla::dom::IdleRequest* aRequest,
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2919,17 +2919,17 @@ public:
   virtual void ScheduleIntersectionObserverNotification() = 0;
   virtual void NotifyIntersectionObservers() = 0;
 
   // Dispatch a runnable related to the document.
   virtual nsresult Dispatch(const char* aName,
                             mozilla::TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
-  virtual nsIEventTarget*
+  virtual nsISerialEventTarget*
   EventTargetFor(mozilla::TaskCategory aCategory) const override;
 
   virtual mozilla::AbstractThread*
   AbstractMainThreadFor(mozilla::TaskCategory aCategory) override;
 
   // The URLs passed to these functions should match what
   // JS::DescribeScriptedCaller() returns, since these APIs are used to
   // determine whether some code is being called from a tracking script.
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -24,20 +24,20 @@
 
 class nsGlobalWindow;
 class nsIArray;
 class nsIContent;
 class nsICSSDeclaration;
 class nsIDocShell;
 class nsIDocShellLoadInfo;
 class nsIDocument;
-class nsIEventTarget;
 class nsIIdleObserver;
 class nsIPrincipal;
 class nsIScriptTimeoutHandler;
+class nsISerialEventTarget;
 class nsIURI;
 class nsPIDOMWindowInner;
 class nsPIDOMWindowOuter;
 class nsPIWindowRoot;
 class nsXBLPrototypeHandler;
 
 typedef uint32_t SuspendTypes;
 
@@ -604,17 +604,17 @@ public:
 
   virtual nsresult MoveBy(int32_t aXDif, int32_t aYDif) = 0;
   virtual nsresult UpdateCommands(const nsAString& anAction, nsISelection* aSel, int16_t aReason) = 0;
 
   mozilla::dom::TabGroup* TabGroup();
 
   mozilla::dom::DocGroup* GetDocGroup() const;
 
-  virtual nsIEventTarget*
+  virtual nsISerialEventTarget*
   EventTargetFor(mozilla::TaskCategory aCategory) const = 0;
 
 protected:
   // The nsPIDOMWindow constructor. The aOuterWindow argument should
   // be null if and only if the created window itself is an outer
   // window. In all other cases aOuterWindow should be the outer
   // window for the inner window that is being created.
   explicit nsPIDOMWindow<T>(nsPIDOMWindowOuter *aOuterWindow);
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -4464,33 +4464,33 @@ WorkerPrivate::WorkerPrivate(WorkerPriva
     mOnLine = aParent->OnLine();
   }
   else {
     AssertIsOnMainThread();
     RuntimeService::GetDefaultPreferences(mPreferences);
     mOnLine = !NS_IsOffline();
   }
 
-  nsCOMPtr<nsIEventTarget> target;
+  nsCOMPtr<nsISerialEventTarget> target;
 
   // A child worker just inherits the parent workers ThrottledEventQueue
   // and main thread target for now.  This is mainly due to the restriction
   // that ThrottledEventQueue can only be created on the main thread at the
   // moment.
   if (aParent) {
     mMainThreadThrottledEventQueue = aParent->mMainThreadThrottledEventQueue;
     mMainThreadEventTarget = aParent->mMainThreadEventTarget;
     return;
   }
 
   MOZ_ASSERT(NS_IsMainThread());
   target = GetWindow() ? GetWindow()->EventTargetFor(TaskCategory::Worker) : nullptr;
 
   if (!target) {
-    target = GetMainThreadEventTarget();
+    target = GetMainThreadSerialEventTarget();
     MOZ_DIAGNOSTIC_ASSERT(target);
   }
 
   // Throttle events to the main thread using a ThrottledEventQueue specific to
   // this worker thread.  This may return nullptr during shutdown.
   mMainThreadThrottledEventQueue = ThrottledEventQueue::Create(target);
 
   // If we were able to creat the throttled event queue, then use it for
--- a/xpcom/tests/gtest/TestEventTargetQI.cpp
+++ b/xpcom/tests/gtest/TestEventTargetQI.cpp
@@ -57,17 +57,17 @@ TEST(TestEventTargetQI, Thread)
 
 TEST(TestEventTargetQI, ThrottledEventQueue)
 {
   nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
   RefPtr<ThrottledEventQueue> thing = ThrottledEventQueue::Create(thread);
   EXPECT_TRUE(thing);
 
   nsCOMPtr<nsISerialEventTarget> serial = do_QueryInterface(thing);
-  EXPECT_FALSE(serial);
+  EXPECT_TRUE(serial);
 
   nsCOMPtr<nsIEventTarget> target = do_QueryInterface(thing);
   EXPECT_TRUE(target);
 }
 
 TEST(TestEventTargetQI, LazyIdleThread)
 {
   nsCOMPtr<nsIThread> thing = new LazyIdleThread(0, NS_LITERAL_CSTRING("TestThread"));
--- a/xpcom/threads/SchedulerGroup.cpp
+++ b/xpcom/threads/SchedulerGroup.cpp
@@ -223,17 +223,17 @@ SchedulerGroup::SchedulerGroup()
 nsresult
 SchedulerGroup::Dispatch(const char* aName,
                          TaskCategory aCategory,
                          already_AddRefed<nsIRunnable>&& aRunnable)
 {
   return LabeledDispatch(aName, aCategory, Move(aRunnable));
 }
 
-nsIEventTarget*
+nsISerialEventTarget*
 SchedulerGroup::EventTargetFor(TaskCategory aCategory) const
 {
   MOZ_ASSERT(aCategory != TaskCategory::Count);
   MOZ_ASSERT(mEventTargets[size_t(aCategory)]);
   return mEventTargets[size_t(aCategory)];
 }
 
 AbstractThread*
@@ -263,37 +263,37 @@ void
 SchedulerGroup::CreateEventTargets(bool aNeedValidation)
 {
   for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
     TaskCategory category = static_cast<TaskCategory>(i);
     if (!aNeedValidation) {
       // The chrome TabGroup dispatches directly to the main thread. This means
       // that we don't have to worry about cyclical references when cleaning up
       // the chrome TabGroup.
-      mEventTargets[i] = do_GetMainThread();
+      mEventTargets[i] = GetMainThreadSerialEventTarget();
     } else {
       mEventTargets[i] = CreateEventTargetFor(category);
     }
   }
 }
 
 void
 SchedulerGroup::Shutdown(bool aXPCOMShutdown)
 {
   // There is a RefPtr cycle TabGroup -> SchedulerEventTarget -> TabGroup. To
   // avoid leaks, we need to break the chain somewhere. We shouldn't be using
   // the ThrottledEventQueue for this TabGroup when no windows belong to it,
   // so it's safe to null out the queue here.
   for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
-    mEventTargets[i] = aXPCOMShutdown ? nullptr : do_GetMainThread();
+    mEventTargets[i] = aXPCOMShutdown ? nullptr : GetMainThreadSerialEventTarget();
     mAbstractThreads[i] = nullptr;
   }
 }
 
-already_AddRefed<nsIEventTarget>
+already_AddRefed<nsISerialEventTarget>
 SchedulerGroup::CreateEventTargetFor(TaskCategory aCategory)
 {
   RefPtr<SchedulerEventTarget> target =
     new SchedulerEventTarget(this, aCategory);
   return target.forget();
 }
 
 /* static */ SchedulerGroup*
--- a/xpcom/threads/SchedulerGroup.h
+++ b/xpcom/threads/SchedulerGroup.h
@@ -11,16 +11,17 @@
 #include "mozilla/TaskCategory.h"
 #include "mozilla/TimeStamp.h"
 #include "nsCOMPtr.h"
 #include "nsISupportsImpl.h"
 #include "nsThreadUtils.h"
 
 class nsIEventTarget;
 class nsIRunnable;
+class nsISerialEventTarget;
 
 namespace mozilla {
 class AbstractThread;
 namespace dom {
 class TabGroup;
 }
 
 #define NS_SCHEDULERGROUPRUNNABLE_IID \
@@ -98,17 +99,17 @@ public:
   friend class Runnable;
 
   bool* GetValidAccessPtr() { return &mAccessValid; }
 
   virtual nsresult Dispatch(const char* aName,
                             TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable);
 
-  virtual nsIEventTarget* EventTargetFor(TaskCategory aCategory) const;
+  virtual nsISerialEventTarget* EventTargetFor(TaskCategory aCategory) const;
 
   // Must always be called on the main thread. The returned AbstractThread can
   // always be used off the main thread.
   AbstractThread* AbstractMainThreadFor(TaskCategory aCategory);
 
   // This method performs a safe cast. It returns null if |this| is not of the
   // requested type.
   virtual dom::TabGroup* AsTabGroup() { return nullptr; }
@@ -122,17 +123,17 @@ public:
   static void MarkVsyncRan();
 
 protected:
   // Implementations are guaranteed that this method is called on the main
   // thread.
   virtual AbstractThread* AbstractMainThreadForImpl(TaskCategory aCategory);
 
   // Helper method to create an event target specific to a particular TaskCategory.
-  virtual already_AddRefed<nsIEventTarget>
+  virtual already_AddRefed<nsISerialEventTarget>
   CreateEventTargetFor(TaskCategory aCategory);
 
   // Given an event target returned by |dispatcher->CreateEventTargetFor|, this
   // function returns |dispatcher|.
   static SchedulerGroup* FromEventTarget(nsIEventTarget* aEventTarget);
 
   nsresult LabeledDispatch(const char* aName,
                            TaskCategory aCategory,
@@ -148,17 +149,17 @@ protected:
     StartValidation,
     EndValidation,
   };
   void SetValidatingAccess(ValidationType aType);
 
   static SchedulerGroup* sRunningDispatcher;
   bool mAccessValid;
 
-  nsCOMPtr<nsIEventTarget> mEventTargets[size_t(TaskCategory::Count)];
+  nsCOMPtr<nsISerialEventTarget> mEventTargets[size_t(TaskCategory::Count)];
   RefPtr<AbstractThread> mAbstractThreads[size_t(TaskCategory::Count)];
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(SchedulerGroup::Runnable, NS_SCHEDULERGROUPRUNNABLE_IID);
 
 } // namespace mozilla
 
 #endif // mozilla_SchedulerGroup_h
--- a/xpcom/threads/SystemGroup.cpp
+++ b/xpcom/threads/SystemGroup.cpp
@@ -87,17 +87,17 @@ SystemGroup::Initialized()
 /* static */ nsresult
 SystemGroup::Dispatch(const char* aName,
                       TaskCategory aCategory,
                       already_AddRefed<nsIRunnable>&& aRunnable)
 {
   return SystemGroupImpl::Get()->Dispatch(aName, aCategory, Move(aRunnable));
 }
 
-/* static */ nsIEventTarget*
+/* static */ nsISerialEventTarget*
 SystemGroup::EventTargetFor(TaskCategory aCategory)
 {
   return SystemGroupImpl::Get()->EventTargetFor(aCategory);
 }
 
 /* static */ AbstractThread*
 SystemGroup::AbstractMainThreadFor(TaskCategory aCategory)
 {
--- a/xpcom/threads/SystemGroup.h
+++ b/xpcom/threads/SystemGroup.h
@@ -21,17 +21,17 @@ class SystemGroup
 {
  public:
   // This method is safe to use from any thread.
   static nsresult Dispatch(const char* aName,
                            TaskCategory aCategory,
                            already_AddRefed<nsIRunnable>&& aRunnable);
 
   // This method is safe to use from any thread.
-  static nsIEventTarget* EventTargetFor(TaskCategory aCategory);
+  static nsISerialEventTarget* EventTargetFor(TaskCategory aCategory);
 
   // Must be called on the main thread. The AbstractThread can always be used
   // off the main thread.
   static AbstractThread* AbstractMainThreadFor(TaskCategory aCategory);
 
   static void InitStatic();
   static void Shutdown();
 
--- a/xpcom/threads/ThrottledEventQueue.cpp
+++ b/xpcom/threads/ThrottledEventQueue.cpp
@@ -86,25 +86,25 @@ class ThrottledEventQueue::Inner final :
   mutable CondVar mIdleCondVar;
 
   mozilla::CondVar mEventsAvailable;
 
   // any thread, protected by mutex
   nsEventQueue mEventQueue;
 
   // written on main thread, read on any thread
-  nsCOMPtr<nsIEventTarget> mBaseTarget;
+  nsCOMPtr<nsISerialEventTarget> mBaseTarget;
 
   // any thread, protected by mutex
   nsCOMPtr<nsIRunnable> mExecutor;
 
   // any thread, protected by mutex
   bool mShutdownStarted;
 
-  explicit Inner(nsIEventTarget* aBaseTarget)
+  explicit Inner(nsISerialEventTarget* aBaseTarget)
     : mMutex("ThrottledEventQueue")
     , mIdleCondVar(mMutex, "ThrottledEventQueue:Idle")
     , mEventsAvailable(mMutex, "[ThrottledEventQueue::Inner.mEventsAvailable]")
     , mEventQueue(mEventsAvailable, nsEventQueue::eNormalQueue)
     , mBaseTarget(aBaseTarget)
     , mShutdownStarted(false)
   {
   }
@@ -205,17 +205,17 @@ class ThrottledEventQueue::Inner final :
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(IsEmpty());
     nsCOMPtr<nsIObserverService> obs = GetObserverService();
     obs->RemoveObserver(this, kShutdownTopic);
   }
 
 public:
   static already_AddRefed<Inner>
-  Create(nsIEventTarget* aBaseTarget)
+  Create(nsISerialEventTarget* aBaseTarget)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     if (ClearOnShutdown_Internal::sCurrentShutdownPhase != ShutdownPhase::NotInShutdown) {
       return nullptr;
     }
 
     nsCOMPtr<nsIObserverService> obs = GetObserverService();
@@ -371,17 +371,18 @@ public:
 
   NS_DECL_THREADSAFE_ISUPPORTS
 };
 
 NS_IMPL_ISUPPORTS(ThrottledEventQueue::Inner, nsIObserver);
 
 NS_IMPL_ISUPPORTS(ThrottledEventQueue,
                   ThrottledEventQueue,
-                  nsIEventTarget);
+                  nsIEventTarget,
+                  nsISerialEventTarget);
 
 ThrottledEventQueue::ThrottledEventQueue(already_AddRefed<Inner> aInner)
   : mInner(aInner)
 {
   MOZ_ASSERT(mInner);
 }
 
 ThrottledEventQueue::~ThrottledEventQueue()
@@ -391,17 +392,17 @@ ThrottledEventQueue::~ThrottledEventQueu
 
 void
 ThrottledEventQueue::MaybeStartShutdown()
 {
   return mInner->MaybeStartShutdown();
 }
 
 already_AddRefed<ThrottledEventQueue>
-ThrottledEventQueue::Create(nsIEventTarget* aBaseTarget)
+ThrottledEventQueue::Create(nsISerialEventTarget* aBaseTarget)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aBaseTarget);
 
   RefPtr<Inner> inner = Inner::Create(aBaseTarget);
   if (NS_WARN_IF(!inner)) {
     return nullptr;
   }
--- a/xpcom/threads/ThrottledEventQueue.h
+++ b/xpcom/threads/ThrottledEventQueue.h
@@ -4,17 +4,17 @@
  * 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/. */
 
 // nsIEventTarget wrapper for throttling event dispatch.
 
 #ifndef mozilla_ThrottledEventQueue_h
 #define mozilla_ThrottledEventQueue_h
 
-#include "nsIEventTarget.h"
+#include "nsISerialEventTarget.h"
 
 #define NS_THROTTLEDEVENTQUEUE_IID \
 { 0x8f3cf7dc, 0xfc14, 0x4ad5, \
   { 0x9f, 0xd5, 0xdb, 0x79, 0xbc, 0xe6, 0xd5, 0x08 } }
 
 namespace mozilla {
 
 // A ThrottledEventQueue is an event target that can be used to throttle
@@ -53,17 +53,17 @@ namespace mozilla {
 // target, you probably want a ThrottledEventQueue.
 //
 // ThrottledEventQueue also implements an automatic shutdown mechanism.
 // De-referencing the queue or browser shutdown will automatically begin
 // shutdown.
 //
 // Once shutdown begins all events will bypass the queue and be dispatched
 // straight to the underlying base target.
-class ThrottledEventQueue final : public nsIEventTarget
+class ThrottledEventQueue final : public nsISerialEventTarget
 {
   class Inner;
   RefPtr<Inner> mInner;
 
   explicit ThrottledEventQueue(already_AddRefed<Inner> aInner);
   ~ThrottledEventQueue();
 
   // Begin shutdown of the event queue.  This has no effect if shutdown
@@ -72,17 +72,17 @@ class ThrottledEventQueue final : public
   // Note, this could be made public if code needs to explicitly shutdown
   // for some reason.
   void MaybeStartShutdown();
 
 public:
   // Attempt to create a ThrottledEventQueue for the given target.  This
   // may return nullptr if the browser is already shutting down.
   static already_AddRefed<ThrottledEventQueue>
-  Create(nsIEventTarget* aBaseTarget);
+  Create(nsISerialEventTarget* aBaseTarget);
 
   // Determine if there are any events pending in the queue.
   bool IsEmpty() const;
 
   // Determine how many events are pending in the queue.
   uint32_t Length() const;
 
   // Block the current thread until the queue is empty.  This may not
--- a/xpcom/threads/nsISerialEventTarget.idl
+++ b/xpcom/threads/nsISerialEventTarget.idl
@@ -5,17 +5,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIEventTarget.idl"
 
 /**
  * A serial event target is an event dispatching interface like
  * nsIEventTarget. Runnables dispatched to an nsISerialEventTarget are required
  * to execute serially. That is, two different runnables dispatched to the
- * target should never be allowed to execute simultaneously. So, for example, a
- * thread pool should not implement nsISerialEventTarget. However, one can
- * "convert" a thread pool into an nsISerialEventTarget by putting a
- * TaskQueue in front of it.
+ * target should never be allowed to execute simultaneously. One exception to
+ * this rule is nested event loops. If a runnable spins a nested event loop,
+ * causing another runnable dispatched to the target to run, the target may
+ * still be considered "serial".
+ *
+ * Examples:
+ * - nsIThread is a serial event target.
+ * - Thread pools are not serial event targets.
+ * - However, one can "convert" a thread pool into an nsISerialEventTarget
+ *   by putting a TaskQueue in front of it.
  */
 [builtinclass, scriptable, uuid(9f982380-24b4-49f3-88f6-45e2952036c7)]
 interface nsISerialEventTarget : nsIEventTarget
 {
 };