Bug 1318506 - Assign a TabGroup to every PBrowser (r?mystor,ehsan) draft
authorBill McCloskey <billm@mozilla.com>
Fri, 04 Nov 2016 11:13:52 -0700
changeset 440691 d4f5ebd64d05158a0952f86cec9bfd60f2432353
parent 440690 1ed4868e3f8190f16de69bcbf1145cc67553bae1
child 440692 491b40571d4cb0220bfb96230c6ebe15ead85e44
child 441365 90ebcc9d9f1b0ad3a5e17344a14cc30a1133e413
push id36297
push userbmo:wmccloskey@mozilla.com
push dateFri, 18 Nov 2016 00:53:48 +0000
reviewersmystor, ehsan
bugs1318506
milestone53.0a1
Bug 1318506 - Assign a TabGroup to every PBrowser (r?mystor,ehsan) Every new PBrowser, whether it's created by the parent or the child, needs to get a TabGroup assigned to it. That way IPC messages for the PBrowser will be dispatched to that TabGroup. For new PBrowsers created by the child, we just create a new TabGroup or reuse the opener's TabGroup. For PBrowsers created by the parent, the child needs to intercept the PBrowserConstructor message and assign a TabGroup immediately. PBrowsers created by the parent never have an opener so we can always create a new TabGroup. In both cases, the nsGlobalWindow::TabGroupOuter logic needs to be updated to read the TabGroup out of the IPC code. Otherwise the DOM and IPC code will get out of sync about TabGroups. MozReview-Commit-ID: D5iEdgirfvK
dom/base/Dispatcher.cpp
dom/base/Dispatcher.h
dom/base/TabGroup.cpp
dom/base/TabGroup.h
dom/base/nsGlobalWindow.cpp
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
ipc/glue/ProtocolUtils.cpp
ipc/glue/ProtocolUtils.h
--- a/dom/base/Dispatcher.cpp
+++ b/dom/base/Dispatcher.cpp
@@ -85,8 +85,14 @@ DispatcherEventTarget::IsOnCurrentThread
 
 already_AddRefed<nsIEventTarget>
 Dispatcher::CreateEventTarget(const char* aName, TaskCategory aCategory)
 {
   RefPtr<DispatcherEventTarget> target =
     new DispatcherEventTarget(this, aName, aCategory);
   return target.forget();
 }
+
+/* static */ Dispatcher*
+Dispatcher::DispatcherFromEventTarget(nsIEventTarget* aEventTarget)
+{
+  return static_cast<DispatcherEventTarget*>(aEventTarget)->Dispatcher();
+}
--- a/dom/base/Dispatcher.h
+++ b/dom/base/Dispatcher.h
@@ -62,14 +62,17 @@ public:
   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);
+
+protected:
+  static Dispatcher* DispatcherFromEventTarget(nsIEventTarget* aEventTarget);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Dispatcher_h
--- a/dom/base/TabGroup.cpp
+++ b/dom/base/TabGroup.cpp
@@ -1,16 +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/TabGroup.h"
 
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/ThrottledEventQueue.h"
 #include "nsIDocShell.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIURI.h"
@@ -25,41 +27,84 @@ TabGroup::TabGroup(bool aIsChrome)
   // 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;
   }
 
+  // This constructor can be called from the IPC I/O thread. In that case, we
+  // won't actually use the TabGroup on the main thread until GetFromWindowActor
+  // is called, so we initialize the throttled event queue there.
+  if (NS_IsMainThread()) {
+    EnsureThrottledEventQueue();
+  }
+}
+
+TabGroup::~TabGroup()
+{
+  MOZ_ASSERT(mDocGroups.IsEmpty());
+  MOZ_ASSERT(mWindows.IsEmpty());
+}
+
+void
+TabGroup::EnsureThrottledEventQueue()
+{
+  if (mThrottledEventQueue) {
+    return;
+  }
+
   nsCOMPtr<nsIThread> mainThread;
   NS_GetMainThread(getter_AddRefs(mainThread));
   MOZ_DIAGNOSTIC_ASSERT(mainThread);
 
   // This may return nullptr during xpcom shutdown.  This is ok as we
   // do not guarantee a ThrottledEventQueue will be present.
   mThrottledEventQueue = ThrottledEventQueue::Create(mainThread);
 }
 
-TabGroup::~TabGroup()
-{
-  MOZ_ASSERT(mDocGroups.IsEmpty());
-  MOZ_ASSERT(mWindows.IsEmpty());
-}
-
 TabGroup*
 TabGroup::GetChromeTabGroup()
 {
   if (!sChromeTabGroup) {
     sChromeTabGroup = new TabGroup(true /* chrome tab group */);
     ClearOnShutdown(&sChromeTabGroup);
   }
   return sChromeTabGroup;
 }
 
+/* static */ TabGroup*
+TabGroup::GetFromWindowActor(mozIDOMWindowProxy* aWindow)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+  TabChild* tabChild = TabChild::GetFrom(aWindow);
+  if (!tabChild) {
+    return nullptr;
+  }
+
+  ContentChild* cc = ContentChild::GetSingleton();
+  nsCOMPtr<nsIEventTarget> target = cc->GetActorEventTarget(tabChild);
+  if (!target) {
+    return nullptr;
+  }
+
+  // We have an event target. We assume the IPC code created it via
+  // TabGroup::CreateEventTarget.
+  Dispatcher* dispatcher = Dispatcher::DispatcherFromEventTarget(target);
+  auto tabGroup = static_cast<TabGroup*>(dispatcher);
+
+  // We delay creating the throttled event queue until now since the TabGroup
+  // constructor ran off the main thread.
+  tabGroup->EnsureThrottledEventQueue();
+
+  return tabGroup;
+}
+
 already_AddRefed<DocGroup>
 TabGroup::GetDocGroup(const nsACString& aKey)
 {
   RefPtr<DocGroup> docGroup(mDocGroups.GetEntry(aKey)->mDocGroup);
   return docGroup.forget();
 }
 
 already_AddRefed<DocGroup>
--- a/dom/base/TabGroup.h
+++ b/dom/base/TabGroup.h
@@ -55,16 +55,24 @@ public:
 
   friend class DocGroup;
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
   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
+  // thread only.
+  static TabGroup*
+  GetFromWindowActor(mozIDOMWindowProxy* aWindow);
+
   explicit TabGroup(bool aIsChrome = false);
 
   // Get the docgroup for the corresponding doc group key.
   // Returns null if the given key hasn't been seen yet.
   already_AddRefed<DocGroup>
   GetDocGroup(const nsACString& aKey);
 
   already_AddRefed<DocGroup>
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -14853,16 +14853,20 @@ nsGlobalWindow::TabGroupOuter()
 
     mozilla::dom::TabGroup* toJoin = nullptr;
     if (GetDocShell()->ItemType() == nsIDocShellTreeItem::typeChrome) {
       toJoin = TabGroup::GetChromeTabGroup();
     } else if (opener) {
       toJoin = opener->TabGroup();
     } else if (parent) {
       toJoin = parent->TabGroup();
+    } else {
+      // If the tab was created by the parent process, the IPC code may have
+      // already created a TabGroup for us. Fetch it in that case.
+      toJoin = TabGroup::GetFromWindowActor(AsOuter());
     }
     mTabGroup = mozilla::dom::TabGroup::Join(AsOuter(), toJoin);
   }
   MOZ_ASSERT(mTabGroup);
 
 #ifdef DEBUG
   // Ensure that we don't recurse forever
   if (!mIsValidatingTabGroup) {
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -23,23 +23,25 @@
 #include "mozilla/Unused.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
 #include "mozilla/docshell/OfflineCacheUpdateChild.h"
 #include "mozilla/dom/ContentBridgeChild.h"
 #include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/VideoDecoderManagerChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DataTransfer.h"
+#include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/FlyWebPublishedServerIPC.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/PCrashReporterChild.h"
 #include "mozilla/dom/ProcessGlobal.h"
 #include "mozilla/dom/PushNotifier.h"
+#include "mozilla/dom/TabGroup.h"
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/psm/PSMContentListener.h"
 #include "mozilla/hal_sandbox/PHalChild.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
@@ -700,16 +702,29 @@ ContentChild::ProvideWindowCommon(TabChi
     return NS_ERROR_ABORT;
   }
 
   if (aTabOpener) {
     MOZ_ASSERT(ipcContext->type() == IPCTabContext::TPopupIPCTabContext);
     ipcContext->get_PopupIPCTabContext().opener() = aTabOpener;
   }
 
+  // We need to assign a TabGroup to the PBrowser actor before we send it to the
+  // parent. Otherwise, the parent could send messages to us before we have a
+  // proper TabGroup for that actor.
+  RefPtr<TabGroup> tabGroup;
+  if (aTabOpener && !aForceNoOpener) {
+    // The new actor will use the same event target as the opener.
+    tabGroup = aTabOpener->TabGroup();
+  } else {
+    tabGroup = new TabGroup();
+  }
+  nsCOMPtr<nsIEventTarget> target = tabGroup->CreateEventTarget("TabChild", TaskCategory::Other);
+  SetEventTargetForActor(newChild, target);
+
   Unused << SendPBrowserConstructor(
     // We release this ref in DeallocPBrowserChild
     RefPtr<TabChild>(newChild).forget().take(),
     tabId, *ipcContext, aChromeFlags,
     GetID(), IsForBrowser());
 
   nsString name(aName);
   nsAutoCString features(aFeatures);
@@ -3204,10 +3219,27 @@ ContentChild::FatalErrorIfNotUsingGPUPro
     formattedMessage.AppendASCII(aProtocolName);
     formattedMessage.AppendLiteral(R"(]: ")");
     formattedMessage.AppendASCII(aErrorMsg);
     formattedMessage.AppendLiteral(R"(".)");
     NS_WARNING(formattedMessage.get());
   }
 }
 
+// The IPC code will call this method asking us to assign an event target to new
+// actors created by the ContentParent.
+already_AddRefed<nsIEventTarget>
+ContentChild::GetConstructedEventTarget(const Message& aMsg)
+{
+  // Currently we only set targets for PBrowser.
+  if (aMsg.type() != PContent::Msg_PBrowserConstructor__ID) {
+    return nullptr;
+  }
+
+  // If the request for a new TabChild is coming from the parent process, then
+  // there is no opener. Therefore, we create a fresh TabGroup.
+  RefPtr<TabGroup> tabGroup = new TabGroup();
+  nsCOMPtr<nsIEventTarget> target = tabGroup->CreateEventTarget("TabChild", TaskCategory::Other);
+  return target.forget();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -643,16 +643,19 @@ public:
 private:
   static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
   void StartForceKillTimer();
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   virtual void ProcessingError(Result aCode, const char* aReason) override;
 
+  virtual already_AddRefed<nsIEventTarget>
+  GetConstructedEventTarget(const Message& aMsg) override;
+
   InfallibleTArray<nsAutoPtr<AlertObserver> > mAlertObservers;
   RefPtr<ConsoleListener> mConsoleListener;
 
   nsTHashtable<nsPtrHashKey<nsIObserver>> mIdleObservers;
 
   InfallibleTArray<nsString> mAvailableDictionaries;
 
   // Temporary storage for a list of available font families, passed from the
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -3066,16 +3066,24 @@ TabChild::ForcePaint(uint64_t aLayerObse
     // message on the PContent channel.
     return;
   }
 
   nsAutoScriptBlocker scriptBlocker;
   RecvSetDocShellIsActive(true, false, aLayerObserverEpoch);
 }
 
+mozilla::dom::TabGroup*
+TabChild::TabGroup()
+{
+  nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
+  MOZ_ASSERT(window);
+  return window->TabGroup();
+}
+
 /*******************************************************************************
  * nsISHistoryListener
  ******************************************************************************/
 
 NS_IMETHODIMP
 TabChildSHistoryListener::OnHistoryNewEntry(nsIURI *aNewURI, int32_t aOldIndex)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -63,16 +63,17 @@ struct AutoCacheNativeKeyCommands;
 
 namespace plugins {
 class PluginWidgetChild;
 } // namespace plugins
 
 namespace dom {
 
 class TabChild;
+class TabGroup;
 class ClonedMessageData;
 class TabChildBase;
 
 class TabChildGlobal : public DOMEventTargetHelper,
                        public nsIContentFrameMessageManager,
                        public nsIScriptObjectPrincipal,
                        public nsIGlobalObject,
                        public nsSupportsWeakReference
@@ -656,16 +657,18 @@ public:
 
   bool TakeIsFreshProcess()
   {
     bool wasFreshProcess = mIsFreshProcess;
     mIsFreshProcess = false;
     return wasFreshProcess;
   }
 
+  mozilla::dom::TabGroup* TabGroup();
+
 protected:
   virtual ~TabChild();
 
   virtual PRenderFrameChild* AllocPRenderFrameChild() override;
 
   virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override;
 
   virtual mozilla::ipc::IPCResult RecvDestroy() override;
--- a/ipc/glue/ProtocolUtils.cpp
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -794,16 +794,26 @@ IToplevelProtocol::GetMessageEventTarget
     }
 
     mEventTargetMap.AddWithID(target, handle.mId);
   }
 
   return target.forget();
 }
 
+already_AddRefed<nsIEventTarget>
+IToplevelProtocol::GetActorEventTarget(IProtocol* aActor)
+{
+  MOZ_RELEASE_ASSERT(aActor->Id() != kNullActorId && aActor->Id() != kFreedActorId);
+
+  MutexAutoLock lock(mEventTargetMutex);
+  nsCOMPtr<nsIEventTarget> target = mEventTargetMap.Lookup(aActor->Id());
+  return target.forget();
+}
+
 void
 IToplevelProtocol::SetEventTargetForActorInternal(IProtocol* aActor,
                                                   nsIEventTarget* aEventTarget)
 {
   // We should only call this function on actors that haven't been used for IPC
   // code yet. Otherwise we'll be posting stuff to the wrong event target before
   // we're called.
   MOZ_RELEASE_ASSERT(aActor->Id() == kNullActorId || aActor->Id() == kFreedActorId);
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -372,16 +372,19 @@ public:
     }
 
     virtual void ProcessRemoteNativeEventsInInterruptCall() {
     }
 
     virtual already_AddRefed<nsIEventTarget>
     GetMessageEventTarget(const Message& aMsg);
 
+    already_AddRefed<nsIEventTarget>
+    GetActorEventTarget(IProtocol* aActor);
+
 protected:
     virtual already_AddRefed<nsIEventTarget>
     GetConstructedEventTarget(const Message& aMsg) { return nullptr; }
 
     virtual void SetEventTargetForActorInternal(IProtocol* aActor, nsIEventTarget* aEventTarget);
 
   private:
     ProtocolId mProtocolId;