Backed out changeset da12c077747f (bug 1448040) for Android build bustage on build/src/obj-firefox/dist/include/mozilla/HangAnnotations.h. CLOSED TREE
authorDorel Luca <dluca@mozilla.com>
Fri, 25 May 2018 20:13:26 +0300
changeset 476632 ff2ef4fe0c6c907e7692c1e92bd1601e2295c468
parent 476631 71dbe00b841a50fb59e89322b5b78376550fccc8
child 476633 65a6064fb267c8665d6a969cad7615390c6a9c29
push id1757
push userffxbld-merge
push dateFri, 24 Aug 2018 17:02:43 +0000
treeherdermozilla-release@736023aebdb1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1448040
milestone62.0a1
backs outda12c077747fdd6e168795cbc60d0a6dad7ef644
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset da12c077747f (bug 1448040) for Android build bustage on build/src/obj-firefox/dist/include/mozilla/HangAnnotations.h. CLOSED TREE
dom/base/nsContentUtils.cpp
dom/ipc/ContentChild.cpp
dom/plugins/ipc/PluginModuleParent.cpp
dom/plugins/ipc/PluginModuleParent.h
ipc/mscom/MainThreadInvoker.cpp
js/src/tests/user.js
modules/libpref/init/all.js
testing/marionette/client/marionette_driver/geckoinstance.py
testing/marionette/components/marionette.js
toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.h
toolkit/components/backgroundhangmonitor/HangAnnotations.cpp
toolkit/components/backgroundhangmonitor/HangAnnotations.h
toolkit/components/backgroundhangmonitor/moz.build
toolkit/components/telemetry/CombinedStacks.cpp
toolkit/components/telemetry/HangReports.cpp
toolkit/components/telemetry/HangReports.h
toolkit/components/telemetry/Telemetry.cpp
toolkit/components/telemetry/Telemetry.h
toolkit/components/telemetry/TelemetrySession.jsm
toolkit/components/telemetry/docs/data/main-ping.rst
toolkit/components/telemetry/nsITelemetry.idl
toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
toolkit/content/aboutTelemetry.js
toolkit/content/aboutTelemetry.xhtml
toolkit/crashreporter/docs/index.rst
toolkit/locales/en-US/chrome/global/aboutTelemetry.dtd
toolkit/locales/en-US/chrome/global/aboutTelemetry.properties
widget/android/nsAppShell.cpp
widget/android/nsAppShell.h
widget/cocoa/nsAppShell.mm
widget/gtk/nsAppShell.cpp
widget/windows/WinUtils.cpp
widget/windows/nsAppShell.cpp
widget/windows/nsWindow.cpp
xpcom/build/XPCOMInit.cpp
xpcom/threads/CPUUsageWatcher.cpp
xpcom/threads/CPUUsageWatcher.h
xpcom/threads/HangAnnotations.cpp
xpcom/threads/HangAnnotations.h
xpcom/threads/HangMonitor.cpp
xpcom/threads/HangMonitor.h
xpcom/threads/moz.build
xpcom/threads/nsThread.cpp
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -28,17 +28,16 @@
 #include "gfxDrawable.h"
 #include "gfxPrefs.h"
 #include "ImageOps.h"
 #include "mozAutoDocUpdate.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/AutoTimelineMarker.h"
-#include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/Base64.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/CustomElementRegistry.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/DOMException.h"
@@ -559,25 +558,25 @@ NS_IMPL_ISUPPORTS(nsContentUtils::nsCont
  * interacting with the browser. It listens to observer events to toggle the
  * value of the sUserActive static.
  *
  * This class is an internal implementation detail.
  * nsContentUtils::GetUserIsInteracting() should be used to access current
  * user interaction status.
  */
 class nsContentUtils::UserInteractionObserver final : public nsIObserver
-                                                    , public BackgroundHangAnnotator
+                                                    , public HangMonitor::Annotator
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   void Init();
   void Shutdown();
-  void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override;
+  void AnnotateHang(HangMonitor::HangAnnotations& aAnnotations) override;
 
   static Atomic<bool> sUserActive;
 
 private:
   ~UserInteractionObserver() {}
 };
 
 /* static */
@@ -10786,43 +10785,43 @@ void
 nsContentUtils::UserInteractionObserver::Init()
 {
   // Listen for the observer messages from EventStateManager which are telling
   // us whether or not the user is interacting.
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   obs->AddObserver(this, kUserInteractionInactive, false);
   obs->AddObserver(this, kUserInteractionActive, false);
 
-  // We can't register ourselves as an annotator yet, as the
-  // BackgroundHangMonitor hasn't started yet. It will have started by the
-  // time we have the chance to spin the event loop.
+  // We can't register ourselves as an annotator yet, as the HangMonitor hasn't
+  // started yet. It will have started by the time we have the chance to spin
+  // the event loop.
   RefPtr<UserInteractionObserver> self = this;
   NS_DispatchToMainThread(
     NS_NewRunnableFunction("nsContentUtils::UserInteractionObserver::Init",
-                           [=]() { BackgroundHangMonitor::RegisterAnnotator(*self); }));
+                           [=]() { HangMonitor::RegisterAnnotator(*self); }));
 }
 
 void
 nsContentUtils::UserInteractionObserver::Shutdown()
 {
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->RemoveObserver(this, kUserInteractionInactive);
     obs->RemoveObserver(this, kUserInteractionActive);
   }
 
-  BackgroundHangMonitor::UnregisterAnnotator(*this);
+  HangMonitor::UnregisterAnnotator(*this);
 }
 
 /**
- * NB: This function is always called by the BackgroundHangMonitor thread.
+ * NB: This function is always called by the HangMonitor thread.
  *     Plan accordingly
  */
 void
-nsContentUtils::UserInteractionObserver::AnnotateHang(BackgroundHangAnnotations& aAnnotations)
+nsContentUtils::UserInteractionObserver::AnnotateHang(HangMonitor::HangAnnotations& aAnnotations)
 {
   // NOTE: Only annotate the hang report if the user is known to be interacting.
   if (sUserActive) {
     aAnnotations.AddAnnotation(NS_LITERAL_STRING("UserInteracting"), true);
   }
 }
 
 NS_IMETHODIMP
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -10,17 +10,16 @@
 
 #include "ContentChild.h"
 
 #include "GeckoProfiler.h"
 #include "TabChild.h"
 #include "HandlerServiceChild.h"
 
 #include "mozilla/Attributes.h"
-#include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
 #include "mozilla/Unused.h"
 #include "mozilla/TelemetryIPC.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
 #include "mozilla/docshell/OfflineCacheUpdateChild.h"
 #include "mozilla/dom/ClientManager.h"
@@ -505,25 +504,25 @@ ConsoleListener::Observe(nsIConsoleMessa
   nsresult rv = aMessage->GetMessageMoz(getter_Copies(msg));
   NS_ENSURE_SUCCESS(rv, rv);
   mChild->SendConsoleMessage(msg);
   return NS_OK;
 }
 
 #ifdef NIGHTLY_BUILD
 /**
- * The singleton of this class is registered with the BackgroundHangMonitor as an
+ * The singleton of this class is registered with the HangMonitor as an
  * annotator, so that the hang monitor can record whether or not there were
  * pending input events when the thread hung.
  */
 class PendingInputEventHangAnnotator final
-  : public BackgroundHangAnnotator
+  : public HangMonitor::Annotator
 {
 public:
-  virtual void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override
+  virtual void AnnotateHang(HangMonitor::HangAnnotations& aAnnotations) override
   {
     int32_t pending = ContentChild::GetSingleton()->GetPendingInputEvents();
     if (pending > 0) {
       aAnnotations.AddAnnotation(NS_LITERAL_STRING("PendingInput"), pending);
     }
   }
 
   static PendingInputEventHangAnnotator sSingleton;
@@ -707,17 +706,17 @@ ContentChild::Init(MessageLoop* aIOLoop,
 
   SetProcessName(NS_LITERAL_STRING("Web Content"));
 
 #ifdef NIGHTLY_BUILD
   // NOTE: We have to register the annotator on the main thread, as annotators
   // only affect a single thread.
   SystemGroup::Dispatch(TaskCategory::Other,
                         NS_NewRunnableFunction("RegisterPendingInputEventHangAnnotator", [] {
-                          BackgroundHangMonitor::RegisterAnnotator(
+                          HangMonitor::RegisterAnnotator(
                             PendingInputEventHangAnnotator::sSingleton);
                         }));
 #endif
 
   return true;
 }
 
 void
@@ -3036,17 +3035,17 @@ ContentChild::ShutdownInternal()
         &ContentChild::ShutdownInternal),
       100);
     return;
   }
 
   mShuttingDown = true;
 
 #ifdef NIGHTLY_BUILD
-  BackgroundHangMonitor::UnregisterAnnotator(PendingInputEventHangAnnotator::sSingleton);
+  HangMonitor::UnregisterAnnotator(PendingInputEventHangAnnotator::sSingleton);
 #endif
 
   if (mPolicy) {
     mPolicy->Deactivate();
     mPolicy = nullptr;
   }
 
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -4,17 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/plugins/PluginModuleParent.h"
 
 #include "base/process_util.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
-#include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/ipc/CrashReporterClient.h"
 #include "mozilla/ipc/CrashReporterHost.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/ipc/ProtocolUtils.h"
@@ -652,17 +651,17 @@ PluginModuleChromeParent::PluginModuleCh
     , mFinishInitTask(nullptr)
 #endif
     , mIsCleaningFromTimeout(false)
 {
     NS_ASSERTION(mSubprocess, "Out of memory!");
     mSandboxLevel = aSandboxLevel;
     mRunID = GeckoChildProcessHost::GetUniqueID();
 
-    mozilla::BackgroundHangMonitor::RegisterAnnotator(*this);
+    mozilla::HangMonitor::RegisterAnnotator(*this);
 }
 
 PluginModuleChromeParent::~PluginModuleChromeParent()
 {
     if (!OkToCleanup()) {
         MOZ_CRASH("unsafe destruction");
     }
 
@@ -705,17 +704,17 @@ PluginModuleChromeParent::~PluginModuleC
     Preferences::UnregisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
 
     if (mHangUIParent) {
         delete mHangUIParent;
         mHangUIParent = nullptr;
     }
 #endif
 
-    mozilla::BackgroundHangMonitor::UnregisterAnnotator(*this);
+    mozilla::HangMonitor::UnregisterAnnotator(*this);
 }
 
 void
 PluginModuleChromeParent::WriteExtraDataForMinidump()
 {
     // mCrashReporterMutex is already held by the caller
     mCrashReporterMutex.AssertCurrentThreadOwns();
 
@@ -984,26 +983,26 @@ PluginModuleChromeParent::ExitedCxxStack
 {
     mHangAnnotationFlags = 0;
 #ifdef XP_WIN
     FinishHangUI();
 #endif
 }
 
 /**
- * This function is always called by the BackgroundHangMonitor thread.
+ * This function is always called by the HangMonitor thread.
  */
 void
-PluginModuleChromeParent::AnnotateHang(mozilla::BackgroundHangAnnotations& aAnnotations)
+PluginModuleChromeParent::AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations)
 {
     uint32_t flags = mHangAnnotationFlags;
     if (flags) {
         /* We don't actually annotate anything specifically for kInPluginCall;
            we use it to determine whether to annotate other things. It will
-           be pretty obvious from the hang stack that we're in a plugin
+           be pretty obvious from the ChromeHang stack that we're in a plugin
            call when the hang occurred. */
         if (flags & kHangUIShown) {
             aAnnotations.AddAnnotation(NS_LITERAL_STRING("HangUIShown"),
                                        true);
         }
         if (flags & kHangUIContinued) {
             aAnnotations.AddAnnotation(NS_LITERAL_STRING("HangUIContinued"),
                                        true);
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -348,17 +348,17 @@ class PluginModuleContentParent : public
 
     static PluginModuleContentParent* sSavedModuleParent;
 
     uint32_t mPluginId;
 };
 
 class PluginModuleChromeParent
     : public PluginModuleParent
-    , public mozilla::BackgroundHangAnnotator
+    , public mozilla::HangMonitor::Annotator
 {
     friend class mozilla::ipc::CrashReporterHost;
     using TerminateChildProcessCallback =
         mozilla::ipc::CrashReporterHost::CallbackWrapper<bool>;
     using TakeFullMinidumpCallback =
         mozilla::ipc::CrashReporterHost::CallbackWrapper<nsString>;
   public:
     /**
@@ -472,17 +472,17 @@ private:
 
     void
     ExitedCxxStack() override;
 
     mozilla::ipc::IProtocol* GetInvokingProtocol();
     PluginInstanceParent* GetManagingInstance(mozilla::ipc::IProtocol* aProtocol);
 
     virtual void
-    AnnotateHang(mozilla::BackgroundHangAnnotations& aAnnotations) override;
+    AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) override;
 
     virtual bool ShouldContinueFromReplyTimeout() override;
 
     void ProcessFirstMinidump();
     void WriteExtraDataForMinidump();
     void RetainPluginRef();
     void ReleasePluginRef();
 
--- a/ipc/mscom/MainThreadInvoker.cpp
+++ b/ipc/mscom/MainThreadInvoker.cpp
@@ -4,19 +4,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/mscom/MainThreadInvoker.h"
 
 #include "GeckoProfiler.h"
 #include "MainThreadUtils.h"
 #include "mozilla/Assertions.h"
-#include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/HangMonitor.h"
 #include "mozilla/mscom/SpinEvent.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/SystemGroup.h"
 #include "private/prpriv.h" // For PR_GetThreadID
 #include <winternl.h> // For NTSTATUS and NTAPI
 
 namespace {
 
@@ -176,17 +176,17 @@ MainThreadInvoker::Invoke(already_AddRef
   mDuration = syncRunnable->GetDuration();
   return result;
 }
 
 /* static */ VOID CALLBACK
 MainThreadInvoker::MainThreadAPC(ULONG_PTR aParam)
 {
   AUTO_PROFILER_THREAD_WAKE;
-  mozilla::BackgroundHangMonitor().NotifyActivity();
+  mozilla::HangMonitor::NotifyActivity(mozilla::HangMonitor::kGeneralActivity);
   MOZ_ASSERT(NS_IsMainThread());
   auto runnable = reinterpret_cast<SyncRunnable*>(aParam);
   runnable->APCRun();
   NS_RELEASE(runnable);
 }
 
 } // namespace mscom
 } // namespace mozilla
--- a/js/src/tests/user.js
+++ b/js/src/tests/user.js
@@ -2,16 +2,17 @@ user_pref("app.update.enabled", false);
 user_pref("browser.dom.window.dump.enabled", true);
 user_pref("browser.sessionstore.resume_from_crash", false);
 user_pref("browser.shell.checkDefaultBrowser", false);
 user_pref("browser.xul.error_pages.enabled", true);
 user_pref("security.fileuri.strict_origin_policy", false);
 user_pref("dom.allow_scripts_to_close_windows", true);
 user_pref("dom.disable_open_during_load", false);
 user_pref("dom.max_script_run_time", 0);
+user_pref("hangmonitor.timeout", 0);
 user_pref("dom.max_chrome_script_run_time", 0);
 user_pref("javascript.allow.mailnews", true);
 user_pref("javascript.options.showInConsole", true);
 user_pref("layout.css.report_errors", true);
 user_pref("browser.warnOnQuit", false);
 user_pref("browser.cache.check_doc_frequency", 1);
 user_pref("extensions.checkCompatibility", false);
 user_pref("extensions.checkUpdateSecurity", false);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -3089,16 +3089,22 @@ pref("input_event_queue.duration.min", 1
 // The default amount of time (milliseconds) required for handling a input
 // event.
 pref("input_event_queue.default_duration_per_event", 1);
 
 // The number of processed input events we use to predict the amount of time
 // required to process the following input events.
 pref("input_event_queue.count_for_prediction", 9);
 
+// Hang monitor timeout after which we kill the browser, in seconds
+// (0 is disabled)
+// Disabled on all platforms per bug 705748 until the found issues are
+// resolved.
+pref("hangmonitor.timeout", 0);
+
 pref("plugins.load_appdir_plugins", false);
 // If true, plugins will be click to play
 pref("plugins.click_to_play", false);
 
 // This only supports one hidden ctp plugin, edit nsPluginArray.cpp if adding a second
 pref("plugins.navigator.hidden_ctp_plugin", "");
 
 // The default value for nsIPluginTag.enabledState (STATE_ENABLED = 2)
--- a/testing/marionette/client/marionette_driver/geckoinstance.py
+++ b/testing/marionette/client/marionette_driver/geckoinstance.py
@@ -85,16 +85,19 @@ class GeckoInstance(object):
         "general.useragent.updates.enabled": False,
 
         # Always use network provider for geolocation tests
         # so we bypass the OSX dialog raised by the corelocation provider
         "geo.provider.testing": True,
         # Do not scan Wifi
         "geo.wifi.scan": False,
 
+        # No hang monitor
+        "hangmonitor.timeout": 0,
+
         "javascript.options.showInConsole": True,
 
         # Enable Marionette component
         "marionette.enabled": True,
         # (deprecated and can be removed when Firefox 60 ships)
         "marionette.defaultPrefs.enabled": True,
 
         # Disable recommended automation prefs in CI
--- a/testing/marionette/components/marionette.js
+++ b/testing/marionette/components/marionette.js
@@ -218,16 +218,19 @@ const RECOMMENDED_PREFS = new Map([
 
   // Always use network provider for geolocation tests so we bypass the
   // macOS dialog raised by the corelocation provider
   ["geo.provider.testing", true],
 
   // Do not scan Wifi
   ["geo.wifi.scan", false],
 
+  // No hang monitor
+  ["hangmonitor.timeout", 0],
+
   // Show chrome errors and warnings in the error console
   ["javascript.options.showInConsole", true],
 
   // Do not prompt for temporary redirects
   ["network.http.prompt-temp-redirect", false],
 
   // Disable speculative connections so they are not reported as leaking
   // when they are hanging around
--- a/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
+++ b/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
@@ -197,19 +197,19 @@ public:
   BackgroundHangMonitor::ThreadType mThreadType;
 #ifdef MOZ_GECKO_PROFILER
   // Platform-specific helper to get hang stacks
   ThreadStackHelper mStackHelper;
 #endif
   // Stack of current hang
   HangStack mHangStack;
   // Annotations for the current hang
-  BackgroundHangAnnotations mAnnotations;
+  HangMonitor::HangAnnotations mAnnotations;
   // Annotators registered for this thread
-  BackgroundHangAnnotators mAnnotators;
+  HangMonitor::Observer::Annotators mAnnotators;
   // The name of the runnable which is hanging the current process
   nsCString mRunnableName;
   // The name of the thread which is being monitored
   nsCString mThreadName;
 
   BackgroundHangThread(const char* aName,
                        uint32_t aTimeoutMs,
                        uint32_t aMaxTimeoutMs,
@@ -483,24 +483,30 @@ BackgroundHangThread::~BackgroundHangThr
 }
 
 void
 BackgroundHangThread::ReportHang(TimeDuration aHangTime)
 {
   // Recovered from a hang; called on the monitor thread
   // mManager->mLock IS locked
 
+  nsTArray<HangAnnotation> annotations;
+  for (auto& annotation : mAnnotations) {
+    HangAnnotation annot(annotation.mName, annotation.mValue);
+    annotations.AppendElement(mozilla::Move(annot));
+  }
+
   HangDetails hangDetails(
     aHangTime,
     nsDependentCString(XRE_ChildProcessTypeToString(XRE_GetProcessType())),
     VoidString(),
     mThreadName,
     mRunnableName,
     Move(mHangStack),
-    Move(mAnnotations)
+    Move(annotations)
   );
 
   // If the hang processing thread exists, we can process the native stack
   // on it. Otherwise, we are unable to report a native stack, so we just
   // report without one.
   if (mManager->mHangProcessingThread) {
     nsCOMPtr<nsIRunnable> processHangStackRunnable =
       new ProcessHangStackRunnable(Move(hangDetails));
@@ -756,31 +762,31 @@ BackgroundHangMonitor::NotifyWait()
 
   if (Telemetry::CanRecordExtended()) {
     mThread->NotifyWait();
   }
 #endif
 }
 
 bool
-BackgroundHangMonitor::RegisterAnnotator(BackgroundHangAnnotator& aAnnotator)
+BackgroundHangMonitor::RegisterAnnotator(HangMonitor::Annotator& aAnnotator)
 {
 #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
   BackgroundHangThread* thisThread = BackgroundHangThread::FindThread();
   if (!thisThread) {
     return false;
   }
   return thisThread->mAnnotators.Register(aAnnotator);
 #else
   return false;
 #endif
 }
 
 bool
-BackgroundHangMonitor::UnregisterAnnotator(BackgroundHangAnnotator& aAnnotator)
+BackgroundHangMonitor::UnregisterAnnotator(HangMonitor::Annotator& aAnnotator)
 {
 #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
   BackgroundHangThread* thisThread = BackgroundHangThread::FindThread();
   if (!thisThread) {
     return false;
   }
   return thisThread->mAnnotators.Unregister(aAnnotator);
 #else
--- a/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.h
+++ b/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.h
@@ -184,22 +184,22 @@ public:
    */
   void NotifyWait();
 
   /**
    * Register an annotator with BHR for the current thread.
    * @param aAnnotator annotator to register
    * @return true if the annotator was registered, otherwise false.
    */
-  static bool RegisterAnnotator(BackgroundHangAnnotator& aAnnotator);
+  static bool RegisterAnnotator(HangMonitor::Annotator& aAnnotator);
 
   /**
    * Unregister an annotator that was previously registered via
    * RegisterAnnotator.
    * @param aAnnotator annotator to unregister
    * @return true if there are still remaining annotators registered
    */
-  static bool UnregisterAnnotator(BackgroundHangAnnotator& aAnnotator);
+  static bool UnregisterAnnotator(HangMonitor::Annotator& aAnnotator);
 };
 
 } // namespace mozilla
 
 #endif // mozilla_BackgroundHangMonitor_h
deleted file mode 100644
--- a/toolkit/components/backgroundhangmonitor/HangAnnotations.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/HangAnnotations.h"
-
-#include <vector>
-
-#include "MainThreadUtils.h"
-#include "mozilla/DebugOnly.h"
-#include "nsXULAppAPI.h"
-#include "mozilla/BackgroundHangMonitor.h"
-
-namespace mozilla {
-
-void
-BackgroundHangAnnotations::AddAnnotation(const nsString& aName, const int32_t aData)
-{
-  nsAutoString dataString;
-  dataString.AppendInt(aData);
-  AppendElement(HangAnnotation(aName, dataString));
-}
-
-void
-BackgroundHangAnnotations::AddAnnotation(const nsString& aName, const double aData)
-{
-  nsAutoString dataString;
-  dataString.AppendFloat(aData);
-  AppendElement(HangAnnotation(aName, dataString));
-}
-
-void
-BackgroundHangAnnotations::AddAnnotation(const nsString& aName, const nsString& aData)
-{
-  AppendElement(HangAnnotation(aName, aData));
-}
-
-void
-BackgroundHangAnnotations::AddAnnotation(const nsString& aName, const nsCString& aData)
-{
-  NS_ConvertUTF8toUTF16 dataString(aData);
-  AppendElement(HangAnnotation(aName, dataString));
-}
-
-void
-BackgroundHangAnnotations::AddAnnotation(const nsString& aName, const bool aData)
-{
-  if (aData) {
-    AppendElement(HangAnnotation(aName, NS_LITERAL_STRING("true")));
-  } else {
-    AppendElement(HangAnnotation(aName, NS_LITERAL_STRING("false")));
-  }
-}
-
-BackgroundHangAnnotators::BackgroundHangAnnotators()
-  : mMutex("BackgroundHangAnnotators::mMutex")
-{
-  MOZ_COUNT_CTOR(BackgroundHangAnnotators);
-}
-
-BackgroundHangAnnotators::~BackgroundHangAnnotators()
-{
-  MOZ_ASSERT(mAnnotators.empty());
-  MOZ_COUNT_DTOR(BackgroundHangAnnotators);
-}
-
-bool
-BackgroundHangAnnotators::Register(BackgroundHangAnnotator& aAnnotator)
-{
-  MutexAutoLock lock(mMutex);
-  auto result = mAnnotators.insert(&aAnnotator);
-  return result.second;
-}
-
-bool
-BackgroundHangAnnotators::Unregister(BackgroundHangAnnotator& aAnnotator)
-{
-  MutexAutoLock lock(mMutex);
-  DebugOnly<std::set<BackgroundHangAnnotator*>::size_type> numErased;
-  numErased = mAnnotators.erase(&aAnnotator);
-  MOZ_ASSERT(numErased == 1);
-  return mAnnotators.empty();
-}
-
-BackgroundHangAnnotations
-BackgroundHangAnnotators::GatherAnnotations()
-{
-  BackgroundHangAnnotations annotations;
-  { // Scope for lock
-    MutexAutoLock lock(mMutex);
-    for (std::set<BackgroundHangAnnotator*>::iterator i = mAnnotators.begin(),
-         e = mAnnotators.end();
-         i != e; ++i) {
-      (*i)->AnnotateHang(annotations);
-    }
-  }
-  return annotations;
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/toolkit/components/backgroundhangmonitor/HangAnnotations.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_HangAnnotations_h
-#define mozilla_HangAnnotations_h
-
-#include <set>
-
-#include "mozilla/HangTypes.h"
-#include "mozilla/MemoryReporting.h"
-#include "mozilla/Mutex.h"
-#include "mozilla/Vector.h"
-#include "nsString.h"
-#include "nsTArray.h"
-#include "mozilla/ipc/IPDLParamTraits.h"
-
-namespace mozilla {
-
-/**
- * This class extends nsTArray<HangAnnotation> with some methods for adding
- * annotations being reported by a registered hang Annotator.
- */
-class BackgroundHangAnnotations : public nsTArray<HangAnnotation>
-{
-public:
-  void AddAnnotation(const nsString& aName, const int32_t aData);
-  void AddAnnotation(const nsString& aName, const double aData);
-  void AddAnnotation(const nsString& aName, const nsString& aData);
-  void AddAnnotation(const nsString& aName, const nsCString& aData);
-  void AddAnnotation(const nsString& aName, const bool aData);
-};
-
-class BackgroundHangAnnotator
-{
-public:
-  /**
-   * NB: This function is always called by the BackgroundHangMonitor thread.
-   *     Plan accordingly.
-   */
-  virtual void AnnotateHang(BackgroundHangAnnotations& aAnnotations) = 0;
-};
-
-class BackgroundHangAnnotators
-{
-public:
-  BackgroundHangAnnotators();
-  ~BackgroundHangAnnotators();
-
-  bool Register(BackgroundHangAnnotator& aAnnotator);
-  bool Unregister(BackgroundHangAnnotator& aAnnotator);
-
-  BackgroundHangAnnotations GatherAnnotations();
-
-private:
-  Mutex mMutex;
-  std::set<BackgroundHangAnnotator*> mAnnotators;
-};
-
-namespace ipc {
-
-template<>
-struct IPDLParamTraits<mozilla::BackgroundHangAnnotations>
-  : public IPDLParamTraits<nsTArray<mozilla::HangAnnotation>>
-{
-  typedef mozilla::BackgroundHangAnnotations paramType;
-};
-
-} // namespace ipc
-
-} // namespace mozilla
-
-#endif // mozilla_HangAnnotations_h
--- a/toolkit/components/backgroundhangmonitor/moz.build
+++ b/toolkit/components/backgroundhangmonitor/moz.build
@@ -26,23 +26,21 @@ if CONFIG['NIGHTLY_BUILD'] and \
 XPIDL_SOURCES += [
     'nsIHangDetails.idl',
 ]
 
 XPIDL_MODULE = 'backgroundhangmonitor'
 
 EXPORTS.mozilla += [
     'BackgroundHangMonitor.h',
-    'HangAnnotations.h',
     'HangDetails.h',
 ]
 
 UNIFIED_SOURCES += [
     'BackgroundHangMonitor.cpp',
-    'HangAnnotations.cpp',
     'HangDetails.cpp',
 ]
 
 IPDL_SOURCES += [
     'HangTypes.ipdlh',
 ]
 
 if CONFIG['MOZ_GECKO_PROFILER']:
--- a/toolkit/components/telemetry/CombinedStacks.cpp
+++ b/toolkit/components/telemetry/CombinedStacks.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CombinedStacks.h"
+#include "HangAnnotations.h"
 #include "mozilla/HangAnnotations.h"
 #include "jsapi.h"
 
 namespace mozilla {
 namespace Telemetry {
 
 // The maximum number of chrome hangs stacks that we're keeping.
 const size_t kMaxChromeStacksKept = 50;
--- a/toolkit/components/telemetry/HangReports.cpp
+++ b/toolkit/components/telemetry/HangReports.cpp
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "HangReports.h"
+
+namespace mozilla {
+namespace Telemetry {
+
+using namespace HangMonitor;
+
+// This utility function generates a string key that is used to index the annotations
+// in a hash map from |HangReports::AddHang|.
+nsresult
+ComputeAnnotationsKey(const HangAnnotations& aAnnotations, nsAString& aKeyOut)
+{
+  if (aAnnotations.IsEmpty()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  for (auto& annotation : aAnnotations) {
+    aKeyOut.Append(annotation.mName);
+    aKeyOut.Append(annotation.mValue);
+  }
+  return NS_OK;
+}
+
+#if defined(MOZ_GECKO_PROFILER)
+/** The maximum number of stacks that we're keeping for hang reports. */
+const size_t kMaxHangStacksKept = 50;
+
+void
+HangReports::AddHang(const Telemetry::ProcessedStack& aStack,
+                     uint32_t aDuration,
+                     int32_t aSystemUptime,
+                     int32_t aFirefoxUptime,
+                     HangAnnotations&& aAnnotations) {
+  // Append the new stack to the stack's circular queue.
+  size_t hangIndex = mStacks.AddStack(aStack);
+  // Append the hang info at the same index, in mHangInfo.
+  HangInfo info = { aDuration, aSystemUptime, aFirefoxUptime };
+  if (mHangInfo.size() < kMaxHangStacksKept) {
+    mHangInfo.push_back(info);
+  } else {
+    mHangInfo[hangIndex] = info;
+    // Remove any reference to the stack overwritten in the circular queue
+    // from the annotations.
+    PruneStackReferences(hangIndex);
+  }
+
+  nsAutoString annotationsKey;
+  // Generate a key to index aAnnotations in the hash map.
+  nsresult rv = ComputeAnnotationsKey(aAnnotations, annotationsKey);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  AnnotationInfo* annotationsEntry = mAnnotationInfo.Get(annotationsKey);
+  if (annotationsEntry) {
+    // If the key is already in the hash map, append the index of the chrome hang
+    // to its indices.
+    annotationsEntry->mHangIndices.AppendElement(hangIndex);
+    return;
+  }
+
+  // If the key was not found, add the annotations to the hash map.
+  mAnnotationInfo.Put(annotationsKey, new AnnotationInfo(hangIndex, Move(aAnnotations)));
+}
+
+/**
+ * This function removes links to discarded chrome hangs stacks and prunes unused
+ * annotations.
+ */
+void
+HangReports::PruneStackReferences(const size_t aRemovedStackIndex) {
+  // We need to adjust the indices that link annotations to chrome hangs. Since we
+  // removed a stack, we must remove all references to it and prune annotations
+  // linked to no stacks.
+  for (auto iter = mAnnotationInfo.Iter(); !iter.Done(); iter.Next()) {
+    nsTArray<uint32_t>& stackIndices = iter.Data()->mHangIndices;
+    size_t toRemove = stackIndices.NoIndex;
+    for (size_t k = 0; k < stackIndices.Length(); k++) {
+      // Is this index referencing the removed stack?
+      if (stackIndices[k] == aRemovedStackIndex) {
+        toRemove = k;
+        break;
+      }
+    }
+
+    // Remove the index referencing the old stack from the annotation.
+    if (toRemove != stackIndices.NoIndex) {
+      stackIndices.RemoveElementAt(toRemove);
+    }
+
+    // If this annotation no longer references any stack, drop it.
+    if (!stackIndices.Length()) {
+      iter.Remove();
+    }
+  }
+}
+#endif
+
+size_t
+HangReports::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+  size_t n = 0;
+  n += mStacks.SizeOfExcludingThis();
+  // This is a crude approximation. See comment on
+  // CombinedStacks::SizeOfExcludingThis.
+  n += mHangInfo.capacity() * sizeof(HangInfo);
+  n += mAnnotationInfo.ShallowSizeOfExcludingThis(aMallocSizeOf);
+  n += mAnnotationInfo.Count() * sizeof(AnnotationInfo);
+  for (auto iter = mAnnotationInfo.ConstIter(); !iter.Done(); iter.Next()) {
+    n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+    auto& annotations = iter.Data()->mAnnotations;
+    n += annotations.ShallowSizeOfExcludingThis(aMallocSizeOf);
+  }
+  return n;
+}
+
+const CombinedStacks&
+HangReports::GetStacks() const {
+  return mStacks;
+}
+
+uint32_t
+HangReports::GetDuration(unsigned aIndex) const {
+  return mHangInfo[aIndex].mDuration;
+}
+
+int32_t
+HangReports::GetSystemUptime(unsigned aIndex) const {
+  return mHangInfo[aIndex].mSystemUptime;
+}
+
+int32_t
+HangReports::GetFirefoxUptime(unsigned aIndex) const {
+  return mHangInfo[aIndex].mFirefoxUptime;
+}
+
+const nsClassHashtable<nsStringHashKey, HangReports::AnnotationInfo>&
+HangReports::GetAnnotationInfo() const {
+  return mAnnotationInfo;
+}
+
+} // namespace Telemetry
+} // namespace mozilla
--- a/toolkit/components/telemetry/HangReports.h
+++ b/toolkit/components/telemetry/HangReports.h
@@ -0,0 +1,90 @@
+/* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef HangReports_h__
+#define HangReports_h__
+
+#include <vector>
+#include "mozilla/HangAnnotations.h"
+#include "ProcessedStack.h"
+#include "nsTArray.h"
+#include "nsString.h"
+#include "nsClassHashtable.h"
+#include "CombinedStacks.h"
+
+namespace mozilla {
+namespace Telemetry {
+
+nsresult
+ComputeAnnotationsKey(const HangMonitor::HangAnnotations& aAnnotations, nsAString& aKeyOut);
+
+class HangReports {
+public:
+  /**
+   * This struct encapsulates information for an individual ChromeHang annotation.
+   * mHangIndex is the index of the corresponding ChromeHang.
+   */
+  struct AnnotationInfo {
+    AnnotationInfo(uint32_t aHangIndex,
+                   HangMonitor::HangAnnotations&& aAnnotations)
+      : mAnnotations(Move(aAnnotations))
+    {
+      mHangIndices.AppendElement(aHangIndex);
+    }
+    AnnotationInfo(AnnotationInfo&& aOther)
+      : mHangIndices(aOther.mHangIndices)
+      , mAnnotations(Move(aOther.mAnnotations))
+    {}
+    ~AnnotationInfo() = default;
+    AnnotationInfo& operator=(AnnotationInfo&& aOther)
+    {
+      mHangIndices = aOther.mHangIndices;
+      mAnnotations = Move(aOther.mAnnotations);
+      return *this;
+    }
+    // To save memory, a single AnnotationInfo can be associated to multiple chrome
+    // hangs. The following array holds the index of each related chrome hang.
+    nsTArray<uint32_t> mHangIndices;
+    HangMonitor::HangAnnotations mAnnotations;
+
+  private:
+    // Force move constructor
+    AnnotationInfo(const AnnotationInfo& aOther) = delete;
+    void operator=(const AnnotationInfo& aOther) = delete;
+  };
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+#if defined(MOZ_GECKO_PROFILER)
+  void AddHang(const Telemetry::ProcessedStack& aStack, uint32_t aDuration,
+               int32_t aSystemUptime, int32_t aFirefoxUptime,
+               HangMonitor::HangAnnotations&& aAnnotations);
+  void PruneStackReferences(const size_t aRemovedStackIndex);
+#endif
+  uint32_t GetDuration(unsigned aIndex) const;
+  int32_t GetSystemUptime(unsigned aIndex) const;
+  int32_t GetFirefoxUptime(unsigned aIndex) const;
+  const nsClassHashtable<nsStringHashKey, AnnotationInfo>& GetAnnotationInfo() const;
+  const CombinedStacks& GetStacks() const;
+private:
+  /**
+   * This struct encapsulates the data for an individual ChromeHang, excluding
+   * annotations.
+   */
+  struct HangInfo {
+    // Hang duration (in seconds)
+    uint32_t mDuration;
+    // System uptime (in minutes) at the time of the hang
+    int32_t mSystemUptime;
+    // Firefox uptime (in minutes) at the time of the hang
+    int32_t mFirefoxUptime;
+  };
+  std::vector<HangInfo> mHangInfo;
+  nsClassHashtable<nsStringHashKey, AnnotationInfo> mAnnotationInfo;
+  CombinedStacks mStacks;
+};
+
+} // namespace Telemetry
+} // namespace mozilla
+
+#endif // CombinedStacks_h__
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -77,42 +77,47 @@
 #include "mozilla/FStream.h"
 #include "mozilla/ProcessedStack.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/IOInterposer.h"
 #include "mozilla/PoisonIOInterposer.h"
 #include "mozilla/StartupTimeline.h"
+#include "mozilla/HangMonitor.h"
 #if defined(XP_WIN)
 #include "mozilla/WinDllServices.h"
 #endif
 #include "nsNativeCharsetUtils.h"
 #include "nsProxyRelease.h"
+#include "HangReports.h"
 
 #if defined(MOZ_GECKO_PROFILER)
 #include "shared-libraries.h"
 #include "KeyedStackCapturer.h"
 #endif // MOZ_GECKO_PROFILER
 
 #if defined(MOZ_TELEMETRY_GECKOVIEW)
 #include "geckoview/TelemetryGeckoViewPersistence.h"
 #endif
 
 namespace {
 
 using namespace mozilla;
+using namespace mozilla::HangMonitor;
 using Telemetry::Common::AutoHashtable;
 using Telemetry::Common::ToJSString;
 using Telemetry::Common::GetCurrentProduct;
 using Telemetry::Common::SetCurrentProduct;
 using Telemetry::Common::SupportedProduct;
 using mozilla::dom::Promise;
 using mozilla::dom::AutoJSAPI;
+using mozilla::Telemetry::HangReports;
 using mozilla::Telemetry::CombinedStacks;
+using mozilla::Telemetry::ComputeAnnotationsKey;
 using mozilla::Telemetry::TelemetryIOInterposeObserver;
 
 #if defined(MOZ_GECKO_PROFILER)
 using mozilla::Telemetry::KeyedStackCapturer;
 #endif
 
 // This is not a member of TelemetryImpl because we want to record I/O during
 // startup.
@@ -140,16 +145,23 @@ class TelemetryImpl final
 public:
   void InitMemoryReporter();
 
   static already_AddRefed<nsITelemetry> CreateTelemetryInstance();
   static void ShutdownTelemetry();
   static void RecordSlowStatement(const nsACString &sql, const nsACString &dbName,
                                   uint32_t delay);
 #if defined(MOZ_GECKO_PROFILER)
+  static void RecordChromeHang(uint32_t aDuration,
+                               Telemetry::ProcessedStack &aStack,
+                               int32_t aSystemUptime,
+                               int32_t aFirefoxUptime,
+                               HangAnnotations&& aAnnotations);
+#endif
+#if defined(MOZ_GECKO_PROFILER)
   static void DoStackCapture(const nsACString& aKey);
 #endif
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
   struct Stat {
     uint32_t hitCount;
     uint32_t totalTime;
   };
   struct StmtStats {
@@ -188,16 +200,18 @@ private:
                    bool includePrivateSql);
 
   void ReadLateWritesStacks(nsIFile* aProfileDir);
 
   static TelemetryImpl *sTelemetry;
   AutoHashtable<SlowSQLEntryType> mPrivateSQL;
   AutoHashtable<SlowSQLEntryType> mSanitizedSQL;
   Mutex mHashMutex;
+  HangReports mHangReports;
+  Mutex mHangReportsMutex;
   Atomic<bool> mCanRecordBase;
   Atomic<bool> mCanRecordExtended;
 
 #if defined(MOZ_GECKO_PROFILER)
   // Stores data about stacks captured on demand.
   KeyedStackCapturer mStackCapturer;
 #endif
 
@@ -476,16 +490,17 @@ TelemetryImpl::AsyncFetchTelemetryData(n
                                                          profileDir);
 
   targetThread->Dispatch(event, NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 TelemetryImpl::TelemetryImpl()
   : mHashMutex("Telemetry::mHashMutex")
+  , mHangReportsMutex("Telemetry::mHangReportsMutex")
   , mCanRecordBase(false)
   , mCanRecordExtended(false)
   , mCachedTelemetryData(false)
   , mLastShutdownTime(0)
   , mFailedLockCount(0)
 {
   // We expect TelemetryHistogram::InitializeGlobalState() to have been
   // called before we get to this point.
@@ -493,16 +508,17 @@ TelemetryImpl::TelemetryImpl()
 }
 
 TelemetryImpl::~TelemetryImpl() {
   UnregisterWeakMemoryReporter(this);
 
   // This is still racey as access to these collections is guarded using sTelemetry.
   // We will fix this in bug 1367344.
   MutexAutoLock hashLock(mHashMutex);
+  MutexAutoLock hangReportsLock(mHangReportsMutex);
 }
 
 void
 TelemetryImpl::InitMemoryReporter() {
   RegisterWeakMemoryReporter(this);
 }
 
 bool
@@ -632,16 +648,132 @@ TelemetryImpl::GetWebrtcStats(JSContext 
 NS_IMETHODIMP
 TelemetryImpl::GetMaximalNumberOfConcurrentThreads(uint32_t *ret)
 {
   *ret = nsThreadManager::get().GetHighestNumberOfThreads();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+TelemetryImpl::GetChromeHangs(JSContext *cx, JS::MutableHandle<JS::Value> ret)
+{
+  MutexAutoLock hangReportMutex(mHangReportsMutex);
+
+  const CombinedStacks& stacks = mHangReports.GetStacks();
+  JS::Rooted<JSObject*> fullReportObj(cx, CreateJSStackObject(cx, stacks));
+  if (!fullReportObj) {
+    return NS_ERROR_FAILURE;
+  }
+
+  ret.setObject(*fullReportObj);
+
+  JS::Rooted<JSObject*> durationArray(cx, JS_NewArrayObject(cx, 0));
+  JS::Rooted<JSObject*> systemUptimeArray(cx, JS_NewArrayObject(cx, 0));
+  JS::Rooted<JSObject*> firefoxUptimeArray(cx, JS_NewArrayObject(cx, 0));
+  JS::Rooted<JSObject*> annotationsArray(cx, JS_NewArrayObject(cx, 0));
+  if (!durationArray || !systemUptimeArray || !firefoxUptimeArray ||
+      !annotationsArray) {
+    return NS_ERROR_FAILURE;
+  }
+
+  bool ok = JS_DefineProperty(cx, fullReportObj, "durations",
+                              durationArray, JSPROP_ENUMERATE);
+  if (!ok) {
+    return NS_ERROR_FAILURE;
+  }
+
+  ok = JS_DefineProperty(cx, fullReportObj, "systemUptime",
+                         systemUptimeArray, JSPROP_ENUMERATE);
+  if (!ok) {
+    return NS_ERROR_FAILURE;
+  }
+
+  ok = JS_DefineProperty(cx, fullReportObj, "firefoxUptime",
+                         firefoxUptimeArray, JSPROP_ENUMERATE);
+  if (!ok) {
+    return NS_ERROR_FAILURE;
+  }
+
+  ok = JS_DefineProperty(cx, fullReportObj, "annotations", annotationsArray,
+                         JSPROP_ENUMERATE);
+  if (!ok) {
+    return NS_ERROR_FAILURE;
+  }
+
+
+  const size_t length = stacks.GetStackCount();
+  for (size_t i = 0; i < length; ++i) {
+    if (!JS_DefineElement(cx, durationArray, i, mHangReports.GetDuration(i),
+                          JSPROP_ENUMERATE)) {
+      return NS_ERROR_FAILURE;
+    }
+    if (!JS_DefineElement(cx, systemUptimeArray, i, mHangReports.GetSystemUptime(i),
+                          JSPROP_ENUMERATE)) {
+      return NS_ERROR_FAILURE;
+    }
+    if (!JS_DefineElement(cx, firefoxUptimeArray, i, mHangReports.GetFirefoxUptime(i),
+                          JSPROP_ENUMERATE)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    size_t annotationIndex = 0;
+    const nsClassHashtable<nsStringHashKey, HangReports::AnnotationInfo>& annotationInfo =
+      mHangReports.GetAnnotationInfo();
+
+    for (auto iter = annotationInfo.ConstIter(); !iter.Done(); iter.Next()) {
+      const HangReports::AnnotationInfo* info = iter.Data();
+
+      JS::Rooted<JSObject*> keyValueArray(cx, JS_NewArrayObject(cx, 0));
+      if (!keyValueArray) {
+        return NS_ERROR_FAILURE;
+      }
+
+      // Create an array containing all the indices of the chrome hangs relative to this
+      // annotation.
+      JS::Rooted<JS::Value> indicesArray(cx);
+      if (!mozilla::dom::ToJSValue(cx, info->mHangIndices, &indicesArray)) {
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+
+      // We're saving the annotation as [[indices], {annotation-data}], so add the indices
+      // array as the first element of that structure.
+      if (!JS_DefineElement(cx, keyValueArray, 0, indicesArray, JSPROP_ENUMERATE)) {
+        return NS_ERROR_FAILURE;
+      }
+
+      // Create the annotations object...
+      JS::Rooted<JSObject*> jsAnnotation(cx, JS_NewPlainObject(cx));
+      if (!jsAnnotation) {
+        return NS_ERROR_FAILURE;
+      }
+
+      for (auto& annot : info->mAnnotations) {
+        JS::RootedValue jsValue(cx);
+        jsValue.setString(JS_NewUCStringCopyN(cx, annot.mValue.get(), annot.mValue.Length()));
+        if (!JS_DefineUCProperty(cx, jsAnnotation, annot.mName.get(), annot.mName.Length(),
+                                 jsValue, JSPROP_ENUMERATE)) {
+          return NS_ERROR_FAILURE;
+        }
+      }
+
+      // ... and append it after the indices array.
+      if (!JS_DefineElement(cx, keyValueArray, 1, jsAnnotation, JSPROP_ENUMERATE)) {
+        return NS_ERROR_FAILURE;
+      }
+      if (!JS_DefineElement(cx, annotationsArray, annotationIndex++,
+                         keyValueArray, JSPROP_ENUMERATE)) {
+        return NS_ERROR_FAILURE;
+      }
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 TelemetryImpl::SnapshotCapturedStacks(bool clear, JSContext *cx, JS::MutableHandle<JS::Value> ret)
 {
 #if defined(MOZ_GECKO_PROFILER)
   nsresult rv = mStackCapturer.ReflectCapturedStacks(cx, ret);
   if (clear) {
     mStackCapturer.Clear();
   }
   return rv;
@@ -1450,16 +1582,32 @@ TelemetryImpl::RecordIceCandidates(const
 {
   if (!sTelemetry || !TelemetryHistogram::CanRecordExtended())
     return;
 
   sTelemetry->mWebrtcTelemetry.RecordIceCandidateMask(iceCandidateBitmask, success);
 }
 
 #if defined(MOZ_GECKO_PROFILER)
+void
+TelemetryImpl::RecordChromeHang(uint32_t aDuration,
+                                Telemetry::ProcessedStack &aStack,
+                                int32_t aSystemUptime,
+                                int32_t aFirefoxUptime,
+                                HangAnnotations&& aAnnotations)
+{
+  if (!sTelemetry || !TelemetryHistogram::CanRecordExtended())
+    return;
+
+  MutexAutoLock hangReportMutex(sTelemetry->mHangReportsMutex);
+
+  sTelemetry->mHangReports.AddHang(aStack, aDuration,
+                                   aSystemUptime, aFirefoxUptime,
+                                   Move(aAnnotations));
+}
 
 void
 TelemetryImpl::DoStackCapture(const nsACString& aKey) {
   if (Telemetry::CanRecordExtended() && XRE_IsParentProcess()) {
     sTelemetry->mStackCapturer.Capture(aKey);
   }
 }
 #endif
@@ -1746,16 +1894,20 @@ TelemetryImpl::SizeOfIncludingThis(mozil
   n += TelemetryHistogram::GetMapShallowSizesOfExcludingThis(aMallocSizeOf);
   n += TelemetryScalar::GetMapShallowSizesOfExcludingThis(aMallocSizeOf);
   n += mWebrtcTelemetry.SizeOfExcludingThis(aMallocSizeOf);
   { // Scope for mHashMutex lock
     MutexAutoLock lock(mHashMutex);
     n += mPrivateSQL.SizeOfExcludingThis(aMallocSizeOf);
     n += mSanitizedSQL.SizeOfExcludingThis(aMallocSizeOf);
   }
+  { // Scope for mHangReportsMutex lock
+    MutexAutoLock lock(mHangReportsMutex);
+    n += mHangReports.SizeOfExcludingThis(aMallocSizeOf);
+  }
 
   // It's a bit gross that we measure this other stuff that lives outside of
   // TelemetryImpl... oh well.
   if (sTelemetryIOObserver) {
     n += sTelemetryIOObserver->SizeOfIncludingThis(aMallocSizeOf);
   }
 
   n += TelemetryHistogram::GetHistogramSizesofIncludingThis(aMallocSizeOf);
@@ -2003,19 +2155,32 @@ void Init()
 {
   // Make the service manager hold a long-lived reference to the service
   nsCOMPtr<nsITelemetry> telemetryService =
     do_GetService("@mozilla.org/base/telemetry;1");
   MOZ_ASSERT(telemetryService);
 }
 
 #if defined(MOZ_GECKO_PROFILER)
+void RecordChromeHang(uint32_t duration,
+                      ProcessedStack &aStack,
+                      int32_t aSystemUptime,
+                      int32_t aFirefoxUptime,
+                      HangAnnotations&& aAnnotations)
+{
+  TelemetryImpl::RecordChromeHang(duration, aStack,
+                                  aSystemUptime, aFirefoxUptime,
+                                  Move(aAnnotations));
+}
+
 void CaptureStack(const nsACString& aKey)
 {
+#ifdef MOZ_GECKO_PROFILER
   TelemetryImpl::DoStackCapture(aKey);
+#endif
 }
 #endif
 
 void
 WriteFailedProfileLock(nsIFile* aProfileDir)
 {
   nsCOMPtr<nsIFile> file;
   nsresult rv = GetFailedProfileLockFile(getter_AddRefs(file), aProfileDir);
--- a/toolkit/components/telemetry/Telemetry.h
+++ b/toolkit/components/telemetry/Telemetry.h
@@ -24,16 +24,19 @@
  * For documentation on how to add and use new Telemetry probes, see:
  * https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Adding_a_new_Telemetry_probe
  *
  * For more general information on Telemetry see:
  * https://wiki.mozilla.org/Telemetry
  *****************************************************************************/
 
 namespace mozilla {
+namespace HangMonitor {
+  class HangAnnotations;
+} // namespace HangMonitor
 namespace Telemetry {
 
 struct HistogramAccumulation;
 struct KeyedHistogramAccumulation;
 struct ScalarAction;
 struct KeyedScalarAction;
 struct ChildEventData;
 
@@ -480,16 +483,22 @@ class ProcessedStack;
  *
  * @param aDuration - Approximate duration of main thread hang, in seconds
  * @param aStack - Array of PCs from the hung call stack
  * @param aSystemUptime - System uptime at the time of the hang, in minutes
  * @param aFirefoxUptime - Firefox uptime at the time of the hang, in minutes
  * @param aAnnotations - Any annotations to be added to the report
  */
 #if defined(MOZ_GECKO_PROFILER)
+void RecordChromeHang(uint32_t aDuration,
+                      ProcessedStack &aStack,
+                      int32_t aSystemUptime,
+                      int32_t aFirefoxUptime,
+                      mozilla::HangMonitor::HangAnnotations&& aAnnotations);
+
 /**
  * Record the current thread's call stack on demand. Note that, the stack is
  * only captured once. Subsequent calls result in incrementing the capture
  * counter.
  *
  * @param aKey - A user defined key associated with the captured stack.
  *
  * NOTE: Unwinding call stacks is an expensive operation performance-wise.
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -1206,16 +1206,17 @@ var Impl = {
     // Payload common to chrome and content processes.
     let payloadObj = {
       ver: PAYLOAD_VERSION,
       simpleMeasurements,
     };
 
     // Add extended set measurements common to chrome & content processes
     if (Telemetry.canRecordExtended) {
+      payloadObj.chromeHangs = protect(() => Telemetry.chromeHangs);
       payloadObj.log = [];
       payloadObj.webrtc = protect(() => Telemetry.webrtcStats);
     }
 
     if (Utils.isContentProcess) {
       return payloadObj;
     }
 
--- a/toolkit/components/telemetry/docs/data/main-ping.rst
+++ b/toolkit/components/telemetry/docs/data/main-ping.rst
@@ -56,17 +56,17 @@ Structure:
       },
 
       processes: {...},
       simpleMeasurements: {...},
 
       // The following properties may all be null if we fail to collect them.
       histograms: {...},
       keyedHistograms: {...},
-      chromeHangs: {...}, // removed in firefox 62
+      chromeHangs: {...},
       threadHangStats: [...], // obsolete in firefox 57, use the 'bhr' ping
       capturedStacks: {...},
       log: [...], // obsolete in firefox 61, use Event Telemetry or Scalars
       webrtc: {...},
       gc: {...},
       fileIOReports: {...},
       lateWrites: {...},
       addonDetails: {...},
@@ -344,19 +344,16 @@ Structure:
       ],
       "captures": [["string-key", stack-index, count], ... ]
     }
 
 .. _chromeHangs:
 
 chromeHangs
 -----------
-As of Firefox 62, chromeHangs has been removed. Please look to the bhr ping for
-similar functionality.
-
 Contains the statistics about the hangs happening exclusively on the main thread of the parent process. Precise C++ stacks are reported. This is only available on Nightly Release on Windows, when building using "--enable-profiling" switch.
 
 Some limits are applied:
 
 * Reported chrome hang stacks are limited in depth to 50 entries.
 * The maximum number of reported stacks is 50.
 
 The module names can contain unicode characters.
--- a/toolkit/components/telemetry/nsITelemetry.idl
+++ b/toolkit/components/telemetry/nsITelemetry.idl
@@ -124,16 +124,24 @@ interface nsITelemetry : nsISupports
 
   /**
    * A number representing the highest number of concurrent threads
    * reached during this session.
    */
   readonly attribute uint32_t maximalNumberOfConcurrentThreads;
 
   /*
+   * An array of chrome hang reports. Each element is a hang report represented
+   * as an object containing the hang duration, call stack PCs and information
+   * about modules in memory.
+   */
+  [implicit_jscontext]
+  readonly attribute jsval chromeHangs;
+
+  /*
    * Record the current thread's call stack on demand. Note that, the stack is
    * only captured at the first call. All subsequent calls result in incrementing
    * the capture counter without doing actual stack unwinding.
    *
    * @param aKey - A user defined key associated with the captured stack.
    *
    * NOTE: Unwinding call stacks is an expensive operation performance-wise.
    */
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
@@ -1824,17 +1824,17 @@ add_task(async function test_schedulerNo
   Assert.ok(!(await OS.File.exists(ABORTED_FILE)));
 
   await TelemetryController.testShutdown();
   PingServer.resetPingHandler();
 });
 
 add_task(async function test_pingExtendedStats() {
   const EXTENDED_PAYLOAD_FIELDS = [
-    "log", "slowSQL", "fileIOReports", "lateWrites",
+    "chromeHangs", "log", "slowSQL", "fileIOReports", "lateWrites",
     "addonDetails", "webrtc"
   ];
 
   if (AppConstants.platform == "android") {
     EXTENDED_PAYLOAD_FIELDS.push("UIMeasurements");
   }
 
   // Reset telemetry and disable sending extended statistics.
--- a/toolkit/content/aboutTelemetry.js
+++ b/toolkit/content/aboutTelemetry.js
@@ -993,16 +993,42 @@ function SymbolicationRequest_fetchSymbo
   this.symbolRequest.setRequestHeader("Content-type", "application/json");
   this.symbolRequest.setRequestHeader("Content-length",
                                       requestJSON.length);
   this.symbolRequest.setRequestHeader("Connection", "close");
   this.symbolRequest.onreadystatechange = this.handleSymbolResponse.bind(this);
   this.symbolRequest.send(requestJSON);
 };
 
+var ChromeHangs = {
+
+  symbolRequest: null,
+
+  /**
+   * Renders raw chrome hang data
+   */
+  render: function ChromeHangs_render(chromeHangs) {
+    setHasData("chrome-hangs-section", !!chromeHangs);
+    if (!chromeHangs) {
+      return;
+    }
+
+    let stacks = chromeHangs.stacks;
+    let memoryMap = chromeHangs.memoryMap;
+    let durations = chromeHangs.durations;
+
+    StackRenderer.renderStacks("chrome-hangs", stacks, memoryMap,
+                               (index) => this.renderHangHeader(index, durations));
+  },
+
+  renderHangHeader: function ChromeHangs_renderHangHeader(aIndex, aDurations) {
+    StackRenderer.renderHeader("chrome-hangs", [aIndex + 1, aDurations[aIndex]]);
+  }
+};
+
 var CapturedStacks = {
   symbolRequest: null,
 
   render: function CapturedStacks_render(payload) {
     // Retrieve captured stacks from telemetry payload.
     let capturedStacks = "processes" in payload && "parent" in payload.processes
       ? payload.processes.parent.capturedStacks
       : false;
@@ -1195,16 +1221,17 @@ var Histogram = {
 
 var Search = {
 
   HASH_SEARCH: "search=",
 
   // A list of ids of sections that do not support search.
   blacklist: [
     "late-writes-section",
+    "chrome-hangs-section",
     "raw-payload-section"
   ],
 
   // Pass if: all non-empty array items match (case-sensitive)
   isPassText(subject, filter) {
     for (let item of filter) {
       if (item.length && !subject.includes(item)) {
         return false; // mismatch and not a spurious space
@@ -1961,16 +1988,40 @@ function setupListeners() {
   search.addEventListener("input", Search.searchHandler);
 
   // Clean up observers when page is closed
   window.addEventListener("unload",
     function(aEvent) {
       Settings.detachObservers();
   }, {once: true});
 
+  document.getElementById("chrome-hangs-fetch-symbols").addEventListener("click",
+    function() {
+      if (!gPingData) {
+        return;
+      }
+
+      let hangs = gPingData.payload.chromeHangs;
+      let req = new SymbolicationRequest("chrome-hangs",
+                                         ChromeHangs.renderHangHeader,
+                                         hangs.memoryMap,
+                                         hangs.stacks,
+                                         hangs.durations);
+      req.fetchSymbols();
+  });
+
+  document.getElementById("chrome-hangs-hide-symbols").addEventListener("click",
+    function() {
+      if (!gPingData) {
+        return;
+      }
+
+      ChromeHangs.render(gPingData.payload.chromeHangs);
+  });
+
   document.getElementById("captured-stacks-fetch-symbols").addEventListener("click",
     function() {
       if (!gPingData) {
         return;
       }
       let capturedStacks = gPingData.payload.processes.parent.capturedStacks;
       let req = new SymbolicationRequest("captured-stacks",
                                          CapturedStacks.renderCaptureHeader,
@@ -2349,14 +2400,17 @@ function displayRichPingData(ping, updat
   // Show event data.
   Events.render(payload);
 
   // Show captured stacks.
   CapturedStacks.render(payload);
 
   LateWritesSingleton.renderLateWrites(payload.lateWrites);
 
+  // Show chrome hang stacks
+  ChromeHangs.render(payload.chromeHangs);
+
   // Show simple measurements
   SimpleMeasurements.render(payload);
 
 }
 
 window.addEventListener("load", onLoad);
--- a/toolkit/content/aboutTelemetry.xhtml
+++ b/toolkit/content/aboutTelemetry.xhtml
@@ -61,16 +61,19 @@
         <span class="category-name">&aboutTelemetry.eventsSection;</span>
       </div>
       <div class="category" value="simple-measurements-section">
         <span class="category-name">&aboutTelemetry.simpleMeasurementsSection;</span>
       </div>
       <div class="category" value="slow-sql-section">
         <span class="category-name">&aboutTelemetry.slowSqlSection;</span>
       </div>
+      <div class="category" value="chrome-hangs-section">
+        <span class="category-name">&aboutTelemetry.chromeHangsSection;</span>
+      </div>
       <div class="category" value="addon-details-section">
         <span class="category-name">&aboutTelemetry.addonDetailsSection;</span>
       </div>
       <div class="category" value="captured-stacks-section">
         <span class="category-name">&aboutTelemetry.capturedStacksSection;</span>
       </div>
       <div class="category" value="late-writes-section">
         <span class="category-name">&aboutTelemetry.lateWritesSection;</span>
@@ -186,16 +189,22 @@
         <div id="simple-measurements" class="data"></div>
       </section>
 
       <section id="slow-sql-section">
         <p id="sql-warning">&aboutTelemetry.fullSqlWarning;</p>
         <div id="slow-sql-tables" class="data"></div>
       </section>
 
+      <section id="chrome-hangs-section">
+        <a id="chrome-hangs-fetch-symbols" href="">&aboutTelemetry.fetchStackSymbols;</a>
+        <a id="chrome-hangs-hide-symbols" href="">&aboutTelemetry.hideStackSymbols;</a>
+        <div id="chrome-hangs" class="data"></div>
+      </section>
+
       <section id="late-writes-section">
         <a id="late-writes-fetch-symbols" href="">&aboutTelemetry.fetchStackSymbols;</a>
         <a id="late-writes-hide-symbols" href="">&aboutTelemetry.hideStackSymbols;</a>
         <div id="late-writes" class="data"></div>
       </section>
 
       <section id="addon-details-section">
         <div id="addon-details" class="data"></div>
--- a/toolkit/crashreporter/docs/index.rst
+++ b/toolkit/crashreporter/docs/index.rst
@@ -194,16 +194,24 @@ but there will be multiple dump files: a
 one for the plugin process, and perhaps also additional dumps for the Flash
 sandbox and broker processes. All of these files are submitted together as a
 unit. Before submission, the filenames of the files are linked:
 
 - **uuid.ini** - *annotations, includes an additional_minidumps field*
 - **uuid.dmp** - *plugin process dump file*
 - **uuid-<other>.dmp** - *other process dump file as listed in additional_minidumps*
 
+Browser Hangs
+=============
+
+There is a feature of Firefox that will crash Firefox if it stops processing
+messages after a certain period of time. This feature doesn't work well and is
+disabled by default. See ``xpcom/threads/HangMonitor.cpp``. Hang crashes
+are annotated with ``Hang=1``.
+
 about:crashes
 =============
 
 If the crash reporter subsystem is enabled, the *about:crashes*
 page will be registered with the application. This page provides
 information about previous and submitted crashes.
 
 It is also possible to submit crashes from *about:crashes*.
--- a/toolkit/locales/en-US/chrome/global/aboutTelemetry.dtd
+++ b/toolkit/locales/en-US/chrome/global/aboutTelemetry.dtd
@@ -32,16 +32,17 @@
 <!ENTITY aboutTelemetry.sessionInfoSection "Session Information">
 <!ENTITY aboutTelemetry.scalarsSection "Scalars">
 <!ENTITY aboutTelemetry.keyedScalarsSection "Keyed Scalars">
 <!ENTITY aboutTelemetry.histogramsSection "Histograms">
 <!ENTITY aboutTelemetry.keyedHistogramsSection "Keyed Histograms">
 <!ENTITY aboutTelemetry.eventsSection "Events">
 <!ENTITY aboutTelemetry.simpleMeasurementsSection "Simple Measurements">
 <!ENTITY aboutTelemetry.slowSqlSection "Slow SQL Statements">
+<!ENTITY aboutTelemetry.chromeHangsSection "Browser Hangs">
 <!ENTITY aboutTelemetry.addonDetailsSection "Add-on Details">
 <!ENTITY aboutTelemetry.capturedStacksSection "Captured Stacks">
 <!ENTITY aboutTelemetry.lateWritesSection "Late Writes">
 <!ENTITY aboutTelemetry.rawPayloadSection "Raw Payload">
 <!ENTITY aboutTelemetry.raw "Raw JSON">
 
 <!ENTITY aboutTelemetry.fullSqlWarning "NOTE: Slow SQL debugging is enabled. Full SQL strings may be displayed below but they will not be submitted to Telemetry.">
 <!ENTITY aboutTelemetry.fetchStackSymbols "Fetch function names for stacks">
--- a/toolkit/locales/en-US/chrome/global/aboutTelemetry.properties
+++ b/toolkit/locales/en-US/chrome/global/aboutTelemetry.properties
@@ -76,16 +76,20 @@ addonTableDetails = Details
 # LOCALIZATION NOTE(addonProvider):
 # - %1$S is replaced by the name of an Add-on Provider (e.g. “XPI”, “Plugin”)
 addonProvider = %1$S Provider
 
 keysHeader = Property
 namesHeader = Name
 valuesHeader = Value
 
+# LOCALIZATION NOTE(chrome-hangs-title):
+# - %1$S is replaced by the number of the hang
+# - %2$S is replaced by the duration of the hang
+chrome-hangs-title = Hang Report #%1$S (%2$S seconds)
 # LOCALIZATION NOTE(captured-stacks-title):
 # - %1$S is replaced by the string key for this stack
 # - %2$S is replaced by the number of times this stack was captured
 captured-stacks-title = %1$S (capture count: %2$S)
 # LOCALIZATION NOTE(late-writes-title):
 # - %1$S is replaced by the number of the late write
 late-writes-title = Late Write #%1$S
 
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -722,26 +722,26 @@ nsAppShell::ProcessNextNativeEvent(bool 
             // wait for new events.
             if (jni::IsAvailable() && XRE_IsParentProcess() &&
                     AndroidBridge::Bridge()->PumpMessageLoop()) {
                 return true;
             }
 
             AUTO_PROFILER_LABEL("nsAppShell::ProcessNextNativeEvent:Wait",
                                 EVENTS);
-            mozilla::BackgroundHangMonitor().NotifyWait();
+            mozilla::HangMonitor::Suspend();
 
             curEvent = mEventQueue.Pop(/* mayWait */ true);
         }
     }
 
     if (!curEvent)
         return false;
 
-    mozilla::BackgroundHangMonitor().NotifyActivity();
+    mozilla::HangMonitor::NotifyActivity(curEvent->ActivityType());
 
     curEvent->Run();
     return true;
 }
 
 void
 nsAppShell::SyncRunEvent(Event&& event,
                          UniquePtr<Event>(*eventFactory)(UniquePtr<Event>&&))
--- a/widget/android/nsAppShell.h
+++ b/widget/android/nsAppShell.h
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsAppShell_h__
 #define nsAppShell_h__
 
 #include <time.h>
 
-#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/HangMonitor.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Move.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
 #include "mozilla/jni/Natives.h"
--- a/widget/cocoa/nsAppShell.mm
+++ b/widget/cocoa/nsAppShell.mm
@@ -25,17 +25,17 @@
 #include "nsIInterfaceRequestor.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsObjCExceptions.h"
 #include "nsCocoaFeatures.h"
 #include "nsCocoaUtils.h"
 #include "nsChildView.h"
 #include "nsToolkit.h"
 #include "TextInputHandler.h"
-#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/HangMonitor.h"
 #include "GeckoProfiler.h"
 #include "ScreenHelperCocoa.h"
 #include "mozilla/widget/ScreenManager.h"
 #include "HeadlessScreenHelper.h"
 #include "pratom.h"
 #if !defined(RELEASE_OR_BETA) || defined(DEBUG)
 #include "nsSandboxViolationSink.h"
 #endif
@@ -126,17 +126,17 @@ private:
 extern int32_t             gXULModalLevel;
 
 static bool gAppShellMethodsSwizzled = false;
 
 @implementation GeckoNSApplication
 
 - (void)sendEvent:(NSEvent *)anEvent
 {
-  mozilla::BackgroundHangMonitor().NotifyActivity();
+  mozilla::HangMonitor::NotifyActivity();
   if ([anEvent type] == NSApplicationDefined &&
       [anEvent subtype] == kEventSubtypeTrace) {
     mozilla::SignalTracerThread();
     return;
   }
   [super sendEvent:anEvent];
 }
 
@@ -148,22 +148,22 @@ static bool gAppShellMethodsSwizzled = f
 #else
 - (NSEvent*)nextEventMatchingMask:(NSUInteger)mask
 #endif
                         untilDate:(NSDate*)expiration
                            inMode:(NSString*)mode
                           dequeue:(BOOL)flag
 {
   if (expiration) {
-    mozilla::BackgroundHangMonitor().NotifyWait();
+    mozilla::HangMonitor::Suspend();
   }
   NSEvent* nextEvent = [super nextEventMatchingMask:mask
                         untilDate:expiration inMode:mode dequeue:flag];
   if (expiration) {
-    mozilla::BackgroundHangMonitor().NotifyActivity();
+    mozilla::HangMonitor::NotifyActivity();
   }
   return nextEvent;
 }
 
 @end
 
 // AppShellDelegate
 //
@@ -589,17 +589,17 @@ nsAppShell::ProcessNextNativeEvent(bool 
     waitUntil = [NSDate distantFuture];
 
   NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop];
 
   EventQueueRef currentEventQueue = GetCurrentEventQueue();
   EventTargetRef eventDispatcherTarget = GetEventDispatcherTarget();
 
   if (aMayWait) {
-    mozilla::BackgroundHangMonitor().NotifyWait();
+    mozilla::HangMonitor::Suspend();
   }
 
   // Only call -[NSApp sendEvent:] (and indirectly send user-input events to
   // Gecko) if aMayWait is true.  Tbis ensures most calls to -[NSApp
   // sendEvent:] happen under nsAppShell::Run(), at the lowest level of
   // recursion -- thereby making it less likely Gecko will process user-input
   // events in the wrong order or skip some of them.  It also avoids eating
   // too much CPU in nsBaseAppShell::OnProcessNextEvent() (which calls
@@ -615,17 +615,17 @@ nsAppShell::ProcessNextNativeEvent(bool 
       currentMode = [currentRunLoop currentMode];
       if (!currentMode)
         currentMode = NSDefaultRunLoopMode;
       NSEvent *nextEvent = [NSApp nextEventMatchingMask:NSAnyEventMask
                                               untilDate:waitUntil
                                                  inMode:currentMode
                                                 dequeue:YES];
       if (nextEvent) {
-        mozilla::BackgroundHangMonitor().NotifyActivity();
+        mozilla::HangMonitor::NotifyActivity();
         [NSApp sendEvent:nextEvent];
         eventProcessed = true;
       }
     } else {
       // AcquireFirstMatchingEventInQueue() doesn't spin the (native) event
       // loop, though it does queue up any newly available events from the
       // window server.
       EventRef currentEvent = AcquireFirstMatchingEventInQueue(currentEventQueue, 0, NULL,
--- a/widget/gtk/nsAppShell.cpp
+++ b/widget/gtk/nsAppShell.cpp
@@ -9,17 +9,17 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <gdk/gdk.h>
 #include "nsAppShell.h"
 #include "nsWindow.h"
 #include "mozilla/Logging.h"
 #include "prenv.h"
-#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/HangMonitor.h"
 #include "mozilla/Unused.h"
 #include "mozilla/WidgetUtils.h"
 #include "GeckoProfiler.h"
 #include "nsIPowerManagerService.h"
 #ifdef MOZ_ENABLE_DBUS
 #include "WakeLockListener.h"
 #endif
 #include "gfxPlatform.h"
@@ -41,23 +41,23 @@ LazyLogModule gWidgetDragLog("WidgetDrag
 LazyLogModule gWidgetDrawLog("WidgetDraw");
 
 static GPollFunc sPollFunc;
 
 // Wrapper function to disable hang monitoring while waiting in poll().
 static gint
 PollWrapper(GPollFD *ufds, guint nfsd, gint timeout_)
 {
-    mozilla::BackgroundHangMonitor().NotifyWait();
+    mozilla::HangMonitor::Suspend();
     gint result;
     {
         AUTO_PROFILER_THREAD_SLEEP;
         result = (*sPollFunc)(ufds, nfsd, timeout_);
     }
-    mozilla::BackgroundHangMonitor().NotifyActivity();
+    mozilla::HangMonitor::NotifyActivity();
     return result;
 }
 
 #ifdef MOZ_WIDGET_GTK
 // For bug 726483.
 static decltype(GtkContainerClass::check_resize) sReal_gtk_window_check_resize;
 
 static void
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -10,20 +10,20 @@
 #include <winioctl.h>
 
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "nsWindow.h"
 #include "nsWindowDefs.h"
 #include "KeyboardLayout.h"
 #include "mozilla/ArrayUtils.h"
-#include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/dom/MouseEventBinding.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
+#include "mozilla/HangMonitor.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/WindowsVersion.h"
 #include "mozilla/Unused.h"
 #include "nsIContentPolicy.h"
 #include "nsContentUtils.h"
 
 #include "mozilla/Logging.h"
@@ -759,17 +759,17 @@ WinUtils::WaitForMessage(DWORD aTimeoutM
       break;
     }
 #if defined(ACCESSIBILITY)
     if (result == WAIT_IO_COMPLETION) {
       if (NS_IsMainThread()) {
         // We executed an APC that would have woken up the hang monitor. Since
         // there are no more APCs pending and we are now going to sleep again,
         // we should notify the hang monitor.
-        mozilla::BackgroundHangMonitor().NotifyWait();
+        mozilla::HangMonitor::Suspend();
       }
       continue;
     }
 #endif // defined(ACCESSIBILITY)
 
     // Sent messages (via SendMessage and friends) are processed differently
     // than queued messages (via PostMessage); the destination window procedure
     // of the sent message is called during (Get|Peek)Message. Since PeekMessage
--- a/widget/windows/nsAppShell.cpp
+++ b/widget/windows/nsAppShell.cpp
@@ -10,17 +10,17 @@
 #include "nsThreadUtils.h"
 #include "WinUtils.h"
 #include "WinTaskbar.h"
 #include "WinMouseScrollHandler.h"
 #include "nsWindowDefs.h"
 #include "nsString.h"
 #include "WinIMEHandler.h"
 #include "mozilla/widget/AudioSession.h"
-#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/HangMonitor.h"
 #include "nsIDOMWakeLockListener.h"
 #include "nsIPowerManagerService.h"
 #include "mozilla/StaticPtr.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "GeckoProfiler.h"
 #include "nsComponentManagerUtils.h"
 #include "ScreenHelperWin.h"
@@ -510,17 +510,19 @@ nsAppShell::ProcessNextNativeEvent(bool 
 
     if (gotMessage) {
       if (msg.message == WM_QUIT) {
         ::PostQuitMessage(msg.wParam);
         Exit();
       } else {
         // If we had UI activity we would be processing it now so we know we
         // have either kUIActivity or kActivityNoUIAVail.
-        mozilla::BackgroundHangMonitor().NotifyActivity();
+        mozilla::HangMonitor::NotifyActivity(
+          uiMessage ? mozilla::HangMonitor::kUIActivity :
+                      mozilla::HangMonitor::kActivityNoUIAVail);
 
         if (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST &&
             IMEHandler::ProcessRawKeyMessage(msg)) {
           continue;  // the message is consumed.
         }
 
         // Store Printer Properties messages for reposting, because they are not
         // processed by a window procedure, but are explicitly waited for in the
@@ -531,17 +533,17 @@ nsAppShell::ProcessNextNativeEvent(bool 
           continue;
         }
 
         ::TranslateMessage(&msg);
         ::DispatchMessageW(&msg);
       }
     } else if (mayWait) {
       // Block and wait for any posted application message
-      mozilla::BackgroundHangMonitor().NotifyWait();
+      mozilla::HangMonitor::Suspend();
       {
         AUTO_PROFILER_THREAD_SLEEP;
         WinUtils::WaitForMessage();
       }
     }
   } while (!gotMessage && mayWait);
 
   // See DoProcessNextNativeEvent, mEventloopNestingLevel will be
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -177,17 +177,17 @@
 
 #include "nsWindowDefs.h"
 
 #include "nsCrashOnException.h"
 #include "nsIXULRuntime.h"
 
 #include "nsIContent.h"
 
-#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/HangMonitor.h"
 #include "WinIMEHandler.h"
 
 #include "npapi.h"
 
 #include <d3d11.h>
 
 #include "InkCollector.h"
 
@@ -4952,24 +4952,38 @@ DisplaySystemMenu(HWND hWnd, nsSizeMode 
     if (cmd) {
       PostMessage(hWnd, WM_SYSCOMMAND, cmd, 0);
       return true;
     }
   }
   return false;
 }
 
+inline static mozilla::HangMonitor::ActivityType ActivityTypeForMessage(UINT msg)
+{
+  if ((msg >= WM_KEYFIRST && msg <= WM_IME_KEYLAST) ||
+      (msg >= WM_MOUSEFIRST && msg <= WM_MOUSELAST) ||
+      (msg >= MOZ_WM_MOUSEWHEEL_FIRST && msg <= MOZ_WM_MOUSEWHEEL_LAST) ||
+      (msg >= NS_WM_IMEFIRST && msg <= NS_WM_IMELAST)) {
+    return mozilla::HangMonitor::kUIActivity;
+  }
+
+  // This may not actually be right, but we don't want to reset the timer if
+  // we're not actually processing a UI message.
+  return mozilla::HangMonitor::kActivityUIAVail;
+}
+
 // The WndProc procedure for all nsWindows in this toolkit. This merely catches
 // exceptions and passes the real work to WindowProcInternal. See bug 587406
 // and http://msdn.microsoft.com/en-us/library/ms633573%28VS.85%29.aspx
 LRESULT CALLBACK nsWindow::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
   mozilla::ipc::CancelCPOWs();
 
-  BackgroundHangMonitor().NotifyActivity();
+  HangMonitor::NotifyActivity(ActivityTypeForMessage(msg));
 
   return mozilla::CallWindowProcCrashProtected(WindowProcInternal, hWnd, msg, wParam, lParam);
 }
 
 LRESULT CALLBACK nsWindow::WindowProcInternal(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
   if (::GetWindowLongPtrW(hWnd, GWLP_ID) == eFakeTrackPointScrollableID) {
     // This message was sent to the FAKETRACKPOINTSCROLLABLE.
--- a/xpcom/build/XPCOMInit.cpp
+++ b/xpcom/build/XPCOMInit.cpp
@@ -102,16 +102,17 @@ extern nsresult nsStringInputStreamConst
 #include "nsMemoryInfoDumper.h"
 #include "nsSecurityConsoleMessage.h"
 #include "nsMessageLoop.h"
 #include "nss.h"
 
 #include <locale.h>
 #include "mozilla/Services.h"
 #include "mozilla/Omnijar.h"
+#include "mozilla/HangMonitor.h"
 #include "mozilla/ScriptPreloader.h"
 #include "mozilla/SystemGroup.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/BackgroundHangMonitor.h"
 
 #include "nsChromeRegistry.h"
 #include "nsChromeProtocolHandler.h"
 #include "mozilla/PoisonIOInterposer.h"
@@ -733,16 +734,17 @@ NS_InitXPCOM2(nsIServiceManager** aResul
   RegisterStrongMemoryReporter(new ICUReporter());
   RegisterStrongMemoryReporter(new OggReporter());
 #ifdef MOZ_VPX
   RegisterStrongMemoryReporter(new VPXReporter());
 #endif
 
   mozilla::Telemetry::Init();
 
+  mozilla::HangMonitor::Startup();
   mozilla::BackgroundHangMonitor::Startup();
 
   const MessageLoop* const loop = MessageLoop::current();
   sMainHangMonitor = new mozilla::BackgroundHangMonitor(
     loop->thread_name().c_str(),
     loop->transient_hang_timeout(),
     loop->permanent_hang_timeout());
 
@@ -791,16 +793,17 @@ NS_InitMinimalXPCOM()
 
   // Global cycle collector initialization.
   if (!nsCycleCollector_init()) {
     return NS_ERROR_UNEXPECTED;
   }
 
   SharedThreadPool::InitStatics();
   mozilla::Telemetry::Init();
+  mozilla::HangMonitor::Startup();
   mozilla::BackgroundHangMonitor::Startup();
 
   return NS_OK;
 }
 
 //
 // NS_ShutdownXPCOM()
 //
@@ -856,17 +859,17 @@ SetGMPMemoryFunctions()
   }
 }
 #endif
 
 nsresult
 ShutdownXPCOM(nsIServiceManager* aServMgr)
 {
   // Make sure the hang monitor is enabled for shutdown.
-  BackgroundHangMonitor().NotifyActivity();
+  HangMonitor::NotifyActivity();
 
   if (!NS_IsMainThread()) {
     MOZ_CRASH("Shutdown on wrong thread");
   }
 
   nsresult rv;
   nsCOMPtr<nsISimpleEnumerator> moduleLoaders;
 
@@ -929,17 +932,17 @@ ShutdownXPCOM(nsIServiceManager* aServMg
 
     // Shutdown all remaining threads.  This method does not return until
     // all threads created using the thread manager (with the exception of
     // the main thread) have exited.
     nsThreadManager::get().Shutdown();
 
     NS_ProcessPendingEvents(thread);
 
-    BackgroundHangMonitor().NotifyActivity();
+    HangMonitor::NotifyActivity();
 
     // Late-write checks needs to find the profile directory, so it has to
     // be initialized before mozilla::services::Shutdown or (because of
     // xpcshell tests replacing the service) modules being unloaded.
     mozilla::InitLateWriteChecks();
 
     // We save the "xpcom-shutdown-loaders" observers to notify after
     // the observerservice is gone.
@@ -1082,16 +1085,17 @@ ShutdownXPCOM(nsIServiceManager* aServMg
     sCommandLineWasInitialized = false;
   }
 
   delete sExitManager;
   sExitManager = nullptr;
 
   Omnijar::CleanUp();
 
+  HangMonitor::Shutdown();
   BackgroundHangMonitor::Shutdown();
 
   delete sMainHangMonitor;
   sMainHangMonitor = nullptr;
 
   NS_LogTerm();
 
   return NS_OK;
--- a/xpcom/threads/CPUUsageWatcher.cpp
+++ b/xpcom/threads/CPUUsageWatcher.cpp
@@ -182,26 +182,26 @@ CPUUsageWatcher::Init()
   mGlobalUpdateTime = globalTimes.updateTime;
   mGlobalUsageTime = globalTimes.usageTime;
 
   mInitialized = true;
 
   CPUUsageWatcher* self = this;
   NS_DispatchToMainThread(
     NS_NewRunnableFunction("CPUUsageWatcher::Init",
-                           [=]() { BackgroundHangMonitor::RegisterAnnotator(*self); }));
+                           [=]() { HangMonitor::RegisterAnnotator(*self); }));
 
   return Ok();
 }
 
 void
 CPUUsageWatcher::Uninit()
 {
   if (mInitialized) {
-    BackgroundHangMonitor::UnregisterAnnotator(*this);
+    HangMonitor::UnregisterAnnotator(*this);
   }
   mInitialized = false;
 }
 
 Result<Ok, CPUUsageWatcherError>
 CPUUsageWatcher::CollectCPUUsage()
 {
   if (!mInitialized) {
@@ -234,17 +234,17 @@ CPUUsageWatcher::CollectCPUUsage()
 
   mExternalUsageRatio = std::max(0.0f,
                                  globalUsageNormalized - processUsageNormalized);
 
   return Ok();
 }
 
 void
-CPUUsageWatcher::AnnotateHang(BackgroundHangAnnotations& aAnnotations) {
+CPUUsageWatcher::AnnotateHang(HangMonitor::HangAnnotations& aAnnotations) {
   if (!mInitialized) {
     return;
   }
 
   if (mExternalUsageRatio > mExternalUsageThreshold) {
     aAnnotations.AddAnnotation(NS_LITERAL_STRING("ExternalCPUHigh"), true);
   }
 }
@@ -260,13 +260,13 @@ CPUUsageWatcher::Init()
 void CPUUsageWatcher::Uninit() {}
 
 Result<Ok, CPUUsageWatcherError>
 CPUUsageWatcher::CollectCPUUsage()
 {
   return Ok();
 }
 
-void CPUUsageWatcher::AnnotateHang(BackgroundHangAnnotations& aAnnotations) {}
+void CPUUsageWatcher::AnnotateHang(HangMonitor::HangAnnotations& aAnnotations) {}
 
 #endif // CPU_USAGE_WATCHER_ACTIVE
 
 } // namespace mozilla
--- a/xpcom/threads/CPUUsageWatcher.h
+++ b/xpcom/threads/CPUUsageWatcher.h
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_CPUUsageWatcher_h
 #define mozilla_CPUUsageWatcher_h
 
 #include <stdint.h>
 
 #include "mozilla/HangAnnotations.h"
-#include "mozilla/BackgroundHangMonitor.h"
 
 // We only support OSX and Windows, because on Linux we're forced to read
 // from /proc/stat in order to get global CPU values. We would prefer to not
 // eat that cost for this.
 #if defined(NIGHTLY_BUILD) && (defined(XP_WIN) || defined(XP_MACOSX))
 #define CPU_USAGE_WATCHER_ACTIVE
 #endif
 
@@ -27,23 +26,23 @@ enum CPUUsageWatcherError : uint8_t
   GetNumberOfProcessorsError,
   GetProcessTimesError,
   GetSystemTimesError,
   HostStatisticsError,
   ProcStatError,
 };
 
 class CPUUsageHangAnnotator
-  : public BackgroundHangAnnotator
+  : public HangMonitor::Annotator
 {
 public:
 };
 
 class CPUUsageWatcher
-  : public BackgroundHangAnnotator
+  : public HangMonitor::Annotator
 {
 public:
 #ifdef CPU_USAGE_WATCHER_ACTIVE
   CPUUsageWatcher()
     : mInitialized(false)
     , mExternalUsageThreshold(0)
     , mExternalUsageRatio(0)
     , mProcessUsageTime(0)
@@ -57,17 +56,17 @@ public:
 
   void Uninit();
 
   // Updates necessary values to allow AnnotateHang to function. This must be
   // called on some semi-regular basis, as it will calculate the mean CPU
   // usage values between now and the last time it was called.
   Result<Ok, CPUUsageWatcherError> CollectCPUUsage();
 
-  void AnnotateHang(BackgroundHangAnnotations& aAnnotations) final;
+  void AnnotateHang(HangMonitor::HangAnnotations& aAnnotations) final;
 private:
 #ifdef CPU_USAGE_WATCHER_ACTIVE
   bool mInitialized;
   // The threshold above which we will mark a hang as occurring under high
   // external CPU usage conditions
   float mExternalUsageThreshold;
   // The CPU usage (0-1) external to our process, averaged between the two
   // most recent monitor thread runs
new file mode 100644
--- /dev/null
+++ b/xpcom/threads/HangAnnotations.cpp
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/HangAnnotations.h"
+
+#include <vector>
+
+#include "MainThreadUtils.h"
+#include "mozilla/DebugOnly.h"
+#include "nsXULAppAPI.h"
+#include "mozilla/BackgroundHangMonitor.h"
+
+namespace mozilla {
+namespace HangMonitor {
+
+// Chrome hang annotators. This can go away once BHR has completely replaced
+// ChromeHangs.
+static StaticAutoPtr<Observer::Annotators> gChromehangAnnotators;
+
+void
+HangAnnotations::AddAnnotation(const nsAString& aName, const int32_t aData)
+{
+  nsAutoString dataString;
+  dataString.AppendInt(aData);
+  AppendElement(Annotation(aName, dataString));
+}
+
+void
+HangAnnotations::AddAnnotation(const nsAString& aName, const double aData)
+{
+  nsAutoString dataString;
+  dataString.AppendFloat(aData);
+  AppendElement(Annotation(aName, dataString));
+}
+
+void
+HangAnnotations::AddAnnotation(const nsAString& aName, const nsAString& aData)
+{
+  AppendElement(Annotation(aName, aData));
+}
+
+void
+HangAnnotations::AddAnnotation(const nsAString& aName, const nsACString& aData)
+{
+  NS_ConvertUTF8toUTF16 dataString(aData);
+  AppendElement(Annotation(aName, dataString));
+}
+
+void
+HangAnnotations::AddAnnotation(const nsAString& aName, const bool aData)
+{
+  if (aData) {
+    AppendElement(Annotation(aName, NS_LITERAL_STRING("true")));
+  } else {
+    AppendElement(Annotation(aName, NS_LITERAL_STRING("false")));
+  }
+}
+
+namespace Observer {
+
+Annotators::Annotators()
+  : mMutex("HangMonitor::Annotators::mMutex")
+{
+  MOZ_COUNT_CTOR(Annotators);
+}
+
+Annotators::~Annotators()
+{
+  MOZ_ASSERT(mAnnotators.empty());
+  MOZ_COUNT_DTOR(Annotators);
+}
+
+bool
+Annotators::Register(Annotator& aAnnotator)
+{
+  MutexAutoLock lock(mMutex);
+  auto result = mAnnotators.insert(&aAnnotator);
+  return result.second;
+}
+
+bool
+Annotators::Unregister(Annotator& aAnnotator)
+{
+  MutexAutoLock lock(mMutex);
+  DebugOnly<std::set<Annotator*>::size_type> numErased;
+  numErased = mAnnotators.erase(&aAnnotator);
+  MOZ_ASSERT(numErased == 1);
+  return mAnnotators.empty();
+}
+
+HangAnnotations
+Annotators::GatherAnnotations()
+{
+  HangAnnotations annotations;
+  { // Scope for lock
+    MutexAutoLock lock(mMutex);
+    for (std::set<Annotator*>::iterator i = mAnnotators.begin(),
+                                        e = mAnnotators.end();
+         i != e; ++i) {
+      (*i)->AnnotateHang(annotations);
+    }
+  }
+  return annotations;
+}
+
+} // namespace Observer
+
+void
+RegisterAnnotator(Annotator& aAnnotator)
+{
+  BackgroundHangMonitor::RegisterAnnotator(aAnnotator);
+  // We still register annotators for ChromeHangs
+  if (NS_IsMainThread() &&
+      GeckoProcessType_Default == XRE_GetProcessType()) {
+    if (!gChromehangAnnotators) {
+      gChromehangAnnotators = new Observer::Annotators();
+    }
+    gChromehangAnnotators->Register(aAnnotator);
+  }
+}
+
+void
+UnregisterAnnotator(Annotator& aAnnotator)
+{
+  BackgroundHangMonitor::UnregisterAnnotator(aAnnotator);
+  // We still register annotators for ChromeHangs
+  if (NS_IsMainThread() &&
+      GeckoProcessType_Default == XRE_GetProcessType()) {
+    if (gChromehangAnnotators->Unregister(aAnnotator)) {
+      gChromehangAnnotators = nullptr;
+    }
+  }
+}
+
+HangAnnotations
+ChromeHangAnnotatorCallout()
+{
+  if (!gChromehangAnnotators) {
+    return HangAnnotations();
+  }
+  return gChromehangAnnotators->GatherAnnotations();
+}
+
+} // namespace HangMonitor
+} // namespace mozilla
+
+namespace IPC {
+
+using mozilla::HangMonitor::Annotation;
+
+void
+ParamTraits<Annotation>::Write(Message* aMsg, const Annotation& aParam)
+{
+  WriteParam(aMsg, aParam.mName);
+  WriteParam(aMsg, aParam.mValue);
+}
+
+bool
+ParamTraits<Annotation>::Read(const Message* aMsg,
+                              PickleIterator* aIter,
+                              Annotation* aResult)
+{
+  if (!ReadParam(aMsg, aIter, &aResult->mName)) {
+    return false;
+  }
+  if (!ReadParam(aMsg, aIter, &aResult->mValue)) {
+    return false;
+  }
+  return true;
+}
+
+} // namespace IPC
new file mode 100644
--- /dev/null
+++ b/xpcom/threads/HangAnnotations.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_HangAnnotations_h
+#define mozilla_HangAnnotations_h
+
+#include <set>
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Vector.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace HangMonitor {
+
+/**
+ * This type represents an individual hang annotation.
+ */
+class Annotation
+{
+public:
+  Annotation() {}
+  Annotation(const nsAString& aName, const nsAString& aValue)
+    : mName(aName), mValue(aValue)
+  {}
+
+  nsString mName;
+  nsString mValue;
+};
+
+/**
+ * This class extends nsTArray<Annotation> with some methods for adding
+ * annotations being reported by a registered hang Annotator.
+ */
+class HangAnnotations : public nsTArray<Annotation>
+{
+public:
+  void AddAnnotation(const nsAString& aName, const int32_t aData);
+  void AddAnnotation(const nsAString& aName, const double aData);
+  void AddAnnotation(const nsAString& aName, const nsAString& aData);
+  void AddAnnotation(const nsAString& aName, const nsACString& aData);
+  void AddAnnotation(const nsAString& aName, const bool aData);
+};
+
+class Annotator
+{
+public:
+  /**
+   * NB: This function is always called by the HangMonitor thread.
+   *     Plan accordingly.
+   */
+  virtual void AnnotateHang(HangAnnotations& aAnnotations) = 0;
+};
+
+/**
+ * Registers an Annotator to be called when a hang is detected.
+ * @param aAnnotator Reference to an object that implements the
+ * HangMonitor::Annotator interface.
+ */
+void RegisterAnnotator(Annotator& aAnnotator);
+
+/**
+ * Registers an Annotator that was previously registered via RegisterAnnotator.
+ * @param aAnnotator Reference to an object that implements the
+ * HangMonitor::Annotator interface.
+ */
+void UnregisterAnnotator(Annotator& aAnnotator);
+
+/**
+ * Gathers annotations. This function should be called by ChromeHangs.
+ * @return HangAnnotations object.
+ */
+HangAnnotations ChromeHangAnnotatorCallout();
+
+namespace Observer {
+
+class Annotators
+{
+public:
+  Annotators();
+  ~Annotators();
+
+  bool Register(Annotator& aAnnotator);
+  bool Unregister(Annotator& aAnnotator);
+
+  HangAnnotations GatherAnnotations();
+
+private:
+  Mutex                mMutex;
+  std::set<Annotator*> mAnnotators;
+};
+
+} // namespace Observer
+
+} // namespace HangMonitor
+} // namespace mozilla
+
+namespace IPC {
+
+template<>
+class ParamTraits<mozilla::HangMonitor::HangAnnotations>
+  : public ParamTraits<nsTArray<mozilla::HangMonitor::Annotation>>
+{
+public:
+  typedef mozilla::HangMonitor::HangAnnotations paramType;
+};
+
+template<>
+class ParamTraits<mozilla::HangMonitor::Annotation>
+{
+public:
+  typedef mozilla::HangMonitor::Annotation paramType;
+  static void Write(Message* aMsg, const paramType& aParam);
+  static bool Read(const Message* aMsg,
+                   PickleIterator* aIter,
+                   paramType* aResult);
+};
+
+} // namespace IPC
+
+#endif // mozilla_HangAnnotations_h
new file mode 100644
--- /dev/null
+++ b/xpcom/threads/HangMonitor.cpp
@@ -0,0 +1,424 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/HangMonitor.h"
+
+#include "mozilla/Atomics.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ProcessedStack.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsExceptionHandler.h"
+#include "nsReadableUtils.h"
+#include "nsThreadUtils.h"
+#include "mozilla/StackWalk.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "GeckoProfiler.h"
+
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+#if defined(MOZ_GECKO_PROFILER) && defined(MOZ_PROFILING) && defined(XP_WIN)
+  #define REPORT_CHROME_HANGS
+#endif
+
+namespace mozilla {
+namespace HangMonitor {
+
+/**
+ * A flag which may be set from within a debugger to disable the hang
+ * monitor.
+ */
+volatile bool gDebugDisableHangMonitor = false;
+
+const char kHangMonitorPrefName[] = "hangmonitor.timeout";
+
+#ifdef REPORT_CHROME_HANGS
+const char kTelemetryPrefName[] = "toolkit.telemetry.enabled";
+#endif
+
+// Monitor protects gShutdown and gTimeout, but not gTimestamp which rely on
+// being atomically set by the processor; synchronization doesn't really matter
+// in this use case.
+Monitor* gMonitor;
+
+// The timeout preference, in seconds.
+int32_t gTimeout;
+
+PRThread* gThread;
+
+// Set when shutdown begins to signal the thread to exit immediately.
+bool gShutdown;
+
+// The timestamp of the last event notification, or PR_INTERVAL_NO_WAIT if
+// we're currently not processing events.
+Atomic<PRIntervalTime> gTimestamp(PR_INTERVAL_NO_WAIT);
+
+#ifdef REPORT_CHROME_HANGS
+// Main thread ID used in reporting chrome hangs under Windows
+static HANDLE winMainThreadHandle = nullptr;
+
+// Default timeout for reporting chrome hangs to Telemetry (5 seconds)
+static const int32_t DEFAULT_CHROME_HANG_INTERVAL = 5;
+
+// Maximum number of PCs to gather from the stack
+static const int32_t MAX_CALL_STACK_PCS = 400;
+#endif
+
+// PrefChangedFunc
+void
+PrefChanged(const char*, void*)
+{
+  int32_t newval = Preferences::GetInt(kHangMonitorPrefName);
+#ifdef REPORT_CHROME_HANGS
+  // Monitor chrome hangs on the profiling branch if Telemetry enabled
+  if (newval == 0) {
+    bool telemetryEnabled = Preferences::GetBool(kTelemetryPrefName);
+    if (telemetryEnabled) {
+      newval = DEFAULT_CHROME_HANG_INTERVAL;
+    }
+  }
+#endif
+  MonitorAutoLock lock(*gMonitor);
+  if (newval != gTimeout) {
+    gTimeout = newval;
+    lock.Notify();
+  }
+}
+
+void
+Crash()
+{
+  if (gDebugDisableHangMonitor) {
+    return;
+  }
+
+#ifdef XP_WIN
+  if (::IsDebuggerPresent()) {
+    return;
+  }
+#endif
+
+  // If you change this, you must also deal with the threadsafety of AnnotateCrashReport in
+  // non-chrome processes!
+  if (GeckoProcessType_Default == XRE_GetProcessType()) {
+    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Hang"),
+                                       NS_LITERAL_CSTRING("1"));
+    CrashReporter::SetMinidumpAnalysisAllThreads();
+  }
+
+  MOZ_CRASH("HangMonitor triggered");
+}
+
+#ifdef REPORT_CHROME_HANGS
+
+static void
+ChromeStackWalker(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure)
+{
+  MOZ_ASSERT(aClosure);
+  std::vector<uintptr_t>* stack =
+    static_cast<std::vector<uintptr_t>*>(aClosure);
+  if (stack->size() == MAX_CALL_STACK_PCS) {
+    return;
+  }
+  MOZ_ASSERT(stack->size() < MAX_CALL_STACK_PCS);
+  stack->push_back(reinterpret_cast<uintptr_t>(aPC));
+}
+
+static void
+GetChromeHangReport(Telemetry::ProcessedStack& aStack,
+                    int32_t& aSystemUptime,
+                    int32_t& aFirefoxUptime)
+{
+  MOZ_ASSERT(winMainThreadHandle);
+
+  // The thread we're about to suspend might have the alloc lock
+  // so allocate ahead of time
+  std::vector<uintptr_t> rawStack;
+  rawStack.reserve(MAX_CALL_STACK_PCS);
+
+  DWORD ret = ::SuspendThread(winMainThreadHandle);
+  bool suspended = false;
+  if (ret != (DWORD)-1) {
+    // SuspendThread is asynchronous, so the thread may still be running. Use
+    // GetThreadContext to ensure it's really suspended.
+    // See https://blogs.msdn.microsoft.com/oldnewthing/20150205-00/?p=44743.
+    CONTEXT context;
+    context.ContextFlags = CONTEXT_CONTROL;
+    if (::GetThreadContext(winMainThreadHandle, &context)) {
+      suspended = true;
+    }
+  }
+
+  if (!suspended) {
+    if (ret != (DWORD)-1) {
+      MOZ_ALWAYS_TRUE(::ResumeThread(winMainThreadHandle) != DWORD(-1));
+    }
+    return;
+  }
+
+  MozStackWalkThread(ChromeStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
+                     &rawStack, winMainThreadHandle, nullptr);
+  ret = ::ResumeThread(winMainThreadHandle);
+  if (ret == (DWORD)-1) {
+    return;
+  }
+  aStack = Telemetry::GetStackAndModules(rawStack);
+
+  // Record system uptime (in minutes) at the time of the hang
+  aSystemUptime = ((GetTickCount() / 1000) - (gTimeout * 2)) / 60;
+
+  // Record Firefox uptime (in minutes) at the time of the hang
+  bool error;
+  TimeStamp processCreation = TimeStamp::ProcessCreation(&error);
+  if (!error) {
+    TimeDuration td = TimeStamp::Now() - processCreation;
+    aFirefoxUptime = (static_cast<int32_t>(td.ToSeconds()) - (gTimeout * 2)) / 60;
+  } else {
+    aFirefoxUptime = -1;
+  }
+}
+
+#endif
+
+void
+ThreadMain(void*)
+{
+  AUTO_PROFILER_REGISTER_THREAD("Hang Monitor");
+  NS_SetCurrentThreadName("Hang Monitor");
+
+  MonitorAutoLock lock(*gMonitor);
+
+  // In order to avoid issues with the hang monitor incorrectly triggering
+  // during a general system stop such as sleeping, the monitor thread must
+  // run twice to trigger hang protection.
+  PRIntervalTime lastTimestamp = 0;
+  int waitCount = 0;
+
+#ifdef REPORT_CHROME_HANGS
+  Telemetry::ProcessedStack stack;
+  int32_t systemUptime = -1;
+  int32_t firefoxUptime = -1;
+  HangAnnotations annotations;
+#endif
+
+  while (true) {
+    if (gShutdown) {
+      return; // Exit the thread
+    }
+
+    // avoid rereading the volatile value in this loop
+    PRIntervalTime timestamp = gTimestamp;
+
+    PRIntervalTime now = PR_IntervalNow();
+
+    if (timestamp != PR_INTERVAL_NO_WAIT &&
+        now < timestamp) {
+      // 32-bit overflow, reset for another waiting period
+      timestamp = 1; // lowest legal PRInterval value
+    }
+
+    if (timestamp != PR_INTERVAL_NO_WAIT &&
+        timestamp == lastTimestamp &&
+        gTimeout > 0) {
+      ++waitCount;
+#ifdef REPORT_CHROME_HANGS
+      // Capture the chrome-hang stack + Firefox & system uptimes after
+      // the minimum hang duration has been reached (not when the hang ends)
+      if (waitCount == 2) {
+        GetChromeHangReport(stack, systemUptime, firefoxUptime);
+        annotations = ChromeHangAnnotatorCallout();
+      }
+#else
+      // This is the crash-on-hang feature.
+      // See bug 867313 for the quirk in the waitCount comparison
+      if (waitCount >= 2) {
+        int32_t delay =
+          int32_t(PR_IntervalToSeconds(now - timestamp));
+        if (delay >= gTimeout) {
+          MonitorAutoUnlock unlock(*gMonitor);
+          Crash();
+        }
+      }
+#endif
+    } else {
+#ifdef REPORT_CHROME_HANGS
+      if (waitCount >= 2) {
+        uint32_t hangDuration = PR_IntervalToSeconds(now - lastTimestamp);
+        Telemetry::RecordChromeHang(hangDuration, stack, systemUptime,
+                                    firefoxUptime, Move(annotations));
+        stack.Clear();
+      }
+#endif
+      lastTimestamp = timestamp;
+      waitCount = 0;
+    }
+
+    TimeDuration timeout;
+    if (gTimeout <= 0) {
+      timeout = TimeDuration::Forever();
+    } else {
+      timeout = TimeDuration::FromMilliseconds(gTimeout * 500);
+    }
+    lock.Wait(timeout);
+  }
+}
+
+void
+Startup()
+{
+  if (GeckoProcessType_Default != XRE_GetProcessType() &&
+      GeckoProcessType_Content != XRE_GetProcessType()) {
+    return;
+  }
+
+  MOZ_ASSERT(!gMonitor, "Hang monitor already initialized");
+  gMonitor = new Monitor("HangMonitor");
+
+  Preferences::RegisterCallback(PrefChanged, kHangMonitorPrefName);
+  PrefChanged(nullptr, nullptr);
+
+#ifdef REPORT_CHROME_HANGS
+  Preferences::RegisterCallback(PrefChanged, kTelemetryPrefName);
+  winMainThreadHandle =
+    OpenThread(THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId());
+  if (!winMainThreadHandle) {
+    return;
+  }
+#endif
+
+  // Don't actually start measuring hangs until we hit the main event loop.
+  // This potentially misses a small class of really early startup hangs,
+  // but avoids dealing with some xpcshell tests and other situations which
+  // start XPCOM but don't ever start the event loop.
+  Suspend();
+
+  gThread = PR_CreateThread(PR_USER_THREAD,
+                            ThreadMain,
+                            nullptr, PR_PRIORITY_LOW, PR_GLOBAL_THREAD,
+                            PR_JOINABLE_THREAD, 0);
+}
+
+void
+Shutdown()
+{
+  if (GeckoProcessType_Default != XRE_GetProcessType() &&
+      GeckoProcessType_Content != XRE_GetProcessType()) {
+    return;
+  }
+
+  MOZ_ASSERT(gMonitor, "Hang monitor not started");
+
+  {
+    // Scope the lock we're going to delete later
+    MonitorAutoLock lock(*gMonitor);
+    gShutdown = true;
+    lock.Notify();
+  }
+
+  // thread creation could theoretically fail
+  if (gThread) {
+    PR_JoinThread(gThread);
+    gThread = nullptr;
+  }
+
+  delete gMonitor;
+  gMonitor = nullptr;
+}
+
+static bool
+IsUIMessageWaiting()
+{
+#ifndef XP_WIN
+  return false;
+#else
+  // There should never be mouse, keyboard, or IME messages in a message queue
+  // in the content process, so don't waste time making multiple PeekMessage
+  // calls.
+  if (GeckoProcessType_Content == XRE_GetProcessType()) {
+    return false;
+  }
+#define NS_WM_IMEFIRST WM_IME_SETCONTEXT
+#define NS_WM_IMELAST  WM_IME_KEYUP
+  BOOL haveUIMessageWaiting = FALSE;
+  MSG msg;
+  haveUIMessageWaiting |= ::PeekMessageW(&msg, nullptr, WM_KEYFIRST,
+                                         WM_IME_KEYLAST, PM_NOREMOVE);
+  haveUIMessageWaiting |= ::PeekMessageW(&msg, nullptr, NS_WM_IMEFIRST,
+                                         NS_WM_IMELAST, PM_NOREMOVE);
+  haveUIMessageWaiting |= ::PeekMessageW(&msg, nullptr, WM_MOUSEFIRST,
+                                         WM_MOUSELAST, PM_NOREMOVE);
+  return haveUIMessageWaiting;
+#endif
+}
+
+void
+NotifyActivity(ActivityType aActivityType)
+{
+  MOZ_ASSERT(NS_IsMainThread(),
+             "HangMonitor::Notify called from off the main thread.");
+
+  // Determine the activity type more specifically
+  if (aActivityType == kGeneralActivity) {
+    aActivityType = IsUIMessageWaiting() ? kActivityUIAVail :
+                                           kActivityNoUIAVail;
+  }
+
+  // Calculate the cumulative amount of lag time since the last UI message
+  static uint32_t cumulativeUILagMS = 0;
+  switch (aActivityType) {
+    case kActivityNoUIAVail:
+      cumulativeUILagMS = 0;
+      break;
+    case kActivityUIAVail:
+    case kUIActivity:
+      if (gTimestamp != PR_INTERVAL_NO_WAIT) {
+        cumulativeUILagMS += PR_IntervalToMilliseconds(PR_IntervalNow() -
+                                                       gTimestamp);
+      }
+      break;
+    default:
+      break;
+  }
+
+  // This is not a locked activity because PRTimeStamp is a 32-bit quantity
+  // which can be read/written atomically, and we don't want to pay locking
+  // penalties here.
+  gTimestamp = PR_IntervalNow();
+
+  // If we have UI activity we should reset the timer and report it
+  if (aActivityType == kUIActivity) {
+    mozilla::Telemetry::Accumulate(mozilla::Telemetry::EVENTLOOP_UI_ACTIVITY_EXP_MS,
+                                     cumulativeUILagMS);
+    cumulativeUILagMS = 0;
+  }
+
+  if (gThread && !gShutdown) {
+    mozilla::BackgroundHangMonitor().NotifyActivity();
+  }
+}
+
+void
+Suspend()
+{
+  MOZ_ASSERT(NS_IsMainThread(),
+             "HangMonitor::Suspend called from off the main thread.");
+
+  // Because gTimestamp changes this resets the wait count.
+  gTimestamp = PR_INTERVAL_NO_WAIT;
+
+  if (gThread && !gShutdown) {
+    mozilla::BackgroundHangMonitor().NotifyWait();
+  }
+}
+
+} // namespace HangMonitor
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/xpcom/threads/HangMonitor.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_HangMonitor_h
+#define mozilla_HangMonitor_h
+
+namespace mozilla {
+namespace HangMonitor {
+
+/**
+ * Signifies the type of activity in question
+*/
+enum ActivityType
+{
+  /* There is activity and it is known to be UI related activity. */
+  kUIActivity,
+
+  /* There is non UI activity and no UI activity is pending */
+  kActivityNoUIAVail,
+
+  /* There is non UI activity and UI activity is known to be pending */
+  kActivityUIAVail,
+
+  /* There is non UI activity and UI activity pending is unknown */
+  kGeneralActivity
+};
+
+/**
+ * Start monitoring hangs. Should be called by the XPCOM startup process only.
+ */
+void Startup();
+
+/**
+ * Stop monitoring hangs and join the thread.
+ */
+void Shutdown();
+
+/**
+ * Notify the hang monitor of activity which will reset its internal timer.
+ *
+ * @param activityType The type of activity being reported.
+ * @see ActivityType
+ */
+void NotifyActivity(ActivityType activityType = kGeneralActivity);
+
+/*
+ * Notify the hang monitor that the browser is now idle and no detection should
+ * be done.
+ */
+void Suspend();
+
+} // namespace HangMonitor
+} // namespace mozilla
+
+#endif // mozilla_HangMonitor_h
--- a/xpcom/threads/moz.build
+++ b/xpcom/threads/moz.build
@@ -39,16 +39,18 @@ EXPORTS.mozilla += [
     'AbstractEventQueue.h',
     'AbstractThread.h',
     'BlockingResourceBase.h',
     'CondVar.h',
     'CooperativeThreadPool.h',
     'CPUUsageWatcher.h',
     'DeadlockDetector.h',
     'EventQueue.h',
+    'HangAnnotations.h',
+    'HangMonitor.h',
     'IdleTaskRunner.h',
     'LazyIdleThread.h',
     'MainThreadIdlePeriod.h',
     'Monitor.h',
     'MozPromise.h',
     'Mutex.h',
     'PerformanceCounter.h',
     'Queue.h',
@@ -75,16 +77,18 @@ SOURCES += [
 ]
 
 UNIFIED_SOURCES += [
     'AbstractThread.cpp',
     'BlockingResourceBase.cpp',
     'CooperativeThreadPool.cpp',
     'CPUUsageWatcher.cpp',
     'EventQueue.cpp',
+    'HangAnnotations.cpp',
+    'HangMonitor.cpp',
     'InputEventStatistics.cpp',
     'LabeledEventQueue.cpp',
     'LazyIdleThread.cpp',
     'MainThreadIdlePeriod.cpp',
     'nsEnvironment.cpp',
     'nsILabelableRunnable.cpp',
     'nsMemoryPressure.cpp',
     'nsProcessCommon.cpp',
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -16,20 +16,20 @@
 #include "mozilla/ReentrantMonitor.h"
 #include "nsMemoryPressure.h"
 #include "nsThreadManager.h"
 #include "nsIClassInfoImpl.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsQueryObject.h"
 #include "pratom.h"
-#include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/Logging.h"
 #include "nsIObserverService.h"
+#include "mozilla/HangMonitor.h"
 #include "mozilla/IOInterposer.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Scheduler.h"
 #include "mozilla/SchedulerGroup.h"
 #include "mozilla/Services.h"
 #include "mozilla/SystemGroup.h"
@@ -996,17 +996,17 @@ nsThread::ProcessNextEvent(bool aMayWait
     }
 
     *aResult = (event.get() != nullptr);
 
     if (event) {
       LOG(("THRD(%p) running [%p]\n", this, event.get()));
 
       if (MAIN_THREAD == mIsMainThread) {
-        BackgroundHangMonitor().NotifyActivity();
+        HangMonitor::NotifyActivity();
       }
 
       bool schedulerLoggingEnabled = GetSchedulerLoggingEnabled();
       if (schedulerLoggingEnabled
           && mNestedEventLoopDepth > mCurrentEventLoopDepth
           && mCurrentPerformanceCounter) {
           // This is a recursive call, we're saving the time
           // spent in the parent event if the runnable is linked to a DocGroup.
@@ -1251,17 +1251,17 @@ nsThread::SetScriptObserver(mozilla::Cyc
 void
 nsThread::DoMainThreadSpecificProcessing(bool aReallyWait)
 {
   MOZ_ASSERT(mIsMainThread == MAIN_THREAD);
 
   ipc::CancelCPOWs();
 
   if (aReallyWait) {
-    BackgroundHangMonitor().NotifyWait();
+    HangMonitor::Suspend();
   }
 
   // Fire a memory pressure notification, if one is pending.
   if (!ShuttingDown()) {
     MemoryPressureState mpPending = NS_GetPendingMemoryPressure();
     if (mpPending != MemPressure_None) {
       nsCOMPtr<nsIObserverService> os = services::GetObserverService();