Bug 1334716 - Make process selection a service and implementable in JS. r=krizsa

MozReview-Commit-ID: CViRvZB8nKe

--HG--
extra : rebase_source : 3e2c4d785ebe9af1b74ad4407d9fc9e2cf371bb4
This commit is contained in:
Blake Kaplan 2016-11-01 16:02:43 -07:00
Родитель 08388a619c
Коммит b83ba23582
8 изменённых файлов: 265 добавлений и 17 удалений

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

@ -520,6 +520,9 @@
@RESPATH@/components/remotebrowserutils.manifest
@RESPATH@/components/RemoteWebNavigation.js
@RESPATH@/components/ProcessSelector.js
@RESPATH@/components/ProcessSelector.manifest
@RESPATH@/components/SlowScriptDebug.manifest
@RESPATH@/components/SlowScriptDebug.js

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

@ -0,0 +1,62 @@
/* 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/. */
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import('resource://gre/modules/Services.jsm');
const BASE_PREF = "dom.ipc.processCount"
const PREF_BRANCH = BASE_PREF + ".";
// Utilities:
function getMaxContentParents(processType) {
let maxContentParents = -1;
try {
maxContentParents = Services.prefs.getIntPref(PREF_BRANCH + aType);
} catch (e) {
// Pref probably didn't exist, get the default number of processes.
try {
maxContentParents = Services.prefs.getIntPref(BASE_PREF);
} catch (e) {
// No prefs? That's odd, use only one process.
maxContentParents = 1;
}
}
return maxContentParents;
}
// Fills up aProcesses until max and then selects randomly from the available
// ones.
function RandomSelector() {
}
RandomSelector.prototype = {
classID: Components.ID("{c616fcfd-9737-41f1-aa74-cee72a38f91b}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentProcessProvider]),
provideProcess(aType, aOpener, aProcesses, aCount) {
let maxContentParents = getMaxContentParents();
if (aCount < maxContentParents) {
return Ci.nsIContentProcessProvider.NEW_PROCESS;
}
let startIdx = Math.floor(Math.random() * maxContentParents);
let curIdx = startIdx;
do {
if (aProcesses[curIdx].opener === aOpener) {
return curIdx;
}
curIdx = (curIdx + 1) % maxContentParents;
} while (curIdx !== startIdx);
return Ci.nsIContentProcessProvider.NEW_PROCESS;
},
};
var components = [RandomSelector];
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

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

@ -0,0 +1,2 @@
component {c616fcfd-9737-41f1-aa74-cee72a38f91b} ProcessSelector.js
contract @mozilla.org/ipc/processselector;1 {c616fcfd-9737-41f1-aa74-cee72a38f91b}

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

@ -391,6 +391,8 @@ EXTRA_COMPONENTS += [
'contentAreaDropListener.manifest',
'messageWakeupService.js',
'messageWakeupService.manifest',
'ProcessSelector.js',
'ProcessSelector.manifest',
'SlowScriptDebug.js',
'SlowScriptDebug.manifest',
]

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

@ -11,6 +11,7 @@ XPIDL_SOURCES += [
'nsIContentPermissionPrompt.idl',
'nsIContentPrefService.idl',
'nsIContentPrefService2.idl',
'nsIContentProcess.idl',
'nsIContentURIGrouper.idl',
'nsIDOMChromeWindow.idl',
'nsIDOMClientRect.idl',

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

@ -0,0 +1,53 @@
/* 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 "nsISupports.idl"
interface nsIDOMElement;
interface nsIMessageSender;
interface nsIURI;
[scriptable, builtinclass, uuid(456f58be-29dd-4973-885b-95aece1c9a8a)]
interface nsIContentProcessInfo : nsISupports
{
/**
* Is this content process alive?
*/
readonly attribute boolean isAlive;
/**
* The content process's PID.
* Throws if the process is not alive.
*/
readonly attribute int32_t processId;
/**
* This content process's opener.
*/
readonly attribute nsIContentProcessInfo opener;
/**
* The process manager for this ContentParent (so a process message manager
* as opposed to a frame message manager.
*/
readonly attribute nsIMessageSender messageManager;
};
[scriptable, uuid(83ffb063-5f65-4c45-ae07-3f553e0809bb)]
interface nsIContentProcessProvider : nsISupports
{
/**
* Return this from provideProcess to create a new process.
*/
const int32_t NEW_PROCESS = -1;
/**
* Given aAliveProcesses (with an opener aOpener), choose which process of
* aType to use. Return nsIContentProcessProvider.NEW_PROCESS to ask the
* caller to create a new content process.
*/
int32_t provideProcess(in AString aType, in nsIContentProcessInfo aOpener,
[array, size_is(aCount)] in nsIContentProcessInfo aAliveProcesses,
in uint32_t aCount);
};

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

@ -112,6 +112,7 @@
#include "nsIAlertsService.h"
#include "nsIClipboard.h"
#include "nsContentPermissionHelper.h"
#include "nsIContentProcess.h"
#include "nsICycleCollectorListener.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIDocument.h"
@ -435,6 +436,90 @@ ContentParentsMemoryReporter::CollectReports(
}
nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>* ContentParent::sBrowserContentParents;
namespace {
class ScriptableCPInfo final : public nsIContentProcessInfo
{
public:
explicit ScriptableCPInfo(ContentParent* aParent)
: mContentParent(aParent)
{
MOZ_ASSERT(mContentParent);
}
NS_DECL_ISUPPORTS
NS_DECL_NSICONTENTPROCESSINFO
void ProcessDied()
{
mContentParent = nullptr;
}
private:
~ScriptableCPInfo()
{
MOZ_ASSERT(!mContentParent, "must call ProcessDied");
}
ContentParent* mContentParent;
};
NS_IMPL_ISUPPORTS(ScriptableCPInfo, nsIContentProcessInfo)
NS_IMETHODIMP
ScriptableCPInfo::GetIsAlive(bool* aIsAlive)
{
*aIsAlive = mContentParent != nullptr;
return NS_OK;
}
NS_IMETHODIMP
ScriptableCPInfo::GetProcessId(int32_t* aPID)
{
if (!mContentParent) {
*aPID = -1;
return NS_ERROR_NOT_INITIALIZED;
}
*aPID = mContentParent->Pid();
if (*aPID == -1) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
ScriptableCPInfo::GetOpener(nsIContentProcessInfo** aInfo)
{
*aInfo = nullptr;
if (!mContentParent) {
return NS_ERROR_NOT_INITIALIZED;
}
if (ContentParent* opener = mContentParent->Opener()) {
nsCOMPtr<nsIContentProcessInfo> info = opener->ScriptableHelper();
info.forget(aInfo);
}
return NS_OK;
}
NS_IMETHODIMP
ScriptableCPInfo::GetMessageManager(nsIMessageSender** aMessenger)
{
*aMessenger = nullptr;
if (!mContentParent) {
return NS_ERROR_NOT_INITIALIZED;
}
nsCOMPtr<nsIMessageSender> manager = mContentParent->GetMessageManager();
manager.forget(aMessenger);
return NS_OK;
}
} // anonymous namespace
nsTArray<ContentParent*>* ContentParent::sPrivateContent;
StaticAutoPtr<LinkedList<ContentParent> > ContentParent::sContentParents;
#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
@ -697,38 +782,65 @@ ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
ContentParent* aOpener)
{
nsTArray<ContentParent*>& contentParents = GetOrCreatePool(aRemoteType);
uint32_t maxContentParents = GetMaxProcessCount(aRemoteType);
RefPtr<ContentParent> p;
if (contentParents.Length() >= maxContentParents) {
if (aRemoteType.EqualsLiteral(LARGE_ALLOCATION_REMOTE_TYPE)) {
// We never want to re-use Large-Allocation processes.
if (aRemoteType.EqualsLiteral(LARGE_ALLOCATION_REMOTE_TYPE)) {
if (contentParents.Length() >= maxContentParents) {
return GetNewOrUsedBrowserProcess(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE),
aPriority,
aOpener);
}
} else {
nsTArray<nsIContentProcessInfo*> infos(contentParents.Length());
for (auto* cp : contentParents) {
infos.AppendElement(cp->mScriptableHelper);
}
if ((p = RandomSelect(contentParents, aOpener, maxContentParents))) {
nsCOMPtr<nsIContentProcessProvider> cpp =
do_GetService("@mozilla.org/ipc/processselector;1");
nsIContentProcessInfo* openerInfo = aOpener ? aOpener->mScriptableHelper.get() : nullptr;
int32_t index;
if (cpp &&
NS_SUCCEEDED(cpp->ProvideProcess(aRemoteType, openerInfo,
infos.Elements(), infos.Length(),
&index))) {
// If the provider returned an existing ContentParent, use that one.
if (0 <= index && static_cast<uint32_t>(index) <= maxContentParents) {
RefPtr<ContentParent> retval = contentParents[index];
return retval.forget();
}
} else {
// If there was a problem with the JS chooser, fall back to a random
// selection.
NS_WARNING("nsIContentProcessProvider failed to return a process");
RefPtr<ContentParent> random;
if (contentParents.Length() >= maxContentParents &&
(random = RandomSelect(contentParents, aOpener, maxContentParents))) {
return random.forget();
}
}
// Try to take the preallocated process only for the default process type.
RefPtr<ContentParent> p;
if (aRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE) &&
(p = PreallocatedProcessManager::Take())) {
// For pre-allocated process we have not set the opener yet.
p->mOpener = aOpener;
contentParents.AppendElement(p);
return p.forget();
}
}
// Try to take the preallocated process only for the default process type.
if (aRemoteType.Equals(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)) &&
(p = PreallocatedProcessManager::Take())) {
// For pre-allocated process we have not set the opener yet.
p->mOpener = aOpener;
} else {
p = new ContentParent(aOpener, aRemoteType);
// Create a new process from scratch.
RefPtr<ContentParent> p = new ContentParent(aOpener, aRemoteType);
if (!p->LaunchSubprocess(aPriority)) {
return nullptr;
}
p->Init();
if (!p->LaunchSubprocess(aPriority)) {
return nullptr;
}
p->Init();
contentParents.AppendElement(p);
return p.forget();
}
@ -1204,6 +1316,8 @@ ContentParent::Init()
RefPtr<GeckoMediaPluginServiceParent> gmps(GeckoMediaPluginServiceParent::GetSingleton());
gmps->UpdateContentProcessGMPCapabilities();
mScriptableHelper = new ScriptableCPInfo(this);
}
namespace {
@ -1274,6 +1388,11 @@ ContentParent::SetPriorityAndCheckIsAlive(ProcessPriority aPriority)
void
ContentParent::ShutDownProcess(ShutDownMethod aMethod)
{
if (mScriptableHelper) {
static_cast<ScriptableCPInfo*>(mScriptableHelper.get())->ProcessDied();
mScriptableHelper = nullptr;
}
// Shutting down by sending a shutdown message works differently than the
// other methods. We first call Shutdown() in the child. After the child is
// ready, it calls FinishShutdown() on us. Then we close the channel.

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

@ -45,6 +45,7 @@
#define LARGE_ALLOCATION_REMOTE_TYPE "webLargeAllocation"
class nsConsoleService;
class nsIContentProcessInfo;
class nsICycleCollectorLogSink;
class nsIDumpGCAndCCLogsCallback;
class nsITabParent;
@ -371,6 +372,10 @@ public:
{
return mOpener;
}
nsIContentProcessInfo* ScriptableHelper() const
{
return mScriptableHelper;
}
bool NeedsPermissionsUpdate() const
{
@ -1173,6 +1178,7 @@ private:
RefPtr<nsConsoleService> mConsoleService;
nsConsoleService* GetConsoleService();
nsCOMPtr<nsIContentProcessInfo> mScriptableHelper;
nsTArray<nsCOMPtr<nsIObserver>> mIdleListeners;