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 LazyLogModule
gQoSLog("QoSPriority"); // For RecvSetMainThreadQoSPriority.
91 /* Child process objects */
93 class HangMonitorChild
: public PProcessHangMonitorChild
,
94 public BackgroundHangAnnotator
{
96 NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
97 HangMonitorChild
, override
)
99 void Bind(Endpoint
<PProcessHangMonitorChild
>&& aEndpoint
);
101 using SlowScriptAction
= ProcessHangMonitor::SlowScriptAction
;
102 SlowScriptAction
NotifySlowScript(nsIBrowserChild
* aBrowserChild
,
103 const char* aFileName
,
104 const nsString
& aAddonId
,
105 const double aDuration
);
106 void NotifySlowScriptAsync(TabId aTabId
, const nsCString
& aFileName
,
107 const nsString
& aAddonId
, const double aDuration
);
109 bool IsDebuggerStartupComplete();
112 void ClearHangAsync();
113 void ClearPaintWhileInterruptingJS();
115 // MaybeStartPaintWhileInterruptingJS will notify the background hang monitor
116 // of activity if this is the first time calling it since
117 // ClearPaintWhileInterruptingJS. It should be callable from any thread, but
118 // you must be holding mMonitor if using it off the main thread, since it
119 // could race with ClearPaintWhileInterruptingJS.
120 void MaybeStartPaintWhileInterruptingJS();
122 mozilla::ipc::IPCResult
RecvTerminateScript() override
;
123 mozilla::ipc::IPCResult
RecvRequestContentJSInterrupt() override
;
124 mozilla::ipc::IPCResult
RecvBeginStartingDebugger() override
;
125 mozilla::ipc::IPCResult
RecvEndStartingDebugger() override
;
127 mozilla::ipc::IPCResult
RecvPaintWhileInterruptingJS(
128 const TabId
& aTabId
) override
;
130 mozilla::ipc::IPCResult
RecvUnloadLayersWhileInterruptingJS(
131 const TabId
& aTabId
) override
;
133 mozilla::ipc::IPCResult
RecvCancelContentJSExecutionIfRunning(
134 const TabId
& aTabId
, const nsIRemoteTab::NavigationType
& aNavigationType
,
135 const int32_t& aNavigationIndex
,
136 const mozilla::Maybe
<nsCString
>& aNavigationURI
,
137 const int32_t& aEpoch
) override
;
139 mozilla::ipc::IPCResult
RecvSetMainThreadQoSPriority(
140 const nsIThread::QoSPriority
& aQoSPriority
) override
;
142 void ActorDestroy(ActorDestroyReason aWhy
) override
;
144 bool InterruptCallback();
147 static HangMonitorChild
* Get() MOZ_REQUIRES(sMainThreadCapability
) {
151 static void CreateAndBind(ProcessHangMonitor
* aMonitor
,
152 Endpoint
<PProcessHangMonitorChild
>&& aEndpoint
);
154 void Dispatch(already_AddRefed
<nsIRunnable
> aRunnable
) {
155 mHangMonitor
->Dispatch(std::move(aRunnable
));
157 bool IsOnThread() { return mHangMonitor
->IsOnThread(); }
159 void AnnotateHang(BackgroundHangAnnotations
& aAnnotations
) override
;
162 friend class mozilla::ProcessHangMonitor
;
165 explicit HangMonitorChild(ProcessHangMonitor
* aMonitor
);
166 ~HangMonitorChild() override
;
168 void ShutdownOnThread();
170 static StaticRefPtr
<HangMonitorChild
> sInstance
171 MOZ_GUARDED_BY(sMainThreadCapability
);
173 const RefPtr
<ProcessHangMonitor
> mHangMonitor
;
176 // On macOS, the pthread_t is required to start a QoS class override. As we
177 // can't recover this from a PRThread*, we need to record it when the
178 // HangMonitorChild is initially created on the main thread.
179 const pthread_t mMainPThread
;
187 // These fields must be accessed with mMonitor held.
188 bool mTerminateScript
MOZ_GUARDED_BY(mMonitor
);
189 bool mStartDebugger
MOZ_GUARDED_BY(mMonitor
);
190 bool mFinishedStartingDebugger
MOZ_GUARDED_BY(mMonitor
);
192 // this variable is used to paint/unload layers
193 // if not set, no action required
194 // true means, we will paint. false - unload layers
195 Maybe
<bool> mPaintWhileInterruptingJS
MOZ_GUARDED_BY(mMonitor
);
196 TabId mPaintWhileInterruptingJSTab
MOZ_GUARDED_BY(mMonitor
);
197 bool mCancelContentJS
MOZ_GUARDED_BY(mMonitor
);
198 TabId mCancelContentJSTab
MOZ_GUARDED_BY(mMonitor
);
199 nsIRemoteTab::NavigationType mCancelContentJSNavigationType
200 MOZ_GUARDED_BY(mMonitor
);
201 int32_t mCancelContentJSNavigationIndex
MOZ_GUARDED_BY(mMonitor
);
202 mozilla::Maybe
<nsCString
> mCancelContentJSNavigationURI
203 MOZ_GUARDED_BY(mMonitor
);
204 int32_t mCancelContentJSEpoch
MOZ_GUARDED_BY(mMonitor
);
205 bool mShutdownDone
MOZ_GUARDED_BY(mMonitor
);
207 JSContext
* mContext
; // const after constructor
209 // This field is only accessed on the hang thread.
212 // Allows us to ensure we NotifyActivity only once, allowing
213 // either thread to do so.
214 Atomic
<bool> mPaintWhileInterruptingJSActive
;
217 StaticRefPtr
<HangMonitorChild
> HangMonitorChild::sInstance
;
219 /* Parent process objects */
221 class HangMonitorParent
;
223 class HangMonitoredProcess final
: public nsIHangReport
{
225 NS_DECL_THREADSAFE_ISUPPORTS
227 HangMonitoredProcess(HangMonitorParent
* aActor
, ContentParent
* aContentParent
)
228 : mActor(aActor
), mContentParent(aContentParent
) {}
230 NS_DECL_NSIHANGREPORT
232 // Called when a content process shuts down.
234 mContentParent
= nullptr;
239 * Sets the information associated with this hang: this includes the tab ID,
240 * filename, duration, and an add-on ID if it was caused by an add-on.
242 * @param aDumpId The ID of a minidump taken when the hang occurred
244 void SetSlowScriptData(const SlowScriptData
& aSlowScriptData
,
245 const nsAString
& aDumpId
) {
246 mSlowScriptData
= aSlowScriptData
;
251 mSlowScriptData
= SlowScriptData();
256 ~HangMonitoredProcess() = default;
258 // Everything here is main thread-only.
259 HangMonitorParent
* mActor
;
260 ContentParent
* mContentParent
;
261 SlowScriptData mSlowScriptData
;
262 nsAutoString mDumpId
;
265 class HangMonitorParent
: public PProcessHangMonitorParent
{
267 NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
268 HangMonitorParent
, override
)
270 explicit HangMonitorParent(ProcessHangMonitor
* aMonitor
);
272 void Bind(Endpoint
<PProcessHangMonitorParent
>&& aEndpoint
);
274 mozilla::ipc::IPCResult
RecvHangEvidence(
275 const SlowScriptData
& aSlowScriptData
) override
;
276 mozilla::ipc::IPCResult
RecvClearHang() override
;
278 void ActorDestroy(ActorDestroyReason aWhy
) override
;
280 void SetProcess(HangMonitoredProcess
* aProcess
) { mProcess
= aProcess
; }
284 void PaintWhileInterruptingJS(dom::BrowserParent
* aTab
);
286 void UnloadLayersWhileInterruptingJS(dom::BrowserParent
* aTab
);
287 void CancelContentJSExecutionIfRunning(
288 dom::BrowserParent
* aBrowserParent
,
289 nsIRemoteTab::NavigationType aNavigationType
,
290 const dom::CancelContentJSOptions
& aCancelContentJSOptions
);
292 void SetMainThreadQoSPriority(nsIThread::QoSPriority aQoSPriority
);
294 void TerminateScript();
295 void BeginStartingDebugger();
296 void EndStartingDebugger();
298 nsresult
Dispatch(already_AddRefed
<nsIRunnable
> aRunnable
) {
299 return mHangMonitor
->Dispatch(std::move(aRunnable
));
301 bool IsOnThread() { return mHangMonitor
->IsOnThread(); }
304 ~HangMonitorParent() override
= default;
306 void SendHangNotification(const SlowScriptData
& aSlowScriptData
,
307 const nsString
& aBrowserDumpId
);
309 void ClearHangNotification();
311 void PaintOrUnloadLayersWhileInterruptingJSOnThread(bool aPaint
,
313 void CancelContentJSExecutionIfRunningOnThread(
314 TabId aTabId
, nsIRemoteTab::NavigationType aNavigationType
,
315 int32_t aNavigationIndex
, nsIURI
* aNavigationURI
, int32_t aEpoch
);
318 void SetMainThreadQoSPriorityOnThread(nsIThread::QoSPriority aQoSPriority
);
321 void ShutdownOnThread();
323 const RefPtr
<ProcessHangMonitor
> mHangMonitor
;
325 // This field is only accessed on the hang thread.
331 RefPtr
<HangMonitoredProcess
> mProcess
;
333 // Must be accessed with mMonitor held.
334 bool mShutdownDone
MOZ_GUARDED_BY(mMonitor
);
335 mozilla::ipc::TaskFactory
<HangMonitorParent
> mMainThreadTaskFactory
336 MOZ_GUARDED_BY(mMonitor
);
341 /* HangMonitorChild implementation */
343 HangMonitorChild::HangMonitorChild(ProcessHangMonitor
* aMonitor
)
344 : mHangMonitor(aMonitor
),
346 mMainPThread(pthread_self()),
348 mMonitor("HangMonitorChild lock"),
350 mTerminateScript(false),
351 mStartDebugger(false),
352 mFinishedStartingDebugger(false),
353 mCancelContentJS(false),
354 mCancelContentJSNavigationType(nsIRemoteTab::NAVIGATE_BACK
),
355 mCancelContentJSNavigationIndex(0),
356 mCancelContentJSEpoch(0),
357 mShutdownDone(false),
359 mPaintWhileInterruptingJSActive(false) {
360 ReleaseAssertIsOnMainThread();
361 MOZ_ASSERT(!sInstance
);
363 mContext
= danger::GetJSContext();
366 HangMonitorChild::~HangMonitorChild() {
367 ReleaseAssertIsOnMainThread();
368 MOZ_ASSERT(sInstance
!= this);
371 void HangMonitorChild::CreateAndBind(
372 ProcessHangMonitor
* aMonitor
,
373 Endpoint
<PProcessHangMonitorChild
>&& aEndpoint
) {
374 ReleaseAssertIsOnMainThread();
375 MOZ_ASSERT(!sInstance
);
377 sInstance
= new HangMonitorChild(aMonitor
);
379 BackgroundHangMonitor::RegisterAnnotator(*sInstance
);
381 aMonitor
->Dispatch(NewRunnableMethod
<Endpoint
<PProcessHangMonitorChild
>&&>(
382 "HangMonitorChild::Bind", sInstance
.get(), &HangMonitorChild::Bind
,
383 std::move(aEndpoint
)));
386 bool HangMonitorChild::InterruptCallback() {
387 MOZ_RELEASE_ASSERT(NS_IsMainThread());
389 if (StaticPrefs::dom_abort_script_on_child_shutdown() &&
390 mozilla::ipc::ProcessChild::ExpectingShutdown()) {
391 // We preserve chrome JS from cancel, but not extension content JS.
392 if (!nsContentUtils::IsCallerChrome()) {
394 "HangMonitorChild::InterruptCallback: ExpectingShutdown, "
395 "canceling content JS execution.\n");
401 // Don't start painting if we're not in a good place to run script. We run
402 // chrome script during layout and such, and it wouldn't be good to interrupt
403 // painting code from there.
404 if (!nsContentUtils::IsSafeToRunScript()) {
408 Maybe
<bool> paintWhileInterruptingJS
;
409 TabId paintWhileInterruptingJSTab
;
412 MonitorAutoLock
lock(mMonitor
);
413 paintWhileInterruptingJS
= mPaintWhileInterruptingJS
;
414 paintWhileInterruptingJSTab
= mPaintWhileInterruptingJSTab
;
416 mPaintWhileInterruptingJS
.reset();
419 if (paintWhileInterruptingJS
.isSome()) {
420 RefPtr
<BrowserChild
> browserChild
=
421 BrowserChild::FindBrowserChild(paintWhileInterruptingJSTab
);
423 js::AutoAssertNoContentJS
nojs(mContext
);
424 if (paintWhileInterruptingJS
.value()) {
425 browserChild
->PaintWhileInterruptingJS();
427 browserChild
->UnloadLayersWhileInterruptingJS();
432 // Only handle the interrupt for cancelling content JS if we have a
433 // non-privileged script (i.e. not part of Gecko or an add-on).
434 JS::Rooted
<JSObject
*> global(mContext
, JS::CurrentGlobalOrNull(mContext
));
435 nsIPrincipal
* principal
= xpc::GetObjectPrincipal(global
);
436 if (principal
&& (principal
->IsSystemPrincipal() ||
437 principal
->GetIsAddonOrExpandedAddonPrincipal())) {
441 nsCOMPtr
<nsPIDOMWindowInner
> win
= xpc::WindowOrNull(global
);
446 bool cancelContentJS
;
447 TabId cancelContentJSTab
;
448 nsIRemoteTab::NavigationType cancelContentJSNavigationType
;
449 int32_t cancelContentJSNavigationIndex
;
450 mozilla::Maybe
<nsCString
> cancelContentJSNavigationURI
;
451 int32_t cancelContentJSEpoch
;
454 MonitorAutoLock
lock(mMonitor
);
455 cancelContentJS
= mCancelContentJS
;
456 cancelContentJSTab
= mCancelContentJSTab
;
457 cancelContentJSNavigationType
= mCancelContentJSNavigationType
;
458 cancelContentJSNavigationIndex
= mCancelContentJSNavigationIndex
;
459 cancelContentJSNavigationURI
= std::move(mCancelContentJSNavigationURI
);
460 cancelContentJSEpoch
= mCancelContentJSEpoch
;
462 mCancelContentJS
= false;
465 if (cancelContentJS
) {
466 js::AutoAssertNoContentJS
nojs(mContext
);
468 RefPtr
<BrowserChild
> browserChild
=
469 BrowserChild::FindBrowserChild(cancelContentJSTab
);
470 RefPtr
<BrowserChild
> browserChildFromWin
= BrowserChild::GetFrom(win
);
471 if (!browserChild
|| !browserChildFromWin
) {
475 TabId tabIdFromWin
= browserChildFromWin
->GetTabId();
476 if (tabIdFromWin
!= cancelContentJSTab
) {
477 // The currently-executing content JS doesn't belong to the tab that
478 // requested cancellation of JS. Just return and let the JS continue.
483 nsCOMPtr
<nsIURI
> uri
;
485 if (cancelContentJSNavigationURI
) {
486 rv
= NS_NewURI(getter_AddRefs(uri
), cancelContentJSNavigationURI
.value());
493 rv
= browserChild
->CanCancelContentJS(cancelContentJSNavigationType
,
494 cancelContentJSNavigationIndex
, uri
,
495 cancelContentJSEpoch
, &canCancel
);
496 if (NS_SUCCEEDED(rv
) && canCancel
) {
497 // Don't add this page to the BF cache, since we're cancelling its JS.
498 if (Document
* doc
= win
->GetExtantDoc()) {
499 doc
->DisallowBFCaching();
509 void HangMonitorChild::AnnotateHang(BackgroundHangAnnotations
& aAnnotations
) {
510 if (mPaintWhileInterruptingJSActive
) {
511 aAnnotations
.AddAnnotation(u
"PaintWhileInterruptingJS"_ns
, true);
515 void HangMonitorChild::Shutdown() {
516 ReleaseAssertIsOnMainThread();
518 BackgroundHangMonitor::UnregisterAnnotator(*this);
521 MonitorAutoLock
lock(mMonitor
);
522 while (!mShutdownDone
) {
527 MOZ_ASSERT(sInstance
== this);
531 void HangMonitorChild::ShutdownOnThread() {
532 MOZ_RELEASE_ASSERT(IsOnThread());
534 MonitorAutoLock
lock(mMonitor
);
535 mShutdownDone
= true;
539 void HangMonitorChild::ActorDestroy(ActorDestroyReason aWhy
) {
540 MOZ_RELEASE_ASSERT(IsOnThread());
544 // We use a task here to ensure that IPDL is finished with this
545 // HangMonitorChild before it gets deleted on the main thread.
546 Dispatch(NewNonOwningRunnableMethod("HangMonitorChild::ShutdownOnThread",
548 &HangMonitorChild::ShutdownOnThread
));
551 mozilla::ipc::IPCResult
HangMonitorChild::RecvTerminateScript() {
552 MOZ_RELEASE_ASSERT(IsOnThread());
554 MonitorAutoLock
lock(mMonitor
);
555 mTerminateScript
= true;
559 mozilla::ipc::IPCResult
HangMonitorChild::RecvRequestContentJSInterrupt() {
560 MOZ_RELEASE_ASSERT(IsOnThread());
562 // In order to cancel JS execution on shutdown, we expect that
563 // ProcessChild::NotifiedImpendingShutdown has been called before.
564 if (mozilla::ipc::ProcessChild::ExpectingShutdown()) {
565 ProcessChild::AppendToIPCShutdownStateAnnotation(
566 "HangMonitorChild::RecvRequestContentJSInterrupt (expected)"_ns
);
568 ProcessChild::AppendToIPCShutdownStateAnnotation(
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 const char* DefineQoS(const nsIThread::QoSPriority
& aQoSPriority
) {
655 if (aQoSPriority
== nsIThread::QOS_PRIORITY_LOW
) {
658 // As of right now, all non-low QoS priorities default to the thread's normal
663 mozilla::ipc::IPCResult
HangMonitorChild::RecvSetMainThreadQoSPriority(
664 const nsIThread::QoSPriority
& aQoSPriority
) {
665 MOZ_RELEASE_ASSERT(IsOnThread());
666 MOZ_LOG(gQoSLog
, LogLevel::Debug
,
667 ("Priority change %s recieved by content process.",
668 DefineQoS(aQoSPriority
)));
671 // If the new priority is the background (low) priority, we can tell the OS to
672 // put the main thread on low-power cores. Alternately, if we are changing
673 // from the background to a higher priority, we change the main thread back to
674 // the |user-interactive| state, defined in MacOS's QoS documentation as
675 // reserved for main threads.
676 qos_class_t qosClass
= aQoSPriority
== nsIThread::QOS_PRIORITY_LOW
677 ? QOS_CLASS_BACKGROUND
678 : QOS_CLASS_USER_INTERACTIVE
;
680 // We can't directly set the main thread's QoS class from off-main-thread.
681 // However, we can start a QoS class override to raise the QoS, then dispatch
682 // a runnable to set the QoS class and clear the override once complete.
683 pthread_override_t qosOverride
=
684 pthread_override_qos_class_start_np(mMainPThread
, qosClass
, 0);
685 if (NS_FAILED(NS_DispatchToMainThread(NS_NewRunnableFunction(
686 "HangMonitorChild::RecvSetMainThreadQoSPriority",
687 [qosClass
, qosOverride
, aQoSPriority
] {
689 gQoSLog
, LogLevel::Debug
,
690 ("Override %s sent to main thread.", DefineQoS(aQoSPriority
)));
691 pthread_set_qos_class_self_np(qosClass
, 0);
693 pthread_override_qos_class_end_np(qosOverride
);
694 MOZ_LOG(gQoSLog
, LogLevel::Debug
,
695 ("Override %s removed from main thread.",
696 DefineQoS(aQoSPriority
)));
699 // If we fail to dispatch, go ahead and end the override anyway.
700 pthread_override_qos_class_end_np(qosOverride
);
701 MOZ_LOG(gQoSLog
, LogLevel::Debug
,
702 ("Override %s removed from main thread.", DefineQoS(aQoSPriority
)));
709 void HangMonitorChild::Bind(Endpoint
<PProcessHangMonitorChild
>&& aEndpoint
) {
710 MOZ_RELEASE_ASSERT(IsOnThread());
712 DebugOnly
<bool> ok
= aEndpoint
.Bind(this);
716 void HangMonitorChild::NotifySlowScriptAsync(TabId aTabId
,
717 const nsCString
& aFileName
,
718 const nsString
& aAddonId
,
719 const double aDuration
) {
721 Unused
<< SendHangEvidence(
722 SlowScriptData(aTabId
, aFileName
, aAddonId
, aDuration
));
726 HangMonitorChild::SlowScriptAction
HangMonitorChild::NotifySlowScript(
727 nsIBrowserChild
* aBrowserChild
, const char* aFileName
,
728 const nsString
& aAddonId
, const double aDuration
) {
729 MOZ_RELEASE_ASSERT(NS_IsMainThread());
734 MonitorAutoLock
lock(mMonitor
);
736 if (mTerminateScript
) {
737 mTerminateScript
= false;
738 return SlowScriptAction::Terminate
;
741 if (mStartDebugger
) {
742 mStartDebugger
= false;
743 return SlowScriptAction::StartDebugger
;
749 RefPtr
<BrowserChild
> browserChild
=
750 static_cast<BrowserChild
*>(aBrowserChild
);
751 id
= browserChild
->GetTabId();
753 nsAutoCString
filename(aFileName
);
755 Dispatch(NewNonOwningRunnableMethod
<TabId
, nsCString
, nsString
, double>(
756 "HangMonitorChild::NotifySlowScriptAsync", this,
757 &HangMonitorChild::NotifySlowScriptAsync
, id
, filename
, aAddonId
,
759 return SlowScriptAction::Continue
;
762 bool HangMonitorChild::IsDebuggerStartupComplete() {
763 MOZ_RELEASE_ASSERT(NS_IsMainThread());
765 MonitorAutoLock
lock(mMonitor
);
767 if (mFinishedStartingDebugger
) {
768 mFinishedStartingDebugger
= false;
775 void HangMonitorChild::ClearHang() {
776 MOZ_ASSERT(NS_IsMainThread());
779 // bounce to background thread
780 Dispatch(NewNonOwningRunnableMethod("HangMonitorChild::ClearHangAsync",
782 &HangMonitorChild::ClearHangAsync
));
784 MonitorAutoLock
lock(mMonitor
);
786 mTerminateScript
= false;
787 mStartDebugger
= false;
788 mFinishedStartingDebugger
= false;
792 void HangMonitorChild::ClearHangAsync() {
793 MOZ_RELEASE_ASSERT(IsOnThread());
795 // bounce back to parent on background thread
797 Unused
<< SendClearHang();
801 /* HangMonitorParent implementation */
803 HangMonitorParent::HangMonitorParent(ProcessHangMonitor
* aMonitor
)
804 : mHangMonitor(aMonitor
),
806 mMonitor("HangMonitorParent lock"),
807 mShutdownDone(false),
808 mMainThreadTaskFactory(this) {
809 MOZ_RELEASE_ASSERT(NS_IsMainThread());
812 void HangMonitorParent::Shutdown() {
813 MOZ_RELEASE_ASSERT(NS_IsMainThread());
815 MonitorAutoLock
lock(mMonitor
);
822 nsresult rv
= Dispatch(
823 NewNonOwningRunnableMethod("HangMonitorParent::ShutdownOnThread", this,
824 &HangMonitorParent::ShutdownOnThread
));
825 if (NS_WARN_IF(NS_FAILED(rv
))) {
829 while (!mShutdownDone
) {
834 void HangMonitorParent::ShutdownOnThread() {
835 MOZ_RELEASE_ASSERT(IsOnThread());
837 // mIPCOpen is only written from this thread, so need need to take the lock
838 // here. We'd be shooting ourselves in the foot, because ActorDestroy takes
844 MonitorAutoLock
lock(mMonitor
);
845 mShutdownDone
= true;
849 void HangMonitorParent::PaintWhileInterruptingJS(dom::BrowserParent
* aTab
) {
850 MOZ_RELEASE_ASSERT(NS_IsMainThread());
851 if (StaticPrefs::browser_tabs_remote_force_paint()) {
852 TabId id
= aTab
->GetTabId();
853 Dispatch(NewNonOwningRunnableMethod
<bool, TabId
>(
854 "HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread ",
856 &HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread
,
861 void HangMonitorParent::UnloadLayersWhileInterruptingJS(
862 dom::BrowserParent
* aTab
) {
863 MOZ_RELEASE_ASSERT(NS_IsMainThread());
864 TabId id
= aTab
->GetTabId();
865 Dispatch(NewNonOwningRunnableMethod
<bool, TabId
>(
866 "HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread ",
867 this, &HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread
,
871 void HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread(
872 const bool aPaint
, TabId aTabId
) {
873 MOZ_RELEASE_ASSERT(IsOnThread());
877 Unused
<< SendPaintWhileInterruptingJS(aTabId
);
879 Unused
<< SendUnloadLayersWhileInterruptingJS(aTabId
);
884 void HangMonitorParent::CancelContentJSExecutionIfRunning(
885 dom::BrowserParent
* aBrowserParent
,
886 nsIRemoteTab::NavigationType aNavigationType
,
887 const dom::CancelContentJSOptions
& aCancelContentJSOptions
) {
888 MOZ_RELEASE_ASSERT(NS_IsMainThread());
890 if (!aBrowserParent
->CanCancelContentJS(aNavigationType
,
891 aCancelContentJSOptions
.mIndex
,
892 aCancelContentJSOptions
.mUri
)) {
896 TabId id
= aBrowserParent
->GetTabId();
897 Dispatch(NewNonOwningRunnableMethod
<TabId
, nsIRemoteTab::NavigationType
,
898 int32_t, nsIURI
*, int32_t>(
899 "HangMonitorParent::CancelContentJSExecutionIfRunningOnThread", this,
900 &HangMonitorParent::CancelContentJSExecutionIfRunningOnThread
, id
,
901 aNavigationType
, aCancelContentJSOptions
.mIndex
,
902 aCancelContentJSOptions
.mUri
, aCancelContentJSOptions
.mEpoch
));
905 void HangMonitorParent::CancelContentJSExecutionIfRunningOnThread(
906 TabId aTabId
, nsIRemoteTab::NavigationType aNavigationType
,
907 int32_t aNavigationIndex
, nsIURI
* aNavigationURI
, int32_t aEpoch
) {
908 MOZ_RELEASE_ASSERT(IsOnThread());
910 mozilla::Maybe
<nsCString
> spec
;
911 if (aNavigationURI
) {
913 nsresult rv
= aNavigationURI
->GetSpec(tmp
);
914 if (NS_SUCCEEDED(rv
)) {
920 Unused
<< SendCancelContentJSExecutionIfRunning(
921 aTabId
, aNavigationType
, aNavigationIndex
, spec
, aEpoch
);
925 void HangMonitorParent::SetMainThreadQoSPriority(
926 nsIThread::QoSPriority aQoSPriority
) {
927 MOZ_RELEASE_ASSERT(NS_IsMainThread());
928 #ifdef XP_MACOSX // Should not be using outside of MacOS.
930 Dispatch(NewNonOwningRunnableMethod
<nsIThread::QoSPriority
>(
931 "HangMonitorParent::SetMainThreadQoSPriorityOnThread", this,
932 &HangMonitorParent::SetMainThreadQoSPriorityOnThread
, aQoSPriority
));
937 void HangMonitorParent::SetMainThreadQoSPriorityOnThread(
938 nsIThread::QoSPriority aQoSPriority
) {
939 MOZ_RELEASE_ASSERT(IsOnThread());
941 Unused
<< SendSetMainThreadQoSPriority(aQoSPriority
);
946 void HangMonitorParent::ActorDestroy(ActorDestroyReason aWhy
) {
947 MOZ_RELEASE_ASSERT(IsOnThread());
951 void HangMonitorParent::Bind(Endpoint
<PProcessHangMonitorParent
>&& aEndpoint
) {
952 MOZ_RELEASE_ASSERT(IsOnThread());
954 DebugOnly
<bool> ok
= aEndpoint
.Bind(this);
958 void HangMonitorParent::SendHangNotification(
959 const SlowScriptData
& aSlowScriptData
, const nsString
& aBrowserDumpId
) {
960 // chrome process, main thread
961 MOZ_RELEASE_ASSERT(NS_IsMainThread());
965 // We already have a full minidump; go ahead and use it.
966 dumpId
= aBrowserDumpId
;
968 mProcess
->SetSlowScriptData(aSlowScriptData
, dumpId
);
970 nsCOMPtr
<nsIObserverService
> observerService
=
971 mozilla::services::GetObserverService();
972 observerService
->NotifyObservers(mProcess
, "process-hang-report", nullptr);
975 void HangMonitorParent::ClearHangNotification() {
976 // chrome process, main thread
977 MOZ_RELEASE_ASSERT(NS_IsMainThread());
979 nsCOMPtr
<nsIObserverService
> observerService
=
980 mozilla::services::GetObserverService();
981 observerService
->NotifyObservers(mProcess
, "clear-hang-report", nullptr);
983 mProcess
->ClearHang();
986 mozilla::ipc::IPCResult
HangMonitorParent::RecvHangEvidence(
987 const SlowScriptData
& aSlowScriptData
) {
988 // chrome process, background thread
989 MOZ_RELEASE_ASSERT(IsOnThread());
991 if (!StaticPrefs::dom_ipc_reportProcessHangs()) {
996 // Don't report hangs if we're debugging the process. You can comment this
997 // line out for testing purposes.
998 if (IsDebuggerPresent()) {
1003 // Before we wake up the browser main thread we want to take a
1004 // browser minidump.
1005 nsAutoString crashId
;
1007 mHangMonitor
->InitiateCPOWTimeout();
1009 MonitorAutoLock
lock(mMonitor
);
1011 NS_DispatchToMainThread(mMainThreadTaskFactory
.NewRunnableMethod(
1012 &HangMonitorParent::SendHangNotification
, aSlowScriptData
, crashId
));
1017 mozilla::ipc::IPCResult
HangMonitorParent::RecvClearHang() {
1018 // chrome process, background thread
1019 MOZ_RELEASE_ASSERT(IsOnThread());
1021 if (!StaticPrefs::dom_ipc_reportProcessHangs()) {
1025 mHangMonitor
->InitiateCPOWTimeout();
1027 MonitorAutoLock
lock(mMonitor
);
1029 NS_DispatchToMainThread(mMainThreadTaskFactory
.NewRunnableMethod(
1030 &HangMonitorParent::ClearHangNotification
));
1035 void HangMonitorParent::TerminateScript() {
1036 MOZ_RELEASE_ASSERT(IsOnThread());
1039 Unused
<< SendTerminateScript();
1043 void HangMonitorParent::BeginStartingDebugger() {
1044 MOZ_RELEASE_ASSERT(IsOnThread());
1047 Unused
<< SendBeginStartingDebugger();
1051 void HangMonitorParent::EndStartingDebugger() {
1052 MOZ_RELEASE_ASSERT(IsOnThread());
1055 Unused
<< SendEndStartingDebugger();
1059 /* HangMonitoredProcess implementation */
1061 NS_IMPL_ISUPPORTS(HangMonitoredProcess
, nsIHangReport
)
1064 HangMonitoredProcess::GetHangDuration(double* aHangDuration
) {
1065 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1066 *aHangDuration
= mSlowScriptData
.duration();
1071 HangMonitoredProcess::GetScriptBrowser(Element
** aBrowser
) {
1072 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1073 TabId tabId
= mSlowScriptData
.tabId();
1074 if (!mContentParent
) {
1075 return NS_ERROR_NOT_AVAILABLE
;
1078 nsTArray
<PBrowserParent
*> tabs
;
1079 mContentParent
->ManagedPBrowserParent(tabs
);
1080 for (size_t i
= 0; i
< tabs
.Length(); i
++) {
1081 BrowserParent
* tp
= BrowserParent::GetFrom(tabs
[i
]);
1082 if (tp
->GetTabId() == tabId
) {
1083 RefPtr
<Element
> node
= tp
->GetOwnerElement();
1084 node
.forget(aBrowser
);
1089 *aBrowser
= nullptr;
1094 HangMonitoredProcess::GetScriptFileName(nsACString
& aFileName
) {
1095 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1096 aFileName
= mSlowScriptData
.filename();
1101 HangMonitoredProcess::GetAddonId(nsAString
& aAddonId
) {
1102 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1103 aAddonId
= mSlowScriptData
.addonId();
1108 HangMonitoredProcess::TerminateScript() {
1109 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1111 return NS_ERROR_UNEXPECTED
;
1114 ProcessHangMonitor::Get()->Dispatch(
1115 NewNonOwningRunnableMethod("HangMonitorParent::TerminateScript", mActor
,
1116 &HangMonitorParent::TerminateScript
));
1121 HangMonitoredProcess::BeginStartingDebugger() {
1122 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1124 return NS_ERROR_UNEXPECTED
;
1127 ProcessHangMonitor::Get()->Dispatch(NewNonOwningRunnableMethod(
1128 "HangMonitorParent::BeginStartingDebugger", mActor
,
1129 &HangMonitorParent::BeginStartingDebugger
));
1134 HangMonitoredProcess::EndStartingDebugger() {
1135 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1137 return NS_ERROR_UNEXPECTED
;
1140 ProcessHangMonitor::Get()->Dispatch(NewNonOwningRunnableMethod(
1141 "HangMonitorParent::EndStartingDebugger", mActor
,
1142 &HangMonitorParent::EndStartingDebugger
));
1147 HangMonitoredProcess::IsReportForBrowserOrChildren(nsFrameLoader
* aFrameLoader
,
1149 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1150 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
1157 NS_ENSURE_STATE(aFrameLoader
);
1159 AutoTArray
<RefPtr
<BrowsingContext
>, 10> bcs
;
1160 bcs
.AppendElement(aFrameLoader
->GetExtantBrowsingContext());
1161 while (!bcs
.IsEmpty()) {
1162 RefPtr
<BrowsingContext
> bc
= bcs
[bcs
.Length() - 1];
1163 bcs
.RemoveLastElement();
1167 if (mContentParent
== bc
->Canonical()->GetContentParent()) {
1171 bc
->GetChildren(bcs
);
1179 HangMonitoredProcess::UserCanceled() { return NS_OK
; }
1182 HangMonitoredProcess::GetChildID(uint64_t* aChildID
) {
1183 if (!mContentParent
) {
1184 return NS_ERROR_NOT_AVAILABLE
;
1186 *aChildID
= mContentParent
->ChildID();
1190 static bool InterruptCallback(JSContext
* cx
) {
1191 AssertIsOnMainThread();
1192 if (HangMonitorChild
* child
= HangMonitorChild::Get()) {
1193 return child
->InterruptCallback();
1199 ProcessHangMonitor
* ProcessHangMonitor::sInstance
;
1201 ProcessHangMonitor::ProcessHangMonitor() : mCPOWTimeout(false) {
1202 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1204 if (XRE_IsContentProcess()) {
1205 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
1206 obs
->AddObserver(this, "xpcom-shutdown", false);
1209 if (NS_FAILED(NS_NewNamedThread("ProcessHangMon", getter_AddRefs(mThread
)))) {
1213 // On MacOS, ensure the priority is high enough to handle dispatches at
1214 // high cpu load. USER_INITIATED class threads are prioritized just below
1216 mThread
->Dispatch(NS_NewRunnableFunction(
1217 "ProcessHangMonitor::SetPriority",
1218 [] { pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED
, 0); }));
1222 ProcessHangMonitor::~ProcessHangMonitor() {
1223 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1225 MOZ_ASSERT(sInstance
== this);
1226 sInstance
= nullptr;
1228 mThread
->Shutdown();
1232 ProcessHangMonitor
* ProcessHangMonitor::GetOrCreate() {
1233 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1235 sInstance
= new ProcessHangMonitor();
1240 NS_IMPL_ISUPPORTS(ProcessHangMonitor
, nsIObserver
)
1243 ProcessHangMonitor::Observe(nsISupports
* aSubject
, const char* aTopic
,
1244 const char16_t
* aData
) {
1245 ReleaseAssertIsOnMainThread();
1246 if (!strcmp(aTopic
, "xpcom-shutdown")) {
1247 if (RefPtr
<HangMonitorChild
> child
= HangMonitorChild::Get()) {
1251 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
1252 obs
->RemoveObserver(this, "xpcom-shutdown");
1257 ProcessHangMonitor::SlowScriptAction
ProcessHangMonitor::NotifySlowScript(
1258 nsIBrowserChild
* aBrowserChild
, const char* aFileName
,
1259 const nsString
& aAddonId
, const double aDuration
) {
1260 ReleaseAssertIsOnMainThread();
1261 return HangMonitorChild::Get()->NotifySlowScript(aBrowserChild
, aFileName
,
1262 aAddonId
, aDuration
);
1265 bool ProcessHangMonitor::IsDebuggerStartupComplete() {
1266 ReleaseAssertIsOnMainThread();
1267 return HangMonitorChild::Get()->IsDebuggerStartupComplete();
1270 bool ProcessHangMonitor::ShouldTimeOutCPOWs() {
1271 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1274 mCPOWTimeout
= false;
1280 void ProcessHangMonitor::InitiateCPOWTimeout() {
1281 MOZ_RELEASE_ASSERT(IsOnThread());
1282 mCPOWTimeout
= true;
1285 static already_AddRefed
<PProcessHangMonitorParent
> CreateHangMonitorParent(
1286 ContentParent
* aContentParent
,
1287 Endpoint
<PProcessHangMonitorParent
>&& aEndpoint
) {
1288 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1290 ProcessHangMonitor
* monitor
= ProcessHangMonitor::GetOrCreate();
1291 RefPtr
<HangMonitorParent
> parent
= new HangMonitorParent(monitor
);
1293 auto* process
= new HangMonitoredProcess(parent
, aContentParent
);
1294 parent
->SetProcess(process
);
1297 NewNonOwningRunnableMethod
<Endpoint
<PProcessHangMonitorParent
>&&>(
1298 "HangMonitorParent::Bind", parent
, &HangMonitorParent::Bind
,
1299 std::move(aEndpoint
)));
1301 return parent
.forget();
1304 void mozilla::CreateHangMonitorChild(
1305 Endpoint
<PProcessHangMonitorChild
>&& aEndpoint
) {
1306 ReleaseAssertIsOnMainThread();
1308 JSContext
* cx
= danger::GetJSContext();
1309 JS_AddInterruptCallback(cx
, InterruptCallback
);
1311 ProcessHangMonitor
* monitor
= ProcessHangMonitor::GetOrCreate();
1312 HangMonitorChild::CreateAndBind(monitor
, std::move(aEndpoint
));
1315 nsresult
ProcessHangMonitor::Dispatch(already_AddRefed
<nsIRunnable
> aRunnable
) {
1316 return mThread
->Dispatch(std::move(aRunnable
),
1317 nsIEventTarget::NS_DISPATCH_NORMAL
);
1320 bool ProcessHangMonitor::IsOnThread() {
1322 return NS_SUCCEEDED(mThread
->IsOnCurrentThread(&on
)) && on
;
1326 already_AddRefed
<PProcessHangMonitorParent
> ProcessHangMonitor::AddProcess(
1327 ContentParent
* aContentParent
) {
1328 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1330 if (!StaticPrefs::dom_ipc_processHangMonitor_AtStartup()) {
1334 Endpoint
<PProcessHangMonitorParent
> parent
;
1335 Endpoint
<PProcessHangMonitorChild
> child
;
1337 rv
= PProcessHangMonitor::CreateEndpoints(&parent
, &child
);
1338 if (NS_FAILED(rv
)) {
1339 MOZ_ASSERT(false, "PProcessHangMonitor::CreateEndpoints failed");
1343 if (!aContentParent
->SendInitProcessHangMonitor(std::move(child
))) {
1348 return CreateHangMonitorParent(aContentParent
, std::move(parent
));
1352 void ProcessHangMonitor::RemoveProcess(PProcessHangMonitorParent
* aParent
) {
1353 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1354 auto parent
= static_cast<HangMonitorParent
*>(aParent
);
1359 void ProcessHangMonitor::ClearHang() {
1360 AssertIsOnMainThread();
1361 if (HangMonitorChild
* child
= HangMonitorChild::Get()) {
1367 void ProcessHangMonitor::PaintWhileInterruptingJS(
1368 PProcessHangMonitorParent
* aParent
, dom::BrowserParent
* aTab
) {
1369 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1370 auto* parent
= static_cast<HangMonitorParent
*>(aParent
);
1371 parent
->PaintWhileInterruptingJS(aTab
);
1375 void ProcessHangMonitor::UnloadLayersWhileInterruptingJS(
1376 PProcessHangMonitorParent
* aParent
, dom::BrowserParent
* aTab
) {
1377 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1378 auto* parent
= static_cast<HangMonitorParent
*>(aParent
);
1379 parent
->UnloadLayersWhileInterruptingJS(aTab
);
1383 void ProcessHangMonitor::ClearPaintWhileInterruptingJS() {
1384 ReleaseAssertIsOnMainThread();
1385 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
1387 if (HangMonitorChild
* child
= HangMonitorChild::Get()) {
1388 child
->ClearPaintWhileInterruptingJS();
1393 void ProcessHangMonitor::MaybeStartPaintWhileInterruptingJS() {
1394 ReleaseAssertIsOnMainThread();
1395 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
1397 if (HangMonitorChild
* child
= HangMonitorChild::Get()) {
1398 child
->MaybeStartPaintWhileInterruptingJS();
1403 void ProcessHangMonitor::CancelContentJSExecutionIfRunning(
1404 PProcessHangMonitorParent
* aParent
, dom::BrowserParent
* aBrowserParent
,
1405 nsIRemoteTab::NavigationType aNavigationType
,
1406 const dom::CancelContentJSOptions
& aCancelContentJSOptions
) {
1407 ReleaseAssertIsOnMainThread();
1408 auto* parent
= static_cast<HangMonitorParent
*>(aParent
);
1409 parent
->CancelContentJSExecutionIfRunning(aBrowserParent
, aNavigationType
,
1410 aCancelContentJSOptions
);
1414 void ProcessHangMonitor::SetMainThreadQoSPriority(
1415 PProcessHangMonitorParent
* aParent
, nsIThread::QoSPriority aQoSPriority
) {
1416 ReleaseAssertIsOnMainThread();
1417 auto* parent
= static_cast<HangMonitorParent
*>(aParent
);
1418 parent
->SetMainThreadQoSPriority(aQoSPriority
);