1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "UtilityProcessManager.h"
8 #include "JSOracleParent.h"
9 #include "mozilla/ipc/UtilityProcessHost.h"
10 #include "mozilla/MemoryReportingProcess.h"
11 #include "mozilla/Preferences.h"
12 #include "mozilla/ProfilerMarkers.h"
13 #include "mozilla/StaticPrefs_media.h"
14 #include "mozilla/SyncRunnable.h" // for LaunchUtilityProcess
15 #include "mozilla/ipc/UtilityProcessParent.h"
16 #include "mozilla/ipc/UtilityAudioDecoderChild.h"
17 #include "mozilla/ipc/UtilityAudioDecoderParent.h"
18 #include "mozilla/dom/ContentParent.h"
19 #include "mozilla/ipc/Endpoint.h"
20 #include "mozilla/ipc/UtilityProcessSandboxing.h"
21 #include "mozilla/ipc/ProcessChild.h"
22 #include "nsAppRunner.h"
23 #include "nsContentUtils.h"
26 # include "mozilla/dom/WindowsUtilsParent.h"
29 #include "mozilla/GeckoArgs.h"
31 namespace mozilla::ipc
{
33 extern LazyLogModule gUtilityProcessLog
;
34 #define LOGD(...) MOZ_LOG(gUtilityProcessLog, LogLevel::Debug, (__VA_ARGS__))
36 static StaticRefPtr
<UtilityProcessManager
> sSingleton
;
38 static bool sXPCOMShutdown
= false;
40 bool UtilityProcessManager::IsShutdown() const {
41 MOZ_ASSERT(NS_IsMainThread());
42 return sXPCOMShutdown
|| !sSingleton
;
45 RefPtr
<UtilityProcessManager
> UtilityProcessManager::GetSingleton() {
46 MOZ_ASSERT(XRE_IsParentProcess());
47 MOZ_ASSERT(NS_IsMainThread());
49 if (!sXPCOMShutdown
&& sSingleton
== nullptr) {
50 sSingleton
= new UtilityProcessManager();
55 RefPtr
<UtilityProcessManager
> UtilityProcessManager::GetIfExists() {
56 MOZ_ASSERT(NS_IsMainThread());
60 UtilityProcessManager::UtilityProcessManager() : mObserver(new Observer(this)) {
61 LOGD("[%p] UtilityProcessManager::UtilityProcessManager", this);
63 // Start listening for pref changes so we can
64 // forward them to the process once it is running.
65 nsContentUtils::RegisterShutdownObserver(mObserver
);
66 Preferences::AddStrongObserver(mObserver
, "");
69 UtilityProcessManager::~UtilityProcessManager() {
70 LOGD("[%p] UtilityProcessManager::~UtilityProcessManager", this);
72 // The Utility process should ALL have already been shut down.
73 MOZ_ASSERT(NoMoreProcesses());
76 NS_IMPL_ISUPPORTS(UtilityProcessManager::Observer
, nsIObserver
);
78 UtilityProcessManager::Observer::Observer(
79 RefPtr
<UtilityProcessManager
> aManager
)
80 : mManager(std::move(aManager
)) {}
83 UtilityProcessManager::Observer::Observe(nsISupports
* aSubject
,
85 const char16_t
* aData
) {
86 if (!strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
)) {
87 mManager
->OnXPCOMShutdown();
88 } else if (!strcmp(aTopic
, "nsPref:changed")) {
89 mManager
->OnPreferenceChange(aData
);
94 void UtilityProcessManager::OnXPCOMShutdown() {
95 LOGD("[%p] UtilityProcessManager::OnXPCOMShutdown", this);
97 MOZ_ASSERT(NS_IsMainThread());
98 sXPCOMShutdown
= true;
99 nsContentUtils::UnregisterShutdownObserver(mObserver
);
100 CleanShutdownAllProcesses();
103 void UtilityProcessManager::OnPreferenceChange(const char16_t
* aData
) {
104 MOZ_ASSERT(NS_IsMainThread());
105 if (NoMoreProcesses()) {
106 // Process hasn't been launched yet
109 // We know prefs are ASCII here.
110 NS_LossyConvertUTF16toASCII
strData(aData
);
112 mozilla::dom::Pref
pref(strData
, /* isLocked */ false,
113 /* isSanitized */ false, Nothing(), Nothing());
114 Preferences::GetPreference(&pref
, GeckoProcessType_Utility
,
115 /* remoteType */ ""_ns
);
117 for (auto& p
: mProcesses
) {
122 if (p
->mProcessParent
) {
123 Unused
<< p
->mProcessParent
->SendPreferenceUpdate(pref
);
124 } else if (IsProcessLaunching(p
->mSandbox
)) {
125 p
->mQueuedPrefs
.AppendElement(pref
);
130 RefPtr
<UtilityProcessManager::ProcessFields
> UtilityProcessManager::GetProcess(
131 SandboxingKind aSandbox
) {
132 if (!mProcesses
[aSandbox
]) {
136 return mProcesses
[aSandbox
];
139 RefPtr
<GenericNonExclusivePromise
> UtilityProcessManager::LaunchProcess(
140 SandboxingKind aSandbox
) {
141 LOGD("[%p] UtilityProcessManager::LaunchProcess SandboxingKind=%" PRIu64
,
144 MOZ_ASSERT(NS_IsMainThread());
147 NS_WARNING("Reject early LaunchProcess() for Shutdown");
148 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE
,
152 RefPtr
<ProcessFields
> p
= GetProcess(aSandbox
);
153 if (p
&& p
->mNumProcessAttempts
) {
154 // We failed to start the Utility process earlier, abort now.
155 NS_WARNING("Reject LaunchProcess() for earlier mNumProcessAttempts");
156 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE
,
160 if (p
&& p
->mLaunchPromise
&& p
->mProcess
) {
161 return p
->mLaunchPromise
;
165 p
= new ProcessFields(aSandbox
);
166 mProcesses
[aSandbox
] = p
;
169 std::vector
<std::string
> extraArgs
;
170 ProcessChild::AddPlatformBuildID(extraArgs
);
171 geckoargs::sSandboxingKind
.Put(aSandbox
, extraArgs
);
173 // The subprocess is launched asynchronously, so we
174 // wait for the promise to be resolved to acquire the IPDL actor.
175 p
->mProcess
= new UtilityProcessHost(aSandbox
, this);
176 if (!p
->mProcess
->Launch(extraArgs
)) {
177 p
->mNumProcessAttempts
++;
178 DestroyProcess(aSandbox
);
179 NS_WARNING("Reject LaunchProcess() for mNumProcessAttempts++");
180 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE
,
184 RefPtr
<UtilityProcessManager
> self
= this;
185 p
->mLaunchPromise
= p
->mProcess
->LaunchPromise()->Then(
186 GetMainThreadSerialEventTarget(), __func__
,
187 [self
, p
, aSandbox
](bool) {
188 if (self
->IsShutdown()) {
190 "Reject LaunchProcess() after LaunchPromise() for Shutdown");
191 return GenericNonExclusivePromise::CreateAndReject(
192 NS_ERROR_NOT_AVAILABLE
, __func__
);
195 if (self
->IsProcessDestroyed(aSandbox
)) {
197 "Reject LaunchProcess() after LaunchPromise() for destroyed "
199 return GenericNonExclusivePromise::CreateAndReject(
200 NS_ERROR_NOT_AVAILABLE
, __func__
);
203 p
->mProcessParent
= p
->mProcess
->GetActor();
205 // Flush any pref updates that happened during
206 // launch and weren't included in the blobs set
207 // up in LaunchUtilityProcess.
208 for (const mozilla::dom::Pref
& pref
: p
->mQueuedPrefs
) {
209 Unused
<< NS_WARN_IF(!p
->mProcessParent
->SendPreferenceUpdate(pref
));
211 p
->mQueuedPrefs
.Clear();
213 CrashReporter::AnnotateCrashReport(
214 CrashReporter::Annotation::UtilityProcessStatus
, "Running"_ns
);
216 return GenericNonExclusivePromise::CreateAndResolve(true, __func__
);
218 [self
, p
, aSandbox
](nsresult aError
) {
219 if (GetSingleton()) {
220 p
->mNumProcessAttempts
++;
221 self
->DestroyProcess(aSandbox
);
223 NS_WARNING("Reject LaunchProcess() for LaunchPromise() rejection");
224 return GenericNonExclusivePromise::CreateAndReject(aError
, __func__
);
227 return p
->mLaunchPromise
;
230 template <typename Actor
>
231 RefPtr
<GenericNonExclusivePromise
> UtilityProcessManager::StartUtility(
232 RefPtr
<Actor
> aActor
, SandboxingKind aSandbox
) {
234 "[%p] UtilityProcessManager::StartUtility actor=%p "
235 "SandboxingKind=%" PRIu64
,
236 this, aActor
.get(), aSandbox
);
238 TimeStamp utilityStart
= TimeStamp::Now();
241 MOZ_ASSERT(false, "Actor singleton failure");
242 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE
,
246 if (aActor
->CanSend()) {
247 // Actor has already been setup, so we:
248 // - know the process has been launched
249 // - the ipc actors are ready
250 PROFILER_MARKER_TEXT(
251 "UtilityProcessManager::StartUtility", IPC
,
252 MarkerOptions(MarkerTiming::InstantNow()),
253 nsPrintfCString("SandboxingKind=%" PRIu64
" aActor->CanSend()",
255 return GenericNonExclusivePromise::CreateAndResolve(true, __func__
);
258 RefPtr
<UtilityProcessManager
> self
= this;
259 return LaunchProcess(aSandbox
)->Then(
260 GetMainThreadSerialEventTarget(), __func__
,
261 [self
, aActor
, aSandbox
, utilityStart
]() {
262 RefPtr
<UtilityProcessParent
> utilityParent
=
263 self
->GetProcessParent(aSandbox
);
264 if (!utilityParent
) {
265 NS_WARNING("Missing parent in StartUtility");
266 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE
,
270 // It is possible if multiple processes concurrently request a utility
271 // actor that the previous CanSend() check returned false for both but
272 // that by the time we have started our process for real, one of them
273 // has already been able to establish the IPC connection and thus we
274 // would perform more than one Open() call.
276 // The tests within browser_utility_multipleAudio.js should be able to
277 // catch that behavior.
278 if (!aActor
->CanSend()) {
279 nsresult rv
= aActor
->BindToUtilityProcess(utilityParent
);
281 MOZ_ASSERT(false, "Protocol endpoints failure");
282 return GenericNonExclusivePromise::CreateAndReject(rv
, __func__
);
285 MOZ_DIAGNOSTIC_ASSERT(aActor
->CanSend(), "IPC established for actor");
286 self
->RegisterActor(utilityParent
, aActor
->GetActorName());
289 PROFILER_MARKER_TEXT(
290 "UtilityProcessManager::StartUtility", IPC
,
291 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(utilityStart
)),
292 nsPrintfCString("SandboxingKind=%" PRIu64
" Resolve", aSandbox
));
293 return GenericNonExclusivePromise::CreateAndResolve(true, __func__
);
295 [self
, aSandbox
, utilityStart
](nsresult aError
) {
296 NS_WARNING("Reject StartUtility() for LaunchProcess() rejection");
297 if (!self
->IsShutdown()) {
298 NS_WARNING("Reject StartUtility() when !IsShutdown()");
300 PROFILER_MARKER_TEXT(
301 "UtilityProcessManager::StartUtility", IPC
,
302 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(utilityStart
)),
303 nsPrintfCString("SandboxingKind=%" PRIu64
" Reject", aSandbox
));
304 return GenericNonExclusivePromise::CreateAndReject(aError
, __func__
);
308 RefPtr
<UtilityProcessManager::StartRemoteDecodingUtilityPromise
>
309 UtilityProcessManager::StartProcessForRemoteMediaDecoding(
310 base::ProcessId aOtherProcess
, SandboxingKind aSandbox
) {
311 // Not supported kinds.
312 if (aSandbox
!= SandboxingKind::GENERIC_UTILITY
313 #ifdef MOZ_APPLEMEDIA
314 && aSandbox
!= SandboxingKind::UTILITY_AUDIO_DECODING_APPLE_MEDIA
317 && aSandbox
!= SandboxingKind::UTILITY_AUDIO_DECODING_WMF
319 #ifdef MOZ_WMF_MEDIA_ENGINE
320 && aSandbox
!= SandboxingKind::MF_MEDIA_ENGINE_CDM
323 return StartRemoteDecodingUtilityPromise::CreateAndReject(NS_ERROR_FAILURE
,
326 TimeStamp remoteDecodingStart
= TimeStamp::Now();
328 RefPtr
<UtilityProcessManager
> self
= this;
329 RefPtr
<UtilityAudioDecoderChild
> uadc
=
330 UtilityAudioDecoderChild::GetSingleton(aSandbox
);
331 MOZ_ASSERT(uadc
, "Unable to get a singleton for UtilityAudioDecoderChild");
332 return StartUtility(uadc
, aSandbox
)
334 GetMainThreadSerialEventTarget(), __func__
,
335 [self
, uadc
, aOtherProcess
, aSandbox
, remoteDecodingStart
]() {
336 RefPtr
<UtilityProcessParent
> parent
=
337 self
->GetProcessParent(aSandbox
);
339 NS_WARNING("UtilityAudioDecoderParent lost in the middle");
340 return StartRemoteDecodingUtilityPromise::CreateAndReject(
341 NS_ERROR_FAILURE
, __func__
);
344 if (!uadc
->CanSend()) {
345 NS_WARNING("UtilityAudioDecoderChild lost in the middle");
346 return StartRemoteDecodingUtilityPromise::CreateAndReject(
347 NS_ERROR_FAILURE
, __func__
);
350 base::ProcessId process
= parent
->OtherPid();
352 Endpoint
<PRemoteDecoderManagerChild
> childPipe
;
353 Endpoint
<PRemoteDecoderManagerParent
> parentPipe
;
354 nsresult rv
= PRemoteDecoderManager::CreateEndpoints(
355 process
, aOtherProcess
, &parentPipe
, &childPipe
);
357 MOZ_ASSERT(false, "Could not create content remote decoder");
358 return StartRemoteDecodingUtilityPromise::CreateAndReject(
362 if (!uadc
->SendNewContentRemoteDecoderManager(
363 std::move(parentPipe
))) {
364 MOZ_ASSERT(false, "SendNewContentRemoteDecoderManager failure");
365 return StartRemoteDecodingUtilityPromise::CreateAndReject(
366 NS_ERROR_FAILURE
, __func__
);
369 #ifdef MOZ_WMF_MEDIA_ENGINE
370 if (aSandbox
== SandboxingKind::MF_MEDIA_ENGINE_CDM
&&
371 !uadc
->CreateVideoBridge()) {
372 MOZ_ASSERT(false, "Failed to create video bridge");
373 return StartRemoteDecodingUtilityPromise::CreateAndReject(
374 NS_ERROR_FAILURE
, __func__
);
377 PROFILER_MARKER_TEXT(
378 "UtilityProcessManager::StartProcessForRemoteMediaDecoding",
381 MarkerTiming::IntervalUntilNowFrom(remoteDecodingStart
)),
383 return StartRemoteDecodingUtilityPromise::CreateAndResolve(
384 std::move(childPipe
), __func__
);
386 [self
, remoteDecodingStart
](nsresult aError
) {
388 "Reject StartProcessForRemoteMediaDecoding() for "
389 "StartUtility() rejection");
390 PROFILER_MARKER_TEXT(
391 "UtilityProcessManager::StartProcessForRemoteMediaDecoding",
394 MarkerTiming::IntervalUntilNowFrom(remoteDecodingStart
)),
396 return StartRemoteDecodingUtilityPromise::CreateAndReject(aError
,
401 RefPtr
<UtilityProcessManager::JSOraclePromise
>
402 UtilityProcessManager::StartJSOracle(dom::JSOracleParent
* aParent
) {
403 return StartUtility(RefPtr
{aParent
}, SandboxingKind::GENERIC_UTILITY
);
410 RefPtr
<UtilityProcessManager::WindowsUtilsPromise
>
411 UtilityProcessManager::GetWindowsUtilsPromise() {
412 TimeStamp windowsUtilsStart
= TimeStamp::Now();
413 RefPtr
<UtilityProcessManager
> self
= this;
414 if (!mWindowsUtils
) {
415 mWindowsUtils
= new dom::WindowsUtilsParent();
418 RefPtr
<dom::WindowsUtilsParent
> wup
= mWindowsUtils
;
419 MOZ_ASSERT(wup
, "Unable to get a singleton for WindowsUtils");
420 return StartUtility(wup
, SandboxingKind::WINDOWS_UTILS
)
422 GetMainThreadSerialEventTarget(), __func__
,
423 [self
, wup
, windowsUtilsStart
]() {
424 if (!wup
->CanSend()) {
425 MOZ_ASSERT(false, "WindowsUtilsParent can't send");
426 return WindowsUtilsPromise::CreateAndReject(NS_ERROR_FAILURE
,
429 PROFILER_MARKER_TEXT(
430 "UtilityProcessManager::GetWindowsUtilsPromise", OTHER
,
432 MarkerTiming::IntervalUntilNowFrom(windowsUtilsStart
)),
434 return WindowsUtilsPromise::CreateAndResolve(wup
, __func__
);
436 [self
, windowsUtilsStart
](nsresult aError
) {
437 NS_WARNING("StartUtility rejected promise for PWindowsUtils");
438 PROFILER_MARKER_TEXT(
439 "UtilityProcessManager::GetWindowsUtilsPromise", OTHER
,
441 MarkerTiming::IntervalUntilNowFrom(windowsUtilsStart
)),
443 return WindowsUtilsPromise::CreateAndReject(aError
, __func__
);
447 void UtilityProcessManager::ReleaseWindowsUtils() { mWindowsUtils
= nullptr; }
451 bool UtilityProcessManager::IsProcessLaunching(SandboxingKind aSandbox
) {
452 MOZ_ASSERT(NS_IsMainThread());
454 RefPtr
<ProcessFields
> p
= GetProcess(aSandbox
);
456 MOZ_CRASH("Cannot check process launching with no process");
460 return p
->mProcess
&& !(p
->mProcessParent
);
463 bool UtilityProcessManager::IsProcessDestroyed(SandboxingKind aSandbox
) {
464 MOZ_ASSERT(NS_IsMainThread());
465 RefPtr
<ProcessFields
> p
= GetProcess(aSandbox
);
467 MOZ_CRASH("Cannot check process destroyed with no process");
470 return !p
->mProcess
&& !p
->mProcessParent
;
473 void UtilityProcessManager::OnProcessUnexpectedShutdown(
474 UtilityProcessHost
* aHost
) {
475 MOZ_ASSERT(NS_IsMainThread());
477 for (auto& it
: mProcesses
) {
478 if (it
&& it
->mProcess
&& it
->mProcess
== aHost
) {
479 it
->mNumUnexpectedCrashes
++;
480 DestroyProcess(it
->mSandbox
);
486 "Called UtilityProcessManager::OnProcessUnexpectedShutdown with invalid "
490 void UtilityProcessManager::CleanShutdownAllProcesses() {
491 LOGD("[%p] UtilityProcessManager::CleanShutdownAllProcesses", this);
493 for (auto& it
: mProcesses
) {
495 DestroyProcess(it
->mSandbox
);
500 void UtilityProcessManager::CleanShutdown(SandboxingKind aSandbox
) {
501 LOGD("[%p] UtilityProcessManager::CleanShutdown SandboxingKind=%" PRIu64
,
504 DestroyProcess(aSandbox
);
507 uint16_t UtilityProcessManager::AliveProcesses() {
509 for (auto& p
: mProcesses
) {
517 bool UtilityProcessManager::NoMoreProcesses() { return AliveProcesses() == 0; }
519 void UtilityProcessManager::DestroyProcess(SandboxingKind aSandbox
) {
520 LOGD("[%p] UtilityProcessManager::DestroyProcess SandboxingKind=%" PRIu64
,
523 MOZ_RELEASE_ASSERT(NS_IsMainThread());
525 if (AliveProcesses() <= 1) {
527 Preferences::RemoveObserver(mObserver
, "");
533 RefPtr
<ProcessFields
> p
= GetProcess(aSandbox
);
538 p
->mQueuedPrefs
.Clear();
539 p
->mProcessParent
= nullptr;
545 p
->mProcess
->Shutdown();
546 p
->mProcess
= nullptr;
548 mProcesses
[aSandbox
] = nullptr;
550 CrashReporter::AnnotateCrashReport(
551 CrashReporter::Annotation::UtilityProcessStatus
, "Destroyed"_ns
);
553 if (NoMoreProcesses()) {
554 sSingleton
= nullptr;
558 Maybe
<base::ProcessId
> UtilityProcessManager::ProcessPid(
559 SandboxingKind aSandbox
) {
560 MOZ_ASSERT(NS_IsMainThread());
561 RefPtr
<ProcessFields
> p
= GetProcess(aSandbox
);
565 if (p
->mProcessParent
) {
566 return Some(p
->mProcessParent
->OtherPid());
571 class UtilityMemoryReporter
: public MemoryReportingProcess
{
573 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UtilityMemoryReporter
, override
)
575 explicit UtilityMemoryReporter(UtilityProcessParent
* aParent
) {
579 bool IsAlive() const override
{ return bool(GetParent()); }
581 bool SendRequestMemoryReport(
582 const uint32_t& aGeneration
, const bool& aAnonymize
,
583 const bool& aMinimizeMemoryUsage
,
584 const Maybe
<ipc::FileDescriptor
>& aDMDFile
) override
{
585 RefPtr
<UtilityProcessParent
> parent
= GetParent();
590 return parent
->SendRequestMemoryReport(aGeneration
, aAnonymize
,
591 aMinimizeMemoryUsage
, aDMDFile
);
594 int32_t Pid() const override
{
595 if (RefPtr
<UtilityProcessParent
> parent
= GetParent()) {
596 return (int32_t)parent
->OtherPid();
602 RefPtr
<UtilityProcessParent
> GetParent() const { return mParent
; }
604 RefPtr
<UtilityProcessParent
> mParent
= nullptr;
607 ~UtilityMemoryReporter() = default;
610 RefPtr
<MemoryReportingProcess
> UtilityProcessManager::GetProcessMemoryReporter(
611 UtilityProcessParent
* parent
) {
612 return new UtilityMemoryReporter(parent
);
615 } // namespace mozilla::ipc