Bug 1337537 - Move Dispatcher.{cpp,h} to xpcom/threads (r=froydnj)
authorBill McCloskey <billm@mozilla.com>
Mon, 30 Jan 2017 16:13:18 -0800
changeset 391683 1021af9fea0035f26e71723905f115bbd64fd099
parent 391682 3092fa33385e44cef90c76ba00c46eb9439797f2
child 391684 7c141bd7f221a3c638786ad64229e1aaec04a02b
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1337537
milestone54.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 1337537 - Move Dispatcher.{cpp,h} to xpcom/threads (r=froydnj) MozReview-Commit-ID: AYyKynUv4bX
dom/base/Dispatcher.cpp
dom/base/Dispatcher.h
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/moz.build
dom/base/nsIDocument.h
dom/base/nsIGlobalObject.h
dom/base/nsPIDOMWindow.h
xpcom/threads/Dispatcher.cpp
xpcom/threads/Dispatcher.h
xpcom/threads/moz.build
deleted file mode 100644
--- a/dom/base/Dispatcher.cpp
+++ /dev/null
@@ -1,126 +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 "mozilla/dom/Dispatcher.h"
-
-#include "mozilla/AbstractThread.h"
-#include "mozilla/Move.h"
-#include "nsINamed.h"
-#include "nsQueryObject.h"
-
-using namespace mozilla;
-
-nsresult
-DispatcherTrait::Dispatch(const char* aName,
-                          TaskCategory aCategory,
-                          already_AddRefed<nsIRunnable>&& aRunnable)
-{
-  nsCOMPtr<nsIRunnable> runnable(aRunnable);
-  if (aName) {
-    if (nsCOMPtr<nsINamed> named = do_QueryInterface(runnable)) {
-      named->SetName(aName);
-    }
-  }
-  if (NS_IsMainThread()) {
-    return NS_DispatchToCurrentThread(runnable.forget());
-  } else {
-    return NS_DispatchToMainThread(runnable.forget());
-  }
-}
-
-nsIEventTarget*
-DispatcherTrait::EventTargetFor(TaskCategory aCategory) const
-{
-  nsCOMPtr<nsIEventTarget> main = do_GetMainThread();
-  return main;
-}
-
-AbstractThread*
-DispatcherTrait::AbstractMainThreadFor(TaskCategory aCategory)
-{
-  // Return non DocGroup version by default.
-  return AbstractThread::MainThread();
-}
-
-namespace {
-
-#define NS_DISPATCHEREVENTTARGET_IID \
-{ 0xbf4e36c8, 0x7d04, 0x4ef4, \
-  { 0xbb, 0xd8, 0x11, 0x09, 0x0a, 0xdb, 0x4d, 0xf7 } }
-
-class DispatcherEventTarget final : public nsIEventTarget
-{
-  RefPtr<dom::Dispatcher> mDispatcher;
-  TaskCategory mCategory;
-
-public:
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_DISPATCHEREVENTTARGET_IID)
-
-  DispatcherEventTarget(dom::Dispatcher* aDispatcher, TaskCategory aCategory)
-   : mDispatcher(aDispatcher)
-   , mCategory(aCategory)
-  {}
-
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIEVENTTARGET
-
-  dom::Dispatcher* Dispatcher() const { return mDispatcher; }
-
-private:
-  virtual ~DispatcherEventTarget() {}
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(DispatcherEventTarget, NS_DISPATCHEREVENTTARGET_IID)
-
-} // namespace
-
-NS_IMPL_ISUPPORTS(DispatcherEventTarget, DispatcherEventTarget, nsIEventTarget)
-
-NS_IMETHODIMP
-DispatcherEventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
-{
-  return Dispatch(do_AddRef(aRunnable), aFlags);
-}
-
-NS_IMETHODIMP
-DispatcherEventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags)
-{
-  if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
-    return NS_ERROR_UNEXPECTED;
-  }
-  return mDispatcher->Dispatch(nullptr, mCategory, Move(aRunnable));
-}
-
-NS_IMETHODIMP
-DispatcherEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-DispatcherEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
-{
-  *aIsOnCurrentThread = NS_IsMainThread();
-  return NS_OK;
-}
-
-already_AddRefed<nsIEventTarget>
-Dispatcher::CreateEventTargetFor(TaskCategory aCategory)
-{
-  RefPtr<DispatcherEventTarget> target =
-    new DispatcherEventTarget(this, aCategory);
-  return target.forget();
-}
-
-/* static */ Dispatcher*
-Dispatcher::FromEventTarget(nsIEventTarget* aEventTarget)
-{
-  RefPtr<DispatcherEventTarget> target = do_QueryObject(aEventTarget);
-  if (!target) {
-    return nullptr;
-  }
-  return target->Dispatcher();
-}
deleted file mode 100644
--- a/dom/base/Dispatcher.h
+++ /dev/null
@@ -1,83 +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/. */
-
-#ifndef mozilla_dom_Dispatcher_h
-#define mozilla_dom_Dispatcher_h
-
-#include "mozilla/AlreadyAddRefed.h"
-#include "mozilla/TaskCategory.h"
-#include "nsISupports.h"
-
-class nsIEventTarget;
-class nsIRunnable;
-
-// This file defines basic functionality for dispatching runnables to various
-// groups: either the SystemGroup or a DocGroup or TabGroup. Ideally all
-// runnables destined for the main thread should be dispatched to a group
-// instead so that we know what kind of web content they'll be
-// touching. Runnables sent to the SystemGroup never touch web
-// content. Runnables sent to a DocGroup can only touch documents belonging to
-// that DocGroup. Runnables sent to a TabGroup can touch any document in any of
-// the tabs belonging to the TabGroup.
-
-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
-// delegate to the DocGroup or TabGroup. We can't use the Dispatcher class
-// directly because it inherits from nsISupports.
-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
-  // always be used off the main thread.
-  virtual nsIEventTarget* 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);
-};
-
-// Base class for DocGroup and TabGroup.
-class Dispatcher : public nsISupports {
-public:
-  // This method is always safe to call off the main thread.
-  virtual nsresult Dispatch(const char* aName,
-                            TaskCategory aCategory,
-                            already_AddRefed<nsIRunnable>&& aRunnable) = 0;
-
-  // This method is always safe to call off the main thread. The nsIEventTarget
-  // can always be used off the main thread.
-  virtual nsIEventTarget* EventTargetFor(TaskCategory aCategory) const = 0;
-
-  // Must be called on the main thread. The AbstractThread can always be used
-  // off the main thread.
-  virtual AbstractThread* AbstractMainThreadFor(TaskCategory aCategory) = 0;
-
-  // This method performs a safe cast. It returns null if |this| is not of the
-  // requested type.
-  virtual TabGroup* AsTabGroup() { return nullptr; }
-
-protected:
-  virtual already_AddRefed<nsIEventTarget>
-  CreateEventTargetFor(TaskCategory aCategory);
-
-  static Dispatcher* FromEventTarget(nsIEventTarget* aEventTarget);
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_Dispatcher_h
new file mode 100644
--- /dev/null
+++ b/dom/base/DispatcherTrait.cpp
@@ -0,0 +1,45 @@
+/* -*- 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 "mozilla/dom/DispatcherTrait.h"
+
+#include "mozilla/AbstractThread.h"
+#include "nsINamed.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+nsresult
+DispatcherTrait::Dispatch(const char* aName,
+                          TaskCategory aCategory,
+                          already_AddRefed<nsIRunnable>&& aRunnable)
+{
+  nsCOMPtr<nsIRunnable> runnable(aRunnable);
+  if (aName) {
+    if (nsCOMPtr<nsINamed> named = do_QueryInterface(runnable)) {
+      named->SetName(aName);
+    }
+  }
+  if (NS_IsMainThread()) {
+    return NS_DispatchToCurrentThread(runnable.forget());
+  } else {
+    return NS_DispatchToMainThread(runnable.forget());
+  }
+}
+
+nsIEventTarget*
+DispatcherTrait::EventTargetFor(TaskCategory aCategory) const
+{
+  nsCOMPtr<nsIEventTarget> main = do_GetMainThread();
+  return main;
+}
+
+AbstractThread*
+DispatcherTrait::AbstractMainThreadFor(TaskCategory aCategory)
+{
+  // Return non DocGroup version by default.
+  return AbstractThread::MainThread();
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/DispatcherTrait.h
@@ -0,0 +1,46 @@
+/* -*- 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_DispatcherTrait_h
+#define mozilla_dom_DispatcherTrait_h
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/TaskCategory.h"
+
+class nsIEventTarget;
+class nsIRunnable;
+
+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
+// delegate to the DocGroup or TabGroup. We can't use the Dispatcher class
+// directly because it inherits from nsISupports.
+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
+  // always be used off the main thread.
+  virtual nsIEventTarget* 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
+
+#endif // mozilla_dom_DispatcherTrait_h
--- a/dom/base/DocGroup.cpp
+++ b/dom/base/DocGroup.cpp
@@ -44,33 +44,31 @@ DocGroup::DocGroup(TabGroup* aTabGroup, 
 }
 
 DocGroup::~DocGroup()
 {
   MOZ_ASSERT(mDocuments.IsEmpty());
   mTabGroup->mDocGroups.RemoveEntry(mKey);
 }
 
-NS_IMPL_ISUPPORTS(DocGroup, nsISupports)
-
 nsresult
 DocGroup::Dispatch(const char* aName,
                    TaskCategory aCategory,
                    already_AddRefed<nsIRunnable>&& aRunnable)
 {
   return mTabGroup->Dispatch(aName, aCategory, Move(aRunnable));
 }
 
 nsIEventTarget*
 DocGroup::EventTargetFor(TaskCategory aCategory) const
 {
   return mTabGroup->EventTargetFor(aCategory);
 }
 
 AbstractThread*
-DocGroup::AbstractMainThreadFor(TaskCategory aCategory)
+DocGroup::AbstractMainThreadForImpl(TaskCategory aCategory)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   return mTabGroup->AbstractMainThreadFor(aCategory);
 }
 
 }
 }
--- a/dom/base/DocGroup.h
+++ b/dom/base/DocGroup.h
@@ -2,23 +2,22 @@
 /* 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 DocGroup_h
 #define DocGroup_h
 
-#include "nsISupports.h"
 #include "nsISupportsImpl.h"
 #include "nsIPrincipal.h"
 #include "nsTHashtable.h"
 #include "nsString.h"
 
-#include "mozilla/dom/Dispatcher.h"
+#include "mozilla/Dispatcher.h"
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
 class AbstractThread;
 namespace dom {
 
 // Two browsing contexts are considered "related" if they are reachable from one
 // another through window.opener, window.parent, or window.frames. This is the
@@ -38,17 +37,17 @@ namespace dom {
 class TabGroup;
 
 class DocGroup final : public Dispatcher
 {
 public:
   typedef nsTArray<nsIDocument*>::iterator Iterator;
   friend class TabGroup;
 
-  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DocGroup, override)
 
   // Returns NS_ERROR_FAILURE and sets |aString| to an empty string if the TLD
   // service isn't available. Returns NS_OK on success, but may still set
   // |aString| may still be set to an empty string.
   static MOZ_MUST_USE nsresult
   GetKey(nsIPrincipal* aPrincipal, nsACString& aString);
 
   bool MatchesKey(const nsACString& aKey)
@@ -72,20 +71,20 @@ public:
   }
 
   virtual nsresult Dispatch(const char* aName,
                             TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   virtual nsIEventTarget* EventTargetFor(TaskCategory aCategory) const override;
 
+private:
   virtual AbstractThread*
-  AbstractMainThreadFor(TaskCategory aCategory) override;
+  AbstractMainThreadForImpl(TaskCategory aCategory) override;
 
-private:
   DocGroup(TabGroup* aTabGroup, const nsACString& aKey);
   ~DocGroup();
 
   nsCString mKey;
   RefPtr<TabGroup> mTabGroup;
   nsTArray<nsIDocument*> mDocuments;
 };
 
--- a/dom/base/TabGroup.cpp
+++ b/dom/base/TabGroup.cpp
@@ -238,18 +238,16 @@ TabGroup::GetTopLevelWindows()
         !outerWindow->GetScriptableParentOrNull()) {
       array.AppendElement(outerWindow);
     }
   }
 
   return array;
 }
 
-NS_IMPL_ISUPPORTS(TabGroup, nsISupports)
-
 TabGroup::HashEntry::HashEntry(const nsACString* aKey)
   : nsCStringHashKey(aKey), mDocGroup(nullptr)
 {}
 
 nsresult
 TabGroup::Dispatch(const char* aName,
                    TaskCategory aCategory,
                    already_AddRefed<nsIRunnable>&& aRunnable)
@@ -281,17 +279,17 @@ TabGroup::EventTargetFor(TaskCategory aC
     nsCOMPtr<nsIEventTarget> main = do_GetMainThread();
     return main;
   }
 
   return mEventTargets[size_t(aCategory)];
 }
 
 AbstractThread*
-TabGroup::AbstractMainThreadFor(TaskCategory aCategory)
+TabGroup::AbstractMainThreadForImpl(TaskCategory aCategory)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aCategory != TaskCategory::Count);
 
   // The mEventTargets of the chrome TabGroup are all set to do_GetMainThread().
   // We could just return AbstractThread::MainThread() without a wrapper.
   // Once we've disconnected everything, we still allow people to dispatch.
   // We'll just go directly to the main thread.
--- a/dom/base/TabGroup.h
+++ b/dom/base/TabGroup.h
@@ -2,24 +2,23 @@
 /* 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 TabGroup_h
 #define TabGroup_h
 
-#include "nsISupports.h"
 #include "nsISupportsImpl.h"
 #include "nsIPrincipal.h"
 #include "nsTHashtable.h"
 #include "nsString.h"
 
 #include "mozilla/Atomics.h"
-#include "mozilla/dom/Dispatcher.h"
+#include "mozilla/Dispatcher.h"
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
 class AbstractThread;
 class ThrottledEventQueue;
 namespace dom {
 
 // Two browsing contexts are considered "related" if they are reachable from one
@@ -52,17 +51,17 @@ private:
   };
 
   typedef nsTHashtable<HashEntry> DocGroupMap;
 public:
   typedef DocGroupMap::Iterator Iterator;
 
   friend class DocGroup;
 
-  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TabGroup, override)
 
   static TabGroup*
   GetChromeTabGroup();
 
   // Checks if the PBrowserChild associated with aWindow already has a TabGroup
   // assigned to it in IPDL. Returns this TabGroup if it does. This could happen
   // if the parent process created the PBrowser and we needed to assign a
   // TabGroup immediately upon receiving the IPDL message. This method is main
@@ -117,20 +116,20 @@ public:
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   // This method is always safe to call off the main thread. The nsIEventTarget
   // can always be used off the main thread.
   virtual nsIEventTarget* EventTargetFor(TaskCategory aCategory) const override;
 
   TabGroup* AsTabGroup() override { return this; }
 
+private:
   virtual AbstractThread*
-  AbstractMainThreadFor(TaskCategory aCategory) override;
+  AbstractMainThreadForImpl(TaskCategory aCategory) override;
 
-private:
   void EnsureThrottledEventQueues();
 
   ~TabGroup();
 
   // Thread-safe members
   Atomic<bool> mLastWindowLeft;
   Atomic<bool> mThrottledQueuesInitialized;
   const bool mIsChrome;
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -149,17 +149,17 @@ EXPORTS.mozilla.dom += [
     'BodyUtil.h',
     'BorrowedAttrInfo.h',
     'ChildIterator.h',
     'ChromeNodeList.h',
     'ChromeUtils.h',
     'Comment.h',
     'CustomElementRegistry.h',
     'DirectionalityUtils.h',
-    'Dispatcher.h',
+    'DispatcherTrait.h',
     'DocGroup.h',
     'DocumentFragment.h',
     'DocumentType.h',
     'DOMCursor.h',
     'DOMError.h',
     'DOMException.h',
     'DOMImplementation.h',
     'DOMIntersectionObserver.h',
@@ -221,17 +221,17 @@ UNIFIED_SOURCES += [
     'BorrowedAttrInfo.cpp',
     'ChildIterator.cpp',
     'ChromeNodeList.cpp',
     'ChromeUtils.cpp',
     'Comment.cpp',
     'Crypto.cpp',
     'CustomElementRegistry.cpp',
     'DirectionalityUtils.cpp',
-    'Dispatcher.cpp',
+    'DispatcherTrait.cpp',
     'DocGroup.cpp',
     'DocumentFragment.cpp',
     'DocumentType.cpp',
     'DOMCursor.cpp',
     'DOMError.cpp',
     'DOMException.cpp',
     'DOMImplementation.cpp',
     'DOMMatrix.cpp',
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -27,17 +27,17 @@
 #include "mozilla/UseCounter.h"
 #include "mozilla/WeakPtr.h"
 #include "Units.h"
 #include "nsContentListDeclarations.h"
 #include "nsExpirationTracker.h"
 #include "nsClassHashtable.h"
 #include "prclist.h"
 #include "mozilla/CORSMode.h"
-#include "mozilla/dom/Dispatcher.h"
+#include "mozilla/dom/DispatcherTrait.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/StyleBackendType.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 #include <bitset>                        // for member
 
 #ifdef MOZILLA_INTERNAL_API
--- a/dom/base/nsIGlobalObject.h
+++ b/dom/base/nsIGlobalObject.h
@@ -2,17 +2,17 @@
 /* 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 nsIGlobalObject_h__
 #define nsIGlobalObject_h__
 
-#include "mozilla/dom/Dispatcher.h"
+#include "mozilla/dom/DispatcherTrait.h"
 #include "nsISupports.h"
 #include "nsTArray.h"
 #include "js/TypeDecls.h"
 
 #define NS_IGLOBALOBJECT_IID \
 { 0x11afa8be, 0xd997, 0x4e07, \
 { 0xa6, 0xa3, 0x6f, 0x87, 0x2e, 0xc3, 0xee, 0x7f } }
 
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -7,33 +7,34 @@
 #ifndef nsPIDOMWindow_h__
 #define nsPIDOMWindow_h__
 
 #include "nsIDOMWindow.h"
 #include "mozIDOMWindow.h"
 
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
-#include "mozilla/dom/Dispatcher.h"
 #include "mozilla/dom/EventTarget.h"
+#include "mozilla/TaskCategory.h"
 #include "js/TypeDecls.h"
 #include "nsRefPtrHashtable.h"
 
 // Only fired for inner windows.
 #define DOM_WINDOW_DESTROYED_TOPIC "dom-window-destroyed"
 #define DOM_WINDOW_FROZEN_TOPIC "dom-window-frozen"
 #define DOM_WINDOW_THAWED_TOPIC "dom-window-thawed"
 
 class nsGlobalWindow;
 class nsIArray;
 class nsIContent;
 class nsICSSDeclaration;
 class nsIDocShell;
 class nsIDocShellLoadInfo;
 class nsIDocument;
+class nsIEventTarget;
 class nsIIdleObserver;
 class nsIPrincipal;
 class nsIScriptTimeoutHandler;
 class nsIURI;
 class nsPIDOMWindowInner;
 class nsPIDOMWindowOuter;
 class nsPIWindowRoot;
 class nsXBLPrototypeHandler;
new file mode 100644
--- /dev/null
+++ b/xpcom/threads/Dispatcher.cpp
@@ -0,0 +1,101 @@
+/* -*- 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 "mozilla/Dispatcher.h"
+
+#include "mozilla/AbstractThread.h"
+#include "mozilla/Move.h"
+#include "nsINamed.h"
+#include "nsQueryObject.h"
+
+using namespace mozilla;
+
+namespace {
+
+#define NS_DISPATCHEREVENTTARGET_IID \
+{ 0xbf4e36c8, 0x7d04, 0x4ef4, \
+  { 0xbb, 0xd8, 0x11, 0x09, 0x0a, 0xdb, 0x4d, 0xf7 } }
+
+class DispatcherEventTarget final : public nsIEventTarget
+{
+  RefPtr<Dispatcher> mDispatcher;
+  TaskCategory mCategory;
+
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_DISPATCHEREVENTTARGET_IID)
+
+  DispatcherEventTarget(Dispatcher* aDispatcher, TaskCategory aCategory)
+   : mDispatcher(aDispatcher)
+   , mCategory(aCategory)
+  {}
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIEVENTTARGET
+
+  Dispatcher* Dispatcher() const { return mDispatcher; }
+
+private:
+  ~DispatcherEventTarget() {}
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(DispatcherEventTarget, NS_DISPATCHEREVENTTARGET_IID)
+
+} // namespace
+
+NS_IMPL_ISUPPORTS(DispatcherEventTarget, DispatcherEventTarget, nsIEventTarget)
+
+NS_IMETHODIMP
+DispatcherEventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
+{
+  return Dispatch(do_AddRef(aRunnable), aFlags);
+}
+
+NS_IMETHODIMP
+DispatcherEventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags)
+{
+  if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+  return mDispatcher->Dispatch(nullptr, mCategory, Move(aRunnable));
+}
+
+NS_IMETHODIMP
+DispatcherEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+DispatcherEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
+{
+  *aIsOnCurrentThread = NS_IsMainThread();
+  return NS_OK;
+}
+
+AbstractThread*
+Dispatcher::AbstractMainThreadFor(TaskCategory aCategory)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  return AbstractMainThreadForImpl(aCategory);
+}
+
+already_AddRefed<nsIEventTarget>
+Dispatcher::CreateEventTargetFor(TaskCategory aCategory)
+{
+  RefPtr<DispatcherEventTarget> target =
+    new DispatcherEventTarget(this, aCategory);
+  return target.forget();
+}
+
+/* static */ Dispatcher*
+Dispatcher::FromEventTarget(nsIEventTarget* aEventTarget)
+{
+  RefPtr<DispatcherEventTarget> target = do_QueryObject(aEventTarget);
+  if (!target) {
+    return nullptr;
+  }
+  return target->Dispatcher();
+}
new file mode 100644
--- /dev/null
+++ b/xpcom/threads/Dispatcher.h
@@ -0,0 +1,70 @@
+/* -*- 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_Dispatcher_h
+#define mozilla_Dispatcher_h
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/TaskCategory.h"
+#include "nsISupportsImpl.h"
+
+class nsIEventTarget;
+class nsIRunnable;
+
+namespace mozilla {
+class AbstractThread;
+namespace dom {
+class TabGroup;
+}
+
+// The "main thread" in Gecko will soon be a set of cooperatively scheduled
+// "fibers". Global state in Gecko will be partitioned into a series of "groups"
+// (with roughly one group per tab). Runnables will be annotated with the set of
+// groups that they touch. Two runnables may run concurrently on different
+// fibers as long as they touch different groups.
+//
+// A Dispatcher is an abstract class to represent a "group". Essentially the
+// only functionality offered by a Dispatcher is the ability to dispatch
+// runnables to the group. TabGroup, DocGroup, and SystemGroup are the concrete
+// implementations of Dispatcher.
+class Dispatcher {
+public:
+  NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+  // Implementations of this method must be safe to call off the main thread.
+  virtual nsresult Dispatch(const char* aName,
+                            TaskCategory aCategory,
+                            already_AddRefed<nsIRunnable>&& aRunnable) = 0;
+
+  // Implementations of this method must be safe to call off the main thread.
+  // The returned nsIEventTarget must also be usable from any thread.
+  virtual nsIEventTarget* EventTargetFor(TaskCategory aCategory) const = 0;
+
+  // 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; }
+
+protected:
+  // Implementations are guaranteed that this method is called on the main
+  // thread.
+  virtual AbstractThread* AbstractMainThreadForImpl(TaskCategory aCategory) = 0;
+
+  // Helper method to create an event target specific to a particular TaskCategory.
+  virtual already_AddRefed<nsIEventTarget>
+  CreateEventTargetFor(TaskCategory aCategory);
+
+  // Given an event target returned by |dispatcher->CreateEventTargetFor|, this
+  // function returns |dispatcher|.
+  static Dispatcher* FromEventTarget(nsIEventTarget* aEventTarget);
+};
+
+} // namespace mozilla
+
+#endif // mozilla_dom_Dispatcher_h
--- a/xpcom/threads/moz.build
+++ b/xpcom/threads/moz.build
@@ -34,16 +34,17 @@ EXPORTS += [
 ]
 
 EXPORTS.mozilla += [
     'AbstractThread.h',
     'BackgroundHangMonitor.h',
     'BlockingResourceBase.h',
     'CondVar.h',
     'DeadlockDetector.h',
+    'Dispatcher.h',
     'HangAnnotations.h',
     'HangMonitor.h',
     'LazyIdleThread.h',
     'MainThreadIdlePeriod.h',
     'Monitor.h',
     'MozPromise.h',
     'Mutex.h',
     'ReentrantMonitor.h',
@@ -57,16 +58,17 @@ EXPORTS.mozilla += [
     'TaskQueue.h',
     'ThrottledEventQueue.h',
 ]
 
 UNIFIED_SOURCES += [
     'AbstractThread.cpp',
     'BackgroundHangMonitor.cpp',
     'BlockingResourceBase.cpp',
+    'Dispatcher.cpp',
     'HangAnnotations.cpp',
     'HangMonitor.cpp',
     'LazyIdleThread.cpp',
     'MainThreadIdlePeriod.cpp',
     'nsEnvironment.cpp',
     'nsEventQueue.cpp',
     'nsMemoryPressure.cpp',
     'nsProcessCommon.cpp',