Backed out 8 changesets (bug 1901851, bug 1728331) for causing bc failures in browser_docshell_type_editor.js CLOSED TREE

Backed out changeset 2cf5cad90099 (bug 1728331)
Backed out changeset d920c2d72d00 (bug 1728331)
Backed out changeset 9e5bd0186aa6 (bug 1728331)
Backed out changeset 45735575df21 (bug 1728331)
Backed out changeset fbafea1663e3 (bug 1901851)
Backed out changeset 30bdf88d3bb7 (bug 1901851)
Backed out changeset 1d994915bd71 (bug 1901851)
Backed out changeset 0b3249432b9c (bug 1901851)
This commit is contained in:
Cristian Tuns 2024-06-19 20:51:53 -04:00
parent 4c320abfc0
commit 535119812a
51 changed files with 1422 additions and 923 deletions

View File

@ -186,8 +186,8 @@ add_task(async function navigate_around() {
// This pref is only accessed in automation to speed up tests.
knownProblematicPrefs["dom.ipc.keepProcessesAlive.webIsolated.perOrigin"] =
{
min: 50,
max: 51,
min: 100,
max: 102,
};
if (AppConstants.platform == "linux") {
// The following sandbox pref is covered by

View File

@ -1738,10 +1738,10 @@ void CanonicalBrowsingContext::PendingRemotenessChange::ProcessLaunched() {
return;
}
if (mContentParentKeepAlive) {
if (mContentParent) {
// If our new content process is still unloading from a previous process
// switch, wait for that unload to complete before continuing.
auto found = mTarget->FindUnloadingHost(mContentParentKeepAlive->ChildID());
auto found = mTarget->FindUnloadingHost(mContentParent->ChildID());
if (found != mTarget->mUnloadingHosts.end()) {
found->mCallbacks.AppendElement(
[self = RefPtr{this}]() { self->ProcessReady(); });
@ -1791,8 +1791,7 @@ nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishTopContent() {
"non-remote iframes");
// Abort if our ContentParent died while process switching.
if (mContentParentKeepAlive &&
NS_WARN_IF(mContentParentKeepAlive->IsShuttingDown())) {
if (mContentParent && NS_WARN_IF(mContentParent->IsShuttingDown())) {
return NS_ERROR_FAILURE;
}
@ -1843,14 +1842,14 @@ nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishTopContent() {
// Some frontend code checks the value of the `remote` attribute on the
// browser to determine if it is remote, so update the value.
browserElement->SetAttr(kNameSpaceID_None, nsGkAtoms::remote,
mContentParentKeepAlive ? u"true"_ns : u"false"_ns,
mContentParent ? u"true"_ns : u"false"_ns,
/* notify */ true);
// The process has been created, hand off to nsFrameLoaderOwner to finish
// the process switch.
ErrorResult error;
frameLoaderOwner->ChangeRemotenessToProcess(mContentParentKeepAlive.get(),
mOptions, mSpecificGroup, error);
frameLoaderOwner->ChangeRemotenessToProcess(mContentParent, mOptions,
mSpecificGroup, error);
if (error.Failed()) {
return error.StealNSResult();
}
@ -1866,7 +1865,7 @@ nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishTopContent() {
RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
RefPtr<BrowserParent> newBrowser = frameLoader->GetBrowserParent();
if (!newBrowser) {
if (mContentParentKeepAlive) {
if (mContentParent) {
// Failed to create the BrowserParent somehow! Abort the process switch
// attempt.
return NS_ERROR_UNEXPECTED;
@ -1905,7 +1904,7 @@ nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishSubframe() {
return NS_ERROR_FAILURE;
}
if (NS_WARN_IF(!mContentParentKeepAlive)) {
if (NS_WARN_IF(!mContentParent)) {
return NS_ERROR_FAILURE;
}
@ -1921,8 +1920,8 @@ nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishSubframe() {
// If we're creating a new remote browser, and the host process is already
// dead, abort the process switch.
if (mContentParentKeepAlive != embedderBrowser->Manager() &&
NS_WARN_IF(mContentParentKeepAlive->IsShuttingDown())) {
if (mContentParent != embedderBrowser->Manager() &&
NS_WARN_IF(mContentParent->IsShuttingDown())) {
return NS_ERROR_FAILURE;
}
@ -1948,11 +1947,11 @@ nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishSubframe() {
}
// Update which process is considered the current owner
target->SetOwnerProcessId(mContentParentKeepAlive->ChildID());
target->SetOwnerProcessId(mContentParent->ChildID());
// If we're switching from remote to local, we don't need to create a
// BrowserBridge, and can instead perform the switch directly.
if (mContentParentKeepAlive == embedderBrowser->Manager()) {
if (mContentParent == embedderBrowser->Manager()) {
MOZ_DIAGNOSTIC_ASSERT(
mPendingSwitchId,
"We always have a PendingSwitchId, except for print-preview loads, "
@ -1988,9 +1987,8 @@ nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishSubframe() {
// Create and initialize our new BrowserBridgeParent.
TabId tabId(nsContentUtils::GenerateTabId());
RefPtr<BrowserBridgeParent> bridge = new BrowserBridgeParent();
nsresult rv =
bridge->InitWithProcess(embedderBrowser, mContentParentKeepAlive.get(),
windowInit, chromeFlags, tabId);
nsresult rv = bridge->InitWithProcess(embedderBrowser, mContentParent,
windowInit, chromeFlags, tabId);
if (NS_WARN_IF(NS_FAILED(rv))) {
// If we've already destroyed our previous document, make a best-effort
// attempt to recover from this failure and show the crashed tab UI. We only
@ -2055,8 +2053,11 @@ void CanonicalBrowsingContext::PendingRemotenessChange::Clear() {
}
// When this PendingRemotenessChange was created, it was given a
// `mContentParentKeepAlive`.
mContentParentKeepAlive = nullptr;
// `mContentParent`.
if (mContentParent) {
mContentParent->RemoveKeepAlive();
mContentParent = nullptr;
}
// If we were given a specific group, stop keeping that group alive manually.
if (mSpecificGroup) {
@ -2077,9 +2078,8 @@ CanonicalBrowsingContext::PendingRemotenessChange::PendingRemotenessChange(
mOptions(aOptions) {}
CanonicalBrowsingContext::PendingRemotenessChange::~PendingRemotenessChange() {
MOZ_ASSERT(
!mPromise && !mTarget && !mContentParentKeepAlive && !mSpecificGroup,
"should've already been Cancel() or Complete()-ed");
MOZ_ASSERT(!mPromise && !mTarget && !mContentParent && !mSpecificGroup,
"should've already been Cancel() or Complete()-ed");
}
BrowserParent* CanonicalBrowsingContext::GetBrowserParent() const {
@ -2226,8 +2226,8 @@ CanonicalBrowsingContext::ChangeRemoteness(
// Switching to local, so we don't need to create a new process, and will
// instead use our embedder process.
change->mContentParentKeepAlive =
embedderBrowser->Manager()->AddKeepAlive(BrowserId());
change->mContentParent = embedderBrowser->Manager();
change->mContentParent->AddKeepAlive();
change->ProcessLaunched();
return promise.forget();
}
@ -2245,8 +2245,8 @@ CanonicalBrowsingContext::ChangeRemoteness(
if (existingProcess && !existingProcess->IsShuttingDown() &&
aOptions.mReplaceBrowsingContext &&
aOptions.mRemoteType == existingProcess->GetRemoteType()) {
change->mContentParentKeepAlive =
existingProcess->AddKeepAlive(BrowserId());
change->mContentParent = existingProcess;
change->mContentParent->AddKeepAlive();
change->ProcessLaunched();
return promise.forget();
}
@ -2265,28 +2265,25 @@ CanonicalBrowsingContext::ChangeRemoteness(
bool preferUsed =
StaticPrefs::browser_tabs_remote_subframesPreferUsed() && !IsTop();
change->mContentParentKeepAlive =
ContentParent::GetNewOrUsedLaunchingBrowserProcess(
/* aRemoteType = */ aOptions.mRemoteType,
/* aGroup = */ finalGroup,
/* aPriority = */ hal::PROCESS_PRIORITY_FOREGROUND,
/* aPreferUsed = */ preferUsed,
/* aBrowserId */ BrowserId());
if (!change->mContentParentKeepAlive) {
change->mContentParent = ContentParent::GetNewOrUsedLaunchingBrowserProcess(
/* aRemoteType = */ aOptions.mRemoteType,
/* aGroup = */ finalGroup,
/* aPriority = */ hal::PROCESS_PRIORITY_FOREGROUND,
/* aPreferUsed = */ preferUsed);
if (!change->mContentParent) {
change->Cancel(NS_ERROR_FAILURE);
return promise.forget();
}
if (change->mContentParentKeepAlive->IsLaunching()) {
change->mContentParentKeepAlive
->WaitForLaunchAsync(/* aPriority */ hal::PROCESS_PRIORITY_FOREGROUND,
/* aBrowserId */ BrowserId())
->Then(
GetMainThreadSerialEventTarget(), __func__,
[change](UniqueContentParentKeepAlive) {
change->ProcessLaunched();
},
[change]() { change->Cancel(NS_ERROR_FAILURE); });
// 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();
if (change->mContentParent->IsLaunching()) {
change->mContentParent->WaitForLaunchAsync()->Then(
GetMainThreadSerialEventTarget(), __func__,
[change](ContentParent*) { change->ProcessLaunched(); },
[change]() { change->Cancel(NS_ERROR_FAILURE); });
} else {
change->ProcessLaunched();
}

View File

@ -15,7 +15,6 @@
#include "mozilla/dom/SessionHistoryEntry.h"
#include "mozilla/dom/SessionStoreRestoreData.h"
#include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/UniqueContentParentKeepAlive.h"
#include "mozilla/dom/ipc/IdType.h"
#include "mozilla/RefPtr.h"
#include "mozilla/MozPromise.h"
@ -459,7 +458,7 @@ class CanonicalBrowsingContext final : public BrowsingContext {
RefPtr<CanonicalBrowsingContext> mTarget;
RefPtr<RemotenessPromise::Private> mPromise;
UniqueContentParentKeepAlive mContentParentKeepAlive;
RefPtr<ContentParent> mContentParent;
RefPtr<BrowsingContextGroup> mSpecificGroup;
bool mProcessReady = false;

View File

@ -0,0 +1,58 @@
/* 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.
export 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.
export 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;
},
};

View File

@ -557,6 +557,7 @@ EXTRA_JS_MODULES += [
"ContentAreaDropListener.sys.mjs",
"IndexedDBHelper.sys.mjs",
"LocationHelper.sys.mjs",
"ProcessSelector.sys.mjs",
"SlowScriptDebug.sys.mjs",
]

View File

@ -271,7 +271,7 @@ void FetchParent::OnResponseAvailableInternal(
}
Unused << SendOnResponseAvailableInternal(
aResponse->ToParentToChildInternalResponse());
aResponse->ToParentToChildInternalResponse(WrapNotNull(Manager())));
}
void FetchParent::OnResponseEnd(const ResponseEndArgs& aArgs) {

View File

@ -56,6 +56,7 @@ ParentToParentStream ToParentToParentStream(
ParentToChildStream ToParentToChildStream(
const NotNull<nsCOMPtr<nsIInputStream>>& aStream, int64_t aStreamSize,
NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent,
bool aSerializeAsLazy) {
MOZ_ASSERT(XRE_IsParentProcess());
@ -72,9 +73,11 @@ ParentToChildStream ToParentToChildStream(
return result;
}
ParentToChildStream ToParentToChildStream(const ParentToParentStream& aStream,
int64_t aStreamSize) {
return ToParentToChildStream(ToInputStream(aStream), aStreamSize);
ParentToChildStream ToParentToChildStream(
const ParentToParentStream& aStream, int64_t aStreamSize,
NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent) {
return ToParentToChildStream(ToInputStream(aStream), aStreamSize,
aBackgroundParent);
}
} // namespace mozilla::dom

View File

@ -40,12 +40,14 @@ ParentToParentStream ToParentToParentStream(
// process. Can only be called in the parent process.
ParentToChildStream ToParentToChildStream(
const NotNull<nsCOMPtr<nsIInputStream>>& aStream, int64_t aStreamSize,
NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent,
bool aSerializeAsLazy = true);
// Convert a ParentToParentStream to a ParentToChildStream. Can only be called
// in the parent process.
ParentToChildStream ToParentToChildStream(const ParentToParentStream& aStream,
int64_t aStreamSize);
ParentToChildStream ToParentToChildStream(
const ParentToParentStream& aStream, int64_t aStreamSize,
NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent);
} // namespace dom

View File

@ -197,8 +197,8 @@ InternalResponse::ToParentToParentInternalResponse() {
return result;
}
ParentToChildInternalResponse
InternalResponse::ToParentToChildInternalResponse() {
ParentToChildInternalResponse InternalResponse::ToParentToChildInternalResponse(
NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent) {
ParentToChildInternalResponse result(GetMetadata(), Nothing(),
UNKNOWN_BODY_SIZE, Nothing());
@ -207,8 +207,8 @@ InternalResponse::ToParentToChildInternalResponse() {
GetUnfilteredBody(getter_AddRefs(body), &bodySize);
if (body) {
ParentToChildStream bodyStream =
ToParentToChildStream(WrapNotNull(body), bodySize, mSerializeAsLazy);
ParentToChildStream bodyStream = ToParentToChildStream(
WrapNotNull(body), bodySize, aBackgroundParent, mSerializeAsLazy);
// The body stream can fail to serialize as an IPCStream. In the case, the
// IPCStream's type would be T__None. Don't set up IPCInternalResponse's
// body with the failed IPCStream.
@ -221,8 +221,9 @@ InternalResponse::ToParentToChildInternalResponse() {
nsCOMPtr<nsIInputStream> alternativeBody = TakeAlternativeBody();
if (alternativeBody) {
ParentToChildStream alterBodyStream = ToParentToChildStream(
WrapNotNull(alternativeBody), UNKNOWN_BODY_SIZE, mSerializeAsLazy);
ParentToChildStream alterBodyStream =
ToParentToChildStream(WrapNotNull(alternativeBody), UNKNOWN_BODY_SIZE,
aBackgroundParent, mSerializeAsLazy);
// The body stream can fail to serialize as an IPCStream. In the case, the
// IPCStream's type would be T__None. Don't set up IPCInternalResponse's
// body with the failed IPCStream.
@ -432,18 +433,19 @@ SafeRefPtr<InternalResponse> InternalResponse::CreateIncompleteCopy() {
}
ParentToChildInternalResponse ToParentToChild(
const ParentToParentInternalResponse& aResponse) {
const ParentToParentInternalResponse& aResponse,
NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent) {
ParentToChildInternalResponse result(aResponse.metadata(), Nothing(),
aResponse.bodySize(), Nothing());
if (aResponse.body().isSome()) {
result.body() = Some(
ToParentToChildStream(aResponse.body().ref(), aResponse.bodySize()));
result.body() = Some(ToParentToChildStream(
aResponse.body().ref(), aResponse.bodySize(), aBackgroundParent));
}
if (aResponse.alternativeBody().isSome()) {
result.alternativeBody() =
Some(ToParentToChildStream(aResponse.alternativeBody().ref(),
InternalResponse::UNKNOWN_BODY_SIZE));
result.alternativeBody() = Some(ToParentToChildStream(
aResponse.alternativeBody().ref(), InternalResponse::UNKNOWN_BODY_SIZE,
aBackgroundParent));
}
return result;

View File

@ -57,7 +57,8 @@ class InternalResponse final : public AtomicSafeRefCounted<InternalResponse> {
ParentToParentInternalResponse ToParentToParentInternalResponse();
ParentToChildInternalResponse ToParentToChildInternalResponse();
ParentToChildInternalResponse ToParentToChildInternalResponse(
NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent);
enum CloneType {
eCloneInputStream,
@ -409,7 +410,8 @@ class InternalResponse final : public AtomicSafeRefCounted<InternalResponse> {
};
ParentToChildInternalResponse ToParentToChild(
const ParentToParentInternalResponse& aResponse);
const ParentToParentInternalResponse& aResponse,
NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent);
} // namespace dom
} // namespace mozilla

View File

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

View File

@ -0,0 +1,51 @@
/* 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

@ -29,7 +29,6 @@
#include "mozilla/dom/Event.h"
#include "mozilla/dom/indexedDB/ActorsParent.h"
#include "mozilla/dom/PaymentRequestParent.h"
#include "mozilla/dom/PContentPermissionRequestParent.h"
#include "mozilla/dom/PointerEventHandler.h"
#include "mozilla/dom/BrowserBridgeParent.h"
#include "mozilla/dom/RemoteDragStartData.h"
@ -65,7 +64,6 @@
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
#include "nsCOMPtr.h"
#include "nsContentPermissionHelper.h"
#include "nsContentUtils.h"
#include "nsDebug.h"
#include "nsFocusManager.h"
@ -330,10 +328,6 @@ BrowserParent::BrowserParent(ContentParent* aManager, const TabId& aTabId,
// so need to initialize it before IPC does.
SetManager(aManager);
// Add a KeepAlive for this BrowserParent upon creation.
mContentParentKeepAlive =
aManager->TryAddKeepAlive(aBrowsingContext->BrowserId());
RequestingAccessKeyEventData::OnBrowserParentCreated();
// When the input event queue is disabled, we don't need to handle the case
@ -742,28 +736,25 @@ void BrowserParent::Destroy() {
}
#endif
// If this fails, it's most likely due to a content-process crash, and
// auto-cleanup will kick in. Otherwise, the child side will destroy itself
// and send back __delete__().
(void)SendDestroy();
mIsDestroyed = true;
{
// The following sequence assumes that the keepalive state does not change
// between the calls, but our ThreadsafeHandle might be accessed from other
// threads in the meantime.
RecursiveMutexAutoLock lock(Manager()->ThreadsafeHandleMutex());
#if !defined(MOZ_WIDGET_ANDROID)
// We're beginning to destroy this BrowserParent. Immediately drop the
// keepalive. This can start the shutdown timer, however the ShutDown message
// will wait for the BrowserParent to be fully destroyed.
//
// NOTE: We intentionally skip this step on Android, keeping the KeepAlive
// active until the BrowserParent is fully destroyed:
// 1. Android has a fixed upper bound on the number of content processes, so
// we prefer to re-use them whenever possible (as opposed to letting an
// old process wind down while we launch a new one). This restriction will
// be relaxed after bug 1565196.
// 2. GeckoView always hard-kills content processes (and if it does not,
// Android itself will), so we don't concern ourselves with the ForceKill
// timer either.
mContentParentKeepAlive = nullptr;
#endif
// If we are shutting down everything or we know to be the last
// BrowserParent, signal the impending shutdown early to the content process
// to avoid to run the SendDestroy before we know we are ExpectingShutdown.
Manager()->NotifyTabWillDestroy();
// If this fails, it's most likely due to a content-process crash, and
// auto-cleanup will kick in. Otherwise, the child side will destroy itself
// and send back __delete__().
(void)SendDestroy();
mIsDestroyed = true;
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
@ -797,20 +788,7 @@ mozilla::ipc::IPCResult BrowserParent::RecvEnsureLayersConnected(
}
void BrowserParent::ActorDestroy(ActorDestroyReason why) {
// Need to close undeleted ContentPermissionRequestParents before tab is
// closed.
// FIXME: Why is PContentPermissionRequest not managed by PBrowser?
nsTArray<PContentPermissionRequestParent*> parentArray =
nsContentPermissionUtils::GetContentPermissionRequestParentById(mTabId);
for (auto& permissionRequestParent : parentArray) {
Unused << PContentPermissionRequestParent::Send__delete__(
permissionRequestParent);
}
// Ensure the ContentParentKeepAlive has been cleared when the actor is
// destroyed, and re-check if it's time to send the ShutDown message.
mContentParentKeepAlive = nullptr;
Manager()->MaybeBeginShutDown();
Manager()->NotifyTabDestroyed(mTabId, mMarkedDestroying);
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
if (cpm) {

View File

@ -19,7 +19,6 @@
#include "mozilla/dom/BrowserBridgeParent.h"
#include "mozilla/dom/PBrowserParent.h"
#include "mozilla/dom/TabContext.h"
#include "mozilla/dom/UniqueContentParentKeepAlive.h"
#include "mozilla/dom/VsyncParent.h"
#include "mozilla/dom/ipc/IdType.h"
#include "mozilla/layout/RemoteLayerTreeOwner.h"
@ -873,12 +872,6 @@ class BrowserParent final : public PBrowserParent,
// non-null.
BrowserHost* mBrowserHost;
// KeepAlive for the containing process.
// NOTE: While this is a strong reference to ContentParent, which is
// cycle-collected, it is cleared as the BrowserParent's IPC connection is
// destroyed, so does not need to be cycle-collected.
UniqueContentParentKeepAlive mContentParentKeepAlive;
ContentCacheInParent mContentCache;
layout::RemoteLayerTreeOwner mRemoteLayerTreeOwner;

View File

@ -2711,6 +2711,13 @@ mozilla::ipc::IPCResult ContentChild::RecvRemoteType(
CrashReporter::RecordAnnotationNSCString(
CrashReporter::Annotation::RemoteType, remoteTypePrefix);
// Defer RemoteWorkerService initialization until the child process does
// receive its specific remoteType and can become actionable for the
// RemoteWorkerManager in the parent process.
if (mRemoteType != PREALLOC_REMOTE_TYPE) {
RemoteWorkerService::Initialize();
}
return IPC_OK();
}
@ -2727,12 +2734,6 @@ void ContentChild::PreallocInit() {
// for telemetry.
const nsACString& ContentChild::GetRemoteType() const { return mRemoteType; }
mozilla::ipc::IPCResult ContentChild::RecvInitRemoteWorkerService(
Endpoint<PRemoteWorkerServiceChild>&& aEndpoint) {
RemoteWorkerService::InitializeChild(std::move(aEndpoint));
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvInitBlobURLs(
nsTArray<BlobURLRegistrationData>&& aRegistrations) {
for (uint32_t i = 0; i < aRegistrations.Length(); ++i) {

View File

@ -365,9 +365,6 @@ class ContentChild final : public PContentChild,
// this for telemetry.
const nsACString& GetRemoteType() const override;
mozilla::ipc::IPCResult RecvInitRemoteWorkerService(
Endpoint<PRemoteWorkerServiceChild>&& aEndpoint);
mozilla::ipc::IPCResult RecvInitBlobURLs(
nsTArray<BlobURLRegistrationData>&& aRegistations);

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,6 @@
#include "mozilla/dom/RemoteType.h"
#include "mozilla/dom/JSProcessActorParent.h"
#include "mozilla/dom/ProcessActor.h"
#include "mozilla/dom/UniqueContentParentKeepAlive.h"
#include "mozilla/dom/UserActivation.h"
#include "mozilla/gfx/gfxVarReceiver.h"
#include "mozilla/gfx/GPUProcessListener.h"
@ -52,6 +51,7 @@
#include "nsIReferrerInfo.h"
class nsConsoleService;
class nsIContentProcessInfo;
class nsICycleCollectorLogSink;
class nsIDumpGCAndCCLogsCallback;
class nsIRemoteTab;
@ -95,7 +95,6 @@ class TabContext;
class GetFilesHelper;
class MemoryReportRequestHost;
class RemoteWorkerManager;
class RemoteWorkerServiceParent;
class ThreadsafeContentParentHandle;
struct CancelContentJSOptions;
@ -129,11 +128,10 @@ class ContentParent final : public PContentParent,
friend class mozilla::PreallocatedProcessManagerImpl;
friend class PContentParent;
friend class mozilla::dom::RemoteWorkerManager;
friend struct mozilla::dom::ContentParentKeepAliveDeleter;
public:
using LaunchPromise =
mozilla::MozPromise<UniqueContentParentKeepAlive, nsresult, true>;
mozilla::MozPromise<RefPtr<ContentParent>, nsresult, false>;
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CONTENTPARENT_IID)
@ -146,7 +144,7 @@ class ContentParent final : public PContentParent,
/**
* Create a ContentParent suitable for use later as a content process.
*/
static UniqueContentParentKeepAlive MakePreallocProcess();
static already_AddRefed<ContentParent> MakePreallocProcess();
/**
* Start up the content-process machinery. This might include
@ -170,81 +168,49 @@ class ContentParent final : public PContentParent,
/**
* Picks a random content parent from |aContentParents| respecting the index
* limit set by |aMaxContentParents|. If |aBrowserId| is non-zero, that tab
* will be ignored when counting tabs in this process.
* limit set by |aMaxContentParents|.
* Returns null if non available.
*/
static already_AddRefed<ContentParent> MinTabSelect(
const nsTArray<ContentParent*>& aContentParents,
int32_t maxContentParents, uint64_t aBrowserId);
int32_t maxContentParents);
/**
* Get or create a content process which can be used for hosting web content
* or workers.
*
* This method returns a |UniqueContentParentKeepAlive|, which manages the
* lifecycle of the process. See the documentation on |AddKeepAlive| for more
* information about managing content process lifecycles.
*
* The returned ContentParent which may still be in the process of launching.
* Use the |WaitForLaunchAsync| or |WaitForLaunchSync| methods to wait for
* this process to finish launching.
*
* @param aRemoteType Required remote type for new & used processes.
* @param aGroup If specified, the |BrowsingContextGroup| requesting process
* selection. Used to ensure that only a single process per
* remoteType is used for each |BrowsingContextGroup|.
* @param aPriority Initial process priority for a new content process.
* @param aPreferUsed If true, process selection will prefer re-using an
* existing ContentProcess over launching a new one.
* @param aBrowserId The |BrowserId| requesting process selection. This
* information is used to reduce unnecessary process churn
* when navigating (see |MinTabSelect|).
* The returned KeepAlive will be for this BrowserId.
*/
static UniqueContentParentKeepAlive GetNewOrUsedLaunchingBrowserProcess(
const nsACString& aRemoteType, BrowsingContextGroup* aGroup = nullptr,
hal::ProcessPriority aPriority =
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
bool aPreferUsed = false, uint64_t aBrowserId = 0);
/**
* Like |GetNewOrUsedLaunchingBrowserProcess|, but returns a promise which
* resolves when the process is finished launching.
* Get or create a content process for:
* 1. browser iframe
* 2. remote xul <browser>
* 3. normal iframe
*/
static RefPtr<ContentParent::LaunchPromise> GetNewOrUsedBrowserProcessAsync(
const nsACString& aRemoteType, BrowsingContextGroup* aGroup = nullptr,
hal::ProcessPriority aPriority =
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
bool aPreferUsed = false, uint64_t aBrowserId = 0);
/**
* Like |GetNewOrUsedLaunchingBrowserProcess|, but blocks the main thread
* until the process process is finished launching before returning.
*/
static UniqueContentParentKeepAlive GetNewOrUsedBrowserProcess(
bool aPreferUsed = false);
static already_AddRefed<ContentParent> GetNewOrUsedBrowserProcess(
const nsACString& aRemoteType, BrowsingContextGroup* aGroup = nullptr,
hal::ProcessPriority aPriority =
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
bool aPreferUsed = false, uint64_t aBrowserId = 0);
bool aPreferUsed = false);
/**
* Asynchronously wait for this content process to finish launching, such that
* the ContentParent actor is ready for IPC.
* Get or create a content process, but without waiting for the process
* launch to have completed. The returned `ContentParent` may still be in the
* "Launching" state.
*
* @param aPriority The initial priority for the process after launching.
* @param aBrowserId The BrowserId to hold a KeepAlive for during the async
* launch which will be used to resolve the LaunchPromise.
* Can return `nullptr` in the case of an error.
*
* Use the `WaitForLaunchAsync` or `WaitForLaunchSync` methods to wait for
* the process to be fully launched.
*/
RefPtr<ContentParent::LaunchPromise> WaitForLaunchAsync(
static already_AddRefed<ContentParent> GetNewOrUsedLaunchingBrowserProcess(
const nsACString& aRemoteType, BrowsingContextGroup* aGroup = nullptr,
hal::ProcessPriority aPriority =
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
uint64_t aBrowserId = 0);
bool aPreferUsed = false);
/**
* Like `WaitForLaunchAsync`, but synchronously blocks the main thread until
* the content process has finished launching.
*/
RefPtr<ContentParent::LaunchPromise> WaitForLaunchAsync(
hal::ProcessPriority aPriority =
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND);
bool WaitForLaunchSync(hal::ProcessPriority aPriority =
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND);
@ -364,38 +330,21 @@ class ContentParent final : public PContentParent,
virtual nsresult DoSendAsyncMessage(const nsAString& aMessage,
StructuredCloneData& aData) override;
/*
* Attempt to add a KeepAlive for the given BrowserId. A KeepAlive will try to
* keep the process alive, though it may still die (e.g. due to a crash,
* explicit shutdown request, or similar).
*
* The returned `UniqueContentParentKeepAlive` will clear this KeepAlive when
* destroyed, and acts as a strong pointer to this `ContentParent`.
*
* Returns nullptr if the process is already being shut down.
*/
[[nodiscard]] UniqueContentParentKeepAlive TryAddKeepAlive(
uint64_t aBrowserId);
RecursiveMutex& ThreadsafeHandleMutex();
/*
* Like `TryAddKeepAlive`, but never returns `nullptr`, instead asserting if
* the ContentParent is already shutting down.
*/
[[nodiscard]] UniqueContentParentKeepAlive AddKeepAlive(uint64_t aBrowserId);
/** Notify that a tab is about to send Destroy to its child. */
void NotifyTabWillDestroy();
/**
* Check if this process is ready to be shut down, and if it is, begin the
* shutdown process. Automatically called whenever a KeepAlive is removed, or
* a BrowserParent is removed.
*
* Returns `true` if shutdown for the process has been started, and `false`
* otherwise.
*
* @param aIgnoreKeepAlivePref If true, the dom.ipc.keepProcessesAlive.*
* preferences will be ignored, for clean-up of
* cached processes.
*/
bool MaybeBeginShutDown(bool aIgnoreKeepAlivePref = false);
/** Notify that a tab is beginning its destruction sequence. */
void NotifyTabDestroying();
/** Notify that a tab was destroyed during normal operation. */
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();
TestShellParent* CreateTestShell();
@ -403,6 +352,13 @@ class ContentParent final : public PContentParent,
TestShellParent* GetTestShellSingleton();
// This method can be called on any thread.
void RegisterRemoteWorkerActor();
// This method _must_ be called on main-thread because it can start the
// shutting down of the content process.
void UnregisterRemoveWorkerActor();
void ReportChildAlreadyBlocked();
bool RequestRunToCompletion();
@ -426,6 +382,8 @@ class ContentParent final : public PContentParent,
GeckoChildProcessHost* Process() const { return mSubprocess; }
nsIContentProcessInfo* ScriptableHelper() const { return mScriptableHelper; }
mozilla::dom::ProcessMessageManager* GetMessageManager() const {
return mMessageManager;
}
@ -727,6 +685,16 @@ class ContentParent final : public PContentParent,
sBrowserContentParents;
static mozilla::StaticAutoPtr<LinkedList<ContentParent>> sContentParents;
/**
* In order to avoid rapidly creating and destroying content processes when
* running under e10s, we may keep alive a single unused "web" content
* process if it previously had a very short lifetime.
*
* This process will be re-used during process selection, avoiding spawning a
* new process, if the "web" remote type is being requested.
*/
static StaticRefPtr<ContentParent> sRecycledE10SProcess;
void AddShutdownBlockers();
void RemoveShutdownBlockers();
@ -753,6 +721,19 @@ class ContentParent final : public PContentParent,
explicit ContentParent(const nsACString& aRemoteType);
// Launch the subprocess and associated initialization.
// Returns false if the process fails to start.
// Deprecated in favor of LaunchSubprocessAsync.
bool LaunchSubprocessSync(hal::ProcessPriority aInitialPriority);
// Launch the subprocess and associated initialization;
// returns a promise and signals failure by rejecting.
// OS-level launching work is dispatched to another thread, but some
// initialization (creating IPDL actors, etc.; see Init()) is run on
// the main thread.
RefPtr<LaunchPromise> LaunchSubprocessAsync(
hal::ProcessPriority aInitialPriority);
// Common implementation of LaunchSubprocess{Sync,Async}.
// Return `true` in case of success, `false` if launch was
// aborted because of shutdown.
@ -776,12 +757,41 @@ class ContentParent final : public PContentParent,
// called after the process has been transformed to browser.
void ForwardKnownInfo();
/**
* We might want to reuse barely used content processes if certain criteria
* are met.
*
* With Fission this is a no-op.
*/
bool TryToRecycleE10SOnly();
/**
* If this process is currently being recycled, unmark it as the recycled
* content process.
* If `aForeground` is true, will also restore the process' foreground
* priority if it was previously the recycled content process.
*
* With Fission this is a no-op.
*/
void StopRecyclingE10SOnly(bool aForeground);
/**
* Removing it from the static array so it won't be returned for new tabs in
* GetNewOrUsedBrowserProcess.
*/
void RemoveFromList();
/**
* Return if the process has an active worker.
*/
bool HasActiveWorker();
/**
* Decide whether the process should be kept alive even when it would normally
* be shut down, for example when all its tabs are closed.
*/
bool ShouldKeepProcessAlive();
/**
* Mark this ContentParent as dead for the purposes of Get*().
* This method is idempotent.
@ -795,6 +805,22 @@ class ContentParent final : public PContentParent,
*/
void SignalImpendingShutdownToContentJS();
bool CheckTabDestroyWillKeepAlive(uint32_t aExpectedBrowserCount);
/**
* Check if this process is ready to be shut down, and if it is, begin the
* shutdown process. Should be called whenever a change occurs which could
* cause the decisions made by `ShouldKeepProcessAlive` to change.
*
* @param aExpectedBrowserCount The number of PBrowser actors which should
* not block shutdown. This should usually be 0.
* @param aSendShutDown If true, will send the shutdown message in addition
* to marking the process as dead and starting the force
* kill timer.
*/
void MaybeBeginShutDown(uint32_t aExpectedBrowserCount = 0,
bool aSendShutDown = true);
/**
* How we will shut down this ContentParent and its subprocess.
*/
@ -1410,27 +1436,18 @@ class ContentParent final : public PContentParent,
void GetIPCTransferableData(nsIDragSession* aSession, BrowserParent* aParent,
nsTArray<IPCTransferableData>& aIPCTransferables);
RemoteWorkerServiceParent* GetRemoteWorkerServiceParent() const {
return mRemoteWorkerServiceActor;
}
private:
// Return an existing ContentParent if possible. Otherwise, `nullptr`.
static UniqueContentParentKeepAlive GetUsedBrowserProcess(
static already_AddRefed<ContentParent> GetUsedBrowserProcess(
const nsACString& aRemoteType, nsTArray<ContentParent*>& aContentParents,
uint32_t aMaxContentParents, bool aPreferUsed, ProcessPriority aPriority,
uint64_t aBrowserId);
uint32_t aMaxContentParents, bool aPreferUsed, ProcessPriority aPriority);
void AddToPool(nsTArray<ContentParent*>&);
void RemoveFromPool(nsTArray<ContentParent*>&);
void AssertNotInPool();
void RemoveKeepAlive(uint64_t aBrowserId);
void AssertAlive();
void StartRemoteWorkerService();
private:
// If you add strong pointers to cycle collected objects here, be sure to
// release these objects in ShutDownProcess. See the comment there for more
@ -1467,6 +1484,13 @@ class ContentParent final : public PContentParent,
// they're attached to.
const RefPtr<ThreadsafeContentParentHandle> mThreadsafeHandle;
// How many tabs we're waiting to finish their destruction
// sequence. Precisely, how many BrowserParents have called
// NotifyTabDestroying() but not called NotifyTabDestroyed().
int32_t mNumDestroyingTabs;
uint32_t mNumKeepaliveCalls;
// The process starts in the LAUNCHING state, and transitions to
// ALIVE once it can accept IPC messages. It remains ALIVE only
// while remote content is being actively used from this process.
@ -1509,9 +1533,12 @@ class ContentParent final : public PContentParent,
uint8_t mGMPCreated : 1;
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
bool mNotifiedImpendingShutdownOnTabWillDestroy = false;
bool mBlockShutdownCalled;
#endif
nsCOMPtr<nsIContentProcessInfo> mScriptableHelper;
nsTArray<nsCOMPtr<nsIObserver>> mIdleListeners;
#ifdef MOZ_X11
@ -1522,8 +1549,6 @@ class ContentParent final : public PContentParent,
RefPtr<PProcessHangMonitorParent> mHangMonitorActor;
RefPtr<RemoteWorkerServiceParent> mRemoteWorkerServiceActor;
UniquePtr<gfx::DriverCrashGuard> mDriverCrashGuard;
UniquePtr<MemoryReportRequestHost> mMemoryReportRequest;
@ -1614,15 +1639,20 @@ class ThreadsafeContentParentHandle final {
return do_AddRef(mWeakActor);
}
// Attempt to add a KeepAlive for the given BrowserId. A KeepAlive will try to
// keep the process alive, though it may still die (e.g. due to a crash,
// explicit shutdown request, or similar).
// Calls `aCallback` with the current remote worker count and whether or not
// shutdown has been started. If the callback returns `true`, registers a new
// actor, and returns `true`, otherwise returns `false`.
//
// Returns nullptr if the process is already being shut down.
//
// May be called from any thread.
[[nodiscard]] UniqueThreadsafeContentParentKeepAlive TryAddKeepAlive(
uint64_t aBrowserId = 0) MOZ_EXCLUDES(mMutex);
// NOTE: The internal mutex is held while evaluating `aCallback`.
bool MaybeRegisterRemoteWorkerActor(
MoveOnlyFunction<bool(uint32_t, bool)> aCallback) MOZ_EXCLUDES(mMutex);
// Like `MaybeRegisterRemoteWorkerActor`, but unconditional.
void RegisterRemoteWorkerActor() MOZ_EXCLUDES(mMutex) {
MaybeRegisterRemoteWorkerActor([](uint32_t, bool) { return true; });
}
RecursiveMutex& Mutex() { return mMutex; }
private:
ThreadsafeContentParentHandle(ContentParent* aActor, ContentParentId aChildID,
@ -1635,17 +1665,7 @@ class ThreadsafeContentParentHandle final {
const ContentParentId mChildID;
nsCString mRemoteType MOZ_GUARDED_BY(mMutex);
// Keepalives for this browser, keyed by BrowserId. A BrowserId of `0` is used
// for non-tab code keeping the process alive (such as for workers).
// Each KeepAlive increments the corresponding BrowserId's counter, and the
// process will begin shutdown when the last KeepAlive is removed.
// FIXME: These sets are probably quite small, so it might make sense to avoid
// hashtable storage.
nsTHashMap<uint64_t, uint32_t> mKeepAlivesPerBrowserId MOZ_GUARDED_BY(mMutex);
// If set, the browser is shutting down, and new workers or tabs should not be
// created in this process.
uint32_t mRemoteWorkerActorCount MOZ_GUARDED_BY(mMutex) = 0;
bool mShutdownStarted MOZ_GUARDED_BY(mMutex) = false;
// Weak reference to the actual ContentParent actor. Only touched on the main

View File

@ -40,7 +40,6 @@ include protocol PURLClassifier;
include protocol PURLClassifierLocal;
include protocol PVRManager;
include protocol PRemoteDecoderManager;
include protocol PRemoteWorkerService;
include protocol PProfiler;
include protocol PScriptCache;
include protocol PSessionStorageObserver;
@ -772,11 +771,6 @@ child:
*/
async RemoteType(nsCString aRemoteType, nsCString aProfile);
/**
* Initialize the RemoteWorkerService thread in the content process.
*/
async InitRemoteWorkerService(Endpoint<PRemoteWorkerServiceChild> aEndpoint);
/**
* Send BlobURLRegistrationData to child process.
*/

View File

@ -41,7 +41,7 @@ class PreallocatedProcessManagerImpl final : public nsIObserver {
// See comments on PreallocatedProcessManager for these methods.
void AddBlocker(ContentParent* aParent);
void RemoveBlocker(ContentParent* aParent);
UniqueContentParentKeepAlive Take(const nsACString& aRemoteType);
already_AddRefed<ContentParent> Take(const nsACString& aRemoteType);
void Erase(ContentParent* aParent);
private:
@ -77,7 +77,7 @@ class PreallocatedProcessManagerImpl final : public nsIObserver {
bool mEnabled;
uint32_t mNumberPreallocs;
AutoTArray<UniqueContentParentKeepAlive, 3> mPreallocatedProcesses;
AutoTArray<RefPtr<ContentParent>, 3> mPreallocatedProcesses;
// Even if we have multiple PreallocatedProcessManagerImpls, we'll have
// one blocker counter
static uint32_t sNumBlockers;
@ -192,14 +192,14 @@ void PreallocatedProcessManagerImpl::RereadPrefs() {
}
}
UniqueContentParentKeepAlive PreallocatedProcessManagerImpl::Take(
already_AddRefed<ContentParent> PreallocatedProcessManagerImpl::Take(
const nsACString& aRemoteType) {
if (!IsEnabled()) {
return nullptr;
}
UniqueContentParentKeepAlive process;
RefPtr<ContentParent> process;
if (!IsEmpty()) {
process = std::move(mPreallocatedProcesses.ElementAt(0));
process = mPreallocatedProcesses.ElementAt(0);
mPreallocatedProcesses.RemoveElementAt(0);
// Don't set the priority to FOREGROUND here, since it may not have
@ -207,7 +207,7 @@ UniqueContentParentKeepAlive PreallocatedProcessManagerImpl::Take(
// We took a preallocated process. Let's try to start up a new one
// soon.
ContentParent* last = mPreallocatedProcesses.SafeLastElement(nullptr).get();
ContentParent* last = mPreallocatedProcesses.SafeLastElement(nullptr);
// There could be a launching process that isn't the last, but that's
// ok (and unlikely)
if (!last || !last->IsLaunching()) {
@ -219,11 +219,11 @@ UniqueContentParentKeepAlive PreallocatedProcessManagerImpl::Take(
(unsigned long)mPreallocatedProcesses.Length()));
}
if (process && !process->IsLaunching()) {
ProcessPriorityManager::SetProcessPriority(process.get(),
ProcessPriorityManager::SetProcessPriority(process,
PROCESS_PRIORITY_FOREGROUND);
} // else this will get set by the caller when they call InitInternal()
return process;
return process.forget();
}
void PreallocatedProcessManagerImpl::Erase(ContentParent* aParent) {
@ -315,14 +315,19 @@ void PreallocatedProcessManagerImpl::AllocateNow() {
return;
}
UniqueContentParentKeepAlive process = ContentParent::MakePreallocProcess();
process->WaitForLaunchAsync(PROCESS_PRIORITY_PREALLOC)
RefPtr<ContentParent> process = ContentParent::MakePreallocProcess();
mPreallocatedProcesses.AppendElement(process);
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("Preallocated = %lu of %d processes",
(unsigned long)mPreallocatedProcesses.Length(), mNumberPreallocs));
RefPtr<PreallocatedProcessManagerImpl> self(this);
process->LaunchSubprocessAsync(PROCESS_PRIORITY_PREALLOC)
->Then(
GetCurrentSerialEventTarget(), __func__,
[self = RefPtr{this},
process = RefPtr{process.get()}](UniqueContentParentKeepAlive) {
[self, this, process](const RefPtr<ContentParent>&) {
if (process->IsDead()) {
self->Erase(process);
Erase(process);
// Process died in startup (before we could add it). If it
// dies after this, MarkAsDead() will Erase() this entry.
// Shouldn't be in the sBrowserContentParents, so we don't need
@ -330,22 +335,24 @@ void PreallocatedProcessManagerImpl::AllocateNow() {
// preallocation here, to avoid possible looping if something is
// causing them to consistently fail; if everything is ok on the
// next allocation request we'll kick off creation.
} else if (self->CanAllocate()) {
} else {
// Continue prestarting processes if needed
if (self->mPreallocatedProcesses.Length() <
self->mNumberPreallocs) {
self->AllocateOnIdle();
if (CanAllocate()) {
if (mPreallocatedProcesses.Length() < mNumberPreallocs) {
AllocateOnIdle();
}
} else if (!IsEnabled()) {
// if this has a remote type set, it's been allocated for use
// already
if (process->mRemoteType == PREALLOC_REMOTE_TYPE) {
// This will Erase() it
process->ShutDownProcess(
ContentParent::SEND_SHUTDOWN_MESSAGE);
}
}
}
},
[self = RefPtr{this}, process = RefPtr{process.get()}]() {
self->Erase(process);
});
mPreallocatedProcesses.AppendElement(std::move(process));
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("Preallocated = %lu of %d processes",
(unsigned long)mPreallocatedProcesses.Length(), mNumberPreallocs));
[self, this, process]() { Erase(process); });
}
void PreallocatedProcessManagerImpl::Disable() {
@ -358,9 +365,20 @@ void PreallocatedProcessManagerImpl::Disable() {
}
void PreallocatedProcessManagerImpl::CloseProcesses() {
// Drop our KeepAlives on these processes. This will automatically lead to the
// processes being shut down when no keepalives are left.
mPreallocatedProcesses.Clear();
while (!IsEmpty()) {
RefPtr<ContentParent> process(mPreallocatedProcesses.ElementAt(0));
mPreallocatedProcesses.RemoveElementAt(0);
process->ShutDownProcess(ContentParent::SEND_SHUTDOWN_MESSAGE);
// drop ref and let it free
}
// Make sure to also clear out the recycled E10S process cache, as it's also
// controlled by the same preference, and can be cleaned up due to memory
// pressure.
if (RefPtr<ContentParent> recycled =
ContentParent::sRecycledE10SProcess.forget()) {
recycled->MaybeBeginShutDown();
}
}
inline PreallocatedProcessManagerImpl*
@ -404,7 +422,7 @@ void PreallocatedProcessManager::RemoveBlocker(const nsACString& aRemoteType,
}
/* static */
UniqueContentParentKeepAlive PreallocatedProcessManager::Take(
already_AddRefed<ContentParent> PreallocatedProcessManager::Take(
const nsACString& aRemoteType) {
if (auto impl = GetPPMImpl()) {
return impl->Take(aRemoteType);

View File

@ -8,10 +8,13 @@
#define mozilla_PreallocatedProcessManager_h
#include "base/basictypes.h"
#include "mozilla/dom/UniqueContentParentKeepAlive.h"
#include "mozilla/AlreadyAddRefed.h"
#include "nsStringFwd.h"
namespace mozilla {
namespace dom {
class ContentParent;
} // namespace dom
/**
* This class manages a ContentParent that it starts up ahead of any particular
@ -29,9 +32,7 @@ namespace mozilla {
class PreallocatedProcessManagerImpl;
class PreallocatedProcessManager final {
using ContentParent = mozilla::dom::ContentParent;
using UniqueContentParentKeepAlive =
mozilla::dom::UniqueContentParentKeepAlive;
typedef mozilla::dom::ContentParent ContentParent;
public:
static PreallocatedProcessManagerImpl* GetPPMImpl();
@ -54,7 +55,7 @@ class PreallocatedProcessManager final {
* If we use a preallocated process, it will schedule the start of
* another on Idle (AllocateOnIdle()).
*/
static UniqueContentParentKeepAlive Take(const nsACString& aRemoteType);
static already_AddRefed<ContentParent> Take(const nsACString& aRemoteType);
/**
* Note that a process was shut down, and should no longer be tracked as a

View File

@ -1,60 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "mozilla/dom/UniqueContentParentKeepAlive.h"
#include "mozilla/dom/ContentParent.h"
namespace mozilla::dom {
void ContentParentKeepAliveDeleter::operator()(ContentParent* aProcess) {
AssertIsOnMainThread();
if (RefPtr<ContentParent> process = dont_AddRef(aProcess)) {
process->RemoveKeepAlive(mBrowserId);
}
}
void ContentParentKeepAliveDeleter::operator()(
ThreadsafeContentParentHandle* aHandle) {
if (RefPtr<ThreadsafeContentParentHandle> handle = dont_AddRef(aHandle)) {
NS_DispatchToMainThread(NS_NewRunnableFunction(
"ThreadsafeContentParentKeepAliveDeleter",
[handle = std::move(handle), browserId = mBrowserId]() {
AssertIsOnMainThread();
if (RefPtr<ContentParent> process = handle->GetContentParent()) {
process->RemoveKeepAlive(browserId);
}
}));
}
}
UniqueContentParentKeepAlive UniqueContentParentKeepAliveFromThreadsafe(
UniqueThreadsafeContentParentKeepAlive aKeepAlive) {
AssertIsOnMainThread();
if (aKeepAlive) {
uint64_t browserId = aKeepAlive.get_deleter().mBrowserId;
RefPtr<ThreadsafeContentParentHandle> handle =
dont_AddRef(aKeepAlive.release());
RefPtr<ContentParent> process = handle->GetContentParent();
return UniqueContentParentKeepAlive{process.forget().take(),
{.mBrowserId = browserId}};
}
return nullptr;
}
UniqueThreadsafeContentParentKeepAlive UniqueContentParentKeepAliveToThreadsafe(
UniqueContentParentKeepAlive aKeepAlive) {
AssertIsOnMainThread();
if (aKeepAlive) {
uint64_t browserId = aKeepAlive.get_deleter().mBrowserId;
RefPtr<ContentParent> process = dont_AddRef(aKeepAlive.release());
RefPtr<ThreadsafeContentParentHandle> handle = process->ThreadsafeHandle();
return UniqueThreadsafeContentParentKeepAlive{handle.forget().take(),
{.mBrowserId = browserId}};
}
return nullptr;
}
} // namespace mozilla::dom

View File

@ -1,42 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_dom_UniqueContentParentKeepAlive_h
#define mozilla_dom_UniqueContentParentKeepAlive_h
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
namespace mozilla::dom {
class ContentParent;
class ThreadsafeContentParentHandle;
struct ContentParentKeepAliveDeleter {
void operator()(ContentParent* aProcess);
void operator()(ThreadsafeContentParentHandle* aHandle);
uint64_t mBrowserId = 0;
};
// Helper for managing a ContentParent combined with the KeepAlive which is
// keeping it alive for use by a specific BrowserId.
//
// This generally should not be created directly, but rather should be created
// using `ContentParent::AddKeepAlive`.
using UniqueContentParentKeepAlive =
UniquePtr<ContentParent, ContentParentKeepAliveDeleter>;
using UniqueThreadsafeContentParentKeepAlive =
UniquePtr<ThreadsafeContentParentHandle, ContentParentKeepAliveDeleter>;
UniqueContentParentKeepAlive UniqueContentParentKeepAliveFromThreadsafe(
UniqueThreadsafeContentParentKeepAlive aKeepAlive);
UniqueThreadsafeContentParentKeepAlive UniqueContentParentKeepAliveToThreadsafe(
UniqueContentParentKeepAlive aKeepAlive);
} // namespace mozilla::dom
#endif // mozilla_dom_UniqueContentParentKeepAlive_h

View File

@ -85,7 +85,6 @@ EXPORTS.mozilla.dom += [
"TabContext.h",
"TabMessageTypes.h",
"TabMessageUtils.h",
"UniqueContentParentKeepAlive.h",
"URLClassifierChild.h",
"URLClassifierParent.h",
"UserActivationIPCUtils.h",
@ -149,7 +148,6 @@ UNIFIED_SOURCES += [
"SharedStringMap.cpp",
"StructuredCloneData.cpp",
"TabContext.cpp",
"UniqueContentParentKeepAlive.cpp",
"URLClassifierParent.cpp",
"WindowGlobalActor.cpp",
"WindowGlobalChild.cpp",

View File

@ -51,8 +51,11 @@ mozilla::ipc::IPCResult FetchEventOpParent::RecvPreloadResponse(
aPending.mPreloadResponse = Some(std::move(aResponse));
},
[&aResponse](Started& aStarted) {
auto backgroundParent = WrapNotNull(
WrapNotNull(aStarted.mFetchEventOpProxyParent->Manager())
->Manager());
Unused << aStarted.mFetchEventOpProxyParent->SendPreloadResponse(
ToParentToChild(aResponse));
ToParentToChild(aResponse, backgroundParent));
},
[](const Finished&) {});

View File

@ -121,8 +121,8 @@ ParentToParentFetchEventRespondWithResult ToParentToParent(
Nothing(), Nothing());
if (aArgs.preloadResponse().isSome()) {
// Convert the preload response to ParentToChildInternalResponse.
copyArgs.preloadResponse() =
Some(ToParentToChild(aArgs.preloadResponse().ref()));
copyArgs.preloadResponse() = Some(ToParentToChild(
aArgs.preloadResponse().ref(), WrapNotNull(aManager->Manager())));
}
if (aArgs.preloadResponseTiming().isSome()) {
@ -145,7 +145,8 @@ ParentToParentFetchEventRespondWithResult ToParentToParent(
auto [preloadResponse, preloadResponseEndArgs] =
actor->mReal->OnStart(WrapNotNull(actor));
if (copyArgs.preloadResponse().isNothing() && preloadResponse.isSome()) {
copyArgs.preloadResponse() = Some(ToParentToChild(preloadResponse.ref()));
copyArgs.preloadResponse() = Some(ToParentToChild(
preloadResponse.ref(), WrapNotNull(aManager->Manager())));
}
if (copyArgs.preloadResponseEndArgs().isNothing() &&
preloadResponseEndArgs.isSome()) {

View File

@ -2,8 +2,8 @@
* 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 protocol PBackground;
include protocol PFetchEventOpProxy;
include protocol PRemoteWorkerService;
include DOMTypes;
include ServiceWorkerOpArgs;
@ -58,7 +58,7 @@ union RemoteWorkerOp {
// PRemoteWorkerController protocol.
protocol PRemoteWorker
{
manager PRemoteWorkerService;
manager PBackground;
manages PFetchEventOpProxy;

View File

@ -2,7 +2,7 @@
* 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 protocol PRemoteWorker;
include protocol PBackground;
include ProtocolTypes;
include RemoteWorkerTypes;
@ -13,13 +13,12 @@ namespace dom {
// Simple protocol to register any active RemoteWorkerService running on any
// process. Initialization/registration is delayed for preallocated processes
// until the process takes on its final remoteType.
[NeedsOtherPid, ChildProc=anydom]
protocol PRemoteWorkerService
{
manages PRemoteWorker;
manager PBackground;
child:
async PRemoteWorker(RemoteWorkerData data);
parent:
async __delete__();
};
} // namespace dom

View File

@ -232,14 +232,50 @@ void RemoteWorkerManager::RegisterActor(RemoteWorkerServiceParent* aActor) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aActor);
if (!aActor->IsOtherProcessActor()) {
if (!BackgroundParent::IsOtherProcessActor(aActor->Manager())) {
MOZ_ASSERT(!mParentActor);
mParentActor = aActor;
MOZ_ASSERT(mPendings.IsEmpty());
return;
}
MOZ_ASSERT(!mChildActors.Contains(aActor));
mChildActors.AppendElement(aActor);
if (!mPendings.IsEmpty()) {
const auto& processRemoteType = aActor->GetRemoteType();
nsTArray<Pending> unlaunched;
// Flush pending launching.
for (Pending& p : mPendings) {
if (p.mController->IsTerminated()) {
continue;
}
const auto& workerRemoteType = p.mData.remoteType();
if (MatchRemoteType(processRemoteType, workerRemoteType)) {
LOG(("RegisterActor - Launch Pending, workerRemoteType=%s",
workerRemoteType.get()));
LaunchInternal(p.mController, aActor, p.mData);
} else {
unlaunched.AppendElement(std::move(p));
continue;
}
}
std::swap(mPendings, unlaunched);
// AddRef is called when the first Pending object is added to mPendings, so
// the balancing Release is called when the last Pending object is removed.
// RemoteWorkerServiceParents will hold strong references to
// RemoteWorkerManager.
if (mPendings.IsEmpty()) {
Release();
}
LOG(("RegisterActor - mPendings length: %zu", mPendings.Length()));
}
}
void RemoteWorkerManager::UnregisterActor(RemoteWorkerServiceParent* aActor) {
@ -261,37 +297,40 @@ void RemoteWorkerManager::Launch(RemoteWorkerController* aController,
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
TargetActorAndKeepAlive target = SelectTargetActor(aData, aProcessId);
RemoteWorkerServiceParent* targetActor = SelectTargetActor(aData, aProcessId);
// If there is not an available actor, let's store the data, and let's spawn a
// new process.
if (!targetActor) {
// If this is the first time we have a pending launching, we must keep alive
// the manager.
if (mPendings.IsEmpty()) {
AddRef();
}
Pending* pending = mPendings.AppendElement();
pending->mController = aController;
pending->mData = aData;
// If there is no available actor, try to start a process, and connect to it.
if (!target.mActor) {
// Launching is async, so we cannot check for failures right here.
LaunchNewContentProcess(aData)->Then(
GetCurrentSerialEventTarget(), __func__,
[self = RefPtr{this}, controller = RefPtr{aController},
data = aData](TargetActorAndKeepAlive&& aTarget) {
if (aTarget.mActor->CanSend()) {
self->LaunchInternal(controller, aTarget.mActor,
std::move(aTarget.mKeepAlive), data);
} else {
controller->CreationFailed();
}
},
[controller = RefPtr{aController}](nsresult) {
controller->CreationFailed();
});
LaunchNewContentProcess(aData);
return;
}
LaunchInternal(aController, target.mActor, std::move(target.mKeepAlive),
aData);
/**
* If a target actor for the remote worker has been selected, the actor has
* already been registered with the corresponding `ContentParent` and we
* should not increment the `mRemoteWorkerActorData`'s `mCount` again (see
* `SelectTargetActorForServiceWorker()` /
* `SelectTargetActorForSharedWorker()`).
*/
LaunchInternal(aController, targetActor, aData, true);
}
void RemoteWorkerManager::LaunchInternal(
RemoteWorkerController* aController,
RemoteWorkerServiceParent* aTargetActor,
UniqueThreadsafeContentParentKeepAlive aKeepAlive,
const RemoteWorkerData& aData) {
RemoteWorkerServiceParent* aTargetActor, const RemoteWorkerData& aData,
bool aRemoteWorkerAlreadyRegistered) {
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
MOZ_ASSERT(aController);
@ -302,13 +341,14 @@ void RemoteWorkerManager::LaunchInternal(
// We need to send permissions to content processes, but not if we're spawning
// the worker here in the parent process.
if (aTargetActor != mParentActor) {
MOZ_ASSERT(aKeepAlive);
RefPtr<ThreadsafeContentParentHandle> contentHandle =
BackgroundParent::GetContentParentHandle(aTargetActor->Manager());
// This won't cause any race conditions because the content process
// should wait for the permissions to be received before executing the
// Service Worker.
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [contentHandle = RefPtr{aKeepAlive.get()},
__func__, [contentHandle = std::move(contentHandle),
principalInfo = aData.principalInfo()] {
AssertIsOnMainThread();
if (RefPtr<ContentParent> contentParent =
@ -321,13 +361,15 @@ void RemoteWorkerManager::LaunchInternal(
MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
}
RefPtr<RemoteWorkerParent> workerActor =
MakeAndAddRef<RemoteWorkerParent>(std::move(aKeepAlive));
if (!aTargetActor->SendPRemoteWorkerConstructor(workerActor, aData)) {
RefPtr<RemoteWorkerParent> workerActor = MakeAndAddRef<RemoteWorkerParent>();
if (!aTargetActor->Manager()->SendPRemoteWorkerConstructor(workerActor,
aData)) {
AsyncCreationFailed(aController);
return;
}
workerActor->Initialize(aRemoteWorkerAlreadyRegistered);
// This makes the link better the 2 actors.
aController->SetWorkerActor(workerActor);
workerActor->SetController(aController);
@ -371,7 +413,7 @@ void RemoteWorkerManager::ForEachActor(
if (MatchRemoteType(actor->GetRemoteType(), aRemoteType)) {
ThreadsafeContentParentHandle* contentHandle =
actor->GetContentParentHandle();
BackgroundParent::GetContentParentHandle(actor->Manager());
if (!aCallback(actor, contentHandle)) {
break;
@ -392,23 +434,29 @@ void RemoteWorkerManager::ForEachActor(
* search's starting position randomly.
*
* - When Fission is enabled, Shared Workers may have to be spawned into
* different child process from the one where it has been registered from, and
* that child process may be going to be marked as dead and shutdown.
* different child process from the one where it has been registered from, and
* that child process may be going to be marked as dead and shutdown.
*
* ContentParent provides a way to add a KeepAlive, which will prevent the
* process from being shut down, through a ThreadsafeContentParentHandle in an
* atomic way. This call will fail if the process is already being shut down.
* When selecting a content process on the PBackground thread, we'll acquire the
* KeepAlive in that way.
* Spawning the workers in a random process makes the process selection criteria
* a little tricky, as a candidate process may imminently shutdown due to a
* remove worker actor unregistering
* (see `ContentParent::UnregisterRemoveWorkerActor`).
*
* In `ContentParent::MaybeBeginShutdown` we only dispatch a runnable
* to call `ContentParent::ShutDownProcess` if there are no registered remote
* worker actors, and we ensure that the check for the number of registered
* actors and the dispatching of the runnable are atomic. That happens on the
* main thread, so here on the background thread, while
* `ContentParent::mRemoteWorkerActorData` is locked, if `mCount` > 0, we can
* register a remote worker actor "early" and guarantee that the corresponding
* content process will not shutdown.
*/
RemoteWorkerManager::TargetActorAndKeepAlive
RemoteWorkerManager::SelectTargetActorInternal(
RemoteWorkerServiceParent* RemoteWorkerManager::SelectTargetActorInternal(
const RemoteWorkerData& aData, base::ProcessId aProcessId) const {
AssertIsOnBackgroundThread();
MOZ_ASSERT(!mChildActors.IsEmpty());
RemoteWorkerServiceParent* actor = nullptr;
UniqueThreadsafeContentParentKeepAlive keepAlive;
const auto& workerRemoteType = aData.remoteType();
@ -423,7 +471,11 @@ RemoteWorkerManager::SelectTargetActorInternal(
// process with a pid equal to aProcessId if any, otherwise it would
// start from a random actor in the mChildActors array, this guarantees
// that we will choose that actor if it does also match the remote type.
if ((keepAlive = aContentHandle->TryAddKeepAlive())) {
if (aContentHandle->MaybeRegisterRemoteWorkerActor(
[&](uint32_t count, bool shutdownStarted) -> bool {
return (count || !shutdownStarted) &&
(aActor->OtherPid() == aProcessId || !actor);
})) {
actor = aActor;
return false;
}
@ -432,19 +484,18 @@ RemoteWorkerManager::SelectTargetActorInternal(
},
workerRemoteType, IsServiceWorker(aData) ? Nothing() : Some(aProcessId));
return {actor, std::move(keepAlive)};
return actor;
}
RemoteWorkerManager::TargetActorAndKeepAlive
RemoteWorkerManager::SelectTargetActor(const RemoteWorkerData& aData,
base::ProcessId aProcessId) {
RemoteWorkerServiceParent* RemoteWorkerManager::SelectTargetActor(
const RemoteWorkerData& aData, base::ProcessId aProcessId) {
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
// System principal workers should run on the parent process.
if (aData.principalInfo().type() == PrincipalInfo::TSystemPrincipalInfo) {
MOZ_ASSERT(mParentActor);
return {mParentActor, nullptr};
return mParentActor;
}
// Extension principal workers are allowed to run on the parent process
@ -454,65 +505,122 @@ RemoteWorkerManager::SelectTargetActor(const RemoteWorkerData& aData,
!StaticPrefs::extensions_webextensions_remote() &&
HasExtensionPrincipal(aData)) {
MOZ_ASSERT(mParentActor);
return {mParentActor, nullptr};
return mParentActor;
}
// If e10s is off, use the parent process.
if (!BrowserTabsRemoteAutostart()) {
MOZ_ASSERT(mParentActor);
return {mParentActor, nullptr};
return mParentActor;
}
// We shouldn't have to worry about content-principal parent-process workers.
MOZ_ASSERT(aProcessId != base::GetCurrentProcId());
if (mChildActors.IsEmpty()) {
return {nullptr, nullptr};
return nullptr;
}
return SelectTargetActorInternal(aData, aProcessId);
}
RefPtr<RemoteWorkerManager::LaunchProcessPromise>
RemoteWorkerManager::LaunchNewContentProcess(const RemoteWorkerData& aData) {
void RemoteWorkerManager::LaunchNewContentProcess(
const RemoteWorkerData& aData) {
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
// Request a process making sure to specify aPreferUsed=true. For a given
// remoteType there's a pool size limit. If we pass aPreferUsed here, then if
// there's any process in the pool already, we will use that. If we pass
// false (which is the default if omitted), then this call will spawn a new
// process if the pool isn't at its limit yet.
//
// (Our intent is never to grow the pool size here. Our logic gets here
// because our current logic on PBackground is only aware of
// RemoteWorkerServiceParent actors that have registered themselves, which is
// fundamentally unaware of processes that will match in the future when they
// register. So we absolutely are fine with and want any existing processes.)
return InvokeAsync(GetMainThreadSerialEventTarget(), __func__,
[remoteType = aData.remoteType()]() {
return ContentParent::GetNewOrUsedBrowserProcessAsync(
/* aRemoteType = */ remoteType,
/* aGroup */ nullptr,
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
/* aPreferUsed */ true);
})
->Then(
GetMainThreadSerialEventTarget(), __func__,
[](UniqueContentParentKeepAlive&& aContentParent) {
RefPtr<RemoteWorkerServiceParent> actor =
aContentParent->GetRemoteWorkerServiceParent();
MOZ_ASSERT(actor, "RemoteWorkerServiceParent not initialized?");
return RemoteWorkerManager::LaunchProcessPromise::CreateAndResolve(
TargetActorAndKeepAlive{
actor, UniqueContentParentKeepAliveToThreadsafe(
std::move(aContentParent))},
__func__);
},
[](nsresult aError) {
return RemoteWorkerManager::LaunchProcessPromise::CreateAndReject(
aError, __func__);
nsCOMPtr<nsISerialEventTarget> bgEventTarget = GetCurrentSerialEventTarget();
using LaunchPromiseType = ContentParent::LaunchPromise;
using CallbackParamType = LaunchPromiseType::ResolveOrRejectValue;
// A new content process must be requested on the main thread. On success,
// the success callback will also run on the main thread. On failure, however,
// the failure callback must be run on the background thread - it uses
// RemoteWorkerManager, and RemoteWorkerManager isn't threadsafe, so the
// promise callback will just dispatch the "real" failure callback to the
// background thread.
auto processLaunchCallback = [principalInfo = aData.principalInfo(),
bgEventTarget = std::move(bgEventTarget),
self = RefPtr<RemoteWorkerManager>(this)](
const CallbackParamType& aValue,
const nsCString& remoteType) mutable {
if (aValue.IsResolve()) {
LOG(("LaunchNewContentProcess: successfully got child process"));
// The failure callback won't run, and we're on the main thread, so
// we need to properly release the thread-unsafe RemoteWorkerManager.
NS_ProxyRelease(__func__, bgEventTarget, self.forget());
} else {
// The "real" failure callback.
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [self = std::move(self), remoteType] {
nsTArray<Pending> uncancelled;
auto pendings = std::move(self->mPendings);
for (const auto& pending : pendings) {
const auto& workerRemoteType = pending.mData.remoteType();
if (self->MatchRemoteType(remoteType, workerRemoteType)) {
LOG(
("LaunchNewContentProcess: Cancel pending with "
"workerRemoteType=%s",
workerRemoteType.get()));
pending.mController->CreationFailed();
} else {
uncancelled.AppendElement(pending);
}
}
std::swap(self->mPendings, uncancelled);
});
bgEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
}
};
LOG(("LaunchNewContentProcess: remoteType=%s", aData.remoteType().get()));
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [callback = std::move(processLaunchCallback),
workerRemoteType = aData.remoteType()]() mutable {
auto remoteType =
workerRemoteType.IsEmpty() ? DEFAULT_REMOTE_TYPE : workerRemoteType;
RefPtr<LaunchPromiseType> onFinished;
if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
// Request a process making sure to specify aPreferUsed=true. For a
// given remoteType there's a pool size limit. If we pass aPreferUsed
// here, then if there's any process in the pool already, we will use
// that. If we pass false (which is the default if omitted), then
// this call will spawn a new process if the pool isn't at its limit
// yet.
//
// (Our intent is never to grow the pool size here. Our logic gets
// here because our current logic on PBackground is only aware of
// RemoteWorkerServiceParent actors that have registered themselves,
// which is fundamentally unaware of processes that will match in the
// future when they register. So we absolutely are fine with and want
// any existing processes.)
onFinished = ContentParent::GetNewOrUsedBrowserProcessAsync(
/* aRemoteType = */ remoteType,
/* aGroup */ nullptr,
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
/* aPreferUsed */ true);
} else {
// We can find this event still in flight after having been asked to
// shutdown. Let's fake a failure to ensure our callback is called
// such that we clean up everything properly.
onFinished = LaunchPromiseType::CreateAndReject(
NS_ERROR_ILLEGAL_DURING_SHUTDOWN, __func__);
}
onFinished->Then(GetCurrentSerialEventTarget(), __func__,
[callback = std::move(callback),
remoteType](const CallbackParamType& aValue) mutable {
callback(aValue, remoteType);
});
});
SchedulerGroup::Dispatch(r.forget());
}
} // namespace dom

View File

@ -22,9 +22,11 @@ class RemoteWorkerServiceParent;
/**
* PBackground instance that keeps tracks of RemoteWorkerServiceParent actors
* (1 per process, including the main process). Decides which
* RemoteWorkerServerParent to use internally via SelectTargetActor in order to
* select a BackgroundParent manager on which to create a RemoteWorkerParent.
* (1 per process, including the main process) and pending
* RemoteWorkerController requests to spawn remote workers if the spawn request
* can't be immediately fulfilled. Decides which RemoteWorkerServerParent to use
* internally via SelectTargetActor in order to select a BackgroundParent
* manager on which to create a RemoteWorkerParent.
*/
class RemoteWorkerManager final {
public:
@ -61,26 +63,18 @@ class RemoteWorkerManager final {
RemoteWorkerManager();
~RemoteWorkerManager();
struct TargetActorAndKeepAlive {
RefPtr<RemoteWorkerServiceParent> mActor;
UniqueThreadsafeContentParentKeepAlive mKeepAlive;
};
RemoteWorkerServiceParent* SelectTargetActor(const RemoteWorkerData& aData,
base::ProcessId aProcessId);
TargetActorAndKeepAlive SelectTargetActor(const RemoteWorkerData& aData,
base::ProcessId aProcessId);
TargetActorAndKeepAlive SelectTargetActorInternal(
RemoteWorkerServiceParent* SelectTargetActorInternal(
const RemoteWorkerData& aData, base::ProcessId aProcessId) const;
void LaunchInternal(RemoteWorkerController* aController,
RemoteWorkerServiceParent* aTargetActor,
UniqueThreadsafeContentParentKeepAlive aKeepAlive,
const RemoteWorkerData& aData);
const RemoteWorkerData& aData,
bool aRemoteWorkerAlreadyRegistered = false);
using LaunchProcessPromise =
MozPromise<TargetActorAndKeepAlive, nsresult, true>;
RefPtr<LaunchProcessPromise> LaunchNewContentProcess(
const RemoteWorkerData& aData);
void LaunchNewContentProcess(const RemoteWorkerData& aData);
void AsyncCreationFailed(RemoteWorkerController* aController);
@ -110,6 +104,13 @@ class RemoteWorkerManager final {
// in order, sorted by PID, to avoid linear lookup times?
nsTArray<RemoteWorkerServiceParent*> mChildActors;
RemoteWorkerServiceParent* mParentActor;
struct Pending {
RefPtr<RemoteWorkerController> mController;
RemoteWorkerData mData;
};
nsTArray<Pending> mPendings;
};
} // namespace mozilla::dom

View File

@ -6,11 +6,12 @@
#include "RemoteWorkerParent.h"
#include "RemoteWorkerController.h"
#include "RemoteWorkerServiceParent.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/PFetchEventOpProxyParent.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/SchedulerGroup.h"
#include "mozilla/Unused.h"
#include "nsProxyRelease.h"
namespace mozilla {
@ -18,9 +19,34 @@ using namespace ipc;
namespace dom {
RemoteWorkerParent::RemoteWorkerParent(
UniqueThreadsafeContentParentKeepAlive aKeepAlive)
: mContentParentKeepAlive(std::move(aKeepAlive)) {
namespace {
class UnregisterActorRunnable final : public Runnable {
public:
explicit UnregisterActorRunnable(
already_AddRefed<ThreadsafeContentParentHandle> aParent)
: Runnable("UnregisterActorRunnable"), mContentHandle(aParent) {
AssertIsOnBackgroundThread();
}
NS_IMETHOD
Run() override {
AssertIsOnMainThread();
if (RefPtr<ContentParent> contentParent =
mContentHandle->GetContentParent()) {
contentParent->UnregisterRemoveWorkerActor();
}
return NS_OK;
}
private:
RefPtr<ThreadsafeContentParentHandle> mContentHandle;
};
} // namespace
RemoteWorkerParent::RemoteWorkerParent() {
AssertIsOnBackgroundThread();
MOZ_ASSERT(XRE_IsParentProcess());
}
@ -30,9 +56,19 @@ RemoteWorkerParent::~RemoteWorkerParent() {
MOZ_ASSERT(XRE_IsParentProcess());
}
RemoteWorkerServiceParent* RemoteWorkerParent::Manager() const {
return static_cast<RemoteWorkerServiceParent*>(
PRemoteWorkerParent::Manager());
void RemoteWorkerParent::Initialize(bool aAlreadyRegistered) {
RefPtr<ThreadsafeContentParentHandle> parent =
BackgroundParent::GetContentParentHandle(Manager());
// Parent is null if the child actor runs on the parent process.
if (parent) {
if (!aAlreadyRegistered) {
parent->RegisterRemoteWorkerActor();
}
NS_ReleaseOnMainThread("RemoteWorkerParent::Initialize ContentParent",
parent.forget());
}
}
already_AddRefed<PFetchEventOpProxyParent>
@ -46,7 +82,15 @@ void RemoteWorkerParent::ActorDestroy(IProtocol::ActorDestroyReason) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(XRE_IsParentProcess());
mContentParentKeepAlive = nullptr;
RefPtr<ThreadsafeContentParentHandle> parent =
BackgroundParent::GetContentParentHandle(Manager());
// Parent is null if the child actor runs on the parent process.
if (parent) {
RefPtr<UnregisterActorRunnable> r =
new UnregisterActorRunnable(parent.forget());
SchedulerGroup::Dispatch(r.forget());
}
if (mController) {
mController->NoteDeadWorkerActor();

View File

@ -8,12 +8,10 @@
#define mozilla_dom_RemoteWorkerParent_h
#include "mozilla/dom/PRemoteWorkerParent.h"
#include "mozilla/dom/UniqueContentParentKeepAlive.h"
namespace mozilla::dom {
class RemoteWorkerController;
class RemoteWorkerServiceParent;
/**
* PBackground-managed parent actor that is mutually associated with a single
@ -26,15 +24,14 @@ class RemoteWorkerParent final : public PRemoteWorkerParent {
public:
NS_INLINE_DECL_REFCOUNTING(RemoteWorkerParent, override);
explicit RemoteWorkerParent(
UniqueThreadsafeContentParentKeepAlive aKeepAlive);
RemoteWorkerParent();
void Initialize(bool aAlreadyRegistered = false);
void SetController(RemoteWorkerController* aController);
void MaybeSendDelete();
RemoteWorkerServiceParent* Manager() const;
private:
~RemoteWorkerParent();
@ -58,7 +55,6 @@ class RemoteWorkerParent final : public PRemoteWorkerParent {
bool mDeleteSent = false;
RefPtr<RemoteWorkerController> mController;
UniqueThreadsafeContentParentKeepAlive mContentParentKeepAlive;
};
} // namespace mozilla::dom

View File

@ -112,15 +112,27 @@ RemoteWorkerServiceKeepAlive::~RemoteWorkerServiceKeepAlive() {
}
/* static */
void RemoteWorkerService::InitializeParent() {
void RemoteWorkerService::Initialize() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_IsParentProcess());
StaticMutexAutoLock lock(sRemoteWorkerServiceMutex);
MOZ_ASSERT(!sRemoteWorkerService);
RefPtr<RemoteWorkerService> service = new RemoteWorkerService();
// ## Content Process Initialization Case
//
// We are being told to initialize now that we know what our remote type is.
// Now is a fine time to call InitializeOnMainThread.
if (!XRE_IsParentProcess()) {
nsresult rv = service->InitializeOnMainThread();
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
sRemoteWorkerService = service;
return;
}
// ## Parent Process Initialization Case
//
// Otherwise we are in the parent process and were invoked by
@ -145,30 +157,6 @@ void RemoteWorkerService::InitializeParent() {
sRemoteWorkerService = service;
}
/* static */
void RemoteWorkerService::InitializeChild(
mozilla::ipc::Endpoint<PRemoteWorkerServiceChild> aEndpoint) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!XRE_IsParentProcess());
StaticMutexAutoLock lock(sRemoteWorkerServiceMutex);
MOZ_ASSERT(!sRemoteWorkerService);
RefPtr<RemoteWorkerService> service = new RemoteWorkerService();
// ## Content Process Initialization Case
//
// We are being told to initialize now that we know what our remote type is.
// Now is a fine time to call InitializeOnMainThread.
nsresult rv = service->InitializeOnMainThread(std::move(aEndpoint));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
sRemoteWorkerService = service;
}
/* static */
nsIThread* RemoteWorkerService::Thread() {
StaticMutexAutoLock lock(sRemoteWorkerServiceMutex);
@ -195,8 +183,7 @@ RemoteWorkerService::MaybeGetKeepAlive() {
return extraRef.forget();
}
nsresult RemoteWorkerService::InitializeOnMainThread(
mozilla::ipc::Endpoint<PRemoteWorkerServiceChild> aEndpoint) {
nsresult RemoteWorkerService::InitializeOnMainThread() {
// I would like to call this thread "DOM Remote Worker Launcher", but the max
// length is 16 chars.
nsresult rv = NS_NewNamedThread("Worker Launcher", getter_AddRefs(mThread));
@ -226,9 +213,7 @@ nsresult RemoteWorkerService::InitializeOnMainThread(
RefPtr<RemoteWorkerService> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
"InitializeThread", [self, endpoint = std::move(aEndpoint)]() mutable {
self->InitializeOnTargetThread(std::move(endpoint));
});
"InitializeThread", [self]() { self->InitializeOnTargetThread(); });
rv = mThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -245,14 +230,20 @@ RemoteWorkerService::RemoteWorkerService()
RemoteWorkerService::~RemoteWorkerService() = default;
void RemoteWorkerService::InitializeOnTargetThread(
mozilla::ipc::Endpoint<PRemoteWorkerServiceChild> aEndpoint) {
void RemoteWorkerService::InitializeOnTargetThread() {
MOZ_ASSERT(mThread);
MOZ_ASSERT(mThread->IsOnCurrentThread());
PBackgroundChild* backgroundActor =
BackgroundChild::GetOrCreateForCurrentThread();
if (NS_WARN_IF(!backgroundActor)) {
return;
}
RefPtr<RemoteWorkerServiceChild> serviceActor =
MakeAndAddRef<RemoteWorkerServiceChild>();
if (NS_WARN_IF(!aEndpoint.Bind(serviceActor))) {
if (NS_WARN_IF(!backgroundActor->SendPRemoteWorkerServiceConstructor(
serviceActor))) {
return;
}
@ -267,7 +258,7 @@ void RemoteWorkerService::CloseActorOnTargetThread() {
// If mActor is nullptr it means that initialization failed.
if (mActor) {
// Here we need to shutdown the IPC protocol.
mActor->Close();
mActor->Send__delete__(mActor);
mActor = nullptr;
}
}
@ -309,12 +300,7 @@ RemoteWorkerService::Observe(nsISupports* aSubject, const char* aTopic,
obs->RemoveObserver(this, "profile-after-change");
}
Endpoint<PRemoteWorkerServiceChild> childEp;
RefPtr<RemoteWorkerServiceParent> parentActor =
RemoteWorkerServiceParent::CreateForProcess(nullptr, &childEp);
NS_ENSURE_TRUE(parentActor, NS_ERROR_FAILURE);
return InitializeOnMainThread(std::move(childEp));
return InitializeOnMainThread();
}
void RemoteWorkerService::BeginShutdown() {

View File

@ -9,7 +9,6 @@
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/DataMutex.h"
#include "mozilla/ipc/Endpoint.h"
#include "nsCOMPtr.h"
#include "nsIObserver.h"
#include "nsISupportsImpl.h"
@ -21,7 +20,6 @@ namespace mozilla::dom {
class RemoteWorkerService;
class RemoteWorkerServiceChild;
class RemoteWorkerServiceShutdownBlocker;
class PRemoteWorkerServiceChild;
/**
* Refcounted lifecycle helper; when its refcount goes to zero its destructor
@ -69,9 +67,7 @@ class RemoteWorkerService final : public nsIObserver {
NS_DECL_NSIOBSERVER
// To be called when a process is initialized on main-thread.
static void InitializeParent();
static void InitializeChild(
mozilla::ipc::Endpoint<PRemoteWorkerServiceChild> aEndpoint);
static void Initialize();
static nsIThread* Thread();
@ -95,11 +91,9 @@ class RemoteWorkerService final : public nsIObserver {
RemoteWorkerService();
~RemoteWorkerService();
nsresult InitializeOnMainThread(
mozilla::ipc::Endpoint<PRemoteWorkerServiceChild> aEndpoint);
nsresult InitializeOnMainThread();
void InitializeOnTargetThread(
mozilla::ipc::Endpoint<PRemoteWorkerServiceChild> aEndpoint);
void InitializeOnTargetThread();
void CloseActorOnTargetThread();

View File

@ -6,7 +6,6 @@
#include "RemoteWorkerServiceChild.h"
#include "RemoteWorkerController.h"
#include "RemoteWorkerChild.h"
namespace mozilla::dom {
@ -14,17 +13,4 @@ RemoteWorkerServiceChild::RemoteWorkerServiceChild() = default;
RemoteWorkerServiceChild::~RemoteWorkerServiceChild() = default;
already_AddRefed<PRemoteWorkerChild>
RemoteWorkerServiceChild::AllocPRemoteWorkerChild(
const RemoteWorkerData& aData) {
return MakeAndAddRef<RemoteWorkerChild>(aData);
}
mozilla::ipc::IPCResult RemoteWorkerServiceChild::RecvPRemoteWorkerConstructor(
PRemoteWorkerChild* aActor, const RemoteWorkerData& aData) {
RemoteWorkerChild* actor = static_cast<RemoteWorkerChild*>(aActor);
actor->ExecWorker(aData);
return IPC_OK();
}
} // namespace mozilla::dom

View File

@ -17,19 +17,14 @@ class RemoteWorkerData;
/**
* "Worker Launcher"-thread child actor created by the RemoteWorkerService to
* receive messages from the PBackground RemoteWorkerManager in the parent.
* register itself with the PBackground RemoteWorkerManager in the parent.
*/
class RemoteWorkerServiceChild final : public PRemoteWorkerServiceChild {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteWorkerServiceChild, final)
NS_INLINE_DECL_REFCOUNTING(RemoteWorkerServiceChild, final)
RemoteWorkerServiceChild();
already_AddRefed<PRemoteWorkerChild> AllocPRemoteWorkerChild(
const RemoteWorkerData& aData);
mozilla::ipc::IPCResult RecvPRemoteWorkerConstructor(
PRemoteWorkerChild* aActor, const RemoteWorkerData& aData);
private:
~RemoteWorkerServiceChild();
};

View File

@ -14,65 +14,20 @@ using namespace ipc;
namespace dom {
RemoteWorkerServiceParent::RemoteWorkerServiceParent(
ThreadsafeContentParentHandle* aProcess)
: mProcess(aProcess) {}
RemoteWorkerServiceParent::RemoteWorkerServiceParent()
: mManager(RemoteWorkerManager::GetOrCreate()) {}
RemoteWorkerServiceParent::~RemoteWorkerServiceParent() {
MOZ_ASSERT(!mManager,
"ActorDestroy not called before ~RemoteWorkerServiceParent");
}
RemoteWorkerServiceParent::~RemoteWorkerServiceParent() = default;
RefPtr<RemoteWorkerServiceParent> RemoteWorkerServiceParent::CreateForProcess(
ContentParent* aProcess, Endpoint<PRemoteWorkerServiceChild>* aChildEp) {
AssertIsOnMainThread();
nsCOMPtr<nsISerialEventTarget> backgroundThread =
BackgroundParent::GetBackgroundThread();
NS_ENSURE_TRUE(backgroundThread, nullptr);
Endpoint<PRemoteWorkerServiceParent> parentEp;
nsresult rv = PRemoteWorkerService::CreateEndpoints(
base::GetCurrentProcId(),
aProcess ? aProcess->OtherPid() : base::GetCurrentProcId(), &parentEp,
aChildEp);
NS_ENSURE_SUCCESS(rv, nullptr);
RefPtr<RemoteWorkerServiceParent> actor = new RemoteWorkerServiceParent(
aProcess ? aProcess->ThreadsafeHandle() : nullptr);
rv = backgroundThread->Dispatch(
NS_NewRunnableFunction("RemoteWorkerServiceParent::CreateForProcess",
[actor, parentEp = std::move(parentEp)]() mutable {
actor->InitializeOnThread(std::move(parentEp));
}));
NS_ENSURE_SUCCESS(rv, nullptr);
return actor;
}
void RemoteWorkerServiceParent::InitializeOnThread(
Endpoint<PRemoteWorkerServiceParent> aEndpoint) {
void RemoteWorkerServiceParent::Initialize(const nsACString& aRemoteType) {
AssertIsOnBackgroundThread();
if (NS_WARN_IF(!aEndpoint.Bind(this))) {
return;
}
mManager = RemoteWorkerManager::GetOrCreate();
mRemoteType = aRemoteType;
mManager->RegisterActor(this);
}
void RemoteWorkerServiceParent::ActorDestroy(IProtocol::ActorDestroyReason) {
AssertIsOnBackgroundThread();
if (mManager) {
mManager->UnregisterActor(this);
mManager = nullptr;
}
}
nsCString RemoteWorkerServiceParent::GetRemoteType() const {
if (mProcess) {
return mProcess->GetRemoteType();
}
return NOT_REMOTE_TYPE;
mManager->UnregisterActor(this);
}
} // namespace dom

View File

@ -12,39 +12,28 @@
namespace mozilla::dom {
class ContentParent;
class RemoteWorkerManager;
class ThreadsafeContentParentHandle;
/**
* PBackground-thread parent actor that registers with the PBackground
* PBackground parent actor that registers with the PBackground
* RemoteWorkerManager and used to relay spawn requests.
*/
class RemoteWorkerServiceParent final : public PRemoteWorkerServiceParent {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteWorkerServiceParent, override);
static RefPtr<RemoteWorkerServiceParent> CreateForProcess(
ContentParent* aProcess, Endpoint<PRemoteWorkerServiceChild>* aChildEp);
RemoteWorkerServiceParent();
NS_INLINE_DECL_REFCOUNTING(RemoteWorkerServiceParent, override);
void ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) override;
bool IsOtherProcessActor() const { return mProcess != nullptr; }
void Initialize(const nsACString& aRemoteType);
ThreadsafeContentParentHandle* GetContentParentHandle() const {
return mProcess;
}
nsCString GetRemoteType() const;
nsCString GetRemoteType() const { return mRemoteType; }
private:
explicit RemoteWorkerServiceParent(ThreadsafeContentParentHandle* aProcess);
~RemoteWorkerServiceParent();
void InitializeOnThread(Endpoint<PRemoteWorkerServiceParent> aEndpoint);
const RefPtr<ThreadsafeContentParentHandle> mProcess;
RefPtr<RemoteWorkerManager> mManager;
nsCString mRemoteType = NOT_REMOTE_TYPE;
};
} // namespace mozilla::dom

View File

@ -69,9 +69,11 @@ namespace mozilla::ipc {
using mozilla::dom::UDPSocketChild;
using mozilla::net::PUDPSocketChild;
using mozilla::dom::PRemoteWorkerChild;
using mozilla::dom::PServiceWorkerChild;
using mozilla::dom::PServiceWorkerContainerChild;
using mozilla::dom::PServiceWorkerRegistrationChild;
using mozilla::dom::RemoteWorkerChild;
using mozilla::dom::StorageDBChild;
using mozilla::dom::cache::PCacheChild;
using mozilla::dom::cache::PCacheStreamControlChild;
@ -250,6 +252,18 @@ bool BackgroundChildImpl::DeallocPBackgroundStorageChild(
return true;
}
already_AddRefed<PRemoteWorkerChild>
BackgroundChildImpl::AllocPRemoteWorkerChild(const RemoteWorkerData& aData) {
return MakeAndAddRef<RemoteWorkerChild>(aData);
}
IPCResult BackgroundChildImpl::RecvPRemoteWorkerConstructor(
PRemoteWorkerChild* aActor, const RemoteWorkerData& aData) {
dom::RemoteWorkerChild* actor = static_cast<dom::RemoteWorkerChild*>(aActor);
actor->ExecWorker(aData);
return IPC_OK();
}
dom::PSharedWorkerChild* BackgroundChildImpl::AllocPSharedWorkerChild(
const dom::RemoteWorkerData& aData, const uint64_t& aWindowID,
const dom::MessagePortIdentifier& aPortIdentifier) {

View File

@ -102,6 +102,12 @@ class BackgroundChildImpl : public PBackgroundChild {
virtual bool DeallocPFileCreatorChild(PFileCreatorChild* aActor) override;
already_AddRefed<mozilla::dom::PRemoteWorkerChild> AllocPRemoteWorkerChild(
const RemoteWorkerData& aData) override;
virtual mozilla::ipc::IPCResult RecvPRemoteWorkerConstructor(
PRemoteWorkerChild* aActor, const RemoteWorkerData& aData) override;
virtual mozilla::dom::PSharedWorkerChild* AllocPSharedWorkerChild(
const mozilla::dom::RemoteWorkerData& aData, const uint64_t& aWindowID,
const mozilla::dom::MessagePortIdentifier& aPortIdentifier) override;

View File

@ -157,12 +157,6 @@ class ParentImpl final : public BackgroundParentImpl {
bool mActorDestroyed;
public:
static already_AddRefed<nsISerialEventTarget> GetBackgroundThread() {
AssertIsInMainProcess();
THREADSAFETY_ASSERT(NS_IsMainThread() || IsOnBackgroundThread());
return do_AddRef(sBackgroundThread);
}
static bool IsOnBackgroundThread() {
return PR_GetCurrentThread() == sBackgroundPRThread;
}
@ -651,11 +645,6 @@ void AssertIsOnBackgroundThread() { ParentImpl::AssertIsOnBackgroundThread(); }
// BackgroundParent Public Methods
// -----------------------------------------------------------------------------
// static
already_AddRefed<nsISerialEventTarget> BackgroundParent::GetBackgroundThread() {
return ParentImpl::GetBackgroundThread();
}
// static
bool BackgroundParent::IsOtherProcessActor(
PBackgroundParent* aBackgroundActor) {

View File

@ -60,12 +60,6 @@ class BackgroundParent final {
mozilla::dom::ThreadsafeContentParentHandle;
public:
// Get the nsISerialEventTarget used to handle messages from BackgroundParent
// actors, if it is running.
//
// This function may only be called on the background or main thread.
static already_AddRefed<nsISerialEventTarget> GetBackgroundThread();
// This function allows the caller to determine if the given parent actor
// corresponds to a child actor from another process or a child actor from a
// different thread in the same process.

View File

@ -527,6 +527,27 @@ IPCResult BackgroundParentImpl::RecvPRemoteWorkerControllerConstructor(
return IPC_OK();
}
already_AddRefed<dom::PRemoteWorkerServiceParent>
BackgroundParentImpl::AllocPRemoteWorkerServiceParent() {
return MakeAndAddRef<dom::RemoteWorkerServiceParent>();
}
IPCResult BackgroundParentImpl::RecvPRemoteWorkerServiceConstructor(
PRemoteWorkerServiceParent* aActor) {
mozilla::dom::RemoteWorkerServiceParent* actor =
static_cast<mozilla::dom::RemoteWorkerServiceParent*>(aActor);
RefPtr<ThreadsafeContentParentHandle> parent =
BackgroundParent::GetContentParentHandle(this);
// If the ContentParent is null we are dealing with a same-process actor.
if (!parent) {
actor->Initialize(NOT_REMOTE_TYPE);
} else {
actor->Initialize(parent->GetRemoteType());
}
return IPC_OK();
}
mozilla::dom::PSharedWorkerParent*
BackgroundParentImpl::AllocPSharedWorkerParent(
const mozilla::dom::RemoteWorkerData& aData, const uint64_t& aWindowID,

View File

@ -169,6 +169,12 @@ class BackgroundParentImpl : public PBackgroundParent {
mozilla::dom::PRemoteWorkerControllerParent* aActor,
const mozilla::dom::RemoteWorkerData& aRemoteWorkerData) override;
already_AddRefed<PRemoteWorkerServiceParent> AllocPRemoteWorkerServiceParent()
override;
mozilla::ipc::IPCResult RecvPRemoteWorkerServiceConstructor(
PRemoteWorkerServiceParent* aActor) override;
mozilla::dom::PSharedWorkerParent* AllocPSharedWorkerParent(
const mozilla::dom::RemoteWorkerData& aData, const uint64_t& aWindowID,
const mozilla::dom::MessagePortIdentifier& aPortIdentifier) override;

View File

@ -27,6 +27,7 @@ include protocol PGamepadEventChannel;
include protocol PGamepadTestChannel;
include protocol PHttpBackgroundChannel;
include protocol PIdleScheduler;
include protocol PRemoteWorker;
include protocol PRemoteWorkerController;
include protocol PRemoteWorkerService;
include protocol PSharedWorker;
@ -106,7 +107,9 @@ sync protocol PBackground
manages PHttpBackgroundChannel;
manages PIdleScheduler;
manages PLockManager;
manages PRemoteWorker;
manages PRemoteWorkerController;
manages PRemoteWorkerService;
manages PSharedWorker;
manages PTemporaryIPCBlob;
manages PFileCreator;
@ -258,6 +261,8 @@ parent:
async PRemoteWorkerController(RemoteWorkerData aData);
async PRemoteWorkerService();
async PServiceWorkerContainer();
async PServiceWorkerRegistration(IPCServiceWorkerRegistrationDescriptor aDescriptor);
@ -284,6 +289,8 @@ parent:
child:
async PCache();
async PCacheStreamControl();
async PRemoteWorker(RemoteWorkerData data);
};
} // namespace ipc

View File

@ -253,7 +253,7 @@ nsresult nsLayoutStatics::Initialize() {
if (XRE_IsParentProcess()) {
// On content process we initialize these components when PContentChild is
// fully initialized.
mozilla::dom::RemoteWorkerService::InitializeParent();
mozilla::dom::RemoteWorkerService::Initialize();
}
ClearSiteData::Initialize();

View File

@ -2933,18 +2933,6 @@
#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.
# WARNING: This will exceed even process limits for the extension or
# privilegedAbout remote types, which may lead to unexpected breakage!
# Should only be used for testing.
- name: dom.ipc.disableContentProcessReuse
type: bool
value: false
mirror: always
# Process launch delay (in milliseconds).
- name: dom.ipc.processPrelaunch.delayMs
type: uint32_t

View File

@ -14,6 +14,7 @@
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { ComponentUtils } from "resource://gre/modules/ComponentUtils.sys.mjs";
import { TestUtils } from "resource://testing-common/TestUtils.sys.mjs";
const lazy = {};
@ -30,9 +31,33 @@ XPCOMUtils.defineLazyServiceGetters(lazy, {
],
});
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;
const DISABLE_CONTENT_PROCESS_REUSE_PREF = "dom.ipc.disableContentProcessReuse";
// 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.generateSingletonFactory(NewProcessSelector);
registrar.registerFactory(OUR_PROCESSSELECTOR_CID, "", null, selectorFactory);
const kAboutPageRegistrationContentScript =
"chrome://mochikit/content/tests/BrowserTestUtils/content-about-page-utils.js";
@ -203,11 +228,18 @@ export var BrowserTestUtils = {
let promises, tab;
try {
// If we're asked to force a new process, set the pref to disable process
// re-use while we insert this new tab.
if (options.forceNewProcess) {
// 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) {
Services.ppmm.releaseCachedProcesses();
Services.prefs.setBoolPref(DISABLE_CONTENT_PROCESS_REUSE_PREF, true);
registrar.registerFactory(
OUR_PROCESSSELECTOR_CID,
"",
PROCESSSELECTOR_CONTRACTID,
null
);
}
promises = [
@ -231,9 +263,14 @@ export var BrowserTestUtils = {
promises.push(BrowserTestUtils.browserStopped(tab.linkedBrowser));
}
} finally {
// Clear the pref once we're done, if needed.
if (options.forceNewProcess) {
Services.prefs.clearUserPref(DISABLE_CONTENT_PROCESS_REUSE_PREF);
// Restore the original process selector, if needed.
if (options.forceNewProcess && DEFAULT_PROCESSSELECTOR_CID) {
registrar.registerFactory(
DEFAULT_PROCESSSELECTOR_CID,
"",
PROCESSSELECTOR_CONTRACTID,
null
);
}
}
return Promise.all(promises).then(() => {

View File

@ -1499,6 +1499,15 @@ nsXULAppInfo::GetIsTextRecognitionSupported(bool* aResult) {
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::EnsureContentProcess() {
if (!XRE_IsParentProcess()) return NS_ERROR_NOT_AVAILABLE;
RefPtr<ContentParent> unused =
ContentParent::GetNewOrUsedBrowserProcess(DEFAULT_REMOTE_TYPE);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::InvalidateCachesOnRestart() {
nsCOMPtr<nsIFile> file;

View File

@ -63,7 +63,6 @@
#include "mozilla/AbstractThread.h"
#include "mozilla/FilePreferences.h"
#include "mozilla/IOInterposer.h"
#include "mozilla/NeverDestroyed.h"
#include "mozilla/ProcessType.h"
#include "mozilla/RDDProcessImpl.h"
#include "mozilla/ipc/UtilityProcessImpl.h"
@ -141,7 +140,6 @@ using mozilla::ipc::ProcessChild;
using mozilla::dom::ContentParent;
using mozilla::dom::ContentProcess;
using mozilla::dom::UniqueContentParentKeepAlive;
using mozilla::gmp::GMPProcessChild;
@ -741,25 +739,22 @@ void XRE_ShutdownChildProcess() {
}
namespace {
UniqueContentParentKeepAlive& TestShellContentParent() {
static NeverDestroyed<UniqueContentParentKeepAlive> sContentParent;
return *sContentParent;
}
ContentParent* gContentParent; // long-lived, manually refcounted
TestShellParent* GetOrCreateTestShellParent() {
if (!TestShellContentParent()) {
if (!gContentParent) {
// Use a "web" child process by default. File a bug if you don't like
// this and you're sure you wouldn't be better off writing a "browser"
// chrome mochitest where you can have multiple types of content
// processes.
TestShellContentParent() =
RefPtr<ContentParent> parent =
ContentParent::GetNewOrUsedBrowserProcess(DEFAULT_REMOTE_TYPE);
} else if (TestShellContentParent()->IsShuttingDown()) {
parent.forget(&gContentParent);
} else if (gContentParent->IsShuttingDown()) {
return nullptr;
}
TestShellParent* tsp = TestShellContentParent()->GetTestShellSingleton();
TestShellParent* tsp = gContentParent->GetTestShellSingleton();
if (!tsp) {
tsp = TestShellContentParent()->CreateTestShell();
tsp = gContentParent->CreateTestShell();
}
return tsp;
}
@ -789,15 +784,15 @@ bool XRE_SendTestShellCommand(JSContext* aCx, JSString* aCommand,
}
bool XRE_ShutdownTestShell() {
if (!TestShellContentParent()) {
if (!gContentParent) {
return true;
}
bool ret = true;
if (TestShellContentParent()->IsAlive()) {
ret = TestShellContentParent()->DestroyTestShell(
TestShellContentParent()->GetTestShellSingleton());
if (gContentParent->IsAlive()) {
ret = gContentParent->DestroyTestShell(
gContentParent->GetTestShellSingleton());
}
TestShellContentParent().reset();
NS_RELEASE(gContentParent);
return ret;
}

View File

@ -298,6 +298,15 @@ interface nsIXULRuntime : nsISupports
*/
void invalidateCachesOnRestart();
/**
* Starts a child process. This method is intented to pre-start a
* content child process so that when it is actually needed, it is
* ready to go.
*
* @throw NS_ERROR_NOT_AVAILABLE if not available.
*/
void ensureContentProcess();
/**
* Modification time of the profile lock before the profile was locked on
* this startup. Used to know the last time the profile was used and not