зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
08388a619c
Коммит
b83ba23582
|
@ -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;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче