Bug 1320753 - Refactor for new event target idea (r=ehsan)
authorBill McCloskey <billm@mozilla.com>
Tue, 22 Nov 2016 21:14:39 -0800
changeset 324957 ac84d68036e44439bb1589dd3f1f8174a2c342be
parent 324956 704915079f6b12990201dc378ee05c8861e98a7c
child 324958 ee28526a9033d7c073009bc5501f71367bb39d4c
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersehsan
bugs1320753
milestone53.0a1
Bug 1320753 - Refactor for new event target idea (r=ehsan) MozReview-Commit-ID: AyhemNqBedb
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/nsDocument.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsIDocument.h
dom/base/nsPIDOMWindow.h
--- a/dom/base/Dispatcher.cpp
+++ b/dom/base/Dispatcher.cpp
@@ -1,49 +1,55 @@
 /* -*- 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"
+#include "nsINamed.h"
 
 using namespace mozilla;
 
 nsresult
 DispatcherTrait::Dispatch(const char* aName,
                           TaskCategory aCategory,
                           already_AddRefed<nsIRunnable>&& aRunnable)
 {
-  return NS_DispatchToMainThread(Move(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());
+  }
 }
 
 already_AddRefed<nsIEventTarget>
-DispatcherTrait::CreateEventTarget(const char* aName,
-                                   TaskCategory aCategory)
+DispatcherTrait::EventTargetFor(TaskCategory aCategory) const
 {
   nsCOMPtr<nsIEventTarget> main = do_GetMainThread();
   return main.forget();
 }
 
 namespace {
 
 class DispatcherEventTarget final : public nsIEventTarget
 {
   RefPtr<dom::Dispatcher> mDispatcher;
-  const char* mName;
   TaskCategory mCategory;
 
 public:
-  DispatcherEventTarget(dom::Dispatcher* aDispatcher,
-                        const char* aName,
-                        TaskCategory aCategory)
+  DispatcherEventTarget(dom::Dispatcher* aDispatcher, TaskCategory aCategory)
    : mDispatcher(aDispatcher)
-   , mName(aName)
    , mCategory(aCategory)
   {}
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIEVENTTARGET
 
   dom::Dispatcher* Dispatcher() const { return mDispatcher; }
 
@@ -62,31 +68,31 @@ DispatcherEventTarget::DispatchFromScrip
 }
 
 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(mName, mCategory, Move(aRunnable));
+  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::CreateEventTarget(const char* aName, TaskCategory aCategory)
+Dispatcher::CreateEventTargetFor(TaskCategory aCategory)
 {
   RefPtr<DispatcherEventTarget> target =
-    new DispatcherEventTarget(this, aName, aCategory);
+    new DispatcherEventTarget(this, aCategory);
   return target.forget();
 }
--- a/dom/base/Dispatcher.h
+++ b/dom/base/Dispatcher.h
@@ -29,16 +29,18 @@ enum class TaskCategory {
   // requestIdleCallback
   IdleCallback,
 
   // Vsync notifications
   RefreshDriver,
 
   // Most DOM events (postMessage, media, plugins)
   Other,
+
+  Count
 };
 
 // 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:
@@ -47,29 +49,33 @@ public:
   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 already_AddRefed<nsIEventTarget>
-  CreateEventTarget(const char* aName, TaskCategory aCategory);
+  EventTargetFor(TaskCategory aCategory) const;
 };
 
 // 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 already_AddRefed<nsIEventTarget>
-  CreateEventTarget(const char* aName, TaskCategory aCategory);
+  EventTargetFor(TaskCategory aCategory) const = 0;
+
+protected:
+  virtual already_AddRefed<nsIEventTarget>
+  CreateEventTargetFor(TaskCategory aCategory);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Dispatcher_h
--- a/dom/base/DocGroup.cpp
+++ b/dom/base/DocGroup.cpp
@@ -55,13 +55,19 @@ DocGroup::~DocGroup()
 
 NS_IMPL_ISUPPORTS(DocGroup, nsISupports)
 
 nsresult
 DocGroup::Dispatch(const char* aName,
                    TaskCategory aCategory,
                    already_AddRefed<nsIRunnable>&& aRunnable)
 {
-  return NS_DispatchToMainThread(Move(aRunnable));
+  return mTabGroup->Dispatch(aName, aCategory, Move(aRunnable));
+}
+
+already_AddRefed<nsIEventTarget>
+DocGroup::EventTargetFor(TaskCategory aCategory) const
+{
+  return mTabGroup->EventTargetFor(aCategory);
 }
 
 }
 }
--- a/dom/base/DocGroup.h
+++ b/dom/base/DocGroup.h
@@ -64,16 +64,19 @@ public:
   {
     return mDocuments.end();
   }
 
   virtual nsresult Dispatch(const char* aName,
                             TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
+  virtual already_AddRefed<nsIEventTarget>
+  EventTargetFor(TaskCategory aCategory) const 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
@@ -16,17 +16,23 @@
 #include "nsIURI.h"
 
 namespace mozilla {
 namespace dom {
 
 static StaticRefPtr<TabGroup> sChromeTabGroup;
 
 TabGroup::TabGroup(bool aIsChrome)
+ : mLastWindowLeft(false)
 {
+  for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
+    TaskCategory category = static_cast<TaskCategory>(i);
+    mEventTargets[i] = CreateEventTargetFor(category);
+  }
+
   // Do not throttle runnables from chrome windows.  In theory we should
   // not have abuse issues from these windows and many browser chrome
   // tests have races that fail if we do throttle chrome runnables.
   if (aIsChrome) {
     MOZ_ASSERT(!sChromeTabGroup);
     return;
   }
 
@@ -94,16 +100,28 @@ TabGroup::Join(nsPIDOMWindowOuter* aWind
   return tabGroup.forget();
 }
 
 void
 TabGroup::Leave(nsPIDOMWindowOuter* aWindow)
 {
   MOZ_ASSERT(mWindows.Contains(aWindow));
   mWindows.RemoveElement(aWindow);
+
+  if (mWindows.IsEmpty()) {
+    mLastWindowLeft = true;
+
+    // There is a RefPtr cycle TabGroup -> DispatcherEventTarget -> 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] = nullptr;
+    }
+  }
 }
 
 nsresult
 TabGroup::FindItemWithName(const nsAString& aName,
                            nsIDocShellTreeItem* aRequestor,
                            nsIDocShellTreeItem* aOriginalRequestor,
                            nsIDocShellTreeItem** aFoundItem)
 {
@@ -167,13 +185,31 @@ TabGroup::HashEntry::HashEntry(const nsA
   : nsCStringHashKey(aKey), mDocGroup(nullptr)
 {}
 
 nsresult
 TabGroup::Dispatch(const char* aName,
                    TaskCategory aCategory,
                    already_AddRefed<nsIRunnable>&& aRunnable)
 {
-  return NS_DispatchToMainThread(Move(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());
+  }
+}
+
+already_AddRefed<nsIEventTarget>
+TabGroup::EventTargetFor(TaskCategory aCategory) const
+{
+  MOZ_RELEASE_ASSERT(!mLastWindowLeft);
+  nsCOMPtr<nsIEventTarget> target = mEventTargets[size_t(aCategory)];
+  return target.forget();
 }
 
 }
 }
--- a/dom/base/TabGroup.h
+++ b/dom/base/TabGroup.h
@@ -105,19 +105,24 @@ public:
   // 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;
 
+  virtual already_AddRefed<nsIEventTarget>
+  EventTargetFor(TaskCategory aCategory) const override;
+
 private:
   ~TabGroup();
   DocGroupMap mDocGroups;
+  bool mLastWindowLeft;
   nsTArray<nsPIDOMWindowOuter*> mWindows;
   RefPtr<ThrottledEventQueue> mThrottledEventQueue;
+  nsCOMPtr<nsIEventTarget> mEventTargets[size_t(TaskCategory::Count)];
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // defined(TabGroup_h)
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2868,17 +2868,17 @@ nsDocument::SetPrincipal(nsIPrincipal *a
   // same docgroup as another document, so this should not be a problem.
   if (aNewPrincipal) {
     GetDocGroup();
   }
 #endif
 }
 
 mozilla::dom::DocGroup*
-nsIDocument::GetDocGroup()
+nsIDocument::GetDocGroup() const
 {
 #ifdef DEBUG
   // Sanity check that we have an up-to-date and accurate docgroup
   if (mDocGroup) {
     nsAutoCString docGroupKey;
     mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
     MOZ_ASSERT(mDocGroup->MatchesKey(docGroupKey));
     // XXX: Check that the TabGroup is correct as well!
@@ -2896,22 +2896,22 @@ 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));
 }
 
 already_AddRefed<nsIEventTarget>
-nsIDocument::CreateEventTarget(const char* aName, TaskCategory aCategory)
+nsIDocument::EventTargetFor(TaskCategory aCategory) const
 {
   if (mDocGroup) {
-    return mDocGroup->CreateEventTarget(aName, aCategory);
-  }
-  return DispatcherTrait::CreateEventTarget(aName, aCategory);
+    return mDocGroup->EventTargetFor(aCategory);
+  }
+  return DispatcherTrait::EventTargetFor(aCategory);
 }
 
 NS_IMETHODIMP
 nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache)
 {
   NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
 
   return NS_OK;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -14993,17 +14993,17 @@ nsPIDOMWindow<T>::TabGroup()
   if (IsInnerWindow()) {
     return globalWindow->TabGroupInner();
   }
   return globalWindow->TabGroupOuter();
 }
 
 template<typename T>
 mozilla::dom::DocGroup*
-nsPIDOMWindow<T>::GetDocGroup()
+nsPIDOMWindow<T>::GetDocGroup() const
 {
   nsIDocument* doc = GetExtantDoc();
   if (doc) {
     return doc->GetDocGroup();
   }
   return nullptr;
 }
 
@@ -15015,24 +15015,23 @@ 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));
 }
 
 already_AddRefed<nsIEventTarget>
-nsGlobalWindow::CreateEventTarget(const char* aName,
-                                  TaskCategory aCategory)
+nsGlobalWindow::EventTargetFor(TaskCategory aCategory) const
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   if (GetDocGroup()) {
-    return GetDocGroup()->CreateEventTarget(aName, aCategory);
-  }
-  return DispatcherTrait::CreateEventTarget(aName, aCategory);
+    return GetDocGroup()->EventTargetFor(aCategory);
+  }
+  return DispatcherTrait::EventTargetFor(aCategory);
 }
 
 nsGlobalWindow::TemporarilyDisableDialogs::TemporarilyDisableDialogs(
   nsGlobalWindow* aWindow MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
 {
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
   MOZ_ASSERT(aWindow);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1726,17 +1726,17 @@ private:
 
 public:
   // Dispatch a runnable related to the global.
   virtual nsresult Dispatch(const char* aName,
                             mozilla::dom::TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   virtual already_AddRefed<nsIEventTarget>
-  CreateEventTarget(const char* aName, mozilla::dom::TaskCategory aCategory) override;
+  EventTargetFor(mozilla::dom::TaskCategory aCategory) const 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;
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2851,35 +2851,34 @@ public:
   bool InlineScriptAllowedByCSP();
 
   void ReportHasScrollLinkedEffect();
   bool HasScrollLinkedEffect() const
   {
     return mHasScrollLinkedEffect;
   }
 
-  mozilla::dom::DocGroup* GetDocGroup();
+  mozilla::dom::DocGroup* GetDocGroup() const;
 
   virtual void AddIntersectionObserver(
     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;
 
   virtual already_AddRefed<nsIEventTarget>
-  CreateEventTarget(const char* aName,
-                    mozilla::dom::TaskCategory aCategory) override;
+  EventTargetFor(mozilla::dom::TaskCategory aCategory) const override;
 
 protected:
   bool GetUseCounter(mozilla::UseCounter aUseCounter)
   {
     return mUseCounters[aUseCounter];
   }
 
   void SetChildDocumentUseCounter(mozilla::UseCounter aUseCounter)
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -575,17 +575,17 @@ public:
   virtual nsresult Focus() = 0;
   virtual nsresult Close() = 0;
 
   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();
+  mozilla::dom::DocGroup* GetDocGroup() const;
 
   virtual mozilla::ThrottledEventQueue* GetThrottledEventQueue() = 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.