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
This commit is contained in:
Bill McCloskey 2016-11-04 11:13:52 -07:00
parent 2113b3347b
commit 563f66dae7
11 changed files with 170 additions and 7 deletions

View File

@ -7,6 +7,7 @@
#include "mozilla/dom/Dispatcher.h"
#include "mozilla/Move.h"
#include "nsINamed.h"
#include "nsQueryObject.h"
using namespace mozilla;
@ -37,12 +38,18 @@ DispatcherTrait::EventTargetFor(TaskCategory aCategory) const
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)
@ -57,9 +64,11 @@ private:
virtual ~DispatcherEventTarget() {}
};
NS_DEFINE_STATIC_IID_ACCESSOR(DispatcherEventTarget, NS_DISPATCHEREVENTTARGET_IID)
} // namespace
NS_IMPL_ISUPPORTS(DispatcherEventTarget, nsIEventTarget)
NS_IMPL_ISUPPORTS(DispatcherEventTarget, DispatcherEventTarget, nsIEventTarget)
NS_IMETHODIMP
DispatcherEventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
@ -96,3 +105,13 @@ Dispatcher::CreateEventTargetFor(TaskCategory aCategory)
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();
}

View File

@ -16,6 +16,9 @@ class nsIRunnable;
namespace mozilla {
namespace dom {
class TabGroup;
class DocGroup;
enum class TaskCategory {
// User input (clicks, keypresses, etc.)
UI,
@ -70,9 +73,15 @@ public:
virtual already_AddRefed<nsIEventTarget>
EventTargetFor(TaskCategory aCategory) const = 0;
// These methods perform a safe cast. They return 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

View File

@ -6,6 +6,8 @@
#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"
@ -22,6 +24,7 @@ static StaticRefPtr<TabGroup> sChromeTabGroup;
TabGroup::TabGroup(bool aIsChrome)
: mLastWindowLeft(false)
, mThrottledQueuesInitialized(false)
{
for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
TaskCategory category = static_cast<TaskCategory>(i);
@ -36,6 +39,29 @@ TabGroup::TabGroup(bool aIsChrome)
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 queues there.
if (NS_IsMainThread()) {
EnsureThrottledEventQueues();
}
}
TabGroup::~TabGroup()
{
MOZ_ASSERT(mDocGroups.IsEmpty());
MOZ_ASSERT(mWindows.IsEmpty());
}
void
TabGroup::EnsureThrottledEventQueues()
{
if (mThrottledQueuesInitialized) {
return;
}
mThrottledQueuesInitialized = true;
nsCOMPtr<nsIThread> mainThread;
NS_GetMainThread(getter_AddRefs(mainThread));
MOZ_DIAGNOSTIC_ASSERT(mainThread);
@ -45,12 +71,6 @@ TabGroup::TabGroup(bool aIsChrome)
mThrottledEventQueue = ThrottledEventQueue::Create(mainThread);
}
TabGroup::~TabGroup()
{
MOZ_ASSERT(mDocGroups.IsEmpty());
MOZ_ASSERT(mWindows.IsEmpty());
}
TabGroup*
TabGroup::GetChromeTabGroup()
{
@ -61,6 +81,36 @@ TabGroup::GetChromeTabGroup()
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.
RefPtr<Dispatcher> dispatcher = Dispatcher::FromEventTarget(target);
MOZ_RELEASE_ASSERT(dispatcher);
auto tabGroup = dispatcher->AsTabGroup();
MOZ_RELEASE_ASSERT(tabGroup);
// We delay creating the event targets until now since the TabGroup
// constructor ran off the main thread.
tabGroup->EnsureThrottledEventQueues();
return tabGroup;
}
already_AddRefed<DocGroup>
TabGroup::GetDocGroup(const nsACString& aKey)
{
@ -176,6 +226,8 @@ TabGroup::GetTopLevelWindows()
ThrottledEventQueue*
TabGroup::GetThrottledEventQueue() const
{
MOZ_RELEASE_ASSERT(mThrottledQueuesInitialized || this == GetChromeTabGroup());
MOZ_RELEASE_ASSERT(!mLastWindowLeft);
return mThrottledEventQueue;
}

View File

@ -60,6 +60,14 @@ public:
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.
@ -113,11 +121,16 @@ public:
virtual already_AddRefed<nsIEventTarget>
EventTargetFor(TaskCategory aCategory) const override;
TabGroup* AsTabGroup() override { return this; }
private:
void EnsureThrottledEventQueues();
~TabGroup();
DocGroupMap mDocGroups;
bool mLastWindowLeft;
nsTArray<nsPIDOMWindowOuter*> mWindows;
bool mThrottledQueuesInitialized;
RefPtr<ThrottledEventQueue> mThrottledEventQueue;
nsCOMPtr<nsIEventTarget> mEventTargets[size_t(TaskCategory::Count)];
};

View File

@ -14161,7 +14161,18 @@ nsGlobalWindow::TabGroupOuter()
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());
}
#ifdef DEBUG
// Make sure that, if we have a tab group from the actor, it matches the one
// we're planning to join.
mozilla::dom::TabGroup* actorTabGroup = TabGroup::GetFromWindowActor(AsOuter());
MOZ_ASSERT_IF(actorTabGroup, actorTabGroup == toJoin);
#endif
mTabGroup = mozilla::dom::TabGroup::Join(AsOuter(), toJoin);
}
MOZ_ASSERT(mTabGroup);

View File

@ -27,6 +27,7 @@
#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"
@ -34,6 +35,7 @@
#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/dom/URLClassifierChild.h"
@ -786,6 +788,19 @@ ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
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 tab group as the opener.
tabGroup = aTabOpener->TabGroup();
} else {
tabGroup = new TabGroup();
}
nsCOMPtr<nsIEventTarget> target = tabGroup->EventTargetFor(TaskCategory::Other);
SetEventTargetForActor(newChild, target);
Unused << SendPBrowserConstructor(
// We release this ref in DeallocPBrowserChild
RefPtr<TabChild>(newChild).forget().take(),
@ -3225,5 +3240,22 @@ ContentChild::DeallocPURLClassifierChild(PURLClassifierChild* aActor)
return true;
}
// 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->EventTargetFor(TaskCategory::Other);
return target.forget();
}
} // namespace dom
} // namespace mozilla

View File

@ -647,6 +647,9 @@ private:
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;

View File

@ -3117,6 +3117,14 @@ TabChildSHistoryListener::SHistoryDidUpdate(bool aTruncate /* = false */)
return NS_OK;
}
mozilla::dom::TabGroup*
TabChild::TabGroup()
{
nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
MOZ_ASSERT(window);
return window->TabGroup();
}
/*******************************************************************************
* nsISHistoryListener
******************************************************************************/

View File

@ -68,6 +68,7 @@ class PluginWidgetChild;
namespace dom {
class TabChild;
class TabGroup;
class ClonedMessageData;
class TabChildBase;
@ -665,6 +666,8 @@ public:
already_AddRefed<nsISHistory> GetRelatedSHistory();
mozilla::dom::TabGroup* TabGroup();
protected:
virtual ~TabChild();

View File

@ -799,6 +799,16 @@ IToplevelProtocol::GetMessageEventTarget(const Message& aMsg)
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)

View File

@ -362,6 +362,9 @@ public:
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; }