Bug 1861467 - [wpt-sync] Update web-platform-tests to eedf737ce39c512d0ca3471f988972e...
[gecko.git] / dom / ipc / ProcessHangMonitor.cpp
blobfd2c63d0c723f4d903133d688791190f7faefef4
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"
10 #include "jsapi.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"
49 #ifdef XP_WIN
50 // For IsDebuggerPresent()
51 # include <windows.h>
52 #endif
54 #ifdef XP_MACOSX
55 // for qos controls
56 # include <sys/qos.h>
57 #endif
59 using namespace mozilla;
60 using namespace mozilla::dom;
61 using namespace mozilla::ipc;
64 * Basic architecture:
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.
88 namespace {
90 /* Child process objects */
92 class HangMonitorChild : public PProcessHangMonitorChild,
93 public BackgroundHangAnnotator {
94 public:
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();
110 void ClearHang();
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();
144 void Shutdown();
146 static HangMonitorChild* Get() MOZ_REQUIRES(sMainThreadCapability) {
147 return sInstance;
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;
160 protected:
161 friend class mozilla::ProcessHangMonitor;
163 private:
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;
174 #ifdef XP_MACOSX
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;
179 #endif
181 Monitor mMonitor;
183 // Main thread-only.
184 bool mSentReport;
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.
209 bool mIPCOpen;
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 {
223 public:
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.
232 void Clear() {
233 mContentParent = nullptr;
234 mActor = 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;
246 mDumpId = aDumpId;
249 void ClearHang() {
250 mSlowScriptData = SlowScriptData();
251 mDumpId.Truncate();
254 private:
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 {
265 public:
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; }
281 void Shutdown();
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(); }
302 private:
303 ~HangMonitorParent() override;
305 void SendHangNotification(const SlowScriptData& aSlowScriptData,
306 const nsString& aBrowserDumpId);
308 void ClearHangNotification();
310 void PaintOrUnloadLayersWhileInterruptingJSOnThread(bool aPaint,
311 TabId aTabId);
312 void CancelContentJSExecutionIfRunningOnThread(
313 TabId aTabId, nsIRemoteTab::NavigationType aNavigationType,
314 int32_t aNavigationIndex, nsIURI* aNavigationURI, int32_t aEpoch);
316 #ifdef XP_MACOSX
317 void SetMainThreadQoSPriorityOnThread(nsIThread::QoSPriority aQoSPriority);
318 #endif
320 void ShutdownOnThread();
322 const RefPtr<ProcessHangMonitor> mHangMonitor;
324 // This field is only accessed on the hang thread.
325 bool mIPCOpen;
327 Monitor mMonitor;
329 // MainThread only
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);
343 } // namespace
345 /* HangMonitorChild implementation */
347 HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor)
348 : mHangMonitor(aMonitor),
349 #ifdef XP_MACOSX
350 mMainPThread(pthread_self()),
351 #endif
352 mMonitor("HangMonitorChild lock"),
353 mSentReport(false),
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),
362 mIPCOpen(true),
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()) {
397 NS_WARNING(
398 "HangMonitorChild::InterruptCallback: ExpectingShutdown, "
399 "canceling content JS execution.\n");
400 return false;
402 return true;
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()) {
409 return true;
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);
426 if (browserChild) {
427 js::AutoAssertNoContentJS nojs(mContext);
428 if (paintWhileInterruptingJS.value()) {
429 browserChild->PaintWhileInterruptingJS();
430 } else {
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())) {
442 return true;
445 nsCOMPtr<nsPIDOMWindowInner> win = xpc::WindowOrNull(global);
446 if (!win) {
447 return true;
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) {
476 return true;
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.
483 return true;
486 nsresult rv;
487 nsCOMPtr<nsIURI> uri;
489 if (cancelContentJSNavigationURI) {
490 rv = NS_NewURI(getter_AddRefs(uri), cancelContentJSNavigationURI.value());
491 if (NS_FAILED(rv)) {
492 return true;
496 bool canCancel;
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();
506 return false;
510 return true;
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) {
527 mMonitor.Wait();
531 MOZ_ASSERT(sInstance == this);
532 sInstance = nullptr;
535 void HangMonitorChild::ShutdownOnThread() {
536 MOZ_RELEASE_ASSERT(IsOnThread());
538 MonitorAutoLock lock(mMonitor);
539 mShutdownDone = true;
540 mMonitor.Notify();
543 void HangMonitorChild::ActorDestroy(ActorDestroyReason aWhy) {
544 MOZ_RELEASE_ASSERT(IsOnThread());
546 mIPCOpen = false;
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",
551 this,
552 &HangMonitorChild::ShutdownOnThread));
555 mozilla::ipc::IPCResult HangMonitorChild::RecvTerminateScript() {
556 MOZ_RELEASE_ASSERT(IsOnThread());
558 MonitorAutoLock lock(mMonitor);
559 mTerminateScript = true;
560 return IPC_OK();
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);
572 } else {
573 CrashReporter::AppendToCrashReportAnnotation(
574 CrashReporter::Annotation::IPCShutdownState,
575 "HangMonitorChild::RecvRequestContentJSInterrupt (unexpected)"_ns);
577 JS_RequestInterruptCallback(mContext);
578 return IPC_OK();
581 mozilla::ipc::IPCResult HangMonitorChild::RecvBeginStartingDebugger() {
582 MOZ_RELEASE_ASSERT(IsOnThread());
584 MonitorAutoLock lock(mMonitor);
585 mStartDebugger = true;
586 return IPC_OK();
589 mozilla::ipc::IPCResult HangMonitorChild::RecvEndStartingDebugger() {
590 MOZ_RELEASE_ASSERT(IsOnThread());
592 MonitorAutoLock lock(mMonitor);
593 mFinishedStartingDebugger = true;
594 return IPC_OK();
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);
610 return IPC_OK();
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);
626 return IPC_OK();
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);
657 return IPC_OK();
660 mozilla::ipc::IPCResult HangMonitorChild::RecvSetMainThreadQoSPriority(
661 const nsIThread::QoSPriority& aQoSPriority) {
662 MOZ_RELEASE_ASSERT(IsOnThread());
664 #ifdef XP_MACOSX
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);
683 if (qosOverride) {
684 pthread_override_qos_class_end_np(qosOverride);
686 })))) {
687 // If we fail to dispatch, go ahead and end the override anyway.
688 pthread_override_qos_class_end_np(qosOverride);
690 #endif
692 return IPC_OK();
695 void HangMonitorChild::Bind(Endpoint<PProcessHangMonitorChild>&& aEndpoint) {
696 MOZ_RELEASE_ASSERT(IsOnThread());
698 DebugOnly<bool> ok = aEndpoint.Bind(this);
699 MOZ_ASSERT(ok);
702 void HangMonitorChild::NotifySlowScriptAsync(TabId aTabId,
703 const nsCString& aFileName,
704 const nsString& aAddonId,
705 const double aDuration) {
706 if (mIPCOpen) {
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());
717 mSentReport = true;
720 MonitorAutoLock lock(mMonitor);
722 if (mTerminateScript) {
723 mTerminateScript = false;
724 return SlowScriptAction::Terminate;
727 if (mStartDebugger) {
728 mStartDebugger = false;
729 return SlowScriptAction::StartDebugger;
733 TabId id;
734 if (aBrowserChild) {
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,
744 aDuration));
745 return SlowScriptAction::Continue;
748 bool HangMonitorChild::IsDebuggerStartupComplete() {
749 MOZ_RELEASE_ASSERT(NS_IsMainThread());
751 MonitorAutoLock lock(mMonitor);
753 if (mFinishedStartingDebugger) {
754 mFinishedStartingDebugger = false;
755 return true;
758 return false;
761 void HangMonitorChild::ClearHang() {
762 MOZ_ASSERT(NS_IsMainThread());
764 if (mSentReport) {
765 // bounce to background thread
766 Dispatch(NewNonOwningRunnableMethod("HangMonitorChild::ClearHangAsync",
767 this,
768 &HangMonitorChild::ClearHangAsync));
770 MonitorAutoLock lock(mMonitor);
771 mSentReport = false;
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
782 if (mIPCOpen) {
783 Unused << SendClearHang();
787 /* HangMonitorParent implementation */
789 HangMonitorParent::HangMonitorParent(ProcessHangMonitor* aMonitor)
790 : mHangMonitor(aMonitor),
791 mIPCOpen(true),
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);
814 if (mProcess) {
815 mProcess->Clear();
816 mProcess = nullptr;
819 nsresult rv = Dispatch(
820 NewNonOwningRunnableMethod("HangMonitorParent::ShutdownOnThread", this,
821 &HangMonitorParent::ShutdownOnThread));
822 if (NS_WARN_IF(NS_FAILED(rv))) {
823 return;
826 while (!mShutdownDone) {
827 mMonitor.Wait();
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
836 // it.
837 if (mIPCOpen) {
838 Close();
841 MonitorAutoLock lock(mMonitor);
842 mShutdownDone = true;
843 mMonitor.Notify();
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 ",
852 this,
853 &HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread,
854 true, id));
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,
865 false, id));
868 void HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread(
869 const bool aPaint, TabId aTabId) {
870 MOZ_RELEASE_ASSERT(IsOnThread());
872 if (mIPCOpen) {
873 if (aPaint) {
874 Unused << SendPaintWhileInterruptingJS(aTabId);
875 } else {
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)) {
890 return;
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) {
909 nsAutoCString tmp;
910 nsresult rv = aNavigationURI->GetSpec(tmp);
911 if (NS_SUCCEEDED(rv)) {
912 spec.emplace(tmp);
916 if (mIPCOpen) {
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));
930 #endif
933 #ifdef XP_MACOSX
934 void HangMonitorParent::SetMainThreadQoSPriorityOnThread(
935 nsIThread::QoSPriority aQoSPriority) {
936 MOZ_RELEASE_ASSERT(IsOnThread());
937 if (mIPCOpen) {
938 Unused << SendSetMainThreadQoSPriority(aQoSPriority);
941 #endif
943 void HangMonitorParent::ActorDestroy(ActorDestroyReason aWhy) {
944 MOZ_RELEASE_ASSERT(IsOnThread());
945 mIPCOpen = false;
948 void HangMonitorParent::Bind(Endpoint<PProcessHangMonitorParent>&& aEndpoint) {
949 MOZ_RELEASE_ASSERT(IsOnThread());
951 DebugOnly<bool> ok = aEndpoint.Bind(this);
952 MOZ_ASSERT(ok);
955 void HangMonitorParent::SendHangNotification(
956 const SlowScriptData& aSlowScriptData, const nsString& aBrowserDumpId) {
957 // chrome process, main thread
958 MOZ_RELEASE_ASSERT(NS_IsMainThread());
960 nsString dumpId;
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()) {
989 return IPC_OK();
992 #ifdef XP_WIN
993 // Don't report hangs if we're debugging the process. You can comment this
994 // line out for testing purposes.
995 if (IsDebuggerPresent()) {
996 return IPC_OK();
998 #endif
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));
1011 return IPC_OK();
1014 mozilla::ipc::IPCResult HangMonitorParent::RecvClearHang() {
1015 // chrome process, background thread
1016 MOZ_RELEASE_ASSERT(IsOnThread());
1018 if (!StaticPrefs::dom_ipc_reportProcessHangs()) {
1019 return IPC_OK();
1022 mHangMonitor->InitiateCPOWTimeout();
1024 MonitorAutoLock lock(mMonitor);
1026 NS_DispatchToMainThread(mMainThreadTaskFactory.NewRunnableMethod(
1027 &HangMonitorParent::ClearHangNotification));
1029 return IPC_OK();
1032 void HangMonitorParent::TerminateScript() {
1033 MOZ_RELEASE_ASSERT(IsOnThread());
1035 if (mIPCOpen) {
1036 Unused << SendTerminateScript();
1040 void HangMonitorParent::BeginStartingDebugger() {
1041 MOZ_RELEASE_ASSERT(IsOnThread());
1043 if (mIPCOpen) {
1044 Unused << SendBeginStartingDebugger();
1048 void HangMonitorParent::EndStartingDebugger() {
1049 MOZ_RELEASE_ASSERT(IsOnThread());
1051 if (mIPCOpen) {
1052 Unused << SendEndStartingDebugger();
1056 /* HangMonitoredProcess implementation */
1058 NS_IMPL_ISUPPORTS(HangMonitoredProcess, nsIHangReport)
1060 NS_IMETHODIMP
1061 HangMonitoredProcess::GetHangDuration(double* aHangDuration) {
1062 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1063 *aHangDuration = mSlowScriptData.duration();
1064 return NS_OK;
1067 NS_IMETHODIMP
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);
1082 return NS_OK;
1086 *aBrowser = nullptr;
1087 return NS_OK;
1090 NS_IMETHODIMP
1091 HangMonitoredProcess::GetScriptFileName(nsACString& aFileName) {
1092 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1093 aFileName = mSlowScriptData.filename();
1094 return NS_OK;
1097 NS_IMETHODIMP
1098 HangMonitoredProcess::GetAddonId(nsAString& aAddonId) {
1099 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1100 aAddonId = mSlowScriptData.addonId();
1101 return NS_OK;
1104 NS_IMETHODIMP
1105 HangMonitoredProcess::TerminateScript() {
1106 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1107 if (!mActor) {
1108 return NS_ERROR_UNEXPECTED;
1111 ProcessHangMonitor::Get()->Dispatch(
1112 NewNonOwningRunnableMethod("HangMonitorParent::TerminateScript", mActor,
1113 &HangMonitorParent::TerminateScript));
1114 return NS_OK;
1117 NS_IMETHODIMP
1118 HangMonitoredProcess::BeginStartingDebugger() {
1119 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1120 if (!mActor) {
1121 return NS_ERROR_UNEXPECTED;
1124 ProcessHangMonitor::Get()->Dispatch(NewNonOwningRunnableMethod(
1125 "HangMonitorParent::BeginStartingDebugger", mActor,
1126 &HangMonitorParent::BeginStartingDebugger));
1127 return NS_OK;
1130 NS_IMETHODIMP
1131 HangMonitoredProcess::EndStartingDebugger() {
1132 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1133 if (!mActor) {
1134 return NS_ERROR_UNEXPECTED;
1137 ProcessHangMonitor::Get()->Dispatch(NewNonOwningRunnableMethod(
1138 "HangMonitorParent::EndStartingDebugger", mActor,
1139 &HangMonitorParent::EndStartingDebugger));
1140 return NS_OK;
1143 NS_IMETHODIMP
1144 HangMonitoredProcess::IsReportForBrowserOrChildren(nsFrameLoader* aFrameLoader,
1145 bool* aResult) {
1146 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1147 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
1149 if (!mActor) {
1150 *aResult = false;
1151 return NS_OK;
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();
1161 if (!bc) {
1162 continue;
1164 if (mContentParent == bc->Canonical()->GetContentParent()) {
1165 *aResult = true;
1166 return NS_OK;
1168 bc->GetChildren(bcs);
1171 *aResult = false;
1172 return NS_OK;
1175 NS_IMETHODIMP
1176 HangMonitoredProcess::UserCanceled() { return NS_OK; }
1178 NS_IMETHODIMP
1179 HangMonitoredProcess::GetChildID(uint64_t* aChildID) {
1180 if (!mContentParent) {
1181 return NS_ERROR_NOT_AVAILABLE;
1183 *aChildID = mContentParent->ChildID();
1184 return NS_OK;
1187 static bool InterruptCallback(JSContext* cx) {
1188 AssertIsOnMainThread();
1189 if (HangMonitorChild* child = HangMonitorChild::Get()) {
1190 return child->InterruptCallback();
1193 return true;
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)))) {
1207 mThread = nullptr;
1209 #ifdef XP_MACOSX
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
1212 // the main thread.
1213 mThread->Dispatch(NS_NewRunnableFunction(
1214 "ProcessHangMonitor::SetPriority",
1215 [] { pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0); }));
1216 #endif
1219 ProcessHangMonitor::~ProcessHangMonitor() {
1220 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1222 MOZ_ASSERT(sInstance == this);
1223 sInstance = nullptr;
1225 mThread->Shutdown();
1226 mThread = nullptr;
1229 ProcessHangMonitor* ProcessHangMonitor::GetOrCreate() {
1230 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1231 if (!sInstance) {
1232 sInstance = new ProcessHangMonitor();
1234 return sInstance;
1237 NS_IMPL_ISUPPORTS(ProcessHangMonitor, nsIObserver)
1239 NS_IMETHODIMP
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()) {
1245 child->Shutdown();
1248 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1249 obs->RemoveObserver(this, "xpcom-shutdown");
1251 return NS_OK;
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());
1270 if (mCPOWTimeout) {
1271 mCPOWTimeout = false;
1272 return true;
1274 return 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);
1293 monitor->Dispatch(
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() {
1318 bool on;
1319 return NS_SUCCEEDED(mThread->IsOnCurrentThread(&on)) && on;
1322 /* static */
1323 already_AddRefed<PProcessHangMonitorParent> ProcessHangMonitor::AddProcess(
1324 ContentParent* aContentParent) {
1325 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1327 if (!StaticPrefs::dom_ipc_processHangMonitor_AtStartup()) {
1328 return nullptr;
1331 Endpoint<PProcessHangMonitorParent> parent;
1332 Endpoint<PProcessHangMonitorChild> child;
1333 nsresult rv;
1334 rv = PProcessHangMonitor::CreateEndpoints(&parent, &child);
1335 if (NS_FAILED(rv)) {
1336 MOZ_ASSERT(false, "PProcessHangMonitor::CreateEndpoints failed");
1337 return nullptr;
1340 if (!aContentParent->SendInitProcessHangMonitor(std::move(child))) {
1341 MOZ_ASSERT(false);
1342 return nullptr;
1345 return CreateHangMonitorParent(aContentParent, std::move(parent));
1348 /* static */
1349 void ProcessHangMonitor::RemoveProcess(PProcessHangMonitorParent* aParent) {
1350 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1351 auto parent = static_cast<HangMonitorParent*>(aParent);
1352 parent->Shutdown();
1355 /* static */
1356 void ProcessHangMonitor::ClearHang() {
1357 AssertIsOnMainThread();
1358 if (HangMonitorChild* child = HangMonitorChild::Get()) {
1359 child->ClearHang();
1363 /* static */
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);
1371 /* static */
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);
1379 /* static */
1380 void ProcessHangMonitor::ClearPaintWhileInterruptingJS() {
1381 ReleaseAssertIsOnMainThread();
1382 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
1384 if (HangMonitorChild* child = HangMonitorChild::Get()) {
1385 child->ClearPaintWhileInterruptingJS();
1389 /* static */
1390 void ProcessHangMonitor::MaybeStartPaintWhileInterruptingJS() {
1391 ReleaseAssertIsOnMainThread();
1392 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
1394 if (HangMonitorChild* child = HangMonitorChild::Get()) {
1395 child->MaybeStartPaintWhileInterruptingJS();
1399 /* static */
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);
1410 /* static */
1411 void ProcessHangMonitor::SetMainThreadQoSPriority(
1412 PProcessHangMonitorParent* aParent, nsIThread::QoSPriority aQoSPriority) {
1413 ReleaseAssertIsOnMainThread();
1414 auto* parent = static_cast<HangMonitorParent*>(aParent);
1415 parent->SetMainThreadQoSPriority(aQoSPriority);