Source code

Revision control

Other Tools

/* -*- 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/DebugOnly.h"
#include "base/basictypes.h"
#include "base/shared_memory.h"
#include "ContentParent.h"
#include "ProcessUtils.h"
#include "BrowserParent.h"
#include "chrome/common/process_watcher.h"
#include "mozilla/Result.h"
#include "nsIBrowserDOMWindow.h"
#ifdef ACCESSIBILITY
# include "mozilla/a11y/PDocAccessible.h"
#endif
#include "GeckoProfiler.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
#endif
#include "GMPServiceParent.h"
#include "HandlerServiceParent.h"
#include "IHistory.h"
#if defined(XP_WIN) && defined(ACCESSIBILITY)
# include "mozilla/a11y/AccessibleWrap.h"
# include "mozilla/a11y/Compatibility.h"
#endif
#include <utility>
#include "BrowserParent.h"
#include "ContentProcessManager.h"
#include "Geolocation.h"
#include "GfxInfoBase.h"
#include "MMPrinter.h"
#include "PreallocatedProcessManager.h"
#include "ProcessPriorityManager.h"
#include "SandboxHal.h"
#include "SourceSurfaceRawData.h"
#include "URIUtils.h"
#include "gfxPlatform.h"
#include "gfxPlatformFontList.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/ContentBlocking.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/BenchmarkStorageParent.h"
#include "mozilla/ContentBlockingUserInteraction.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Components.h"
#include "mozilla/DataStorage.h"
#ifdef MOZ_GLEAN
# include "mozilla/FOGIPC.h"
#endif
#include "mozilla/GlobalStyleSheetCache.h"
#include "mozilla/HangDetails.h"
#include "mozilla/LoginReputationIPC.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/PerformanceMetricsCollector.h"
#include "mozilla/Preferences.h"
#include "mozilla/PresShell.h"
#include "mozilla/ProcessHangMonitor.h"
#include "mozilla/ProcessHangMonitorIPC.h"
#include "mozilla/RDDProcessManager.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/ScriptPreloader.h"
#include "mozilla/Services.h"
#include "mozilla/Sprintf.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TelemetryIPC.h"
#include "mozilla/Unused.h"
#include "mozilla/WebBrowserPersistDocumentParent.h"
#include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
#include "mozilla/docshell/OfflineCacheUpdateParent.h"
#include "mozilla/dom/BlobURLProtocolHandler.h"
#include "mozilla/dom/BrowserHost.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/CancelContentJSOptionsBinding.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ClientManager.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ExternalHelperAppParent.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileSystemSecurity.h"
#include "mozilla/dom/GeolocationBinding.h"
#include "mozilla/dom/GeolocationPositionError.h"
#include "mozilla/dom/GetFilesHelper.h"
#include "mozilla/dom/IPCBlobUtils.h"
#include "mozilla/dom/JSActorService.h"
#include "mozilla/dom/JSProcessActorBinding.h"
#include "mozilla/dom/LocalStorageCommon.h"
#include "mozilla/dom/MediaController.h"
#include "mozilla/dom/MemoryReportRequest.h"
#include "mozilla/dom/MediaStatusManager.h"
#include "mozilla/dom/Notification.h"
#include "mozilla/dom/PContentPermissionRequestParent.h"
#include "mozilla/dom/PCycleCollectWithLogsParent.h"
#include "mozilla/dom/PPresentationParent.h"
#include "mozilla/dom/ParentProcessMessageManager.h"
#include "mozilla/dom/Permissions.h"
#include "mozilla/dom/PresentationParent.h"
#include "mozilla/dom/ProcessMessageManager.h"
#include "mozilla/dom/PushNotifier.h"
#include "mozilla/dom/ServiceWorkerManager.h"
#include "mozilla/dom/ServiceWorkerRegistrar.h"
#include "mozilla/dom/ServiceWorkerUtils.h"
#include "mozilla/dom/SessionHistoryEntry.h"
#include "mozilla/dom/SessionStorageManager.h"
#include "mozilla/dom/StorageIPC.h"
#include "mozilla/dom/URLClassifierParent.h"
#include "mozilla/dom/WakeLock.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/ipc/SharedMap.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
#include "mozilla/dom/nsMixedContentBlocker.h"
#include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/quota/QuotaManagerService.h"
#include "mozilla/embedding/printingui/PrintingParent.h"
#include "mozilla/extensions/StreamFilterParent.h"
#include "mozilla/gfx/GPUProcessManager.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/hal_sandbox/PHalParent.h"
#include "mozilla/intl/LocaleService.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/ipc/CrashReporterHost.h"
#include "mozilla/ipc/FileDescriptorSetParent.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/ipc/IPCStreamAlloc.h"
#include "mozilla/ipc/IPCStreamDestination.h"
#include "mozilla/ipc/IPCStreamSource.h"
#include "mozilla/ipc/IPCStreamUtils.h"
#include "mozilla/ipc/PChildToParentStreamParent.h"
#include "mozilla/ipc/TestShellParent.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/ImageBridgeParent.h"
#include "mozilla/layers/LayerTreeOwnerTracker.h"
#include "mozilla/layers/PAPZParent.h"
#include "mozilla/loader/ScriptCacheActors.h"
#include "mozilla/media/MediaParent.h"
#include "mozilla/mozSpellChecker.h"
#include "mozilla/net/CookieServiceParent.h"
#include "mozilla/net/NeckoMessageUtils.h"
#include "mozilla/net/NeckoParent.h"
#include "mozilla/net/PCookieServiceParent.h"
#include "mozilla/plugins/PluginBridge.h"
#include "mozilla/RemoteLazyInputStreamParent.h"
#include "mozilla/widget/ScreenManager.h"
#include "nsAnonymousTemporaryFile.h"
#include "nsAppRunner.h"
#include "nsCExternalHandlerService.h"
#include "nsCOMPtr.h"
#include "nsChromeRegistryChrome.h"
#include "nsConsoleMessage.h"
#include "nsConsoleService.h"
#include "nsContentPermissionHelper.h"
#include "nsContentUtils.h"
#include "nsDebugImpl.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDocShell.h"
#include "nsEmbedCID.h"
#include "nsFocusManager.h"
#include "nsFrameLoader.h"
#include "nsFrameMessageManager.h"
#include "nsHashPropertyBag.h"
#include "nsHyphenationManager.h"
#include "nsIAlertsService.h"
#include "nsIAppStartup.h"
#include "nsIAppWindow.h"
#include "nsIAsyncInputStream.h"
#include "nsIBidiKeyboard.h"
#include "nsICaptivePortalService.h"
#include "nsICertOverrideService.h"
#include "nsIClipboard.h"
#include "nsIContentProcess.h"
#include "nsIContentSecurityPolicy.h"
#include "nsICookie.h"
#include "nsICrashService.h"
#include "nsICycleCollectorListener.h"
#include "nsIDOMChromeWindow.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIDragService.h"
#include "nsIExternalProtocolService.h"
#include "nsIGfxInfo.h"
#include "nsIUserIdleService.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsILocalStorageManager.h"
#include "nsIMemoryInfoDumper.h"
#include "nsIMemoryReporter.h"
#include "nsIMozBrowserFrame.h"
#include "nsINetworkLinkService.h"
#include "nsIObserverService.h"
#include "nsIParentChannel.h"
#include "nsIScriptError.h"
#include "nsIScriptSecurityManager.h"
#include "nsIServiceWorkerManager.h"
#include "nsISiteSecurityService.h"
#include "nsISound.h"
#include "nsIStringBundle.h"
#include "nsITimer.h"
#include "nsIURL.h"
#include "nsIWebBrowserChrome.h"
#include "nsIX509Cert.h"
#include "nsIXULRuntime.h"
#include "nsMemoryInfoDumper.h"
#include "nsMemoryReporterManager.h"
#include "nsOpenURIInFrameParams.h"
#include "nsPIWindowWatcher.h"
#include "nsPluginHost.h"
#include "nsPluginTags.h"
#include "nsQueryObject.h"
#include "nsReadableUtils.h"
#include "nsSHistory.h"
#include "nsScriptError.h"
#include "nsSerializationHelper.h"
#include "nsServiceManagerUtils.h"
#include "nsStreamUtils.h"
#include "nsStyleSheetService.h"
#include "nsThread.h"
#include "nsThreadUtils.h"
#include "nsWidgetsCID.h"
#include "nsWindowWatcher.h"
#include "prio.h"
#include "private/pprio.h"
#include "xpcpublic.h"
#include "nsOpenWindowInfo.h"
#ifdef MOZ_WEBRTC
# include "jsapi/WebrtcGlobalParent.h"
#endif
#if defined(XP_MACOSX)
# include "nsMacUtilsImpl.h"
#endif
#if defined(ANDROID) || defined(LINUX)
# include "nsSystemInfo.h"
#endif
#if defined(XP_LINUX)
# include "mozilla/Hal.h"
#endif
#ifdef ANDROID
# include "gfxAndroidPlatform.h"
#endif
#include "mozilla/PermissionManager.h"
#ifdef MOZ_WIDGET_ANDROID
# include "AndroidBridge.h"
# include "mozilla/java/GeckoProcessManagerWrappers.h"
# include "mozilla/java/GeckoProcessTypeWrappers.h"
#endif
#ifdef MOZ_WIDGET_GTK
# include <gdk/gdk.h>
#endif
#include "mozilla/RemoteSpellCheckEngineParent.h"
#include "Crypto.h"
#ifdef MOZ_WEBSPEECH
# include "mozilla/dom/SpeechSynthesisParent.h"
#endif
#if defined(MOZ_SANDBOX)
# include "mozilla/SandboxSettings.h"
# if defined(XP_LINUX)
# include "mozilla/SandboxInfo.h"
# include "mozilla/SandboxBroker.h"
# include "mozilla/SandboxBrokerPolicyFactory.h"
# endif
# if defined(XP_MACOSX)
# include "mozilla/Sandbox.h"
# endif
#endif
#ifdef XP_WIN
# include "mozilla/audio/AudioNotificationSender.h"
# include "mozilla/widget/AudioSession.h"
# include "mozilla/widget/WinContentSystemParameters.h"
# include "mozilla/WinDllServices.h"
#endif
#ifdef ACCESSIBILITY
# include "nsAccessibilityService.h"
#endif
#ifdef MOZ_GECKO_PROFILER
# include "nsIProfiler.h"
# include "ProfilerParent.h"
#endif
#ifdef MOZ_CODE_COVERAGE
# include "mozilla/CodeCoverageHandler.h"
#endif
// For VP9Benchmark::sBenchmarkFpsPref
#include "Benchmark.h"
// XXX need another bug to move this to a common header.
#ifdef DISABLE_ASSERTS_FOR_FUZZING
# define ASSERT_UNLESS_FUZZING(...) \
do { \
} while (0)
#else
# define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
#endif
static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
using base::KillProcess;
using namespace CrashReporter;
using namespace mozilla::dom::power;
using namespace mozilla::media;
using namespace mozilla::embedding;
using namespace mozilla::gfx;
using namespace mozilla::gmp;
using namespace mozilla::hal;
using namespace mozilla::ipc;
using namespace mozilla::intl;
using namespace mozilla::layers;
using namespace mozilla::layout;
using namespace mozilla::net;
using namespace mozilla::psm;
using namespace mozilla::widget;
using mozilla::loader::PScriptCacheParent;
using mozilla::Telemetry::ProcessID;
// XXX Workaround for bug 986973 to maintain the existing broken semantics
template <>
struct nsIConsoleService::COMTypeInfo<nsConsoleService, void> {
static const nsIID kIID;
};
const nsIID nsIConsoleService::COMTypeInfo<nsConsoleService, void>::kIID =
NS_ICONSOLESERVICE_IID;
namespace mozilla {
namespace CubebUtils {
extern FileDescriptor CreateAudioIPCConnection();
}
namespace dom {
LazyLogModule gProcessLog("Process");
/* static */
LogModule* ContentParent::GetLog() { return gProcessLog; }
#define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
#define NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC "ipc:network:set-connectivity"
// IPC receiver for remote GC/CC logging.
class CycleCollectWithLogsParent final : public PCycleCollectWithLogsParent {
public:
MOZ_COUNTED_DTOR(CycleCollectWithLogsParent)
static bool AllocAndSendConstructor(ContentParent* aManager,
bool aDumpAllTraces,
nsICycleCollectorLogSink* aSink,
nsIDumpGCAndCCLogsCallback* aCallback) {
CycleCollectWithLogsParent* actor;
FILE* gcLog;
FILE* ccLog;
nsresult rv;
actor = new CycleCollectWithLogsParent(aSink, aCallback);
rv = actor->mSink->Open(&gcLog, &ccLog);
if (NS_WARN_IF(NS_FAILED(rv))) {
delete actor;
return false;
}
return aManager->SendPCycleCollectWithLogsConstructor(
actor, aDumpAllTraces, FILEToFileDescriptor(gcLog),
FILEToFileDescriptor(ccLog));
}
private:
virtual mozilla::ipc::IPCResult RecvCloseGCLog() override {
Unused << mSink->CloseGCLog();
return IPC_OK();
}
virtual mozilla::ipc::IPCResult RecvCloseCCLog() override {
Unused << mSink->CloseCCLog();
return IPC_OK();
}
virtual mozilla::ipc::IPCResult Recv__delete__() override {
// Report completion to mCallback only on successful
// completion of the protocol.
nsCOMPtr<nsIFile> gcLog, ccLog;
mSink->GetGcLog(getter_AddRefs(gcLog));
mSink->GetCcLog(getter_AddRefs(ccLog));
Unused << mCallback->OnDump(gcLog, ccLog, /* parent = */ false);
return IPC_OK();
}
virtual void ActorDestroy(ActorDestroyReason aReason) override {
// If the actor is unexpectedly destroyed, we deliberately
// don't call Close[GC]CLog on the sink, because the logs may
// be incomplete. See also the nsCycleCollectorLogSinkToFile
// implementaiton of those methods, and its destructor.
}
CycleCollectWithLogsParent(nsICycleCollectorLogSink* aSink,
nsIDumpGCAndCCLogsCallback* aCallback)
: mSink(aSink), mCallback(aCallback) {
MOZ_COUNT_CTOR(CycleCollectWithLogsParent);
}
nsCOMPtr<nsICycleCollectorLogSink> mSink;
nsCOMPtr<nsIDumpGCAndCCLogsCallback> mCallback;
};
// A memory reporter for ContentParent objects themselves.
class ContentParentsMemoryReporter final : public nsIMemoryReporter {
~ContentParentsMemoryReporter() = default;
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMEMORYREPORTER
};
NS_IMPL_ISUPPORTS(ContentParentsMemoryReporter, nsIMemoryReporter)
NS_IMETHODIMP
ContentParentsMemoryReporter::CollectReports(
nsIHandleReportCallback* aHandleReport, nsISupports* aData,
bool aAnonymize) {
AutoTArray<ContentParent*, 16> cps;
ContentParent::GetAllEvenIfDead(cps);
for (uint32_t i = 0; i < cps.Length(); i++) {
ContentParent* cp = cps[i];
MessageChannel* channel = cp->GetIPCChannel();
nsString friendlyName;
cp->FriendlyName(friendlyName, aAnonymize);
cp->AddRef();
nsrefcnt refcnt = cp->Release();
const char* channelStr = "no channel";
uint32_t numQueuedMessages = 0;
if (channel) {
if (channel->Unsound_IsClosed()) {
channelStr = "closed channel";
} else {
channelStr = "open channel";
}
numQueuedMessages = channel->Unsound_NumQueuedMessages();
}
nsPrintfCString path(
"queued-ipc-messages/content-parent"
"(%s, pid=%d, %s, 0x%p, refcnt=%" PRIuPTR ")",
NS_ConvertUTF16toUTF8(friendlyName).get(), cp->Pid(), channelStr,
static_cast<nsIObserver*>(cp), refcnt);
constexpr auto desc =
"The number of unset IPC messages held in this ContentParent's "
"channel. A large value here might indicate that we're leaking "
"messages. Similarly, a ContentParent object for a process that's no "
"longer running could indicate that we're leaking ContentParents."_ns;
aHandleReport->Callback(/* process */ ""_ns, path, KIND_OTHER, UNITS_COUNT,
numQueuedMessages, desc, aData);
}
return NS_OK;
}
// A hashtable (by type) of processes/ContentParents. This includes
// processes that are in the Preallocator cache (which would be type
// 'prealloc'), and recycled processes ('web' and in the future
// eTLD+1-locked) processes).
nsClassHashtable<nsCStringHashKey, nsTArray<ContentParent*>>*
ContentParent::sBrowserContentParents;
namespace {
uint64_t ComputeLoadedOriginHash(nsIPrincipal* aPrincipal) {
uint32_t originNoSuffix =
BasePrincipal::Cast(aPrincipal)->GetOriginNoSuffixHash();
uint32_t originSuffix =
BasePrincipal::Cast(aPrincipal)->GetOriginSuffixHash();
return ((uint64_t)originNoSuffix) << 32 | originSuffix;
}
class ScriptableCPInfo final : public nsIContentProcessInfo {
public:
explicit ScriptableCPInfo(ContentParent* aParent) : mContentParent(aParent) {
MOZ_ASSERT(mContentParent);
}
NS_DECL_ISUPPORTS
NS_DECL_NSICONTENTPROCESSINFO
void ProcessDied() { mContentParent = nullptr; }
private:
~ScriptableCPInfo() { MOZ_ASSERT(!mContentParent, "must call ProcessDied"); }
ContentParent* mContentParent;
};
NS_IMPL_ISUPPORTS(ScriptableCPInfo, nsIContentProcessInfo)
NS_IMETHODIMP
ScriptableCPInfo::GetIsAlive(bool* aIsAlive) {
*aIsAlive = mContentParent != nullptr;
return NS_OK;
}
NS_IMETHODIMP
ScriptableCPInfo::GetProcessId(int32_t* aPID) {
if (!mContentParent) {
*aPID = -1;
return NS_ERROR_NOT_INITIALIZED;
}
*aPID = mContentParent->Pid();
if (*aPID == -1) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
ScriptableCPInfo::GetTabCount(int32_t* aTabCount) {
if (!mContentParent) {
return NS_ERROR_NOT_INITIALIZED;
}
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
*aTabCount = cpm->GetBrowserParentCountByProcessId(mContentParent->ChildID());
return NS_OK;
}
NS_IMETHODIMP
ScriptableCPInfo::GetMessageManager(nsISupports** aMessenger) {
*aMessenger = nullptr;
if (!mContentParent) {
return NS_ERROR_NOT_INITIALIZED;
}
RefPtr<ProcessMessageManager> manager = mContentParent->GetMessageManager();
manager.forget(aMessenger);
return NS_OK;
}
ProcessID GetTelemetryProcessID(const nsACString& remoteType) {
// OOP WebExtensions run in a content process.
// For Telemetry though we want to break out collected data from the
// WebExtensions process into a separate bucket, to make sure we can analyze
// it separately and avoid skewing normal content process metrics.
return remoteType == EXTENSION_REMOTE_TYPE ? ProcessID::Extension
: ProcessID::Content;
}
} // anonymous namespace
UniquePtr<nsDataHashtable<nsUint32HashKey, ContentParent*>>
ContentParent::sJSPluginContentParents;
UniquePtr<nsTArray<ContentParent*>> ContentParent::sPrivateContent;
UniquePtr<LinkedList<ContentParent>> ContentParent::sContentParents;
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
UniquePtr<SandboxBrokerPolicyFactory>
ContentParent::sSandboxBrokerPolicyFactory;
#endif
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
UniquePtr<std::vector<std::string>> ContentParent::sMacSandboxParams;
#endif
// Whether a private docshell has been seen before.
static bool sHasSeenPrivateDocShell = false;
// This is true when subprocess launching is enabled. This is the
// case between StartUp() and ShutDown().
static bool sCanLaunchSubprocesses;
// Set to true when the first content process gets created.
static bool sCreatedFirstContentProcess = false;
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
// True when we're running the process selection code, and do not expect to
// enter code paths where processes may die.
static bool sInProcessSelector = false;
#endif
// The first content child has ID 1, so the chrome process can have ID 0.
static uint64_t gContentChildID = 1;
static const char* sObserverTopics[] = {
NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC,
NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC,
NS_IPC_CAPTIVE_PORTAL_SET_STATE,
"application-background",
"application-foreground",
"memory-pressure",
"child-gc-request",
"child-cc-request",
"child-mmu-request",
"child-ghost-request",
"last-pb-context-exited",
"file-watcher-update",
#ifdef ACCESSIBILITY
"a11y-init-or-shutdown",
#endif
"cacheservice:empty-cache",
"intl:app-locales-changed",
"intl:requested-locales-changed",
"cookie-changed",
"private-cookie-changed",
NS_NETWORK_LINK_TYPE_TOPIC,
};
// PreallocateProcess is called by the PreallocatedProcessManager.
// ContentParent then takes this process back within GetNewOrUsedBrowserProcess.
/*static*/ RefPtr<ContentParent::LaunchPromise>
ContentParent::PreallocateProcess() {
RefPtr<ContentParent> process = new ContentParent(PREALLOC_REMOTE_TYPE);
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("Preallocating process of type prealloc"));
return process->LaunchSubprocessAsync(PROCESS_PRIORITY_PREALLOC);
}
/*static*/
void ContentParent::StartUp() {
// We could launch sub processes from content process
// FIXME Bug 1023701 - Stop using ContentParent static methods in
// child process
sCanLaunchSubprocesses = true;
if (!XRE_IsParentProcess()) {
return;
}
// Note: This reporter measures all ContentParents.
RegisterStrongMemoryReporter(new ContentParentsMemoryReporter());
BackgroundChild::Startup();
ClientManager::Startup();
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
sSandboxBrokerPolicyFactory = MakeUnique<SandboxBrokerPolicyFactory>();
#endif
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
sMacSandboxParams = MakeUnique<std::vector<std::string>>();
#endif
}
/*static*/
void ContentParent::ShutDown() {
// No-op for now. We rely on normal process shutdown and
// ClearOnShutdown() to clean up our state.
sCanLaunchSubprocesses = false;
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
sSandboxBrokerPolicyFactory = nullptr;
#endif
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
sMacSandboxParams = nullptr;
#endif
}
/*static*/
uint32_t ContentParent::GetPoolSize(const nsACString& aContentProcessType) {
if (!sBrowserContentParents) {
return 0;
}
nsTArray<ContentParent*>* parents =
sBrowserContentParents->Get(aContentProcessType);
return parents ? parents->Length() : 0;
}
/*static*/ nsTArray<ContentParent*>& ContentParent::GetOrCreatePool(
const nsACString& aContentProcessType) {
if (!sBrowserContentParents) {
sBrowserContentParents =
new nsClassHashtable<nsCStringHashKey, nsTArray<ContentParent*>>;
}
return *sBrowserContentParents->LookupOrAdd(aContentProcessType);
}
const nsDependentCSubstring RemoteTypePrefix(
const nsACString& aContentProcessType) {
// The suffix after a `=` in a remoteType is dynamic, and used to control the
// process pool to use.
int32_t equalIdx = aContentProcessType.FindChar(L'=');
if (equalIdx == kNotFound) {
equalIdx = aContentProcessType.Length();
}
return StringHead(aContentProcessType, equalIdx);
}
bool IsWebRemoteType(const nsACString& aContentProcessType) {
// Note: matches webIsolated as well as web (and webLargeAllocation, and
// webCOOP+COEP)
return StringBeginsWith(aContentProcessType, DEFAULT_REMOTE_TYPE);
}
bool IsWebCoopCoepRemoteType(const nsACString& aContentProcessType) {
return StringBeginsWith(aContentProcessType,
WITH_COOP_COEP_REMOTE_TYPE_PREFIX);
}
bool IsPriviligedMozillaRemoteType(const nsACString& aContentProcessType) {
return aContentProcessType == PRIVILEGEDMOZILLA_REMOTE_TYPE;
}
/*static*/
uint32_t ContentParent::GetMaxProcessCount(
const nsACString& aContentProcessType) {
// Max process count is based only on the prefix.
const nsDependentCSubstring processTypePrefix =
RemoteTypePrefix(aContentProcessType);
// Check for the default remote type of "web", as it uses different prefs.
if (processTypePrefix == DEFAULT_REMOTE_TYPE) {
return GetMaxWebProcessCount();
}
// Read the pref controling this remote type. `dom.ipc.processCount` is not
// used as a fallback, as it is intended to control the number of "web"
// content processes, checked in `mozilla::GetMaxWebProcessCount()`.
nsAutoCString processCountPref("dom.ipc.processCount.");
processCountPref.Append(processTypePrefix);
int32_t maxContentParents = Preferences::GetInt(processCountPref.get(), 1);
if (maxContentParents < 1) {
maxContentParents = 1;
}
return static_cast<uint32_t>(maxContentParents);
}
/*static*/
bool ContentParent::IsMaxProcessCountReached(
const nsACString& aContentProcessType) {
return GetPoolSize(aContentProcessType) >=
GetMaxProcessCount(aContentProcessType);
}
// Really more ReleaseUnneededProcesses()
/*static*/
void ContentParent::ReleaseCachedProcesses() {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("ReleaseCachedProcesses:"));
if (!sBrowserContentParents) {
return;
}
#ifdef DEBUG
int num = 0;
for (auto iter = sBrowserContentParents->Iter(); !iter.Done(); iter.Next()) {
nsTArray<ContentParent*>* contentParents = iter.Data().get();
num += contentParents->Length();
for (auto* cp : *contentParents) {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("%s: %zu processes", cp->mRemoteType.get(),
contentParents->Length()));
break;
}
}
#endif
// We process the toRelease array outside of the iteration to avoid modifying
// the list (via RemoveFromList()) while we're iterating it.
nsTArray<ContentParent*> toRelease;
for (auto iter = sBrowserContentParents->Iter(); !iter.Done(); iter.Next()) {
nsTArray<ContentParent*>* contentParents = iter.Data().get();
// Shutting down these processes will change the array so let's use another
// array for the removal.
for (auto* cp : *contentParents) {
if (cp->ManagedPBrowserParent().Count() == 0 &&
!cp->HasActiveWorkerOrJSPlugin() &&
cp->mRemoteType == DEFAULT_REMOTE_TYPE) {
toRelease.AppendElement(cp);
} else {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
(" Skipping %p (%s), count %d, HasActiveWorkerOrJSPlugin %d",
cp, cp->mRemoteType.get(), cp->ManagedPBrowserParent().Count(),
cp->HasActiveWorkerOrJSPlugin()));
}
}
}
for (auto* cp : toRelease) {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
(" Shutdown %p (%s)", cp, cp->mRemoteType.get()));
PreallocatedProcessManager::Erase(cp);
// Start a soft shutdown.
cp->ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
// Make sure we don't select this process for new tabs.
cp->MarkAsDead();
// Make sure that this process is no longer accessible from JS by its
// message manager.
cp->ShutDownMessageManager();
}
}
/*static*/
already_AddRefed<ContentParent> ContentParent::MinTabSelect(
const nsTArray<ContentParent*>& aContentParents,
int32_t aMaxContentParents) {
uint32_t maxSelectable =
std::min(static_cast<uint32_t>(aContentParents.Length()),
static_cast<uint32_t>(aMaxContentParents));
uint32_t min = INT_MAX;
RefPtr<ContentParent> candidate;
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
for (uint32_t i = 0; i < maxSelectable; i++) {
ContentParent* p = aContentParents[i];
MOZ_DIAGNOSTIC_ASSERT(!p->IsDead());
MOZ_DIAGNOSTIC_ASSERT(!p->mShutdownPending);
uint32_t tabCount = cpm->GetBrowserParentCountByProcessId(p->ChildID());
if (tabCount < min) {
candidate = p;
min = tabCount;
}
}
// If all current processes have at least one tab and we have not yet reached
// the maximum, use a new process.
if (min > 0 &&
aContentParents.Length() < static_cast<uint32_t>(aMaxContentParents)) {
return nullptr;
}
// Otherwise we return candidate.
return candidate.forget();
}
/*static*/
already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
const nsACString& aRemoteType, nsTArray<ContentParent*>& aContentParents,
uint32_t aMaxContentParents, bool aPreferUsed) {
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
AutoRestore ar(sInProcessSelector);
sInProcessSelector = true;
#endif
uint32_t numberOfParents = aContentParents.Length();
nsTArray<RefPtr<nsIContentProcessInfo>> infos(numberOfParents);
for (auto* cp : aContentParents) {
infos.AppendElement(cp->mScriptableHelper);
}
if (aPreferUsed && numberOfParents) {
// For the preloaded browser we don't want to create a new process but
// reuse an existing one.
aMaxContentParents = numberOfParents;
}
nsCOMPtr<nsIContentProcessProvider> cpp =
do_GetService("@mozilla.org/ipc/processselector;1");
int32_t index;
if (cpp && NS_SUCCEEDED(cpp->ProvideProcess(aRemoteType, infos,
aMaxContentParents, &index))) {
// If the provider returned an existing ContentParent, use that one.
if (0 <= index && static_cast<uint32_t>(index) <= aMaxContentParents) {
RefPtr<ContentParent> retval = aContentParents[index];
#ifdef MOZ_GECKO_PROFILER
if (profiler_thread_is_being_profiled()) {
nsPrintfCString marker("Reused process %u",
(unsigned int)retval->ChildID());
TimeStamp now = TimeStamp::Now();
PROFILER_ADD_MARKER_WITH_PAYLOAD("Process", DOM, TextMarkerPayload,
(marker, now, now));
}
#endif
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("GetUsedProcess: Reused process %p (%u) for %s", retval.get(),
(unsigned int)retval->ChildID(),
PromiseFlatCString(aRemoteType).get()));
retval->AssertAlive();
return retval.forget();
}
} else {
// If there was a problem with the JS chooser, fall back to a random
// selection.
NS_WARNING("nsIContentProcessProvider failed to return a process");
RefPtr<ContentParent> random;
if ((random = MinTabSelect(aContentParents, aMaxContentParents))) {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("GetUsedProcess: Reused random process %p (%d) for %s",
random.get(), (unsigned int)random->ChildID(),
PromiseFlatCString(aRemoteType).get()));
random->AssertAlive();
return random.forget();
}
}
// Try to take the preallocated process except for certain remote types.
// The preallocated process manager might not had the chance yet to release
// the process after a very recent ShutDownProcess, let's make sure we don't
// try to reuse a process that is being shut down.
RefPtr<ContentParent> p;
bool preallocated = false;
if (aRemoteType != FILE_REMOTE_TYPE &&
aRemoteType != EXTENSION_REMOTE_TYPE && // Bug 1638119
(p = PreallocatedProcessManager::Take(aRemoteType)) &&
!p->mShutdownPending) {
p->AssertAlive();
// p may be a preallocated process, or (if not PREALLOC_REMOTE_TYPE)
// a previously-used process that's being recycled. Currently this is
// only done for short-duration web (DEFAULT_REMOTE_TYPE) processes
preallocated = p->mRemoteType == PREALLOC_REMOTE_TYPE;
// For pre-allocated process we have not set the opener yet.
#ifdef MOZ_GECKO_PROFILER
if (profiler_thread_is_being_profiled()) {
nsPrintfCString marker("Assigned %s process %u",
preallocated ? "preallocated" : "reused web",
(unsigned int)p->ChildID());
TimeStamp now = TimeStamp::Now();
PROFILER_ADD_MARKER_WITH_PAYLOAD("Process", DOM, TextMarkerPayload,
(marker, now, now));
}
#endif
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("Adopted %s process %p for type %s",
preallocated ? "preallocated" : "reused web", p.get(),
PromiseFlatCString(aRemoteType).get()));
p->mActivateTS = TimeStamp::Now();
p->AddToPool(aContentParents);
if (preallocated) {
p->mRemoteType.Assign(aRemoteType);
// Specialize this process for the appropriate eTLD+1
Unused << p->SendRemoteType(p->mRemoteType);
nsCOMPtr<nsIObserverService> obs =
mozilla::services::GetObserverService();
if (obs) {
nsAutoString cpId;
cpId.AppendInt(static_cast<uint64_t>(p->ChildID()));
obs->NotifyObservers(static_cast<nsIObserver*>(p), "process-type-set",
cpId.get());
p->AssertAlive();
}
} else {
// we only allow "web" to "web" for security reasons
MOZ_RELEASE_ASSERT(p->mRemoteType == DEFAULT_REMOTE_TYPE &&
aRemoteType == DEFAULT_REMOTE_TYPE);
}
return p.forget();
}
return nullptr;
}
/*static*/
already_AddRefed<ContentParent>
ContentParent::GetNewOrUsedLaunchingBrowserProcess(
const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
ProcessPriority aPriority, bool aPreferUsed) {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("GetNewOrUsedProcess for type %s",
PromiseFlatCString(aRemoteType).get()));
// If we have an existing host process attached to this BrowsingContextGroup,
// always return it, as we can never have multiple host processes within a
// single BrowsingContextGroup.
RefPtr<ContentParent> contentParent;
if (aGroup) {
contentParent = aGroup->GetHostProcess(aRemoteType);
if (contentParent) {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("GetNewOrUsedProcess: Existing host process %p (launching %d)",
contentParent.get(), contentParent->IsLaunching()));
contentParent->AssertAlive();
return contentParent.forget();
}
}
nsTArray<ContentParent*>& contentParents = GetOrCreatePool(aRemoteType);
uint32_t maxContentParents = GetMaxProcessCount(aRemoteType);
// We never want to re-use Large-Allocation processes.
if (aRemoteType == LARGE_ALLOCATION_REMOTE_TYPE &&
contentParents.Length() >= maxContentParents) {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("GetNewOrUsedProcess: returning Large Used process"));
return GetNewOrUsedLaunchingBrowserProcess(DEFAULT_REMOTE_TYPE, aGroup,
aPriority,
/*aPreferUsed =*/false);
}
// Let's try and reuse an existing process.
contentParent = GetUsedBrowserProcess(aRemoteType, contentParents,
maxContentParents, aPreferUsed);
if (contentParent) {
// We have located a process. It may not have finished initializing,
// this will be for the caller to handle.
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("GetNewOrUsedProcess: Used process %p (launching %d)",
contentParent.get(), contentParent->IsLaunching()));
contentParent->AssertAlive();
if (aGroup) {
aGroup->EnsureHostProcess(contentParent);
}
return contentParent.forget();
}
// No reusable process. Let's create and launch one.
// The life cycle will be set to `LifecycleState::LAUNCHING`.
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("Launching new process immediately for type %s",
PromiseFlatCString(aRemoteType).get()));
contentParent = new ContentParent(aRemoteType);
if (!contentParent->BeginSubprocessLaunch(aPriority)) {
// Launch aborted because of shutdown. Bailout.
contentParent->LaunchSubprocessReject();
return nullptr;
}
// Store this process for future reuse.
contentParent->AddToPool(contentParents);
// Until the new process is ready let's not allow to start up any
// preallocated processes. The blocker will be removed once we receive
// the first idle message.
contentParent->mIsAPreallocBlocker = true;
PreallocatedProcessManager::AddBlocker(aRemoteType, contentParent);
MOZ_ASSERT(contentParent->IsLaunching());
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("GetNewOrUsedProcess: new process %p", contentParent.get()));
contentParent->AssertAlive();
if (aGroup) {
aGroup->EnsureHostProcess(contentParent);
}
return contentParent.forget();
}
/*static*/
RefPtr<ContentParent::LaunchPromise>
ContentParent::GetNewOrUsedBrowserProcessAsync(const nsACString& aRemoteType,
BrowsingContextGroup* aGroup,
ProcessPriority aPriority,
bool aPreferUsed) {
// Obtain a `ContentParent` launched asynchronously.
RefPtr<ContentParent> contentParent = GetNewOrUsedLaunchingBrowserProcess(
aRemoteType, aGroup, aPriority, aPreferUsed);
if (!contentParent) {
// In case of launch error, stop here.
return LaunchPromise::CreateAndReject(LaunchError(), __func__);
}
return contentParent->WaitForLaunchAsync(aPriority);
}
/*static*/
already_AddRefed<ContentParent> ContentParent::GetNewOrUsedBrowserProcess(
const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
ProcessPriority aPriority, bool aPreferUsed) {
RefPtr<ContentParent> contentParent = GetNewOrUsedLaunchingBrowserProcess(
aRemoteType, aGroup, aPriority, aPreferUsed);
if (!contentParent || !contentParent->WaitForLaunchSync(aPriority)) {
// In case of launch error, stop here.
return nullptr;
}
return contentParent.forget();
}
RefPtr<ContentParent::LaunchPromise> ContentParent::WaitForLaunchAsync(
ProcessPriority aPriority) {
MOZ_DIAGNOSTIC_ASSERT(!IsDead());
if (!IsLaunching()) {
return LaunchPromise::CreateAndResolve(this, __func__);
}
// We've started an async content process launch.
Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 0);
// We have located a process that hasn't finished initializing. Let's race
// against whoever launched it (and whoever else is already racing). Once
// the race is complete, the winner will finish the initialization.
return mSubprocess->WhenProcessHandleReady()->Then(
GetCurrentSerialEventTarget(), __func__,
// On resolve.
[self = RefPtr{this}, aPriority]() {
if (self->IsLaunching()) {
if (!self->LaunchSubprocessResolve(/* aIsSync = */ false,
aPriority)) {
self->LaunchSubprocessReject();
return LaunchPromise::CreateAndReject(LaunchError(), __func__);
}
self->mActivateTS = TimeStamp::Now();
} else if (self->IsDead()) {
// This could happen if we're racing against a sync launch and it
// failed.
return LaunchPromise::CreateAndReject(LaunchError(), __func__);
}
return LaunchPromise::CreateAndResolve(self, __func__);
},
// On reject.
[self = RefPtr{this}]() {
if (self->IsLaunching()) {
self->LaunchSubprocessReject();
}
return LaunchPromise::CreateAndReject(LaunchError(), __func__);
});
}
bool ContentParent::WaitForLaunchSync(ProcessPriority aPriority) {
MOZ_DIAGNOSTIC_ASSERT(!IsDead());
if (!IsLaunching()) {
return true;
}
// We've started a sync content process launch.
Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 1);
// We're a process which hasn't finished initializing. We may be racing
// against whoever launched it (and whoever else is already racing). Since
// we're sync, we win the race and finish the initialization.
bool launchSuccess = mSubprocess->WaitForProcessHandle();
if (launchSuccess &&
LaunchSubprocessResolve(/* aIsSync = */ true, aPriority)) {
mActivateTS = TimeStamp::Now();
return true;
}
// In case of failure.
LaunchSubprocessReject();
return false;
}
/*static*/
already_AddRefed<ContentParent> ContentParent::GetNewOrUsedJSPluginProcess(
uint32_t aPluginID, const hal::ProcessPriority& aPriority) {
RefPtr<ContentParent> p;
if (sJSPluginContentParents) {
p = sJSPluginContentParents->Get(aPluginID);
} else {
sJSPluginContentParents =
MakeUnique<nsDataHashtable<nsUint32HashKey, ContentParent*>>();
}
if (p) {
return p.forget();
}
p = new ContentParent(aPluginID);
if (!p->LaunchSubprocessSync(aPriority)) {
return nullptr;
}
sJSPluginContentParents->Put(aPluginID, p);
return p.forget();
}
#if defined(XP_WIN)
extern const wchar_t* kPluginWidgetContentParentProperty;
/*static*/
void ContentParent::SendAsyncUpdate(nsIWidget* aWidget) {
if (!aWidget || aWidget->Destroyed()) {
return;
}
// Fire off an async request to the plugin to paint its window
HWND hwnd = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW);
NS_ASSERTION(hwnd, "Expected valid hwnd value.");
ContentParent* cp = reinterpret_cast<ContentParent*>(
::GetPropW(hwnd, kPluginWidgetContentParentProperty));
if (cp && !cp->IsDestroyed()) {
Unused << cp->SendUpdateWindow((uintptr_t)hwnd);
}
}
#endif // defined(XP_WIN)
static nsIDocShell* GetOpenerDocShellHelper(Element* aFrameElement) {
// Propagate the private-browsing status of the element's parent
// docshell to the remote docshell, via the chrome flags.
MOZ_ASSERT(aFrameElement);
nsPIDOMWindowOuter* win = aFrameElement->OwnerDoc()->GetWindow();
if (!win) {
NS_WARNING("Remote frame has no window");
return nullptr;
}
nsIDocShell* docShell = win->GetDocShell();
if (!docShell) {
NS_WARNING("Remote frame has no docshell");
return nullptr;
}
return docShell;
}
mozilla::ipc::IPCResult ContentParent::RecvCreateGMPService() {
Endpoint<PGMPServiceParent> parent;
Endpoint<PGMPServiceChild> child;
nsresult rv;
rv = PGMPService::CreateEndpoints(base::GetCurrentProcId(), OtherPid(),
&parent, &child);
if (NS_FAILED(rv)) {
MOZ_ASSERT(false, "CreateEndpoints failed");
return IPC_FAIL_NO_REASON(this);
}
if (!GMPServiceParent::Create(std::move(parent))) {
MOZ_ASSERT(false, "GMPServiceParent::Create failed");
return IPC_FAIL_NO_REASON(this);
}
if (!SendInitGMPService(std::move(child))) {
MOZ_ASSERT(false, "SendInitGMPService failed");
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvLoadPlugin(
const uint32_t& aPluginId, nsresult* aRv, uint32_t* aRunID,
Endpoint<PPluginModuleParent>* aEndpoint) {
*aRv = NS_OK;
if (!mozilla::plugins::SetupBridge(aPluginId, this, aRv, aRunID, aEndpoint)) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvUngrabPointer(
const uint32_t& aTime) {
#if !defined(MOZ_WIDGET_GTK)
MOZ_CRASH("This message only makes sense on GTK platforms");
#else
gdk_pointer_ungrab(aTime);
return IPC_OK();
#endif
}
mozilla::ipc::IPCResult ContentParent::RecvRemovePermission(
const IPC::Principal& aPrincipal, const nsCString& aPermissionType,
nsresult* aRv) {
*aRv = Permissions::RemovePermission(aPrincipal, aPermissionType);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvConnectPluginBridge(
const uint32_t& aPluginId, nsresult* aRv,
Endpoint<PPluginModuleParent>* aEndpoint) {
*aRv = NS_OK;
// We don't need to get the run ID for the plugin, since we already got it
// in the first call to SetupBridge in RecvLoadPlugin, so we pass in a dummy
// pointer and just throw it away.
uint32_t dummy = 0;
if (!mozilla::plugins::SetupBridge(aPluginId, this, aRv, &dummy, aEndpoint)) {
return IPC_FAIL(this, "SetupBridge failed");
}
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvLaunchRDDProcess(
nsresult* aRv, Endpoint<PRemoteDecoderManagerChild>* aEndpoint) {
*aRv = NS_OK;
if (XRE_IsParentProcess() &&
BrowserTabsRemoteAutostart() && // only do rdd process if e10s on
Preferences::GetBool("media.rdd-process.enabled", false)) {
RDDProcessManager* rdd = RDDProcessManager::Get();
if (rdd) {
bool rddOpened = rdd->LaunchRDDProcess();
if (rddOpened) {
rddOpened = rdd->CreateContentBridge(OtherPid(), aEndpoint);
}
if (NS_WARN_IF(!rddOpened)) {
*aRv = NS_ERROR_NOT_AVAILABLE;
}
} else {
*aRv = NS_ERROR_NOT_AVAILABLE;
}
}
return IPC_OK();
}
/*static*/
already_AddRefed<RemoteBrowser> ContentParent::CreateBrowser(
const TabContext& aContext, Element* aFrameElement,
const nsACString& aRemoteType, BrowsingContext* aBrowsingContext,
ContentParent* aOpenerContentParent) {
AUTO_PROFILER_LABEL("ContentParent::CreateBrowser", OTHER);
if (!sCanLaunchSubprocesses) {
return nullptr;
}
nsAutoCString remoteType(aRemoteType);
if (remoteType.IsEmpty()) {
remoteType = DEFAULT_REMOTE_TYPE;
}
TabId tabId(nsContentUtils::GenerateTabId());
nsIDocShell* docShell = GetOpenerDocShellHelper(aFrameElement);
TabId openerTabId;
if (docShell) {
openerTabId = BrowserParent::GetTabIdFrom(docShell);
}
bool isPreloadBrowser = false;
nsAutoString isPreloadBrowserStr;
if (aFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::preloadedState,
isPreloadBrowserStr)) {
isPreloadBrowser = isPreloadBrowserStr.EqualsLiteral("preloaded");
}
RefPtr<ContentParent> constructorSender;
MOZ_RELEASE_ASSERT(XRE_IsParentProcess(),
"Cannot allocate BrowserParent in content process");
if (aOpenerContentParent && aOpenerContentParent->IsAlive()) {
constructorSender = aOpenerContentParent;
} else {
if (aContext.IsJSPlugin()) {
constructorSender = GetNewOrUsedJSPluginProcess(
aContext.JSPluginId(), PROCESS_PRIORITY_FOREGROUND);
} else {
constructorSender = GetNewOrUsedBrowserProcess(
remoteType, aBrowsingContext->Group(), PROCESS_PRIORITY_FOREGROUND,
isPreloadBrowser);
}
if (!constructorSender) {
return nullptr;
}
}
aBrowsingContext->SetEmbedderElement(aFrameElement);
// Ensure that the process which we're using to launch is set as the host
// process for this BrowsingContextGroup.
aBrowsingContext->Group()->EnsureHostProcess(constructorSender);
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
docShell->GetTreeOwner(getter_AddRefs(treeOwner));
if (!treeOwner) {
return nullptr;
}
nsCOMPtr<nsIWebBrowserChrome> wbc = do_GetInterface(treeOwner);
if (!wbc) {
return nullptr;
}
uint32_t chromeFlags = 0;
wbc->GetChromeFlags(&chromeFlags);
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
if (loadContext && loadContext->UsePrivateBrowsing()) {
chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
}
if (loadContext && loadContext->UseRemoteTabs()) {
chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
}
if (loadContext && loadContext->UseRemoteSubframes()) {
chromeFlags |= nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
}
if (docShell->GetAffectPrivateSessionLifetime()) {
chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME;
}
if (tabId == 0) {
return nullptr;
}
aBrowsingContext->Canonical()->SetOwnerProcessId(
constructorSender->ChildID());
RefPtr<BrowserParent> browserParent =
new BrowserParent(constructorSender, tabId, aContext,
aBrowsingContext->Canonical(), chromeFlags);
// Open a remote endpoint for our PBrowser actor.
ManagedEndpoint<PBrowserChild> childEp =
constructorSender->OpenPBrowserEndpoint(browserParent);
if (NS_WARN_IF(!childEp.IsValid())) {
return nullptr;
}
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
cpm->RegisterRemoteFrame(browserParent);
nsCOMPtr<nsIPrincipal> initialPrincipal =
NullPrincipal::Create(aBrowsingContext->OriginAttributesRef());
WindowGlobalInit windowInit = WindowGlobalActor::AboutBlankInitializer(
aBrowsingContext, initialPrincipal);
RefPtr<WindowGlobalParent> windowParent =
WindowGlobalParent::CreateDisconnected(windowInit);
if (NS_WARN_IF(!windowParent)) {
return nullptr;
}
// Open a remote endpoint for the initial PWindowGlobal actor.
ManagedEndpoint<PWindowGlobalChild> windowEp =
browserParent->OpenPWindowGlobalEndpoint(windowParent);
if (NS_WARN_IF(!windowEp.IsValid())) {
return nullptr;
}
// Tell the content process to set up its PBrowserChild.
bool ok = constructorSender->SendConstructBrowser(
std::move(childEp), std::move(windowEp), tabId,
aContext.AsIPCTabContext(), windowInit, chromeFlags,
constructorSender->ChildID(), constructorSender->IsForBrowser(),
/* aIsTopLevel */ true);
if (NS_WARN_IF(!ok)) {
return nullptr;
}
windowParent->Init();
RefPtr<BrowserHost> browserHost = new BrowserHost(browserParent);
browserParent->SetOwnerElement(aFrameElement);
return browserHost.forget();
}
void ContentParent::GetAll(nsTArray<ContentParent*>& aArray) {
aArray.Clear();
for (auto* cp : AllProcesses(eLive)) {
aArray.AppendElement(cp);
}
}
void ContentParent::GetAllEvenIfDead(nsTArray<ContentParent*>& aArray) {
aArray.Clear();
for (auto* cp : AllProcesses(eAll)) {
aArray.AppendElement(cp);
}
}
void ContentParent::BroadcastStringBundle(
const StringBundleDescriptor& aBundle) {
AutoTArray<StringBundleDescriptor, 1> array;
array.AppendElement(aBundle);
for (auto* cp : AllProcesses(eLive)) {
Unused << cp->SendRegisterStringBundles(array);
}
}
void ContentParent::BroadcastFontListChanged() {
for (auto* cp : AllProcesses(eLive)) {
Unused << cp->SendFontListChanged();
}
}
const nsACString& ContentParent::GetRemoteType() const { return mRemoteType; }
void ContentParent::Init() {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
size_t length = ArrayLength(sObserverTopics);
for (size_t i = 0; i < length; ++i) {
obs->AddObserver(this, sObserverTopics[i], false);
}
}
AddShutdownBlockers();
// Flush any pref updates that happened during launch and weren't
// included in the blobs set up in BeginSubprocessLaunch.
for (const Pref& pref : mQueuedPrefs) {
Unused << NS_WARN_IF(!SendPreferenceUpdate(pref));
}
mQueuedPrefs.Clear();
if (obs) {
nsAutoString cpId;
cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-created",
cpId.get());
}
#ifdef ACCESSIBILITY
// If accessibility is running in chrome process then start it in content
// process.
if (PresShell::IsAccessibilityActive()) {
# if defined(XP_WIN)
// Don't init content a11y if we detect an incompat version of JAWS in use.
if (!mozilla::a11y::Compatibility::IsOldJAWS()) {
Unused << SendActivateA11y(
::GetCurrentThreadId(),
a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
}
# else
Unused << SendActivateA11y(0, 0);
# endif
}
#endif // #ifdef ACCESSIBILITY
#ifdef MOZ_GECKO_PROFILER
Unused << SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid()));
#endif
// Ensure that the default set of permissions are avaliable in the content
// process before we try to load any URIs in it.
EnsurePermissionsByKey(""_ns, ""_ns);
RefPtr<GeckoMediaPluginServiceParent> gmps(
GeckoMediaPluginServiceParent::GetSingleton());
gmps->UpdateContentProcessGMPCapabilities();
}
void ContentParent::MaybeAsyncSendShutDownMessage() {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
("MaybeAsyncSendShutDownMessage %p", this));
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!TryToRecycle());
#ifdef DEBUG
// Calling this below while the lock is acquired will deadlock.
bool shouldKeepProcessAlive = ShouldKeepProcessAlive();
#endif
auto lock = mRemoteWorkerActorData.Lock();
MOZ_ASSERT_IF(!lock->mCount, !shouldKeepProcessAlive);
if (lock->mCount) {
return;
}
MOZ_ASSERT(!lock->mShutdownStarted);
lock->mShutdownStarted = true;
// In the case of normal shutdown, send a shutdown message to child to
// allow it to perform shutdown tasks.
GetCurrentSerialEventTarget()->Dispatch(NewRunnableMethod<ShutDownMethod>(
"dom::ContentParent::ShutDownProcess", this,
&ContentParent::ShutDownProcess, SEND_SHUTDOWN_MESSAGE));
}
void ContentParent::ShutDownProcess(ShutDownMethod aMethod) {
// Shutting down by sending a shutdown message works differently than the
// other methods. We first call Shutdown() in the child. After the child is
// ready, it calls FinishShutdown() on us. Then we close the channel.
if (aMethod == SEND_SHUTDOWN_MESSAGE) {
if (mIPCOpen && !mShutdownPending) {
// Stop sending input events with input priority when shutting down.
SetInputPriorityEventEnabled(false);
if (SendShutdown()) {
RemoveFromList();
mShutdownPending = true;
// Start the force-kill timer if we haven't already.
StartForceKillTimer();
}
}
// If call was not successful, the channel must have been broken
// somehow, and we will clean up the error in ActorDestroy.
return;
}
using mozilla::dom::quota::QuotaManagerService;
if (QuotaManagerService* qms = QuotaManagerService::GetOrCreate()) {
qms->AbortOperationsForProcess(mChildID);
}
// If Close() fails with an error, we'll end up back in this function, but
// with aMethod = CLOSE_CHANNEL_WITH_ERROR.
if (aMethod == CLOSE_CHANNEL && !mCalledClose) {
// Close() can only be called once: It kicks off the destruction
// sequence.
mCalledClose = true;
Close();
}
const ManagedContainer<POfflineCacheUpdateParent>& ocuParents =
ManagedPOfflineCacheUpdateParent();
for (auto iter = ocuParents.ConstIter(); !iter.Done(); iter.Next()) {
RefPtr<mozilla::docshell::OfflineCacheUpdateParent> ocuParent =
static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(
iter.Get()->GetKey());
ocuParent->StopSendingMessagesToChild();
}
// NB: must MarkAsDead() here so that this isn't accidentally
// returned from Get*() while in the midst of shutdown.
MarkAsDead();
// A ContentParent object might not get freed until after XPCOM shutdown has
// shut down the cycle collector. But by then it's too late to release any
// CC'ed objects, so we need to null them out here, while we still can. See
ShutDownMessageManager();
}
mozilla::ipc::IPCResult ContentParent::RecvFinishShutdown() {
// At this point, we already called ShutDownProcess once with
// SEND_SHUTDOWN_MESSAGE. To actually close the channel, we call
// ShutDownProcess again with CLOSE_CHANNEL.
MOZ_ASSERT(mShutdownPending);
ShutDownProcess(CLOSE_CHANNEL);
return IPC_OK();
}
void ContentParent::ShutDownMessageManager() {
if (!mMessageManager) {
return;
}
mMessageManager->ReceiveMessage(mMessageManager, nullptr,
CHILD_PROCESS_SHUTDOWN_MESSAGE, false,
nullptr, nullptr, IgnoreErrors());
mMessageManager->SetOsPid(-1);
mMessageManager->Disconnect();
mMessageManager = nullptr;
}
void ContentParent::AddToPool(nsTArray<ContentParent*>& aPool) {
MOZ_DIAGNOSTIC_ASSERT(!mIsInPool);
AssertAlive();
MOZ_DIAGNOSTIC_ASSERT(!mCalledKillHard);
aPool.AppendElement(this);
mIsInPool = true;
}
void ContentParent::RemoveFromPool(nsTArray<ContentParent*>& aPool) {
MOZ_DIAGNOSTIC_ASSERT(mIsInPool);
aPool.RemoveElement(this);
mIsInPool = false;
}
void ContentParent::AssertNotInPool() {
MOZ_RELEASE_ASSERT(!mIsInPool);
MOZ_RELEASE_ASSERT(!sPrivateContent || !sPrivateContent->Contains(this));
if (IsForJSPlugin()) {
MOZ_RELEASE_ASSERT(!sJSPluginContentParents ||
!sJSPluginContentParents->Get(mJSPluginID));
} else {
MOZ_RELEASE_ASSERT(
!sBrowserContentParents ||
!sBrowserContentParents->Contains(mRemoteType) ||
!sBrowserContentParents->Get(mRemoteType)->Contains(this) ||
!sCanLaunchSubprocesses); // aka in shutdown - avoid timing issues
}
}
void ContentParent::AssertAlive() {
MOZ_DIAGNOSTIC_ASSERT(!IsDead());
MOZ_DIAGNOSTIC_ASSERT(!mShutdownPending);
}
void ContentParent::RemoveFromList() {
if (IsForJSPlugin()) {
if (sJSPluginContentParents) {
sJSPluginContentParents->Remove(mJSPluginID);
if (!sJSPluginContentParents->Count()) {
sJSPluginContentParents = nullptr;
}
}
return;
}
if (sPrivateContent) {
sPrivateContent->RemoveElement(this);
if (!sPrivateContent->Length()) {
sPrivateContent = nullptr;
}
}
if (!mIsInPool) {
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
AssertNotInPool();
#endif
return;
}
// Ensure that this BrowsingContextGroup is no longer used to host new
// documents from any associated BrowsingContextGroups. It may become a host
// again in the future, if it is restored to the pool.
for (auto& group : mGroups) {
group.GetKey()->RemoveHostProcess(this);
}
if (sBrowserContentParents) {
if (auto entry = sBrowserContentParents->Lookup(mRemoteType)) {
const auto& contentParents = entry.Data();
RemoveFromPool(*contentParents);
if (contentParents->IsEmpty()) {
entry.Remove();
}
}
if (sBrowserContentParents->IsEmpty()) {
delete sBrowserContentParents;
sBrowserContentParents = nullptr;
}
}
}
void ContentParent::MarkAsDead() {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
("Marking ContentProcess %p as dead", this));
MOZ_DIAGNOSTIC_ASSERT(!sInProcessSelector);
if (!mShutdownPending) {
RemoveFromList();
}
PreallocatedProcessManager::Erase(this);
#ifdef MOZ_WIDGET_ANDROID
if (mLifecycleState == LifecycleState::ALIVE) {
nsCOMPtr<nsIEventTarget> launcherThread(GetIPCLauncher());
MOZ_ASSERT(launcherThread);
auto procType = java::GeckoProcessType::CONTENT();
auto selector =
java::GeckoProcessManager::Selector::New(procType, OtherPid());
launcherThread->Dispatch(NS_NewRunnableFunction(
"ContentParent::MarkAsDead",
[selector =
java::GeckoProcessManager::Selector::GlobalRef(selector)]() {
java::GeckoProcessManager::MarkAsDead(selector);
}));
}
#endif
if (mScriptableHelper) {
static_cast<ScriptableCPInfo*>(mScriptableHelper.get())->ProcessDied();
mScriptableHelper = nullptr;
}
mLifecycleState = LifecycleState::DEAD;
}
void ContentParent::OnChannelError() {
RefPtr<ContentParent> kungFuDeathGrip(this);
PContentParent::OnChannelError();
}
void ContentParent::OnChannelConnected(int32_t pid) {
MOZ_ASSERT(NS_IsMainThread());
SetOtherProcessId(pid);
}
void ContentParent::ProcessingError(Result aCode, const char* aReason) {
if (MsgDropped == aCode) {
return;
}
#ifndef FUZZING
// Other errors are big deals.
KillHard(aReason);
#endif
}
void ContentParent::ActorDestroy(ActorDestroyReason why) {
MOZ_RELEASE_ASSERT(mSelfRef);
if (mForceKillTimer) {
mForceKillTimer->Cancel();
mForceKillTimer = nullptr;
}
// Signal shutdown completion regardless of error state, so we can
// finish waiting in the xpcom-shutdown/profile-before-change observer.
mIPCOpen = false;
RemoveShutdownBlockers();
if (mHangMonitorActor) {
ProcessHangMonitor::RemoveProcess(mHangMonitorActor);
mHangMonitorActor = nullptr;
}
RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
if (fss) {
fss->Forget(ChildID());
}
if (why == NormalShutdown && !mCalledClose) {
// If we shut down normally but haven't called Close, assume somebody
// else called Close on us. In that case, we still need to call
// ShutDownProcess below to perform other necessary clean up.
mCalledClose = true;
}
// Make sure we always clean up.
ShutDownProcess(why == NormalShutdown ? CLOSE_CHANNEL
: CLOSE_CHANNEL_WITH_ERROR);
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
size_t length = ArrayLength(sObserverTopics);
for (size_t i = 0; i < length; ++i) {
obs->RemoveObserver(static_cast<nsIObserver*>(this), sObserverTopics[i]);
}
}
// remove the global remote preferences observers
Preferences::RemoveObserver(this, "");
gfxVars::RemoveReceiver(this);
if (GPUProcessManager* gpu = GPUProcessManager::Get()) {
// Note: the manager could have shutdown already.
gpu->RemoveListener(this);
}
RecvRemoveGeolocationListener();
mConsoleService = nullptr;
// Destroy our JSProcessActors, and reject any pending queries.
JSActorDidDestroy();
if (obs) {
RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
props->SetPropertyAsUint64(u"childID"_ns, mChildID);
if (AbnormalShutdown == why) {
Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT, "content"_ns,
1);
props->SetPropertyAsBool(u"abnormal"_ns, true);
nsAutoString dumpID;
// There's a window in which child processes can crash
// after IPC is established, but before a crash reporter
// is created.
if (mCrashReporter) {
// if mCreatedPairedMinidumps is true, we've already generated
// parent/child dumps for desktop crashes.
if (!mCreatedPairedMinidumps) {
if (mCrashReporter->GenerateCrashReport(OtherPid())) {
// Propagate `isLikelyOOM`.
Unused << props->SetPropertyAsBool(u"isLikelyOOM"_ns,
mCrashReporter->IsLikelyOOM());
}
}
if (mCrashReporter->HasMinidump()) {
dumpID = mCrashReporter->MinidumpID();
}
} else {
HandleOrphanedMinidump(&dumpID);
}
if (!dumpID.IsEmpty()) {
props->SetPropertyAsAString(u"dumpID"_ns, dumpID);
}
}
nsAutoString cpId;
cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
obs->NotifyObservers((nsIPropertyBag2*)props, "ipc:content-shutdown",
cpId.get());
}
// Remove any and all idle listeners.
nsCOMPtr<nsIUserIdleService> idleService =
do_GetService("@mozilla.org/widget/useridleservice;1");
MOZ_ASSERT(idleService);
RefPtr<ParentIdleListener> listener;
for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) {
listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get());
idleService->RemoveIdleObserver(listener, listener->mTime);
}
mIdleListeners.Clear();
MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
("destroying Subprocess in ActorDestroy: ContentParent %p "
"mSubprocess %p handle %" PRIuPTR,
this, mSubprocess,
mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
// FIXME (bug 1520997): does this really need an additional dispatch?
GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
"DelayedDeleteSubprocessRunnable", [subprocess = mSubprocess] {
MOZ_LOG(
ContentParent::GetLog(), LogLevel::Debug,
("destroyed Subprocess in ActorDestroy: Subprocess %p handle "
"%" PRIuPTR,
subprocess,
subprocess ? (uintptr_t)subprocess->GetChildProcessHandle() : -1));
subprocess->Destroy();
}));
mSubprocess = nullptr;
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
cpm->RemoveContentProcess(this->ChildID());
if (mDriverCrashGuard) {
mDriverCrashGuard->NotifyCrashed();
}
// Unregister all the BlobURLs registered by the ContentChild.
for (uint32_t i = 0; i < mBlobURLs.Length(); ++i) {
BlobURLProtocolHandler::RemoveDataEntry(mBlobURLs[i]);
}
mBlobURLs.Clear();
#if defined(XP_WIN) && defined(ACCESSIBILITY)
a11y::AccessibleWrap::ReleaseContentProcessIdFor(ChildID());
#endif
// As this process is going away, ensure that every BrowsingContext hosted by
// it has been detached, and every BrowsingContextGroup has been fully
// unsubscribed.
BrowsingContext::DiscardFromContentParent(this);
nsTHashtable<nsRefPtrHashKey<BrowsingContextGroup>> groups;
mGroups.SwapElements(groups);
for (auto& group : groups) {
group.GetKey()->Unsubscribe(this);
}
MOZ_DIAGNOSTIC_ASSERT(mGroups.IsEmpty());
}
void ContentParent::ActorDealloc() { mSelfRef = nullptr; }
bool ContentParent::TryToRecycle() {
// We can only do this if we have a separate cache for recycled
// 'web' processes, and handle them differently than webIsolated ones
if (mRemoteType != DEFAULT_REMOTE_TYPE) {
return false;
}
// This life time check should be replaced by a memory health check (memory
// usage + fragmentation).
// Note that this is specifically to help with edge cases that rapidly
// create-and-destroy processes
const double kMaxLifeSpan = 5;
MOZ_LOG(
ContentParent::GetLog(), LogLevel::Debug,
("TryToRecycle ContentProcess %p (%u) with lifespan %f seconds", this,
(unsigned int)ChildID(), (TimeStamp::Now() - mActivateTS).ToSeconds()));
if (mShutdownPending || mCalledKillHard || !IsAlive() ||
(TimeStamp::Now() - mActivateTS).ToSeconds() > kMaxLifeSpan) {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("TryToRecycle did not take ownership of %p", this));
// It's possible that the process was already cached via Provide() (such
// as from TabDestroyed), and we're being called from a different path,
// such as UnregisterRemoveWorkerActor(), and we're now past kMaxLifeSpan
// (or some other). Ensure that if we're going to destroy this process
// that we don't have it in the cache.
PreallocatedProcessManager::Erase(this);
return false;
}
// This will either cache it and take ownership, realize it was already
// cached (due to this being called a second time via a different
// path), or it will decide to not take ownership (if it has another
// already cached)
bool retval = PreallocatedProcessManager::Provide(this);
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("Provide did %stake ownership of %p", retval ? "" : "not ", this));
if (retval) {
// The PreallocatedProcessManager took over the ownership let's not keep a