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/. */
7 #include "mozilla/ProcessHangMonitor.h"
8 #include "mozilla/ProcessHangMonitorIPC.h"
11 #include "xpcprivate.h"
13 #include "mozilla/Atomics.h"
14 #include "mozilla/BackgroundHangMonitor.h"
15 #include "mozilla/BasePrincipal.h"
16 #include "mozilla/dom/CancelContentJSOptionsBinding.h"
17 #include "mozilla/dom/CanonicalBrowsingContext.h"
18 #include "mozilla/dom/ContentParent.h"
19 #include "mozilla/dom/Document.h"
20 #include "mozilla/dom/Element.h"
21 #include "mozilla/dom/ScriptSettings.h"
22 #include "mozilla/dom/BrowserChild.h"
23 #include "mozilla/dom/BrowserParent.h"
24 #include "mozilla/ipc/Endpoint.h"
25 #include "mozilla/ipc/ProcessChild.h"
26 #include "mozilla/ipc/TaskFactory.h"
27 #include "mozilla/Monitor.h"
28 #include "mozilla/Preferences.h"
29 #include "mozilla/StaticMonitor.h"
30 #include "mozilla/StaticPrefs_browser.h"
31 #include "mozilla/StaticPrefs_dom.h"
32 #include "mozilla/StaticPtr.h"
33 #include "mozilla/Unused.h"
34 #include "mozilla/WeakPtr.h"
36 #include "MainThreadUtils.h"
37 #include "nsExceptionHandler.h"
38 #include "nsFrameLoader.h"
39 #include "nsIHangReport.h"
40 #include "nsIRemoteTab.h"
41 #include "nsNetUtil.h"
42 #include "nsQueryObject.h"
43 #include "nsThreadUtils.h"
45 #include "base/task.h"
46 #include "base/thread.h"
49 // For IsDebuggerPresent()
58 using namespace mozilla
;
59 using namespace mozilla::dom
;
60 using namespace mozilla::ipc
;
65 * Each process has its own ProcessHangMonitor singleton. This singleton exists
66 * as long as there is at least one content process in the system. Each content
67 * process has a HangMonitorChild and the chrome process has one
68 * HangMonitorParent per process. Each process (including the chrome process)
69 * runs a hang monitoring thread. The PHangMonitor actors are bound to this
70 * thread so that they never block on the main thread.
72 * When the content process detects a hang, it posts a task to its hang thread,
73 * which sends an IPC message to the hang thread in the parent. The parent
74 * cancels any ongoing CPOW requests and then posts a runnable to the main
75 * thread that notifies Firefox frontend code of the hang. The frontend code is
76 * passed an nsIHangReport, which can be used to terminate the hang.
78 * If the user chooses to terminate a script, a task is posted to the chrome
79 * process's hang monitoring thread, which sends an IPC message to the hang
80 * thread in the content process. That thread sets a flag to indicate that JS
81 * execution should be terminated the next time it hits the interrupt
82 * callback. A similar scheme is used for debugging slow scripts. If a content
83 * process or plug-in needs to be terminated, the chrome process does so
84 * directly, without messaging the content process.
89 /* Child process objects */
91 class HangMonitorChild
: public PProcessHangMonitorChild
,
92 public BackgroundHangAnnotator
{
94 NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
95 HangMonitorChild
, override
)
97 void Bind(Endpoint
<PProcessHangMonitorChild
>&& aEndpoint
);
99 using SlowScriptAction
= ProcessHangMonitor::SlowScriptAction
;
100 SlowScriptAction
NotifySlowScript(nsIBrowserChild
* aBrowserChild
,
101 const char* aFileName
,
102 const nsString
& aAddonId
,
103 const double aDuration
);
104 void NotifySlowScriptAsync(TabId aTabId
, const nsCString
& aFileName
,
105 const nsString
& aAddonId
, const double aDuration
);
107 bool IsDebuggerStartupComplete();
110 void ClearHangAsync();
111 void ClearPaintWhileInterruptingJS();
113 // MaybeStartPaintWhileInterruptingJS will notify the background hang monitor
114 // of activity if this is the first time calling it since
115 // ClearPaintWhileInterruptingJS. It should be callable from any thread, but
116 // you must be holding mMonitor if using it off the main thread, since it
117 // could race with ClearPaintWhileInterruptingJS.
118 void MaybeStartPaintWhileInterruptingJS();
120 mozilla::ipc::IPCResult
RecvTerminateScript() override
;
121 mozilla::ipc::IPCResult
RecvRequestContentJSInterrupt() override
;
122 mozilla::ipc::IPCResult
RecvBeginStartingDebugger() override
;
123 mozilla::ipc::IPCResult
RecvEndStartingDebugger() override
;
125 mozilla::ipc::IPCResult
RecvPaintWhileInterruptingJS(
126 const TabId
& aTabId
) override
;
128 mozilla::ipc::IPCResult
RecvUnloadLayersWhileInterruptingJS(
129 const TabId
& aTabId
) override
;
131 mozilla::ipc::IPCResult
RecvCancelContentJSExecutionIfRunning(
132 const TabId
& aTabId
, const nsIRemoteTab::NavigationType
& aNavigationType
,
133 const int32_t& aNavigationIndex
,
134 const mozilla::Maybe
<nsCString
>& aNavigationURI
,
135 const int32_t& aEpoch
) override
;
137 mozilla::ipc::IPCResult
RecvSetMainThreadQoSPriority(
138 const nsIThread::QoSPriority
& aQoSPriority
) override
;
140 void ActorDestroy(ActorDestroyReason aWhy
) override
;
142 bool InterruptCallback();
145 static HangMonitorChild
* Get() MOZ_REQUIRES(sMainThreadCapability
) {
149 static void CreateAndBind(ProcessHangMonitor
* aMonitor
,
150 Endpoint
<PProcessHangMonitorChild
>&& aEndpoint
);
152 void Dispatch(already_AddRefed
<nsIRunnable
> aRunnable
) {
153 mHangMonitor
->Dispatch(std::move(aRunnable
));
155 bool IsOnThread() { return mHangMonitor
->IsOnThread(); }
157 void AnnotateHang(BackgroundHangAnnotations
& aAnnotations
) override
;
160 friend class mozilla::ProcessHangMonitor
;
163 explicit HangMonitorChild(ProcessHangMonitor
* aMonitor
);
164 ~HangMonitorChild() override
;
166 void ShutdownOnThread();
168 static StaticRefPtr
<HangMonitorChild
> sInstance
169 MOZ_GUARDED_BY(sMainThreadCapability
);
171 const RefPtr
<ProcessHangMonitor
> mHangMonitor
;
174 // On macOS, the pthread_t is required to start a QoS class override. As we
175 // can't recover this from a PRThread*, we need to record it when the
176 // HangMonitorChild is initially created on the main thread.
177 const pthread_t mMainPThread
;
185 // These fields must be accessed with mMonitor held.
186 bool mTerminateScript
MOZ_GUARDED_BY(mMonitor
);
187 bool mStartDebugger
MOZ_GUARDED_BY(mMonitor
);
188 bool mFinishedStartingDebugger
MOZ_GUARDED_BY(mMonitor
);
190 // this variable is used to paint/unload layers
191 // if not set, no action required
192 // true means, we will paint. false - unload layers
193 Maybe
<bool> mPaintWhileInterruptingJS
MOZ_GUARDED_BY(mMonitor
);
194 TabId mPaintWhileInterruptingJSTab
MOZ_GUARDED_BY(mMonitor
);
195 bool mCancelContentJS
MOZ_GUARDED_BY(mMonitor
);
196 TabId mCancelContentJSTab
MOZ_GUARDED_BY(mMonitor
);
197 nsIRemoteTab::NavigationType mCancelContentJSNavigationType
198 MOZ_GUARDED_BY(mMonitor
);
199 int32_t mCancelContentJSNavigationIndex
MOZ_GUARDED_BY(mMonitor
);
200 mozilla::Maybe
<nsCString
> mCancelContentJSNavigationURI
201 MOZ_GUARDED_BY(mMonitor
);
202 int32_t mCancelContentJSEpoch
MOZ_GUARDED_BY(mMonitor
);
203 bool mShutdownDone
MOZ_GUARDED_BY(mMonitor
);
205 JSContext
* mContext
; // const after constructor
207 // This field is only accessed on the hang thread.
210 // Allows us to ensure we NotifyActivity only once, allowing
211 // either thread to do so.
212 Atomic
<bool> mPaintWhileInterruptingJSActive
;
215 StaticRefPtr
<HangMonitorChild
> HangMonitorChild::sInstance
;
217 /* Parent process objects */
219 class HangMonitorParent
;
221 class HangMonitoredProcess final
: public nsIHangReport
{
223 NS_DECL_THREADSAFE_ISUPPORTS
225 HangMonitoredProcess(HangMonitorParent
* aActor
, ContentParent
* aContentParent
)
226 : mActor(aActor
), mContentParent(aContentParent
) {}
228 NS_DECL_NSIHANGREPORT
230 // Called when a content process shuts down.
232 mContentParent
= nullptr;
237 * Sets the information associated with this hang: this includes the tab ID,
238 * filename, duration, and an add-on ID if it was caused by an add-on.
240 * @param aDumpId The ID of a minidump taken when the hang occurred
242 void SetSlowScriptData(const SlowScriptData
& aSlowScriptData
,
243 const nsAString
& aDumpId
) {
244 mSlowScriptData
= aSlowScriptData
;
249 mSlowScriptData
= SlowScriptData();
254 ~HangMonitoredProcess() = default;
256 // Everything here is main thread-only.
257 HangMonitorParent
* mActor
;
258 ContentParent
* mContentParent
;
259 SlowScriptData mSlowScriptData
;
260 nsAutoString mDumpId
;
263 class HangMonitorParent
: public PProcessHangMonitorParent
{
265 NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
266 HangMonitorParent
, override
)
268 explicit HangMonitorParent(ProcessHangMonitor
* aMonitor
);
270 void Bind(Endpoint
<PProcessHangMonitorParent
>&& aEndpoint
);
272 mozilla::ipc::IPCResult
RecvHangEvidence(
273 const SlowScriptData
& aSlowScriptData
) override
;
274 mozilla::ipc::IPCResult
RecvClearHang() override
;
276 void ActorDestroy(ActorDestroyReason aWhy
) override
;
278 void SetProcess(HangMonitoredProcess
* aProcess
) { mProcess
= aProcess
; }
282 void PaintWhileInterruptingJS(dom::BrowserParent
* aTab
);
284 void UnloadLayersWhileInterruptingJS(dom::BrowserParent
* aTab
);
285 void CancelContentJSExecutionIfRunning(
286 dom::BrowserParent
* aBrowserParent
,
287 nsIRemoteTab::NavigationType aNavigationType
,
288 const dom::CancelContentJSOptions
& aCancelContentJSOptions
);
290 void SetMainThreadQoSPriority(nsIThread::QoSPriority aQoSPriority
);
292 void TerminateScript();
293 void BeginStartingDebugger();
294 void EndStartingDebugger();
296 nsresult
Dispatch(already_AddRefed
<nsIRunnable
> aRunnable
) {
297 return mHangMonitor
->Dispatch(std::move(aRunnable
));
299 bool IsOnThread() { return mHangMonitor
->IsOnThread(); }
302 ~HangMonitorParent() override
= default;
304 void SendHangNotification(const SlowScriptData
& aSlowScriptData
,
305 const nsString
& aBrowserDumpId
);
307 void ClearHangNotification();
309 void PaintOrUnloadLayersWhileInterruptingJSOnThread(bool aPaint
,
311 void CancelContentJSExecutionIfRunningOnThread(
312 TabId aTabId
, nsIRemoteTab::NavigationType aNavigationType
,
313 int32_t aNavigationIndex
, nsIURI
* aNavigationURI
, int32_t aEpoch
);
316 void SetMainThreadQoSPriorityOnThread(nsIThread::QoSPriority aQoSPriority
);
319 void ShutdownOnThread();
321 const RefPtr
<ProcessHangMonitor
> mHangMonitor
;
323 // This field is only accessed on the hang thread.
329 RefPtr
<HangMonitoredProcess
> mProcess
;
331 // Must be accessed with mMonitor held.
332 bool mShutdownDone
MOZ_GUARDED_BY(mMonitor
);
333 mozilla::ipc::TaskFactory
<HangMonitorParent
> mMainThreadTaskFactory
334 MOZ_GUARDED_BY(mMonitor
);
339 /* HangMonitorChild implementation */
341 HangMonitorChild::HangMonitorChild(ProcessHangMonitor
* aMonitor
)
342 : mHangMonitor(aMonitor
),
344 mMainPThread(pthread_self()),
346 mMonitor("HangMonitorChild lock"),
348 mTerminateScript(false),
349 mStartDebugger(false),
350 mFinishedStartingDebugger(false),
351 mCancelContentJS(false),
352 mCancelContentJSNavigationType(nsIRemoteTab::NAVIGATE_BACK
),
353 mCancelContentJSNavigationIndex(0),
354 mCancelContentJSEpoch(0),
355 mShutdownDone(false),
357 mPaintWhileInterruptingJSActive(false) {
358 ReleaseAssertIsOnMainThread();
359 MOZ_ASSERT(!sInstance
);
361 mContext
= danger::GetJSContext();
364 HangMonitorChild::~HangMonitorChild() {
365 ReleaseAssertIsOnMainThread();
366 MOZ_ASSERT(sInstance
!= this);
369 void HangMonitorChild::CreateAndBind(
370 ProcessHangMonitor
* aMonitor
,
371 Endpoint
<PProcessHangMonitorChild
>&& aEndpoint
) {
372 ReleaseAssertIsOnMainThread();
373 MOZ_ASSERT(!sInstance
);
375 sInstance
= new HangMonitorChild(aMonitor
);
377 BackgroundHangMonitor::RegisterAnnotator(*sInstance
);
379 aMonitor
->Dispatch(NewRunnableMethod
<Endpoint
<PProcessHangMonitorChild
>&&>(
380 "HangMonitorChild::Bind", sInstance
.get(), &HangMonitorChild::Bind
,
381 std::move(aEndpoint
)));
384 bool HangMonitorChild::InterruptCallback() {
385 MOZ_RELEASE_ASSERT(NS_IsMainThread());
387 if (StaticPrefs::dom_abort_script_on_child_shutdown() &&
388 mozilla::ipc::ProcessChild::ExpectingShutdown()) {
389 // We preserve chrome JS from cancel, but not extension content JS.
390 if (!nsContentUtils::IsCallerChrome()) {
392 "HangMonitorChild::InterruptCallback: ExpectingShutdown, "
393 "canceling content JS execution.\n");
399 // Don't start painting if we're not in a good place to run script. We run
400 // chrome script during layout and such, and it wouldn't be good to interrupt
401 // painting code from there.
402 if (!nsContentUtils::IsSafeToRunScript()) {
406 Maybe
<bool> paintWhileInterruptingJS
;
407 TabId paintWhileInterruptingJSTab
;
410 MonitorAutoLock
lock(mMonitor
);
411 paintWhileInterruptingJS
= mPaintWhileInterruptingJS
;
412 paintWhileInterruptingJSTab
= mPaintWhileInterruptingJSTab
;
414 mPaintWhileInterruptingJS
.reset();
417 if (paintWhileInterruptingJS
.isSome()) {
418 RefPtr
<BrowserChild
> browserChild
=
419 BrowserChild::FindBrowserChild(paintWhileInterruptingJSTab
);
421 js::AutoAssertNoContentJS
nojs(mContext
);
422 if (paintWhileInterruptingJS
.value()) {
423 browserChild
->PaintWhileInterruptingJS();
425 browserChild
->UnloadLayersWhileInterruptingJS();
430 // Only handle the interrupt for cancelling content JS if we have a
431 // non-privileged script (i.e. not part of Gecko or an add-on).
432 JS::Rooted
<JSObject
*> global(mContext
, JS::CurrentGlobalOrNull(mContext
));
433 nsIPrincipal
* principal
= xpc::GetObjectPrincipal(global
);
434 if (principal
&& (principal
->IsSystemPrincipal() ||
435 principal
->GetIsAddonOrExpandedAddonPrincipal())) {
439 nsCOMPtr
<nsPIDOMWindowInner
> win
= xpc::WindowOrNull(global
);
444 bool cancelContentJS
;
445 TabId cancelContentJSTab
;
446 nsIRemoteTab::NavigationType cancelContentJSNavigationType
;
447 int32_t cancelContentJSNavigationIndex
;
448 mozilla::Maybe
<nsCString
> cancelContentJSNavigationURI
;
449 int32_t cancelContentJSEpoch
;
452 MonitorAutoLock
lock(mMonitor
);
453 cancelContentJS
= mCancelContentJS
;
454 cancelContentJSTab
= mCancelContentJSTab
;
455 cancelContentJSNavigationType
= mCancelContentJSNavigationType
;
456 cancelContentJSNavigationIndex
= mCancelContentJSNavigationIndex
;
457 cancelContentJSNavigationURI
= std::move(mCancelContentJSNavigationURI
);
458 cancelContentJSEpoch
= mCancelContentJSEpoch
;
460 mCancelContentJS
= false;
463 if (cancelContentJS
) {
464 js::AutoAssertNoContentJS
nojs(mContext
);
466 RefPtr
<BrowserChild
> browserChild
=
467 BrowserChild::FindBrowserChild(cancelContentJSTab
);
468 RefPtr
<BrowserChild
> browserChildFromWin
= BrowserChild::GetFrom(win
);
469 if (!browserChild
|| !browserChildFromWin
) {
473 TabId tabIdFromWin
= browserChildFromWin
->GetTabId();
474 if (tabIdFromWin
!= cancelContentJSTab
) {
475 // The currently-executing content JS doesn't belong to the tab that
476 // requested cancellation of JS. Just return and let the JS continue.
481 nsCOMPtr
<nsIURI
> uri
;
483 if (cancelContentJSNavigationURI
) {
484 rv
= NS_NewURI(getter_AddRefs(uri
), cancelContentJSNavigationURI
.value());
491 rv
= browserChild
->CanCancelContentJS(cancelContentJSNavigationType
,
492 cancelContentJSNavigationIndex
, uri
,
493 cancelContentJSEpoch
, &canCancel
);
494 if (NS_SUCCEEDED(rv
) && canCancel
) {
495 // Don't add this page to the BF cache, since we're cancelling its JS.
496 if (Document
* doc
= win
->GetExtantDoc()) {
497 doc
->DisallowBFCaching();
507 void HangMonitorChild::AnnotateHang(BackgroundHangAnnotations
& aAnnotations
) {
508 if (mPaintWhileInterruptingJSActive
) {
509 aAnnotations
.AddAnnotation(u
"PaintWhileInterruptingJS"_ns
, true);
513 void HangMonitorChild::Shutdown() {
514 ReleaseAssertIsOnMainThread();
516 BackgroundHangMonitor::UnregisterAnnotator(*this);
519 MonitorAutoLock
lock(mMonitor
);
520 while (!mShutdownDone
) {
525 MOZ_ASSERT(sInstance
== this);
529 void HangMonitorChild::ShutdownOnThread() {
530 MOZ_RELEASE_ASSERT(IsOnThread());
532 MonitorAutoLock
lock(mMonitor
);
533 mShutdownDone
= true;
537 void HangMonitorChild::ActorDestroy(ActorDestroyReason aWhy
) {
538 MOZ_RELEASE_ASSERT(IsOnThread());
542 // We use a task here to ensure that IPDL is finished with this
543 // HangMonitorChild before it gets deleted on the main thread.
544 Dispatch(NewNonOwningRunnableMethod("HangMonitorChild::ShutdownOnThread",
546 &HangMonitorChild::ShutdownOnThread
));
549 mozilla::ipc::IPCResult
HangMonitorChild::RecvTerminateScript() {
550 MOZ_RELEASE_ASSERT(IsOnThread());
552 MonitorAutoLock
lock(mMonitor
);
553 mTerminateScript
= true;
557 mozilla::ipc::IPCResult
HangMonitorChild::RecvRequestContentJSInterrupt() {
558 MOZ_RELEASE_ASSERT(IsOnThread());
560 // In order to cancel JS execution on shutdown, we expect that
561 // ProcessChild::NotifiedImpendingShutdown has been called before.
562 if (mozilla::ipc::ProcessChild::ExpectingShutdown()) {
563 CrashReporter::AppendToCrashReportAnnotation(
564 CrashReporter::Annotation::IPCShutdownState
,
565 "HangMonitorChild::RecvRequestContentJSInterrupt (expected)"_ns
);
567 CrashReporter::AppendToCrashReportAnnotation(
568 CrashReporter::Annotation::IPCShutdownState
,
569 "HangMonitorChild::RecvRequestContentJSInterrupt (unexpected)"_ns
);
571 JS_RequestInterruptCallback(mContext
);
575 mozilla::ipc::IPCResult
HangMonitorChild::RecvBeginStartingDebugger() {
576 MOZ_RELEASE_ASSERT(IsOnThread());
578 MonitorAutoLock
lock(mMonitor
);
579 mStartDebugger
= true;
583 mozilla::ipc::IPCResult
HangMonitorChild::RecvEndStartingDebugger() {
584 MOZ_RELEASE_ASSERT(IsOnThread());
586 MonitorAutoLock
lock(mMonitor
);
587 mFinishedStartingDebugger
= true;
591 mozilla::ipc::IPCResult
HangMonitorChild::RecvPaintWhileInterruptingJS(
592 const TabId
& aTabId
) {
593 MOZ_RELEASE_ASSERT(IsOnThread());
596 MonitorAutoLock
lock(mMonitor
);
597 MaybeStartPaintWhileInterruptingJS();
598 mPaintWhileInterruptingJS
= Some(true);
599 mPaintWhileInterruptingJSTab
= aTabId
;
602 JS_RequestInterruptCallback(mContext
);
607 mozilla::ipc::IPCResult
HangMonitorChild::RecvUnloadLayersWhileInterruptingJS(
608 const TabId
& aTabId
) {
609 MOZ_RELEASE_ASSERT(IsOnThread());
612 MonitorAutoLock
lock(mMonitor
);
613 MaybeStartPaintWhileInterruptingJS();
614 mPaintWhileInterruptingJS
= Some(false);
615 mPaintWhileInterruptingJSTab
= aTabId
;
618 JS_RequestInterruptCallback(mContext
);
623 void HangMonitorChild::MaybeStartPaintWhileInterruptingJS() {
624 mPaintWhileInterruptingJSActive
= true;
627 void HangMonitorChild::ClearPaintWhileInterruptingJS() {
628 MOZ_RELEASE_ASSERT(NS_IsMainThread());
629 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
630 mPaintWhileInterruptingJSActive
= false;
633 mozilla::ipc::IPCResult
HangMonitorChild::RecvCancelContentJSExecutionIfRunning(
634 const TabId
& aTabId
, const nsIRemoteTab::NavigationType
& aNavigationType
,
635 const int32_t& aNavigationIndex
,
636 const mozilla::Maybe
<nsCString
>& aNavigationURI
, const int32_t& aEpoch
) {
637 MOZ_RELEASE_ASSERT(IsOnThread());
640 MonitorAutoLock
lock(mMonitor
);
641 mCancelContentJS
= true;
642 mCancelContentJSTab
= aTabId
;
643 mCancelContentJSNavigationType
= aNavigationType
;
644 mCancelContentJSNavigationIndex
= aNavigationIndex
;
645 mCancelContentJSNavigationURI
= aNavigationURI
;
646 mCancelContentJSEpoch
= aEpoch
;
649 JS_RequestInterruptCallback(mContext
);
654 mozilla::ipc::IPCResult
HangMonitorChild::RecvSetMainThreadQoSPriority(
655 const nsIThread::QoSPriority
& aQoSPriority
) {
656 MOZ_RELEASE_ASSERT(IsOnThread());
659 // If the new priority is the background (low) priority, we can tell the OS to
660 // put the main thread on low-power cores. Alternately, if we are changing
661 // from the background to a higher priority, we change the main thread back to
662 // the |user-interactive| state, defined in MacOS's QoS documentation as
663 // reserved for main threads.
664 qos_class_t qosClass
= aQoSPriority
== nsIThread::QOS_PRIORITY_LOW
665 ? QOS_CLASS_BACKGROUND
666 : QOS_CLASS_USER_INTERACTIVE
;
668 // We can't directly set the main thread's QoS class from off-main-thread.
669 // However, we can start a QoS class override to raise the QoS, then dispatch
670 // a runnable to set the QoS class and clear the override once complete.
671 pthread_override_t qosOverride
=
672 pthread_override_qos_class_start_np(mMainPThread
, qosClass
, 0);
673 if (NS_FAILED(NS_DispatchToMainThread(NS_NewRunnableFunction(
674 "HangMonitorChild::RecvSetMainThreadQoSPriority",
675 [qosClass
, qosOverride
] {
676 pthread_set_qos_class_self_np(qosClass
, 0);
678 pthread_override_qos_class_end_np(qosOverride
);
681 // If we fail to dispatch, go ahead and end the override anyway.
682 pthread_override_qos_class_end_np(qosOverride
);
689 void HangMonitorChild::Bind(Endpoint
<PProcessHangMonitorChild
>&& aEndpoint
) {
690 MOZ_RELEASE_ASSERT(IsOnThread());
692 DebugOnly
<bool> ok
= aEndpoint
.Bind(this);
696 void HangMonitorChild::NotifySlowScriptAsync(TabId aTabId
,
697 const nsCString
& aFileName
,
698 const nsString
& aAddonId
,
699 const double aDuration
) {
701 Unused
<< SendHangEvidence(
702 SlowScriptData(aTabId
, aFileName
, aAddonId
, aDuration
));
706 HangMonitorChild::SlowScriptAction
HangMonitorChild::NotifySlowScript(
707 nsIBrowserChild
* aBrowserChild
, const char* aFileName
,
708 const nsString
& aAddonId
, const double aDuration
) {
709 MOZ_RELEASE_ASSERT(NS_IsMainThread());
714 MonitorAutoLock
lock(mMonitor
);
716 if (mTerminateScript
) {
717 mTerminateScript
= false;
718 return SlowScriptAction::Terminate
;
721 if (mStartDebugger
) {
722 mStartDebugger
= false;
723 return SlowScriptAction::StartDebugger
;
729 RefPtr
<BrowserChild
> browserChild
=
730 static_cast<BrowserChild
*>(aBrowserChild
);
731 id
= browserChild
->GetTabId();
733 nsAutoCString
filename(aFileName
);
735 Dispatch(NewNonOwningRunnableMethod
<TabId
, nsCString
, nsString
, double>(
736 "HangMonitorChild::NotifySlowScriptAsync", this,
737 &HangMonitorChild::NotifySlowScriptAsync
, id
, filename
, aAddonId
,
739 return SlowScriptAction::Continue
;
742 bool HangMonitorChild::IsDebuggerStartupComplete() {
743 MOZ_RELEASE_ASSERT(NS_IsMainThread());
745 MonitorAutoLock
lock(mMonitor
);
747 if (mFinishedStartingDebugger
) {
748 mFinishedStartingDebugger
= false;
755 void HangMonitorChild::ClearHang() {
756 MOZ_ASSERT(NS_IsMainThread());
759 // bounce to background thread
760 Dispatch(NewNonOwningRunnableMethod("HangMonitorChild::ClearHangAsync",
762 &HangMonitorChild::ClearHangAsync
));
764 MonitorAutoLock
lock(mMonitor
);
766 mTerminateScript
= false;
767 mStartDebugger
= false;
768 mFinishedStartingDebugger
= false;
772 void HangMonitorChild::ClearHangAsync() {
773 MOZ_RELEASE_ASSERT(IsOnThread());
775 // bounce back to parent on background thread
777 Unused
<< SendClearHang();
781 /* HangMonitorParent implementation */
783 HangMonitorParent::HangMonitorParent(ProcessHangMonitor
* aMonitor
)
784 : mHangMonitor(aMonitor
),
786 mMonitor("HangMonitorParent lock"),
787 mShutdownDone(false),
788 mMainThreadTaskFactory(this) {
789 MOZ_RELEASE_ASSERT(NS_IsMainThread());
792 void HangMonitorParent::Shutdown() {
793 MOZ_RELEASE_ASSERT(NS_IsMainThread());
795 MonitorAutoLock
lock(mMonitor
);
802 nsresult rv
= Dispatch(
803 NewNonOwningRunnableMethod("HangMonitorParent::ShutdownOnThread", this,
804 &HangMonitorParent::ShutdownOnThread
));
805 if (NS_WARN_IF(NS_FAILED(rv
))) {
809 while (!mShutdownDone
) {
814 void HangMonitorParent::ShutdownOnThread() {
815 MOZ_RELEASE_ASSERT(IsOnThread());
817 // mIPCOpen is only written from this thread, so need need to take the lock
818 // here. We'd be shooting ourselves in the foot, because ActorDestroy takes
824 MonitorAutoLock
lock(mMonitor
);
825 mShutdownDone
= true;
829 void HangMonitorParent::PaintWhileInterruptingJS(dom::BrowserParent
* aTab
) {
830 MOZ_RELEASE_ASSERT(NS_IsMainThread());
831 if (StaticPrefs::browser_tabs_remote_force_paint()) {
832 TabId id
= aTab
->GetTabId();
833 Dispatch(NewNonOwningRunnableMethod
<bool, TabId
>(
834 "HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread ",
836 &HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread
,
841 void HangMonitorParent::UnloadLayersWhileInterruptingJS(
842 dom::BrowserParent
* aTab
) {
843 MOZ_RELEASE_ASSERT(NS_IsMainThread());
844 TabId id
= aTab
->GetTabId();
845 Dispatch(NewNonOwningRunnableMethod
<bool, TabId
>(
846 "HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread ",
847 this, &HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread
,
851 void HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread(
852 const bool aPaint
, TabId aTabId
) {
853 MOZ_RELEASE_ASSERT(IsOnThread());
857 Unused
<< SendPaintWhileInterruptingJS(aTabId
);
859 Unused
<< SendUnloadLayersWhileInterruptingJS(aTabId
);
864 void HangMonitorParent::CancelContentJSExecutionIfRunning(
865 dom::BrowserParent
* aBrowserParent
,
866 nsIRemoteTab::NavigationType aNavigationType
,
867 const dom::CancelContentJSOptions
& aCancelContentJSOptions
) {
868 MOZ_RELEASE_ASSERT(NS_IsMainThread());
870 if (!aBrowserParent
->CanCancelContentJS(aNavigationType
,
871 aCancelContentJSOptions
.mIndex
,
872 aCancelContentJSOptions
.mUri
)) {
876 TabId id
= aBrowserParent
->GetTabId();
877 Dispatch(NewNonOwningRunnableMethod
<TabId
, nsIRemoteTab::NavigationType
,
878 int32_t, nsIURI
*, int32_t>(
879 "HangMonitorParent::CancelContentJSExecutionIfRunningOnThread", this,
880 &HangMonitorParent::CancelContentJSExecutionIfRunningOnThread
, id
,
881 aNavigationType
, aCancelContentJSOptions
.mIndex
,
882 aCancelContentJSOptions
.mUri
, aCancelContentJSOptions
.mEpoch
));
885 void HangMonitorParent::CancelContentJSExecutionIfRunningOnThread(
886 TabId aTabId
, nsIRemoteTab::NavigationType aNavigationType
,
887 int32_t aNavigationIndex
, nsIURI
* aNavigationURI
, int32_t aEpoch
) {
888 MOZ_RELEASE_ASSERT(IsOnThread());
890 mozilla::Maybe
<nsCString
> spec
;
891 if (aNavigationURI
) {
893 nsresult rv
= aNavigationURI
->GetSpec(tmp
);
894 if (NS_SUCCEEDED(rv
)) {
900 Unused
<< SendCancelContentJSExecutionIfRunning(
901 aTabId
, aNavigationType
, aNavigationIndex
, spec
, aEpoch
);
905 void HangMonitorParent::SetMainThreadQoSPriority(
906 nsIThread::QoSPriority aQoSPriority
) {
907 MOZ_RELEASE_ASSERT(NS_IsMainThread());
908 #ifdef XP_MACOSX // Should not be using outside of MacOS.
910 Dispatch(NewNonOwningRunnableMethod
<nsIThread::QoSPriority
>(
911 "HangMonitorParent::SetMainThreadQoSPriorityOnThread", this,
912 &HangMonitorParent::SetMainThreadQoSPriorityOnThread
, aQoSPriority
));
917 void HangMonitorParent::SetMainThreadQoSPriorityOnThread(
918 nsIThread::QoSPriority aQoSPriority
) {
919 MOZ_RELEASE_ASSERT(IsOnThread());
921 Unused
<< SendSetMainThreadQoSPriority(aQoSPriority
);
926 void HangMonitorParent::ActorDestroy(ActorDestroyReason aWhy
) {
927 MOZ_RELEASE_ASSERT(IsOnThread());
931 void HangMonitorParent::Bind(Endpoint
<PProcessHangMonitorParent
>&& aEndpoint
) {
932 MOZ_RELEASE_ASSERT(IsOnThread());
934 DebugOnly
<bool> ok
= aEndpoint
.Bind(this);
938 void HangMonitorParent::SendHangNotification(
939 const SlowScriptData
& aSlowScriptData
, const nsString
& aBrowserDumpId
) {
940 // chrome process, main thread
941 MOZ_RELEASE_ASSERT(NS_IsMainThread());
945 // We already have a full minidump; go ahead and use it.
946 dumpId
= aBrowserDumpId
;
948 mProcess
->SetSlowScriptData(aSlowScriptData
, dumpId
);
950 nsCOMPtr
<nsIObserverService
> observerService
=
951 mozilla::services::GetObserverService();
952 observerService
->NotifyObservers(mProcess
, "process-hang-report", nullptr);
955 void HangMonitorParent::ClearHangNotification() {
956 // chrome process, main thread
957 MOZ_RELEASE_ASSERT(NS_IsMainThread());
959 nsCOMPtr
<nsIObserverService
> observerService
=
960 mozilla::services::GetObserverService();
961 observerService
->NotifyObservers(mProcess
, "clear-hang-report", nullptr);
963 mProcess
->ClearHang();
966 mozilla::ipc::IPCResult
HangMonitorParent::RecvHangEvidence(
967 const SlowScriptData
& aSlowScriptData
) {
968 // chrome process, background thread
969 MOZ_RELEASE_ASSERT(IsOnThread());
971 if (!StaticPrefs::dom_ipc_reportProcessHangs()) {
976 // Don't report hangs if we're debugging the process. You can comment this
977 // line out for testing purposes.
978 if (IsDebuggerPresent()) {
983 // Before we wake up the browser main thread we want to take a
985 nsAutoString crashId
;
987 mHangMonitor
->InitiateCPOWTimeout();
989 MonitorAutoLock
lock(mMonitor
);
991 NS_DispatchToMainThread(mMainThreadTaskFactory
.NewRunnableMethod(
992 &HangMonitorParent::SendHangNotification
, aSlowScriptData
, crashId
));
997 mozilla::ipc::IPCResult
HangMonitorParent::RecvClearHang() {
998 // chrome process, background thread
999 MOZ_RELEASE_ASSERT(IsOnThread());
1001 if (!StaticPrefs::dom_ipc_reportProcessHangs()) {
1005 mHangMonitor
->InitiateCPOWTimeout();
1007 MonitorAutoLock
lock(mMonitor
);
1009 NS_DispatchToMainThread(mMainThreadTaskFactory
.NewRunnableMethod(
1010 &HangMonitorParent::ClearHangNotification
));
1015 void HangMonitorParent::TerminateScript() {
1016 MOZ_RELEASE_ASSERT(IsOnThread());
1019 Unused
<< SendTerminateScript();
1023 void HangMonitorParent::BeginStartingDebugger() {
1024 MOZ_RELEASE_ASSERT(IsOnThread());
1027 Unused
<< SendBeginStartingDebugger();
1031 void HangMonitorParent::EndStartingDebugger() {
1032 MOZ_RELEASE_ASSERT(IsOnThread());
1035 Unused
<< SendEndStartingDebugger();
1039 /* HangMonitoredProcess implementation */
1041 NS_IMPL_ISUPPORTS(HangMonitoredProcess
, nsIHangReport
)
1044 HangMonitoredProcess::GetHangDuration(double* aHangDuration
) {
1045 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1046 *aHangDuration
= mSlowScriptData
.duration();
1051 HangMonitoredProcess::GetScriptBrowser(Element
** aBrowser
) {
1052 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1053 TabId tabId
= mSlowScriptData
.tabId();
1054 if (!mContentParent
) {
1055 return NS_ERROR_NOT_AVAILABLE
;
1058 nsTArray
<PBrowserParent
*> tabs
;
1059 mContentParent
->ManagedPBrowserParent(tabs
);
1060 for (size_t i
= 0; i
< tabs
.Length(); i
++) {
1061 BrowserParent
* tp
= BrowserParent::GetFrom(tabs
[i
]);
1062 if (tp
->GetTabId() == tabId
) {
1063 RefPtr
<Element
> node
= tp
->GetOwnerElement();
1064 node
.forget(aBrowser
);
1069 *aBrowser
= nullptr;
1074 HangMonitoredProcess::GetScriptFileName(nsACString
& aFileName
) {
1075 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1076 aFileName
= mSlowScriptData
.filename();
1081 HangMonitoredProcess::GetAddonId(nsAString
& aAddonId
) {
1082 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1083 aAddonId
= mSlowScriptData
.addonId();
1088 HangMonitoredProcess::TerminateScript() {
1089 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1091 return NS_ERROR_UNEXPECTED
;
1094 ProcessHangMonitor::Get()->Dispatch(
1095 NewNonOwningRunnableMethod("HangMonitorParent::TerminateScript", mActor
,
1096 &HangMonitorParent::TerminateScript
));
1101 HangMonitoredProcess::BeginStartingDebugger() {
1102 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1104 return NS_ERROR_UNEXPECTED
;
1107 ProcessHangMonitor::Get()->Dispatch(NewNonOwningRunnableMethod(
1108 "HangMonitorParent::BeginStartingDebugger", mActor
,
1109 &HangMonitorParent::BeginStartingDebugger
));
1114 HangMonitoredProcess::EndStartingDebugger() {
1115 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1117 return NS_ERROR_UNEXPECTED
;
1120 ProcessHangMonitor::Get()->Dispatch(NewNonOwningRunnableMethod(
1121 "HangMonitorParent::EndStartingDebugger", mActor
,
1122 &HangMonitorParent::EndStartingDebugger
));
1127 HangMonitoredProcess::IsReportForBrowserOrChildren(nsFrameLoader
* aFrameLoader
,
1129 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1130 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
1137 NS_ENSURE_STATE(aFrameLoader
);
1139 AutoTArray
<RefPtr
<BrowsingContext
>, 10> bcs
;
1140 bcs
.AppendElement(aFrameLoader
->GetExtantBrowsingContext());
1141 while (!bcs
.IsEmpty()) {
1142 RefPtr
<BrowsingContext
> bc
= bcs
[bcs
.Length() - 1];
1143 bcs
.RemoveLastElement();
1147 if (mContentParent
== bc
->Canonical()->GetContentParent()) {
1151 bc
->GetChildren(bcs
);
1159 HangMonitoredProcess::UserCanceled() { return NS_OK
; }
1162 HangMonitoredProcess::GetChildID(uint64_t* aChildID
) {
1163 if (!mContentParent
) {
1164 return NS_ERROR_NOT_AVAILABLE
;
1166 *aChildID
= mContentParent
->ChildID();
1170 static bool InterruptCallback(JSContext
* cx
) {
1171 AssertIsOnMainThread();
1172 if (HangMonitorChild
* child
= HangMonitorChild::Get()) {
1173 return child
->InterruptCallback();
1179 ProcessHangMonitor
* ProcessHangMonitor::sInstance
;
1181 ProcessHangMonitor::ProcessHangMonitor() : mCPOWTimeout(false) {
1182 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1184 if (XRE_IsContentProcess()) {
1185 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
1186 obs
->AddObserver(this, "xpcom-shutdown", false);
1189 if (NS_FAILED(NS_NewNamedThread("ProcessHangMon", getter_AddRefs(mThread
)))) {
1193 // On MacOS, ensure the priority is high enough to handle dispatches at
1194 // high cpu load. USER_INITIATED class threads are prioritized just below
1196 mThread
->Dispatch(NS_NewRunnableFunction(
1197 "ProcessHangMonitor::SetPriority",
1198 [] { pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED
, 0); }));
1202 ProcessHangMonitor::~ProcessHangMonitor() {
1203 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1205 MOZ_ASSERT(sInstance
== this);
1206 sInstance
= nullptr;
1208 mThread
->Shutdown();
1212 ProcessHangMonitor
* ProcessHangMonitor::GetOrCreate() {
1213 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1215 sInstance
= new ProcessHangMonitor();
1220 NS_IMPL_ISUPPORTS(ProcessHangMonitor
, nsIObserver
)
1223 ProcessHangMonitor::Observe(nsISupports
* aSubject
, const char* aTopic
,
1224 const char16_t
* aData
) {
1225 ReleaseAssertIsOnMainThread();
1226 if (!strcmp(aTopic
, "xpcom-shutdown")) {
1227 if (RefPtr
<HangMonitorChild
> child
= HangMonitorChild::Get()) {
1231 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
1232 obs
->RemoveObserver(this, "xpcom-shutdown");
1237 ProcessHangMonitor::SlowScriptAction
ProcessHangMonitor::NotifySlowScript(
1238 nsIBrowserChild
* aBrowserChild
, const char* aFileName
,
1239 const nsString
& aAddonId
, const double aDuration
) {
1240 ReleaseAssertIsOnMainThread();
1241 return HangMonitorChild::Get()->NotifySlowScript(aBrowserChild
, aFileName
,
1242 aAddonId
, aDuration
);
1245 bool ProcessHangMonitor::IsDebuggerStartupComplete() {
1246 ReleaseAssertIsOnMainThread();
1247 return HangMonitorChild::Get()->IsDebuggerStartupComplete();
1250 bool ProcessHangMonitor::ShouldTimeOutCPOWs() {
1251 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1254 mCPOWTimeout
= false;
1260 void ProcessHangMonitor::InitiateCPOWTimeout() {
1261 MOZ_RELEASE_ASSERT(IsOnThread());
1262 mCPOWTimeout
= true;
1265 static already_AddRefed
<PProcessHangMonitorParent
> CreateHangMonitorParent(
1266 ContentParent
* aContentParent
,
1267 Endpoint
<PProcessHangMonitorParent
>&& aEndpoint
) {
1268 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1270 ProcessHangMonitor
* monitor
= ProcessHangMonitor::GetOrCreate();
1271 RefPtr
<HangMonitorParent
> parent
= new HangMonitorParent(monitor
);
1273 auto* process
= new HangMonitoredProcess(parent
, aContentParent
);
1274 parent
->SetProcess(process
);
1277 NewNonOwningRunnableMethod
<Endpoint
<PProcessHangMonitorParent
>&&>(
1278 "HangMonitorParent::Bind", parent
, &HangMonitorParent::Bind
,
1279 std::move(aEndpoint
)));
1281 return parent
.forget();
1284 void mozilla::CreateHangMonitorChild(
1285 Endpoint
<PProcessHangMonitorChild
>&& aEndpoint
) {
1286 ReleaseAssertIsOnMainThread();
1288 JSContext
* cx
= danger::GetJSContext();
1289 JS_AddInterruptCallback(cx
, InterruptCallback
);
1291 ProcessHangMonitor
* monitor
= ProcessHangMonitor::GetOrCreate();
1292 HangMonitorChild::CreateAndBind(monitor
, std::move(aEndpoint
));
1295 nsresult
ProcessHangMonitor::Dispatch(already_AddRefed
<nsIRunnable
> aRunnable
) {
1296 return mThread
->Dispatch(std::move(aRunnable
),
1297 nsIEventTarget::NS_DISPATCH_NORMAL
);
1300 bool ProcessHangMonitor::IsOnThread() {
1302 return NS_SUCCEEDED(mThread
->IsOnCurrentThread(&on
)) && on
;
1306 already_AddRefed
<PProcessHangMonitorParent
> ProcessHangMonitor::AddProcess(
1307 ContentParent
* aContentParent
) {
1308 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1310 if (!StaticPrefs::dom_ipc_processHangMonitor_AtStartup()) {
1314 Endpoint
<PProcessHangMonitorParent
> parent
;
1315 Endpoint
<PProcessHangMonitorChild
> child
;
1317 rv
= PProcessHangMonitor::CreateEndpoints(&parent
, &child
);
1318 if (NS_FAILED(rv
)) {
1319 MOZ_ASSERT(false, "PProcessHangMonitor::CreateEndpoints failed");
1323 if (!aContentParent
->SendInitProcessHangMonitor(std::move(child
))) {
1328 return CreateHangMonitorParent(aContentParent
, std::move(parent
));
1332 void ProcessHangMonitor::RemoveProcess(PProcessHangMonitorParent
* aParent
) {
1333 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1334 auto parent
= static_cast<HangMonitorParent
*>(aParent
);
1339 void ProcessHangMonitor::ClearHang() {
1340 AssertIsOnMainThread();
1341 if (HangMonitorChild
* child
= HangMonitorChild::Get()) {
1347 void ProcessHangMonitor::PaintWhileInterruptingJS(
1348 PProcessHangMonitorParent
* aParent
, dom::BrowserParent
* aTab
) {
1349 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1350 auto* parent
= static_cast<HangMonitorParent
*>(aParent
);
1351 parent
->PaintWhileInterruptingJS(aTab
);
1355 void ProcessHangMonitor::UnloadLayersWhileInterruptingJS(
1356 PProcessHangMonitorParent
* aParent
, dom::BrowserParent
* aTab
) {
1357 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1358 auto* parent
= static_cast<HangMonitorParent
*>(aParent
);
1359 parent
->UnloadLayersWhileInterruptingJS(aTab
);
1363 void ProcessHangMonitor::ClearPaintWhileInterruptingJS() {
1364 ReleaseAssertIsOnMainThread();
1365 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
1367 if (HangMonitorChild
* child
= HangMonitorChild::Get()) {
1368 child
->ClearPaintWhileInterruptingJS();
1373 void ProcessHangMonitor::MaybeStartPaintWhileInterruptingJS() {
1374 ReleaseAssertIsOnMainThread();
1375 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
1377 if (HangMonitorChild
* child
= HangMonitorChild::Get()) {
1378 child
->MaybeStartPaintWhileInterruptingJS();
1383 void ProcessHangMonitor::CancelContentJSExecutionIfRunning(
1384 PProcessHangMonitorParent
* aParent
, dom::BrowserParent
* aBrowserParent
,
1385 nsIRemoteTab::NavigationType aNavigationType
,
1386 const dom::CancelContentJSOptions
& aCancelContentJSOptions
) {
1387 ReleaseAssertIsOnMainThread();
1388 auto* parent
= static_cast<HangMonitorParent
*>(aParent
);
1389 parent
->CancelContentJSExecutionIfRunning(aBrowserParent
, aNavigationType
,
1390 aCancelContentJSOptions
);
1394 void ProcessHangMonitor::SetMainThreadQoSPriority(
1395 PProcessHangMonitorParent
* aParent
, nsIThread::QoSPriority aQoSPriority
) {
1396 ReleaseAssertIsOnMainThread();
1397 auto* parent
= static_cast<HangMonitorParent
*>(aParent
);
1398 parent
->SetMainThreadQoSPriority(aQoSPriority
);