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 "nsPluginHost.h"
44 #include "nsThreadUtils.h"
46 #include "base/task.h"
47 #include "base/thread.h"
50 // For IsDebuggerPresent()
59 using namespace mozilla
;
60 using namespace mozilla::dom
;
61 using namespace mozilla::ipc
;
66 * Each process has its own ProcessHangMonitor singleton. This singleton exists
67 * as long as there is at least one content process in the system. Each content
68 * process has a HangMonitorChild and the chrome process has one
69 * HangMonitorParent per process. Each process (including the chrome process)
70 * runs a hang monitoring thread. The PHangMonitor actors are bound to this
71 * thread so that they never block on the main thread.
73 * When the content process detects a hang, it posts a task to its hang thread,
74 * which sends an IPC message to the hang thread in the parent. The parent
75 * cancels any ongoing CPOW requests and then posts a runnable to the main
76 * thread that notifies Firefox frontend code of the hang. The frontend code is
77 * passed an nsIHangReport, which can be used to terminate the hang.
79 * If the user chooses to terminate a script, a task is posted to the chrome
80 * process's hang monitoring thread, which sends an IPC message to the hang
81 * thread in the content process. That thread sets a flag to indicate that JS
82 * execution should be terminated the next time it hits the interrupt
83 * callback. A similar scheme is used for debugging slow scripts. If a content
84 * process or plug-in needs to be terminated, the chrome process does so
85 * directly, without messaging the content process.
90 /* Child process objects */
92 class HangMonitorChild
: public PProcessHangMonitorChild
,
93 public BackgroundHangAnnotator
{
95 NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
96 HangMonitorChild
, override
)
98 void Bind(Endpoint
<PProcessHangMonitorChild
>&& aEndpoint
);
100 using SlowScriptAction
= ProcessHangMonitor::SlowScriptAction
;
101 SlowScriptAction
NotifySlowScript(nsIBrowserChild
* aBrowserChild
,
102 const char* aFileName
,
103 const nsString
& aAddonId
,
104 const double aDuration
);
105 void NotifySlowScriptAsync(TabId aTabId
, const nsCString
& aFileName
,
106 const nsString
& aAddonId
, const double aDuration
);
108 bool IsDebuggerStartupComplete();
111 void ClearHangAsync();
112 void ClearPaintWhileInterruptingJS();
114 // MaybeStartPaintWhileInterruptingJS will notify the background hang monitor
115 // of activity if this is the first time calling it since
116 // ClearPaintWhileInterruptingJS. It should be callable from any thread, but
117 // you must be holding mMonitor if using it off the main thread, since it
118 // could race with ClearPaintWhileInterruptingJS.
119 void MaybeStartPaintWhileInterruptingJS();
121 mozilla::ipc::IPCResult
RecvTerminateScript() override
;
122 mozilla::ipc::IPCResult
RecvRequestContentJSInterrupt() override
;
123 mozilla::ipc::IPCResult
RecvBeginStartingDebugger() override
;
124 mozilla::ipc::IPCResult
RecvEndStartingDebugger() override
;
126 mozilla::ipc::IPCResult
RecvPaintWhileInterruptingJS(
127 const TabId
& aTabId
) override
;
129 mozilla::ipc::IPCResult
RecvUnloadLayersWhileInterruptingJS(
130 const TabId
& aTabId
) override
;
132 mozilla::ipc::IPCResult
RecvCancelContentJSExecutionIfRunning(
133 const TabId
& aTabId
, const nsIRemoteTab::NavigationType
& aNavigationType
,
134 const int32_t& aNavigationIndex
,
135 const mozilla::Maybe
<nsCString
>& aNavigationURI
,
136 const int32_t& aEpoch
) override
;
138 mozilla::ipc::IPCResult
RecvSetMainThreadQoSPriority(
139 const nsIThread::QoSPriority
& aQoSPriority
) override
;
141 void ActorDestroy(ActorDestroyReason aWhy
) override
;
143 bool InterruptCallback();
146 static HangMonitorChild
* Get() MOZ_REQUIRES(sMainThreadCapability
) {
150 static void CreateAndBind(ProcessHangMonitor
* aMonitor
,
151 Endpoint
<PProcessHangMonitorChild
>&& aEndpoint
);
153 void Dispatch(already_AddRefed
<nsIRunnable
> aRunnable
) {
154 mHangMonitor
->Dispatch(std::move(aRunnable
));
156 bool IsOnThread() { return mHangMonitor
->IsOnThread(); }
158 void AnnotateHang(BackgroundHangAnnotations
& aAnnotations
) override
;
161 friend class mozilla::ProcessHangMonitor
;
164 explicit HangMonitorChild(ProcessHangMonitor
* aMonitor
);
165 ~HangMonitorChild() override
;
167 void ShutdownOnThread();
169 static StaticRefPtr
<HangMonitorChild
> sInstance
170 MOZ_GUARDED_BY(sMainThreadCapability
);
172 const RefPtr
<ProcessHangMonitor
> mHangMonitor
;
175 // On macOS, the pthread_t is required to start a QoS class override. As we
176 // can't recover this from a PRThread*, we need to record it when the
177 // HangMonitorChild is initially created on the main thread.
178 const pthread_t mMainPThread
;
186 // These fields must be accessed with mMonitor held.
187 bool mTerminateScript
MOZ_GUARDED_BY(mMonitor
);
188 bool mStartDebugger
MOZ_GUARDED_BY(mMonitor
);
189 bool mFinishedStartingDebugger
MOZ_GUARDED_BY(mMonitor
);
191 // this variable is used to paint/unload layers
192 // if not set, no action required
193 // true means, we will paint. false - unload layers
194 Maybe
<bool> mPaintWhileInterruptingJS
MOZ_GUARDED_BY(mMonitor
);
195 TabId mPaintWhileInterruptingJSTab
MOZ_GUARDED_BY(mMonitor
);
196 bool mCancelContentJS
MOZ_GUARDED_BY(mMonitor
);
197 TabId mCancelContentJSTab
MOZ_GUARDED_BY(mMonitor
);
198 nsIRemoteTab::NavigationType mCancelContentJSNavigationType
199 MOZ_GUARDED_BY(mMonitor
);
200 int32_t mCancelContentJSNavigationIndex
MOZ_GUARDED_BY(mMonitor
);
201 mozilla::Maybe
<nsCString
> mCancelContentJSNavigationURI
202 MOZ_GUARDED_BY(mMonitor
);
203 int32_t mCancelContentJSEpoch
MOZ_GUARDED_BY(mMonitor
);
204 bool mShutdownDone
MOZ_GUARDED_BY(mMonitor
);
206 JSContext
* mContext
; // const after constructor
208 // This field is only accessed on the hang thread.
211 // Allows us to ensure we NotifyActivity only once, allowing
212 // either thread to do so.
213 Atomic
<bool> mPaintWhileInterruptingJSActive
;
216 StaticRefPtr
<HangMonitorChild
> HangMonitorChild::sInstance
;
218 /* Parent process objects */
220 class HangMonitorParent
;
222 class HangMonitoredProcess final
: public nsIHangReport
{
224 NS_DECL_THREADSAFE_ISUPPORTS
226 HangMonitoredProcess(HangMonitorParent
* aActor
, ContentParent
* aContentParent
)
227 : mActor(aActor
), mContentParent(aContentParent
) {}
229 NS_DECL_NSIHANGREPORT
231 // Called when a content process shuts down.
233 mContentParent
= nullptr;
238 * Sets the information associated with this hang: this includes the tab ID,
239 * filename, duration, and an add-on ID if it was caused by an add-on.
241 * @param aDumpId The ID of a minidump taken when the hang occurred
243 void SetSlowScriptData(const SlowScriptData
& aSlowScriptData
,
244 const nsAString
& aDumpId
) {
245 mSlowScriptData
= aSlowScriptData
;
250 mSlowScriptData
= SlowScriptData();
255 ~HangMonitoredProcess() = default;
257 // Everything here is main thread-only.
258 HangMonitorParent
* mActor
;
259 ContentParent
* mContentParent
;
260 SlowScriptData mSlowScriptData
;
261 nsAutoString mDumpId
;
264 class HangMonitorParent
: public PProcessHangMonitorParent
{
266 NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
267 HangMonitorParent
, override
)
269 explicit HangMonitorParent(ProcessHangMonitor
* aMonitor
);
271 void Bind(Endpoint
<PProcessHangMonitorParent
>&& aEndpoint
);
273 mozilla::ipc::IPCResult
RecvHangEvidence(
274 const SlowScriptData
& aSlowScriptData
) override
;
275 mozilla::ipc::IPCResult
RecvClearHang() override
;
277 void ActorDestroy(ActorDestroyReason aWhy
) override
;
279 void SetProcess(HangMonitoredProcess
* aProcess
) { mProcess
= aProcess
; }
283 void PaintWhileInterruptingJS(dom::BrowserParent
* aTab
);
285 void UnloadLayersWhileInterruptingJS(dom::BrowserParent
* aTab
);
286 void CancelContentJSExecutionIfRunning(
287 dom::BrowserParent
* aBrowserParent
,
288 nsIRemoteTab::NavigationType aNavigationType
,
289 const dom::CancelContentJSOptions
& aCancelContentJSOptions
);
291 void SetMainThreadQoSPriority(nsIThread::QoSPriority aQoSPriority
);
293 void TerminateScript();
294 void BeginStartingDebugger();
295 void EndStartingDebugger();
297 nsresult
Dispatch(already_AddRefed
<nsIRunnable
> aRunnable
) {
298 return mHangMonitor
->Dispatch(std::move(aRunnable
));
300 bool IsOnThread() { return mHangMonitor
->IsOnThread(); }
303 ~HangMonitorParent() override
;
305 void SendHangNotification(const SlowScriptData
& aSlowScriptData
,
306 const nsString
& aBrowserDumpId
);
308 void ClearHangNotification();
310 void PaintOrUnloadLayersWhileInterruptingJSOnThread(bool aPaint
,
312 void CancelContentJSExecutionIfRunningOnThread(
313 TabId aTabId
, nsIRemoteTab::NavigationType aNavigationType
,
314 int32_t aNavigationIndex
, nsIURI
* aNavigationURI
, int32_t aEpoch
);
317 void SetMainThreadQoSPriorityOnThread(nsIThread::QoSPriority aQoSPriority
);
320 void ShutdownOnThread();
322 const RefPtr
<ProcessHangMonitor
> mHangMonitor
;
324 // This field is only accessed on the hang thread.
330 RefPtr
<HangMonitoredProcess
> mProcess
;
332 // Must be accessed with mMonitor held.
333 bool mShutdownDone
MOZ_GUARDED_BY(mMonitor
);
334 // Map from plugin ID to crash dump ID. Protected by
335 // mBrowserCrashDumpHashLock.
336 nsTHashMap
<nsUint32HashKey
, nsString
> mBrowserCrashDumpIds
337 MOZ_GUARDED_BY(mMonitor
);
338 Mutex mBrowserCrashDumpHashLock
MOZ_GUARDED_BY(mMonitor
);
339 mozilla::ipc::TaskFactory
<HangMonitorParent
> mMainThreadTaskFactory
340 MOZ_GUARDED_BY(mMonitor
);
345 /* HangMonitorChild implementation */
347 HangMonitorChild::HangMonitorChild(ProcessHangMonitor
* aMonitor
)
348 : mHangMonitor(aMonitor
),
350 mMainPThread(pthread_self()),
352 mMonitor("HangMonitorChild lock"),
354 mTerminateScript(false),
355 mStartDebugger(false),
356 mFinishedStartingDebugger(false),
357 mCancelContentJS(false),
358 mCancelContentJSNavigationType(nsIRemoteTab::NAVIGATE_BACK
),
359 mCancelContentJSNavigationIndex(0),
360 mCancelContentJSEpoch(0),
361 mShutdownDone(false),
363 mPaintWhileInterruptingJSActive(false) {
364 ReleaseAssertIsOnMainThread();
365 MOZ_ASSERT(!sInstance
);
367 mContext
= danger::GetJSContext();
370 HangMonitorChild::~HangMonitorChild() {
371 ReleaseAssertIsOnMainThread();
372 MOZ_ASSERT(sInstance
!= this);
375 void HangMonitorChild::CreateAndBind(
376 ProcessHangMonitor
* aMonitor
,
377 Endpoint
<PProcessHangMonitorChild
>&& aEndpoint
) {
378 ReleaseAssertIsOnMainThread();
379 MOZ_ASSERT(!sInstance
);
381 sInstance
= new HangMonitorChild(aMonitor
);
383 BackgroundHangMonitor::RegisterAnnotator(*sInstance
);
385 aMonitor
->Dispatch(NewRunnableMethod
<Endpoint
<PProcessHangMonitorChild
>&&>(
386 "HangMonitorChild::Bind", sInstance
.get(), &HangMonitorChild::Bind
,
387 std::move(aEndpoint
)));
390 bool HangMonitorChild::InterruptCallback() {
391 MOZ_RELEASE_ASSERT(NS_IsMainThread());
393 if (StaticPrefs::dom_abort_script_on_child_shutdown() &&
394 mozilla::ipc::ProcessChild::ExpectingShutdown()) {
395 // We preserve chrome JS from cancel, but not extension content JS.
396 if (!nsContentUtils::IsCallerChrome()) {
398 "HangMonitorChild::InterruptCallback: ExpectingShutdown, "
399 "canceling content JS execution.\n");
405 // Don't start painting if we're not in a good place to run script. We run
406 // chrome script during layout and such, and it wouldn't be good to interrupt
407 // painting code from there.
408 if (!nsContentUtils::IsSafeToRunScript()) {
412 Maybe
<bool> paintWhileInterruptingJS
;
413 TabId paintWhileInterruptingJSTab
;
416 MonitorAutoLock
lock(mMonitor
);
417 paintWhileInterruptingJS
= mPaintWhileInterruptingJS
;
418 paintWhileInterruptingJSTab
= mPaintWhileInterruptingJSTab
;
420 mPaintWhileInterruptingJS
.reset();
423 if (paintWhileInterruptingJS
.isSome()) {
424 RefPtr
<BrowserChild
> browserChild
=
425 BrowserChild::FindBrowserChild(paintWhileInterruptingJSTab
);
427 js::AutoAssertNoContentJS
nojs(mContext
);
428 if (paintWhileInterruptingJS
.value()) {
429 browserChild
->PaintWhileInterruptingJS();
431 browserChild
->UnloadLayersWhileInterruptingJS();
436 // Only handle the interrupt for cancelling content JS if we have a
437 // non-privileged script (i.e. not part of Gecko or an add-on).
438 JS::Rooted
<JSObject
*> global(mContext
, JS::CurrentGlobalOrNull(mContext
));
439 nsIPrincipal
* principal
= xpc::GetObjectPrincipal(global
);
440 if (principal
&& (principal
->IsSystemPrincipal() ||
441 principal
->GetIsAddonOrExpandedAddonPrincipal())) {
445 nsCOMPtr
<nsPIDOMWindowInner
> win
= xpc::WindowOrNull(global
);
450 bool cancelContentJS
;
451 TabId cancelContentJSTab
;
452 nsIRemoteTab::NavigationType cancelContentJSNavigationType
;
453 int32_t cancelContentJSNavigationIndex
;
454 mozilla::Maybe
<nsCString
> cancelContentJSNavigationURI
;
455 int32_t cancelContentJSEpoch
;
458 MonitorAutoLock
lock(mMonitor
);
459 cancelContentJS
= mCancelContentJS
;
460 cancelContentJSTab
= mCancelContentJSTab
;
461 cancelContentJSNavigationType
= mCancelContentJSNavigationType
;
462 cancelContentJSNavigationIndex
= mCancelContentJSNavigationIndex
;
463 cancelContentJSNavigationURI
= std::move(mCancelContentJSNavigationURI
);
464 cancelContentJSEpoch
= mCancelContentJSEpoch
;
466 mCancelContentJS
= false;
469 if (cancelContentJS
) {
470 js::AutoAssertNoContentJS
nojs(mContext
);
472 RefPtr
<BrowserChild
> browserChild
=
473 BrowserChild::FindBrowserChild(cancelContentJSTab
);
474 RefPtr
<BrowserChild
> browserChildFromWin
= BrowserChild::GetFrom(win
);
475 if (!browserChild
|| !browserChildFromWin
) {
479 TabId tabIdFromWin
= browserChildFromWin
->GetTabId();
480 if (tabIdFromWin
!= cancelContentJSTab
) {
481 // The currently-executing content JS doesn't belong to the tab that
482 // requested cancellation of JS. Just return and let the JS continue.
487 nsCOMPtr
<nsIURI
> uri
;
489 if (cancelContentJSNavigationURI
) {
490 rv
= NS_NewURI(getter_AddRefs(uri
), cancelContentJSNavigationURI
.value());
497 rv
= browserChild
->CanCancelContentJS(cancelContentJSNavigationType
,
498 cancelContentJSNavigationIndex
, uri
,
499 cancelContentJSEpoch
, &canCancel
);
500 if (NS_SUCCEEDED(rv
) && canCancel
) {
501 // Don't add this page to the BF cache, since we're cancelling its JS.
502 if (Document
* doc
= win
->GetExtantDoc()) {
503 doc
->DisallowBFCaching();
513 void HangMonitorChild::AnnotateHang(BackgroundHangAnnotations
& aAnnotations
) {
514 if (mPaintWhileInterruptingJSActive
) {
515 aAnnotations
.AddAnnotation(u
"PaintWhileInterruptingJS"_ns
, true);
519 void HangMonitorChild::Shutdown() {
520 ReleaseAssertIsOnMainThread();
522 BackgroundHangMonitor::UnregisterAnnotator(*this);
525 MonitorAutoLock
lock(mMonitor
);
526 while (!mShutdownDone
) {
531 MOZ_ASSERT(sInstance
== this);
535 void HangMonitorChild::ShutdownOnThread() {
536 MOZ_RELEASE_ASSERT(IsOnThread());
538 MonitorAutoLock
lock(mMonitor
);
539 mShutdownDone
= true;
543 void HangMonitorChild::ActorDestroy(ActorDestroyReason aWhy
) {
544 MOZ_RELEASE_ASSERT(IsOnThread());
548 // We use a task here to ensure that IPDL is finished with this
549 // HangMonitorChild before it gets deleted on the main thread.
550 Dispatch(NewNonOwningRunnableMethod("HangMonitorChild::ShutdownOnThread",
552 &HangMonitorChild::ShutdownOnThread
));
555 mozilla::ipc::IPCResult
HangMonitorChild::RecvTerminateScript() {
556 MOZ_RELEASE_ASSERT(IsOnThread());
558 MonitorAutoLock
lock(mMonitor
);
559 mTerminateScript
= true;
563 mozilla::ipc::IPCResult
HangMonitorChild::RecvRequestContentJSInterrupt() {
564 MOZ_RELEASE_ASSERT(IsOnThread());
566 // In order to cancel JS execution on shutdown, we expect that
567 // ProcessChild::NotifiedImpendingShutdown has been called before.
568 if (mozilla::ipc::ProcessChild::ExpectingShutdown()) {
569 CrashReporter::AppendToCrashReportAnnotation(
570 CrashReporter::Annotation::IPCShutdownState
,
571 "HangMonitorChild::RecvRequestContentJSInterrupt (expected)"_ns
);
573 CrashReporter::AppendToCrashReportAnnotation(
574 CrashReporter::Annotation::IPCShutdownState
,
575 "HangMonitorChild::RecvRequestContentJSInterrupt (unexpected)"_ns
);
577 JS_RequestInterruptCallback(mContext
);
581 mozilla::ipc::IPCResult
HangMonitorChild::RecvBeginStartingDebugger() {
582 MOZ_RELEASE_ASSERT(IsOnThread());
584 MonitorAutoLock
lock(mMonitor
);
585 mStartDebugger
= true;
589 mozilla::ipc::IPCResult
HangMonitorChild::RecvEndStartingDebugger() {
590 MOZ_RELEASE_ASSERT(IsOnThread());
592 MonitorAutoLock
lock(mMonitor
);
593 mFinishedStartingDebugger
= true;
597 mozilla::ipc::IPCResult
HangMonitorChild::RecvPaintWhileInterruptingJS(
598 const TabId
& aTabId
) {
599 MOZ_RELEASE_ASSERT(IsOnThread());
602 MonitorAutoLock
lock(mMonitor
);
603 MaybeStartPaintWhileInterruptingJS();
604 mPaintWhileInterruptingJS
= Some(true);
605 mPaintWhileInterruptingJSTab
= aTabId
;
608 JS_RequestInterruptCallback(mContext
);
613 mozilla::ipc::IPCResult
HangMonitorChild::RecvUnloadLayersWhileInterruptingJS(
614 const TabId
& aTabId
) {
615 MOZ_RELEASE_ASSERT(IsOnThread());
618 MonitorAutoLock
lock(mMonitor
);
619 MaybeStartPaintWhileInterruptingJS();
620 mPaintWhileInterruptingJS
= Some(false);
621 mPaintWhileInterruptingJSTab
= aTabId
;
624 JS_RequestInterruptCallback(mContext
);
629 void HangMonitorChild::MaybeStartPaintWhileInterruptingJS() {
630 mPaintWhileInterruptingJSActive
= true;
633 void HangMonitorChild::ClearPaintWhileInterruptingJS() {
634 MOZ_RELEASE_ASSERT(NS_IsMainThread());
635 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
636 mPaintWhileInterruptingJSActive
= false;
639 mozilla::ipc::IPCResult
HangMonitorChild::RecvCancelContentJSExecutionIfRunning(
640 const TabId
& aTabId
, const nsIRemoteTab::NavigationType
& aNavigationType
,
641 const int32_t& aNavigationIndex
,
642 const mozilla::Maybe
<nsCString
>& aNavigationURI
, const int32_t& aEpoch
) {
643 MOZ_RELEASE_ASSERT(IsOnThread());
646 MonitorAutoLock
lock(mMonitor
);
647 mCancelContentJS
= true;
648 mCancelContentJSTab
= aTabId
;
649 mCancelContentJSNavigationType
= aNavigationType
;
650 mCancelContentJSNavigationIndex
= aNavigationIndex
;
651 mCancelContentJSNavigationURI
= aNavigationURI
;
652 mCancelContentJSEpoch
= aEpoch
;
655 JS_RequestInterruptCallback(mContext
);
660 mozilla::ipc::IPCResult
HangMonitorChild::RecvSetMainThreadQoSPriority(
661 const nsIThread::QoSPriority
& aQoSPriority
) {
662 MOZ_RELEASE_ASSERT(IsOnThread());
665 // If the new priority is the background (low) priority, we can tell the OS to
666 // put the main thread on low-power cores. Alternately, if we are changing
667 // from the background to a higher priority, we change the main thread back to
668 // the |user-interactive| state, defined in MacOS's QoS documentation as
669 // reserved for main threads.
670 qos_class_t qosClass
= aQoSPriority
== nsIThread::QOS_PRIORITY_LOW
671 ? QOS_CLASS_BACKGROUND
672 : QOS_CLASS_USER_INTERACTIVE
;
674 // We can't directly set the main thread's QoS class from off-main-thread.
675 // However, we can start a QoS class override to raise the QoS, then dispatch
676 // a runnable to set the QoS class and clear the override once complete.
677 pthread_override_t qosOverride
=
678 pthread_override_qos_class_start_np(mMainPThread
, qosClass
, 0);
679 if (NS_FAILED(NS_DispatchToMainThread(NS_NewRunnableFunction(
680 "HangMonitorChild::RecvSetMainThreadQoSPriority",
681 [qosClass
, qosOverride
] {
682 pthread_set_qos_class_self_np(qosClass
, 0);
684 pthread_override_qos_class_end_np(qosOverride
);
687 // If we fail to dispatch, go ahead and end the override anyway.
688 pthread_override_qos_class_end_np(qosOverride
);
695 void HangMonitorChild::Bind(Endpoint
<PProcessHangMonitorChild
>&& aEndpoint
) {
696 MOZ_RELEASE_ASSERT(IsOnThread());
698 DebugOnly
<bool> ok
= aEndpoint
.Bind(this);
702 void HangMonitorChild::NotifySlowScriptAsync(TabId aTabId
,
703 const nsCString
& aFileName
,
704 const nsString
& aAddonId
,
705 const double aDuration
) {
707 Unused
<< SendHangEvidence(
708 SlowScriptData(aTabId
, aFileName
, aAddonId
, aDuration
));
712 HangMonitorChild::SlowScriptAction
HangMonitorChild::NotifySlowScript(
713 nsIBrowserChild
* aBrowserChild
, const char* aFileName
,
714 const nsString
& aAddonId
, const double aDuration
) {
715 MOZ_RELEASE_ASSERT(NS_IsMainThread());
720 MonitorAutoLock
lock(mMonitor
);
722 if (mTerminateScript
) {
723 mTerminateScript
= false;
724 return SlowScriptAction::Terminate
;
727 if (mStartDebugger
) {
728 mStartDebugger
= false;
729 return SlowScriptAction::StartDebugger
;
735 RefPtr
<BrowserChild
> browserChild
=
736 static_cast<BrowserChild
*>(aBrowserChild
);
737 id
= browserChild
->GetTabId();
739 nsAutoCString
filename(aFileName
);
741 Dispatch(NewNonOwningRunnableMethod
<TabId
, nsCString
, nsString
, double>(
742 "HangMonitorChild::NotifySlowScriptAsync", this,
743 &HangMonitorChild::NotifySlowScriptAsync
, id
, filename
, aAddonId
,
745 return SlowScriptAction::Continue
;
748 bool HangMonitorChild::IsDebuggerStartupComplete() {
749 MOZ_RELEASE_ASSERT(NS_IsMainThread());
751 MonitorAutoLock
lock(mMonitor
);
753 if (mFinishedStartingDebugger
) {
754 mFinishedStartingDebugger
= false;
761 void HangMonitorChild::ClearHang() {
762 MOZ_ASSERT(NS_IsMainThread());
765 // bounce to background thread
766 Dispatch(NewNonOwningRunnableMethod("HangMonitorChild::ClearHangAsync",
768 &HangMonitorChild::ClearHangAsync
));
770 MonitorAutoLock
lock(mMonitor
);
772 mTerminateScript
= false;
773 mStartDebugger
= false;
774 mFinishedStartingDebugger
= false;
778 void HangMonitorChild::ClearHangAsync() {
779 MOZ_RELEASE_ASSERT(IsOnThread());
781 // bounce back to parent on background thread
783 Unused
<< SendClearHang();
787 /* HangMonitorParent implementation */
789 HangMonitorParent::HangMonitorParent(ProcessHangMonitor
* aMonitor
)
790 : mHangMonitor(aMonitor
),
792 mMonitor("HangMonitorParent lock"),
793 mShutdownDone(false),
794 mBrowserCrashDumpHashLock("mBrowserCrashDumpIds lock"),
795 mMainThreadTaskFactory(this) {
796 MOZ_RELEASE_ASSERT(NS_IsMainThread());
799 HangMonitorParent::~HangMonitorParent() {
800 MutexAutoLock
lock(mBrowserCrashDumpHashLock
);
802 for (const auto& crashId
: mBrowserCrashDumpIds
.Values()) {
803 if (!crashId
.IsEmpty()) {
804 CrashReporter::DeleteMinidumpFilesForID(crashId
);
809 void HangMonitorParent::Shutdown() {
810 MOZ_RELEASE_ASSERT(NS_IsMainThread());
812 MonitorAutoLock
lock(mMonitor
);
819 nsresult rv
= Dispatch(
820 NewNonOwningRunnableMethod("HangMonitorParent::ShutdownOnThread", this,
821 &HangMonitorParent::ShutdownOnThread
));
822 if (NS_WARN_IF(NS_FAILED(rv
))) {
826 while (!mShutdownDone
) {
831 void HangMonitorParent::ShutdownOnThread() {
832 MOZ_RELEASE_ASSERT(IsOnThread());
834 // mIPCOpen is only written from this thread, so need need to take the lock
835 // here. We'd be shooting ourselves in the foot, because ActorDestroy takes
841 MonitorAutoLock
lock(mMonitor
);
842 mShutdownDone
= true;
846 void HangMonitorParent::PaintWhileInterruptingJS(dom::BrowserParent
* aTab
) {
847 MOZ_RELEASE_ASSERT(NS_IsMainThread());
848 if (StaticPrefs::browser_tabs_remote_force_paint()) {
849 TabId id
= aTab
->GetTabId();
850 Dispatch(NewNonOwningRunnableMethod
<bool, TabId
>(
851 "HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread ",
853 &HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread
,
858 void HangMonitorParent::UnloadLayersWhileInterruptingJS(
859 dom::BrowserParent
* aTab
) {
860 MOZ_RELEASE_ASSERT(NS_IsMainThread());
861 TabId id
= aTab
->GetTabId();
862 Dispatch(NewNonOwningRunnableMethod
<bool, TabId
>(
863 "HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread ",
864 this, &HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread
,
868 void HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread(
869 const bool aPaint
, TabId aTabId
) {
870 MOZ_RELEASE_ASSERT(IsOnThread());
874 Unused
<< SendPaintWhileInterruptingJS(aTabId
);
876 Unused
<< SendUnloadLayersWhileInterruptingJS(aTabId
);
881 void HangMonitorParent::CancelContentJSExecutionIfRunning(
882 dom::BrowserParent
* aBrowserParent
,
883 nsIRemoteTab::NavigationType aNavigationType
,
884 const dom::CancelContentJSOptions
& aCancelContentJSOptions
) {
885 MOZ_RELEASE_ASSERT(NS_IsMainThread());
887 if (!aBrowserParent
->CanCancelContentJS(aNavigationType
,
888 aCancelContentJSOptions
.mIndex
,
889 aCancelContentJSOptions
.mUri
)) {
893 TabId id
= aBrowserParent
->GetTabId();
894 Dispatch(NewNonOwningRunnableMethod
<TabId
, nsIRemoteTab::NavigationType
,
895 int32_t, nsIURI
*, int32_t>(
896 "HangMonitorParent::CancelContentJSExecutionIfRunningOnThread", this,
897 &HangMonitorParent::CancelContentJSExecutionIfRunningOnThread
, id
,
898 aNavigationType
, aCancelContentJSOptions
.mIndex
,
899 aCancelContentJSOptions
.mUri
, aCancelContentJSOptions
.mEpoch
));
902 void HangMonitorParent::CancelContentJSExecutionIfRunningOnThread(
903 TabId aTabId
, nsIRemoteTab::NavigationType aNavigationType
,
904 int32_t aNavigationIndex
, nsIURI
* aNavigationURI
, int32_t aEpoch
) {
905 MOZ_RELEASE_ASSERT(IsOnThread());
907 mozilla::Maybe
<nsCString
> spec
;
908 if (aNavigationURI
) {
910 nsresult rv
= aNavigationURI
->GetSpec(tmp
);
911 if (NS_SUCCEEDED(rv
)) {
917 Unused
<< SendCancelContentJSExecutionIfRunning(
918 aTabId
, aNavigationType
, aNavigationIndex
, spec
, aEpoch
);
922 void HangMonitorParent::SetMainThreadQoSPriority(
923 nsIThread::QoSPriority aQoSPriority
) {
924 MOZ_RELEASE_ASSERT(NS_IsMainThread());
925 #ifdef XP_MACOSX // Should not be using outside of MacOS.
927 Dispatch(NewNonOwningRunnableMethod
<nsIThread::QoSPriority
>(
928 "HangMonitorParent::SetMainThreadQoSPriorityOnThread", this,
929 &HangMonitorParent::SetMainThreadQoSPriorityOnThread
, aQoSPriority
));
934 void HangMonitorParent::SetMainThreadQoSPriorityOnThread(
935 nsIThread::QoSPriority aQoSPriority
) {
936 MOZ_RELEASE_ASSERT(IsOnThread());
938 Unused
<< SendSetMainThreadQoSPriority(aQoSPriority
);
943 void HangMonitorParent::ActorDestroy(ActorDestroyReason aWhy
) {
944 MOZ_RELEASE_ASSERT(IsOnThread());
948 void HangMonitorParent::Bind(Endpoint
<PProcessHangMonitorParent
>&& aEndpoint
) {
949 MOZ_RELEASE_ASSERT(IsOnThread());
951 DebugOnly
<bool> ok
= aEndpoint
.Bind(this);
955 void HangMonitorParent::SendHangNotification(
956 const SlowScriptData
& aSlowScriptData
, const nsString
& aBrowserDumpId
) {
957 // chrome process, main thread
958 MOZ_RELEASE_ASSERT(NS_IsMainThread());
962 // We already have a full minidump; go ahead and use it.
963 dumpId
= aBrowserDumpId
;
965 mProcess
->SetSlowScriptData(aSlowScriptData
, dumpId
);
967 nsCOMPtr
<nsIObserverService
> observerService
=
968 mozilla::services::GetObserverService();
969 observerService
->NotifyObservers(mProcess
, "process-hang-report", nullptr);
972 void HangMonitorParent::ClearHangNotification() {
973 // chrome process, main thread
974 MOZ_RELEASE_ASSERT(NS_IsMainThread());
976 nsCOMPtr
<nsIObserverService
> observerService
=
977 mozilla::services::GetObserverService();
978 observerService
->NotifyObservers(mProcess
, "clear-hang-report", nullptr);
980 mProcess
->ClearHang();
983 mozilla::ipc::IPCResult
HangMonitorParent::RecvHangEvidence(
984 const SlowScriptData
& aSlowScriptData
) {
985 // chrome process, background thread
986 MOZ_RELEASE_ASSERT(IsOnThread());
988 if (!StaticPrefs::dom_ipc_reportProcessHangs()) {
993 // Don't report hangs if we're debugging the process. You can comment this
994 // line out for testing purposes.
995 if (IsDebuggerPresent()) {
1000 // Before we wake up the browser main thread we want to take a
1001 // browser minidump.
1002 nsAutoString crashId
;
1004 mHangMonitor
->InitiateCPOWTimeout();
1006 MonitorAutoLock
lock(mMonitor
);
1008 NS_DispatchToMainThread(mMainThreadTaskFactory
.NewRunnableMethod(
1009 &HangMonitorParent::SendHangNotification
, aSlowScriptData
, crashId
));
1014 mozilla::ipc::IPCResult
HangMonitorParent::RecvClearHang() {
1015 // chrome process, background thread
1016 MOZ_RELEASE_ASSERT(IsOnThread());
1018 if (!StaticPrefs::dom_ipc_reportProcessHangs()) {
1022 mHangMonitor
->InitiateCPOWTimeout();
1024 MonitorAutoLock
lock(mMonitor
);
1026 NS_DispatchToMainThread(mMainThreadTaskFactory
.NewRunnableMethod(
1027 &HangMonitorParent::ClearHangNotification
));
1032 void HangMonitorParent::TerminateScript() {
1033 MOZ_RELEASE_ASSERT(IsOnThread());
1036 Unused
<< SendTerminateScript();
1040 void HangMonitorParent::BeginStartingDebugger() {
1041 MOZ_RELEASE_ASSERT(IsOnThread());
1044 Unused
<< SendBeginStartingDebugger();
1048 void HangMonitorParent::EndStartingDebugger() {
1049 MOZ_RELEASE_ASSERT(IsOnThread());
1052 Unused
<< SendEndStartingDebugger();
1056 /* HangMonitoredProcess implementation */
1058 NS_IMPL_ISUPPORTS(HangMonitoredProcess
, nsIHangReport
)
1061 HangMonitoredProcess::GetHangDuration(double* aHangDuration
) {
1062 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1063 *aHangDuration
= mSlowScriptData
.duration();
1068 HangMonitoredProcess::GetScriptBrowser(Element
** aBrowser
) {
1069 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1070 TabId tabId
= mSlowScriptData
.tabId();
1071 if (!mContentParent
) {
1072 return NS_ERROR_NOT_AVAILABLE
;
1075 nsTArray
<PBrowserParent
*> tabs
;
1076 mContentParent
->ManagedPBrowserParent(tabs
);
1077 for (size_t i
= 0; i
< tabs
.Length(); i
++) {
1078 BrowserParent
* tp
= BrowserParent::GetFrom(tabs
[i
]);
1079 if (tp
->GetTabId() == tabId
) {
1080 RefPtr
<Element
> node
= tp
->GetOwnerElement();
1081 node
.forget(aBrowser
);
1086 *aBrowser
= nullptr;
1091 HangMonitoredProcess::GetScriptFileName(nsACString
& aFileName
) {
1092 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1093 aFileName
= mSlowScriptData
.filename();
1098 HangMonitoredProcess::GetAddonId(nsAString
& aAddonId
) {
1099 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1100 aAddonId
= mSlowScriptData
.addonId();
1105 HangMonitoredProcess::TerminateScript() {
1106 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1108 return NS_ERROR_UNEXPECTED
;
1111 ProcessHangMonitor::Get()->Dispatch(
1112 NewNonOwningRunnableMethod("HangMonitorParent::TerminateScript", mActor
,
1113 &HangMonitorParent::TerminateScript
));
1118 HangMonitoredProcess::BeginStartingDebugger() {
1119 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1121 return NS_ERROR_UNEXPECTED
;
1124 ProcessHangMonitor::Get()->Dispatch(NewNonOwningRunnableMethod(
1125 "HangMonitorParent::BeginStartingDebugger", mActor
,
1126 &HangMonitorParent::BeginStartingDebugger
));
1131 HangMonitoredProcess::EndStartingDebugger() {
1132 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1134 return NS_ERROR_UNEXPECTED
;
1137 ProcessHangMonitor::Get()->Dispatch(NewNonOwningRunnableMethod(
1138 "HangMonitorParent::EndStartingDebugger", mActor
,
1139 &HangMonitorParent::EndStartingDebugger
));
1144 HangMonitoredProcess::IsReportForBrowserOrChildren(nsFrameLoader
* aFrameLoader
,
1146 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1147 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
1154 NS_ENSURE_STATE(aFrameLoader
);
1156 AutoTArray
<RefPtr
<BrowsingContext
>, 10> bcs
;
1157 bcs
.AppendElement(aFrameLoader
->GetExtantBrowsingContext());
1158 while (!bcs
.IsEmpty()) {
1159 RefPtr
<BrowsingContext
> bc
= bcs
[bcs
.Length() - 1];
1160 bcs
.RemoveLastElement();
1164 if (mContentParent
== bc
->Canonical()->GetContentParent()) {
1168 bc
->GetChildren(bcs
);
1176 HangMonitoredProcess::UserCanceled() { return NS_OK
; }
1179 HangMonitoredProcess::GetChildID(uint64_t* aChildID
) {
1180 if (!mContentParent
) {
1181 return NS_ERROR_NOT_AVAILABLE
;
1183 *aChildID
= mContentParent
->ChildID();
1187 static bool InterruptCallback(JSContext
* cx
) {
1188 AssertIsOnMainThread();
1189 if (HangMonitorChild
* child
= HangMonitorChild::Get()) {
1190 return child
->InterruptCallback();
1196 ProcessHangMonitor
* ProcessHangMonitor::sInstance
;
1198 ProcessHangMonitor::ProcessHangMonitor() : mCPOWTimeout(false) {
1199 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1201 if (XRE_IsContentProcess()) {
1202 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
1203 obs
->AddObserver(this, "xpcom-shutdown", false);
1206 if (NS_FAILED(NS_NewNamedThread("ProcessHangMon", getter_AddRefs(mThread
)))) {
1210 // On MacOS, ensure the priority is high enough to handle dispatches at
1211 // high cpu load. USER_INITIATED class threads are prioritized just below
1213 mThread
->Dispatch(NS_NewRunnableFunction(
1214 "ProcessHangMonitor::SetPriority",
1215 [] { pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED
, 0); }));
1219 ProcessHangMonitor::~ProcessHangMonitor() {
1220 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1222 MOZ_ASSERT(sInstance
== this);
1223 sInstance
= nullptr;
1225 mThread
->Shutdown();
1229 ProcessHangMonitor
* ProcessHangMonitor::GetOrCreate() {
1230 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1232 sInstance
= new ProcessHangMonitor();
1237 NS_IMPL_ISUPPORTS(ProcessHangMonitor
, nsIObserver
)
1240 ProcessHangMonitor::Observe(nsISupports
* aSubject
, const char* aTopic
,
1241 const char16_t
* aData
) {
1242 ReleaseAssertIsOnMainThread();
1243 if (!strcmp(aTopic
, "xpcom-shutdown")) {
1244 if (RefPtr
<HangMonitorChild
> child
= HangMonitorChild::Get()) {
1248 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
1249 obs
->RemoveObserver(this, "xpcom-shutdown");
1254 ProcessHangMonitor::SlowScriptAction
ProcessHangMonitor::NotifySlowScript(
1255 nsIBrowserChild
* aBrowserChild
, const char* aFileName
,
1256 const nsString
& aAddonId
, const double aDuration
) {
1257 ReleaseAssertIsOnMainThread();
1258 return HangMonitorChild::Get()->NotifySlowScript(aBrowserChild
, aFileName
,
1259 aAddonId
, aDuration
);
1262 bool ProcessHangMonitor::IsDebuggerStartupComplete() {
1263 ReleaseAssertIsOnMainThread();
1264 return HangMonitorChild::Get()->IsDebuggerStartupComplete();
1267 bool ProcessHangMonitor::ShouldTimeOutCPOWs() {
1268 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1271 mCPOWTimeout
= false;
1277 void ProcessHangMonitor::InitiateCPOWTimeout() {
1278 MOZ_RELEASE_ASSERT(IsOnThread());
1279 mCPOWTimeout
= true;
1282 static already_AddRefed
<PProcessHangMonitorParent
> CreateHangMonitorParent(
1283 ContentParent
* aContentParent
,
1284 Endpoint
<PProcessHangMonitorParent
>&& aEndpoint
) {
1285 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1287 ProcessHangMonitor
* monitor
= ProcessHangMonitor::GetOrCreate();
1288 RefPtr
<HangMonitorParent
> parent
= new HangMonitorParent(monitor
);
1290 auto* process
= new HangMonitoredProcess(parent
, aContentParent
);
1291 parent
->SetProcess(process
);
1294 NewNonOwningRunnableMethod
<Endpoint
<PProcessHangMonitorParent
>&&>(
1295 "HangMonitorParent::Bind", parent
, &HangMonitorParent::Bind
,
1296 std::move(aEndpoint
)));
1298 return parent
.forget();
1301 void mozilla::CreateHangMonitorChild(
1302 Endpoint
<PProcessHangMonitorChild
>&& aEndpoint
) {
1303 ReleaseAssertIsOnMainThread();
1305 JSContext
* cx
= danger::GetJSContext();
1306 JS_AddInterruptCallback(cx
, InterruptCallback
);
1308 ProcessHangMonitor
* monitor
= ProcessHangMonitor::GetOrCreate();
1309 HangMonitorChild::CreateAndBind(monitor
, std::move(aEndpoint
));
1312 nsresult
ProcessHangMonitor::Dispatch(already_AddRefed
<nsIRunnable
> aRunnable
) {
1313 return mThread
->Dispatch(std::move(aRunnable
),
1314 nsIEventTarget::NS_DISPATCH_NORMAL
);
1317 bool ProcessHangMonitor::IsOnThread() {
1319 return NS_SUCCEEDED(mThread
->IsOnCurrentThread(&on
)) && on
;
1323 already_AddRefed
<PProcessHangMonitorParent
> ProcessHangMonitor::AddProcess(
1324 ContentParent
* aContentParent
) {
1325 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1327 if (!StaticPrefs::dom_ipc_processHangMonitor_AtStartup()) {
1331 Endpoint
<PProcessHangMonitorParent
> parent
;
1332 Endpoint
<PProcessHangMonitorChild
> child
;
1334 rv
= PProcessHangMonitor::CreateEndpoints(&parent
, &child
);
1335 if (NS_FAILED(rv
)) {
1336 MOZ_ASSERT(false, "PProcessHangMonitor::CreateEndpoints failed");
1340 if (!aContentParent
->SendInitProcessHangMonitor(std::move(child
))) {
1345 return CreateHangMonitorParent(aContentParent
, std::move(parent
));
1349 void ProcessHangMonitor::RemoveProcess(PProcessHangMonitorParent
* aParent
) {
1350 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1351 auto parent
= static_cast<HangMonitorParent
*>(aParent
);
1356 void ProcessHangMonitor::ClearHang() {
1357 AssertIsOnMainThread();
1358 if (HangMonitorChild
* child
= HangMonitorChild::Get()) {
1364 void ProcessHangMonitor::PaintWhileInterruptingJS(
1365 PProcessHangMonitorParent
* aParent
, dom::BrowserParent
* aTab
) {
1366 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1367 auto* parent
= static_cast<HangMonitorParent
*>(aParent
);
1368 parent
->PaintWhileInterruptingJS(aTab
);
1372 void ProcessHangMonitor::UnloadLayersWhileInterruptingJS(
1373 PProcessHangMonitorParent
* aParent
, dom::BrowserParent
* aTab
) {
1374 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1375 auto* parent
= static_cast<HangMonitorParent
*>(aParent
);
1376 parent
->UnloadLayersWhileInterruptingJS(aTab
);
1380 void ProcessHangMonitor::ClearPaintWhileInterruptingJS() {
1381 ReleaseAssertIsOnMainThread();
1382 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
1384 if (HangMonitorChild
* child
= HangMonitorChild::Get()) {
1385 child
->ClearPaintWhileInterruptingJS();
1390 void ProcessHangMonitor::MaybeStartPaintWhileInterruptingJS() {
1391 ReleaseAssertIsOnMainThread();
1392 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
1394 if (HangMonitorChild
* child
= HangMonitorChild::Get()) {
1395 child
->MaybeStartPaintWhileInterruptingJS();
1400 void ProcessHangMonitor::CancelContentJSExecutionIfRunning(
1401 PProcessHangMonitorParent
* aParent
, dom::BrowserParent
* aBrowserParent
,
1402 nsIRemoteTab::NavigationType aNavigationType
,
1403 const dom::CancelContentJSOptions
& aCancelContentJSOptions
) {
1404 ReleaseAssertIsOnMainThread();
1405 auto* parent
= static_cast<HangMonitorParent
*>(aParent
);
1406 parent
->CancelContentJSExecutionIfRunning(aBrowserParent
, aNavigationType
,
1407 aCancelContentJSOptions
);
1411 void ProcessHangMonitor::SetMainThreadQoSPriority(
1412 PProcessHangMonitorParent
* aParent
, nsIThread::QoSPriority aQoSPriority
) {
1413 ReleaseAssertIsOnMainThread();
1414 auto* parent
= static_cast<HangMonitorParent
*>(aParent
);
1415 parent
->SetMainThreadQoSPriority(aQoSPriority
);