Bug 558184 - Part 8 - Load js plugins in a separate process. r=billm.

Every JS plugin is assigned a unique ID. When an instance of a JS plugin is created the
frame loader that we use to load the plugin's handler URI will create a special
TabContext. This TabContext causes the ContentParent to use the process for this specific
JS plugin (creating one if it hasn't already) when it creates the PBrowser actors.
This causes the iframes for all the instances of a specific JS plugin to be grouped in the
same process.

--HG--
extra : rebase_source : c39560bdf66cda1a005c7b823b3a46e4734878a4
extra : source : 9cba1db527c7eed4371c9f4caf96fd942608cab6
This commit is contained in:
Peter Van der Beken 2017-05-29 12:38:46 +02:00
Родитель f7f426524e
Коммит 2310b3bd39
18 изменённых файлов: 351 добавлений и 115 удалений

Просмотреть файл

@ -156,12 +156,14 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersistable) NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersistable)
NS_INTERFACE_MAP_END NS_INTERFACE_MAP_END
nsFrameLoader::nsFrameLoader(Element* aOwner, nsPIDOMWindowOuter* aOpener, bool aNetworkCreated) nsFrameLoader::nsFrameLoader(Element* aOwner, nsPIDOMWindowOuter* aOpener,
bool aNetworkCreated, int32_t aJSPluginID)
: mOwnerContent(aOwner) : mOwnerContent(aOwner)
, mDetachedSubdocFrame(nullptr) , mDetachedSubdocFrame(nullptr)
, mOpener(aOpener) , mOpener(aOpener)
, mRemoteBrowser(nullptr) , mRemoteBrowser(nullptr)
, mChildID(0) , mChildID(0)
, mJSPluginID(aJSPluginID)
, mEventMode(EVENT_MODE_NORMAL_DISPATCH) , mEventMode(EVENT_MODE_NORMAL_DISPATCH)
, mBrowserChangingProcessBlockers(nullptr) , mBrowserChangingProcessBlockers(nullptr)
, mIsPrerendered(false) , mIsPrerendered(false)
@ -193,7 +195,8 @@ nsFrameLoader::~nsFrameLoader()
} }
nsFrameLoader* nsFrameLoader*
nsFrameLoader::Create(Element* aOwner, nsPIDOMWindowOuter* aOpener, bool aNetworkCreated) nsFrameLoader::Create(Element* aOwner, nsPIDOMWindowOuter* aOpener, bool aNetworkCreated,
int32_t aJSPluginId)
{ {
NS_ENSURE_TRUE(aOwner, nullptr); NS_ENSURE_TRUE(aOwner, nullptr);
nsIDocument* doc = aOwner->OwnerDoc(); nsIDocument* doc = aOwner->OwnerDoc();
@ -223,7 +226,7 @@ nsFrameLoader::Create(Element* aOwner, nsPIDOMWindowOuter* aOpener, bool aNetwor
doc->IsStaticDocument()), doc->IsStaticDocument()),
nullptr); nullptr);
return new nsFrameLoader(aOwner, aOpener, aNetworkCreated); return new nsFrameLoader(aOwner, aOpener, aNetworkCreated, aJSPluginId);
} }
NS_IMETHODIMP NS_IMETHODIMP
@ -314,8 +317,15 @@ nsFrameLoader::LoadURI(nsIURI* aURI)
nsCOMPtr<nsIDocument> doc = mOwnerContent->OwnerDoc(); nsCOMPtr<nsIDocument> doc = mOwnerContent->OwnerDoc();
nsresult rv = CheckURILoad(aURI); nsresult rv;
// If IsForJSPlugin() returns true then we want to allow the load. We're just
// loading the source for the implementation of the JS plugin from a URI
// that's under our control. We will already have done the security checks for
// loading the plugin content itself in the object/embed loading code.
if (!IsForJSPlugin()) {
rv = CheckURILoad(aURI);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
}
mURIToLoad = aURI; mURIToLoad = aURI;
rv = doc->InitializeFrameLoader(this); rv = doc->InitializeFrameLoader(this);
@ -2246,6 +2256,10 @@ nsFrameLoader::OwnerIsIsolatedMozBrowserFrame()
bool bool
nsFrameLoader::ShouldUseRemoteProcess() nsFrameLoader::ShouldUseRemoteProcess()
{ {
if (IsForJSPlugin()) {
return true;
}
if (PR_GetEnv("MOZ_DISABLE_OOP_TABS") || if (PR_GetEnv("MOZ_DISABLE_OOP_TABS") ||
Preferences::GetBool("dom.ipc.tabs.disabled", false)) { Preferences::GetBool("dom.ipc.tabs.disabled", false)) {
return false; return false;
@ -2910,7 +2924,9 @@ nsFrameLoader::TryRemoteBrowser()
} }
// <iframe mozbrowser> gets to skip these checks. // <iframe mozbrowser> gets to skip these checks.
if (!OwnerIsMozBrowserFrame()) { // iframes for JS plugins also get to skip these checks. We control the URL that gets
// loaded, but the load is triggered from the document containing the plugin.
if (!OwnerIsMozBrowserFrame() && !IsForJSPlugin()) {
if (parentDocShell->ItemType() != nsIDocShellTreeItem::typeChrome) { if (parentDocShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
// Allow about:addon an exception to this rule so it can load remote // Allow about:addon an exception to this rule so it can load remote
// extension options pages. // extension options pages.
@ -3624,6 +3640,11 @@ nsresult
nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext, nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext,
nsIURI* aURI) nsIURI* aURI)
{ {
if (IsForJSPlugin()) {
return aTabContext->SetTabContextForJSPluginFrame(mJSPluginID) ? NS_OK :
NS_ERROR_FAILURE;
}
OriginAttributes attrs; OriginAttributes attrs;
attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame(); attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame();
nsresult rv; nsresult rv;

Просмотреть файл

@ -26,6 +26,7 @@
#include "nsIWebBrowserPersistable.h" #include "nsIWebBrowserPersistable.h"
#include "nsIFrame.h" #include "nsIFrame.h"
#include "nsIGroupedSHistory.h" #include "nsIGroupedSHistory.h"
#include "nsPluginTags.h"
class nsIURI; class nsIURI;
class nsSubDocumentFrame; class nsSubDocumentFrame;
@ -79,7 +80,8 @@ class nsFrameLoader final : public nsIFrameLoader,
public: public:
static nsFrameLoader* Create(mozilla::dom::Element* aOwner, static nsFrameLoader* Create(mozilla::dom::Element* aOwner,
nsPIDOMWindowOuter* aOpener, nsPIDOMWindowOuter* aOpener,
bool aNetworkCreated); bool aNetworkCreated,
int32_t aJSPluginID = nsFakePluginTag::NOT_JSPLUGIN);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFrameLoader, nsIFrameLoader) NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFrameLoader, nsIFrameLoader)
@ -230,7 +232,8 @@ public:
private: private:
nsFrameLoader(mozilla::dom::Element* aOwner, nsFrameLoader(mozilla::dom::Element* aOwner,
nsPIDOMWindowOuter* aOpener, nsPIDOMWindowOuter* aOpener,
bool aNetworkCreated); bool aNetworkCreated,
int32_t aJSPluginID);
~nsFrameLoader(); ~nsFrameLoader();
void SetOwnerContent(mozilla::dom::Element* aContent); void SetOwnerContent(mozilla::dom::Element* aContent);
@ -242,6 +245,11 @@ private:
*/ */
bool IsRemoteFrame(); bool IsRemoteFrame();
bool IsForJSPlugin()
{
return mJSPluginID != nsFakePluginTag::NOT_JSPLUGIN;
}
/** /**
* Is this a frame loader for a bona fide <iframe mozbrowser>? * Is this a frame loader for a bona fide <iframe mozbrowser>?
* <xul:browser> is not a mozbrowser, so this is false for that case. * <xul:browser> is not a mozbrowser, so this is false for that case.
@ -343,6 +351,8 @@ private:
TabParent* mRemoteBrowser; TabParent* mRemoteBrowser;
uint64_t mChildID; uint64_t mChildID;
int32_t mJSPluginID;
// See nsIFrameLoader.idl. EVENT_MODE_NORMAL_DISPATCH automatically // See nsIFrameLoader.idl. EVENT_MODE_NORMAL_DISPATCH automatically
// forwards some input events to out-of-process content. // forwards some input events to out-of-process content.
uint32_t mEventMode; uint32_t mEventMode;

Просмотреть файл

@ -556,9 +556,9 @@ nsObjectLoadingContent::MakePluginListener()
return true; return true;
} }
// Helper to spawn the frameloader and return a pointer to its docshell // Helper to spawn the frameloader.
already_AddRefed<nsIDocShell> void
nsObjectLoadingContent::SetupFrameLoader(nsIURI *aRecursionCheckURI) nsObjectLoadingContent::SetupFrameLoader(int32_t aJSPluginId)
{ {
nsCOMPtr<nsIContent> thisContent = nsCOMPtr<nsIContent> thisContent =
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
@ -566,9 +566,18 @@ nsObjectLoadingContent::SetupFrameLoader(nsIURI *aRecursionCheckURI)
mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(), mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(),
/* aOpener = */ nullptr, /* aOpener = */ nullptr,
mNetworkCreated); mNetworkCreated, aJSPluginId);
if (!mFrameLoader) { if (!mFrameLoader) {
NS_NOTREACHED("nsFrameLoader::Create failed"); NS_NOTREACHED("nsFrameLoader::Create failed");
}
}
// Helper to spawn the frameloader and return a pointer to its docshell.
already_AddRefed<nsIDocShell>
nsObjectLoadingContent::SetupDocShell(nsIURI* aRecursionCheckURI)
{
SetupFrameLoader(nsFakePluginTag::NOT_JSPLUGIN);
if (!mFrameLoader) {
return nullptr; return nullptr;
} }
@ -2378,6 +2387,23 @@ nsObjectLoadingContent::LoadObject(bool aNotify,
nsCOMPtr<nsIPluginTag> basetag = nsCOMPtr<nsIPluginTag> basetag =
nsContentUtils::PluginTagForType(mContentType, false); nsContentUtils::PluginTagForType(mContentType, false);
nsCOMPtr<nsIFakePluginTag> tag = do_QueryInterface(basetag); nsCOMPtr<nsIFakePluginTag> tag = do_QueryInterface(basetag);
uint32_t id;
if (NS_FAILED(tag->GetId(&id))) {
rv = NS_ERROR_FAILURE;
break;
}
MOZ_ASSERT(id <= PR_INT32_MAX,
"Something went wrong, nsPluginHost::RegisterFakePlugin shouldn't have "
"given out this id.");
SetupFrameLoader(int32_t(id));
if (!mFrameLoader) {
rv = NS_ERROR_FAILURE;
break;
}
nsCOMPtr<nsIURI> handlerURI; nsCOMPtr<nsIURI> handlerURI;
if (tag) { if (tag) {
tag->GetHandlerURI(getter_AddRefs(handlerURI)); tag->GetHandlerURI(getter_AddRefs(handlerURI));
@ -2389,35 +2415,11 @@ nsObjectLoadingContent::LoadObject(bool aNotify,
break; break;
} }
nsCOMPtr<nsIDocShell> docShell = SetupFrameLoader(handlerURI);
if (!docShell) {
rv = NS_ERROR_FAILURE;
break;
}
nsCString spec; nsCString spec;
handlerURI->GetSpec(spec); handlerURI->GetSpec(spec);
LOG(("OBJLC [%p]: Loading fake plugin handler (%s)", this, spec.get())); LOG(("OBJLC [%p]: Loading fake plugin handler (%s)", this, spec.get()));
// XXX(johns): This and moreso the document case below are rv = mFrameLoader->LoadURI(handlerURI);
// sidestepping/duplicating nsFrameLoader's LoadURI code,
// which is not great.
nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
docShell->CreateLoadInfo(getter_AddRefs(loadInfo));
if (loadInfo) {
loadInfo->SetTriggeringPrincipal(thisContent->NodePrincipal());
nsCOMPtr<nsIURI> referrer;
thisContent->NodePrincipal()->GetURI(getter_AddRefs(referrer));
loadInfo->SetReferrer(referrer);
rv = docShell->LoadURI(handlerURI, loadInfo,
nsIWebNavigation::LOAD_FLAGS_NONE, false);
} else {
NS_NOTREACHED("CreateLoadInfo failed");
rv = NS_ERROR_FAILURE;
}
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
LOG(("OBJLC [%p]: LoadURI() failed for fake handler", this)); LOG(("OBJLC [%p]: LoadURI() failed for fake handler", this));
mFrameLoader->Destroy(); mFrameLoader->Destroy();
@ -2435,7 +2437,7 @@ nsObjectLoadingContent::LoadObject(bool aNotify,
break; break;
} }
nsCOMPtr<nsIDocShell> docShell = SetupFrameLoader(mURI); nsCOMPtr<nsIDocShell> docShell = SetupDocShell(mURI);
if (!docShell) { if (!docShell) {
rv = NS_ERROR_FAILURE; rv = NS_ERROR_FAILURE;
break; break;
@ -3122,6 +3124,12 @@ nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner)
mIsStopping = true; mIsStopping = true;
RefPtr<nsPluginInstanceOwner> kungFuDeathGrip(aInstanceOwner); RefPtr<nsPluginInstanceOwner> kungFuDeathGrip(aInstanceOwner);
if (mType == eType_FakePlugin) {
if (mFrameLoader) {
mFrameLoader->Destroy();
mFrameLoader = nullptr;
}
} else {
RefPtr<nsNPAPIPluginInstance> inst; RefPtr<nsNPAPIPluginInstance> inst;
aInstanceOwner->GetInstance(getter_AddRefs(inst)); aInstanceOwner->GetInstance(getter_AddRefs(inst));
if (inst) { if (inst) {
@ -3133,6 +3141,7 @@ nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner)
NS_ASSERTION(pluginHost, "No plugin host?"); NS_ASSERTION(pluginHost, "No plugin host?");
pluginHost->StopPluginInstance(inst); pluginHost->StopPluginInstance(inst);
} }
}
aInstanceOwner->Destroy(); aInstanceOwner->Destroy();

Просмотреть файл

@ -531,13 +531,15 @@ class nsObjectLoadingContent : public nsImageLoadingContent
*/ */
bool MakePluginListener(); bool MakePluginListener();
void SetupFrameLoader(int32_t aJSPluginId);
/** /**
* Helper to spawn mFrameLoader and return a pointer to its docshell * Helper to spawn mFrameLoader and return a pointer to its docshell
* *
* @param aURI URI we intend to load for the recursive load check (does not * @param aURI URI we intend to load for the recursive load check (does not
* actually load anything) * actually load anything)
*/ */
already_AddRefed<nsIDocShell> SetupFrameLoader(nsIURI *aRecursionCheckURI); already_AddRefed<nsIDocShell> SetupDocShell(nsIURI* aRecursionCheckURI);
/** /**
* Unloads all content and resets the object to a completely unloaded state * Unloads all content and resets the object to a completely unloaded state

Просмотреть файл

@ -95,6 +95,7 @@
#include "GeckoProfiler.h" #include "GeckoProfiler.h"
#include "Units.h" #include "Units.h"
#include "mozilla/layers/APZCTreeManager.h" #include "mozilla/layers/APZCTreeManager.h"
#include "nsIObjectLoadingContent.h"
#ifdef XP_MACOSX #ifdef XP_MACOSX
#import <ApplicationServices/ApplicationServices.h> #import <ApplicationServices/ApplicationServices.h>
@ -1282,27 +1283,11 @@ EventStateManager::DispatchCrossProcessEvent(WidgetEvent* aEvent,
} }
bool bool
EventStateManager::IsRemoteTarget(nsIContent* target) { EventStateManager::IsRemoteTarget(nsIContent* target)
if (!target) { {
return false;
}
// <browser/iframe remote=true> from XUL
if (target->IsAnyOfXULElements(nsGkAtoms::browser, nsGkAtoms::iframe) &&
target->AttrValueIs(kNameSpaceID_None, nsGkAtoms::Remote,
nsGkAtoms::_true, eIgnoreCase)) {
return true;
}
// <frame/iframe mozbrowser>
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(target);
if (browserFrame && browserFrame->GetReallyIsBrowser()) {
return !!TabParent::GetFrom(target); return !!TabParent::GetFrom(target);
} }
return false;
}
bool bool
EventStateManager::HandleCrossProcessEvent(WidgetEvent* aEvent, EventStateManager::HandleCrossProcessEvent(WidgetEvent* aEvent,
nsEventStatus *aStatus) { nsEventStatus *aStatus) {

Просмотреть файл

@ -22,6 +22,7 @@ NS_IMPL_ISUPPORTS(ContentBridgeParent,
nsIObserver) nsIObserver)
ContentBridgeParent::ContentBridgeParent() ContentBridgeParent::ContentBridgeParent()
: mIsForJSPlugin(false)
{} {}
ContentBridgeParent::~ContentBridgeParent() ContentBridgeParent::~ContentBridgeParent()

Просмотреть файл

@ -62,6 +62,11 @@ public:
// XXX: do we need this for ContentBridgeParent? // XXX: do we need this for ContentBridgeParent?
return -1; return -1;
} }
virtual bool IsForJSPlugin() const override
{
return mIsForJSPlugin;
}
virtual mozilla::ipc::PParentToChildStreamParent* virtual mozilla::ipc::PParentToChildStreamParent*
SendPParentToChildStreamConstructor(mozilla::ipc::PParentToChildStreamParent*) override; SendPParentToChildStreamConstructor(mozilla::ipc::PParentToChildStreamParent*) override;
@ -94,6 +99,10 @@ protected:
{ {
mIsForBrowser = aIsForBrowser; mIsForBrowser = aIsForBrowser;
} }
void SetIsForJSPlugin(bool aIsForJSPlugin)
{
mIsForJSPlugin = aIsForJSPlugin;
}
void Close() void Close()
{ {
@ -164,6 +173,7 @@ protected: // members
RefPtr<ContentBridgeParent> mSelfRef; RefPtr<ContentBridgeParent> mSelfRef;
ContentParentId mChildID; ContentParentId mChildID;
bool mIsForBrowser; bool mIsForBrowser;
bool mIsForJSPlugin;
private: private:
friend class ContentParent; friend class ContentParent;

Просмотреть файл

@ -550,6 +550,7 @@ GetTelemetryProcessID(const nsAString& remoteType)
} // anonymous namespace } // anonymous namespace
nsDataHashtable<nsUint32HashKey, ContentParent*>* ContentParent::sJSPluginContentParents;
nsTArray<ContentParent*>* ContentParent::sPrivateContent; nsTArray<ContentParent*>* ContentParent::sPrivateContent;
StaticAutoPtr<LinkedList<ContentParent> > ContentParent::sContentParents; StaticAutoPtr<LinkedList<ContentParent> > ContentParent::sContentParents;
#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
@ -880,6 +881,35 @@ ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
return p.forget(); return p.forget();
} }
/*static*/ already_AddRefed<ContentParent>
ContentParent::GetNewOrUsedJSPluginProcess(uint32_t aPluginID,
const hal::ProcessPriority& aPriority)
{
RefPtr<ContentParent> p;
if (sJSPluginContentParents) {
p = sJSPluginContentParents->Get(aPluginID);
} else {
sJSPluginContentParents =
new nsDataHashtable<nsUint32HashKey, ContentParent*>();
}
if (p) {
return p.forget();
}
p = new ContentParent(aPluginID);
if (!p->LaunchSubprocess(aPriority)) {
return nullptr;
}
p->Init();
sJSPluginContentParents->Put(aPluginID, p);
return p.forget();
}
/*static*/ ProcessPriority /*static*/ ProcessPriority
ContentParent::GetInitialProcessPriority(Element* aFrameElement) ContentParent::GetInitialProcessPriority(Element* aFrameElement)
{ {
@ -940,8 +970,14 @@ ContentParent::RecvCreateChildProcess(const IPCTabContext& aContext,
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
if (tc.GetTabContext().IsJSPlugin()) {
cp = GetNewOrUsedJSPluginProcess(tc.GetTabContext().JSPluginId(),
aPriority);
}
else {
cp = GetNewOrUsedBrowserProcess(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), cp = GetNewOrUsedBrowserProcess(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE),
aPriority, this); aPriority, this);
}
if (!cp) { if (!cp) {
*aCpId = 0; *aCpId = 0;
@ -953,6 +989,16 @@ ContentParent::RecvCreateChildProcess(const IPCTabContext& aContext,
*aIsForBrowser = cp->IsForBrowser(); *aIsForBrowser = cp->IsForBrowser();
ContentProcessManager *cpm = ContentProcessManager::GetSingleton(); ContentProcessManager *cpm = ContentProcessManager::GetSingleton();
if (cp->IsForJSPlugin()) {
// We group all the iframes for a specific JS plugin into one process, regardless of
// origin. As a consequence that process can't be a child of the content process that
// contains the document with the element loading the plugin. All content processes
// need to be able to communicate with the process for the JS plugin.
cpm->RegisterRemoteFrame(aTabId, ChildID(), aOpenerTabId, aContext, cp->ChildID());
return IPC_OK();
}
// cp was already added to the ContentProcessManager, this just sets the parent ID.
cpm->AddContentProcess(cp, this->ChildID()); cpm->AddContentProcess(cp, this->ChildID());
if (cpm->AddGrandchildProcess(this->ChildID(), cp->ChildID()) && if (cpm->AddGrandchildProcess(this->ChildID(), cp->ChildID()) &&
@ -970,11 +1016,7 @@ ContentParent::RecvBridgeToChildProcess(const ContentParentId& aCpId,
ContentProcessManager *cpm = ContentProcessManager::GetSingleton(); ContentProcessManager *cpm = ContentProcessManager::GetSingleton();
ContentParent* cp = cpm->GetContentProcessById(aCpId); ContentParent* cp = cpm->GetContentProcessById(aCpId);
if (cp) { if (cp && cp->CanCommunicateWith(ChildID())) {
ContentParentId parentId;
if (cpm->GetParentProcessId(cp->ChildID(), &parentId) &&
parentId == this->ChildID()) {
Endpoint<PContentBridgeParent> parent; Endpoint<PContentBridgeParent> parent;
Endpoint<PContentBridgeChild> child; Endpoint<PContentBridgeChild> child;
@ -991,7 +1033,6 @@ ContentParent::RecvBridgeToChildProcess(const ContentParentId& aCpId,
return IPC_OK(); return IPC_OK();
} }
}
// You can't bridge to a process you didn't open! // You can't bridge to a process you didn't open!
KillHard("BridgeToChildProcess"); KillHard("BridgeToChildProcess");
@ -1173,15 +1214,21 @@ ContentParent::CreateBrowser(const TabContext& aContext,
RefPtr<nsIContentParent> constructorSender; RefPtr<nsIContentParent> constructorSender;
if (isInContentProcess) { if (isInContentProcess) {
MOZ_ASSERT(aContext.IsMozBrowserElement()); MOZ_ASSERT(aContext.IsMozBrowserElement() || aContext.IsJSPlugin());
constructorSender = CreateContentBridgeParent(aContext, initialPriority, constructorSender = CreateContentBridgeParent(aContext, initialPriority,
openerTabId, tabId); openerTabId, tabId);
} else { } else {
if (aOpenerContentParent) { if (aOpenerContentParent) {
constructorSender = aOpenerContentParent; constructorSender = aOpenerContentParent;
} else {
if (aContext.IsJSPlugin()) {
constructorSender =
GetNewOrUsedJSPluginProcess(aContext.JSPluginId(),
initialPriority);
} else { } else {
constructorSender = constructorSender =
GetNewOrUsedBrowserProcess(remoteType, initialPriority, nullptr); GetNewOrUsedBrowserProcess(remoteType, initialPriority, nullptr);
}
if (!constructorSender) { if (!constructorSender) {
return nullptr; return nullptr;
} }
@ -1276,6 +1323,7 @@ ContentParent::CreateContentBridgeParent(const TabContext& aContext,
ContentBridgeParent* parent = ContentBridgeParent::Create(Move(endpoint)); ContentBridgeParent* parent = ContentBridgeParent::Create(Move(endpoint));
parent->SetChildID(cpId); parent->SetChildID(cpId);
parent->SetIsForBrowser(isForBrowser); parent->SetIsForBrowser(isForBrowser);
parent->SetIsForJSPlugin(aContext.IsJSPlugin());
return parent; return parent;
} }
@ -1478,7 +1526,15 @@ ContentParent::ShutDownMessageManager()
void void
ContentParent::MarkAsTroubled() ContentParent::MarkAsTroubled()
{ {
if (sBrowserContentParents) { if (IsForJSPlugin()) {
if (sJSPluginContentParents) {
sJSPluginContentParents->Remove(mJSPluginID);
if (!sJSPluginContentParents->Count()) {
delete sJSPluginContentParents;
sJSPluginContentParents = nullptr;
}
}
} else if (sBrowserContentParents) {
nsTArray<ContentParent*>* contentParents = nsTArray<ContentParent*>* contentParents =
sBrowserContentParents->Get(mRemoteType); sBrowserContentParents->Get(mRemoteType);
if (contentParents) { if (contentParents) {
@ -1593,13 +1649,9 @@ ContentParent::RecvAllocateLayerTreeId(const ContentParentId& aCpId,
// child of it. // child of it.
ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
RefPtr<ContentParent> contentParent = cpm->GetContentProcessById(aCpId); RefPtr<ContentParent> contentParent = cpm->GetContentProcessById(aCpId);
if (ChildID() != aCpId) { if (ChildID() != aCpId && !contentParent->CanCommunicateWith(ChildID())) {
ContentParentId parent;
if (!cpm->GetParentProcessId(contentParent->ChildID(), &parent) ||
ChildID() != parent) {
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
}
// GetTopLevelTabParentByProcessAndTabId will make sure that aTabId // GetTopLevelTabParentByProcessAndTabId will make sure that aTabId
// lives in the process for aCpId. // lives in the process for aCpId.
@ -1614,17 +1666,23 @@ ContentParent::RecvAllocateLayerTreeId(const ContentParentId& aCpId,
} }
mozilla::ipc::IPCResult mozilla::ipc::IPCResult
ContentParent::RecvDeallocateLayerTreeId(const uint64_t& aId) ContentParent::RecvDeallocateLayerTreeId(const ContentParentId& aCpId,
const uint64_t& aId)
{ {
GPUProcessManager* gpu = GPUProcessManager::Get(); GPUProcessManager* gpu = GPUProcessManager::Get();
if (!gpu->IsLayerTreeIdMapped(aId, OtherPid())) ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
{ RefPtr<ContentParent> contentParent = cpm->GetContentProcessById(aCpId);
if (!contentParent->CanCommunicateWith(ChildID())) {
return IPC_FAIL(this, "Spoofed DeallocateLayerTreeId call");
}
if (!gpu->IsLayerTreeIdMapped(aId, contentParent->OtherPid())) {
// You can't deallocate layer tree ids that you didn't allocate // You can't deallocate layer tree ids that you didn't allocate
KillHard("DeallocateLayerTreeId"); KillHard("DeallocateLayerTreeId");
} }
gpu->UnmapLayerTreeId(aId, OtherPid()); gpu->UnmapLayerTreeId(aId, contentParent->OtherPid());
return IPC_OK(); return IPC_OK();
} }
@ -1801,6 +1859,10 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
bool bool
ContentParent::ShouldKeepProcessAlive() const ContentParent::ShouldKeepProcessAlive() const
{ {
if (IsForJSPlugin()) {
return true;
}
if (!sBrowserContentParents) { if (!sBrowserContentParents) {
return false; return false;
} }
@ -2033,7 +2095,8 @@ ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PR
} }
ContentParent::ContentParent(ContentParent* aOpener, ContentParent::ContentParent(ContentParent* aOpener,
const nsAString& aRemoteType) const nsAString& aRemoteType,
int32_t aJSPluginID)
: nsIContentParent() : nsIContentParent()
, mSubprocess(nullptr) , mSubprocess(nullptr)
, mLaunchTS(TimeStamp::Now()) , mLaunchTS(TimeStamp::Now())
@ -2041,6 +2104,7 @@ ContentParent::ContentParent(ContentParent* aOpener,
, mRemoteType(aRemoteType) , mRemoteType(aRemoteType)
, mChildID(gContentChildID++) , mChildID(gContentChildID++)
, mGeolocationWatchID(-1) , mGeolocationWatchID(-1)
, mJSPluginID(aJSPluginID)
, mNumDestroyingTabs(0) , mNumDestroyingTabs(0)
, mIsAvailable(true) , mIsAvailable(true)
, mIsAlive(true) , mIsAlive(true)
@ -2086,10 +2150,15 @@ ContentParent::~ContentParent()
// We should be removed from all these lists in ActorDestroy. // We should be removed from all these lists in ActorDestroy.
MOZ_ASSERT(!sPrivateContent || !sPrivateContent->Contains(this)); MOZ_ASSERT(!sPrivateContent || !sPrivateContent->Contains(this));
if (IsForJSPlugin()) {
MOZ_ASSERT(!sJSPluginContentParents ||
!sJSPluginContentParents->Get(mJSPluginID));
} else {
MOZ_ASSERT(!sBrowserContentParents || MOZ_ASSERT(!sBrowserContentParents ||
!sBrowserContentParents->Contains(mRemoteType) || !sBrowserContentParents->Contains(mRemoteType) ||
!sBrowserContentParents->Get(mRemoteType)->Contains(this)); !sBrowserContentParents->Get(mRemoteType)->Contains(this));
} }
}
void void
ContentParent::InitInternal(ProcessPriority aInitialPriority, ContentParent::InitInternal(ProcessPriority aInitialPriority,
@ -5212,3 +5281,19 @@ ContentParent::RecvFileCreationRequest(const nsID& aID,
return IPC_OK(); return IPC_OK();
} }
bool
ContentParent::CanCommunicateWith(ContentParentId aOtherProcess)
{
// Normally a process can only communicate with its parent, but a JS plugin process can
// communicate with any process.
ContentProcessManager *cpm = ContentProcessManager::GetSingleton();
ContentParentId parentId;
if (!cpm->GetParentProcessId(ChildID(), &parentId)) {
return false;
}
if (IsForJSPlugin()) {
return parentId == ContentParentId(0);
}
return parentId == aOtherProcess;
}

Просмотреть файл

@ -22,6 +22,7 @@
#include "mozilla/UniquePtr.h" #include "mozilla/UniquePtr.h"
#include "nsDataHashtable.h" #include "nsDataHashtable.h"
#include "nsPluginTags.h"
#include "nsFrameMessageManager.h" #include "nsFrameMessageManager.h"
#include "nsHashKeys.h" #include "nsHashKeys.h"
#include "nsIObserver.h" #include "nsIObserver.h"
@ -177,6 +178,14 @@ public:
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND, hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
ContentParent* aOpener = nullptr); ContentParent* aOpener = nullptr);
/**
* Get or create a content process for a JS plugin. aPluginID is the id of the JS plugin
* (@see nsFakePlugin::mId). There is a maximum of one process per JS plugin.
*/
static already_AddRefed<ContentParent>
GetNewOrUsedJSPluginProcess(uint32_t aPluginID,
const hal::ProcessPriority& aPriority);
/** /**
* Get or create a content process for the given TabContext. aFrameElement * Get or create a content process for the given TabContext. aFrameElement
* should be the frame/iframe element with which this process will * should be the frame/iframe element with which this process will
@ -362,6 +371,10 @@ public:
{ {
return mIsForBrowser; return mIsForBrowser;
} }
virtual bool IsForJSPlugin() const override
{
return mJSPluginID != nsFakePluginTag::NOT_JSPLUGIN;
}
GeckoChildProcessHost* Process() const GeckoChildProcessHost* Process() const
{ {
@ -668,6 +681,7 @@ private:
*/ */
static nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>* sBrowserContentParents; static nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>* sBrowserContentParents;
static nsTArray<ContentParent*>* sPrivateContent; static nsTArray<ContentParent*>* sPrivateContent;
static nsDataHashtable<nsUint32HashKey, ContentParent*> *sJSPluginContentParents;
static StaticAutoPtr<LinkedList<ContentParent> > sContentParents; static StaticAutoPtr<LinkedList<ContentParent> > sContentParents;
static void JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses, static void JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses,
@ -711,8 +725,17 @@ private:
FORWARD_SHMEM_ALLOCATOR_TO(PContentParent) FORWARD_SHMEM_ALLOCATOR_TO(PContentParent)
explicit ContentParent(int32_t aPluginID)
: ContentParent(nullptr, EmptyString(), aPluginID)
{}
ContentParent(ContentParent* aOpener, ContentParent(ContentParent* aOpener,
const nsAString& aRemoteType); const nsAString& aRemoteType)
: ContentParent(aOpener, aRemoteType, nsFakePluginTag::NOT_JSPLUGIN)
{}
ContentParent(ContentParent* aOpener,
const nsAString& aRemoteType,
int32_t aPluginID);
// Launch the subprocess and associated initialization. // Launch the subprocess and associated initialization.
// Returns false if the process fails to start. // Returns false if the process fails to start.
@ -1050,7 +1073,8 @@ private:
const TabId& aTabId, const TabId& aTabId,
uint64_t* aId) override; uint64_t* aId) override;
virtual mozilla::ipc::IPCResult RecvDeallocateLayerTreeId(const uint64_t& aId) override; virtual mozilla::ipc::IPCResult RecvDeallocateLayerTreeId(const ContentParentId& aCpId,
const uint64_t& aId) override;
virtual mozilla::ipc::IPCResult RecvGraphicsError(const nsCString& aError) override; virtual mozilla::ipc::IPCResult RecvGraphicsError(const nsCString& aError) override;
@ -1153,6 +1177,8 @@ public:
const bool& aMinimizeMemoryUsage, const bool& aMinimizeMemoryUsage,
const MaybeFileDesc& aDMDFile) override; const MaybeFileDesc& aDMDFile) override;
bool CanCommunicateWith(ContentParentId aOtherProcess);
private: private:
// If you add strong pointers to cycle collected objects here, be sure to // If you add strong pointers to cycle collected objects here, be sure to
@ -1168,6 +1194,12 @@ private:
ContentParentId mChildID; ContentParentId mChildID;
int32_t mGeolocationWatchID; int32_t mGeolocationWatchID;
// This contains the id for the JS plugin (@see nsFakePluginTag) if this is the
// ContentParent for a process containing iframes for that JS plugin.
// If this is not a ContentParent for a JS plugin then it contains the value
// nsFakePluginTag::NOT_JSPLUGIN.
int32_t mJSPluginID;
nsCString mKillHardAnnotation; nsCString mKillHardAnnotation;
// After we initiate shutdown, we also start a timer to ensure // After we initiate shutdown, we also start a timer to ensure

Просмотреть файл

@ -846,7 +846,7 @@ parent:
// Tell the compositor to allocate a layer tree id for nested remote mozbrowsers. // Tell the compositor to allocate a layer tree id for nested remote mozbrowsers.
sync AllocateLayerTreeId(ContentParentId cpId, TabId tabId) sync AllocateLayerTreeId(ContentParentId cpId, TabId tabId)
returns (uint64_t id); returns (uint64_t id);
async DeallocateLayerTreeId(uint64_t id); async DeallocateLayerTreeId(ContentParentId cpId, uint64_t id);
/** /**
* Notifies the parent about a recording device is starting or shutdown. * Notifies the parent about a recording device is starting or shutdown.

Просмотреть файл

@ -56,6 +56,11 @@ struct FrameIPCTabContext
UIStateChangeType showFocusRings; UIStateChangeType showFocusRings;
}; };
struct JSPluginFrameIPCTabContext
{
uint32_t jsPluginId;
};
// XXXcatalinb: This is only used by ServiceWorkerClients::OpenWindow. // XXXcatalinb: This is only used by ServiceWorkerClients::OpenWindow.
// Because service workers don't have an associated TabChild // Because service workers don't have an associated TabChild
// we can't satisfy the security constraints on b2g. As such, the parent // we can't satisfy the security constraints on b2g. As such, the parent
@ -74,6 +79,7 @@ union IPCTabContext
{ {
PopupIPCTabContext; PopupIPCTabContext;
FrameIPCTabContext; FrameIPCTabContext;
JSPluginFrameIPCTabContext;
UnsafeIPCTabContext; UnsafeIPCTabContext;
}; };

Просмотреть файл

@ -23,7 +23,7 @@ TabContext::TabContext()
: mIsPrerendered(false) : mIsPrerendered(false)
, mInitialized(false) , mInitialized(false)
, mIsMozBrowserElement(false) , mIsMozBrowserElement(false)
, mOriginAttributes() , mJSPluginID(-1)
, mShowAccelerators(UIStateChangeType_NoChange) , mShowAccelerators(UIStateChangeType_NoChange)
, mShowFocusRings(UIStateChangeType_NoChange) , mShowFocusRings(UIStateChangeType_NoChange)
{ {
@ -47,6 +47,18 @@ TabContext::IsMozBrowser() const
return IsMozBrowserElement(); return IsMozBrowserElement();
} }
bool
TabContext::IsJSPlugin() const
{
return mJSPluginID >= 0;
}
int32_t
TabContext::JSPluginId() const
{
return mJSPluginID;
}
bool bool
TabContext::SetTabContext(const TabContext& aContext) TabContext::SetTabContext(const TabContext& aContext)
{ {
@ -127,9 +139,23 @@ TabContext::SetTabContext(bool aIsMozBrowserElement,
return true; return true;
} }
bool
TabContext::SetTabContextForJSPluginFrame(int32_t aJSPluginID)
{
NS_ENSURE_FALSE(mInitialized, false);
mInitialized = true;
mJSPluginID = aJSPluginID;
return true;
}
IPCTabContext IPCTabContext
TabContext::AsIPCTabContext() const TabContext::AsIPCTabContext() const
{ {
if (IsJSPlugin()) {
return IPCTabContext(JSPluginFrameIPCTabContext(mJSPluginID));
}
return IPCTabContext(FrameIPCTabContext(mOriginAttributes, return IPCTabContext(FrameIPCTabContext(mOriginAttributes,
mIsMozBrowserElement, mIsMozBrowserElement,
mIsPrerendered, mIsPrerendered,
@ -143,6 +169,7 @@ MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
{ {
bool isMozBrowserElement = false; bool isMozBrowserElement = false;
bool isPrerendered = false; bool isPrerendered = false;
int32_t jsPluginId = -1;
OriginAttributes originAttributes; OriginAttributes originAttributes;
nsAutoString presentationURL; nsAutoString presentationURL;
UIStateChangeType showAccelerators = UIStateChangeType_NoChange; UIStateChangeType showAccelerators = UIStateChangeType_NoChange;
@ -195,6 +222,13 @@ MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
originAttributes = context->mOriginAttributes; originAttributes = context->mOriginAttributes;
break; break;
} }
case IPCTabContext::TJSPluginFrameIPCTabContext: {
const JSPluginFrameIPCTabContext &ipcContext =
aParams.get_JSPluginFrameIPCTabContext();
jsPluginId = ipcContext.jsPluginId();
break;
}
case IPCTabContext::TFrameIPCTabContext: { case IPCTabContext::TFrameIPCTabContext: {
const FrameIPCTabContext &ipcContext = const FrameIPCTabContext &ipcContext =
aParams.get_FrameIPCTabContext(); aParams.get_FrameIPCTabContext();
@ -229,12 +263,16 @@ MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
} }
bool rv; bool rv;
if (jsPluginId >= 0) {
rv = mTabContext.SetTabContextForJSPluginFrame(jsPluginId);
} else {
rv = mTabContext.SetTabContext(isMozBrowserElement, rv = mTabContext.SetTabContext(isMozBrowserElement,
isPrerendered, isPrerendered,
showAccelerators, showAccelerators,
showFocusRings, showFocusRings,
originAttributes, originAttributes,
presentationURL); presentationURL);
}
if (!rv) { if (!rv) {
mInvalidReason = "Couldn't initialize TabContext."; mInvalidReason = "Couldn't initialize TabContext.";
} }

Просмотреть файл

@ -63,6 +63,9 @@ public:
*/ */
bool IsMozBrowser() const; bool IsMozBrowser() const;
bool IsJSPlugin() const;
int32_t JSPluginId() const;
/** /**
* OriginAttributesRef() returns the OriginAttributes of this frame to * OriginAttributesRef() returns the OriginAttributes of this frame to
* the caller. This is used to store any attribute associated with the frame's * the caller. This is used to store any attribute associated with the frame's
@ -124,6 +127,14 @@ protected:
*/ */
bool mIsPrerendered; bool mIsPrerendered;
/**
* Set this TabContext to be for a JS plugin. aPluginID is the id of the JS plugin
* (@see nsFakePlugin::mId).
* As with the other protected mutator methods, this lets you modify a TabContext once.
* (@see TabContext::SetTabContext above for more details).
*/
bool SetTabContextForJSPluginFrame(int32_t aJSPluginID);
private: private:
/** /**
* Has this TabContext been initialized? If so, mutator methods will fail. * Has this TabContext been initialized? If so, mutator methods will fail.
@ -138,6 +149,8 @@ private:
*/ */
bool mIsMozBrowserElement; bool mIsMozBrowserElement;
int32_t mJSPluginID;
/** /**
* OriginAttributes of the top level tab docShell * OriginAttributes of the top level tab docShell
*/ */
@ -183,6 +196,12 @@ public:
aOriginAttributes, aOriginAttributes,
aPresentationURL); aPresentationURL);
} }
bool SetTabContextForJSPluginFrame(uint32_t aJSPluginID)
{
return TabContext::SetTabContextForJSPluginFrame(aJSPluginID);
}
}; };
/** /**

Просмотреть файл

@ -61,6 +61,7 @@ public:
virtual ContentParentId ChildID() const = 0; virtual ContentParentId ChildID() const = 0;
virtual bool IsForBrowser() const = 0; virtual bool IsForBrowser() const = 0;
virtual bool IsForJSPlugin() const = 0;
virtual mozilla::ipc::PIPCBlobInputStreamParent* virtual mozilla::ipc::PIPCBlobInputStreamParent*
SendPIPCBlobInputStreamConstructor(mozilla::ipc::PIPCBlobInputStreamParent* aActor, SendPIPCBlobInputStreamConstructor(mozilla::ipc::PIPCBlobInputStreamParent* aActor,

Просмотреть файл

@ -73,4 +73,9 @@ interface nsIFakePluginTag : nsIPluginTag
// plugin. Note that the original data/src value for the plugin is not loaded // plugin. Note that the original data/src value for the plugin is not loaded
// and will need to be requested by the handler via XHR or similar if desired. // and will need to be requested by the handler via XHR or similar if desired.
readonly attribute nsIURI handlerURI; readonly attribute nsIURI handlerURI;
/**
* A unique id for this JS-implemented plugin. 0 is a valid id.
*/
readonly attribute unsigned long id;
}; };

Просмотреть файл

@ -864,6 +864,7 @@ nsresult
nsFakePluginTag::Create(const FakePluginTagInit& aInitDictionary, nsFakePluginTag::Create(const FakePluginTagInit& aInitDictionary,
nsFakePluginTag** aPluginTag) nsFakePluginTag** aPluginTag)
{ {
NS_ENSURE_TRUE(sNextId <= PR_INT32_MAX, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_TRUE(!aInitDictionary.mMimeEntries.IsEmpty(), NS_ERROR_INVALID_ARG); NS_ENSURE_TRUE(!aInitDictionary.mMimeEntries.IsEmpty(), NS_ERROR_INVALID_ARG);
RefPtr<nsFakePluginTag> tag = new nsFakePluginTag(); RefPtr<nsFakePluginTag> tag = new nsFakePluginTag();
@ -1059,3 +1060,10 @@ nsFakePluginTag::GetLoaded(bool* ret)
*ret = true; *ret = true;
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsFakePluginTag::GetId(uint32_t* aId)
{
*aId = mId;
return NS_OK;
}

Просмотреть файл

@ -229,6 +229,8 @@ public:
uint32_t Id() const { return mId; } uint32_t Id() const { return mId; }
static const int32_t NOT_JSPLUGIN = -1;
private: private:
nsFakePluginTag(); nsFakePluginTag();
virtual ~nsFakePluginTag(); virtual ~nsFakePluginTag();
@ -237,6 +239,7 @@ private:
// nsPluginHost::RegisterFakePlugin assigns a new id. The id is transferred // nsPluginHost::RegisterFakePlugin assigns a new id. The id is transferred
// through IPC when getting the list of JS-implemented plugins from child // through IPC when getting the list of JS-implemented plugins from child
// processes, so it should be consistent across processes. // processes, so it should be consistent across processes.
// 0 is a valid id.
uint32_t mId; uint32_t mId;
// The URI of the handler for our fake plugin. // The URI of the handler for our fake plugin.

Просмотреть файл

@ -245,7 +245,8 @@ RenderFrameParent::ActorDestroy(ActorDestroyReason why)
if (XRE_IsParentProcess()) { if (XRE_IsParentProcess()) {
GPUProcessManager::Get()->UnmapLayerTreeId(mLayersId, OtherPid()); GPUProcessManager::Get()->UnmapLayerTreeId(mLayersId, OtherPid());
} else if (XRE_IsContentProcess()) { } else if (XRE_IsContentProcess()) {
ContentChild::GetSingleton()->SendDeallocateLayerTreeId(mLayersId); TabParent* browser = TabParent::GetFrom(mFrameLoader);
ContentChild::GetSingleton()->SendDeallocateLayerTreeId(browser->Manager()->ChildID(), mLayersId);
} }
} }