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:
@@ -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__,
|
||||
|
||||
@@ -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"];
|
||||
@@ -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'],
|
||||
|
||||
@@ -521,7 +521,6 @@ EXTRA_JS_MODULES += [
|
||||
"DOMRequestHelper.jsm",
|
||||
"IndexedDBHelper.jsm",
|
||||
"LocationHelper.jsm",
|
||||
"ProcessSelector.jsm",
|
||||
"SlowScriptDebug.jsm",
|
||||
]
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ XPIDL_SOURCES += [
|
||||
"nsIBrowserUsage.idl",
|
||||
"nsIContentPermissionPrompt.idl",
|
||||
"nsIContentPrefService2.idl",
|
||||
"nsIContentProcess.idl",
|
||||
"nsIDOMChromeWindow.idl",
|
||||
"nsIDOMGlobalPropertyInitializer.idl",
|
||||
"nsIDOMWindow.idl",
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user