tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ProcessHangMonitor.cpp (43627B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/ProcessHangMonitor.h"
      8 
      9 #include "MainThreadUtils.h"
     10 #include "base/task.h"
     11 #include "base/thread.h"
     12 #include "jsapi.h"
     13 #include "mozilla/AppShutdown.h"
     14 #include "mozilla/Atomics.h"
     15 #include "mozilla/BackgroundHangMonitor.h"
     16 #include "mozilla/BasePrincipal.h"
     17 #include "mozilla/Monitor.h"
     18 #include "mozilla/Preferences.h"
     19 #include "mozilla/ProcessHangMonitorIPC.h"
     20 #include "mozilla/ProfilerMarkers.h"
     21 #include "mozilla/StaticMonitor.h"
     22 #include "mozilla/StaticPrefs_browser.h"
     23 #include "mozilla/StaticPrefs_dom.h"
     24 #include "mozilla/StaticPtr.h"
     25 #include "mozilla/dom/BrowserChild.h"
     26 #include "mozilla/dom/BrowserParent.h"
     27 #include "mozilla/dom/CancelContentJSOptionsBinding.h"
     28 #include "mozilla/dom/CanonicalBrowsingContext.h"
     29 #include "mozilla/dom/ContentParent.h"
     30 #include "mozilla/dom/Document.h"
     31 #include "mozilla/dom/Element.h"
     32 #include "mozilla/dom/ScriptSettings.h"
     33 #include "mozilla/ipc/Endpoint.h"
     34 #include "mozilla/ipc/ProcessChild.h"
     35 #include "mozilla/ipc/TaskFactory.h"
     36 #include "nsExceptionHandler.h"
     37 #include "nsFrameLoader.h"
     38 #include "nsIHangReport.h"
     39 #include "nsIRemoteTab.h"
     40 #include "nsNetUtil.h"
     41 #include "nsQueryObject.h"
     42 #include "nsThreadUtils.h"
     43 #include "xpcprivate.h"
     44 
     45 #ifdef XP_WIN
     46 // For IsDebuggerPresent()
     47 #  include <windows.h>
     48 #endif
     49 
     50 #ifdef XP_MACOSX
     51 // for qos controls
     52 #  include <sys/qos.h>
     53 #endif
     54 
     55 using namespace mozilla;
     56 using namespace mozilla::dom;
     57 using namespace mozilla::ipc;
     58 
     59 /*
     60 * Basic architecture:
     61 *
     62 * Each process has its own ProcessHangMonitor singleton. This singleton exists
     63 * as long as there is at least one content process in the system. Each content
     64 * process has a HangMonitorChild and the chrome process has one
     65 * HangMonitorParent per process. Each process (including the chrome process)
     66 * runs a hang monitoring thread. The PHangMonitor actors are bound to this
     67 * thread so that they never block on the main thread.
     68 *
     69 * When the content process detects a hang, it posts a task to its hang thread,
     70 * which sends an IPC message to the hang thread in the parent. The parent
     71 * cancels any ongoing CPOW requests and then posts a runnable to the main
     72 * thread that notifies Firefox frontend code of the hang. The frontend code is
     73 * passed an nsIHangReport, which can be used to terminate the hang.
     74 *
     75 * If the user chooses to terminate a script, a task is posted to the chrome
     76 * process's hang monitoring thread, which sends an IPC message to the hang
     77 * thread in the content process. That thread sets a flag to indicate that JS
     78 * execution should be terminated the next time it hits the interrupt
     79 * callback. A similar scheme is used for debugging slow scripts. If a content
     80 * process or plug-in needs to be terminated, the chrome process does so
     81 * directly, without messaging the content process.
     82 */
     83 
     84 namespace {
     85 
     86 LazyLogModule gQoSLog("QoSPriority");  // For RecvSetMainThreadQoSPriority.
     87 
     88 /* Child process objects */
     89 
     90 class HangMonitorChild : public PProcessHangMonitorChild,
     91                         public BackgroundHangAnnotator {
     92 public:
     93  NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
     94      HangMonitorChild, override)
     95 
     96  void Bind(Endpoint<PProcessHangMonitorChild>&& aEndpoint);
     97 
     98  using SlowScriptAction = ProcessHangMonitor::SlowScriptAction;
     99  SlowScriptAction NotifySlowScript(nsIBrowserChild* aBrowserChild,
    100                                    const char* aFileName,
    101                                    const nsString& aAddonId,
    102                                    const double aDuration);
    103  void NotifySlowScriptAsync(TabId aTabId, const nsCString& aFileName,
    104                             const nsString& aAddonId, const double aDuration);
    105 
    106  bool IsDebuggerStartupComplete();
    107 
    108  void ClearHang();
    109  void ClearHangAsync();
    110  void ClearPaintWhileInterruptingJS();
    111 
    112  // MaybeStartPaintWhileInterruptingJS will notify the background hang monitor
    113  // of activity if this is the first time calling it since
    114  // ClearPaintWhileInterruptingJS. It should be callable from any thread, but
    115  // you must be holding mMonitor if using it off the main thread, since it
    116  // could race with ClearPaintWhileInterruptingJS.
    117  void MaybeStartPaintWhileInterruptingJS();
    118 
    119  mozilla::ipc::IPCResult RecvTerminateScript() override;
    120  mozilla::ipc::IPCResult RecvRequestContentJSInterrupt() override;
    121  mozilla::ipc::IPCResult RecvBeginStartingDebugger() override;
    122  mozilla::ipc::IPCResult RecvEndStartingDebugger() override;
    123 
    124  mozilla::ipc::IPCResult RecvPaintWhileInterruptingJS(
    125      const TabId& aTabId) override;
    126 
    127  mozilla::ipc::IPCResult RecvUnloadLayersWhileInterruptingJS(
    128      const TabId& aTabId) override;
    129 
    130  mozilla::ipc::IPCResult RecvCancelContentJSExecutionIfRunning(
    131      const TabId& aTabId, const nsIRemoteTab::NavigationType& aNavigationType,
    132      const int32_t& aNavigationIndex,
    133      const mozilla::Maybe<nsCString>& aNavigationURI,
    134      const int32_t& aEpoch) override;
    135 
    136  mozilla::ipc::IPCResult RecvSetMainThreadQoSPriority(
    137      const nsIThread::QoSPriority& aQoSPriority) override;
    138 
    139  void ActorDestroy(ActorDestroyReason aWhy) override;
    140 
    141  bool InterruptCallback();
    142  void Shutdown();
    143 
    144  static HangMonitorChild* Get() MOZ_REQUIRES(sMainThreadCapability) {
    145    return sInstance;
    146  }
    147 
    148  static void CreateAndBind(ProcessHangMonitor* aMonitor,
    149                            Endpoint<PProcessHangMonitorChild>&& aEndpoint);
    150 
    151  void Dispatch(already_AddRefed<nsIRunnable> aRunnable) {
    152    mHangMonitor->Dispatch(std::move(aRunnable));
    153  }
    154  bool IsOnThread() { return mHangMonitor->IsOnThread(); }
    155 
    156  void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override;
    157 
    158 protected:
    159  friend class mozilla::ProcessHangMonitor;
    160 
    161 private:
    162  explicit HangMonitorChild(ProcessHangMonitor* aMonitor);
    163  ~HangMonitorChild() override;
    164 
    165  void ShutdownOnThread();
    166 
    167  static StaticRefPtr<HangMonitorChild> sInstance
    168      MOZ_GUARDED_BY(sMainThreadCapability);
    169 
    170  const RefPtr<ProcessHangMonitor> mHangMonitor;
    171 
    172 #ifdef XP_MACOSX
    173  // On macOS, the pthread_t is required to start a QoS class override. As we
    174  // can't recover this from a PRThread*, we need to record it when the
    175  // HangMonitorChild is initially created on the main thread.
    176  const pthread_t mMainPThread;
    177 #endif
    178 
    179  Monitor mMonitor;
    180 
    181  // Main thread-only.
    182  bool mSentReport;
    183 
    184  // These fields must be accessed with mMonitor held.
    185  bool mTerminateScript MOZ_GUARDED_BY(mMonitor);
    186  bool mStartDebugger MOZ_GUARDED_BY(mMonitor);
    187  bool mFinishedStartingDebugger MOZ_GUARDED_BY(mMonitor);
    188 
    189  // this variable is used to paint/unload layers
    190  // if not set, no action required
    191  // true means, we will paint. false - unload layers
    192  Maybe<bool> mPaintWhileInterruptingJS MOZ_GUARDED_BY(mMonitor);
    193  TabId mPaintWhileInterruptingJSTab MOZ_GUARDED_BY(mMonitor);
    194  bool mCancelContentJS MOZ_GUARDED_BY(mMonitor);
    195  TabId mCancelContentJSTab MOZ_GUARDED_BY(mMonitor);
    196  nsIRemoteTab::NavigationType mCancelContentJSNavigationType
    197      MOZ_GUARDED_BY(mMonitor);
    198  int32_t mCancelContentJSNavigationIndex MOZ_GUARDED_BY(mMonitor);
    199  mozilla::Maybe<nsCString> mCancelContentJSNavigationURI
    200      MOZ_GUARDED_BY(mMonitor);
    201  int32_t mCancelContentJSEpoch MOZ_GUARDED_BY(mMonitor);
    202  bool mShutdownDone MOZ_GUARDED_BY(mMonitor);
    203 
    204  JSContext* mContext;  // const after constructor
    205 
    206  // This field is only accessed on the hang thread.
    207  bool mIPCOpen;
    208 
    209  // Allows us to ensure we NotifyActivity only once, allowing
    210  // either thread to do so.
    211  Atomic<bool> mPaintWhileInterruptingJSActive;
    212 };
    213 
    214 StaticRefPtr<HangMonitorChild> HangMonitorChild::sInstance;
    215 
    216 /* Parent process objects */
    217 
    218 class HangMonitorParent;
    219 
    220 class HangMonitoredProcess final : public nsIHangReport {
    221 public:
    222  NS_DECL_THREADSAFE_ISUPPORTS
    223 
    224  HangMonitoredProcess(HangMonitorParent* aActor, ContentParent* aContentParent)
    225      : mActor(aActor), mContentParent(aContentParent) {}
    226 
    227  NS_DECL_NSIHANGREPORT
    228 
    229  // Called when a content process shuts down.
    230  void Clear() {
    231    mContentParent = nullptr;
    232    mActor = nullptr;
    233  }
    234 
    235  /**
    236   * Sets the information associated with this hang: this includes the tab ID,
    237   * filename, duration, and an add-on ID if it was caused by an add-on.
    238   *
    239   * @param aDumpId The ID of a minidump taken when the hang occurred
    240   */
    241  void SetSlowScriptData(const SlowScriptData& aSlowScriptData,
    242                         const nsAString& aDumpId) {
    243    mSlowScriptData = aSlowScriptData;
    244    mDumpId = aDumpId;
    245  }
    246 
    247  void ClearHang() {
    248    mSlowScriptData = SlowScriptData();
    249    mDumpId.Truncate();
    250  }
    251 
    252 private:
    253  ~HangMonitoredProcess() = default;
    254 
    255  // Everything here is main thread-only.
    256  HangMonitorParent* mActor;
    257  ContentParent* mContentParent;
    258  SlowScriptData mSlowScriptData;
    259  nsAutoString mDumpId;
    260 };
    261 
    262 class HangMonitorParent : public PProcessHangMonitorParent {
    263 public:
    264  NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
    265      HangMonitorParent, override)
    266 
    267  explicit HangMonitorParent(ProcessHangMonitor* aMonitor);
    268 
    269  void Bind(Endpoint<PProcessHangMonitorParent>&& aEndpoint);
    270 
    271  mozilla::ipc::IPCResult RecvHangEvidence(
    272      const SlowScriptData& aSlowScriptData) override;
    273  mozilla::ipc::IPCResult RecvClearHang() override;
    274 
    275  void ActorDestroy(ActorDestroyReason aWhy) override;
    276 
    277  void SetProcess(HangMonitoredProcess* aProcess) { mProcess = aProcess; }
    278 
    279  void Shutdown();
    280 
    281  void PaintWhileInterruptingJS(dom::BrowserParent* aTab);
    282 
    283  void UnloadLayersWhileInterruptingJS(dom::BrowserParent* aTab);
    284  void CancelContentJSExecutionIfRunning(
    285      dom::BrowserParent* aBrowserParent,
    286      nsIRemoteTab::NavigationType aNavigationType,
    287      const dom::CancelContentJSOptions& aCancelContentJSOptions);
    288 
    289  void SetMainThreadQoSPriority(nsIThread::QoSPriority aQoSPriority);
    290 
    291  void TerminateScript();
    292  void BeginStartingDebugger();
    293  void EndStartingDebugger();
    294 
    295  nsresult Dispatch(already_AddRefed<nsIRunnable> aRunnable) {
    296    return mHangMonitor->Dispatch(std::move(aRunnable));
    297  }
    298  bool IsOnThread() { return mHangMonitor->IsOnThread(); }
    299 
    300 private:
    301  ~HangMonitorParent() override = default;
    302 
    303  void SendHangNotification(const SlowScriptData& aSlowScriptData,
    304                            const nsString& aBrowserDumpId);
    305 
    306  void ClearHangNotification();
    307 
    308  void PaintOrUnloadLayersWhileInterruptingJSOnThread(bool aPaint,
    309                                                      TabId aTabId);
    310  void CancelContentJSExecutionIfRunningOnThread(
    311      TabId aTabId, nsIRemoteTab::NavigationType aNavigationType,
    312      int32_t aNavigationIndex, nsIURI* aNavigationURI, int32_t aEpoch);
    313 
    314 #ifdef XP_MACOSX
    315  void SetMainThreadQoSPriorityOnThread(nsIThread::QoSPriority aQoSPriority);
    316 #endif
    317 
    318  void ShutdownOnThread();
    319 
    320  const RefPtr<ProcessHangMonitor> mHangMonitor;
    321 
    322  // This field is only accessed on the hang thread.
    323  bool mIPCOpen;
    324 
    325  Monitor mMonitor;
    326 
    327  // MainThread only
    328  RefPtr<HangMonitoredProcess> mProcess;
    329 
    330  // Must be accessed with mMonitor held.
    331  bool mShutdownDone MOZ_GUARDED_BY(mMonitor);
    332  mozilla::ipc::TaskFactory<HangMonitorParent> mMainThreadTaskFactory
    333      MOZ_GUARDED_BY(mMonitor);
    334 };
    335 
    336 }  // namespace
    337 
    338 /* HangMonitorChild implementation */
    339 
    340 HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor)
    341    : mHangMonitor(aMonitor),
    342 #ifdef XP_MACOSX
    343      mMainPThread(pthread_self()),
    344 #endif
    345      mMonitor("HangMonitorChild lock"),
    346      mSentReport(false),
    347      mTerminateScript(false),
    348      mStartDebugger(false),
    349      mFinishedStartingDebugger(false),
    350      mCancelContentJS(false),
    351      mCancelContentJSNavigationType(nsIRemoteTab::NAVIGATE_BACK),
    352      mCancelContentJSNavigationIndex(0),
    353      mCancelContentJSEpoch(0),
    354      mShutdownDone(false),
    355      mIPCOpen(true),
    356      mPaintWhileInterruptingJSActive(false) {
    357  ReleaseAssertIsOnMainThread();
    358  MOZ_ASSERT(!sInstance);
    359 
    360  mContext = danger::GetJSContext();
    361 }
    362 
    363 HangMonitorChild::~HangMonitorChild() {
    364  ReleaseAssertIsOnMainThread();
    365  MOZ_ASSERT(sInstance != this);
    366 }
    367 
    368 void HangMonitorChild::CreateAndBind(
    369    ProcessHangMonitor* aMonitor,
    370    Endpoint<PProcessHangMonitorChild>&& aEndpoint) {
    371  ReleaseAssertIsOnMainThread();
    372  MOZ_ASSERT(!sInstance);
    373 
    374  sInstance = new HangMonitorChild(aMonitor);
    375 
    376  BackgroundHangMonitor::RegisterAnnotator(*sInstance);
    377 
    378  aMonitor->Dispatch(NewRunnableMethod<Endpoint<PProcessHangMonitorChild>&&>(
    379      "HangMonitorChild::Bind", sInstance.get(), &HangMonitorChild::Bind,
    380      std::move(aEndpoint)));
    381 }
    382 
    383 bool HangMonitorChild::InterruptCallback() {
    384  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    385 
    386  if (StaticPrefs::dom_abort_script_on_child_shutdown() &&
    387      mozilla::AppShutdown::IsShutdownImpending()) {
    388    // We preserve chrome JS from cancel, but not extension content JS.
    389    if (!nsContentUtils::IsCallerChrome()) {
    390      NS_WARNING(
    391          "HangMonitorChild::InterruptCallback: ExpectingShutdown, "
    392          "canceling content JS execution.\n");
    393      return false;
    394    }
    395    return true;
    396  }
    397 
    398  // Don't start painting if we're not in a good place to run script. We run
    399  // chrome script during layout and such, and it wouldn't be good to interrupt
    400  // painting code from there.
    401  if (!nsContentUtils::IsSafeToRunScript()) {
    402    return true;
    403  }
    404 
    405  Maybe<bool> paintWhileInterruptingJS;
    406  TabId paintWhileInterruptingJSTab;
    407 
    408  {
    409    MonitorAutoLock lock(mMonitor);
    410    paintWhileInterruptingJS = mPaintWhileInterruptingJS;
    411    paintWhileInterruptingJSTab = mPaintWhileInterruptingJSTab;
    412 
    413    mPaintWhileInterruptingJS.reset();
    414  }
    415 
    416  if (paintWhileInterruptingJS.isSome()) {
    417    RefPtr<BrowserChild> browserChild =
    418        BrowserChild::FindBrowserChild(paintWhileInterruptingJSTab);
    419    if (browserChild) {
    420      js::AutoAssertNoContentJS nojs(mContext);
    421      if (paintWhileInterruptingJS.value()) {
    422        AUTO_PROFILER_MARKER_UNTYPED(
    423            "InterruptCallback: PaintWhileInterruptingJS", DOM, {});
    424        browserChild->PaintWhileInterruptingJS();
    425      } else {
    426        AUTO_PROFILER_MARKER_UNTYPED(
    427            "InterruptCallback: UnloadLayersWhileInterruptingJS", DOM, {});
    428        browserChild->UnloadLayersWhileInterruptingJS();
    429      }
    430    }
    431  }
    432 
    433  // Only handle the interrupt for cancelling content JS if we have a
    434  // non-privileged script (i.e. not part of Gecko or an add-on).
    435  JS::Rooted<JSObject*> global(mContext, JS::CurrentGlobalOrNull(mContext));
    436  nsIPrincipal* principal = xpc::GetObjectPrincipal(global);
    437  if (principal && (principal->IsSystemPrincipal() ||
    438                    principal->GetIsAddonOrExpandedAddonPrincipal())) {
    439    return true;
    440  }
    441 
    442  nsCOMPtr<nsPIDOMWindowInner> win = xpc::WindowOrNull(global);
    443  if (!win) {
    444    return true;
    445  }
    446 
    447  bool cancelContentJS;
    448  TabId cancelContentJSTab;
    449  nsIRemoteTab::NavigationType cancelContentJSNavigationType;
    450  int32_t cancelContentJSNavigationIndex;
    451  mozilla::Maybe<nsCString> cancelContentJSNavigationURI;
    452  int32_t cancelContentJSEpoch;
    453 
    454  {
    455    MonitorAutoLock lock(mMonitor);
    456    cancelContentJS = mCancelContentJS;
    457    cancelContentJSTab = mCancelContentJSTab;
    458    cancelContentJSNavigationType = mCancelContentJSNavigationType;
    459    cancelContentJSNavigationIndex = mCancelContentJSNavigationIndex;
    460    cancelContentJSNavigationURI = std::move(mCancelContentJSNavigationURI);
    461    cancelContentJSEpoch = mCancelContentJSEpoch;
    462 
    463    mCancelContentJS = false;
    464  }
    465 
    466  if (cancelContentJS) {
    467    js::AutoAssertNoContentJS nojs(mContext);
    468 
    469    RefPtr<BrowserChild> browserChild =
    470        BrowserChild::FindBrowserChild(cancelContentJSTab);
    471    RefPtr<BrowserChild> browserChildFromWin = BrowserChild::GetFrom(win);
    472    if (!browserChild || !browserChildFromWin) {
    473      return true;
    474    }
    475 
    476    TabId tabIdFromWin = browserChildFromWin->GetTabId();
    477    if (tabIdFromWin != cancelContentJSTab) {
    478      // The currently-executing content JS doesn't belong to the tab that
    479      // requested cancellation of JS. Just return and let the JS continue.
    480      return true;
    481    }
    482 
    483    nsresult rv;
    484    nsCOMPtr<nsIURI> uri;
    485 
    486    if (cancelContentJSNavigationURI) {
    487      rv = NS_NewURI(getter_AddRefs(uri), cancelContentJSNavigationURI.value());
    488      if (NS_FAILED(rv)) {
    489        return true;
    490      }
    491    }
    492 
    493    bool canCancel;
    494    rv = browserChild->CanCancelContentJS(cancelContentJSNavigationType,
    495                                          cancelContentJSNavigationIndex, uri,
    496                                          cancelContentJSEpoch, &canCancel);
    497    if (NS_SUCCEEDED(rv) && canCancel) {
    498      // Don't add this page to the BF cache, since we're cancelling its JS.
    499      if (Document* doc = win->GetExtantDoc()) {
    500        doc->DisallowBFCaching();
    501      }
    502 
    503      return false;
    504    }
    505  }
    506 
    507  return true;
    508 }
    509 
    510 void HangMonitorChild::AnnotateHang(BackgroundHangAnnotations& aAnnotations) {
    511  if (mPaintWhileInterruptingJSActive) {
    512    aAnnotations.AddAnnotation(u"PaintWhileInterruptingJS"_ns, true);
    513  }
    514 }
    515 
    516 void HangMonitorChild::Shutdown() {
    517  ReleaseAssertIsOnMainThread();
    518 
    519  BackgroundHangMonitor::UnregisterAnnotator(*this);
    520 
    521  {
    522    MonitorAutoLock lock(mMonitor);
    523    while (!mShutdownDone) {
    524      mMonitor.Wait();
    525    }
    526  }
    527 
    528  MOZ_ASSERT(sInstance == this);
    529  sInstance = nullptr;
    530 }
    531 
    532 void HangMonitorChild::ShutdownOnThread() {
    533  MOZ_RELEASE_ASSERT(IsOnThread());
    534 
    535  MonitorAutoLock lock(mMonitor);
    536  mShutdownDone = true;
    537  mMonitor.Notify();
    538 }
    539 
    540 void HangMonitorChild::ActorDestroy(ActorDestroyReason aWhy) {
    541  MOZ_RELEASE_ASSERT(IsOnThread());
    542 
    543  mIPCOpen = false;
    544 
    545  // We use a task here to ensure that IPDL is finished with this
    546  // HangMonitorChild before it gets deleted on the main thread.
    547  Dispatch(NewNonOwningRunnableMethod("HangMonitorChild::ShutdownOnThread",
    548                                      this,
    549                                      &HangMonitorChild::ShutdownOnThread));
    550 }
    551 
    552 mozilla::ipc::IPCResult HangMonitorChild::RecvTerminateScript() {
    553  MOZ_RELEASE_ASSERT(IsOnThread());
    554 
    555  MonitorAutoLock lock(mMonitor);
    556  mTerminateScript = true;
    557  return IPC_OK();
    558 }
    559 
    560 mozilla::ipc::IPCResult HangMonitorChild::RecvRequestContentJSInterrupt() {
    561  MOZ_RELEASE_ASSERT(IsOnThread());
    562 
    563  // In order to cancel JS execution on shutdown, we expect that
    564  // ProcessChild::NotifiedImpendingShutdown has been called before.
    565  if (AppShutdown::IsShutdownImpending()) {
    566    ProcessChild::AppendToIPCShutdownStateAnnotation(
    567        "HangMonitorChild::RecvRequestContentJSInterrupt (expected)"_ns);
    568  } else {
    569    ProcessChild::AppendToIPCShutdownStateAnnotation(
    570        "HangMonitorChild::RecvRequestContentJSInterrupt (unexpected)"_ns);
    571  }
    572  JS_RequestInterruptCallback(mContext);
    573  return IPC_OK();
    574 }
    575 
    576 mozilla::ipc::IPCResult HangMonitorChild::RecvBeginStartingDebugger() {
    577  MOZ_RELEASE_ASSERT(IsOnThread());
    578 
    579  MonitorAutoLock lock(mMonitor);
    580  mStartDebugger = true;
    581  return IPC_OK();
    582 }
    583 
    584 mozilla::ipc::IPCResult HangMonitorChild::RecvEndStartingDebugger() {
    585  MOZ_RELEASE_ASSERT(IsOnThread());
    586 
    587  MonitorAutoLock lock(mMonitor);
    588  mFinishedStartingDebugger = true;
    589  return IPC_OK();
    590 }
    591 
    592 mozilla::ipc::IPCResult HangMonitorChild::RecvPaintWhileInterruptingJS(
    593    const TabId& aTabId) {
    594  PROFILER_MARKER_UNTYPED("PaintWhileInterruptingJS", DOM, {});
    595  MOZ_RELEASE_ASSERT(IsOnThread());
    596 
    597  {
    598    MonitorAutoLock lock(mMonitor);
    599    MaybeStartPaintWhileInterruptingJS();
    600    mPaintWhileInterruptingJS = Some(true);
    601    mPaintWhileInterruptingJSTab = aTabId;
    602  }
    603 
    604  JS_RequestInterruptCallback(mContext);
    605 
    606  return IPC_OK();
    607 }
    608 
    609 mozilla::ipc::IPCResult HangMonitorChild::RecvUnloadLayersWhileInterruptingJS(
    610    const TabId& aTabId) {
    611  MOZ_RELEASE_ASSERT(IsOnThread());
    612 
    613  {
    614    MonitorAutoLock lock(mMonitor);
    615    MaybeStartPaintWhileInterruptingJS();
    616    mPaintWhileInterruptingJS = Some(false);
    617    mPaintWhileInterruptingJSTab = aTabId;
    618  }
    619 
    620  JS_RequestInterruptCallback(mContext);
    621 
    622  return IPC_OK();
    623 }
    624 
    625 void HangMonitorChild::MaybeStartPaintWhileInterruptingJS() {
    626  mPaintWhileInterruptingJSActive = true;
    627 }
    628 
    629 void HangMonitorChild::ClearPaintWhileInterruptingJS() {
    630  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    631  MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
    632  mPaintWhileInterruptingJSActive = false;
    633 }
    634 
    635 mozilla::ipc::IPCResult HangMonitorChild::RecvCancelContentJSExecutionIfRunning(
    636    const TabId& aTabId, const nsIRemoteTab::NavigationType& aNavigationType,
    637    const int32_t& aNavigationIndex,
    638    const mozilla::Maybe<nsCString>& aNavigationURI, const int32_t& aEpoch) {
    639  MOZ_RELEASE_ASSERT(IsOnThread());
    640 
    641  {
    642    MonitorAutoLock lock(mMonitor);
    643    mCancelContentJS = true;
    644    mCancelContentJSTab = aTabId;
    645    mCancelContentJSNavigationType = aNavigationType;
    646    mCancelContentJSNavigationIndex = aNavigationIndex;
    647    mCancelContentJSNavigationURI = aNavigationURI;
    648    mCancelContentJSEpoch = aEpoch;
    649  }
    650 
    651  JS_RequestInterruptCallback(mContext);
    652 
    653  return IPC_OK();
    654 }
    655 
    656 const char* DefineQoS(const nsIThread::QoSPriority& aQoSPriority) {
    657  if (aQoSPriority == nsIThread::QOS_PRIORITY_LOW) {
    658    return "BACKGROUND";
    659  }
    660  // As of right now, all non-low QoS priorities default to the thread's normal
    661  // priority.
    662  return "NORMAL";
    663 }
    664 
    665 mozilla::ipc::IPCResult HangMonitorChild::RecvSetMainThreadQoSPriority(
    666    const nsIThread::QoSPriority& aQoSPriority) {
    667  MOZ_RELEASE_ASSERT(IsOnThread());
    668  MOZ_LOG(gQoSLog, LogLevel::Debug,
    669          ("Priority change %s recieved by content process.",
    670           DefineQoS(aQoSPriority)));
    671 
    672 #ifdef XP_MACOSX
    673  // If the new priority is the background (low) priority, we can tell the OS to
    674  // put the main thread on low-power cores. Alternately, if we are changing
    675  // from the background to a higher priority, we change the main thread back to
    676  // the |user-interactive| state, defined in MacOS's QoS documentation as
    677  // reserved for main threads.
    678  qos_class_t qosClass = aQoSPriority == nsIThread::QOS_PRIORITY_LOW
    679                             ? QOS_CLASS_BACKGROUND
    680                             : QOS_CLASS_USER_INTERACTIVE;
    681 
    682  // We can't directly set the main thread's QoS class from off-main-thread.
    683  // However, we can start a QoS class override to raise the QoS, then dispatch
    684  // a runnable to set the QoS class and clear the override once complete.
    685  pthread_override_t qosOverride =
    686      pthread_override_qos_class_start_np(mMainPThread, qosClass, 0);
    687  if (NS_FAILED(NS_DispatchToMainThread(NS_NewRunnableFunction(
    688          "HangMonitorChild::RecvSetMainThreadQoSPriority",
    689          [qosClass, qosOverride, aQoSPriority] {
    690            MOZ_LOG(
    691                gQoSLog, LogLevel::Debug,
    692                ("Override %s sent to main thread.", DefineQoS(aQoSPriority)));
    693            pthread_set_qos_class_self_np(qosClass, 0);
    694            if (qosOverride) {
    695              pthread_override_qos_class_end_np(qosOverride);
    696              MOZ_LOG(gQoSLog, LogLevel::Debug,
    697                      ("Override %s removed from main thread.",
    698                       DefineQoS(aQoSPriority)));
    699            }
    700          })))) {
    701    // If we fail to dispatch, go ahead and end the override anyway.
    702    pthread_override_qos_class_end_np(qosOverride);
    703    MOZ_LOG(gQoSLog, LogLevel::Debug,
    704            ("Override %s removed from main thread.", DefineQoS(aQoSPriority)));
    705  }
    706 #endif
    707 
    708  return IPC_OK();
    709 }
    710 
    711 void HangMonitorChild::Bind(Endpoint<PProcessHangMonitorChild>&& aEndpoint) {
    712  MOZ_RELEASE_ASSERT(IsOnThread());
    713 
    714  DebugOnly<bool> ok = aEndpoint.Bind(this);
    715  MOZ_ASSERT(ok);
    716 }
    717 
    718 void HangMonitorChild::NotifySlowScriptAsync(TabId aTabId,
    719                                             const nsCString& aFileName,
    720                                             const nsString& aAddonId,
    721                                             const double aDuration) {
    722  if (mIPCOpen) {
    723    (void)SendHangEvidence(
    724        SlowScriptData(aTabId, aFileName, aAddonId, aDuration));
    725  }
    726 }
    727 
    728 HangMonitorChild::SlowScriptAction HangMonitorChild::NotifySlowScript(
    729    nsIBrowserChild* aBrowserChild, const char* aFileName,
    730    const nsString& aAddonId, const double aDuration) {
    731  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    732 
    733  mSentReport = true;
    734 
    735  {
    736    MonitorAutoLock lock(mMonitor);
    737 
    738    if (mTerminateScript) {
    739      mTerminateScript = false;
    740      return SlowScriptAction::Terminate;
    741    }
    742 
    743    if (mStartDebugger) {
    744      mStartDebugger = false;
    745      return SlowScriptAction::StartDebugger;
    746    }
    747  }
    748 
    749  TabId id;
    750  if (aBrowserChild) {
    751    RefPtr<BrowserChild> browserChild =
    752        static_cast<BrowserChild*>(aBrowserChild);
    753    id = browserChild->GetTabId();
    754  }
    755  nsAutoCString filename(aFileName);
    756 
    757  Dispatch(NewNonOwningRunnableMethod<TabId, nsCString, nsString, double>(
    758      "HangMonitorChild::NotifySlowScriptAsync", this,
    759      &HangMonitorChild::NotifySlowScriptAsync, id, filename, aAddonId,
    760      aDuration));
    761  return SlowScriptAction::Continue;
    762 }
    763 
    764 bool HangMonitorChild::IsDebuggerStartupComplete() {
    765  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    766 
    767  MonitorAutoLock lock(mMonitor);
    768 
    769  if (mFinishedStartingDebugger) {
    770    mFinishedStartingDebugger = false;
    771    return true;
    772  }
    773 
    774  return false;
    775 }
    776 
    777 void HangMonitorChild::ClearHang() {
    778  MOZ_ASSERT(NS_IsMainThread());
    779 
    780  if (mSentReport) {
    781    // bounce to background thread
    782    Dispatch(NewNonOwningRunnableMethod("HangMonitorChild::ClearHangAsync",
    783                                        this,
    784                                        &HangMonitorChild::ClearHangAsync));
    785 
    786    MonitorAutoLock lock(mMonitor);
    787    mSentReport = false;
    788    mTerminateScript = false;
    789    mStartDebugger = false;
    790    mFinishedStartingDebugger = false;
    791  }
    792 }
    793 
    794 void HangMonitorChild::ClearHangAsync() {
    795  MOZ_RELEASE_ASSERT(IsOnThread());
    796 
    797  // bounce back to parent on background thread
    798  if (mIPCOpen) {
    799    (void)SendClearHang();
    800  }
    801 }
    802 
    803 /* HangMonitorParent implementation */
    804 
    805 HangMonitorParent::HangMonitorParent(ProcessHangMonitor* aMonitor)
    806    : mHangMonitor(aMonitor),
    807      mIPCOpen(true),
    808      mMonitor("HangMonitorParent lock"),
    809      mShutdownDone(false),
    810      mMainThreadTaskFactory(this) {
    811  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    812 }
    813 
    814 void HangMonitorParent::Shutdown() {
    815  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    816 
    817  MonitorAutoLock lock(mMonitor);
    818 
    819  if (mProcess) {
    820    mProcess->Clear();
    821    mProcess = nullptr;
    822  }
    823 
    824  nsresult rv = Dispatch(
    825      NewNonOwningRunnableMethod("HangMonitorParent::ShutdownOnThread", this,
    826                                 &HangMonitorParent::ShutdownOnThread));
    827  if (NS_WARN_IF(NS_FAILED(rv))) {
    828    return;
    829  }
    830 
    831  while (!mShutdownDone) {
    832    mMonitor.Wait();
    833  }
    834 }
    835 
    836 void HangMonitorParent::ShutdownOnThread() {
    837  MOZ_RELEASE_ASSERT(IsOnThread());
    838 
    839  // mIPCOpen is only written from this thread, so need need to take the lock
    840  // here. We'd be shooting ourselves in the foot, because ActorDestroy takes
    841  // it.
    842  if (mIPCOpen) {
    843    Close();
    844  }
    845 
    846  MonitorAutoLock lock(mMonitor);
    847  mShutdownDone = true;
    848  mMonitor.Notify();
    849 }
    850 
    851 void HangMonitorParent::PaintWhileInterruptingJS(dom::BrowserParent* aTab) {
    852  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    853  if (StaticPrefs::browser_tabs_remote_force_paint()) {
    854    TabId id = aTab->GetTabId();
    855    Dispatch(NewNonOwningRunnableMethod<bool, TabId>(
    856        "HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread ",
    857        this,
    858        &HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread,
    859        true, id));
    860  }
    861 }
    862 
    863 void HangMonitorParent::UnloadLayersWhileInterruptingJS(
    864    dom::BrowserParent* aTab) {
    865  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    866  TabId id = aTab->GetTabId();
    867  Dispatch(NewNonOwningRunnableMethod<bool, TabId>(
    868      "HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread ",
    869      this, &HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread,
    870      false, id));
    871 }
    872 
    873 void HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread(
    874    const bool aPaint, TabId aTabId) {
    875  MOZ_RELEASE_ASSERT(IsOnThread());
    876 
    877  if (mIPCOpen) {
    878    if (aPaint) {
    879      (void)SendPaintWhileInterruptingJS(aTabId);
    880    } else {
    881      (void)SendUnloadLayersWhileInterruptingJS(aTabId);
    882    }
    883  }
    884 }
    885 
    886 void HangMonitorParent::CancelContentJSExecutionIfRunning(
    887    dom::BrowserParent* aBrowserParent,
    888    nsIRemoteTab::NavigationType aNavigationType,
    889    const dom::CancelContentJSOptions& aCancelContentJSOptions) {
    890  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    891 
    892  if (!aBrowserParent->CanCancelContentJS(aNavigationType,
    893                                          aCancelContentJSOptions.mIndex,
    894                                          aCancelContentJSOptions.mUri)) {
    895    return;
    896  }
    897 
    898  TabId id = aBrowserParent->GetTabId();
    899  Dispatch(NewNonOwningRunnableMethod<TabId, nsIRemoteTab::NavigationType,
    900                                      int32_t, nsIURI*, int32_t>(
    901      "HangMonitorParent::CancelContentJSExecutionIfRunningOnThread", this,
    902      &HangMonitorParent::CancelContentJSExecutionIfRunningOnThread, id,
    903      aNavigationType, aCancelContentJSOptions.mIndex,
    904      aCancelContentJSOptions.mUri, aCancelContentJSOptions.mEpoch));
    905 }
    906 
    907 void HangMonitorParent::CancelContentJSExecutionIfRunningOnThread(
    908    TabId aTabId, nsIRemoteTab::NavigationType aNavigationType,
    909    int32_t aNavigationIndex, nsIURI* aNavigationURI, int32_t aEpoch) {
    910  MOZ_RELEASE_ASSERT(IsOnThread());
    911 
    912  mozilla::Maybe<nsCString> spec;
    913  if (aNavigationURI) {
    914    nsAutoCString tmp;
    915    nsresult rv = aNavigationURI->GetSpec(tmp);
    916    if (NS_SUCCEEDED(rv)) {
    917      spec.emplace(tmp);
    918    }
    919  }
    920 
    921  if (mIPCOpen) {
    922    (void)SendCancelContentJSExecutionIfRunning(aTabId, aNavigationType,
    923                                                aNavigationIndex, spec, aEpoch);
    924  }
    925 }
    926 
    927 void HangMonitorParent::SetMainThreadQoSPriority(
    928    nsIThread::QoSPriority aQoSPriority) {
    929  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    930 #ifdef XP_MACOSX  // Should not be using outside of MacOS.
    931 
    932  Dispatch(NewNonOwningRunnableMethod<nsIThread::QoSPriority>(
    933      "HangMonitorParent::SetMainThreadQoSPriorityOnThread", this,
    934      &HangMonitorParent::SetMainThreadQoSPriorityOnThread, aQoSPriority));
    935 #endif
    936 }
    937 
    938 #ifdef XP_MACOSX
    939 void HangMonitorParent::SetMainThreadQoSPriorityOnThread(
    940    nsIThread::QoSPriority aQoSPriority) {
    941  MOZ_RELEASE_ASSERT(IsOnThread());
    942  if (mIPCOpen) {
    943    (void)SendSetMainThreadQoSPriority(aQoSPriority);
    944  }
    945 }
    946 #endif
    947 
    948 void HangMonitorParent::ActorDestroy(ActorDestroyReason aWhy) {
    949  MOZ_RELEASE_ASSERT(IsOnThread());
    950  mIPCOpen = false;
    951 }
    952 
    953 void HangMonitorParent::Bind(Endpoint<PProcessHangMonitorParent>&& aEndpoint) {
    954  MOZ_RELEASE_ASSERT(IsOnThread());
    955 
    956  DebugOnly<bool> ok = aEndpoint.Bind(this);
    957  MOZ_ASSERT(ok);
    958 }
    959 
    960 void HangMonitorParent::SendHangNotification(
    961    const SlowScriptData& aSlowScriptData, const nsString& aBrowserDumpId) {
    962  // chrome process, main thread
    963  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    964 
    965  nsString dumpId;
    966 
    967  // We already have a full minidump; go ahead and use it.
    968  dumpId = aBrowserDumpId;
    969 
    970  mProcess->SetSlowScriptData(aSlowScriptData, dumpId);
    971 
    972  nsCOMPtr<nsIObserverService> observerService =
    973      mozilla::services::GetObserverService();
    974  observerService->NotifyObservers(mProcess, "process-hang-report", nullptr);
    975 }
    976 
    977 void HangMonitorParent::ClearHangNotification() {
    978  // chrome process, main thread
    979  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    980 
    981  nsCOMPtr<nsIObserverService> observerService =
    982      mozilla::services::GetObserverService();
    983  observerService->NotifyObservers(mProcess, "clear-hang-report", nullptr);
    984 
    985  mProcess->ClearHang();
    986 }
    987 
    988 mozilla::ipc::IPCResult HangMonitorParent::RecvHangEvidence(
    989    const SlowScriptData& aSlowScriptData) {
    990  // chrome process, background thread
    991  MOZ_RELEASE_ASSERT(IsOnThread());
    992 
    993  if (!StaticPrefs::dom_ipc_reportProcessHangs()) {
    994    return IPC_OK();
    995  }
    996 
    997 #ifdef XP_WIN
    998  // Don't report hangs if we're debugging the process. You can comment this
    999  // line out for testing purposes.
   1000  if (IsDebuggerPresent()) {
   1001    return IPC_OK();
   1002  }
   1003 #endif
   1004 
   1005  // Before we wake up the browser main thread we want to take a
   1006  // browser minidump.
   1007  nsAutoString crashId;
   1008 
   1009  mHangMonitor->InitiateCPOWTimeout();
   1010 
   1011  MonitorAutoLock lock(mMonitor);
   1012 
   1013  NS_DispatchToMainThread(mMainThreadTaskFactory.NewRunnableMethod(
   1014      &HangMonitorParent::SendHangNotification, aSlowScriptData, crashId));
   1015 
   1016  return IPC_OK();
   1017 }
   1018 
   1019 mozilla::ipc::IPCResult HangMonitorParent::RecvClearHang() {
   1020  // chrome process, background thread
   1021  MOZ_RELEASE_ASSERT(IsOnThread());
   1022 
   1023  if (!StaticPrefs::dom_ipc_reportProcessHangs()) {
   1024    return IPC_OK();
   1025  }
   1026 
   1027  mHangMonitor->InitiateCPOWTimeout();
   1028 
   1029  MonitorAutoLock lock(mMonitor);
   1030 
   1031  NS_DispatchToMainThread(mMainThreadTaskFactory.NewRunnableMethod(
   1032      &HangMonitorParent::ClearHangNotification));
   1033 
   1034  return IPC_OK();
   1035 }
   1036 
   1037 void HangMonitorParent::TerminateScript() {
   1038  MOZ_RELEASE_ASSERT(IsOnThread());
   1039 
   1040  if (mIPCOpen) {
   1041    (void)SendTerminateScript();
   1042  }
   1043 }
   1044 
   1045 void HangMonitorParent::BeginStartingDebugger() {
   1046  MOZ_RELEASE_ASSERT(IsOnThread());
   1047 
   1048  if (mIPCOpen) {
   1049    (void)SendBeginStartingDebugger();
   1050  }
   1051 }
   1052 
   1053 void HangMonitorParent::EndStartingDebugger() {
   1054  MOZ_RELEASE_ASSERT(IsOnThread());
   1055 
   1056  if (mIPCOpen) {
   1057    (void)SendEndStartingDebugger();
   1058  }
   1059 }
   1060 
   1061 /* HangMonitoredProcess implementation */
   1062 
   1063 NS_IMPL_ISUPPORTS(HangMonitoredProcess, nsIHangReport)
   1064 
   1065 NS_IMETHODIMP
   1066 HangMonitoredProcess::GetHangDuration(double* aHangDuration) {
   1067  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1068  *aHangDuration = mSlowScriptData.duration();
   1069  return NS_OK;
   1070 }
   1071 
   1072 NS_IMETHODIMP
   1073 HangMonitoredProcess::GetScriptBrowser(Element** aBrowser) {
   1074  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1075  TabId tabId = mSlowScriptData.tabId();
   1076  if (!mContentParent) {
   1077    return NS_ERROR_NOT_AVAILABLE;
   1078  }
   1079 
   1080  nsTArray<PBrowserParent*> tabs;
   1081  mContentParent->ManagedPBrowserParent(tabs);
   1082  for (size_t i = 0; i < tabs.Length(); i++) {
   1083    BrowserParent* tp = BrowserParent::GetFrom(tabs[i]);
   1084    if (tp->GetTabId() == tabId) {
   1085      RefPtr<Element> node = tp->GetOwnerElement();
   1086      node.forget(aBrowser);
   1087      return NS_OK;
   1088    }
   1089  }
   1090 
   1091  *aBrowser = nullptr;
   1092  return NS_OK;
   1093 }
   1094 
   1095 NS_IMETHODIMP
   1096 HangMonitoredProcess::GetScriptFileName(nsACString& aFileName) {
   1097  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1098  aFileName = mSlowScriptData.filename();
   1099  return NS_OK;
   1100 }
   1101 
   1102 NS_IMETHODIMP
   1103 HangMonitoredProcess::GetAddonId(nsAString& aAddonId) {
   1104  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1105  aAddonId = mSlowScriptData.addonId();
   1106  return NS_OK;
   1107 }
   1108 
   1109 NS_IMETHODIMP
   1110 HangMonitoredProcess::TerminateScript() {
   1111  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1112  if (!mActor) {
   1113    return NS_ERROR_UNEXPECTED;
   1114  }
   1115 
   1116  ProcessHangMonitor::Get()->Dispatch(
   1117      NewNonOwningRunnableMethod("HangMonitorParent::TerminateScript", mActor,
   1118                                 &HangMonitorParent::TerminateScript));
   1119  return NS_OK;
   1120 }
   1121 
   1122 NS_IMETHODIMP
   1123 HangMonitoredProcess::BeginStartingDebugger() {
   1124  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1125  if (!mActor) {
   1126    return NS_ERROR_UNEXPECTED;
   1127  }
   1128 
   1129  ProcessHangMonitor::Get()->Dispatch(NewNonOwningRunnableMethod(
   1130      "HangMonitorParent::BeginStartingDebugger", mActor,
   1131      &HangMonitorParent::BeginStartingDebugger));
   1132  return NS_OK;
   1133 }
   1134 
   1135 NS_IMETHODIMP
   1136 HangMonitoredProcess::EndStartingDebugger() {
   1137  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1138  if (!mActor) {
   1139    return NS_ERROR_UNEXPECTED;
   1140  }
   1141 
   1142  ProcessHangMonitor::Get()->Dispatch(NewNonOwningRunnableMethod(
   1143      "HangMonitorParent::EndStartingDebugger", mActor,
   1144      &HangMonitorParent::EndStartingDebugger));
   1145  return NS_OK;
   1146 }
   1147 
   1148 NS_IMETHODIMP
   1149 HangMonitoredProcess::IsReportForBrowserOrChildren(nsFrameLoader* aFrameLoader,
   1150                                                   bool* aResult) {
   1151  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1152  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
   1153 
   1154  if (!mActor) {
   1155    *aResult = false;
   1156    return NS_OK;
   1157  }
   1158 
   1159  NS_ENSURE_STATE(aFrameLoader);
   1160 
   1161  AutoTArray<RefPtr<BrowsingContext>, 10> bcs;
   1162  bcs.AppendElement(aFrameLoader->GetExtantBrowsingContext());
   1163  while (!bcs.IsEmpty()) {
   1164    RefPtr<BrowsingContext> bc = bcs[bcs.Length() - 1];
   1165    bcs.RemoveLastElement();
   1166    if (!bc) {
   1167      continue;
   1168    }
   1169    if (mContentParent == bc->Canonical()->GetContentParent()) {
   1170      *aResult = true;
   1171      return NS_OK;
   1172    }
   1173    bc->GetChildren(bcs);
   1174  }
   1175 
   1176  *aResult = false;
   1177  return NS_OK;
   1178 }
   1179 
   1180 NS_IMETHODIMP
   1181 HangMonitoredProcess::UserCanceled() { return NS_OK; }
   1182 
   1183 NS_IMETHODIMP
   1184 HangMonitoredProcess::GetChildID(uint64_t* aChildID) {
   1185  if (!mContentParent) {
   1186    return NS_ERROR_NOT_AVAILABLE;
   1187  }
   1188  *aChildID = mContentParent->ChildID();
   1189  return NS_OK;
   1190 }
   1191 
   1192 static bool InterruptCallback(JSContext* cx) {
   1193  AssertIsOnMainThread();
   1194  if (HangMonitorChild* child = HangMonitorChild::Get()) {
   1195    return child->InterruptCallback();
   1196  }
   1197 
   1198  return true;
   1199 }
   1200 
   1201 ProcessHangMonitor* ProcessHangMonitor::sInstance;
   1202 
   1203 ProcessHangMonitor::ProcessHangMonitor() : mCPOWTimeout(false) {
   1204  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1205 
   1206  if (XRE_IsContentProcess()) {
   1207    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   1208    obs->AddObserver(this, "xpcom-shutdown", false);
   1209  }
   1210 
   1211  if (NS_FAILED(NS_NewNamedThread("ProcessHangMon", getter_AddRefs(mThread)))) {
   1212    mThread = nullptr;
   1213  }
   1214 #ifdef XP_MACOSX
   1215  // On MacOS, ensure the priority is high enough to handle dispatches at
   1216  // high cpu load. USER_INITIATED class threads are prioritized just below
   1217  // the main thread.
   1218  mThread->Dispatch(NS_NewRunnableFunction(
   1219      "ProcessHangMonitor::SetPriority",
   1220      [] { pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0); }));
   1221 #endif
   1222 }
   1223 
   1224 ProcessHangMonitor::~ProcessHangMonitor() {
   1225  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1226 
   1227  MOZ_ASSERT(sInstance == this);
   1228  sInstance = nullptr;
   1229 
   1230  mThread->Shutdown();
   1231  mThread = nullptr;
   1232 }
   1233 
   1234 ProcessHangMonitor* ProcessHangMonitor::GetOrCreate() {
   1235  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1236  if (!sInstance) {
   1237    sInstance = new ProcessHangMonitor();
   1238  }
   1239  return sInstance;
   1240 }
   1241 
   1242 NS_IMPL_ISUPPORTS(ProcessHangMonitor, nsIObserver)
   1243 
   1244 NS_IMETHODIMP
   1245 ProcessHangMonitor::Observe(nsISupports* aSubject, const char* aTopic,
   1246                            const char16_t* aData) {
   1247  ReleaseAssertIsOnMainThread();
   1248  if (!strcmp(aTopic, "xpcom-shutdown")) {
   1249    if (RefPtr<HangMonitorChild> child = HangMonitorChild::Get()) {
   1250      child->Shutdown();
   1251    }
   1252 
   1253    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   1254    obs->RemoveObserver(this, "xpcom-shutdown");
   1255  }
   1256  return NS_OK;
   1257 }
   1258 
   1259 ProcessHangMonitor::SlowScriptAction ProcessHangMonitor::NotifySlowScript(
   1260    nsIBrowserChild* aBrowserChild, const char* aFileName,
   1261    const nsString& aAddonId, const double aDuration) {
   1262  ReleaseAssertIsOnMainThread();
   1263  return HangMonitorChild::Get()->NotifySlowScript(aBrowserChild, aFileName,
   1264                                                   aAddonId, aDuration);
   1265 }
   1266 
   1267 bool ProcessHangMonitor::IsDebuggerStartupComplete() {
   1268  ReleaseAssertIsOnMainThread();
   1269  return HangMonitorChild::Get()->IsDebuggerStartupComplete();
   1270 }
   1271 
   1272 bool ProcessHangMonitor::ShouldTimeOutCPOWs() {
   1273  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1274 
   1275  if (mCPOWTimeout) {
   1276    mCPOWTimeout = false;
   1277    return true;
   1278  }
   1279  return false;
   1280 }
   1281 
   1282 void ProcessHangMonitor::InitiateCPOWTimeout() {
   1283  MOZ_RELEASE_ASSERT(IsOnThread());
   1284  mCPOWTimeout = true;
   1285 }
   1286 
   1287 static already_AddRefed<PProcessHangMonitorParent> CreateHangMonitorParent(
   1288    ContentParent* aContentParent,
   1289    Endpoint<PProcessHangMonitorParent>&& aEndpoint) {
   1290  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1291 
   1292  ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
   1293  RefPtr<HangMonitorParent> parent = new HangMonitorParent(monitor);
   1294 
   1295  auto* process = new HangMonitoredProcess(parent, aContentParent);
   1296  parent->SetProcess(process);
   1297 
   1298  monitor->Dispatch(
   1299      NewNonOwningRunnableMethod<Endpoint<PProcessHangMonitorParent>&&>(
   1300          "HangMonitorParent::Bind", parent, &HangMonitorParent::Bind,
   1301          std::move(aEndpoint)));
   1302 
   1303  return parent.forget();
   1304 }
   1305 
   1306 void mozilla::CreateHangMonitorChild(
   1307    Endpoint<PProcessHangMonitorChild>&& aEndpoint) {
   1308  ReleaseAssertIsOnMainThread();
   1309 
   1310  JSContext* cx = danger::GetJSContext();
   1311  JS_AddInterruptCallback(cx, InterruptCallback);
   1312 
   1313  ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
   1314  HangMonitorChild::CreateAndBind(monitor, std::move(aEndpoint));
   1315 }
   1316 
   1317 nsresult ProcessHangMonitor::Dispatch(already_AddRefed<nsIRunnable> aRunnable) {
   1318  return mThread->Dispatch(std::move(aRunnable),
   1319                           nsIEventTarget::NS_DISPATCH_NORMAL);
   1320 }
   1321 
   1322 bool ProcessHangMonitor::IsOnThread() {
   1323  bool on;
   1324  return NS_SUCCEEDED(mThread->IsOnCurrentThread(&on)) && on;
   1325 }
   1326 
   1327 /* static */
   1328 already_AddRefed<PProcessHangMonitorParent> ProcessHangMonitor::AddProcess(
   1329    ContentParent* aContentParent) {
   1330  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1331 
   1332  if (!StaticPrefs::dom_ipc_processHangMonitor_AtStartup()) {
   1333    return nullptr;
   1334  }
   1335 
   1336  Endpoint<PProcessHangMonitorParent> parent;
   1337  Endpoint<PProcessHangMonitorChild> child;
   1338  nsresult rv;
   1339  rv = PProcessHangMonitor::CreateEndpoints(&parent, &child);
   1340  if (NS_FAILED(rv)) {
   1341    MOZ_ASSERT(false, "PProcessHangMonitor::CreateEndpoints failed");
   1342    return nullptr;
   1343  }
   1344 
   1345  if (!aContentParent->SendInitProcessHangMonitor(std::move(child))) {
   1346    MOZ_ASSERT(false);
   1347    return nullptr;
   1348  }
   1349 
   1350  return CreateHangMonitorParent(aContentParent, std::move(parent));
   1351 }
   1352 
   1353 /* static */
   1354 void ProcessHangMonitor::RemoveProcess(PProcessHangMonitorParent* aParent) {
   1355  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1356  auto parent = static_cast<HangMonitorParent*>(aParent);
   1357  parent->Shutdown();
   1358 }
   1359 
   1360 /* static */
   1361 void ProcessHangMonitor::ClearHang() {
   1362  AssertIsOnMainThread();
   1363  if (HangMonitorChild* child = HangMonitorChild::Get()) {
   1364    child->ClearHang();
   1365  }
   1366 }
   1367 
   1368 /* static */
   1369 void ProcessHangMonitor::PaintWhileInterruptingJS(
   1370    PProcessHangMonitorParent* aParent, dom::BrowserParent* aTab) {
   1371  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1372  auto* parent = static_cast<HangMonitorParent*>(aParent);
   1373  parent->PaintWhileInterruptingJS(aTab);
   1374 }
   1375 
   1376 /* static */
   1377 void ProcessHangMonitor::UnloadLayersWhileInterruptingJS(
   1378    PProcessHangMonitorParent* aParent, dom::BrowserParent* aTab) {
   1379  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   1380  auto* parent = static_cast<HangMonitorParent*>(aParent);
   1381  parent->UnloadLayersWhileInterruptingJS(aTab);
   1382 }
   1383 
   1384 /* static */
   1385 void ProcessHangMonitor::ClearPaintWhileInterruptingJS() {
   1386  ReleaseAssertIsOnMainThread();
   1387  MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
   1388 
   1389  if (HangMonitorChild* child = HangMonitorChild::Get()) {
   1390    child->ClearPaintWhileInterruptingJS();
   1391  }
   1392 }
   1393 
   1394 /* static */
   1395 void ProcessHangMonitor::MaybeStartPaintWhileInterruptingJS() {
   1396  ReleaseAssertIsOnMainThread();
   1397  MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
   1398 
   1399  if (HangMonitorChild* child = HangMonitorChild::Get()) {
   1400    child->MaybeStartPaintWhileInterruptingJS();
   1401  }
   1402 }
   1403 
   1404 /* static */
   1405 void ProcessHangMonitor::CancelContentJSExecutionIfRunning(
   1406    PProcessHangMonitorParent* aParent, dom::BrowserParent* aBrowserParent,
   1407    nsIRemoteTab::NavigationType aNavigationType,
   1408    const dom::CancelContentJSOptions& aCancelContentJSOptions) {
   1409  ReleaseAssertIsOnMainThread();
   1410  auto* parent = static_cast<HangMonitorParent*>(aParent);
   1411  parent->CancelContentJSExecutionIfRunning(aBrowserParent, aNavigationType,
   1412                                            aCancelContentJSOptions);
   1413 }
   1414 
   1415 /* static */
   1416 void ProcessHangMonitor::SetMainThreadQoSPriority(
   1417    PProcessHangMonitorParent* aParent, nsIThread::QoSPriority aQoSPriority) {
   1418  ReleaseAssertIsOnMainThread();
   1419  auto* parent = static_cast<HangMonitorParent*>(aParent);
   1420  parent->SetMainThreadQoSPriority(aQoSPriority);
   1421 }