зеркало из https://github.com/mozilla/gecko-dev.git
410 строки
13 KiB
C++
410 строки
13 KiB
C++
/* -*- 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 "UtilityProcessChild.h"
|
|
|
|
#include "mozilla/AppShutdown.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "mozilla/dom/ContentParent.h"
|
|
#include "mozilla/dom/JSOracleChild.h"
|
|
#include "mozilla/dom/MemoryReportRequest.h"
|
|
#include "mozilla/ipc/CrashReporterClient.h"
|
|
#include "mozilla/ipc/Endpoint.h"
|
|
#include "mozilla/ipc/UtilityProcessManager.h"
|
|
#include "mozilla/ipc/UtilityProcessSandboxing.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/RemoteDecoderManagerParent.h"
|
|
|
|
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
|
|
# include "mozilla/Sandbox.h"
|
|
# include "mozilla/SandboxProfilerObserver.h"
|
|
#endif
|
|
|
|
#if defined(XP_OPENBSD) && defined(MOZ_SANDBOX)
|
|
# include "mozilla/SandboxSettings.h"
|
|
#endif
|
|
|
|
#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
|
|
# include "mozilla/SandboxTestingChild.h"
|
|
#endif
|
|
|
|
#include "mozilla/Telemetry.h"
|
|
|
|
#if defined(XP_WIN)
|
|
# include "mozilla/WinDllServices.h"
|
|
# include "mozilla/dom/WindowsUtilsChild.h"
|
|
# include "mozilla/widget/filedialog/WinFileDialogChild.h"
|
|
#endif
|
|
|
|
#include "nsDebugImpl.h"
|
|
#include "nsIXULRuntime.h"
|
|
#include "nsThreadManager.h"
|
|
#include "GeckoProfiler.h"
|
|
|
|
#include "mozilla/ipc/ProcessChild.h"
|
|
#include "mozilla/FOGIPC.h"
|
|
#include "mozilla/glean/GleanMetrics.h"
|
|
|
|
#include "mozilla/Services.h"
|
|
|
|
namespace mozilla::ipc {
|
|
|
|
using namespace layers;
|
|
|
|
static StaticMutex sUtilityProcessChildMutex;
|
|
static StaticRefPtr<UtilityProcessChild> sUtilityProcessChild
|
|
MOZ_GUARDED_BY(sUtilityProcessChildMutex);
|
|
|
|
UtilityProcessChild::UtilityProcessChild() : mChildStartTime(TimeStamp::Now()) {
|
|
nsDebugImpl::SetMultiprocessMode("Utility");
|
|
}
|
|
|
|
UtilityProcessChild::~UtilityProcessChild() = default;
|
|
|
|
/* static */
|
|
RefPtr<UtilityProcessChild> UtilityProcessChild::GetSingleton() {
|
|
MOZ_ASSERT(XRE_IsUtilityProcess());
|
|
if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal)) {
|
|
return nullptr;
|
|
}
|
|
StaticMutexAutoLock lock(sUtilityProcessChildMutex);
|
|
if (!sUtilityProcessChild) {
|
|
sUtilityProcessChild = new UtilityProcessChild();
|
|
}
|
|
return sUtilityProcessChild;
|
|
}
|
|
|
|
/* static */
|
|
RefPtr<UtilityProcessChild> UtilityProcessChild::Get() {
|
|
StaticMutexAutoLock lock(sUtilityProcessChildMutex);
|
|
return sUtilityProcessChild;
|
|
}
|
|
|
|
bool UtilityProcessChild::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
|
|
const nsCString& aParentBuildID,
|
|
uint64_t aSandboxingKind) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// Initialize the thread manager before starting IPC. Otherwise, messages
|
|
// may be posted to the main thread and we won't be able to process them.
|
|
if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) {
|
|
return false;
|
|
}
|
|
|
|
// Now it's safe to start IPC.
|
|
if (NS_WARN_IF(!aEndpoint.Bind(this))) {
|
|
return false;
|
|
}
|
|
|
|
// 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.get())) {
|
|
// 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.
|
|
ipc::ProcessChild::QuickExit();
|
|
}
|
|
|
|
// Init crash reporter support.
|
|
ipc::CrashReporterClient::InitSingleton(this);
|
|
|
|
if (NS_FAILED(NS_InitMinimalXPCOM())) {
|
|
return false;
|
|
}
|
|
|
|
mSandbox = (SandboxingKind)aSandboxingKind;
|
|
|
|
// At the moment, only ORB uses JSContext in the
|
|
// Utility Process and ORB uses GENERIC_UTILITY
|
|
if (mSandbox == SandboxingKind::GENERIC_UTILITY) {
|
|
if (!JS_FrontendOnlyInit()) {
|
|
return false;
|
|
}
|
|
#if defined(__OpenBSD__) && defined(MOZ_SANDBOX)
|
|
// Bug 1823458: delay pledge initialization, otherwise
|
|
// JS_FrontendOnlyInit triggers sysctl(KERN_PROC_ID) which isnt
|
|
// permitted with the current pledge.utility config
|
|
StartOpenBSDSandbox(GeckoProcessType_Utility, mSandbox);
|
|
#endif
|
|
}
|
|
|
|
profiler_set_process_name(nsCString("Utility Process"));
|
|
|
|
// Notify the parent process that we have finished our init and that it can
|
|
// now resolve the pending promise of process startup
|
|
SendInitCompleted();
|
|
|
|
PROFILER_MARKER_UNTYPED(
|
|
"UtilityProcessChild::SendInitCompleted", IPC,
|
|
MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
|
|
|
|
RunOnShutdown(
|
|
[sandboxKind = mSandbox] {
|
|
StaticMutexAutoLock lock(sUtilityProcessChildMutex);
|
|
sUtilityProcessChild = nullptr;
|
|
if (sandboxKind == SandboxingKind::GENERIC_UTILITY) {
|
|
JS_FrontendOnlyShutDown();
|
|
}
|
|
},
|
|
ShutdownPhase::XPCOMShutdownFinal);
|
|
|
|
return true;
|
|
}
|
|
|
|
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
|
|
extern "C" {
|
|
void CGSShutdownServerConnections();
|
|
};
|
|
#endif
|
|
|
|
mozilla::ipc::IPCResult UtilityProcessChild::RecvInit(
|
|
const Maybe<FileDescriptor>& aBrokerFd,
|
|
const bool& aCanRecordReleaseTelemetry,
|
|
const bool& aIsReadyForBackgroundProcessing) {
|
|
// Do this now (before closing WindowServer on macOS) to avoid risking
|
|
// blocking in GetCurrentProcess() called on that platform
|
|
mozilla::ipc::SetThisProcessName("Utility Process");
|
|
|
|
#if defined(MOZ_SANDBOX)
|
|
# if defined(XP_MACOSX)
|
|
// 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.
|
|
CGSShutdownServerConnections();
|
|
|
|
# elif defined(XP_LINUX)
|
|
int fd = -1;
|
|
if (aBrokerFd.isSome()) {
|
|
fd = aBrokerFd.value().ClonePlatformHandle().release();
|
|
}
|
|
|
|
RegisterProfilerObserversForSandboxProfiler();
|
|
SetUtilitySandbox(fd, mSandbox);
|
|
|
|
# endif // XP_MACOSX/XP_LINUX
|
|
#endif // MOZ_SANDBOX
|
|
|
|
#if defined(XP_WIN)
|
|
if (aCanRecordReleaseTelemetry) {
|
|
RefPtr<DllServices> dllSvc(DllServices::Get());
|
|
dllSvc->StartUntrustedModulesProcessor(aIsReadyForBackgroundProcessing);
|
|
}
|
|
#endif // defined(XP_WIN)
|
|
|
|
PROFILER_MARKER_UNTYPED(
|
|
"UtilityProcessChild::RecvInit", IPC,
|
|
MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UtilityProcessChild::RecvPreferenceUpdate(
|
|
const Pref& aPref) {
|
|
Preferences::SetPreference(aPref);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UtilityProcessChild::RecvInitProfiler(
|
|
Endpoint<PProfilerChild>&& aEndpoint) {
|
|
mProfilerController = ChildProfilerController::Create(std::move(aEndpoint));
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UtilityProcessChild::RecvRequestMemoryReport(
|
|
const uint32_t& aGeneration, const bool& aAnonymize,
|
|
const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile,
|
|
const RequestMemoryReportResolver& aResolver) {
|
|
nsPrintfCString processName("Utility (pid %" PRIPID
|
|
", sandboxingKind %" PRIu64 ")",
|
|
base::GetCurrentProcId(), mSandbox);
|
|
|
|
mozilla::dom::MemoryReportRequestClient::Start(
|
|
aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, processName,
|
|
[&](const MemoryReport& aReport) {
|
|
Unused << GetSingleton()->SendAddMemoryReport(aReport);
|
|
},
|
|
aResolver);
|
|
return IPC_OK();
|
|
}
|
|
|
|
#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
|
|
mozilla::ipc::IPCResult UtilityProcessChild::RecvInitSandboxTesting(
|
|
Endpoint<PSandboxTestingChild>&& aEndpoint) {
|
|
if (!SandboxTestingChild::Initialize(std::move(aEndpoint))) {
|
|
return IPC_FAIL(
|
|
this, "InitSandboxTesting failed to initialise the child process.");
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
#endif
|
|
|
|
mozilla::ipc::IPCResult UtilityProcessChild::RecvFlushFOGData(
|
|
FlushFOGDataResolver&& aResolver) {
|
|
glean::FlushFOGData(std::move(aResolver));
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UtilityProcessChild::RecvTestTriggerMetrics(
|
|
TestTriggerMetricsResolver&& aResolve) {
|
|
mozilla::glean::test_only_ipc::a_counter.Add(
|
|
nsIXULRuntime::PROCESS_TYPE_UTILITY);
|
|
aResolve(true);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UtilityProcessChild::RecvTestTelemetryProbes() {
|
|
const uint32_t kExpectedUintValue = 42;
|
|
Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UTILITY_ONLY_UINT,
|
|
kExpectedUintValue);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
UtilityProcessChild::RecvStartUtilityAudioDecoderService(
|
|
Endpoint<PUtilityAudioDecoderParent>&& aEndpoint,
|
|
nsTArray<gfx::GfxVarUpdate>&& aUpdates) {
|
|
PROFILER_MARKER_UNTYPED(
|
|
"UtilityProcessChild::RecvStartUtilityAudioDecoderService", MEDIA,
|
|
MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
|
|
mUtilityAudioDecoderInstance =
|
|
new UtilityAudioDecoderParent(std::move(aUpdates));
|
|
if (!mUtilityAudioDecoderInstance) {
|
|
return IPC_FAIL(this, "Failed to create UtilityAudioDecoderParent");
|
|
}
|
|
|
|
mUtilityAudioDecoderInstance->Start(std::move(aEndpoint));
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UtilityProcessChild::RecvStartJSOracleService(
|
|
Endpoint<PJSOracleChild>&& aEndpoint) {
|
|
PROFILER_MARKER_UNTYPED(
|
|
"UtilityProcessChild::RecvStartJSOracleService", JS,
|
|
MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
|
|
mJSOracleInstance = new mozilla::dom::JSOracleChild();
|
|
if (!mJSOracleInstance) {
|
|
return IPC_FAIL(this, "Failed to create JSOracleParent");
|
|
}
|
|
|
|
mJSOracleInstance->Start(std::move(aEndpoint));
|
|
return IPC_OK();
|
|
}
|
|
|
|
#if defined(XP_WIN)
|
|
mozilla::ipc::IPCResult UtilityProcessChild::RecvStartWindowsUtilsService(
|
|
Endpoint<dom::PWindowsUtilsChild>&& aEndpoint) {
|
|
PROFILER_MARKER_UNTYPED(
|
|
"UtilityProcessChild::RecvStartWindowsUtilsService", OTHER,
|
|
MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
|
|
mWindowsUtilsInstance = new dom::WindowsUtilsChild();
|
|
if (!mWindowsUtilsInstance) {
|
|
return IPC_FAIL(this, "Failed to create WindowsUtilsChild");
|
|
}
|
|
|
|
[[maybe_unused]] bool ok = std::move(aEndpoint).Bind(mWindowsUtilsInstance);
|
|
MOZ_ASSERT(ok);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UtilityProcessChild::RecvStartWinFileDialogService(
|
|
Endpoint<widget::filedialog::PWinFileDialogChild>&& aEndpoint) {
|
|
PROFILER_MARKER_UNTYPED(
|
|
"UtilityProcessChild::RecvStartWinFileDialogService", OTHER,
|
|
MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
|
|
|
|
auto instance = MakeRefPtr<widget::filedialog::WinFileDialogChild>();
|
|
if (!instance) {
|
|
return IPC_FAIL(this, "Failed to create WinFileDialogChild");
|
|
}
|
|
|
|
bool const ok = std::move(aEndpoint).Bind(instance.get());
|
|
if (!ok) {
|
|
return IPC_FAIL(this, "Failed to bind created WinFileDialogChild");
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UtilityProcessChild::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
|
|
UtilityProcessChild::RecvUnblockUntrustedModulesThread() {
|
|
if (nsCOMPtr<nsIObserverService> obs =
|
|
mozilla::services::GetObserverService()) {
|
|
obs->NotifyObservers(nullptr, "unblock-untrusted-modules-thread", nullptr);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
#endif // defined(XP_WIN)
|
|
|
|
void UtilityProcessChild::ActorDestroy(ActorDestroyReason aWhy) {
|
|
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
|
|
DestroySandboxProfiler();
|
|
#endif
|
|
|
|
if (AbnormalShutdown == aWhy) {
|
|
NS_WARNING("Shutting down Utility process early due to a crash!");
|
|
ipc::ProcessChild::QuickExit();
|
|
}
|
|
|
|
// Send the last bits of Glean data over to the main process.
|
|
glean::FlushFOGData(
|
|
[](ByteBuf&& aBuf) { glean::SendFOGData(std::move(aBuf)); });
|
|
|
|
#ifndef NS_FREE_PERMANENT_DATA
|
|
ProcessChild::QuickExit();
|
|
#else
|
|
|
|
if (mProfilerController) {
|
|
mProfilerController->Shutdown();
|
|
mProfilerController = nullptr;
|
|
}
|
|
|
|
uint32_t timeout = 0;
|
|
if (mUtilityAudioDecoderInstance) {
|
|
mUtilityAudioDecoderInstance = nullptr;
|
|
timeout = 10 * 1000;
|
|
}
|
|
|
|
mJSOracleInstance = nullptr;
|
|
|
|
# ifdef XP_WIN
|
|
mWindowsUtilsInstance = nullptr;
|
|
# endif
|
|
|
|
// Wait until all RemoteDecoderManagerParent have closed.
|
|
// It is still possible some may not have clean up yet, and we might hit
|
|
// timeout. Our xpcom-shutdown listener should take care of cleaning the
|
|
// reference of our singleton.
|
|
//
|
|
// FIXME: Should move from using AsyncBlockers to proper
|
|
// nsIAsyncShutdownService once it is not JS, see bug 1760855
|
|
mShutdownBlockers.WaitUntilClear(timeout)->Then(
|
|
GetCurrentSerialEventTarget(), __func__, [&]() {
|
|
# ifdef XP_WIN
|
|
{
|
|
RefPtr<DllServices> dllSvc(DllServices::Get());
|
|
dllSvc->DisableFull();
|
|
}
|
|
# endif // defined(XP_WIN)
|
|
|
|
ipc::CrashReporterClient::DestroySingleton();
|
|
XRE_ShutdownChildProcess();
|
|
});
|
|
#endif // NS_FREE_PERMANENT_DATA
|
|
}
|
|
|
|
} // namespace mozilla::ipc
|