Bug 1728331 - Part 1: Avoid cycling between processes when navigating within a tab, r=smaug

This patch replaces the previous process selection infrastructure with a
new setup implemented entirely in C++, which should more accurately
track the set of processes in use, and should encourage re-use of the
existing content process when navigating by not counting the current
tab.

This approach intentionally allows for process switching to another
process during navigation if there is uneven load between processes to
encourage balanced process use.

I think this may also fix some of the session restore issues with many
tabs using the same process, rather than being spread over 4, as we now
track a tab earlier in its lifecycle before the BrowserParent instance
is created.

Differential Revision: https://phabricator.services.mozilla.com/D126405
This commit is contained in:
Nika Layzell
2022-03-28 16:18:04 +00:00
parent ace0a315bf
commit 169d7e48bb
12 changed files with 136 additions and 357 deletions

View File

@@ -1758,7 +1758,7 @@ void CanonicalBrowsingContext::PendingRemotenessChange::Clear() {
// When this PendingRemotenessChange was created, it was given a
// `mContentParent`.
if (mContentParent) {
mContentParent->RemoveKeepAlive();
mContentParent->RemoveKeepAlive(mTarget->BrowserId());
mContentParent = nullptr;
}
@@ -1914,7 +1914,7 @@ CanonicalBrowsingContext::ChangeRemoteness(
// Switching to local, so we don't need to create a new process, and will
// instead use our embedder process.
change->mContentParent = embedderBrowser->Manager();
change->mContentParent->AddKeepAlive();
change->mContentParent->AddKeepAlive(BrowserId());
change->ProcessLaunched();
return promise.forget();
}
@@ -1933,7 +1933,7 @@ CanonicalBrowsingContext::ChangeRemoteness(
aOptions.mReplaceBrowsingContext &&
aOptions.mRemoteType == existingProcess->GetRemoteType()) {
change->mContentParent = existingProcess;
change->mContentParent->AddKeepAlive();
change->mContentParent->AddKeepAlive(BrowserId());
change->ProcessLaunched();
return promise.forget();
}
@@ -1955,6 +1955,7 @@ CanonicalBrowsingContext::ChangeRemoteness(
change->mContentParent = ContentParent::GetNewOrUsedLaunchingBrowserProcess(
/* aRemoteType = */ aOptions.mRemoteType,
/* aGroup = */ finalGroup,
/* aBrowserId */ BrowserId(),
/* aPriority = */ hal::PROCESS_PRIORITY_FOREGROUND,
/* aPreferUsed = */ preferUsed);
if (!change->mContentParent) {
@@ -1965,7 +1966,7 @@ CanonicalBrowsingContext::ChangeRemoteness(
// Add a KeepAlive used by this ContentParent, which will be cleared when
// the change is complete. This should prevent the process dying before
// we're ready to use it.
change->mContentParent->AddKeepAlive();
change->mContentParent->AddKeepAlive(BrowserId());
if (change->mContentParent->IsLaunching()) {
change->mContentParent->WaitForLaunchAsync()->Then(
GetMainThreadSerialEventTarget(), __func__,

View File

@@ -1,60 +0,0 @@
/* 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/. */
// 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: ChromeUtils.generateQI(["nsIContentProcessProvider"]),
provideProcess(aType, aProcesses, aMaxCount) {
if (aProcesses.length < aMaxCount) {
return Ci.nsIContentProcessProvider.NEW_PROCESS;
}
return Math.floor(Math.random() * aMaxCount);
},
};
// Fills up aProcesses until max and then selects one from the available
// ones that host the least number of tabs.
function MinTabSelector() {}
MinTabSelector.prototype = {
classID: Components.ID("{2dc08eaf-6eef-4394-b1df-a3a927c1290b}"),
QueryInterface: ChromeUtils.generateQI(["nsIContentProcessProvider"]),
provideProcess(aType, aProcesses, aMaxCount) {
let min = Number.MAX_VALUE;
let candidate = Ci.nsIContentProcessProvider.NEW_PROCESS;
// The reason for not directly using aProcesses.length here is because if
// we keep processes alive for testing but want a test to use only single
// content process we can just keep relying on dom.ipc.processCount = 1
// this way.
let numIters = Math.min(aProcesses.length, aMaxCount);
for (let i = 0; i < numIters; i++) {
let process = aProcesses[i];
let tabCount = process.tabCount;
if (tabCount < min) {
min = tabCount;
candidate = i;
}
}
// If all current processes have at least one tab and we have not yet
// reached the maximum, spawn a new process.
if (min > 0 && aProcesses.length < aMaxCount) {
return Ci.nsIContentProcessProvider.NEW_PROCESS;
}
// Otherwise we use candidate.
return candidate;
},
};
var EXPORTED_SYMBOLS = ["RandomSelector", "MinTabSelector"];

View File

@@ -13,17 +13,6 @@ Classes = [
'jsm': 'resource://gre/modules/ContentAreaDropListener.jsm',
'constructor': 'ContentAreaDropListener',
},
{
'cid': '{c616fcfd-9737-41f1-aa74-cee72a38f91b}',
'jsm': 'resource://gre/modules/ProcessSelector.jsm',
'constructor': 'RandomSelector',
},
{
'cid': '{2dc08eaf-6eef-4394-b1df-a3a927c1290b}',
'contract_ids': ['@mozilla.org/ipc/processselector;1'],
'jsm': 'resource://gre/modules/ProcessSelector.jsm',
'constructor': 'MinTabSelector',
},
{
'cid': '{e740ddb4-18b4-4aac-8ae1-9b0f4320769d}',
'contract_ids': ['@mozilla.org/dom/slow-script-debug;1'],

View File

@@ -521,7 +521,6 @@ EXTRA_JS_MODULES += [
"DOMRequestHelper.jsm",
"IndexedDBHelper.jsm",
"LocationHelper.jsm",
"ProcessSelector.jsm",
"SlowScriptDebug.jsm",
]

View File

@@ -15,7 +15,6 @@ XPIDL_SOURCES += [
"nsIBrowserUsage.idl",
"nsIContentPermissionPrompt.idl",
"nsIContentPrefService2.idl",
"nsIContentProcess.idl",
"nsIDOMChromeWindow.idl",
"nsIDOMGlobalPropertyInitializer.idl",
"nsIDOMWindow.idl",

View File

@@ -1,51 +0,0 @@
/* 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 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;
/**
* Number of opened tabs living in this content process.
*/
readonly attribute int32_t tabCount;
/**
* The process manager for this ContentParent (so a process message manager
* as opposed to a frame message manager.
*/
readonly attribute nsISupports 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, 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 AUTF8String aType,
in Array<nsIContentProcessInfo> aAliveProcesses,
in uint32_t aMaxCount);
};

View File

@@ -249,6 +249,8 @@ BrowserParent::BrowserParent(ContentParent* aManager, const TabId& aTabId,
if (aBrowsingContext->Top()->IsPriorityActive()) {
ProcessPriorityManager::ActivityChanged(this, true);
}
mManager->AddKeepAlive(aBrowsingContext->BrowserId());
}
BrowserParent::~BrowserParent() = default;
@@ -638,13 +640,17 @@ void BrowserParent::Destroy() {
mIsDestroyed = true;
if (CanSend()) {
Manager()->RemoveKeepAlive(mBrowsingContext->BrowserId());
}
Manager()->NotifyTabDestroying();
// This `AddKeepAlive` will be cleared if `mMarkedDestroying` is set in
// `ActorDestroy`. Out of caution, we don't add the `KeepAlive` if our IPC
// actor has somehow already been destroyed, as that would mean `ActorDestroy`
// won't be called.
if (CanRecv()) {
if (CanSend()) {
mBrowsingContext->Group()->AddKeepAlive();
}
@@ -672,6 +678,10 @@ mozilla::ipc::IPCResult BrowserParent::RecvEnsureLayersConnected(
}
void BrowserParent::ActorDestroy(ActorDestroyReason why) {
if (!mIsDestroyed) {
Manager()->RemoveKeepAlive(mBrowsingContext->BrowserId());
}
Manager()->NotifyTabDestroyed(mTabId, mMarkedDestroying);
ContentProcessManager::GetSingleton()->UnregisterRemoteFrame(mTabId);

View File

@@ -197,7 +197,6 @@
#include "nsICaptivePortalService.h"
#include "nsICertOverrideService.h"
#include "nsIClipboard.h"
#include "nsIContentProcess.h"
#include "nsIContentSecurityPolicy.h"
#include "nsICookie.h"
#include "nsICrashService.h"
@@ -525,70 +524,6 @@ uint64_t ComputeLoadedOriginHash(nsIPrincipal* aPrincipal) {
return ((uint64_t)originNoSuffix) << 32 | originSuffix;
}
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::GetTabCount(int32_t* aTabCount) {
if (!mContentParent) {
return NS_ERROR_NOT_INITIALIZED;
}
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
*aTabCount = cpm->GetBrowserParentCountByProcessId(mContentParent->ChildID());
return NS_OK;
}
NS_IMETHODIMP
ScriptableCPInfo::GetMessageManager(nsISupports** aMessenger) {
*aMessenger = nullptr;
if (!mContentParent) {
return NS_ERROR_NOT_INITIALIZED;
}
RefPtr<ProcessMessageManager> manager = mContentParent->GetMessageManager();
manager.forget(aMessenger);
return NS_OK;
}
ProcessID GetTelemetryProcessID(const nsACString& remoteType) {
// OOP WebExtensions run in a content process.
// For Telemetry though we want to break out collected data from the
@@ -841,39 +776,6 @@ void ContentParent::ReleaseCachedProcesses() {
}
}
/*static*/
already_AddRefed<ContentParent> ContentParent::MinTabSelect(
const nsTArray<ContentParent*>& aContentParents,
int32_t aMaxContentParents) {
uint32_t maxSelectable =
std::min(static_cast<uint32_t>(aContentParents.Length()),
static_cast<uint32_t>(aMaxContentParents));
uint32_t min = INT_MAX;
RefPtr<ContentParent> candidate;
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
for (uint32_t i = 0; i < maxSelectable; i++) {
ContentParent* p = aContentParents[i];
MOZ_DIAGNOSTIC_ASSERT(!p->IsDead());
uint32_t tabCount = cpm->GetBrowserParentCountByProcessId(p->ChildID());
if (tabCount < min) {
candidate = p;
min = tabCount;
}
}
// If all current processes have at least one tab and we have not yet reached
// the maximum, use a new process.
if (min > 0 &&
aContentParents.Length() < static_cast<uint32_t>(aMaxContentParents)) {
return nullptr;
}
// Otherwise we return candidate.
return candidate.forget();
}
/* static */
already_AddRefed<nsIPrincipal>
ContentParent::CreateRemoteTypeIsolationPrincipal(
@@ -894,62 +796,81 @@ ContentParent::CreateRemoteTypeIsolationPrincipal(
return principal.forget();
}
// Determine the effective number of tabs which are loaded in a given content
// process, based on the keepalive table. We don't count `0` (as it doesn't
// correspond to a tab), and `aIgnoreBrowserId` (as it corresponds to the
// navigating tab, or is `0`) when doing the counts.
uint32_t ContentParent::EffectiveTabCount(uint64_t aIgnoreBrowserId) {
nsTHashSet<uint64_t> browserIds;
// First, collect the BrowserIds associated with each KeepAlive.
for (const auto& id : mKeepAlivesByBrowserId.Keys()) {
if (id != 0 && id != aIgnoreBrowserId) {
browserIds.Insert(aIgnoreBrowserId);
}
}
// Then, collect the BrowserIds associated with each existing BrowserParent,
// even if they're still actively being destroyed. (FIXME: Why should we be
// counting tabs as they're being unloaded?)
for (auto* bp : ManagedPBrowserParent()) {
uint64_t browserId =
BrowserParent::GetFrom(bp)->GetBrowsingContext()->BrowserId();
if (browserId != 0 && browserId != aIgnoreBrowserId) {
browserIds.Insert(browserId);
}
}
return browserIds.Count();
}
/*static*/
already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
const nsACString& aRemoteType, nsTArray<ContentParent*>& aContentParents,
uint32_t aMaxContentParents, bool aPreferUsed, ProcessPriority aPriority) {
uint32_t aMaxContentParents, uint64_t aBrowserId, bool aPreferUsed,
ProcessPriority aPriority) {
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
AutoRestore ar(sInProcessSelector);
sInProcessSelector = true;
#endif
uint32_t numberOfParents = aContentParents.Length();
nsTArray<RefPtr<nsIContentProcessInfo>> infos(numberOfParents);
for (auto* cp : aContentParents) {
infos.AppendElement(cp->mScriptableHelper);
}
if (!StaticPrefs::dom_ipc_disableContentProcessReuse() &&
!aContentParents.IsEmpty()) {
// Collect a list of eligible content processes which are tied for the
// lowest effective tab count. We only consider at most `aMaxContentParents`
// processes even if more are available, in case the process count was
// dropped by a test.
RefPtr<ContentParent> selected;
uint32_t minTabCount = UINT32_MAX;
uint32_t processCount =
std::min(aMaxContentParents, uint32_t(aContentParents.Length()));
for (uint32_t i = 0; i < processCount; ++i) {
uint32_t effectiveTabCount =
aContentParents[i]->EffectiveTabCount(aBrowserId);
if (effectiveTabCount < minTabCount) {
minTabCount = effectiveTabCount;
selected = aContentParents[i];
}
}
if (aPreferUsed && numberOfParents) {
// If we prefer re-using existing content processes, we don't want to create
// a new process, and instead re-use an existing one, so pretend the process
// limit is at the current number of processes.
aMaxContentParents = numberOfParents;
}
// If every tab has at least one tab (other than our tab, which was excluded
// from the count) already loaded in it, prefer creating a new process.
if (minTabCount > 0 && !aPreferUsed &&
aMaxContentParents > aContentParents.Length()) {
selected = nullptr;
}
nsCOMPtr<nsIContentProcessProvider> cpp =
do_GetService("@mozilla.org/ipc/processselector;1");
int32_t index;
if (cpp && NS_SUCCEEDED(cpp->ProvideProcess(aRemoteType, infos,
aMaxContentParents, &index))) {
// If the provider returned an existing ContentParent, use that one.
if (0 <= index && static_cast<uint32_t>(index) <= aMaxContentParents) {
RefPtr<ContentParent> retval = aContentParents[index];
// If we successfully selected a content process, return it.
if (selected) {
if (profiler_thread_is_being_profiled_for_markers()) {
nsPrintfCString marker("Reused process %u",
(unsigned int)retval->ChildID());
nsPrintfCString marker("Reused process %" PRIu64,
(uint64_t)selected->ChildID());
PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
}
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("GetUsedProcess: Reused process %p (%u) for %s", retval.get(),
(unsigned int)retval->ChildID(),
("GetUsedProcess: Reused process %p (%" PRIu64 ") for %s",
selected.get(), (uint64_t)selected->ChildID(),
PromiseFlatCString(aRemoteType).get()));
retval->AssertAlive();
retval->StopRecycling();
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 ((random = MinTabSelect(aContentParents, aMaxContentParents))) {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("GetUsedProcess: Reused random process %p (%d) for %s",
random.get(), (unsigned int)random->ChildID(),
PromiseFlatCString(aRemoteType).get()));
random->AssertAlive();
random->StopRecycling();
return random.forget();
selected->AssertAlive();
selected->StopRecycling();
return selected.forget();
}
}
@@ -1029,7 +950,7 @@ already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
already_AddRefed<ContentParent>
ContentParent::GetNewOrUsedLaunchingBrowserProcess(
const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
ProcessPriority aPriority, bool aPreferUsed) {
uint64_t aBrowserId, ProcessPriority aPriority, bool aPreferUsed) {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("GetNewOrUsedProcess for type %s",
PromiseFlatCString(aRemoteType).get()));
@@ -1054,8 +975,9 @@ ContentParent::GetNewOrUsedLaunchingBrowserProcess(
uint32_t maxContentParents = GetMaxProcessCount(aRemoteType);
// Let's try and reuse an existing process.
contentParent = GetUsedBrowserProcess(
aRemoteType, contentParents, maxContentParents, aPreferUsed, aPriority);
contentParent =
GetUsedBrowserProcess(aRemoteType, contentParents, maxContentParents,
aBrowserId, aPreferUsed, aPriority);
if (!contentParent) {
// No reusable process. Let's create and launch one.
@@ -1098,11 +1020,12 @@ ContentParent::GetNewOrUsedLaunchingBrowserProcess(
RefPtr<ContentParent::LaunchPromise>
ContentParent::GetNewOrUsedBrowserProcessAsync(const nsACString& aRemoteType,
BrowsingContextGroup* aGroup,
uint64_t aBrowserId,
ProcessPriority aPriority,
bool aPreferUsed) {
// Obtain a `ContentParent` launched asynchronously.
RefPtr<ContentParent> contentParent = GetNewOrUsedLaunchingBrowserProcess(
aRemoteType, aGroup, aPriority, aPreferUsed);
aRemoteType, aGroup, aBrowserId, aPriority, aPreferUsed);
if (!contentParent) {
// In case of launch error, stop here.
return LaunchPromise::CreateAndReject(LaunchError(), __func__);
@@ -1113,9 +1036,9 @@ ContentParent::GetNewOrUsedBrowserProcessAsync(const nsACString& aRemoteType,
/*static*/
already_AddRefed<ContentParent> ContentParent::GetNewOrUsedBrowserProcess(
const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
ProcessPriority aPriority, bool aPreferUsed) {
uint64_t aBrowserId, ProcessPriority aPriority, bool aPreferUsed) {
RefPtr<ContentParent> contentParent = GetNewOrUsedLaunchingBrowserProcess(
aRemoteType, aGroup, aPriority, aPreferUsed);
aRemoteType, aGroup, aBrowserId, aPriority, aPreferUsed);
if (!contentParent || !contentParent->WaitForLaunchSync(aPriority)) {
// In case of launch error, stop here.
return nullptr;
@@ -1481,7 +1404,8 @@ already_AddRefed<RemoteBrowser> ContentParent::CreateBrowser(
aContext.JSPluginId(), PROCESS_PRIORITY_FOREGROUND);
} else {
constructorSender = GetNewOrUsedBrowserProcess(
remoteType, aBrowsingContext->Group(), PROCESS_PRIORITY_FOREGROUND);
remoteType, aBrowsingContext->Group(), aBrowsingContext->BrowserId(),
PROCESS_PRIORITY_FOREGROUND);
}
if (!constructorSender) {
return nullptr;
@@ -1940,11 +1864,6 @@ void ContentParent::MarkAsDead() {
}
#endif
if (mScriptableHelper) {
static_cast<ScriptableCPInfo*>(mScriptableHelper.get())->ProcessDied();
mScriptableHelper = nullptr;
}
mLifecycleState = LifecycleState::DEAD;
}
@@ -2222,7 +2141,7 @@ bool ContentParent::ShouldKeepProcessAlive() {
return true;
}
if (mNumKeepaliveCalls > 0) {
if (!mKeepAlivesByBrowserId.IsEmpty()) {
return true;
}
@@ -2293,14 +2212,21 @@ void ContentParent::NotifyTabDestroying() {
#endif // !defined(MOZ_WIDGET_ANDROID)
}
void ContentParent::AddKeepAlive() {
void ContentParent::AddKeepAlive(uint64_t aBrowserId) {
// Something wants to keep this content process alive.
++mNumKeepaliveCalls;
auto& numKeepaliveCalls = mKeepAlivesByBrowserId.LookupOrInsert(aBrowserId);
++numKeepaliveCalls;
}
void ContentParent::RemoveKeepAlive() {
MOZ_DIAGNOSTIC_ASSERT(mNumKeepaliveCalls > 0);
--mNumKeepaliveCalls;
void ContentParent::RemoveKeepAlive(uint64_t aBrowserId) {
MOZ_DIAGNOSTIC_ASSERT(!mKeepAlivesByBrowserId.IsEmpty());
auto entry = mKeepAlivesByBrowserId.Lookup(aBrowserId);
MOZ_DIAGNOSTIC_ASSERT(entry && entry.Data() > 0);
--entry.Data();
if (entry.Data() == 0) {
entry.Remove();
}
MaybeBeginShutDown();
}
@@ -2742,7 +2668,6 @@ ContentParent::ContentParent(const nsACString& aRemoteType, int32_t aJSPluginID)
mJSPluginID(aJSPluginID),
mRemoteWorkerActorData("ContentParent::mRemoteWorkerActorData"),
mNumDestroyingTabs(0),
mNumKeepaliveCalls(0),
mLifecycleState(LifecycleState::LAUNCHING),
mIsForBrowser(!mRemoteType.IsEmpty()),
mCalledClose(false),
@@ -2784,10 +2709,6 @@ ContentParent::ContentParent(const nsACString& aRemoteType, int32_t aJSPluginID)
("CreateSubprocess: ContentParent %p mSubprocess %p handle %" PRIuPTR,
this, mSubprocess,
mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
// This is safe to do in the constructor, as it doesn't take a strong
// reference.
mScriptableHelper = new ScriptableCPInfo(this);
}
ContentParent::~ContentParent() {
@@ -2817,13 +2738,6 @@ ContentParent::~ContentParent() {
mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
mSubprocess->Destroy();
}
// Make sure to clear the connection from `mScriptableHelper` if it hasn't
// been cleared yet.
if (mScriptableHelper) {
static_cast<ScriptableCPInfo*>(mScriptableHelper.get())->ProcessDied();
mScriptableHelper = nullptr;
}
}
bool ContentParent::InitInternal(ProcessPriority aInitialPriority) {

View File

@@ -53,7 +53,6 @@
#define CHILD_PROCESS_SHUTDOWN_MESSAGE u"child-process-shutdown"_ns
class nsConsoleService;
class nsIContentProcessInfo;
class nsICycleCollectorLogSink;
class nsIDumpGCAndCCLogsCallback;
class nsIRemoteTab;
@@ -176,15 +175,6 @@ class ContentParent final
static void LogAndAssertFailedPrincipalValidationInfo(
nsIPrincipal* aPrincipal, const char* aMethod);
/**
* Picks a random content parent from |aContentParents| respecting the index
* limit set by |aMaxContentParents|.
* Returns null if non available.
*/
static already_AddRefed<ContentParent> MinTabSelect(
const nsTArray<ContentParent*>& aContentParents,
int32_t maxContentParents);
/**
* Get or create a content process for:
* 1. browser iframe
@@ -193,11 +183,13 @@ class ContentParent final
*/
static RefPtr<ContentParent::LaunchPromise> GetNewOrUsedBrowserProcessAsync(
const nsACString& aRemoteType, BrowsingContextGroup* aGroup = nullptr,
uint64_t aBrowserId = 0,
hal::ProcessPriority aPriority =
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
bool aPreferUsed = false);
static already_AddRefed<ContentParent> GetNewOrUsedBrowserProcess(
const nsACString& aRemoteType, BrowsingContextGroup* aGroup = nullptr,
uint64_t aBrowserId = 0,
hal::ProcessPriority aPriority =
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
bool aPreferUsed = false);
@@ -214,6 +206,7 @@ class ContentParent final
*/
static already_AddRefed<ContentParent> GetNewOrUsedLaunchingBrowserProcess(
const nsACString& aRemoteType, BrowsingContextGroup* aGroup = nullptr,
uint64_t aBrowserId = 0,
hal::ProcessPriority aPriority =
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
bool aPreferUsed = false);
@@ -373,9 +366,10 @@ class ContentParent final
void NotifyTabDestroyed(const TabId& aTabId, bool aNotifiedDestroying);
// Manage the set of `KeepAlive`s on this ContentParent which are preventing
// it from being destroyed.
void AddKeepAlive();
void RemoveKeepAlive();
// it from being destroyed. This is keyed by BrowserId to allow it to be used
// to assist in process selection.
void AddKeepAlive(uint64_t aBrowserId = 0);
void RemoveKeepAlive(uint64_t aBrowserId = 0);
TestShellParent* CreateTestShell();
@@ -410,8 +404,6 @@ class ContentParent final
GeckoChildProcessHost* Process() const { return mSubprocess; }
nsIContentProcessInfo* ScriptableHelper() const { return mScriptableHelper; }
mozilla::dom::ProcessMessageManager* GetMessageManager() const {
return mMessageManager;
}
@@ -1474,7 +1466,12 @@ class ContentParent final
// Return an existing ContentParent if possible. Otherwise, `nullptr`.
static already_AddRefed<ContentParent> GetUsedBrowserProcess(
const nsACString& aRemoteType, nsTArray<ContentParent*>& aContentParents,
uint32_t aMaxContentParents, bool aPreferUsed, ProcessPriority aPriority);
uint32_t aMaxContentParents, uint64_t aBrowserId, bool aPreferUsed,
ProcessPriority aPriority);
// Count the number of tabs loaded in this ContentParent. Tabs associated with
// the given BrowserId will not be counted, if passed.
uint32_t EffectiveTabCount(uint64_t aIgnoreBrowserId = 0);
void AddToPool(nsTArray<ContentParent*>&);
void RemoveFromPool(nsTArray<ContentParent*>&);
@@ -1550,7 +1547,10 @@ class ContentParent final
// NotifyTabDestroying() but not called NotifyTabDestroyed().
int32_t mNumDestroyingTabs;
uint32_t mNumKeepaliveCalls;
// The number of KeepAlive calls for this ContentParent, keyed by BrowserId.
// This is used to track the number of tabs which are actively being hosted by
// each ContentParent.
nsTHashMap<uint64_t, uint32_t> mKeepAlivesByBrowserId;
// The process starts in the LAUNCHING state, and transitions to
// ALIVE once it can accept IPC messages. It remains ALIVE only
@@ -1590,8 +1590,6 @@ class ContentParent final
uint8_t mIsInPool : 1;
nsCOMPtr<nsIContentProcessInfo> mScriptableHelper;
nsTArray<nsCOMPtr<nsIObserver>> mIdleListeners;
#ifdef MOZ_X11

View File

@@ -716,6 +716,7 @@ void RemoteWorkerManager::LaunchNewContentProcess(
ContentParent::GetNewOrUsedBrowserProcessAsync(
/* aRemoteType = */ remoteType,
/* aGroup */ nullptr,
/* aBrowserId */ 0,
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
/* aPreferUsed */ true)
->Then(GetCurrentSerialEventTarget(), __func__,

View File

@@ -2560,6 +2560,15 @@
#endif
mirror: always
# If true, disables non-required re-use of content processes. This can be used
# in tests to force a new process to be used whenever a process selection
# decision is made. Setting this pref can cause dom.ipc.processCount limits to
# be exceeded.
- name: dom.ipc.disableContentProcessReuse
type: bool
value: false
mirror: always
- name: dom.ipc.tabs.disabled
type: bool
value: false

View File

@@ -19,9 +19,6 @@ var EXPORTED_SYMBOLS = ["BrowserTestUtils"];
const { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
const { ComponentUtils } = ChromeUtils.import(
"resource://gre/modules/ComponentUtils.jsm"
);
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
@@ -43,32 +40,9 @@ XPCOMUtils.defineLazyServiceGetters(this, {
],
});
const PROCESSSELECTOR_CONTRACTID = "@mozilla.org/ipc/processselector;1";
const OUR_PROCESSSELECTOR_CID = Components.ID(
"{f9746211-3d53-4465-9aeb-ca0d96de0253}"
);
const EXISTING_JSID = Cc[PROCESSSELECTOR_CONTRACTID];
const DEFAULT_PROCESSSELECTOR_CID = EXISTING_JSID
? Components.ID(EXISTING_JSID.number)
: null;
let gListenerId = 0;
// A process selector that always asks for a new process.
function NewProcessSelector() {}
NewProcessSelector.prototype = {
classID: OUR_PROCESSSELECTOR_CID,
QueryInterface: ChromeUtils.generateQI(["nsIContentProcessProvider"]),
provideProcess() {
return Ci.nsIContentProcessProvider.NEW_PROCESS;
},
};
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
let selectorFactory = ComponentUtils._getFactory(NewProcessSelector);
registrar.registerFactory(OUR_PROCESSSELECTOR_CID, "", null, selectorFactory);
const DISABLE_CONTENT_PROCESS_REUSE_PREF = "dom.ipc.disableContentProcessReuse";
const kAboutPageRegistrationContentScript =
"chrome://mochikit/content/tests/BrowserTestUtils/content-about-page-utils.js";
@@ -226,19 +200,17 @@ var BrowserTestUtils = {
} = options;
let promises, tab;
let disableReusePrefValue = Services.prefs.getBoolPref(
DISABLE_CONTENT_PROCESS_REUSE_PREF
);
try {
// If we're asked to force a new process, replace the normal process
// selector with one that always asks for a new process.
// If DEFAULT_PROCESSSELECTOR_CID is null, we're in non-e10s mode and we
// should skip this.
if (options.forceNewProcess && DEFAULT_PROCESSSELECTOR_CID) {
if (options.forceNewProcess) {
Services.ppmm.releaseCachedProcesses();
registrar.registerFactory(
OUR_PROCESSSELECTOR_CID,
"",
PROCESSSELECTOR_CONTRACTID,
null
);
Services.prefs.setBoolPref(DISABLE_CONTENT_PROCESS_REUSE_PREF, true);
}
promises = [
@@ -263,12 +235,10 @@ var BrowserTestUtils = {
}
} finally {
// Restore the original process selector, if needed.
if (options.forceNewProcess && DEFAULT_PROCESSSELECTOR_CID) {
registrar.registerFactory(
DEFAULT_PROCESSSELECTOR_CID,
"",
PROCESSSELECTOR_CONTRACTID,
null
if (options.forceNewProcess) {
Services.prefs.setBoolPref(
DISABLE_CONTENT_PROCESS_REUSE_PREF,
disableReusePrefValue
);
}
}