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
--- 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;