/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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/. */
#ifdef MOZ_WIDGET_ANDROID
# include
"AndroidDecoderModule.h"
#endif
#include "BrowserChild.h"
#include "nsNSSComponent.h"
#include "ContentChild.h"
#include "GeckoProfiler.h"
#include "HandlerServiceChild.h"
#include "nsXPLookAndFeel.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/Attributes.h"
#include "mozilla/BackgroundHangMonitor.h"
#include "mozilla/BenchmarkStorageChild.h"
#include "mozilla/FOGIPC.h"
#include "GMPServiceChild.h"
#include "Geolocation.h"
#include "imgLoader.h"
#include "ScrollingMetrics.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/ClipboardContentAnalysisChild.h"
#include "mozilla/ClipboardReadRequestChild.h"
#include "mozilla/Components.h"
#include "mozilla/HangDetails.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/Logging.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/MemoryTelemetry.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/PerfStats.h"
#include "mozilla/Preferences.h"
#include "mozilla/ProcessHangMonitorIPC.h"
#include "mozilla/RemoteDecoderManagerChild.h"
#include "mozilla/RemoteLazyInputStreamChild.h"
#include "mozilla/SchedulerGroup.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/SharedStyleSheetCache.h"
#include "mozilla/dom/SharedScriptCache.h"
#include "mozilla/SimpleEnumerator.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/StaticPrefs_javascript.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/StaticPrefs_threads.h"
#include "mozilla/StorageAccessAPIHelper.h"
#include "mozilla/TelemetryIPC.h"
#include "mozilla/Unused.h"
#include "mozilla/WebBrowserPersistDocumentChild.h"
#include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
#include "mozilla/dom/AutoSuppressEventHandlingAndSuspend.h"
#include "mozilla/dom/BlobImpl.h"
#include "mozilla/dom/BrowserBridgeHost.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/ChildProcessChannelListener.h"
#include "mozilla/dom/ChildProcessMessageManager.h"
#include "mozilla/dom/ClientManager.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ContentProcessManager.h"
#include "mozilla/dom/ContentPlaybackController.h"
#include "mozilla/dom/ContentProcessMessageManager.h"
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/ExternalHelperAppChild.h"
#include "mozilla/dom/GetFilesHelper.h"
#include "mozilla/dom/IPCBlobUtils.h"
#include "mozilla/dom/InProcessChild.h"
#include "mozilla/dom/JSActorService.h"
#include "mozilla/dom/JSProcessActorBinding.h"
#include "mozilla/dom/JSProcessActorChild.h"
#include "mozilla/dom/LSObject.h"
#include "mozilla/dom/MemoryReportRequest.h"
#include "mozilla/dom/PSessionStorageObserverChild.h"
#include "mozilla/dom/PostMessageEvent.h"
#include "mozilla/dom/PushNotifier.h"
#include "mozilla/dom/RemoteWorkerService.h"
#include "mozilla/dom/ScreenOrientation.h"
#include "mozilla/dom/ServiceWorkerManager.h"
#include "mozilla/dom/SessionStorageManager.h"
#include "mozilla/dom/URLClassifierChild.h"
#include "mozilla/dom/UserActivation.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/WorkerDebugger.h"
#include "mozilla/dom/WorkerDebuggerManager.h"
#include "mozilla/dom/ipc/SharedMap.h"
#include "mozilla/extensions/ExtensionsChild.h"
#include "mozilla/extensions/StreamFilterParent.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/hal_sandbox/PHalChild.h"
#include "mozilla/intl/L10nRegistry.h"
#include "mozilla/intl/LocaleService.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/ipc/ProcessChild.h"
#include "mozilla/ipc/TestShellChild.h"
#include "mozilla/layers/APZChild.h"
#include "mozilla/layers/CompositorManagerChild.h"
#include "mozilla/layers/ContentProcessController.h"
#include "mozilla/layers/ImageBridgeChild.h"
#ifdef NS_PRINTING
# include
"mozilla/layout/RemotePrintJobChild.h"
#endif
#include "mozilla/loader/ScriptCacheActors.h"
#include "mozilla/media/MediaChild.h"
#include "mozilla/net/CaptivePortalService.h"
#include "mozilla/net/ChildDNSService.h"
#include "mozilla/net/CookieServiceChild.h"
#include "mozilla/net/DocumentChannelChild.h"
#include "mozilla/net/HttpChannelChild.h"
#include "mozilla/widget/RemoteLookAndFeel.h"
#include "mozilla/widget/ScreenManager.h"
#include "mozilla/widget/WidgetMessageUtils.h"
#include "nsBaseDragService.h"
#include "nsDocShellLoadTypes.h"
#include "nsFocusManager.h"
#include "nsHttpHandler.h"
#include "nsIConsoleService.h"
#include "nsIInputStreamChannel.h"
#include "nsILayoutHistoryState.h"
#include "nsILoadGroup.h"
#include "nsIOpenWindowInfo.h"
#include "nsISimpleEnumerator.h"
#include "nsIStringBundle.h"
#include "nsIURIMutator.h"
#include "nsQueryObject.h"
#include "nsRefreshDriver.h"
#include "nsSandboxFlags.h"
#include "mozmemory.h"
#include "ChildProfilerController.h"
#if defined(MOZ_SANDBOX)
# include
"mozilla/SandboxSettings.h"
# if defined(XP_WIN)
# include
"mozilla/sandboxTarget.h"
# include
"mozilla/ProcInfo.h"
# elif
defined(XP_LINUX)
# include
"CubebUtils.h"
# include
"mozilla/Sandbox.h"
# include
"mozilla/SandboxInfo.h"
# include
"mozilla/SandboxProfilerObserver.h"
# elif
defined(XP_MACOSX)
# include <CoreGraphics/CGError.h>
# include
"mozilla/Sandbox.h"
# elif
defined(__OpenBSD__)
# include <err.h>
# include <sys/stat.h>
# include <unistd.h>
# include <fstream>
# include
"BinaryPath.h"
# include
"SpecialSystemDirectory.h"
# include
"nsILineInputStream.h"
# include
"mozilla/ipc/UtilityProcessSandboxing.h"
# endif
# if defined(MOZ_DEBUG) &&
defined(ENABLE_TESTS)
# include
"mozilla/SandboxTestingChild.h"
# endif
#endif
#include "SandboxHal.h"
#include "mozInlineSpellChecker.h"
#include "mozilla/GlobalStyleSheetCache.h"
#include "nsAnonymousTemporaryFile.h"
#include "nsCategoryManagerUtils.h"
#include "nsClipboardProxy.h"
#include "nsContentPermissionHelper.h"
#include "nsDebugImpl.h"
#include "nsDirectoryService.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDocShell.h"
#include "nsDocShellLoadState.h"
#include "nsHashPropertyBag.h"
#include "nsIConsoleListener.h"
#include "nsICycleCollectorListener.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIDocumentViewer.h"
#include "nsIDragService.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIMemoryInfoDumper.h"
#include "nsIMemoryReporter.h"
#include "nsIObserverService.h"
#include "nsIOService.h"
#include "nsIScriptError.h"
#include "nsIScriptSecurityManager.h"
#include "nsJSEnvironment.h"
#include "nsJSUtils.h"
#include "nsMemoryInfoDumper.h"
#include "nsServiceManagerUtils.h"
#include "nsStyleSheetService.h"
#include "nsThreadManager.h"
#include "nsXULAppAPI.h"
#include "IHistory.h"
#include "ReferrerInfo.h"
#include "base/message_loop.h"
#include "base/process_util.h"
#include "base/task.h"
#include "mozilla/dom/BlobURLProtocolHandler.h"
#include "mozilla/dom/PCycleCollectWithLogsChild.h"
#include "mozilla/dom/PerformanceStorage.h"
#include "nsChromeRegistryContent.h"
#include "nsFrameMessageManager.h"
#include "nsNetUtil.h"
#include "nsWindowMemoryReporter.h"
#ifdef MOZ_WEBRTC
# include
"jsapi/WebrtcGlobalChild.h"
#endif
#include "PermissionMessageUtils.h"
#include "mozilla/Permission.h"
#include "mozilla/PermissionManager.h"
#if defined(MOZ_WIDGET_ANDROID)
# include
"APKOpen.h"
# include <sched.h>
#endif
#ifdef XP_WIN
# include <process.h>
# define getpid _getpid
# include
"mozilla/WinDllServices.h"
#endif
#if defined(XP_MACOSX)
# include
"nsMacUtilsImpl.h"
# include <sys/qos.h>
#endif /* XP_MACOSX */
#ifdef MOZ_X11
# include
"mozilla/X11Util.h"
#endif
#ifdef ACCESSIBILITY
# include
"nsAccessibilityService.h"
# ifdef XP_WIN
# include
"mozilla/a11y/AccessibleWrap.h"
# endif
# include
"mozilla/a11y/DocAccessible.h"
# include
"mozilla/a11y/DocManager.h"
# include
"mozilla/a11y/OuterDocAccessible.h"
#endif
#include "mozilla/dom/File.h"
#include "mozilla/dom/MediaControllerBinding.h"
#ifdef MOZ_WEBSPEECH
# include
"mozilla/dom/PSpeechSynthesisChild.h"
#endif
#include "ClearOnShutdown.h"
#include "DomainPolicy.h"
#include "GfxInfoBase.h"
#include "MMPrinter.h"
#include "mozilla/ipc/ProcessUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "VRManagerChild.h"
#include "gfxPlatform.h"
#include "gfxPlatformFontList.h"
#include "mozilla/RemoteSpellCheckEngineChild.h"
#include "mozilla/dom/TabContext.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
#include "mozilla/ipc/CrashReporterClient.h"
#include "mozilla/net/NeckoMessageUtils.h"
#include "mozilla/widget/PuppetBidiKeyboard.h"
#include "nsContentUtils.h"
#include "nsIPrincipal.h"
#include "nsString.h"
#include "nscore.h" // for NS_FREE_PERMANENT_DATA
#include "private/pprio.h"
#ifdef MOZ_WIDGET_GTK
# include
"mozilla/WidgetUtilsGtk.h"
# include
"nsAppRunner.h"
# include <gtk/gtk.h>
#endif
#ifdef MOZ_CODE_COVERAGE
# include
"mozilla/CodeCoverageHandler.h"
#endif
extern mozilla::LazyLogModule gSHIPBFCacheLog;
using namespace mozilla;
using namespace mozilla::dom::ipc;
using namespace mozilla::media;
using namespace mozilla::embedding;
using namespace mozilla::gmp;
using namespace mozilla::hal_sandbox;
using namespace mozilla::ipc;
using namespace mozilla::intl;
using namespace mozilla::layers;
using namespace mozilla::layout;
using namespace mozilla::net;
using namespace mozilla::widget;
using mozilla::loader::PScriptCacheChild;
namespace geckoprofiler::markers {
struct ProcessPriorityChange {
static constexpr Span<
const char> MarkerTypeName() {
return MakeStringSpan(
"ProcessPriorityChange");
}
static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
const ProfilerString8View& aPreviousPriority,
const ProfilerString8View& aNewPriority) {
aWriter.StringProperty(
"Before", aPreviousPriority);
aWriter.StringProperty(
"After", aNewPriority);
}
static MarkerSchema MarkerTypeDisplay() {
using MS = MarkerSchema;
MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
schema.AddKeyFormat(
"Before", MS::Format::String);
schema.AddKeyFormat(
"After", MS::Format::String);
schema.AddStaticLabelValue(
"Note",
"This is a notification of the priority change "
"that was done by the parent process");
schema.SetAllLabels(
"priority: {marker.data.Before} -> {marker.data.After}");
return schema;
}
};
struct ProcessPriority {
static constexpr Span<
const char> MarkerTypeName() {
return MakeStringSpan(
"ProcessPriority");
}
static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
const ProfilerString8View& aPriority,
const ProfilingState& aProfilingState) {
aWriter.StringProperty(
"Priority", aPriority);
aWriter.StringProperty(
"Marker cause",
ProfilerString8View::WrapNullTerminatedString(
ProfilingStateToString(aProfilingState)));
}
static MarkerSchema MarkerTypeDisplay() {
using MS = MarkerSchema;
MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
schema.AddKeyFormat(
"Priority", MS::Format::String);
schema.AddKeyFormat(
"Marker cause", MS::Format::String);
schema.SetAllLabels(
"priority: {marker.data.Priority}");
return schema;
}
};
}
// namespace geckoprofiler::markers
namespace mozilla {
namespace dom {
// IPC sender for remote GC/CC logging.
class CycleCollectWithLogsChild final :
public PCycleCollectWithLogsChild {
public:
NS_INLINE_DECL_REFCOUNTING(CycleCollectWithLogsChild)
class Sink final :
public nsICycleCollectorLogSink {
NS_DECL_ISUPPORTS
Sink(CycleCollectWithLogsChild* aActor,
const FileDescriptor& aGCLog,
const FileDescriptor& aCCLog) {
mActor = aActor;
mGCLog = FileDescriptorToFILE(aGCLog,
"w");
mCCLog = FileDescriptorToFILE(aCCLog,
"w");
}
NS_IMETHOD Open(FILE** aGCLog, FILE** aCCLog) override {
if (NS_WARN_IF(!mGCLog) || NS_WARN_IF(!mCCLog)) {
return NS_ERROR_FAILURE;
}
*aGCLog = mGCLog;
*aCCLog = mCCLog;
return NS_OK;
}
NS_IMETHOD CloseGCLog() override {
MOZ_ASSERT(mGCLog);
fclose(mGCLog);
mGCLog = nullptr;
mActor->SendCloseGCLog();
return NS_OK;
}
NS_IMETHOD CloseCCLog() override {
MOZ_ASSERT(mCCLog);
fclose(mCCLog);
mCCLog = nullptr;
mActor->SendCloseCCLog();
return NS_OK;
}
NS_IMETHOD GetFilenameIdentifier(nsAString& aIdentifier) override {
return UnimplementedProperty();
}
NS_IMETHOD SetFilenameIdentifier(
const nsAString& aIdentifier) override {
return UnimplementedProperty();
}
NS_IMETHOD GetProcessIdentifier(int32_t* aIdentifier) override {
return UnimplementedProperty();
}
NS_IMETHOD SetProcessIdentifier(int32_t aIdentifier) override {
return UnimplementedProperty();
}
NS_IMETHOD GetGcLog(nsIFile** aPath) override {
return UnimplementedProperty();
}
NS_IMETHOD GetCcLog(nsIFile** aPath) override {
return UnimplementedProperty();
}
private:
~Sink() {
if (mGCLog) {
fclose(mGCLog);
mGCLog = nullptr;
}
if (mCCLog) {
fclose(mCCLog);
mCCLog = nullptr;
}
// The XPCOM refcount drives the IPC lifecycle;
Unused << mActor->Send__delete__(mActor);
}
nsresult UnimplementedProperty() {
MOZ_ASSERT(
false,
"This object is a remote GC/CC logger;"
" this property isn't meaningful.");
return NS_ERROR_UNEXPECTED;
}
RefPtr<CycleCollectWithLogsChild> mActor;
FILE* mGCLog;
FILE* mCCLog;
};
private:
~CycleCollectWithLogsChild() =
default;
};
NS_IMPL_ISUPPORTS(CycleCollectWithLogsChild::Sink, nsICycleCollectorLogSink);
class ConsoleListener final :
public nsIConsoleListener {
public:
explicit ConsoleListener(ContentChild* aChild) : mChild(aChild) {}
NS_DECL_ISUPPORTS
NS_DECL_NSICONSOLELISTENER
private:
~ConsoleListener() =
default;
ContentChild* mChild;
friend class ContentChild;
};
NS_IMPL_ISUPPORTS(ConsoleListener, nsIConsoleListener)
// Before we send the error to the parent process (which
// involves copying the memory), truncate any long lines. CSS
// errors in particular share the memory for long lines with
// repeated errors, but the IPC communication we're about to do
// will break that sharing, so we better truncate now.
template <
typename CharT>
static void TruncateString(nsTSubstring<CharT>& aString) {
if (aString.Length() > 1000) {
aString.Truncate(1000);
}
}
NS_IMETHODIMP
ConsoleListener::Observe(nsIConsoleMessage* aMessage) {
if (!mChild) {
return NS_OK;
}
nsCOMPtr<nsIScriptError> scriptError = do_QueryInterface(aMessage);
if (scriptError) {
nsAutoString msg;
nsAutoCString sourceName;
nsCString category;
uint32_t lineNum, colNum, flags;
bool fromPrivateWindow, fromChromeContext;
nsresult rv = scriptError->GetErrorMessage(msg);
NS_ENSURE_SUCCESS(rv, rv);
TruncateString(msg);
rv = scriptError->GetSourceName(sourceName);
NS_ENSURE_SUCCESS(rv, rv);
TruncateString(sourceName);
rv = scriptError->GetCategory(getter_Copies(category));
NS_ENSURE_SUCCESS(rv, rv);
rv = scriptError->GetLineNumber(&lineNum);
NS_ENSURE_SUCCESS(rv, rv);
rv = scriptError->GetColumnNumber(&colNum);
NS_ENSURE_SUCCESS(rv, rv);
rv = scriptError->GetFlags(&flags);
NS_ENSURE_SUCCESS(rv, rv);
rv = scriptError->GetIsFromPrivateWindow(&fromPrivateWindow);
NS_ENSURE_SUCCESS(rv, rv);
rv = scriptError->GetIsFromChromeContext(&fromChromeContext);
NS_ENSURE_SUCCESS(rv, rv);
{
AutoJSAPI jsapi;
jsapi.Init();
JSContext* cx = jsapi.cx();
JS::Rooted<JS::Value> stack(cx);
rv = scriptError->GetStack(&stack);
NS_ENSURE_SUCCESS(rv, rv);
if (stack.isObject()) {
// Because |stack| might be a cross-compartment wrapper, we can't use it
// with JSAutoRealm. Use the stackGlobal for that.
JS::Rooted<JS::Value> stackGlobal(cx);
rv = scriptError->GetStackGlobal(&stackGlobal);
NS_ENSURE_SUCCESS(rv, rv);
JSAutoRealm ar(cx, &stackGlobal.toObject());
StructuredCloneData data;
ErrorResult err;
data.Write(cx, stack, err);
if (err.Failed()) {
return err.StealNSResult();
}
ClonedMessageData cloned;
if (!data.BuildClonedMessageData(cloned)) {
return NS_ERROR_FAILURE;
}
mChild->SendScriptErrorWithStack(msg, sourceName, lineNum, colNum,
flags, category, fromPrivateWindow,
fromChromeContext, cloned);
return NS_OK;
}
}
mChild->SendScriptError(msg, sourceName, lineNum, colNum, flags, category,
fromPrivateWindow, 0, fromChromeContext);
return NS_OK;
}
nsString msg;
nsresult rv = aMessage->GetMessageMoz(msg);
NS_ENSURE_SUCCESS(rv, rv);
mChild->SendConsoleMessage(msg);
return NS_OK;
}
#ifdef NIGHTLY_BUILD
/**
* The singleton of this class is registered with the BackgroundHangMonitor as
* an annotator, so that the hang monitor can record whether or not there were
* pending input events when the thread hung.
*/
class PendingInputEventHangAnnotator final :
public BackgroundHangAnnotator {
public:
virtual void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override {
int32_t pending = ContentChild::GetSingleton()->GetPendingInputEvents();
if (pending > 0) {
aAnnotations.AddAnnotation(u
"PendingInput"_ns, pending);
}
}
static PendingInputEventHangAnnotator sSingleton;
};
PendingInputEventHangAnnotator PendingInputEventHangAnnotator::sSingleton;
#endif
class ContentChild::ShutdownCanary final {};
ContentChild* ContentChild::sSingleton;
StaticAutoPtr<ContentChild::ShutdownCanary> ContentChild::sShutdownCanary;
ContentChild::ContentChild()
: mIsForBrowser(
false), mIsAlive(
true), mShuttingDown(
false) {
// This process is a content process, so it's clearly running in
// multiprocess mode!
nsDebugImpl::SetMultiprocessMode(
"Child");
// Our static analysis doesn't allow capturing ref-counted pointers in
// lambdas, so we need to hide it in a uintptr_t. This is safe because this
// lambda will be destroyed in ~ContentChild().
uintptr_t self =
reinterpret_cast<uintptr_t>(
this);
profiler_add_state_change_callback(
AllProfilingStates(),
[self](ProfilingState aProfilingState) {
const ContentChild* selfPtr =
reinterpret_cast<
const ContentChild*>(self);
PROFILER_MARKER(
"Process Priority", OTHER,
mozilla::MarkerThreadId::MainThread(), ProcessPriority,
ProfilerString8View::WrapNullTerminatedString(
ProcessPriorityToString(selfPtr->mProcessPriority)),
aProfilingState);
},
self);
// When ContentChild is created, the observer service does not even exist.
// When ContentChild::RecvSetXPCOMProcessAttributes is called (the first
// IPDL call made on this object), shutdown may have already happened. Thus
// we create a canary here that relies upon getting cleared if shutdown
// happens without requiring the observer service at this time.
if (!sShutdownCanary) {
sShutdownCanary =
new ShutdownCanary();
ClearOnShutdown(&sShutdownCanary, ShutdownPhase::XPCOMShutdown);
}
}
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning( \
disable : 4722)
/* Silence "destructor never returns" warning \
*/
#endif
ContentChild::~ContentChild() {
profiler_remove_state_change_callback(
reinterpret_cast<uintptr_t>(
this));
#ifndef NS_FREE_PERMANENT_DATA
MOZ_CRASH(
"Content Child shouldn't be destroyed.");
#endif
}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
NS_INTERFACE_MAP_BEGIN(ContentChild)
NS_INTERFACE_MAP_ENTRY(nsIDOMProcessChild)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMProcessChild)
NS_INTERFACE_MAP_END
mozilla::ipc::IPCResult ContentChild::RecvSetXPCOMProcessAttributes(
XPCOMInitData&& aXPCOMInit,
const StructuredCloneData& aInitialData,
FullLookAndFeel&& aLookAndFeelData, dom::SystemFontList&& aFontList,
Maybe<SharedMemoryHandle>&& aSharedUASheetHandle,
const uintptr_t& aSharedUASheetAddress,
nsTArray<SharedMemoryHandle>&& aSharedFontListBlocks,
const bool& aIsReadyForBackgroundProcessing) {
if (!sShutdownCanary) {
return IPC_OK();
}
mLookAndFeelData = std::move(aLookAndFeelData);
mFontList = std::move(aFontList);
mSharedFontListBlocks = std::move(aSharedFontListBlocks);
gfx::gfxVars::SetValuesForInitialize(aXPCOMInit.gfxNonDefaultVarUpdates());
PerfStats::SetCollectionMask(aXPCOMInit.perfStatsMask());
InitSharedUASheets(std::move(aSharedUASheetHandle), aSharedUASheetAddress);
InitXPCOM(std::move(aXPCOMInit), aInitialData,
aIsReadyForBackgroundProcessing);
InitGraphicsDeviceData(aXPCOMInit.contentDeviceData());
RefPtr<net::ChildDNSService> dnsServiceChild =
dont_AddRef(net::ChildDNSService::GetSingleton());
if (dnsServiceChild) {
dnsServiceChild->SetTRRDomain(aXPCOMInit.trrDomain());
dnsServiceChild->SetTRRModeInChild(aXPCOMInit.trrMode(),
aXPCOMInit.trrModeFromPref());
}
return IPC_OK();
}
class nsGtkNativeInitRunnable :
public Runnable {
public:
nsGtkNativeInitRunnable() : Runnable(
"nsGtkNativeInitRunnable") {}
NS_IMETHOD Run() override {
LookAndFeel::NativeInit();
return NS_OK;
}
};
void ContentChild::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
const char* aParentBuildID,
bool aIsForBrowser) {
#ifdef MOZ_WIDGET_GTK
// When running X11 only build we need to pass a display down
// to gtk_init because it's not going to use the one from the environment
// on its own when deciding which backend to use, and when starting under
// XWayland, it may choose to start with the wayland backend
// instead of the x11 backend.
// The DISPLAY environment variable is normally set by the parent process.
// The MOZ_GDK_DISPLAY environment variable is set from nsAppRunner.cpp
// when --display is set by the command line.
if (!gfxPlatform::IsHeadless()) {
const char* display_name = PR_GetEnv(
"MOZ_GDK_DISPLAY");
if (!display_name) {
bool waylandEnabled =
false;
# ifdef MOZ_WAYLAND
waylandEnabled = IsWaylandEnabled();
# endif
if (!waylandEnabled) {
display_name = PR_GetEnv(
"DISPLAY");
}
}
if (display_name) {
int argc = 3;
char option_name[] =
"--display";
char* argv[] = {
// argv0 is unused because g_set_prgname() was called in
// XRE_InitChildProcess().
nullptr, option_name,
const_cast<
char*>(display_name), nullptr};
char** argvp = argv;
gtk_init(&argc, &argvp);
}
else {
gtk_init(nullptr, nullptr);
}
}
#endif
#ifdef MOZ_X11
if (!gfxPlatform::IsHeadless()) {
// Do this after initializing GDK, or GDK will install its own handler.
XRE_InstallX11ErrorHandler();
}
#endif
MOZ_ASSERT(!sSingleton,
"only one ContentChild per child");
// Once we start sending IPC messages, we need the thread manager to be
// initialized so we can deal with the responses. Do that here before we
// try to construct the crash reporter.
nsresult rv = nsThreadManager::get().Init();
if (NS_WARN_IF(NS_FAILED(rv))) {
MOZ_CRASH(
"Failed to initialize the thread manager in ContentChild::Init");
}
if (!aEndpoint.Bind(
this)) {
MOZ_CRASH(
"Bind failed in ContentChild::Init");
}
sSingleton =
this;
// If communications with the parent have broken down, take the process
// down so it's not hanging around.
GetIPCChannel()->SetAbortOnError(
true);
// This must be checked before any IPDL message, which may hit sentinel
// errors due to parent and content processes having different
// versions.
MessageChannel* channel = GetIPCChannel();
if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID)) {
// We need to quit this process if the buildID doesn't match the parent's.
// This can occur when an update occurred in the background.
ProcessChild::QuickExit();
}
#if defined(__OpenBSD__) &&
defined(MOZ_SANDBOX)
StartOpenBSDSandbox(GeckoProcessType_Content);
#endif
#ifdef MOZ_X11
# ifdef MOZ_WIDGET_GTK
if (GdkIsX11Display() && !gfxPlatform::IsHeadless()) {
// Send the parent our X socket to act as a proxy reference for our X
// resources.
int xSocketFd = ConnectionNumber(DefaultXDisplay());
SendBackUpXResources(FileDescriptor(xSocketFd));
}
# endif
#endif
CrashReporterClient::InitSingleton(
this);
mIsForBrowser = aIsForBrowser;
SetProcessName(
"Web Content"_ns);
#ifdef NIGHTLY_BUILD
// NOTE: We have to register the annotator on the main thread, as annotators
// only affect a single thread.
SchedulerGroup::Dispatch(
NS_NewRunnableFunction(
"RegisterPendingInputEventHangAnnotator", [] {
BackgroundHangMonitor::RegisterAnnotator(
PendingInputEventHangAnnotator::sSingleton);
}));
#endif
#if defined(MOZ_MEMORY) &&
defined(DEBUG) && !
defined(MOZ_UBSAN)
jemalloc_stats_t stats;
jemalloc_stats(&stats);
MOZ_ASSERT(!stats.opt_randomize_small,
"Content process should not randomize small allocations");
#endif
}
void ContentChild::AddProfileToProcessName(
const nsACString& aProfile) {
nsCOMPtr<nsIPrincipal> isolationPrincipal =
ContentParent::CreateRemoteTypeIsolationPrincipal(mRemoteType);
if (isolationPrincipal) {
if (isolationPrincipal->OriginAttributesRef().IsPrivateBrowsing()) {
return;
}
}
mProcessName = aProfile +
":"_ns + mProcessName;
//<profile_name>:example.com
}
void ContentChild::SetProcessName(
const nsACString& aName,
const nsACString* aSite,
const nsACString* aCurrentProfile) {
char* name;
if ((name = PR_GetEnv(
"MOZ_DEBUG_APP_PROCESS")) && aName.EqualsASCII(name)) {
#ifdef XP_UNIX
printf_stderr(
"\n\nCHILDCHILDCHILDCHILD\n [%s] debug me @%d\n\n", name,
getpid());
sleep(30);
#elif defined(XP_WIN)
// Windows has a decent JIT debugging story, so NS_DebugBreak does the
// right thing.
NS_DebugBreak(NS_DEBUG_BREAK,
"Invoking NS_DebugBreak() to debug child process", nullptr,
__FILE__, __LINE__);
#endif
}
if (aSite) {
profiler_set_process_name(aName, aSite);
}
else {
profiler_set_process_name(aName);
}
mProcessName = aName;
// Requires pref flip
if (aSite && StaticPrefs::fission_processSiteNames()) {
nsCOMPtr<nsIPrincipal> isolationPrincipal =
ContentParent::CreateRemoteTypeIsolationPrincipal(mRemoteType);
if (isolationPrincipal) {
// DEFAULT_PRIVATE_BROWSING_ID is the value when it's not private
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
(
"private = %d, pref = %d",
isolationPrincipal->OriginAttributesRef().IsPrivateBrowsing(),
StaticPrefs::fission_processPrivateWindowSiteNames()));
if (!isolationPrincipal->OriginAttributesRef().IsPrivateBrowsing()
#ifdef NIGHTLY_BUILD
// Nightly can show site names for private windows, with a second pref
|| StaticPrefs::fission_processPrivateWindowSiteNames()
#endif
) {
#if !
defined(XP_MACOSX)
// Mac doesn't have the 15-character limit Linux does
// Sets profiler process name
if (isolationPrincipal->SchemeIs(
"https")) {
nsAutoCString schemeless;
isolationPrincipal->GetHostPort(schemeless);
nsAutoCString originSuffix;
isolationPrincipal->GetOriginSuffix(originSuffix);
schemeless.Append(originSuffix);
mProcessName = schemeless;
}
else
#endif
{
mProcessName = *aSite;
}
}
}
}
if (StaticPrefs::fission_processProfileName() && aCurrentProfile &&
!aCurrentProfile->IsEmpty()) {
AddProfileToProcessName(*aCurrentProfile);
}
// else private window, don't change process name, or the pref isn't set
// mProcessName is always flat (mProcessName == aName)
mozilla::ipc::SetThisProcessName(mProcessName.get());
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
(
"Changed name of process %d to %s", getpid(),
PromiseFlatCString(mProcessName).get()));
}
static nsresult GetCreateWindowParams(nsIOpenWindowInfo* aOpenWindowInfo,
nsDocShellLoadState* aLoadState,
bool aForceNoReferrer,
nsIReferrerInfo** aReferrerInfo,
nsIPrincipal** aTriggeringPrincipal,
nsIContentSecurityPolicy** aCsp) {
if (!aTriggeringPrincipal || !aCsp) {
NS_ERROR(
"aTriggeringPrincipal || aCsp is null");
return NS_ERROR_FAILURE;
}
if (!aReferrerInfo) {
NS_ERROR(
"aReferrerInfo is null");
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIReferrerInfo> referrerInfo;
if (aForceNoReferrer) {
referrerInfo =
new ReferrerInfo(nullptr, ReferrerPolicy::_empty,
false);
}
if (aLoadState && !referrerInfo) {
referrerInfo = aLoadState->GetReferrerInfo();
}
RefPtr<BrowsingContext> parent = aOpenWindowInfo->GetParent();
nsCOMPtr<nsPIDOMWindowOuter> opener =
parent ? parent->GetDOMWindow() : nullptr;
if (!opener) {
nsCOMPtr<nsIPrincipal> nullPrincipal =
NullPrincipal::Create(aOpenWindowInfo->GetOriginAttributes());
if (!referrerInfo) {
referrerInfo =
new ReferrerInfo(nullptr, ReferrerPolicy::_empty);
}
referrerInfo.swap(*aReferrerInfo);
NS_ADDREF(*aTriggeringPrincipal = nullPrincipal);
return NS_OK;
}
nsCOMPtr<Document> doc = opener->GetDoc();
NS_ADDREF(*aTriggeringPrincipal = doc->NodePrincipal());
nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp();
if (csp) {
csp.forget(aCsp);
}
nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
if (!baseURI) {
NS_ERROR(
"Document didn't return a base URI");
return NS_ERROR_FAILURE;
}
if (!referrerInfo) {
referrerInfo =
new ReferrerInfo(*doc);
}
referrerInfo.swap(*aReferrerInfo);
return NS_OK;
}
nsresult ContentChild::ProvideWindowCommon(
NotNull<BrowserChild*> aTabOpener, nsIOpenWindowInfo* aOpenWindowInfo,
uint32_t aChromeFlags,
bool aCalledFromJS, nsIURI* aURI,
const nsAString& aName,
const nsACString& aFeatures,
const UserActivation::Modifiers& aModifiers,
bool aForceNoOpener,
bool aForceNoReferrer,
bool aIsPopupRequested,
nsDocShellLoadState* aLoadState,
bool* aWindowIsNew,
BrowsingContext** aReturn) {
*aReturn = nullptr;
nsAutoCString features(aFeatures);
nsAutoString name(aName);
nsresult rv;
RefPtr<BrowsingContext> parent = aOpenWindowInfo->GetParent();
MOZ_DIAGNOSTIC_ASSERT(parent,
"We must have a parent BC");
// Block the attempt to open a new window if the opening BrowsingContext is
// not marked to use remote tabs. This ensures that the newly opened window is
// correctly remote.
if (NS_WARN_IF(!parent->UseRemoteTabs())) {
return NS_ERROR_ABORT;
}
bool useRemoteSubframes =
aChromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
uint32_t parentSandboxFlags = parent->SandboxFlags();
Document* doc = parent->GetDocument();
if (doc) {
parentSandboxFlags = doc->GetSandboxFlags();
}
const bool isForPrinting = aOpenWindowInfo->GetIsForPrinting();
// Certain conditions complicate the process of creating the new
// BrowsingContext, and prevent us from using the
// "CreateWindowInDifferentProcess" codepath.
// * With Fission enabled, process selection will happen during the load, so
// switching processes eagerly will not provide a benefit.
// * Windows created for printing must be created within the current process
// so that a static clone of the source document can be created.
// * Sandboxed popups require the full window creation codepath.
// * Loads with form or POST data require the full window creation codepath.
const bool cannotLoadInDifferentProcess =
useRemoteSubframes || isForPrinting ||
(parentSandboxFlags &
SANDBOX_PROPAGATES_TO_AUXILIARY_BROWSING_CONTEXTS) ||
(aLoadState &&
(aLoadState->IsFormSubmission() || aLoadState->PostDataStream()));
if (!cannotLoadInDifferentProcess) {
// If we're in a content process and we have noopener set, there's no reason
// to load in our process, so let's load it elsewhere!
bool loadInDifferentProcess =
aForceNoOpener && StaticPrefs::dom_noopener_newprocess_enabled();
if (loadInDifferentProcess) {
nsCOMPtr<nsIPrincipal> triggeringPrincipal;
nsCOMPtr<nsIContentSecurityPolicy> csp;
nsCOMPtr<nsIReferrerInfo> referrerInfo;
rv = GetCreateWindowParams(aOpenWindowInfo, aLoadState, aForceNoReferrer,
getter_AddRefs(referrerInfo),
getter_AddRefs(triggeringPrincipal),
getter_AddRefs(csp));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (name.LowerCaseEqualsLiteral(
"_blank")) {
name.Truncate();
}
MOZ_DIAGNOSTIC_ASSERT(!nsContentUtils::IsSpecialName(name));
const bool hasValidUserGestureActivation = [aLoadState, doc] {
if (aLoadState) {
return aLoadState->HasValidUserGestureActivation();
}
if (doc) {
return doc->HasValidTransientUserGestureActivation();
}
return false;
}();
const bool textDirectiveUserActivation = [aLoadState, doc] {
if (doc && doc->ConsumeTextDirectiveUserActivation()) {
return true;
}
if (aLoadState) {
return aLoadState->GetTextDirectiveUserActivation();
}
return false;
}() || hasValidUserGestureActivation;
Unused << SendCreateWindowInDifferentProcess(
aTabOpener, parent, aChromeFlags, aCalledFromJS,
aOpenWindowInfo->GetIsTopLevelCreatedByWebContent(), aURI, features,
aModifiers, name, triggeringPrincipal, csp, referrerInfo,
aOpenWindowInfo->GetOriginAttributes(), hasValidUserGestureActivation,
textDirectiveUserActivation);
// We return NS_ERROR_ABORT, so that the caller knows that we've abandoned
// the window open as far as it is concerned.
return NS_ERROR_ABORT;
}
}
TabId tabId(nsContentUtils::GenerateTabId());
// We need to assign a TabGroup to the PBrowser actor before we send it to the
// parent. Otherwise, the parent could send messages to us before we have a
// proper TabGroup for that actor.
RefPtr<BrowsingContext> openerBC;
if (!aForceNoOpener) {
openerBC = parent;
}
RefPtr<BrowsingContext> browsingContext = BrowsingContext::CreateDetached(
nullptr, openerBC, nullptr, aName, BrowsingContext::Type::Content,
BrowsingContext::CreateDetachedOptions{
.isPopupRequested = aIsPopupRequested,
.topLevelCreatedByWebContent =
true,
.isForPrinting = isForPrinting,
});
MOZ_ALWAYS_SUCCEEDS(browsingContext->SetRemoteTabs(
true));
MOZ_ALWAYS_SUCCEEDS(browsingContext->SetRemoteSubframes(useRemoteSubframes));
MOZ_ALWAYS_SUCCEEDS(browsingContext->SetOriginAttributes(
aOpenWindowInfo->GetOriginAttributes()));
browsingContext->InitPendingInitialization(
true);
auto unsetPending = MakeScopeExit([browsingContext]() {
Unused << browsingContext->SetPendingInitialization(
false);
});
browsingContext->EnsureAttached();
// The initial about:blank document we generate within the nsDocShell will
// almost certainly be replaced at some point. Unfortunately, getting the
// principal right here causes bugs due to frame scripts not getting events
// they expect, due to the real initial about:blank not being created yet.
//
// For this reason, we intentionally mispredict the initial principal here, so
// that we can act the same as we did before when not predicting a result
// principal. This `PWindowGlobal` will almost immediately be destroyed.
nsCOMPtr<nsIPrincipal> initialPrincipal =
NullPrincipal::Create(browsingContext->OriginAttributesRef());
WindowGlobalInit windowInit = WindowGlobalActor::AboutBlankInitializer(
browsingContext, initialPrincipal);
RefPtr<WindowGlobalChild> windowChild =
WindowGlobalChild::CreateDisconnected(windowInit);
if (NS_WARN_IF(!windowChild)) {
return NS_ERROR_ABORT;
}
auto newChild = MakeNotNull<RefPtr<BrowserChild>>(
this, tabId, *aTabOpener, browsingContext, aChromeFlags,
/* aIsTopLevel */ true);
if (IsShuttingDown()) {
return NS_ERROR_ABORT;
}
// Open a remote endpoint for our PBrowser actor.
ManagedEndpoint<PBrowserParent> parentEp = OpenPBrowserEndpoint(newChild);
if (NS_WARN_IF(!parentEp.IsValid())) {
return NS_ERROR_ABORT;
}
// Open a remote endpoint for our PWindowGlobal actor.
ManagedEndpoint<PWindowGlobalParent> windowParentEp =
newChild->OpenPWindowGlobalEndpoint(windowChild);
if (NS_WARN_IF(!windowParentEp.IsValid())) {
return NS_ERROR_ABORT;
}
// Tell the parent process to set up its PBrowserParent.
PopupIPCTabContext ipcContext(aTabOpener, 0);
if (NS_WARN_IF(!SendConstructPopupBrowser(
std::move(parentEp), std::move(windowParentEp), tabId, ipcContext,
windowInit, aChromeFlags))) {
return NS_ERROR_ABORT;
}
windowChild->Init();
auto guardNullWindowGlobal = MakeScopeExit([&] {
if (!windowChild->GetWindowGlobal()) {
windowChild->Destroy();
}
});
// Now that |newChild| has had its IPC link established, call |Init| to set it
// up.
// XXX: This MOZ_KnownLive is only necessary because the static analysis can't
// tell that NotNull<RefPtr<BrowserChild>> is a strong pointer.
RefPtr<nsPIDOMWindowOuter> parentWindow =
parent ? parent->GetDOMWindow() : nullptr;
if (NS_FAILED(MOZ_KnownLive(newChild)->Init(parentWindow, windowChild))) {
return NS_ERROR_ABORT;
}
// Set to true when we're ready to return from this function.
bool ready =
false;
// NOTE: Capturing by reference here is safe, as this function won't return
// until one of these callbacks is called.
auto resolve = [&](CreatedWindowInfo&& info) {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
rv = info.rv();
*aWindowIsNew = info.windowOpened();
nsTArray<FrameScriptInfo> frameScripts(std::move(info.frameScripts()));
uint32_t maxTouchPoints = info.maxTouchPoints();
DimensionInfo dimensionInfo = std::move(info.dimensions());
// Once this function exits, we should try to exit the nested event loop.
ready =
true;
// NOTE: We have to handle this immediately in the resolve callback in order
// to make sure that we don't process any more IPC messages before returning
// from ProvideWindowCommon.
// Handle the error which we got back from the parent process, if we got
// one.
if (NS_FAILED(rv)) {
return;
}
if (!*aWindowIsNew) {
rv = NS_ERROR_ABORT;
return;
}
// If the BrowserChild has been torn down, we don't need to do this anymore.
if (NS_WARN_IF(!newChild->IPCOpen() || newChild->IsDestroyed())) {
rv = NS_ERROR_ABORT;
return;
}
ParentShowInfo showInfo(u
""_ns,
/* fakeShowInfo = */ true,
/* isTransparent = */ false,
newChild->WebWidget()->GetDPI(),
newChild->WebWidget()->RoundsWidgetCoordinatesTo(),
newChild->WebWidget()->GetDefaultScale().scale);
newChild->SetMaxTouchPoints(maxTouchPoints);
if (aForceNoOpener || !parent) {
MOZ_DIAGNOSTIC_ASSERT(!browsingContext->HadOriginalOpener());
MOZ_DIAGNOSTIC_ASSERT(browsingContext->GetTopLevelCreatedByWebContent());
MOZ_DIAGNOSTIC_ASSERT(browsingContext->GetOpenerId() == 0);
}
else {
MOZ_DIAGNOSTIC_ASSERT(browsingContext->HadOriginalOpener());
MOZ_DIAGNOSTIC_ASSERT(browsingContext->GetTopLevelCreatedByWebContent());
MOZ_DIAGNOSTIC_ASSERT(browsingContext->GetOpenerId() == parent->Id());
}
// Unfortunately we don't get a window unless we've shown the frame. That's
// pretty bogus; see bug 763602.
newChild->DoFakeShow(showInfo);
newChild->RecvUpdateDimensions(dimensionInfo);
for (size_t i = 0; i < frameScripts.Length(); i++) {
FrameScriptInfo& info = frameScripts[i];
if (!newChild->RecvLoadRemoteScript(info.url(),
info.runInGlobalScope())) {
MOZ_CRASH();
}
}
if (xpc::IsInAutomation()) {
if (nsCOMPtr<nsPIDOMWindowOuter> outer =
do_GetInterface(newChild->WebNavigation())) {
nsCOMPtr<nsIObserverService> obs(services::GetObserverService());
obs->NotifyObservers(
outer,
"dangerous:test-only:new-browser-child-ready", nullptr);
}
}
browsingContext.forget(aReturn);
};
// NOTE: Capturing by reference here is safe, as this function won't return
// until one of these callbacks is called.
auto reject = [&](ResponseRejectReason) {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
NS_WARNING(
"windowCreated promise rejected");
rv = NS_ERROR_NOT_AVAILABLE;
ready =
true;
};
// Send down the request to open the window.
nsCOMPtr<nsIPrincipal> triggeringPrincipal;
nsCOMPtr<nsIContentSecurityPolicy> csp;
nsCOMPtr<nsIReferrerInfo> referrerInfo;
rv = GetCreateWindowParams(aOpenWindowInfo, aLoadState, aForceNoReferrer,
getter_AddRefs(referrerInfo),
getter_AddRefs(triggeringPrincipal),
getter_AddRefs(csp));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
SendCreateWindow(
aTabOpener, parent, newChild, aChromeFlags, aCalledFromJS,
aOpenWindowInfo->GetIsForPrinting(),
aOpenWindowInfo->GetIsForWindowDotPrint(),
aOpenWindowInfo->GetIsTopLevelCreatedByWebContent(), aURI, features,
aModifiers, triggeringPrincipal, csp, referrerInfo,
aOpenWindowInfo->GetOriginAttributes(),
aLoadState ? aLoadState->HasValidUserGestureActivation() :
false,
aLoadState ? aLoadState->GetTextDirectiveUserActivation() :
false,
std::move(resolve), std::move(reject));
// =======================
// Begin Nested Event Loop
// =======================
// We have to wait for a response from SendCreateWindow or with information
// we're going to need to return from this function, So we spin a nested event
// loop until they get back to us.
{
// Suppress event handling for all contexts in our BrowsingContextGroup so
// that event handlers cannot target our new window while it's still being
// opened. Note that pending events that were suppressed while our blocker
// was active will be dispatched asynchronously from a runnable dispatched
// to the main event loop after this function returns, not immediately when
// we leave this scope.
AutoSuppressEventHandlingAndSuspend seh(browsingContext->Group());
AutoNoJSAPI nojsapi;
// Spin the event loop until we get a response. Callers of this function
// already have to guard against an inner event loop spinning in the
// non-e10s case because of the need to spin one to create a new chrome
// window.
SpinEventLoopUntil(
"ContentChild::ProvideWindowCommon"_ns,
[&]() {
return ready; });
MOZ_RELEASE_ASSERT(ready,
"We are on the main thread, so we should not exit this "
"loop without ready being true.");
}
// =====================
// End Nested Event Loop
// =====================
// It's possible for our new BrowsingContext to become discarded during the
// nested event loop, in which case we shouldn't return it, since our callers
// will generally not be prepared to deal with that.
if (*aReturn && (*aReturn)->IsDiscarded()) {
NS_RELEASE(*aReturn);
return NS_ERROR_ABORT;
}
// We should have the results already set by the callbacks.
MOZ_ASSERT_IF(NS_SUCCEEDED(rv), *aReturn);
return rv;
}
bool ContentChild::IsAlive()
const {
return mIsAlive; }
bool ContentChild::IsShuttingDown()
const {
return mShuttingDown; }
void ContentChild::GetProcessName(nsACString& aName)
const {
aName = mProcessName;
}
/* static */
void ContentChild::AppendProcessId(nsACString& aName) {
if (!aName.IsEmpty()) {
aName.Append(
' ');
}
unsigned pid = getpid();
aName.Append(nsPrintfCString(
"(pid %u)", pid));
}
void ContentChild::InitGraphicsDeviceData(
const ContentDeviceData& aData) {
gfxPlatform::InitChild(aData);
}
void ContentChild::InitSharedUASheets(Maybe<SharedMemoryHandle>&& aHandle,
uintptr_t aAddress) {
MOZ_ASSERT_IF(!aHandle, !aAddress);
if (!aAddress) {
return;
}
// Map the shared memory storing the user agent style sheets. Do this as
// early as possible to maximize the chance of being able to map at the
// address we want.
GlobalStyleSheetCache::SetSharedMemory(std::move(*aHandle), aAddress);
}
void ContentChild::InitXPCOM(
XPCOMInitData&& aXPCOMInit,
const mozilla::dom::ipc::StructuredCloneData& aInitialData,
bool aIsReadyForBackgroundProcessing) {
#ifdef MOZ_WIDGET_GTK
// LookAndFeel::NativeInit takes a long time to run on Linux, here we schedule
// it as soon as possible after BackgroundChild::Startup to give
// it chance to run ahead of ConstructBrowser
nsCOMPtr<nsIRunnable> event =
new nsGtkNativeInitRunnable();
NS_DispatchToMainThreadQueue(event.forget(), EventQueuePriority::Idle);
#endif
#if defined(XP_WIN)
// DLL services untrusted modules processing depends on
// BackgroundChild::Startup having been called
RefPtr<DllServices> dllSvc(DllServices::Get());
dllSvc->StartUntrustedModulesProcessor(aIsReadyForBackgroundProcessing);
#endif // defined(XP_WIN)
PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
if (NS_WARN_IF(!actorChild)) {
MOZ_ASSERT_UNREACHABLE(
"PBackground init can't fail at this point");
return;
}
ClientManager::Startup();
// RemoteWorkerService will be initialized in RecvRemoteType, to avoid to
// register it to the RemoteWorkerManager while it is still a prealloc
// remoteType and defer it to the point the child process is assigned a.
// actual remoteType.
nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
if (!svc) {
NS_WARNING(
"Couldn't acquire console service");
return;
}
mConsoleListener =
new ConsoleListener(
this);
if (NS_FAILED(svc->RegisterListener(mConsoleListener)))
NS_WARNING(
"Couldn't register console listener for child process");
mAvailableDictionaries = std::move(aXPCOMInit.dictionaries());
RecvSetOffline(aXPCOMInit.isOffline());
RecvSetConnectivity(aXPCOMInit.isConnected());
LocaleService::GetInstance()->AssignAppLocales(aXPCOMInit.appLocales());
LocaleService::GetInstance()->AssignRequestedLocales(
aXPCOMInit.requestedLocales());
L10nRegistry::RegisterFileSourcesFromParentProcess(
aXPCOMInit.l10nFileSources());
RecvSetCaptivePortalState(aXPCOMInit.captivePortalState());
RecvBidiKeyboardNotify(aXPCOMInit.isLangRTL(),
aXPCOMInit.haveBidiKeyboards());
if (aXPCOMInit.domainPolicy().active()) {
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
MOZ_ASSERT(ssm);
ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy));
if (!mPolicy) {
MOZ_CRASH(
"Failed to activate domain policy.");
}
mPolicy->ApplyClone(&aXPCOMInit.domainPolicy());
}
nsCOMPtr<nsIClipboard> clipboard(
do_GetService(
"@mozilla.org/widget/clipboard;1"));
if (nsCOMPtr<nsIClipboardProxy> clipboardProxy =
do_QueryInterface(clipboard)) {
clipboardProxy->SetCapabilities(aXPCOMInit.clipboardCaps());
}
{
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
MOZ_CRASH();
}
ErrorResult rv;
JS::Rooted<JS::Value> data(jsapi.cx());
mozilla::dom::ipc::StructuredCloneData id;
id.Copy(aInitialData);
id.Read(jsapi.cx(), &data, rv);
if (NS_WARN_IF(rv.Failed())) {
MOZ_CRASH();
}
auto* global = ContentProcessMessageManager::Get();
global->SetInitialProcessData(data);
}
// The stylesheet cache is not ready yet. Store this URL for future use.
nsCOMPtr<nsIURI> ucsURL = std::move(aXPCOMInit.userContentSheetURL());
GlobalStyleSheetCache::SetUserContentCSSURL(ucsURL);
GfxInfoBase::SetFeatureStatus(std::move(aXPCOMInit.gfxFeatureStatus()));
// Initialize the RemoteDecoderManager thread and its associated PBackground
// channel.
RemoteDecoderManagerChild::Init();
Preferences::RegisterCallbackAndCall(&OnFissionBlocklistPrefChange,
kFissionEnforceBlockList);
Preferences::RegisterCallbackAndCall(&OnFissionBlocklistPrefChange,
kFissionOmitBlockListValues);
// Set the dynamic scalar definitions for this process.
TelemetryIPC::AddDynamicScalarDefinitions(aXPCOMInit.dynamicScalarDefs());
}
mozilla::ipc::IPCResult ContentChild::RecvRequestMemoryReport(
const uint32_t& aGeneration,
const bool& aAnonymize,
const bool& aMinimizeMemoryUsage,
const Maybe<mozilla::ipc::FileDescriptor>& aDMDFile,
const RequestMemoryReportResolver& aResolver) {
nsCString process;
if (aAnonymize || mRemoteType.IsEmpty()) {
GetProcessName(process);
}
else {
process = mRemoteType;
}
AppendProcessId(process);
MOZ_ASSERT(!process.IsEmpty());
MemoryReportRequestClient::Start(
aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, process,
[&](
const MemoryReport& aReport) {
Unused << GetSingleton()->SendAddMemoryReport(aReport);
},
aResolver);
return IPC_OK();
}
#if defined(XP_WIN)
mozilla::ipc::IPCResult ContentChild::RecvGetUntrustedModulesData(
GetUntrustedModulesDataResolver&& aResolver) {
RefPtr<DllServices> dllSvc(DllServices::Get());
dllSvc->GetUntrustedModulesData()->Then(
GetMainThreadSerialEventTarget(), __func__,
[aResolver](Maybe<UntrustedModulesData>&& aData) {
aResolver(std::move(aData));
},
[aResolver](nsresult aReason) { aResolver(Nothing()); });
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvUnblockUntrustedModulesThread() {
if (nsCOMPtr<nsIObserverService> obs =
mozilla::services::GetObserverService()) {
obs->NotifyObservers(nullptr,
"unblock-untrusted-modules-thread", nullptr);
}
return IPC_OK();
}
#endif // defined(XP_WIN)
PCycleCollectWithLogsChild* ContentChild::AllocPCycleCollectWithLogsChild(
const bool& aDumpAllTraces,
const FileDescriptor& aGCLog,
const FileDescriptor& aCCLog) {
return do_AddRef(
new CycleCollectWithLogsChild()).take();
}
mozilla::ipc::IPCResult ContentChild::RecvPCycleCollectWithLogsConstructor(
PCycleCollectWithLogsChild* aActor,
const bool& aDumpAllTraces,
const FileDescriptor& aGCLog,
const FileDescriptor& aCCLog) {
// The sink's destructor is called when the last reference goes away, which
// will cause the actor to be closed down.
auto* actor =
static_cast<CycleCollectWithLogsChild*>(aActor);
RefPtr<CycleCollectWithLogsChild::Sink> sink =
new CycleCollectWithLogsChild::Sink(actor, aGCLog, aCCLog);
// Invoke the dumper, which will take a reference to the sink.
nsCOMPtr<nsIMemoryInfoDumper> dumper =
do_GetService(
"@mozilla.org/memory-info-dumper;1");
dumper->DumpGCAndCCLogsToSink(aDumpAllTraces, sink);
return IPC_OK();
}
bool ContentChild::DeallocPCycleCollectWithLogsChild(
PCycleCollectWithLogsChild* aActor) {
RefPtr<CycleCollectWithLogsChild> actor =
dont_AddRef(
static_cast<CycleCollectWithLogsChild*>(aActor));
return true;
}
mozilla::ipc::IPCResult ContentChild::RecvInitGMPService(
Endpoint<PGMPServiceChild>&& aGMPService) {
if (!GMPServiceChild::Create(std::move(aGMPService))) {
return IPC_FAIL_NO_REASON(
this);
}
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvInitProfiler(
Endpoint<PProfilerChild>&& aEndpoint) {
mProfilerController = ChildProfilerController::Create(std::move(aEndpoint));
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvGMPsChanged(
nsTArray<GMPCapabilityData>&& capabilities) {
GeckoMediaPluginServiceChild::UpdateGMPCapabilities(std::move(capabilities));
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvInitProcessHangMonitor(
Endpoint<PProcessHangMonitorChild>&& aHangMonitor) {
CreateHangMonitorChild(std::move(aHangMonitor));
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::GetResultForRenderingInitFailure(
GeckoChildID aOtherChildID) {
if (aOtherChildID == XRE_GetChildID() || aOtherChildID == OtherChildID()) {
// If we are talking to ourselves, or the UI process, then that is a fatal
// protocol error.
return IPC_FAIL_NO_REASON(
this);
}
// If we are talking to the GPU process, then we should recover from this on
// the next ContentChild::RecvReinitRendering call.
gfxCriticalNote <<
"Could not initialize rendering with GPU process";
return IPC_OK();
}
#if defined(XP_MACOSX)
extern "C" {
void CGSShutdownServerConnections();
};
#endif
mozilla::ipc::IPCResult ContentChild::RecvInitRendering(
Endpoint<PCompositorManagerChild>&& aCompositor,
Endpoint<PImageBridgeChild>&& aImageBridge,
Endpoint<PVRManagerChild>&& aVRBridge,
Endpoint<PRemoteDecoderManagerChild>&& aVideoManager,
nsTArray<uint32_t>&& namespaces) {
MOZ_ASSERT(namespaces.Length() == 3);
// Note that for all of the methods below, if it can fail, it should only
// return false if the failure is an IPDL error. In such situations,
// ContentChild can reason about whether or not to wait for
// RecvReinitRendering (because we surmised the GPU process crashed), or if it
// should crash itself (because we are actually talking to the UI process). If
// there are localized failures (e.g. failed to spawn a thread), then it
// should MOZ_RELEASE_ASSERT or MOZ_CRASH as necessary instead.
if (!CompositorManagerChild::Init(std::move(aCompositor), namespaces[0])) {
return GetResultForRenderingInitFailure(aCompositor.OtherChildID());
}
if (!CompositorManagerChild::CreateContentCompositorBridge(namespaces[1])) {
return GetResultForRenderingInitFailure(aCompositor.OtherChildID());
}
if (!ImageBridgeChild::InitForContent(std::move(aImageBridge),
namespaces[2])) {
return GetResultForRenderingInitFailure(aImageBridge.OtherChildID());
}
if (!gfx::VRManagerChild::InitForContent(std::move(aVRBridge))) {
return GetResultForRenderingInitFailure(aVRBridge.OtherChildID());
}
RemoteDecoderManagerChild::InitForGPUProcess(std::move(aVideoManager));
#if defined(XP_MACOSX) && !
defined(MOZ_SANDBOX)
// Close all current connections to the WindowServer. This ensures that the
// Activity Monitor will not label the content process as "Not responding"
// because it's not running a native event loop. See bug 1384336. When the
// build is configured with sandbox support, this is called during sandbox
// setup.
CGSShutdownServerConnections();
#endif
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvReinitRendering(
Endpoint<PCompositorManagerChild>&& aCompositor,
Endpoint<PImageBridgeChild>&& aImageBridge,
Endpoint<PVRManagerChild>&& aVRBridge,
Endpoint<PRemoteDecoderManagerChild>&& aVideoManager,
nsTArray<uint32_t>&& namespaces) {
MOZ_ASSERT(namespaces.Length() == 3);
nsTArray<RefPtr<BrowserChild>> tabs = BrowserChild::GetAll();
// Re-establish singleton bridges to the compositor.
if (!CompositorManagerChild::Init(std::move(aCompositor), namespaces[0])) {
return GetResultForRenderingInitFailure(aCompositor.OtherChildID());
}
if (!CompositorManagerChild::CreateContentCompositorBridge(namespaces[1])) {
return GetResultForRenderingInitFailure(aCompositor.OtherChildID());
}
if (!ImageBridgeChild::ReinitForContent(std::move(aImageBridge),
namespaces[2])) {
return GetResultForRenderingInitFailure(aImageBridge.OtherChildID());
}
if (!gfx::VRManagerChild::InitForContent(std::move(aVRBridge))) {
return GetResultForRenderingInitFailure(aVRBridge.OtherChildID());
}
gfxPlatform::GetPlatform()->CompositorUpdated();
// Establish new PLayerTransactions.
for (
const auto& browserChild : tabs) {
if (browserChild->GetLayersId().IsValid()) {
browserChild->ReinitRendering();
}
}
// Notify any observers that the compositor has been reinitialized,
// eg the ZoomConstraintsClients for documents in this process.
// This must occur after the ReinitRendering call above so that the
// APZCTreeManagers have been connected.
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
if (observerService) {
observerService->NotifyObservers(nullptr,
"compositor-reinitialized",
nullptr);
}
RemoteDecoderManagerChild::InitForGPUProcess(std::move(aVideoManager));
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvReinitRenderingForDeviceReset() {
gfxPlatform::GetPlatform()->CompositorUpdated();
nsTArray<RefPtr<BrowserChild>> tabs = BrowserChild::GetAll();
for (
const auto& browserChild : tabs) {
if (browserChild->GetLayersId().IsValid()) {
browserChild->ReinitRenderingForDeviceReset();
}
}
return IPC_OK();
}
#if defined(XP_MACOSX) &&
defined(MOZ_SANDBOX)
extern "C" {
CGError CGSSetDenyWindowServerConnections(
bool);
};
static void DisconnectWindowServer(
bool aIsSandboxEnabled) {
// Close all current connections to the WindowServer. This ensures that the
// Activity Monitor will not label the content process as "Not responding"
// because it's not running a native event loop. See bug 1384336.
// This is required with or without the sandbox enabled. Until the
// window server is blocked as the policy level, this should be called
// just before CGSSetDenyWindowServerConnections() so there are no
// windowserver connections active when CGSSetDenyWindowServerConnections()
// is called.
CGSShutdownServerConnections();
// Actual security benefits are only achieved when we additionally deny
// future connections using the sandbox policy. WebGL must be remoted if
// the windowserver connections are blocked. WebGL remoting is disabled
// for some tests.
if (aIsSandboxEnabled &&
Preferences::GetBool(
"security.sandbox.content.mac.disconnect-windowserver") &&
Preferences::GetBool(
"webgl.out-of-process")) {
CGError result = CGSSetDenyWindowServerConnections(
true);
MOZ_DIAGNOSTIC_ASSERT(result == kCGErrorSuccess);
# if !MOZ_DIAGNOSTIC_ASSERT_ENABLED
Unused << result;
# endif
}
}
#endif
mozilla::ipc::IPCResult ContentChild::RecvSetProcessSandbox(
const Maybe<mozilla::ipc::FileDescriptor>& aBroker) {
// We may want to move the sandbox initialization somewhere else
// at some point; see bug 880808.
#if defined(MOZ_SANDBOX)
bool sandboxEnabled =
true;
# if defined(XP_LINUX)
// On Linux, we have to support systems that can't use any sandboxing.
sandboxEnabled = SandboxInfo::Get().CanSandboxContent();
if (sandboxEnabled && !StaticPrefs::media_cubeb_sandbox()) {
// Pre-start audio before sandboxing; see bug 1443612.
Unused << CubebUtils::GetCubeb();
}
if (sandboxEnabled) {
RegisterProfilerObserversForSandboxProfiler();
sandboxEnabled = SetContentProcessSandbox(
ContentProcessSandboxParams::ForThisProcess(aBroker));
}
# elif
defined(XP_WIN)
if (GetEffectiveContentSandboxLevel() > 7) {
// Libraries required by Network Security Services (NSS).
::LoadLibraryW(L
"freebl3.dll");
::LoadLibraryW(L
"softokn3.dll");
// Cache value that is retrieved from a registry entry.
Unused << GetCpuFrequencyMHz();
}
mozilla::SandboxTarget::Instance()->StartSandbox();
# elif
defined(XP_MACOSX)
sandboxEnabled = (GetEffectiveContentSandboxLevel() >= 1);
DisconnectWindowServer(sandboxEnabled);
# endif
CrashReporter::RecordAnnotationBool(
CrashReporter::Annotation::ContentSandboxEnabled, sandboxEnabled);
# if defined(XP_LINUX) && !
defined(ANDROID)
CrashReporter::RecordAnnotationU32(
CrashReporter::Annotation::ContentSandboxCapabilities,
SandboxInfo::Get().AsInteger());
# endif
/* XP_LINUX && !ANDROID */
#endif /* MOZ_SANDBOX */
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvBidiKeyboardNotify(
const bool& aIsLangRTL,
const bool& aHaveBidiKeyboards) {
// bidi is always of type PuppetBidiKeyboard* (because in the child, the only
// possible implementation of nsIBidiKeyboard is PuppetBidiKeyboard).
PuppetBidiKeyboard* bidi =
static_cast<PuppetBidiKeyboard*>(nsContentUtils::GetBidiKeyboard());
if (bidi) {
bidi->SetBidiKeyboardInfo(aIsLangRTL, aHaveBidiKeyboards);
}
return IPC_OK();
}
static StaticRefPtr<CancelableRunnable> gFirstIdleTask;
static void FirstIdle(
void) {
MOZ_ASSERT(gFirstIdleTask);
gFirstIdleTask = nullptr;
ContentChild::GetSingleton()->SendFirstIdle();
}
mozilla::ipc::IPCResult ContentChild::RecvConstructBrowser(
ManagedEndpoint<PBrowserChild>&& aBrowserEp,
ManagedEndpoint<PWindowGlobalChild>&& aWindowEp,
const TabId& aTabId,
const IPCTabContext& aContext,
const WindowGlobalInit& aWindowInit,
const uint32_t& aChromeFlags,
const ContentParentId& aCpID,
const bool& aIsForBrowser,
const bool& aIsTopLevel) {
MOZ_DIAGNOSTIC_ASSERT(!IsShuttingDown());
static bool hasRunOnce =
false;
if (!hasRunOnce) {
hasRunOnce =
true;
MOZ_ASSERT(!gFirstIdleTask);
RefPtr<CancelableRunnable> firstIdleTask =
NewCancelableRunnableFunction(
"FirstIdleRunnable", FirstIdle);
gFirstIdleTask = firstIdleTask;
if (NS_FAILED(NS_DispatchToCurrentThreadQueue(firstIdleTask.forget(),
EventQueuePriority::Idle))) {
gFirstIdleTask = nullptr;
hasRunOnce =
false;
}
}
RefPtr<BrowsingContext> browsingContext =
BrowsingContext::Get(aWindowInit.context().mBrowsingContextId);
if (!browsingContext || browsingContext->IsDiscarded()) {
nsPrintfCString reason(
"%s initial %s BrowsingContext",
browsingContext ?
"discarded" :
"missing",
aIsTopLevel ?
"top" :
"frame");
MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning, (
"%s", reason.get()));
if (!aIsTopLevel) {
// Recover if the BrowsingContext is missing for a new subframe. The
// `ManagedEndpoint` instances will be automatically destroyed.
NS_WARNING(reason.get());
return IPC_OK();
}
// (these are the only possible values of `reason` at this point)
return browsingContext
? IPC_FAIL(
this,
"discarded initial top BrowsingContext")
: IPC_FAIL(
this,
"missing initial top BrowsingContext");
}
if (xpc::IsInAutomation() &&
StaticPrefs::
browser_tabs_remote_testOnly_failPBrowserCreation_enabled()) {
nsAutoCString idString;
if (NS_SUCCEEDED(Preferences::GetCString(
"browser.tabs.remote.testOnly.failPBrowserCreation.browsingContext",
idString))) {
nsresult rv = NS_OK;
uint64_t bcid = idString.ToInteger64(&rv);
if (NS_SUCCEEDED(rv) && bcid == browsingContext->Id()) {
NS_WARNING(
"Injecting artificial PBrowser creation failure");
return IPC_OK();
}
}
}
if (!aWindowInit.isInitialDocument() ||
!NS_IsAboutBlank(aWindowInit.documentURI())) {
return IPC_FAIL(
this,
"Logic in CreateDocumentViewerForActor currently requires "
"actors to be initial about:blank documents");
}
--> --------------------
--> maximum size reached
--> --------------------