Bug 1305926 - Dispatcher API for assigning DocGroups to runnables (r=ehsan)
authorBill McCloskey <billm@mozilla.com>
Fri, 28 Oct 2016 15:25:08 -0700
changeset 896474 5b16c69a1b81c1e1c101c67639bcf489f0d8cdcb
parent 896473 48a32fd177a4828744b02189bb7b9f3eba561071
child 896475 dcd436e553ed85cadb6538ca4f4382610a7bcd5f
push id155501
push userashiue@mozilla.com
push dateFri, 18 Nov 2016 03:26:06 +0000
treeherdertry@c775a1cfc96e [default view] [failures only]
reviewersehsan
bugs1305926
milestone53.0a1
Bug 1305926 - Dispatcher API for assigning DocGroups to runnables (r=ehsan) MozReview-Commit-ID: 8qO95a6z1Ut
dom/base/CORSMode.h
dom/base/Dispatcher.cpp
dom/base/Dispatcher.h
dom/base/DocGroup.cpp
dom/base/DocGroup.h
dom/base/TabGroup.cpp
dom/base/TabGroup.h
dom/base/Timeout.h
dom/base/moz.build
dom/base/nsDocument.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsIDocument.h
dom/base/nsIGlobalObject.h
--- a/dom/base/CORSMode.h
+++ b/dom/base/CORSMode.h
@@ -2,16 +2,18 @@
 /* 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 CORSMode_h_
 #define CORSMode_h_
 
+#include <stdint.h>
+
 namespace mozilla {
 
 enum CORSMode : uint8_t {
   /**
    * The default of not using CORS to validate cross-origin loads.
    */
   CORS_NONE,
 
new file mode 100644
--- /dev/null
+++ b/dom/base/Dispatcher.cpp
@@ -0,0 +1,18 @@
+/* -*- 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/Move.h"
+
+using namespace mozilla;
+
+nsresult
+DispatcherTrait::Dispatch(const char* aName,
+                          TaskCategory aCategory,
+                          already_AddRefed<nsIRunnable>&& aRunnable)
+{
+  return NS_DispatchToMainThread(Move(aRunnable));
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/Dispatcher.h
@@ -0,0 +1,63 @@
+/* -*- 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 "nsISupports.h"
+
+class nsIRunnable;
+
+namespace mozilla {
+namespace dom {
+
+enum class TaskCategory {
+  // User input (clicks, keypresses, etc.)
+  UI,
+
+  // Data from the network
+  Network,
+
+  // setTimeout, setInterval
+  Timer,
+
+  // requestIdleCallback
+  IdleCallback,
+
+  // Vsync notifications
+  RefreshDriver,
+
+  // Most DOM events (postMessage, media, plugins)
+  Other,
+};
+
+// 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);
+};
+
+// 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;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_Dispatcher_h
--- a/dom/base/DocGroup.cpp
+++ b/dom/base/DocGroup.cpp
@@ -46,10 +46,18 @@ 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 NS_DispatchToMainThread(Move(aRunnable));
+}
+
 }
 }
--- a/dom/base/DocGroup.h
+++ b/dom/base/DocGroup.h
@@ -8,16 +8,17 @@
 #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/RefPtr.h"
 
 namespace mozilla {
 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
 // spec concept of a "unit of related browsing contexts"
@@ -30,17 +31,17 @@ namespace dom {
 // TabGroup, browsing contexts are broken into "similar-origin" DocGroups. In
 // more detail, a DocGroup is actually a collection of documents, and a
 // TabGroup is a collection of DocGroups. A TabGroup typically will contain
 // (through its DocGroups) the documents from one or more tabs related by
 // window.opener. A DocGroup is a member of exactly one TabGroup.
 
 class TabGroup;
 
-class DocGroup final : public nsISupports
+class DocGroup final : public Dispatcher
 {
 public:
   typedef nsTArray<nsIDocument*>::iterator Iterator;
   friend class TabGroup;
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
   static void GetKey(nsIPrincipal* aPrincipal, nsACString& aString);
@@ -59,16 +60,20 @@ public:
   {
     return mDocuments.begin();
   }
   Iterator end()
   {
     return mDocuments.end();
   }
 
+  virtual nsresult Dispatch(const char* aName,
+                            TaskCategory aCategory,
+                            already_AddRefed<nsIRunnable>&& aRunnable) 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
@@ -161,10 +161,18 @@ TabGroup::GetThrottledEventQueue() const
 }
 
 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)
+{
+  return NS_DispatchToMainThread(Move(aRunnable));
+}
+
 }
 }
--- a/dom/base/TabGroup.h
+++ b/dom/base/TabGroup.h
@@ -8,16 +8,17 @@
 #define TabGroup_h
 
 #include "nsISupports.h"
 #include "nsISupportsImpl.h"
 #include "nsIPrincipal.h"
 #include "nsTHashtable.h"
 #include "nsString.h"
 
+#include "mozilla/dom/Dispatcher.h"
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
 class ThrottledEventQueue;
 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
@@ -31,17 +32,17 @@ namespace dom {
 // TabGroup, browsing contexts are broken into "similar-origin" DocGroups. In
 // more detail, a DocGroup is actually a collection of documents, and a
 // TabGroup is a collection of DocGroups. A TabGroup typically will contain
 // (through its DocGroups) the documents from one or more tabs related by
 // window.opener. A DocGroup is a member of exactly one TabGroup.
 
 class DocGroup;
 
-class TabGroup final : public nsISupports
+class TabGroup final : public Dispatcher
 {
 private:
   class HashEntry : public nsCStringHashKey
   {
   public:
     // NOTE: Weak reference. The DocGroup destructor removes itself from its
     // owning TabGroup.
     DocGroup* mDocGroup;
@@ -100,16 +101,20 @@ public:
 
   nsTArray<nsPIDOMWindowOuter*> GetTopLevelWindows();
 
   // Get the event queue that associated windows can use to issue runnables to
   // the main thread.  This may return nullptr during browser shutdown.
   ThrottledEventQueue*
   GetThrottledEventQueue() const;
 
+  virtual nsresult Dispatch(const char* aName,
+                            TaskCategory aCategory,
+                            already_AddRefed<nsIRunnable>&& aRunnable) override;
+
 private:
   ~TabGroup();
   DocGroupMap mDocGroups;
   nsTArray<nsPIDOMWindowOuter*> mWindows;
   RefPtr<ThrottledEventQueue> mThrottledEventQueue;
 };
 
 } // namespace dom
--- a/dom/base/Timeout.h
+++ b/dom/base/Timeout.h
@@ -9,16 +9,17 @@
 
 #include "mozilla/LinkedList.h"
 #include "mozilla/TimeStamp.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsPIDOMWindow.h"
 
 class nsGlobalWindow;
+class nsIEventTarget;
 class nsIPrincipal;
 class nsITimeoutHandler;
 class nsITimer;
 class nsIEventTarget;
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -155,16 +155,17 @@ EXPORTS.mozilla.dom += [
     'BodyUtil.h',
     'BorrowedAttrInfo.h',
     'ChildIterator.h',
     'ChromeNodeList.h',
     'ChromeUtils.h',
     'Comment.h',
     'CustomElementRegistry.h',
     'DirectionalityUtils.h',
+    'Dispatcher.h',
     'DocGroup.h',
     'DocumentFragment.h',
     'DocumentType.h',
     'DOMCursor.h',
     'DOMError.h',
     'DOMException.h',
     'DOMImplementation.h',
     'DOMIntersectionObserver.h',
@@ -230,16 +231,17 @@ UNIFIED_SOURCES += [
     'BorrowedAttrInfo.cpp',
     'ChildIterator.cpp',
     'ChromeNodeList.cpp',
     'ChromeUtils.cpp',
     'Comment.cpp',
     'Crypto.cpp',
     'CustomElementRegistry.cpp',
     'DirectionalityUtils.cpp',
+    'Dispatcher.cpp',
     'DocGroup.cpp',
     'DocumentFragment.cpp',
     'DocumentType.cpp',
     'DOMCursor.cpp',
     'DOMError.cpp',
     'DOMException.cpp',
     'DOMImplementation.cpp',
     'DOMMatrix.cpp',
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2883,16 +2883,28 @@ nsIDocument::GetDocGroup()
     MOZ_ASSERT(mDocGroup->MatchesKey(docGroupKey));
     // XXX: Check that the TabGroup is correct as well!
   }
 #endif
 
   return mDocGroup;
 }
 
+nsresult
+nsIDocument::Dispatch(const char* aName,
+                      TaskCategory aCategory,
+                      already_AddRefed<nsIRunnable>&& aRunnable)
+{
+  // 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));
+}
+
 NS_IMETHODIMP
 nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache)
 {
   NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
 
   return NS_OK;
 }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -14928,16 +14928,28 @@ nsPIDOMWindow<T>::GetDocGroup()
 {
   nsIDocument* doc = GetExtantDoc();
   if (doc) {
     return doc->GetDocGroup();
   }
   return nullptr;
 }
 
+nsresult
+nsGlobalWindow::Dispatch(const char* aName,
+                         TaskCategory aCategory,
+                         already_AddRefed<nsIRunnable>&& aRunnable)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  if (GetDocGroup()) {
+    return GetDocGroup()->Dispatch(aName, aCategory, Move(aRunnable));
+  }
+  return DispatcherTrait::Dispatch(aName, aCategory, Move(aRunnable));
+}
+
 nsGlobalWindow::TemporarilyDisableDialogs::TemporarilyDisableDialogs(
   nsGlobalWindow* aWindow MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
 {
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
   MOZ_ASSERT(aWindow);
   nsGlobalWindow* topWindow = aWindow->GetScriptableTopInternal();
   if (!topWindow) {
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1712,16 +1712,22 @@ private:
   // exists and has too many runnables waiting to run.  For example, suspend
   // timers until we have a chance to catch up, etc.
   void
   MaybeApplyBackPressure();
 
   mozilla::dom::TabGroup* TabGroupInner();
   mozilla::dom::TabGroup* TabGroupOuter();
 
+public:
+  // Dispatch a runnable related to the global.
+  virtual nsresult Dispatch(const char* aName,
+                            mozilla::dom::TaskCategory aCategory,
+                            already_AddRefed<nsIRunnable>&& aRunnable) override;
+
 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
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -27,22 +27,23 @@
 #include "nsWeakReference.h"
 #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/UniquePtr.h"
 #include "mozilla/CORSMode.h"
+#include "mozilla/dom/Dispatcher.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
 #include "mozilla/dom/DocumentBinding.h"
 #else
 namespace mozilla {
 namespace dom {
 class ElementCreationOptionsOrString;
@@ -199,17 +200,18 @@ enum class HSTSPrimingState {
 
 // Some function forward-declarations
 class nsContentList;
 
 //----------------------------------------------------------------------
 
 // Document interface.  This is implemented by all document objects in
 // Gecko.
-class nsIDocument : public nsINode
+class nsIDocument : public nsINode,
+                    public mozilla::dom::DispatcherTrait
 {
   typedef mozilla::dom::GlobalObject GlobalObject;
 
 public:
   typedef mozilla::net::ReferrerPolicy ReferrerPolicyEnum;
   typedef mozilla::dom::Element Element;
   typedef mozilla::dom::FullscreenRequest FullscreenRequest;
 
@@ -2860,16 +2862,21 @@ public:
     mozilla::dom::DOMIntersectionObserver* aObserver) = 0;
   virtual void RemoveIntersectionObserver(
     mozilla::dom::DOMIntersectionObserver* aObserver) = 0;
   
   virtual void UpdateIntersectionObservations() = 0;
   virtual void ScheduleIntersectionObserverNotification() = 0;
   virtual void NotifyIntersectionObservers() = 0;
 
+  // Dispatch a runnable related to the document.
+  virtual nsresult Dispatch(const char* aName,
+                            mozilla::dom::TaskCategory aCategory,
+                            already_AddRefed<nsIRunnable>&& aRunnable) override;
+
 protected:
   bool GetUseCounter(mozilla::UseCounter aUseCounter)
   {
     return mUseCounters[aUseCounter];
   }
 
   void SetChildDocumentUseCounter(mozilla::UseCounter aUseCounter)
   {
--- a/dom/base/nsIGlobalObject.h
+++ b/dom/base/nsIGlobalObject.h
@@ -2,30 +2,32 @@
 /* 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 "nsISupports.h"
 #include "nsTArray.h"
 #include "js/TypeDecls.h"
 
 #define NS_IGLOBALOBJECT_IID \
 { 0x11afa8be, 0xd997, 0x4e07, \
 { 0xa6, 0xa3, 0x6f, 0x87, 0x2e, 0xc3, 0xee, 0x7f } }
 
 class nsACString;
 class nsCString;
 class nsCycleCollectionTraversalCallback;
 class nsIPrincipal;
 
-class nsIGlobalObject : public nsISupports
+class nsIGlobalObject : public nsISupports,
+                        public mozilla::dom::DispatcherTrait
 {
   nsTArray<nsCString> mHostObjectURIs;
   bool mIsDying;
 
 protected:
   nsIGlobalObject()
    : mIsDying(false)
   {}