merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 16 Aug 2017 11:23:24 +0200
changeset 375075 6ebc251bd288c268b020815025b05854ccde5c08
parent 375015 0aa944d3ac94757842143a13fe66f6534817383f (current diff)
parent 375074 d1d85eed4d06d513cb6e6937e7e487f4f2386543 (diff)
child 375076 5a020658c1a1f604747e3903e386078b0f945d07
child 375164 4fc086912e1862c592f1696259a10f9438d52c1a
child 375171 ecff6a69e042a3b3a618f7a83d77728d7aca2d3e
push id48904
push usercbook@mozilla.com
push dateWed, 16 Aug 2017 12:21:44 +0000
treeherderautoland@5a020658c1a1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone57.0a1
first release with
nightly linux32
6ebc251bd288 / 57.0a1 / 20170816100153 / files
nightly linux64
6ebc251bd288 / 57.0a1 / 20170816100153 / files
nightly mac
6ebc251bd288 / 57.0a1 / 20170816100153 / files
nightly win32
6ebc251bd288 / 57.0a1 / 20170816100153 / files
nightly win64
6ebc251bd288 / 57.0a1 / 20170816100153 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
browser/themes/shared/jar.inc.mn
browser/themes/shared/notification-icons.svg
devtools/client/inspector/fonts/components/App.js
devtools/client/inspector/rules/views/rule-editor.js
dom/base/nsContentUtils.cpp
dom/base/nsDocument.cpp
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
gfx/layers/wr/WebRenderLayerManager.cpp
js/xpconnect/src/XPCShellImpl.cpp
toolkit/components/telemetry/ThreadHangStats.cpp
toolkit/components/telemetry/ThreadHangStats.h
toolkit/components/telemetry/tests/unit/test_ThreadHangStats.js
toolkit/content/aboutTelemetry.js
xpcom/tests/gtest/TestXPIDLString.cpp
xpcom/threads/BackgroundHangMonitor.cpp
xpcom/threads/BackgroundHangMonitor.h
xpcom/threads/ThreadStackHelper.cpp
xpcom/threads/ThreadStackHelper.h
xpcom/threads/nsIHangDetails.idl
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1497,16 +1497,18 @@ pref("toolkit.telemetry.archive.enabled"
 // Enables sending the shutdown ping when Firefox shuts down.
 pref("toolkit.telemetry.shutdownPingSender.enabled", true);
 // Enables sending the shutdown ping using the pingsender from the first session.
 pref("toolkit.telemetry.shutdownPingSender.enabledFirstSession", false);
 // Enables sending the 'new-profile' ping on new profiles.
 pref("toolkit.telemetry.newProfilePing.enabled", true);
 // Enables sending 'update' pings on Firefox updates.
 pref("toolkit.telemetry.updatePing.enabled", true);
+// Enables sending 'bhr' pings when the browser hangs.
+pref("toolkit.telemetry.bhrPing.enabled", true);
 
 // Telemetry experiments settings.
 pref("experiments.enabled", true);
 pref("experiments.manifest.fetchIntervalSeconds", 86400);
 pref("experiments.manifest.uri", "https://telemetry-experiment.cdn.mozilla.net/manifest/v1/firefox/%VERSION%/%CHANNEL%");
 // Whether experiments are supported by the current application profile.
 pref("experiments.supported", true);
 
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -834,8 +834,18 @@ bin/libfreebl_32int64_3.so
 @RESPATH@/fix_stack_using_bpsyms.py
 #ifdef XP_MACOSX
 @RESPATH@/fix_macosx_stack.py
 #endif
 #ifdef XP_LINUX
 @RESPATH@/fix_linux_stack.py
 #endif
 #endif
+
+; Background Hang Monitor
+@RESPATH@/components/backgroundhangmonitor.xpt
+
+; NOTE: This must match the config checks in
+; /toolkit/components/backgroundhangmonitor/moz.build.
+#if defined(NIGHTLY_BUILD) && !defined(MOZ_DEBUG) && !defined(MOZ_TSAN)
+@RESPATH@/components/BHRTelemetryService.js
+@RESPATH@/components/BHRTelemetryService.manifest
+#endif
--- a/browser/themes/shared/autocomplete.inc.css
+++ b/browser/themes/shared/autocomplete.inc.css
@@ -25,17 +25,17 @@
 #PopupAutoComplete > richlistbox {
   padding: 0;
 }
 
 
 /* Login form autocompletion */
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="login"] > .ac-site-icon {
   display: initial;
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#login);
+  list-style-image: url(chrome://browser/skin/notification-icons/login.svg);
   -moz-context-properties: fill;
   fill: GrayText;
 }
 
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="login"] > .ac-site-icon[selected] {
   fill: HighlightText;
 }
 
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -121,25 +121,25 @@
   margin-inline-start: -16px;
   position: relative;
   -moz-context-properties: fill;
   fill: rgb(224, 41, 29);
   display: none;
 }
 
 #identity-box[sharing="camera"] > #sharing-icon {
-  list-style-image: url("chrome://browser/skin/notification-icons.svg#camera-sharing");
+  list-style-image: url("chrome://browser/skin/notification-icons/camera.svg");
 }
 
 #identity-box[sharing="microphone"] > #sharing-icon {
-  list-style-image: url("chrome://browser/skin/notification-icons.svg#microphone-sharing");
+  list-style-image: url("chrome://browser/skin/notification-icons/microphone.svg");
 }
 
 #identity-box[sharing="screen"] > #sharing-icon {
-  list-style-image: url("chrome://browser/skin/notification-icons.svg#screen-sharing");
+  list-style-image: url("chrome://browser/skin/notification-icons/screen.svg");
 }
 
 #identity-box[sharing] > #sharing-icon {
   display: -moz-box;
   animation-delay: -1.5s;
 }
 
 #identity-box[sharing] > #identity-icon,
--- a/browser/themes/shared/incontentprefs/privacy.css
+++ b/browser/themes/shared/incontentprefs/privacy.css
@@ -6,22 +6,28 @@
   max-height: 20px;
   max-width: 20px;
   vertical-align: middle;
   -moz-context-properties: fill;
   fill: currentColor;
 }
 
 .geo-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-linux);
+%ifdef XP_MACOSX
+  list-style-image: url(chrome://browser/skin/notification-icons/geo-osx.svg);
+%elif defined(MOZ_WIDGET_GTK)
+  list-style-image: url(chrome://browser/skin/notification-icons/geo-linux.svg);
+%else
+  list-style-image: url(chrome://browser/skin/notification-icons/geo-windows.svg);
+%endif
 }
 
 .camera-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#camera);
+  list-style-image: url(chrome://browser/skin/notification-icons/camera.svg);
 }
 
 .microphone-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone);
+  list-style-image: url(chrome://browser/skin/notification-icons/microphone.svg);
 }
 
 .desktop-notification-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#desktop-notification);
+  list-style-image: url(chrome://browser/skin/notification-icons/desktop-notification.svg);
 }
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -54,17 +54,52 @@
   skin/classic/browser/fullscreen/secure.svg                   (../shared/fullscreen/secure.svg)
   skin/classic/browser/connection-secure.svg                   (../shared/identity-block/connection-secure.svg)
   skin/classic/browser/connection-mixed-passive-loaded.svg     (../shared/identity-block/connection-mixed-passive-loaded.svg)
   skin/classic/browser/connection-mixed-active-loaded.svg      (../shared/identity-block/connection-mixed-active-loaded.svg)
   skin/classic/browser/identity-icon.svg                       (../shared/identity-block/identity-icon.svg)
   skin/classic/browser/identity-icon-notice.svg                (../shared/identity-block/identity-icon-notice.svg)
   skin/classic/browser/info.svg                                (../shared/info.svg)
 * skin/classic/browser/menuPanel-small.svg                     (../shared/menuPanel-small.svg)
-  skin/classic/browser/notification-icons.svg                  (../shared/notification-icons.svg)
+
+* skin/classic/browser/notification-icons/camera-blocked.svg                (../shared/notification-icons/camera-blocked.svg)
+* skin/classic/browser/notification-icons/camera.svg                        (../shared/notification-icons/camera.svg)
+  skin/classic/browser/notification-icons/default-info.svg                  (../shared/notification-icons/default-info.svg)
+* skin/classic/browser/notification-icons/desktop-notification-blocked.svg  (../shared/notification-icons/desktop-notification-blocked.svg)
+* skin/classic/browser/notification-icons/desktop-notification.svg          (../shared/notification-icons/desktop-notification.svg)
+  skin/classic/browser/notification-icons/focus-tab-by-prompt.svg           (../shared/notification-icons/focus-tab-by-prompt.svg)
+#ifdef XP_MACOSX
+* skin/classic/browser/notification-icons/geo-osx-blocked.svg               (../shared/notification-icons/geo-osx-blocked.svg)
+* skin/classic/browser/notification-icons/geo-osx.svg                       (../shared/notification-icons/geo-osx.svg)
+#elif MOZ_WIDGET_GTK
+* skin/classic/browser/notification-icons/geo-linux-blocked.svg             (../shared/notification-icons/geo-linux-blocked.svg)
+  skin/classic/browser/notification-icons/geo-linux-detailed.svg            (../shared/notification-icons/geo-linux-detailed.svg)
+* skin/classic/browser/notification-icons/geo-linux.svg                     (../shared/notification-icons/geo-linux.svg)
+#elif XP_WIN
+* skin/classic/browser/notification-icons/geo-windows-blocked.svg           (../shared/notification-icons/geo-windows-blocked.svg)
+  skin/classic/browser/notification-icons/geo-windows-detailed.svg          (../shared/notification-icons/geo-windows-detailed.svg)
+* skin/classic/browser/notification-icons/geo-windows.svg                   (../shared/notification-icons/geo-windows.svg)
+#endif
+* skin/classic/browser/notification-icons/indexedDB-blocked.svg             (../shared/notification-icons/indexedDB-blocked.svg)
+* skin/classic/browser/notification-icons/indexedDB.svg                     (../shared/notification-icons/indexedDB.svg)
+  skin/classic/browser/notification-icons/login-detailed.svg                (../shared/notification-icons/login-detailed.svg)
+  skin/classic/browser/notification-icons/login.svg                         (../shared/notification-icons/login.svg)
+* skin/classic/browser/notification-icons/microphone-blocked.svg            (../shared/notification-icons/microphone-blocked.svg)
+  skin/classic/browser/notification-icons/microphone-detailed.svg           (../shared/notification-icons/microphone-detailed.svg)
+* skin/classic/browser/notification-icons/microphone.svg                    (../shared/notification-icons/microphone.svg)
+* skin/classic/browser/notification-icons/persistent-storage-blocked.svg    (../shared/notification-icons/persistent-storage-blocked.svg)
+* skin/classic/browser/notification-icons/persistent-storage.svg            (../shared/notification-icons/persistent-storage.svg)
+  skin/classic/browser/notification-icons/plugin-badge.svg                  (../shared/notification-icons/plugin-badge.svg)
+* skin/classic/browser/notification-icons/plugin-blocked.svg                (../shared/notification-icons/plugin-blocked.svg)
+* skin/classic/browser/notification-icons/plugin.svg                        (../shared/notification-icons/plugin.svg)
+  skin/classic/browser/notification-icons/popup.svg                         (../shared/notification-icons/popup.svg)
+* skin/classic/browser/notification-icons/screen-blocked.svg                (../shared/notification-icons/screen-blocked.svg)
+* skin/classic/browser/notification-icons/screen.svg                        (../shared/notification-icons/screen.svg)
+  skin/classic/browser/notification-icons/update.svg                        (../shared/notification-icons/update.svg)
+
   skin/classic/browser/tracking-protection-16.svg              (../shared/identity-block/tracking-protection-16.svg)
   skin/classic/browser/newtab/close.png                        (../shared/newtab/close.png)
   skin/classic/browser/newtab/controls.svg                     (../shared/newtab/controls.svg)
   skin/classic/browser/panel-icon-arrow-left.svg               (../shared/panel-icon-arrow-left.svg)
   skin/classic/browser/panel-icon-arrow-right.svg              (../shared/panel-icon-arrow-right.svg)
   skin/classic/browser/panel-icon-cancel.svg                   (../shared/panel-icon-cancel.svg)
 #ifndef XP_MACOSX
   skin/classic/browser/panel-icon-folder.svg                   (../shared/panel-icon-folder.svg)
@@ -80,17 +115,17 @@
   skin/classic/browser/preferences/in-content-new/logo-android.svg (../shared/incontentprefs/logo-android.svg)
   skin/classic/browser/preferences/in-content-new/logo-ios.svg     (../shared/incontentprefs/logo-ios.svg)
   skin/classic/browser/preferences/in-content-new/no-search-results.svg       (../shared/incontentprefs/no-search-results.svg)
   skin/classic/browser/preferences/in-content-new/privacy-security.svg        (../shared/incontentprefs/privacy-security.svg)
   skin/classic/browser/preferences/in-content-new/search-arrow-indicator.svg  (../shared/incontentprefs/search-arrow-indicator.svg)
   skin/classic/browser/preferences/in-content-new/search.css       (../shared/incontentprefs/search.css)
   skin/classic/browser/preferences/in-content-new/search.svg       (../shared/incontentprefs/search.svg)
   skin/classic/browser/preferences/in-content-new/siteDataSettings.css (../shared/incontentprefs/siteDataSettings.css)
-  skin/classic/browser/preferences/in-content-new/privacy.css      (../shared/incontentprefs/privacy.css)
+* skin/classic/browser/preferences/in-content-new/privacy.css      (../shared/incontentprefs/privacy.css)
   skin/classic/browser/preferences/in-content-new/sync.svg         (../shared/incontentprefs/sync.svg)
 * skin/classic/browser/preferences/in-content-new/containers.css   (../shared/incontentprefs/containers.css)
   skin/classic/browser/preferences/in-content/icons.svg        (../shared/incontentprefs-old/icons.svg)
   skin/classic/browser/preferences/in-content/search.css       (../shared/incontentprefs-old/search.css)
 * skin/classic/browser/preferences/in-content/containers.css   (../shared/incontentprefs-old/containers.css)
 * skin/classic/browser/preferences/containers.css              (../shared/preferences/containers.css)
   skin/classic/browser/fxa/default-avatar.svg                  (../shared/fxa/default-avatar.svg)
   skin/classic/browser/fxa/logo.png                            (../shared/fxa/logo.png)
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -16,128 +16,128 @@
   margin-inline-end: -5px;
   padding-inline-end: 5px;
 }
 
 /* This class can be used alone or in combination with the class defining the
    type of icon displayed. This rule must be defined before the others in order
    for its list-style-image to be overridden. */
 .notification-anchor-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#default-info);
+  list-style-image: url(chrome://browser/skin/notification-icons/default-info.svg);
 }
 
 /* INDIVIDUAL NOTIFICATIONS */
 
 .focus-tab-by-prompt-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#focus-tab-by-prompt);
+  list-style-image: url(chrome://browser/skin/notification-icons/focus-tab-by-prompt.svg);
 }
 
 .popup-notification-icon[popupid="persistent-storage"],
 .persistent-storage-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#persistent-storage);
+  list-style-image: url(chrome://browser/skin/notification-icons/persistent-storage.svg);
 }
 
 .persistent-storage-icon.blocked-permission-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#persistent-storage-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons/persistent-storage-blocked.svg);
 }
 
 .popup-notification-icon[popupid="web-notifications"],
 .desktop-notification-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#desktop-notification);
+  list-style-image: url(chrome://browser/skin/notification-icons/desktop-notification.svg);
 }
 
 .desktop-notification-icon.blocked-permission-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#desktop-notification-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons/desktop-notification-blocked.svg);
 }
 
 .geo-icon {
 %ifdef XP_MACOSX
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-osx);
+  list-style-image: url(chrome://browser/skin/notification-icons/geo-osx.svg);
 %elif defined(MOZ_WIDGET_GTK)
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-linux);
+  list-style-image: url(chrome://browser/skin/notification-icons/geo-linux.svg);
 %else
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-windows);
+  list-style-image: url(chrome://browser/skin/notification-icons/geo-windows.svg);
 %endif
 }
 
 .geo-icon.blocked-permission-icon {
 %ifdef XP_MACOSX
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-osx-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons/geo-osx-blocked.svg);
 %elif defined(MOZ_WIDGET_GTK)
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-linux-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons/geo-linux-blocked.svg);
 %else
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-windows-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons/geo-windows-blocked.svg);
 %endif
 }
 
 .popup-notification-icon[popupid="geolocation"] {
 %ifdef XP_MACOSX
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-osx);
+  list-style-image: url(chrome://browser/skin/notification-icons/geo-osx.svg);
 %elif defined(MOZ_WIDGET_GTK)
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-linux-detailed);
+  list-style-image: url(chrome://browser/skin/notification-icons/geo-linux-detailed.svg);
 %else
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-windows-detailed);
+  list-style-image: url(chrome://browser/skin/notification-icons/geo-windows-detailed.svg);
 %endif
 }
 
 .popup-notification-icon[popupid="indexedDB-permissions-prompt"],
 .indexedDB-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#indexedDB);
+  list-style-image: url(chrome://browser/skin/notification-icons/indexedDB.svg);
 }
 
 .indexedDB-icon.blocked-permission-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#indexedDB-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons/indexedDB-blocked.svg);
 }
 
 .login-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#login);
+  list-style-image: url(chrome://browser/skin/notification-icons/login.svg);
 }
 
 .popup-notification-icon[popupid="password"] {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#login-detailed);
+  list-style-image: url(chrome://browser/skin/notification-icons/login-detailed.svg);
 }
 
 .camera-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#camera);
+  list-style-image: url(chrome://browser/skin/notification-icons/camera.svg);
 }
 
 .camera-icon.in-use {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#camera-sharing);
+  list-style-image: url(chrome://browser/skin/notification-icons/camera.svg);
 }
 
 .camera-icon.blocked-permission-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#camera-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons/camera-blocked.svg);
 }
 
 .microphone-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone);
+  list-style-image: url(chrome://browser/skin/notification-icons/microphone.svg);
 }
 
 .microphone-icon.in-use {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone-sharing);
+  list-style-image: url(chrome://browser/skin/notification-icons/microphone.svg);
 }
 
 .microphone-icon.blocked-permission-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons/microphone-blocked.svg);
 }
 
 .popup-notification-icon.microphone-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone-detailed);
+  list-style-image: url(chrome://browser/skin/notification-icons/microphone-detailed.svg);
 }
 
 .screen-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#screen);
+  list-style-image: url(chrome://browser/skin/notification-icons/screen.svg);
 }
 
 .screen-icon.in-use {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#screen-sharing);
+  list-style-image: url(chrome://browser/skin/notification-icons/screen.svg);
 }
 
 .screen-icon.blocked-permission-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#screen-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons/screen-blocked.svg);
 }
 
 #webRTC-preview:not([hidden]) {
   display: -moz-stack;
   border-radius: 4px;
   border: 1px solid GrayText;
   overflow: hidden;
   min-width: 300px;
@@ -160,17 +160,17 @@ html|*#webRTC-previewVideo {
 }
 
 #webRTC-previewWarning > .text-link {
   margin-inline-start: 0;
 }
 
 /* This icon has a block sign in it, so we don't need a blocked version. */
 .popup-icon {
-  list-style-image: url("chrome://browser/skin/notification-icons.svg#popup");
+  list-style-image: url("chrome://browser/skin/notification-icons/popup.svg");
 }
 
 /* EME */
 
 .popup-notification-icon[popupid="drmContentPlaying"],
 .drm-icon {
   list-style-image: url("chrome://browser/skin/drm-icon.svg#chains");
 }
@@ -235,22 +235,22 @@ html|*#webRTC-previewVideo {
 .popup-notification-icon[popupid*="offline-app-requested"],
 .popup-notification-icon[popupid="offline-app-usage"] {
   list-style-image: url(chrome://global/skin/icons/question-64.png);
 }
 
 /* PLUGINS */
 
 .plugin-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#plugin);
+  list-style-image: url(chrome://browser/skin/notification-icons/plugin.svg);
   transition: fill 1.5s;
 }
 
 #plugin-icon-badge {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#plugin-badge);
+  list-style-image: url(chrome://browser/skin/notification-icons/plugin-badge.svg);
   opacity: 0;
   transition: opacity 1.5s;
 }
 
 #plugins-notification-icon[extraAttr="inactive"] > .plugin-icon {
   fill: #bfbfbf;
 }
 
@@ -267,17 +267,17 @@ html|*#webRTC-previewVideo {
     opacity: 0;
   }
   to {
     opacity: 1;
   }
 }
 
 .plugin-blocked > .plugin-icon {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#plugin-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons/plugin-blocked.svg);
   fill: #d92215 !important;
 }
 
 .plugin-blocked > #plugin-icon-badge {
   visibility: collapse;
 }
 
 #notification-popup-box[hidden] {
@@ -345,11 +345,11 @@ html|*#webRTC-previewVideo {
   }
 }
 %endif
 
 /* UPDATE */
 .popup-notification-icon[popupid="update-available"],
 .popup-notification-icon[popupid="update-manual"],
 .popup-notification-icon[popupid="update-restart"] {
-  background: #74BF43 url(chrome://browser/skin/notification-icons.svg#update) no-repeat center;
+  background: #74BF43 url(chrome://browser/skin/notification-icons/update.svg) no-repeat center;
   border-radius: 50%;
 }
deleted file mode 100644
--- a/browser/themes/shared/notification-icons.svg
+++ /dev/null
@@ -1,104 +0,0 @@
-<!-- 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/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-  <style>
-    :root > use:not(:target),
-    :root > g:not(:target),
-    :root > circle:not(:target),
-    #strikeout {
-      display: none;
-    }
-    .blocked:target ~ #strikeout {
-      display: block;
-    }
-    .blocked {
-      clip-path: url(#blocked-clipPath);
-    }
-
-    #badge {
-      fill: #3088d4;
-    }
-
-    #update-icon {
-      stroke: #fff;
-      stroke-width: 3px;
-      stroke-linecap: round;
-    }
-  </style>
-
-  <defs>
-    <path id="camera-icon" d="m 2,23 a 3,3 0 0 0 3,3 l 14,0 a 3,3 0 0 0 3,-3 l 0,-4 6,5.5 c 0.5,0.5 1,0.7 2,0.5 l 0,-18 c -1,-0.2 -1.5,0 -2,0.5 l -6,5.5 0,-4 a 3,3 0 0 0 -3,-3 l -14,0 a 3,3 0 0 0 -3,3 z" />
-    <path id="desktop-notification-icon" d="m 2,20 a 4,4 0 0 0 4,4 l 13,0 7,7 0,-7 a 4,4 0 0 0 4,-4 l 0,-12 a 4,4 0 0 0 -4,-4 l -20,0 a 4,4 0 0 0 -4,4 z m 5,-2 a 1,1 0 1 1 0,-2 l 10,0 a 1,1 0 1 1 0,2 z m 0,-4 a 1,1 0 1 1 0,-2 l 14,0 a 1,1 0 1 1 0,2 z m 0,-4 a 1,1 0 1 1 0,-2 l 18,0 a 1,1 0 1 1 0,2 z" />
-    <path id="focus-tab-by-prompt-icon" d="M29.43,25,18.57,3.8A2.92,2.92,0,0,0,16,2a2.92,2.92,0,0,0-2.57,1.8L2.57,25a3.47,3.47,0,0,0,0,3.4A3.15,3.15,0,0,0,5.33,30H26.66a3.15,3.15,0,0,0,2.77-1.6A3.47,3.47,0,0,0,29.43,25ZM16,7.2a2.3,2.3,0,0,1,2.37,2.4L18,18a1.88,1.88,0,0,1-2,2,1.88,1.88,0,0,1-2-2l-.4-8.4A2.3,2.3,0,0,1,16,7.2ZM16,28a3,3,0,0,1,0-6,3,3,0,0,1,0,6Z"/>
-    <path id="geo-linux-icon" d="m 2,15.9 a 14,14 0 1 1 0,0.2 z m 4,2.1 a 10,10 0 0 0 8,8 l 0,-4 4,0 0,4 a 10,10 0 0 0 8,-8 l -4,0 0,-4 4,0 a 10,10 0 0 0 -8,-8 l 0,4 -4,0 0,-4 a 10,10 0 0 0 -8,8 l 4,0 0,4 z" />
-    <path id="geo-linux-detailed-icon" d="m 2,15.9 a 14,14 0 1 1 0,0.2 z m 3,2.1 a 11,11 0 0 0 9,9 l 1,-5 2,0 1,5 a 11,11 0 0 0 9,-9 l -5,-1 0,-2 5,-1 a 11,11 0 0 0 -9,-9 l -1,5 -2,0 -1,-5 a 11,11 0 0 0 -9,9 l 5,1 0,2 z" />
-    <path id="geo-osx-icon" d="m 0,16 16,0 0,16 12,-28 z" />
-    <path id="geo-windows-icon" d="m 2,14 0,4 2,0 a 12,12 0 0 0 10,10 l 0,2 4,0 0,-2 a 12,12 0 0 0 10,-10 l 2,0 0,-4 -2,0 a 12,12 0 0 0 -10,-10 l 0,-2 -4,0 0,2 a 12,12 0 0 0 -10,10 z m 4,1.9 a 10,10 0 1 1 0,0.2 z m 4,0 a 6,6 0 1 1 0,0.2 z" />
-    <path id="geo-windows-detailed-icon" d="m 2,14.5 0,3 2,0.5 a 12,12 0 0 0 10,10 l 0.5,2 3,0 0.5,-2 a 12,12 0 0 0 10,-10 l 2,-0.5 0,-3 -2,-0.5 a 12,12 0 0 0 -10,-10 l -0.5,-2 -3,0 -0.5,2 a 12,12 0 0 0 -10,10 z m 4,1.4 a 10,10 0 1 1 0,0.2 z m 3,0 a 7,7 0 1 1 0,0.2 z" />
-    <path id="indexedDB-icon" d="m 2,24 a 4,4 0 0 0 4,4 l 2,0 0,-4 -2,0 0,-16 20,0 0,16 -2,0 0,4 2,0 a 4,4 0 0 0 4,-4 l 0,-16 a 4,4 0 0 0 -4,-4 l -20,0 a 4,4 0 0 0 -4,4 z m 8,-2 6,7 6,-7 -4,0 0,-8 -4,0 0,8 z" />
-    <path id="login-icon" d="m 2,26 0,4 6,0 0,-2 2,0 0,-2 1,0 0,-1 2,0 0,-3 2,0 2.5,-2.5 1.5,1.5 3,-3 a 8,8 0 1 0 -8,-8 l -3,3 2,2 z m 20,-18.1 a 2,2 0 1 1 0,0.2 z" />
-    <path id="login-detailed-icon" d="m 1,27 0,3.5 a 0.5,0.5 0 0 0 0.5,0.5 l 5,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1.5 1.5,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1.5 1,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1 1,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-2 2,0 2.5,-2.5 q 0.5,-0.5 1,0 l 1,1 c 0.5,0.5 1,0.5 1.5,-0.5 l 1,-2 a 9,9 0 1 0 -8,-8 l -2,1 c -1,0.5 -1,1 -0.5,1.5 l 1.5,1.5 q 0.5,0.5 0,1 z m 21,-19.1 a 2,2 0 1 1 0,0.2 z" />
-    <path id="microphone-icon" d="m 8,14 0,4 a 8,8 0 0 0 6,7.7 l 0,2.3 -2,0 a 2,2 0 0 0 -2,2 l 12,0 a 2,2 0 0 0 -2,-2 l -2,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 -2,0 0,4 a 6,6 0 0 1 -12,0 l 0,-4 z m 4,4 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z" />
-    <path id="microphone-detailed-icon" d="m 8,18 a 8,8 0 0 0 6,7.7 l 0,2.3 -1,0 a 3,2 0 0 0 -3,2 l 12,0 a 3,2 0 0 0 -3,-2 l -1,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 a 1,1 0 0 0 -2,0 l 0,4 a 6,6 0 0 1 -12,0 l 0,-4 a 1,1 0 0 0 -2,0 z m 4,0 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z" />
-    <path id="persistent-storage-icon" d="M26 21.1H6c-1.1 0-2 .9-2 2V27c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2v-3.9c0-1.1-.9-2-2-2zM24.1 27c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM25 3H7C5.3 3 4 4.4 4 6.2v13.3c.6-.3 1.3-.5 2-.5h20c.7 0 1.4.2 2 .5V6.2C28 4.4 26.7 3 25 3z"/>
-    <path id="plugin-icon" d="m 2,26 a 2,2 0 0 0 2,2 l 24,0 a 2,2 0 0 0 2,-2 l 0,-16 a 2,2 0 0 0 -2,-2 l -24,0 a 2,2 0 0 0 -2,2 z m 2,-20 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z m 14,0 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z" />
-    <path id="popup-icon" d="m 2,24 a 4,4 0 0 0 4,4 l 8,0 a 10,10 0 0 1 -2,-4 l -4,0 a 2,2 0 0 1 -2,-2 l 0,-12 18,0 0,2 a 10,10 0 0 1 4,2 l 0,-8 a 4,4 0 0 0 -4,-4 l -18,0 a 4,4 0 0 0 -4,4 z m 12,-2.1 a 8,8 0 1 1 0,0.2 m 10.7,-4.3 a 5,5 0 0 0 -6.9,6.9 z m -5.4,8.4 a 5,5 0 0 0 6.9,-6.9 z" />
-    <path id="screen-icon" d="m 2,18 a 2,2 0 0 0 2,2 l 2,0 0,-6 a 4,4 0 0 1 4,-4 l 14,0 0,-6 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z m 6,10 a 2,2 0 0 0 2,2 l 18,0 a 2,2 0 0 0 2,-2 l 0,-14 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z" />
-    <path id="update-icon" d="M 16,9 L 16,24 M 16,9 L 11,14 M 16,9 L 21,14" />
-
-    <clipPath id="blocked-clipPath">
-      <path d="m 0,0 0,31 31,-31 z m 6,32 26,0 0,-26 z"/>
-    </clipPath>
-
-    <mask id="i-mask" style="fill-opacity: 1;">
-      <rect fill="white" width="32" height="32"/>
-      <circle fill="black" cx="16" cy="9" r="2.5"/>
-      <rect fill="black" x="14" y="14" width="4" height="10" rx="2" ry="2"/>
-    </mask>
-  </defs>
-
-  <g id="default-info">
-    <circle cx="16" cy="16" r="14" mask="url(#i-mask)"/>
-  </g>
-
-  <use id="camera" href="#camera-icon" />
-  <use id="camera-sharing" href="#camera-icon"/>
-  <use id="camera-indicator" href="#camera-icon" />
-  <use id="camera-blocked" class="blocked" href="#camera-icon" />
-  <use id="desktop-notification" href="#desktop-notification-icon" />
-  <use id="desktop-notification-blocked" class="blocked" href="#desktop-notification-icon" />
-  <use id="focus-tab-by-prompt" href="#focus-tab-by-prompt-icon" />
-  <use id="geo-osx" href="#geo-osx-icon" />
-  <use id="geo-osx-blocked" class="blocked" href="#geo-osx-icon" />
-  <use id="geo-linux" href="#geo-linux-icon" />
-  <use id="geo-linux-blocked" class="blocked" href="#geo-linux-icon" />
-  <use id="geo-linux-detailed" href="#geo-linux-detailed-icon" />
-  <use id="geo-windows" href="#geo-windows-icon" />
-  <use id="geo-windows-blocked" class="blocked" href="#geo-windows-icon" />
-  <use id="geo-windows-detailed" href="#geo-windows-detailed-icon" />
-  <use id="indexedDB" href="#indexedDB-icon" />
-  <use id="indexedDB-blocked" class="blocked" href="#indexedDB-icon" />
-  <use id="login" href="#login-icon" />
-  <use id="login-detailed" href="#login-detailed-icon" />
-  <use id="microphone" href="#microphone-icon" />
-  <use id="microphone-sharing" href="#microphone-icon"/>
-  <use id="microphone-indicator" href="#microphone-icon"/>
-  <use id="microphone-blocked" class="blocked" href="#microphone-icon" />
-  <use id="microphone-detailed" href="#microphone-detailed-icon" />
-  <use id="persistent-storage" href="#persistent-storage-icon" />
-  <use id="persistent-storage-blocked" class="blocked" href="#persistent-storage-icon" />
-  <use id="plugin" href="#plugin-icon" />
-  <use id="plugin-blocked" class="blocked" href="#plugin-icon" />
-  <use id="plugin-badge" href="#badge" />
-  <use id="popup" href="#popup-icon" />
-  <use id="screen" href="#screen-icon" />
-  <use id="screen-sharing" href="#screen-icon"/>
-  <use id="screen-indicator" href="#screen-icon"/>
-  <use id="screen-blocked" class="blocked" href="#screen-icon" />
-  <use id="update" href="#update-icon" />
-
-  <path id="strikeout" d="m 2,28 2,2 26,-26 -2,-2 z"/>
-  <circle id="badge" cx="27" cy="5" r="5"/>
-</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/camera-blocked.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <defs>
+#include clip-path.inc.svg
+#include camera-icon.inc.svg
+  </defs>
+  <use clip-path="url(#blocked-clipPath)" href="#camera-icon"/>
+#include strikeout.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/camera-icon.inc.svg
@@ -0,0 +1,1 @@
+<path id="camera-icon" d="m 2,23 a 3,3 0 0 0 3,3 l 14,0 a 3,3 0 0 0 3,-3 l 0,-4 6,5.5 c 0.5,0.5 1,0.7 2,0.5 l 0,-18 c -1,-0.2 -1.5,0 -2,0.5 l -6,5.5 0,-4 a 3,3 0 0 0 -3,-3 l -14,0 a 3,3 0 0 0 -3,3 z"/>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/camera.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+#include camera-icon.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/clip-path.inc.svg
@@ -0,0 +1,3 @@
+<clipPath id="blocked-clipPath">
+    <path d="m 0,0 0,31 31,-31 z m 6,32 26,0 0,-26 z"/>
+</clipPath>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/default-info.svg
@@ -0,0 +1,16 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <defs>
+    <mask id="i-mask" fill-opacity="1">
+      <rect fill="white" width="32" height="32"/>
+      <circle fill="black" cx="16" cy="9" r="2.5"/>
+      <rect fill="black" x="14" y="14" width="4" height="10" rx="2" ry="2"/>
+    </mask>
+  </defs>
+  <g>
+    <circle cx="16" cy="16" r="14" mask="url(#i-mask)"/>
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/desktop-notification-blocked.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <defs>
+#include clip-path.inc.svg
+#include desktop-notification-icon.inc.svg
+  </defs>
+  <use clip-path="url(#blocked-clipPath)" href="#desktop-notification-icon"/>
+#include strikeout.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/desktop-notification-icon.inc.svg
@@ -0,0 +1,1 @@
+<path id="desktop-notification-icon" d="m 2,20 a 4,4 0 0 0 4,4 l 13,0 7,7 0,-7 a 4,4 0 0 0 4,-4 l 0,-12 a 4,4 0 0 0 -4,-4 l -20,0 a 4,4 0 0 0 -4,4 z m 5,-2 a 1,1 0 1 1 0,-2 l 10,0 a 1,1 0 1 1 0,2 z m 0,-4 a 1,1 0 1 1 0,-2 l 14,0 a 1,1 0 1 1 0,2 z m 0,-4 a 1,1 0 1 1 0,-2 l 18,0 a 1,1 0 1 1 0,2 z"/>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/desktop-notification.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+#include desktop-notification-icon.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/focus-tab-by-prompt.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <path d="M29.43,25,18.57,3.8A2.92,2.92,0,0,0,16,2a2.92,2.92,0,0,0-2.57,1.8L2.57,25a3.47,3.47,0,0,0,0,3.4A3.15,3.15,0,0,0,5.33,30H26.66a3.15,3.15,0,0,0,2.77-1.6A3.47,3.47,0,0,0,29.43,25ZM16,7.2a2.3,2.3,0,0,1,2.37,2.4L18,18a1.88,1.88,0,0,1-2,2,1.88,1.88,0,0,1-2-2l-.4-8.4A2.3,2.3,0,0,1,16,7.2ZM16,28a3,3,0,0,1,0-6,3,3,0,0,1,0,6Z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/geo-linux-blocked.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <defs>
+#include clip-path.inc.svg
+#include geo-linux-icon.inc.svg
+  </defs>
+  <use clip-path="url(#blocked-clipPath)" href="#geo-linux-icon"/>
+#include strikeout.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/geo-linux-detailed.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <path d="m 2,15.9 a 14,14 0 1 1 0,0.2 z m 3,2.1 a 11,11 0 0 0 9,9 l 1,-5 2,0 1,5 a 11,11 0 0 0 9,-9 l -5,-1 0,-2 5,-1 a 11,11 0 0 0 -9,-9 l -1,5 -2,0 -1,-5 a 11,11 0 0 0 -9,9 l 5,1 0,2 z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/geo-linux-icon.inc.svg
@@ -0,0 +1,1 @@
+<path id="geo-linux-icon" d="m 2,15.9 a 14,14 0 1 1 0,0.2 z m 4,2.1 a 10,10 0 0 0 8,8 l 0,-4 4,0 0,4 a 10,10 0 0 0 8,-8 l -4,0 0,-4 4,0 a 10,10 0 0 0 -8,-8 l 0,4 -4,0 0,-4 a 10,10 0 0 0 -8,8 l 4,0 0,4 z"/>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/geo-linux.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+#include geo-linux-icon.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/geo-osx-blocked.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <defs>
+#include clip-path.inc.svg
+#include geo-osx-icon.inc.svg
+  </defs>
+  <use clip-path="url(#blocked-clipPath)" href="#geo-osx-icon"/>
+#include strikeout.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/geo-osx-icon.inc.svg
@@ -0,0 +1,1 @@
+<path id="geo-osx-icon" d="m 0,16 16,0 0,16 12,-28 z"/>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/geo-osx.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+#include geo-osx-icon.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/geo-windows-blocked.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <defs>
+#include clip-path.inc.svg
+#include geo-windows-icon.inc.svg
+  </defs>
+  <use clip-path="url(#blocked-clipPath)" href="#geo-windows-icon"/>
+#include strikeout.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/geo-windows-detailed.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <path d="m 2,14.5 0,3 2,0.5 a 12,12 0 0 0 10,10 l 0.5,2 3,0 0.5,-2 a 12,12 0 0 0 10,-10 l 2,-0.5 0,-3 -2,-0.5 a 12,12 0 0 0 -10,-10 l -0.5,-2 -3,0 -0.5,2 a 12,12 0 0 0 -10,10 z m 4,1.4 a 10,10 0 1 1 0,0.2 z m 3,0 a 7,7 0 1 1 0,0.2 z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/geo-windows-icon.inc.svg
@@ -0,0 +1,1 @@
+<path id="geo-windows-icon" d="m 2,14 0,4 2,0 a 12,12 0 0 0 10,10 l 0,2 4,0 0,-2 a 12,12 0 0 0 10,-10 l 2,0 0,-4 -2,0 a 12,12 0 0 0 -10,-10 l 0,-2 -4,0 0,2 a 12,12 0 0 0 -10,10 z m 4,1.9 a 10,10 0 1 1 0,0.2 z m 4,0 a 6,6 0 1 1 0,0.2 z"/>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/geo-windows.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+#include geo-windows-icon.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/indexedDB-blocked.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <defs>
+#include clip-path.inc.svg
+#include indexedDB-icon.inc.svg
+  </defs>
+  <use clip-path="url(#blocked-clipPath)" href="#indexedDB-icon"/>
+#include strikeout.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/indexedDB-icon.inc.svg
@@ -0,0 +1,1 @@
+<path id="indexedDB-icon" d="m 2,24 a 4,4 0 0 0 4,4 l 2,0 0,-4 -2,0 0,-16 20,0 0,16 -2,0 0,4 2,0 a 4,4 0 0 0 4,-4 l 0,-16 a 4,4 0 0 0 -4,-4 l -20,0 a 4,4 0 0 0 -4,4 z m 8,-2 6,7 6,-7 -4,0 0,-8 -4,0 0,8 z"/>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/indexedDB.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+#include indexedDB-icon.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/login-detailed.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <path d="m 1,27 0,3.5 a 0.5,0.5 0 0 0 0.5,0.5 l 5,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1.5 1.5,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1.5 1,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1 1,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-2 2,0 2.5,-2.5 q 0.5,-0.5 1,0 l 1,1 c 0.5,0.5 1,0.5 1.5,-0.5 l 1,-2 a 9,9 0 1 0 -8,-8 l -2,1 c -1,0.5 -1,1 -0.5,1.5 l 1.5,1.5 q 0.5,0.5 0,1 z m 21,-19.1 a 2,2 0 1 1 0,0.2 z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/login.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <path d="m 2,26 0,4 6,0 0,-2 2,0 0,-2 1,0 0,-1 2,0 0,-3 2,0 2.5,-2.5 1.5,1.5 3,-3 a 8,8 0 1 0 -8,-8 l -3,3 2,2 z m 20,-18.1 a 2,2 0 1 1 0,0.2 z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/microphone-blocked.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <defs>
+#include clip-path.inc.svg
+#include microphone-icon.inc.svg
+  </defs>
+  <use clip-path="url(#blocked-clipPath)" href="#microphone-icon"/>
+#include strikeout.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/microphone-detailed.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <path d="m 8,18 a 8,8 0 0 0 6,7.7 l 0,2.3 -1,0 a 3,2 0 0 0 -3,2 l 12,0 a 3,2 0 0 0 -3,-2 l -1,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 a 1,1 0 0 0 -2,0 l 0,4 a 6,6 0 0 1 -12,0 l 0,-4 a 1,1 0 0 0 -2,0 z m 4,0 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/microphone-icon.inc.svg
@@ -0,0 +1,1 @@
+<path id="microphone-icon" d="m 8,14 0,4 a 8,8 0 0 0 6,7.7 l 0,2.3 -2,0 a 2,2 0 0 0 -2,2 l 12,0 a 2,2 0 0 0 -2,-2 l -2,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 -2,0 0,4 a 6,6 0 0 1 -12,0 l 0,-4 z m 4,4 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z"/>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/microphone.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+#include microphone-icon.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/persistent-storage-blocked.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <defs>
+#include clip-path.inc.svg
+#include persistent-storage-icon.inc.svg
+  </defs>
+  <use clip-path="url(#blocked-clipPath)" href="#persistent-storage-icon"/>
+#include strikeout.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/persistent-storage-icon.inc.svg
@@ -0,0 +1,1 @@
+<path id="persistent-storage-icon" d="M26 21.1H6c-1.1 0-2 .9-2 2V27c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2v-3.9c0-1.1-.9-2-2-2zM24.1 27c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM25 3H7C5.3 3 4 4.4 4 6.2v13.3c.6-.3 1.3-.5 2-.5h20c.7 0 1.4.2 2 .5V6.2C28 4.4 26.7 3 25 3z"/>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/persistent-storage.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+#include persistent-storage-icon.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/plugin-badge.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <circle fill="#3088d4" cx="27" cy="5" r="5"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/plugin-blocked.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <defs>
+#include clip-path.inc.svg
+#include plugin-icon.inc.svg
+  </defs>
+  <use clip-path="url(#blocked-clipPath)" href="#plugin-icon"/>
+#include strikeout.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/plugin-icon.inc.svg
@@ -0,0 +1,1 @@
+<path id="plugin-icon" d="m 2,26 a 2,2 0 0 0 2,2 l 24,0 a 2,2 0 0 0 2,-2 l 0,-16 a 2,2 0 0 0 -2,-2 l -24,0 a 2,2 0 0 0 -2,2 z m 2,-20 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z m 14,0 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z"/>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/plugin.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+#include plugin-icon.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/popup.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <path d="m 2,24 a 4,4 0 0 0 4,4 l 8,0 a 10,10 0 0 1 -2,-4 l -4,0 a 2,2 0 0 1 -2,-2 l 0,-12 18,0 0,2 a 10,10 0 0 1 4,2 l 0,-8 a 4,4 0 0 0 -4,-4 l -18,0 a 4,4 0 0 0 -4,4 z m 12,-2.1 a 8,8 0 1 1 0,0.2 m 10.7,-4.3 a 5,5 0 0 0 -6.9,6.9 z m -5.4,8.4 a 5,5 0 0 0 6.9,-6.9 z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/screen-blocked.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <defs>
+#include clip-path.inc.svg
+#include screen-icon.inc.svg
+  </defs>
+  <use clip-path="url(#blocked-clipPath)" href="#screen-icon"/>
+#include strikeout.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/screen-icon.inc.svg
@@ -0,0 +1,1 @@
+<path id="screen-icon" d="m 2,18 a 2,2 0 0 0 2,2 l 2,0 0,-6 a 4,4 0 0 1 4,-4 l 14,0 0,-6 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z m 6,10 a 2,2 0 0 0 2,2 l 18,0 a 2,2 0 0 0 2,-2 l 0,-14 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z"/>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/screen.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+#include screen-icon.inc.svg
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/strikeout.inc.svg
@@ -0,0 +1,1 @@
+<path id="strikeout" d="m 2,28 2,2 26,-26 -2,-2 z"/>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/update.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <path stroke="#fff" stroke-width="3px" stroke-linecap="round" d="M 16,9 L 16,24 M 16,9 L 11,14 M 16,9 L 21,14"/>
+</svg>
--- a/browser/themes/shared/plugin-doorhanger.inc.css
+++ b/browser/themes/shared/plugin-doorhanger.inc.css
@@ -45,21 +45,21 @@
 }
 
 .click-to-play-plugins-notification-link,
 .center-item-link {
   margin: 0;
 }
 
 .messageImage[value="plugin-hidden"] {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#plugin);
+  list-style-image: url(chrome://browser/skin/notification-icons/plugin.svg);
 }
 
 /* Keep any changes to this style in sync with pluginProblem.css */
 notification.pluginVulnerable {
   background-color: rgb(72,72,72);
   background-image: url(chrome://mozapps/skin/plugins/contentPluginStripe.png);
   color: white;
 }
 
 notification.pluginVulnerable .messageImage {
-  list-style-image: url(chrome://browser/skin/notification-icons.svg#plugin-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons/plugin-blocked.svg);
 }
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -116,25 +116,25 @@
   fill: rgb(224, 41, 29);
 }
 
 .tab-sharing-icon-overlay[pinned] {
   margin-inline-start: -16px;
 }
 
 .tab-sharing-icon-overlay[sharing="camera"] {
-  list-style-image: url("chrome://browser/skin/notification-icons.svg#camera-sharing");
+  list-style-image: url("chrome://browser/skin/notification-icons/camera.svg");
 }
 
 .tab-sharing-icon-overlay[sharing="microphone"] {
-  list-style-image: url("chrome://browser/skin/notification-icons.svg#microphone-sharing");
+  list-style-image: url("chrome://browser/skin/notification-icons/microphone.svg");
 }
 
 .tab-sharing-icon-overlay[sharing="screen"] {
-  list-style-image: url("chrome://browser/skin/notification-icons.svg#screen-sharing");
+  list-style-image: url("chrome://browser/skin/notification-icons/screen.svg");
 }
 
 .tab-icon-overlay {
   width: 16px;
   height: 16px;
   margin-top: -8px;
   margin-inline-start: -6px;
   margin-inline-end: -10px;
--- a/browser/themes/shared/webRTC-indicator.css
+++ b/browser/themes/shared/webRTC-indicator.css
@@ -30,17 +30,17 @@ window {
   background-color: white;
 }
 
 #firefoxButton:hover {
   background-color: #f2f2f2;
 }
 
 #screenShareButton {
-  background-image: url("chrome://browser/skin/notification-icons.svg#screen-indicator");
+  background-image: url("chrome://browser/skin/notification-icons/screen.svg");
   background-position: center center;
   background-repeat: no-repeat;
   background-size: 16px;
   min-width: 27px;
   display: none;
 }
 
 window[sharingscreen] > #screenShareButton {
@@ -62,27 +62,27 @@ window[sharingvideo] > #audioVideoButton
 window[sharingaudio] > #audioVideoButton {
   display: -moz-box;
   background-position: center center;
   background-size: 16px;
   min-width: 26px;
 }
 
 window[sharingvideo] > #audioVideoButton {
-  background-image: url("chrome://browser/skin/notification-icons.svg#camera-indicator");
+  background-image: url("chrome://browser/skin/notification-icons/camera.svg");
 }
 
 window[sharingaudio] > #audioVideoButton {
-  background-image: url("chrome://browser/skin/notification-icons.svg#microphone-indicator");
+  background-image: url("chrome://browser/skin/notification-icons/microphone.svg");
 }
 
 /* Multi-icon button: */
 window[sharingaudio][sharingvideo] > #audioVideoButton {
-  background-image: url("chrome://browser/skin/notification-icons.svg#camera-indicator"),
-                    url("chrome://browser/skin/notification-icons.svg#microphone-indicator");
+  background-image: url("chrome://browser/skin/notification-icons/camera.svg"),
+                    url("chrome://browser/skin/notification-icons/microphone.svg");
   background-position: 6px center, 26px center;
   background-size: 16px, 16px;
   min-width: 46px;
 }
 
 /* Hover styles */
 #audioVideoButton,
 #screenShareButton {
--- a/devtools/client/animationinspector/animation-inspector.xhtml
+++ b/devtools/client/animationinspector/animation-inspector.xhtml
@@ -5,17 +5,17 @@
 <!DOCTYPE html>
 <html xmlns="http://www.w3.org/1999/xhtml">
   <head>
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
     <link rel="stylesheet" href="chrome://devtools/skin/animationinspector.css" type="text/css"/>
     <link rel="stylesheet" href="resource://devtools/client/shared/components/splitter/split-box.css"/>
     <script type="application/javascript" src="chrome://devtools/content/shared/theme-switching.js"/>
   </head>
-  <body class="theme-sidebar devtools-monospace" role="application" empty="true">
+  <body class="theme-sidebar" role="application" empty="true">
     <div id="global-toolbar" class="theme-toolbar">
       <span id="all-animations-label" class="label"></span>
       <button id="toggle-all" class="devtools-button pause-button"></button>
     </div>
     <div id="timeline-toolbar" class="theme-toolbar">
       <button id="rewind-timeline" class="devtools-button"></button>
       <button id="pause-resume-timeline" class="devtools-button pause-button paused"></button>
       <span id="timeline-rate" class="devtools-button"></span>
--- a/devtools/client/animationinspector/components/animation-timeline.js
+++ b/devtools/client/animationinspector/components/animation-timeline.js
@@ -165,17 +165,17 @@ AnimationsTimeline.prototype = {
         "class": "time-body track-container"
       }
     });
 
     this.animationsEl = createNode({
       parent: animationTimelineEl,
       nodeType: "ul",
       attributes: {
-        "class": "animations"
+        "class": "animations devtools-monospace"
       }
     });
   },
 
   setupAnimationDetail: function () {
     const animationDetailEl = this.rootWrapperEl.querySelector(".animation-detail");
 
     const animationDetailHeaderEl = createNode({
--- a/devtools/client/inspector/boxmodel/components/BoxModelMain.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelMain.js
@@ -434,17 +434,17 @@ module.exports = createClass({
             title: BOXMODEL_L10N.getStr("boxmodel.content"),
           },
           SHARED_L10N.getFormatStr("dimensions", width, height)
         )
       );
 
     return dom.div(
       {
-        className: "boxmodel-main",
+        className: "boxmodel-main devtools-monospace",
         "data-box": "position",
         ref: div => {
           this.positionLayout = div;
         },
         onClick: this.onLevelClick,
         onKeyDown: this.onKeyDown,
         onMouseOver: this.onHighlightMouseOver,
         onMouseOut: this.props.onHideBoxModelHighlighter,
--- a/devtools/client/inspector/boxmodel/components/BoxModelProperties.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelProperties.js
@@ -109,17 +109,17 @@ module.exports = createClass({
             open: this.state.isOpen,
             onClick: this.onToggleExpander,
           }
         ),
         BOXMODEL_L10N.getStr("boxmodel.propertiesLabel")
       ),
       dom.div(
         {
-          className: "boxmodel-properties-wrapper",
+          className: "boxmodel-properties-wrapper devtools-monospace",
           hidden: !this.state.isOpen,
           tabIndex: 0,
         },
         properties
       )
     );
   },
 
--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_navigation.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_navigation.js
@@ -28,17 +28,17 @@ add_task(function* () {
 
 function* testInitialFocus(inspector, view) {
   info("Test that the focus is on margin layout.");
   let viewdoc = view.document;
   let boxmodel = viewdoc.querySelector(".boxmodel-container");
   boxmodel.focus();
   EventUtils.synthesizeKey("VK_RETURN", {});
 
-  is(boxmodel.getAttribute("activedescendant"), "boxmodel-main",
+  is(boxmodel.getAttribute("activedescendant"), "boxmodel-main devtools-monospace",
     "Should be set to the position layout.");
 }
 
 function* testChangingLevels(inspector, view) {
   info("Test that using arrow keys updates level.");
   let viewdoc = view.document;
   let boxmodel = viewdoc.querySelector(".boxmodel-container");
   boxmodel.focus();
@@ -69,28 +69,28 @@ function* testChangingLevels(inspector, 
   is(boxmodel.getAttribute("activedescendant"), "boxmodel-borders",
     "Should be set to the border layout.");
 
   EventUtils.synthesizeKey("VK_UP", {});
   is(boxmodel.getAttribute("activedescendant"), "boxmodel-margins",
     "Should be set to the margin layout.");
 
   EventUtils.synthesizeKey("VK_UP", {});
-  is(boxmodel.getAttribute("activedescendant"), "boxmodel-main",
+  is(boxmodel.getAttribute("activedescendant"), "boxmodel-main devtools-monospace",
     "Should be set to the position layout.");
 }
 
 function* testTabbingWrapAround(inspector, view) {
   info("Test that using arrow keys updates level.");
   let viewdoc = view.document;
   let boxmodel = viewdoc.querySelector(".boxmodel-container");
   boxmodel.focus();
   EventUtils.synthesizeKey("VK_RETURN", {});
 
-  let editLevel = boxmodel.getAttribute("activedescendant");
+  let editLevel = boxmodel.getAttribute("activedescendant").split(" ")[0];
   let dataLevel = viewdoc.querySelector(`.${editLevel}`).getAttribute("data-box");
   let editBoxes = [...viewdoc.querySelectorAll(
     `[data-box="${dataLevel}"].boxmodel-editable`)];
 
   EventUtils.synthesizeKey("VK_ESCAPE", {});
   editBoxes[3].focus();
   EventUtils.synthesizeKey("VK_TAB", {});
   is(editBoxes[0], viewdoc.activeElement, "Top edit box should have focus.");
--- a/devtools/client/inspector/fonts/components/App.js
+++ b/devtools/client/inspector/fonts/components/App.js
@@ -32,17 +32,17 @@ const App = createClass({
     let {
       fonts,
       onPreviewFonts,
       onShowAllFont,
     } = this.props;
 
     return dom.div(
       {
-        className: "devtools-monospace theme-sidebar inspector-tabpanel",
+        className: "theme-sidebar inspector-tabpanel",
         id: "sidebar-panel-fontinspector"
       },
       dom.div(
         {
           className: "devtools-toolbar"
         },
         SearchBox({
           delay: PREVIEW_UPDATE_DELAY,
--- a/devtools/client/inspector/grids/components/GridList.js
+++ b/devtools/client/inspector/grids/components/GridList.js
@@ -45,16 +45,17 @@ module.exports = createClass({
       },
       dom.span(
         {},
         getStr("layout.overlayGrid")
       ),
       dom.ul(
         {
           id: "grid-list",
+          className: "devtools-monospace",
         },
         grids.map(grid => GridItem({
           key: grid.id,
           getSwatchColorPickerTooltip,
           grid,
           setSelectedNode,
           onHideBoxModelHighlighter,
           onSetGridOverlayColor,
--- a/devtools/client/inspector/inspector.xhtml
+++ b/devtools/client/inspector/inspector.xhtml
@@ -73,17 +73,17 @@
 
     <!-- Sidebar Container -->
     <div id="inspector-sidebar-container">
       <div xmlns="http://www.w3.org/1999/xhtml" id="inspector-sidebar" hidden="true"></div>
     </div>
 
     <!-- Sidebar panel definitions -->
     <div id="tabpanels" style="visibility:collapse">
-      <div id="sidebar-panel-ruleview" class="devtools-monospace theme-sidebar inspector-tabpanel"
+      <div id="sidebar-panel-ruleview" class="theme-sidebar inspector-tabpanel"
            data-localization-bundle="devtools/client/locales/inspector.properties">
         <div id="ruleview-toolbar-container" class="devtools-toolbar">
           <div id="ruleview-toolbar">
             <div class="devtools-searchbox has-clear-btn">
               <input id="ruleview-searchbox"
                      class="devtools-filterinput devtools-rule-searchbox"
                      type="search"
                      data-localization="placeholder=inspector.filterStyles.placeholder"/>
@@ -104,17 +104,17 @@
         </div>
 
         <div id="ruleview-container" class="ruleview">
           <div id="ruleview-container-focusable" tabindex="-1">
           </div>
         </div>
       </div>
 
-      <div id="sidebar-panel-computedview" class="devtools-monospace theme-sidebar inspector-tabpanel"
+      <div id="sidebar-panel-computedview" class="theme-sidebar inspector-tabpanel"
            data-localization-bundle="devtools/client/locales/inspector.properties">
         <div id="computed-toolbar" class="devtools-toolbar">
           <div class="devtools-searchbox has-clear-btn">
             <input id="computed-searchbox"
                    class="devtools-filterinput devtools-rule-searchbox"
                    type="search"
                    data-localization="placeholder=inspector.filterStyles.placeholder"/>
             <button id="computed-searchinput-clear" class="devtools-searchinput-clear"></button>
@@ -124,22 +124,22 @@
                  class="includebrowserstyles"/>
           <label id="browser-style-checkbox-label" for="browser-style-checkbox"
                  data-localization="content=inspector.browserStyles.label"></label>
         </div>
 
         <div id="computed-container">
           <div id="computed-container-focusable" tabindex="-1">
             <div id="boxmodel-wrapper"></div>
-            <div id="computed-property-container" class="theme-separator" tabindex="0" dir="ltr"></div>
+            <div id="computed-property-container" class="theme-separator devtools-monospace" tabindex="0" dir="ltr"></div>
             <div id="computed-no-results" hidden="" data-localization="content=inspector.noProperties"></div>
           </div>
         </div>
       </div>
 
-      <div id="sidebar-panel-animationinspector" class="devtools-monospace theme-sidebar inspector-tabpanel">
+      <div id="sidebar-panel-animationinspector" class="theme-sidebar inspector-tabpanel">
         <iframe class="devtools-inspector-tab-frame"></iframe>
       </div>
     </div>
 
   </div>
 </body>
 </html>
--- a/devtools/client/inspector/layout/components/App.js
+++ b/devtools/client/inspector/layout/components/App.js
@@ -54,17 +54,16 @@ const App = createClass({
   mixins: [ addons.PureRenderMixin ],
 
   render() {
     let { onPromoteLearnMoreClick } = this.props;
 
     return dom.div(
       {
         id: "layout-container",
-        className: "devtools-monospace",
       },
       LayoutPromoteBar({
         onPromoteLearnMoreClick,
       }),
       Accordion({
         items: [
           {
             header: LAYOUT_L10N.getStr("layout.header"),
--- a/devtools/client/inspector/rules/views/rule-editor.js
+++ b/devtools/client/inspector/rules/views/rule-editor.js
@@ -92,17 +92,17 @@ RuleEditor.prototype = {
 
     // Do not allow editing anonymousselectors until we can
     // detect mutations on  pseudo elements in Bug 1034110.
     return trait && !this.rule.elementStyle.element.isAnonymous;
   },
 
   _create: function () {
     this.element = this.doc.createElement("div");
-    this.element.className = "ruleview-rule theme-separator";
+    this.element.className = "ruleview-rule theme-separator devtools-monospace";
     this.element.setAttribute("uneditable", !this.isEditable);
     this.element.setAttribute("unmatched", this.rule.isUnmatched);
     this.element._ruleEditor = this;
 
     // Give a relative position for the inplace editor's measurement
     // span to be placed absolutely against.
     this.element.style.position = "relative";
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9725,17 +9725,16 @@ public:
                                    NullString(), mPostData, mHeadersData,
                                    mLoadType, mSHEntry, mFirstParty,
                                    mSrcdoc, mSourceDocShell, mBaseURI,
                                    mCheckForPrerender, nullptr, nullptr);
   }
 
 private:
   // Use IDL strings so .get() returns null by default
-  nsXPIDLString mWindowTarget;
   nsXPIDLCString mTypeHint;
   nsString mSrcdoc;
 
   RefPtr<nsDocShell> mDocShell;
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIURI> mOriginalURI;
   Maybe<nsCOMPtr<nsIURI>> mResultPrincipalURI;
   bool mLoadReplace;
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -2670,34 +2670,16 @@ Selection::CollapseToEnd(ErrorResult& aR
   nsINode* container = lastRange->GetEndContainer();
   if (!container) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
   Collapse(*container, lastRange->EndOffset(), aRv);
 }
 
-/*
- * IsCollapsed -- is the whole selection just one point, or unset?
- */
-bool
-Selection::IsCollapsed() const
-{
-  uint32_t cnt = mRanges.Length();
-  if (cnt == 0) {
-    return true;
-  }
-
-  if (cnt != 1) {
-    return false;
-  }
-
-  return mRanges[0].mRange->Collapsed();
-}
-
 /* virtual */
 bool
 Selection::Collapsed()
 {
   return IsCollapsed();
 }
 
 NS_IMETHODIMP
@@ -3772,23 +3754,28 @@ Selection::NotifySelectionListeners()
     }
   }
 
   RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
   if (frameSelection->GetBatching()) {
     frameSelection->SetDirty();
     return NS_OK;
   }
+  if (mSelectionListeners.IsEmpty()) {
+    // If there are no selection listeners, we're done!
+    return NS_OK;
+  }
   AutoTArray<nsCOMPtr<nsISelectionListener>, 8>
     selectionListeners(mSelectionListeners);
 
   nsCOMPtr<nsIDOMDocument> domdoc;
   nsIPresShell* ps = GetPresShell();
   if (ps) {
-    domdoc = do_QueryInterface(ps->GetDocument());
+    // Avoid using QueryInterface() here because it can be expensive.
+    domdoc = static_cast<nsIDOMDocument*>(ps->GetDocument()->AsDOMNode());
   }
 
   short reason = frameSelection->PopReason();
   for (auto& listener : selectionListeners) {
     listener->NotifySelectionChanged(domdoc, this, reason);
   }
   return NS_OK;
 }
--- a/dom/base/Selection.h
+++ b/dom/base/Selection.h
@@ -169,17 +169,32 @@ public:
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL methods
   nsINode*     GetAnchorNode();
   uint32_t     AnchorOffset();
   nsINode*     GetFocusNode();
   uint32_t     FocusOffset();
 
-  bool IsCollapsed() const;
+  /*
+   * IsCollapsed -- is the whole selection just one point, or unset?
+   */
+  bool IsCollapsed() const
+  {
+    uint32_t cnt = mRanges.Length();
+    if (cnt == 0) {
+      return true;
+    }
+
+    if (cnt != 1) {
+      return false;
+    }
+
+    return mRanges[0].mRange->Collapsed();
+  }
 
   // *JS() methods are mapped to Selection.*().
   // They may move focus only when the range represents normal selection.
   // These methods shouldn't be used by non-JS callers.
   void CollapseJS(nsINode* aContainer, uint32_t aOffset,
                   mozilla::ErrorResult& aRv);
   void CollapseToStartJS(mozilla::ErrorResult& aRv);
   void CollapseToEndJS(mozilla::ErrorResult& aRv);
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -10682,16 +10682,39 @@ nsContentUtils::GetSourceMapURL(nsIHttpC
 {
   nsresult rv = aChannel->GetResponseHeader(NS_LITERAL_CSTRING("SourceMap"), aResult);
   if (NS_FAILED(rv)) {
     rv = aChannel->GetResponseHeader(NS_LITERAL_CSTRING("X-SourceMap"), aResult);
   }
   return NS_SUCCEEDED(rv);
 }
 
+/* static */  bool
+nsContentUtils::IsMessageInputEvent(const IPC::Message& aMsg)
+{
+  if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart)
+      == mozilla::dom::PBrowser::PBrowserStart) {
+    switch (aMsg.type()) {
+      case mozilla::dom::PBrowser::Msg_RealMouseMoveEvent__ID:
+      case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
+      case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
+      case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
+      case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
+      case mozilla::dom::PBrowser::Msg_RealTouchMoveEvent__ID:
+      case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
+      case mozilla::dom::PBrowser::Msg_UpdateDimensions__ID:
+      case mozilla::dom::PBrowser::Msg_MouseEvent__ID:
+      case mozilla::dom::PBrowser::Msg_KeyEvent__ID:
+      case mozilla::dom::PBrowser::Msg_SetDocShellIsActive__ID:
+        return true;
+    }
+  }
+  return false;
+}
+
 static const char* kUserInteractionInactive = "user-interaction-inactive";
 static const char* kUserInteractionActive = "user-interaction-active";
 
 void
 nsContentUtils::UserInteractionObserver::Init()
 {
   // Listen for the observer messages from EventStateManager which are telling
   // us whether or not the user is interacting.
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -3121,16 +3121,23 @@ public:
    * precedence) response header in |aChannel|, set |aResult| to the
    * header's value and return true.  Otherwise, return false.
    *
    * @param aChannel The HTTP channel
    * @param aResult The string result.
    */
   static bool GetSourceMapURL(nsIHttpChannel* aChannel, nsACString& aResult);
 
+  /**
+   * Returns true if the passed-in mesasge is a pending InputEvent.
+   *
+   * @param aMsg  The message to check
+   */
+  static bool IsMessageInputEvent(const IPC::Message& aMsg);
+
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
   static void AsyncPrecreateStringBundles();
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -44,16 +44,17 @@
 #include "nsDocShell.h"
 #include "nsDocShellLoadTypes.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsCOMArray.h"
 #include "nsQueryObject.h"
 #include "nsDOMClassInfo.h"
 #include "mozilla/Services.h"
 #include "nsScreen.h"
+#include "ChildIterator.h"
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "nsIDOMNodeFilter.h"
 
 #include "nsIDOMStyleSheet.h"
@@ -12491,50 +12492,67 @@ nsDocument::AddSizeOfExcludingThis(SizeO
 {
   // This AddSizeOfExcludingThis() overrides the one from nsINode.  But
   // nsDocuments can only appear at the top of the DOM tree, and we use the
   // specialized DocAddSizeOfExcludingThis() in that case.  So this should never
   // be called.
   MOZ_CRASH();
 }
 
+static void
+AddSizeOfNodeTree(nsIContent* aNode, nsWindowSizes& aWindowSizes)
+{
+  size_t nodeSize = 0;
+  aNode->AddSizeOfIncludingThis(aWindowSizes.mState, aWindowSizes.mStyleSizes,
+                                &nodeSize);
+
+  // This is where we transfer the nodeSize obtained from
+  // nsINode::AddSizeOfIncludingThis() to a value in nsWindowSizes.
+  switch (aNode->NodeType()) {
+  case nsIDOMNode::ELEMENT_NODE:
+    aWindowSizes.mDOMElementNodesSize += nodeSize;
+    break;
+  case nsIDOMNode::TEXT_NODE:
+    aWindowSizes.mDOMTextNodesSize += nodeSize;
+    break;
+  case nsIDOMNode::CDATA_SECTION_NODE:
+    aWindowSizes.mDOMCDATANodesSize += nodeSize;
+    break;
+  case nsIDOMNode::COMMENT_NODE:
+    aWindowSizes.mDOMCommentNodesSize += nodeSize;
+    break;
+  default:
+    aWindowSizes.mDOMOtherSize += nodeSize;
+    break;
+  }
+
+  if (EventListenerManager* elm = aNode->GetExistingListenerManager()) {
+    aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
+  }
+
+  AllChildrenIterator iter(aNode, nsIContent::eAllChildren);
+  for (nsIContent* n = iter.GetNextChild(); n; n = iter.GetNextChild()) {
+    AddSizeOfNodeTree(n, aWindowSizes);
+  }
+}
+
 void
 nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const
 {
+  // We use AllChildrenIterator to iterate over DOM nodes in
+  // AddSizeOfNodeTree(). The obvious place to start is at the document's root
+  // element, using GetRootElement(). However, that will miss comment nodes
+  // that are siblings of the root element. Instead we use
+  // GetFirstChild()/GetNextSibling() to traverse the document's immediate
+  // child nodes, calling AddSizeOfNodeTree() on each to measure them and then
+  // all their descendants. (The comment nodes won't have any descendants).
   for (nsIContent* node = nsINode::GetFirstChild();
        node;
-       node = node->GetNextNode(this))
-  {
-    size_t nodeSize = 0;
-    node->AddSizeOfIncludingThis(aWindowSizes.mState, aWindowSizes.mStyleSizes,
-                                 &nodeSize);
-
-    // This is where we transfer the nodeSize obtained from
-    // nsINode::AddSizeOfIncludingThis() to a value in nsWindowSizes.
-    switch (node->NodeType()) {
-    case nsIDOMNode::ELEMENT_NODE:
-      aWindowSizes.mDOMElementNodesSize += nodeSize;
-      break;
-    case nsIDOMNode::TEXT_NODE:
-      aWindowSizes.mDOMTextNodesSize += nodeSize;
-      break;
-    case nsIDOMNode::CDATA_SECTION_NODE:
-      aWindowSizes.mDOMCDATANodesSize += nodeSize;
-      break;
-    case nsIDOMNode::COMMENT_NODE:
-      aWindowSizes.mDOMCommentNodesSize += nodeSize;
-      break;
-    default:
-      aWindowSizes.mDOMOtherSize += nodeSize;
-      break;
-    }
-
-    if (EventListenerManager* elm = node->GetExistingListenerManager()) {
-      aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
-    }
+       node = node->GetNextSibling()) {
+    AddSizeOfNodeTree(node, aWindowSizes);
   }
 
   // IMPORTANT: for our ComputedValues measurements, we want to measure
   // ComputedValues accessible from DOM elements before ComputedValues not
   // accessible from DOM elements (i.e. accessible only from the frame tree).
   //
   // Therefore, the measurement of the nsIDocument superclass must happen after
   // the measurement of DOM nodes (above), because nsIDocument contains the
--- a/dom/base/nsIDOMClassInfo.h
+++ b/dom/base/nsIDOMClassInfo.h
@@ -7,17 +7,16 @@
 #ifndef nsIDOMClassInfo_h___
 #define nsIDOMClassInfo_h___
 
 #include "nsIXPCScriptable.h"
 
 #define DOM_BASE_SCRIPTABLE_FLAGS                                          \
   (XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY |                             \
    XPC_SCRIPTABLE_USE_JSSTUB_FOR_DELPROPERTY |                             \
-   XPC_SCRIPTABLE_USE_JSSTUB_FOR_SETPROPERTY |                             \
    XPC_SCRIPTABLE_ALLOW_PROP_MODS_TO_PROTOTYPE |                           \
    XPC_SCRIPTABLE_DONT_ASK_INSTANCE_FOR_SCRIPTABLE |                       \
    XPC_SCRIPTABLE_DONT_REFLECT_INTERFACE_NAMES)
 
 #define DEFAULT_SCRIPTABLE_FLAGS                                           \
   (DOM_BASE_SCRIPTABLE_FLAGS |                                             \
    XPC_SCRIPTABLE_WANT_RESOLVE |                                           \
    XPC_SCRIPTABLE_WANT_PRECREATE)
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -302,17 +302,17 @@ public:
 
   template<class T>
   using Sequence = mozilla::dom::Sequence<T>;
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_INODE_IID)
 
   // The |aNodeSize| outparam on this function is where the actual node size
   // value is put. It gets added to the appropriate value within |aSizes| by
-  // nsDocument::DocAddSizeOfExcludingThis().
+  // AddSizeOfNodeTree().
   //
   // Among the sub-classes that inherit (directly or indirectly) from nsINode,
   // measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - nsGenericHTMLElement:  mForm, mFieldSet
   // - nsGenericHTMLFrameElement: mFrameLoader (bug 672539)
   // - HTMLBodyElement:       mContentStyleRule
   // - HTMLDataListElement:   mOptions
--- a/dom/canvas/CanvasImageCache.cpp
+++ b/dom/canvas/CanvasImageCache.cpp
@@ -8,16 +8,17 @@
 #include "nsIImageLoadingContent.h"
 #include "nsExpirationTracker.h"
 #include "imgIRequest.h"
 #include "mozilla/dom/Element.h"
 #include "nsTHashtable.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "nsContentUtils.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/SystemGroup.h"
 #include "mozilla/gfx/2D.h"
 #include "gfx2DGlue.h"
 
 namespace mozilla {
 
 using namespace dom;
 using namespace gfx;
 
@@ -255,17 +256,18 @@ class CanvasImageCacheShutdownObserver f
 {
   ~CanvasImageCacheShutdownObserver() {}
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 };
 
 ImageCache::ImageCache()
-  : nsExpirationTracker<ImageCacheEntryData,4>(GENERATION_MS, "ImageCache")
+  : nsExpirationTracker<ImageCacheEntryData,4>(
+      GENERATION_MS, "ImageCache", SystemGroup::EventTargetFor(TaskCategory::Other))
   , mTotal(0)
 {
   if (!sPrefsInitialized) {
     sPrefsInitialized = true;
     Preferences::AddIntVarCache(&sCanvasImageCacheLimit, "canvas.image.cache.limit", 0);
   }
   mImageCacheObserver = new ImageCacheObserver(this);
   MOZ_RELEASE_ASSERT(mImageCacheObserver, "GFX: Can't alloc ImageCacheObserver");
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -713,17 +713,17 @@ WebGLContext::CreateAndInitGL(bool force
     bool tryANGLE = false;
 
     if (forceEnabled) {
         flags |= gl::CreateContextFlags::FORCE_ENABLE_HARDWARE;
     }
 
     if (IsWebGL2()) {
         flags |= gl::CreateContextFlags::PREFER_ES3;
-    } else {
+    } else if (!gfxPrefs::WebGL1AllowCoreProfile()) {
         flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
     }
 
     //////
 
     const bool useEGL = PR_GetEnv("MOZ_WEBGL_FORCE_EGL");
 
 #ifdef XP_WIN
--- a/dom/canvas/WebGLContextExtensions.cpp
+++ b/dom/canvas/WebGLContextExtensions.cpp
@@ -138,18 +138,17 @@ WebGLContext::IsExtensionSupported(WebGL
     case WebGLExtensionID::WEBGL_compressed_texture_etc1:
         return gl->IsExtensionSupported(gl::GLContext::OES_compressed_ETC1_RGB8_texture) &&
                !gl->IsANGLE();
     case WebGLExtensionID::WEBGL_compressed_texture_pvrtc:
         return gl->IsExtensionSupported(gl::GLContext::IMG_texture_compression_pvrtc);
     case WebGLExtensionID::WEBGL_compressed_texture_s3tc:
         return WebGLExtensionCompressedTextureS3TC::IsSupported(this);
     case WebGLExtensionID::WEBGL_compressed_texture_s3tc_srgb:
-        return WebGLExtensionCompressedTextureS3TC::IsSupported(this) &&
-               gl->IsExtensionSupported(gl::GLContext::EXT_texture_sRGB);
+        return WebGLExtensionCompressedTextureS3TC_SRGB::IsSupported(this);
     case WebGLExtensionID::WEBGL_debug_renderer_info:
         return Preferences::GetBool("webgl.enable-debug-renderer-info", false);
     case WebGLExtensionID::WEBGL_debug_shaders:
         return !nsContentUtils::ShouldResistFingerprinting();
     case WebGLExtensionID::WEBGL_lose_context:
         // We always support this extension.
         return true;
 
--- a/dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp
@@ -40,20 +40,20 @@ WebGLExtensionCompressedTextureS3TC::Web
 
 WebGLExtensionCompressedTextureS3TC::~WebGLExtensionCompressedTextureS3TC()
 {
 }
 
 bool
 WebGLExtensionCompressedTextureS3TC::IsSupported(const WebGLContext* webgl)
 {
-   gl::GLContext* gl = webgl->GL();
-   if (gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_s3tc))
-      return true;
+    gl::GLContext* gl = webgl->GL();
+    if (gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_s3tc))
+        return true;
 
-   return gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_dxt1) &&
-          gl->IsExtensionSupported(gl::GLContext::ANGLE_texture_compression_dxt3) &&
-          gl->IsExtensionSupported(gl::GLContext::ANGLE_texture_compression_dxt5);
+    return gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_dxt1) &&
+           gl->IsExtensionSupported(gl::GLContext::ANGLE_texture_compression_dxt3) &&
+           gl->IsExtensionSupported(gl::GLContext::ANGLE_texture_compression_dxt5);
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionCompressedTextureS3TC, WEBGL_compressed_texture_s3tc)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLExtensionCompressedTextureS3TC_SRGB.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureS3TC_SRGB.cpp
@@ -37,11 +37,24 @@ WebGLExtensionCompressedTextureS3TC_SRGB
 
 #undef FOO
 }
 
 WebGLExtensionCompressedTextureS3TC_SRGB::~WebGLExtensionCompressedTextureS3TC_SRGB()
 {
 }
 
+bool
+WebGLExtensionCompressedTextureS3TC_SRGB::IsSupported(const WebGLContext* webgl)
+{
+    gl::GLContext* gl = webgl->GL();
+    if (gl->IsGLES())
+        return gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_s3tc_srgb);
+
+    // Desktop GL is more complicated: It's EXT_texture_sRGB, when
+    // EXT_texture_compression_s3tc is supported, that enables srgb+s3tc.
+    return gl->IsExtensionSupported(gl::GLContext::EXT_texture_sRGB) &&
+           gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_s3tc);
+}
+
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionCompressedTextureS3TC_SRGB, WEBGL_compressed_texture_s3tc_srgb)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLExtensionDrawBuffers.cpp
+++ b/dom/canvas/WebGLExtensionDrawBuffers.cpp
@@ -43,21 +43,18 @@ WebGLExtensionDrawBuffers::DrawBuffersWE
     mContext->DrawBuffers(buffers);
 }
 
 bool
 WebGLExtensionDrawBuffers::IsSupported(const WebGLContext* webgl)
 {
     gl::GLContext* gl = webgl->GL();
 
-    if (!gl->IsExtensionSupported(gl::GLContext::ARB_draw_buffers) &&
-        !gl->IsExtensionSupported(gl::GLContext::EXT_draw_buffers))
-    {
+    if (!gl->IsSupported(gl::GLFeature::draw_buffers))
         return false;
-    }
 
     // WEBGL_draw_buffers requires at least 4 color attachments.
     if (webgl->mGLMaxDrawBuffers < webgl->kMinMaxDrawBuffers ||
         webgl->mGLMaxColorAttachments < webgl->kMinMaxColorAttachments)
     {
         return false;
     }
 
--- a/dom/canvas/WebGLExtensions.h
+++ b/dom/canvas/WebGLExtensions.h
@@ -133,16 +133,18 @@ public:
 
 class WebGLExtensionCompressedTextureS3TC_SRGB
     : public WebGLExtensionBase
 {
 public:
     explicit WebGLExtensionCompressedTextureS3TC_SRGB(WebGLContext*);
     virtual ~WebGLExtensionCompressedTextureS3TC_SRGB();
 
+    static bool IsSupported(const WebGLContext*);
+
     DECL_WEBGL_EXTENSION_GOOP
 };
 
 class WebGLExtensionDebugRendererInfo
     : public WebGLExtensionBase
 {
 public:
     explicit WebGLExtensionDebugRendererInfo(WebGLContext*);
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -7611,17 +7611,17 @@ fail-if = (os == 'mac') || (os == 'linux
 skip-if = (os == 'win')
 [generated/test_conformance__extensions__webgl-debug-renderer-info.html]
 [generated/test_conformance__extensions__webgl-debug-shaders.html]
 [generated/test_conformance__extensions__webgl-depth-texture.html]
 [generated/test_conformance__extensions__webgl-draw-buffers-framebuffer-unsupported.html]
 [generated/test_conformance__extensions__webgl-draw-buffers-max-draw-buffers.html]
 [generated/test_conformance__extensions__webgl-draw-buffers.html]
 skip-if = (os == 'linux')
-fail-if = (os == 'mac') || (os == 'win')
+fail-if = (os == 'win')
 [generated/test_conformance__extensions__webgl-shared-resources.html]
 [generated/test_conformance__glsl__bugs__angle-ambiguous-function-call.html]
 [generated/test_conformance__glsl__bugs__angle-constructor-invalid-parameters.html]
 [generated/test_conformance__glsl__bugs__angle-d3d11-compiler-error.html]
 [generated/test_conformance__glsl__bugs__angle-dx-variable-bug.html]
 [generated/test_conformance__glsl__bugs__array-of-struct-with-int-first-position.html]
 skip-if = (os == 'android')
 [generated/test_conformance__glsl__bugs__bool-type-cast-bug-int-float.html]
@@ -7908,17 +7908,16 @@ fail-if = (os == 'android')
 [generated/test_conformance__glsl__misc__shader-with-version-100.vert.html]
 [generated/test_conformance__glsl__misc__shader-with-version-120.vert.html]
 [generated/test_conformance__glsl__misc__shader-with-version-130.vert.html]
 [generated/test_conformance__glsl__misc__shader-with-webgl-identifier.vert.html]
 [generated/test_conformance__glsl__misc__shader-with-while-loop.html]
 [generated/test_conformance__glsl__misc__shader-without-precision.frag.html]
 [generated/test_conformance__glsl__misc__shaders-with-constant-expression-loop-conditions.html]
 [generated/test_conformance__glsl__misc__shaders-with-invariance.html]
-fail-if = (os == 'mac')
 [generated/test_conformance__glsl__misc__shaders-with-mis-matching-uniforms.html]
 [generated/test_conformance__glsl__misc__shaders-with-mis-matching-varyings.html]
 [generated/test_conformance__glsl__misc__shaders-with-missing-varyings.html]
 [generated/test_conformance__glsl__misc__shaders-with-name-conflicts.html]
 [generated/test_conformance__glsl__misc__shaders-with-uniform-structs.html]
 [generated/test_conformance__glsl__misc__shaders-with-varyings.html]
 [generated/test_conformance__glsl__misc__shared.html]
 [generated/test_conformance__glsl__misc__struct-assign.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -75,17 +75,17 @@ skip-if = (os == 'win')
 # Complicated
 
 [generated/test_conformance__context__context-attributes-alpha-depth-stencil-antialias.html]
 fail-if = (os == 'mac' && os_version == '10.6')
 # Asserts on 'B2G ICS Emulator Debug' and linux debug. Crashes on Android.
 skip-if = (os == 'b2g') || (os == 'linux') || (os == 'android')
 
 [generated/test_conformance__extensions__webgl-draw-buffers.html]
-fail-if = (os == 'mac') || (os == 'win')
+fail-if = (os == 'win')
 # Crashes
 skip-if = (os == 'linux')
 
 [generated/test_conformance__glsl__constructors__glsl-construct-bvec3.html]
 # Crashes from libglsl.so
 # application crashed [@ jemalloc_crash] on Android
 skip-if = (os == 'linux') || (os == 'mac') || (os == 'android')
 [generated/test_conformance__glsl__constructors__glsl-construct-bvec4.html]
@@ -667,18 +667,16 @@ fail-if = (os == 'mac' && os_version == 
 fail-if = (os == 'mac' && os_version == '10.8')
 [generated/test_conformance__limits__gl-max-texture-dimensions.html]
 fail-if = (os == 'mac' && os_version == '10.8')
 
 ####################
 # failure on OSX
 [generated/test_conformance__extensions__angle-instanced-arrays.html]
 fail-if = (os == 'mac')
-[generated/test_conformance__glsl__misc__shaders-with-invariance.html]
-fail-if = (os == 'mac')
 [generated/test_2_conformance2__textures__misc__tex-unpack-params.html]
 skip-if = (os == 'mac' && debug)
 fail-if = (os == 'mac')
 [generated/test_2_conformance2__glsl3__valid-invariant.html]
 fail-if = (os == 'mac')
 [generated/test_2_conformance2__reading__format-r11f-g11f-b10f.html]
 fail-if = (os == 'mac')
 [generated/test_2_conformance2__rendering__blitframebuffer-filter-outofbounds.html]
--- a/dom/canvas/test/webgl-mochitest/driver-info.js
+++ b/dom/canvas/test/webgl-mochitest/driver-info.js
@@ -1,169 +1,136 @@
 DriverInfo = (function() {
   // ---------------------------------------------------------------------------
   // Debug info handling
 
-  function defaultInfoFunc(str) {
-    console.log('Info: ' + str);
-  }
-
-  var gInfoFunc = defaultInfoFunc;
-  function setInfoFunc(func) {
-    gInfoFunc = func;
-  }
-
   function info(str) {
-    gInfoFunc(str);
+    window.console.log('Info: ' + str);
   }
 
   // ---------------------------------------------------------------------------
   // OS and driver identification
   //   Stolen from dom/canvas/test/webgl/test_webgl_conformance_test_suite.html
   function detectDriverInfo() {
-    try {
-      var cc = SpecialPowers.Cc;
-    } catch (e) {
-      throw 'No SpecialPowers!';
-    }
-
-    const Cc = SpecialPowers.Cc;
-    const Ci = SpecialPowers.Ci;
-    var doc = Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser).parseFromString("<html/>", "text/html");
-
-    var canvas = doc.createElement("canvas");
+    var canvas = document.createElement("canvas");
     canvas.width = 1;
     canvas.height = 1;
 
     var type = "";
     var gl = null;
     try {
       gl = canvas.getContext("experimental-webgl");
     } catch(e) {}
 
     if (!gl) {
       info('Failed to create WebGL context for querying driver info.');
       throw 'WebGL failed';
     }
 
     var ext = gl.getExtension("WEBGL_debug_renderer_info");
-    // this extension is unconditionally available to chrome. No need to check.
+    if (!ext)
+      throw 'WEBGL_debug_renderer_info not available';
 
     var webglRenderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
-    var webglVendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL);
-    return [webglVendor, webglRenderer];
+    return webglRenderer;
   }
 
   function detectOSInfo() {
-    try {
-      var cc = SpecialPowers.Cc;
-    } catch (e) {
-      throw 'No SpecialPowers!';
-    }
-
-    const Cc = SpecialPowers.Cc;
-    const Ci = SpecialPowers.Ci;
-
-    // From reftest.js:
-    var runtime = Cc['@mozilla.org/xre/app-info;1'].getService(Ci.nsIXULRuntime);
-
     var os = null;
     var version = null;
     if (navigator.platform.indexOf('Win') == 0) {
       os = OS.WINDOWS;
 
-      // code borrowed from browser/modules/test/browser_taskbar_preview.js
-      version = SpecialPowers.Services.sysinfo.getProperty('version');
-      version = parseFloat(version);
+      var versionMatch = /Windows NT (\d+.\d+)/.exec(navigator.userAgent);
+      version = versionMatch ? parseFloat(versionMatch[1]) : null;
       // Version 6.0 is Vista, 6.1 is 7.
 
     } else if (navigator.platform.indexOf('Mac') == 0) {
       os = OS.MAC;
 
       var versionMatch = /Mac OS X (\d+.\d+)/.exec(navigator.userAgent);
       version = versionMatch ? parseFloat(versionMatch[1]) : null;
 
-    } else if (runtime.widgetToolkit == 'gonk') {
-      os = OS.B2G;
-
     } else if (navigator.appVersion.indexOf('Android') != -1) {
       os = OS.ANDROID;
-      // From layout/tools/reftest/reftest.js:
-      version = SpecialPowers.Services.sysinfo.getProperty('version');
+
+      try {
+        // From layout/tools/reftest/reftest.js:
+        version = SpecialPowers.Services.sysinfo.getProperty('version');
+      } catch (e) {
+        info('No SpecialPowers: can\'t query android version');
+      }
 
     } else if (navigator.platform.indexOf('Linux') == 0) {
       // Must be checked after android, as android also has a 'Linux' platform string.
       os = OS.LINUX;
     }
 
     return [os, version];
   }
 
   var OS = {
     WINDOWS: 'windows',
     MAC: 'mac',
     LINUX: 'linux',
     ANDROID: 'android',
-    B2G: 'b2g',
   };
 
   var DRIVER = {
+    INTEL: 'intel',
     MESA: 'mesa',
     NVIDIA: 'nvidia',
     ANDROID_X86_EMULATOR: 'android x86 emulator',
     ANGLE: 'angle',
   };
 
   var kOS = null;
   var kOSVersion = null;
+  var kRawDriver = null;
   var kDriver = null;
 
   try {
     [kOS, kOSVersion] = detectOSInfo();
   } catch (e) {
     // Generally just fails when we don't have SpecialPowers.
   }
 
   try {
-    var glVendor, glRenderer;
-    [glVendor, glRenderer] = detectDriverInfo();
-    info('GL vendor: ' + glVendor);
-    info('GL renderer: ' + glRenderer);
+    kRawDriver = detectDriverInfo();
 
-    if (glRenderer.includes('llvmpipe')) {
+    if (kRawDriver.includes('llvmpipe')) {
       kDriver = DRIVER.MESA;
-    } else if (glRenderer.includes('Android Emulator')) {
+    } else if (kRawDriver.includes('Android Emulator')) {
       kDriver = DRIVER.ANDROID_X86_EMULATOR;
-    } else if (glRenderer.includes('ANGLE')) {
+    } else if (kRawDriver.includes('ANGLE')) {
       kDriver = DRIVER.ANGLE;
-    } else if (glVendor.includes('NVIDIA')) {
+    } else if (kRawDriver.includes('NVIDIA')) {
       kDriver = DRIVER.NVIDIA;
+    } else if (kRawDriver.includes('Intel')) {
+      kDriver = DRIVER.INTEL;
     }
   } catch (e) {
     // detectDriverInfo is fallible where WebGL fails.
   }
 
-  if (kOS) {
-    info('OS detected as: ' + kOS);
-    info('  Version: ' + kOSVersion);
-  } else {
-    info('OS not detected.');
-    info('  `platform`:   ' + navigator.platform);
-    info('  `appVersion`: ' + navigator.appVersion);
-    info('  `userAgent`:  ' + navigator.userAgent);
-  }
-  if (kDriver) {
-    info('GL driver detected as: ' + kDriver);
-  } else {
-    info('GL driver not detected.');
+  function dump(line_out_func) {
+    let lines = [
+      '[DriverInfo] userAgent: ' + navigator.userAgent,
+      '[DriverInfo] kRawDriver: ' + kRawDriver,
+      '[DriverInfo] kDriver: ' + kDriver,
+      '[DriverInfo] kOS: ' + kOS,
+      '[DriverInfo] kOSVersion: ' + kOSVersion,
+    ];
+    lines.forEach(line_out_func);
   }
 
+  dump(x => console.log(x));
+
   return {
-    setInfoFunc: setInfoFunc,
-
+    DRIVER: DRIVER,
     OS: OS,
-    DRIVER: DRIVER,
+    dump: dump,
+    getDriver: function() { return kDriver; },
     getOS: function() { return kOS; },
-    getDriver: function() { return kDriver; },
     getOSVersion: function() { return kOSVersion; },
   };
 })();
 
--- a/dom/canvas/test/webgl-mochitest/mochitest.ini
+++ b/dom/canvas/test/webgl-mochitest/mochitest.ini
@@ -14,17 +14,17 @@ support-files =
 
 [ensure-exts/test_ANGLE_instanced_arrays.html]
 fail-if = (os == 'android') || (os == 'mac' && os_version == '10.6')
 [ensure-exts/test_EXT_blend_minmax.html]
 fail-if = (os == 'android')
 [ensure-exts/test_EXT_color_buffer_half_float.html]
 fail-if = (os == 'android')
 [ensure-exts/test_EXT_disjoint_timer_query.html]
-fail-if = (os == 'android') || (os == 'mac') || (os == 'win' && os_version == '5.1')
+fail-if = (os == 'android') || (os == 'win' && os_version == '5.1')
 [ensure-exts/test_EXT_frag_depth.html]
 fail-if = (os == 'android')
 [ensure-exts/test_EXT_sRGB.html]
 fail-if = (os == 'android') || (os == 'mac' && os_version == '10.6') || (os == 'win')
 [ensure-exts/test_EXT_shader_texture_lod.html]
 fail-if = (os == 'android')
 [ensure-exts/test_EXT_texture_filter_anisotropic.html]
 fail-if = (os == 'android') || (os == 'linux')
@@ -97,14 +97,14 @@ skip-if = toolkit == 'android' #bug 8654
 [test_webgl2_invalidate_framebuffer.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [test_webgl2_alpha_luminance.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [test_fuzzing_bugs.html]
 [test_video_fastpath_mp4.html]
 fail-if = (os == 'mac') || (os == 'win' && !(e10s && os_version == '6.1')) # no fast path on windows yet (bug 1373165 or 1373770), and mac (bug 1373669)
 [test_video_fastpath_theora.html]
-fail-if = (os == 'mac') || (os == 'win' && os_version != '6.1') # no fast path on windows yet (bug 1373192), and mac (bug 1373702)
+fail-if = (os == 'win' && os_version != '6.1') # no fast path on windows yet (bug 1373192), and mac (bug 1373702)
 [test_video_fastpath_vp8.html]
-fail-if = (os == 'mac') || (os == 'win' && os_version != '6.1') # no fast path on windows yet (bug 1373192), and mac (bug 1373702)
+fail-if = (os == 'win' && os_version != '6.1') # no fast path on windows yet (bug 1373192), and mac (bug 1373702)
 [test_video_fastpath_vp9.html]
-fail-if = (os == 'mac') || (os == 'win' && os_version != '6.1') # no fast path on windows yet (bug 1373192), and mac (bug 1373702)
+fail-if = (os == 'win' && os_version != '6.1') # no fast path on windows yet (bug 1373192), and mac (bug 1373702)
 [test_webglcontextcreationerror.html]
--- a/dom/canvas/test/webgl-mochitest/test_no_arr_points.html
+++ b/dom/canvas/test/webgl-mochitest/test_no_arr_points.html
@@ -38,16 +38,18 @@ void main(void) {
 // Give ourselves a scope to return early from:
 (function() {
   var gl = WebGLUtil.getWebGL('c');
   if (!gl) {
     todo(false, 'WebGL is unavailable.');
     return;
   }
 
+  DriverInfo.dump(x => ok(true, x));
+
   function errorFunc(str) {
     ok(false, 'Error: ' + str);
   }
   WebGLUtil.setErrorFunc(errorFunc);
   WebGLUtil.setWarningFunc(errorFunc);
 
   var attribProg   = WebGLUtil.createProgramByIds(gl, 'vs-attrib',    'fs');
   var noAttribProg = WebGLUtil.createProgramByIds(gl, 'vs-no-attrib', 'fs');
@@ -112,25 +114,25 @@ void main(void) {
     gl.drawArrays(gl.POINTS, hugeFirst, 1);
     ok(!isScreenBlack(), '[' + info + '] drawArrays[huge first] should color pixels.');
 
     checkGLError(ok, info);
 
     var elemTestFunc = todo; // We fail on most implementations.
     var checkGLTestFunc = todo;
     if (DriverInfo.getDriver() == DriverInfo.DRIVER.ANGLE ||
+        (DriverInfo.getOS() == DriverInfo.OS.MAC &&
+         DriverInfo.getDriver() == DriverInfo.DRIVER.INTEL) ||
         DriverInfo.getOS() == DriverInfo.OS.ANDROID)
     {
-      // ANGLE and Android slaves seem to work fine.
+      // These seem to work fine.
       elemTestFunc = ok;
       checkGLTestFunc = ok;
     }
-    if (DriverInfo.getDriver() == DriverInfo.DRIVER.ANDROID_X86_EMULATOR ||
-        DriverInfo.getOS() == DriverInfo.OS.B2G)
-    {
+    if (DriverInfo.getDriver() == DriverInfo.DRIVER.ANDROID_X86_EMULATOR) {
       // ...but the Android 4.2 x86 emulator environment is different
       elemTestFunc = todo;
       checkGLTestFunc = ok;
     }
 
     // Now for drawElements:
     gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());
     gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexArr, gl.STATIC_DRAW);
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -486,16 +486,39 @@ private:
 
   void
   ActorFailed() override
   {
     MOZ_CRASH("Failed to create a PBackgroundChild actor!");
   }
 };
 
+#ifdef NIGHTLY_BUILD
+/**
+ * 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 HangMonitor::Annotator
+{
+public:
+  virtual void AnnotateHang(HangMonitor::HangAnnotations& aAnnotations)
+  {
+    int32_t pending = ContentChild::GetSingleton()->GetPendingInputEvents();
+    if (pending > 0) {
+      aAnnotations.AddAnnotation(NS_LITERAL_STRING("PendingInput"), pending);
+    }
+  }
+
+  static PendingInputEventHangAnnotator sSingleton;
+};
+PendingInputEventHangAnnotator PendingInputEventHangAnnotator::sSingleton;
+#endif
+
 NS_IMPL_ISUPPORTS(BackgroundChildPrimer, nsIIPCBackgroundChildCreateCallback)
 
 ContentChild* ContentChild::sSingleton;
 
 ContentChild::ContentChild()
  : mID(uint64_t(-1))
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
  , mMainChromeTid(0)
@@ -633,16 +656,20 @@ ContentChild::Init(MessageLoop* aIOLoop,
   CrashReporterClient::InitSingleton(this);
 #endif
 
   mID = aChildID;
   mIsForBrowser = aIsForBrowser;
 
   SetProcessName(NS_LITERAL_STRING("Web Content"));
 
+#ifdef NIGHTLY_BUILD
+  HangMonitor::RegisterAnnotator(PendingInputEventHangAnnotator::sSingleton);
+#endif
+
   return true;
 }
 
 void
 ContentChild::SetProcessName(const nsAString& aName)
 {
   char* name;
   if ((name = PR_GetEnv("MOZ_DEBUG_APP_PROCESS")) &&
@@ -2925,16 +2952,20 @@ ContentChild::RecvShutdown()
       NewRunnableMethod(
         "dom::ContentChild::RecvShutdown", this, &ContentChild::RecvShutdown),
       100);
     return IPC_OK();
   }
 
   mShuttingDown = true;
 
+#ifdef NIGHTLY_BUILD
+  HangMonitor::UnregisterAnnotator(PendingInputEventHangAnnotator::sSingleton);
+#endif
+
   if (mPolicy) {
     mPolicy->Deactivate();
     mPolicy = nullptr;
   }
 
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     os->NotifyObservers(static_cast<nsIContentChild*>(this),
@@ -3575,16 +3606,43 @@ ContentChild::GetSpecificMessageEventTar
       || aMsg.type() == PJavaScript::Msg_DropObject__ID
       || aMsg.type() == PContent::Msg_NotifyVisited__ID) {
     return do_AddRef(SystemGroup::EventTargetFor(TaskCategory::Other));
   }
 
   return nullptr;
 }
 
+#ifdef NIGHTLY_BUILD
+void
+ContentChild::OnChannelReceivedMessage(const Message& aMsg)
+{
+  if (nsContentUtils::IsMessageInputEvent(aMsg)) {
+    mPendingInputEvents++;
+  }
+}
+
+PContentChild::Result
+ContentChild::OnMessageReceived(const Message& aMsg)
+{
+  if (nsContentUtils::IsMessageInputEvent(aMsg)) {
+    DebugOnly<uint32_t> prevEvts = mPendingInputEvents--;
+    MOZ_ASSERT(prevEvts > 0);
+  }
+
+  return PContentChild::OnMessageReceived(aMsg);
+}
+
+PContentChild::Result
+ContentChild::OnMessageReceived(const Message& aMsg, Message*& aReply)
+{
+  return PContentChild::OnMessageReceived(aMsg, aReply);
+}
+#endif
+
 } // namespace dom
 
 #if !defined(XP_WIN)
 bool IsDevelopmentBuild()
 {
   nsCOMPtr<nsIFile> path = mozilla::Omnijar::GetPath(mozilla::Omnijar::GRE);
   // If the path doesn't exist, we're a dev build.
   return path == nullptr;
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -676,30 +676,52 @@ public:
 
   virtual already_AddRefed<nsIEventTarget> GetEventTargetFor(TabChild* aTabChild) override;
 
   mozilla::ipc::IPCResult
   RecvSetPluginList(const uint32_t& aPluginEpoch,
                     nsTArray<PluginTag>&& aPluginTags,
                     nsTArray<FakePluginTag>&& aFakePluginTags) override;
 
+#ifdef NIGHTLY_BUILD
+  // Fetch the current number of pending input events.
+  //
+  // NOTE: This method performs an atomic read, and is safe to call from all threads.
+  uint32_t
+  GetPendingInputEvents()
+  {
+    return mPendingInputEvents;
+  }
+#endif
+
 private:
   static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
   void StartForceKillTimer();
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   virtual void ProcessingError(Result aCode, const char* aReason) override;
 
   virtual already_AddRefed<nsIEventTarget>
   GetConstructedEventTarget(const Message& aMsg) override;
 
   virtual already_AddRefed<nsIEventTarget>
   GetSpecificMessageEventTarget(const Message& aMsg) override;
 
+#ifdef NIGHTLY_BUILD
+  virtual void
+  OnChannelReceivedMessage(const Message& aMsg) override;
+
+  virtual PContentChild::Result
+  OnMessageReceived(const Message& aMsg) override;
+
+  virtual PContentChild::Result
+  OnMessageReceived(const Message& aMsg, Message*& aReply) override;
+#endif
+
   InfallibleTArray<nsAutoPtr<AlertObserver> > mAlertObservers;
   RefPtr<ConsoleListener> mConsoleListener;
 
   nsTHashtable<nsPtrHashKey<nsIObserver>> mIdleObservers;
 
   InfallibleTArray<nsString> mAvailableDictionaries;
 
   // Temporary storage for a list of available font families, passed from the
@@ -760,16 +782,21 @@ private:
   // These items are removed when RecvFileCreationResponse is received.
   nsRefPtrHashtable<nsIDHashKey, FileCreatorHelper> mFileCreationPending;
 
 
   nsClassHashtable<nsUint64HashKey, AnonymousTemporaryFileCallback> mPendingAnonymousTemporaryFiles;
 
   mozilla::Atomic<bool> mShuttingDown;
 
+#ifdef NIGHTLY_BUILD
+  // NOTE: This member is atomic because it can be accessed from off-main-thread.
+  mozilla::Atomic<uint32_t> mPendingInputEvents;
+#endif
+
   DISALLOW_EVIL_CONSTRUCTORS(ContentChild);
 };
 
 uint64_t
 NextWindowID();
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5433,8 +5433,24 @@ ContentParent::RecvDeviceReset()
 {
   GPUProcessManager* pm = GPUProcessManager::Get();
   if (pm) {
     pm->SimulateDeviceReset();
   }
 
   return IPC_OK();
 }
+
+mozilla::ipc::IPCResult
+ContentParent::RecvBHRThreadHang(const HangDetails& aDetails)
+{
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    // Copy the HangDetails recieved over the network into a nsIHangDetails, and
+    // then fire our own observer notification.
+    // XXX: We should be able to avoid this potentially expensive copy here by
+    // moving our deserialized argument.
+    nsCOMPtr<nsIHangDetails> hangDetails =
+      new nsHangDetails(HangDetails(aDetails));
+    obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr);
+  }
+  return IPC_OK();
+}
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -1162,16 +1162,19 @@ private:
   virtual mozilla::ipc::IPCResult RecvUpdateChildScalars(
     InfallibleTArray<ScalarAction>&& aScalarActions) override;
   virtual mozilla::ipc::IPCResult RecvUpdateChildKeyedScalars(
     InfallibleTArray<KeyedScalarAction>&& aScalarActions) override;
   virtual mozilla::ipc::IPCResult RecvRecordChildEvents(
     nsTArray<ChildEventData>&& events) override;
   virtual mozilla::ipc::IPCResult RecvRecordDiscardedData(
     const DiscardedData& aDiscardedData) override;
+
+  virtual mozilla::ipc::IPCResult RecvBHRThreadHang(
+    const HangDetails& aHangDetails) override;
 public:
   void SendGetFilesResponseAndForget(const nsID& aID,
                                      const GetFilesResponseResult& aResult);
 
   bool SendRequestMemoryReport(const uint32_t& aGeneration,
                                const bool& aAnonymize,
                                const bool& aMinimizeMemoryUsage,
                                const MaybeFileDesc& aDMDFile) override;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -91,16 +91,17 @@ using mozilla::layers::CompositorOptions
 using struct mozilla::dom::FlyWebPublishOptions from "mozilla/dom/FlyWebPublishOptionsIPCSerializer.h";
 using mozilla::Telemetry::Accumulation from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedAccumulation from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
 using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
+using mozilla::HangDetails from "mozilla/HangDetails.h";
 
 union ChromeRegistryItem
 {
     ChromePackage;
     OverrideMapping;
     SubstitutionMapping;
 };
 
@@ -1050,16 +1051,18 @@ parent:
     sync GetA11yContentId() returns (uint32_t aContentId);
     async A11yHandlerControl(uint32_t aPid,
                              IHandlerControlHolder aHandlerControl);
 
     async AddMemoryReport(MemoryReport aReport);
     async FinishMemoryReport(uint32_t aGeneration);
 
     async MaybeReloadPlugins();
+
+    async BHRThreadHang(HangDetails aHangDetails);
 both:
      async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
                         Principal aPrincipal, ClonedMessageData aData);
 
     /**
      * Notify `push-subscription-modified` observers in the parent and child.
      */
     async NotifyPushSubscriptionModifiedObservers(nsCString scope,
--- a/dom/media/webaudio/ConvolverNode.cpp
+++ b/dom/media/webaudio/ConvolverNode.cpp
@@ -267,33 +267,16 @@ ConvolverNode::SetBuffer(JSContext* aCx,
 
   // Send the buffer to the stream
   AudioNodeStream* ns = mStream;
   MOZ_ASSERT(ns, "Why don't we have a stream here?");
   if (mBuffer) {
     uint32_t length = mBuffer->Length();
     RefPtr<ThreadSharedFloatArrayBufferList> data =
       mBuffer->GetThreadSharedChannelsForRate(aCx);
-    if (data && length < WEBAUDIO_BLOCK_SIZE) {
-      // For very small impulse response buffers, we need to pad the
-      // buffer with 0 to make sure that the Reverb implementation
-      // has enough data to compute FFTs from.
-      length = WEBAUDIO_BLOCK_SIZE;
-      RefPtr<ThreadSharedFloatArrayBufferList> paddedBuffer =
-        new ThreadSharedFloatArrayBufferList(data->GetChannels());
-      void* channelData = malloc(sizeof(float) * length * data->GetChannels() + 15);
-      float* alignedChannelData = ALIGNED16(channelData);
-      ASSERT_ALIGNED16(alignedChannelData);
-      for (uint32_t i = 0; i < data->GetChannels(); ++i) {
-        PodCopy(alignedChannelData + length * i, data->GetData(i), mBuffer->Length());
-        PodZero(alignedChannelData + length * i + mBuffer->Length(), WEBAUDIO_BLOCK_SIZE - mBuffer->Length());
-        paddedBuffer->SetData(i, (i == 0) ? channelData : nullptr, free, alignedChannelData);
-      }
-      data = paddedBuffer;
-    }
     SendInt32ParameterToStream(ConvolverNodeEngine::BUFFER_LENGTH, length);
     SendDoubleParameterToStream(ConvolverNodeEngine::SAMPLE_RATE,
                                 mBuffer->SampleRate());
     ns->SetBuffer(data.forget());
   } else {
     ns->SetBuffer(nullptr);
   }
 }
--- a/dom/media/webaudio/blink/Reverb.cpp
+++ b/dom/media/webaudio/blink/Reverb.cpp
@@ -33,17 +33,17 @@
 #include "ReverbConvolver.h"
 #include "mozilla/FloatingPoint.h"
 
 using namespace mozilla;
 
 namespace WebCore {
 
 // Empirical gain calibration tested across many impulse responses to ensure perceived volume is same as dry (unprocessed) signal
-const float GainCalibration = -58;
+const float GainCalibration = 0.00125f;
 const float GainCalibrationSampleRate = 44100;
 
 // A minimum power value to when normalizing a silent (or very quiet) impulse response
 const float MinPower = 0.000125f;
 
 static float calculateNormalizationScale(ThreadSharedFloatArrayBufferList* response, size_t aLength, float sampleRate)
 {
     // Normalize by RMS power
@@ -59,17 +59,17 @@ static float calculateNormalizationScale
     power = sqrt(power / (numberOfChannels * aLength));
 
     // Protect against accidental overload
     if (!IsFinite(power) || IsNaN(power) || power < MinPower)
         power = MinPower;
 
     float scale = 1 / power;
 
-    scale *= powf(10, GainCalibration * 0.05f); // calibrate to make perceived volume same as unprocessed
+    scale *= GainCalibration; // calibrate to make perceived volume same as unprocessed
 
     // Scale depends on sample-rate.
     if (sampleRate)
         scale *= GainCalibrationSampleRate / sampleRate;
 
     // True-stereo compensation
     if (response->GetChannels() == 4)
         scale *= 0.5f;
--- a/dom/payments/test/mochitest.ini
+++ b/dom/payments/test/mochitest.ini
@@ -5,14 +5,15 @@ scheme = https
 support-files =
   simple_payment_request.html
   BasiccardChromeScript.js
   ConstructorChromeScript.js
   GeneralChromeScript.js
   ShowPaymentChromeScript.js
 
 [test_abortPayment.html]
+run-if = nightly_build # Bug 1390018: Depends on the Nightly-only UI service
 [test_basiccard.html]
 [test_canMakePayment.html]
 [test_constructor.html]
 [test_showPayment.html]
 [test_validate_decimal_value.html]
 [test_payment-request-in-iframe.html]
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -735,17 +735,17 @@ nsCSPContext::EnsureEventTarget(nsIEvent
     return NS_OK;
   }
 
   mEventTarget = aEventTarget;
   return NS_OK;
 }
 
 struct ConsoleMsgQueueElem {
-  nsXPIDLString mMsg;
+  nsString      mMsg;
   nsString      mSourceName;
   nsString      mSourceLine;
   uint32_t      mLineNumber;
   uint32_t      mColumnNumber;
   uint32_t      mSeverityFlag;
 };
 
 void
--- a/dom/security/test/gtest/TestCSPParser.cpp
+++ b/dom/security/test/gtest/TestCSPParser.cpp
@@ -14,17 +14,16 @@
 #define nsAString_h___
 #define nsString_h___
 #define nsStringFwd_h___
 #define nsReadableUtils_h___
 class nsACString;
 class nsAString;
 class nsString;
 class nsCString;
-class nsXPIDLString;
 template<class T> class nsReadingIterator;
 #endif
 
 #include "nsIContentSecurityPolicy.h"
 #include "nsNetUtil.h"
 #include "mozilla/dom/nsCSPContext.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -146,16 +146,17 @@ static const char* const sExtensionNames
     "GL_EXT_read_format_bgra",
     "GL_EXT_robustness",
     "GL_EXT_sRGB",
     "GL_EXT_sRGB_write_control",
     "GL_EXT_shader_texture_lod",
     "GL_EXT_texture3D",
     "GL_EXT_texture_compression_dxt1",
     "GL_EXT_texture_compression_s3tc",
+    "GL_EXT_texture_compression_s3tc_srgb",
     "GL_EXT_texture_filter_anisotropic",
     "GL_EXT_texture_format_BGRA8888",
     "GL_EXT_texture_sRGB",
     "GL_EXT_texture_storage",
     "GL_EXT_timer_query",
     "GL_EXT_transform_feedback",
     "GL_EXT_unpack_subimage",
     "GL_IMG_read_format",
@@ -1734,16 +1735,23 @@ GLContext::InitExtensions()
         // and Intel HD 5000/Iris that I tested.
         // Bug 1124996: Appears to be the same on OSX Yosemite (10.10)
         if (nsCocoaFeatures::OSXVersionMajor() == 10 &&
             nsCocoaFeatures::OSXVersionMinor() >= 9 &&
             Renderer() == GLRenderer::IntelHD3000)
         {
             MarkExtensionUnsupported(EXT_texture_compression_s3tc);
         }
+
+        // OSX supports EXT_texture_sRGB in Legacy contexts, but not in Core contexts.
+        // Though EXT_texture_sRGB was included into GL2.1, it *excludes* the interactions
+        // with s3tc. Strictly speaking, you must advertize support for EXT_texture_sRGB
+        // in order to allow for srgb+s3tc on desktop GL. The omission of EXT_texture_sRGB
+        // in OSX Core contexts appears to be a bug.
+        MarkExtensionSupported(EXT_texture_sRGB);
 #endif
     }
 
     if (shouldDumpExts) {
         printf_stderr("\nActivated extensions:\n");
 
         for (size_t i = 0; i < mAvailableExtensions.size(); i++) {
             if (!mAvailableExtensions[i])
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -412,16 +412,17 @@ public:
         EXT_read_format_bgra,
         EXT_robustness,
         EXT_sRGB,
         EXT_sRGB_write_control,
         EXT_shader_texture_lod,
         EXT_texture3D,
         EXT_texture_compression_dxt1,
         EXT_texture_compression_s3tc,
+        EXT_texture_compression_s3tc_srgb,
         EXT_texture_filter_anisotropic,
         EXT_texture_format_BGRA8888,
         EXT_texture_sRGB,
         EXT_texture_storage,
         EXT_timer_query,
         EXT_transform_feedback,
         EXT_unpack_subimage,
         IMG_read_format,
--- a/gfx/gl/GLContextFeatures.cpp
+++ b/gfx/gl/GLContextFeatures.cpp
@@ -582,18 +582,18 @@ static const FeatureInfo sFeatureInfoArr
         GLESVersion::NONE,
         GLContext::ARB_seamless_cube_map,
         {
             GLContext::Extensions_End
         }
     },
     {
         "shader_texture_lod",
-        GLVersion::NONE,
-        GLESVersion::NONE,
+        GLVersion::GL3,
+        GLESVersion::ES3,
         GLContext::Extension_None,
         {
             GLContext::ARB_shader_texture_lod,
             GLContext::EXT_shader_texture_lod,
             GLContext::Extensions_End
         }
     },
     {
--- a/gfx/ipc/GPUChild.cpp
+++ b/gfx/ipc/GPUChild.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/gfx/gfxVars.h"
 #if defined(XP_WIN)
 # include "mozilla/gfx/DeviceManagerDx.h"
 #endif
 #include "mozilla/ipc/CrashReporterHost.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "mozilla/Unused.h"
+#include "nsIObserverService.h"
 
 #ifdef MOZ_GECKO_PROFILER
 #include "ProfilerParent.h"
 #endif
 
 namespace mozilla {
 namespace gfx {
 
@@ -282,16 +283,32 @@ GPUChild::RecvUpdateFeature(const Featur
 
 mozilla::ipc::IPCResult
 GPUChild::RecvUsedFallback(const Fallback& aFallback, const nsCString& aMessage)
 {
   gfxConfig::EnableFallback(aFallback, aMessage.get());
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+GPUChild::RecvBHRThreadHang(const HangDetails& aDetails)
+{
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    // Copy the HangDetails recieved over the network into a nsIHangDetails, and
+    // then fire our own observer notification.
+    // XXX: We should be able to avoid this potentially expensive copy here by
+    // moving our deserialized argument.
+    nsCOMPtr<nsIHangDetails> hangDetails =
+      new nsHangDetails(HangDetails(aDetails));
+    obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr);
+  }
+  return IPC_OK();
+}
+
 class DeferredDeleteGPUChild : public Runnable
 {
 public:
   explicit DeferredDeleteGPUChild(UniquePtr<GPUChild>&& aChild)
     : Runnable("gfx::DeferredDeleteGPUChild")
     , mChild(Move(aChild))
   {
   }
--- a/gfx/ipc/GPUChild.h
+++ b/gfx/ipc/GPUChild.h
@@ -55,16 +55,17 @@ public:
   void ActorDestroy(ActorDestroyReason aWhy) override;
   mozilla::ipc::IPCResult RecvGraphicsError(const nsCString& aError) override;
   mozilla::ipc::IPCResult RecvNotifyUiObservers(const nsCString& aTopic) override;
   mozilla::ipc::IPCResult RecvNotifyDeviceReset(const GPUDeviceData& aData) override;
   mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport) override;
   mozilla::ipc::IPCResult RecvFinishMemoryReport(const uint32_t& aGeneration) override;
   mozilla::ipc::IPCResult RecvUpdateFeature(const Feature& aFeature, const FeatureFailure& aChange) override;
   mozilla::ipc::IPCResult RecvUsedFallback(const Fallback& aFallback, const nsCString& aMessage) override;
+  mozilla::ipc::IPCResult RecvBHRThreadHang(const HangDetails& aDetails) override;
 
   bool SendRequestMemoryReport(const uint32_t& aGeneration,
                                const bool& aAnonymize,
                                const bool& aMinimizeMemoryUsage,
                                const MaybeFileDesc& aDMDFile);
 
   static void Destroy(UniquePtr<GPUChild>&& aChild);
 
--- a/gfx/ipc/PGPU.ipdl
+++ b/gfx/ipc/PGPU.ipdl
@@ -18,16 +18,17 @@ using mozilla::dom::NativeThreadId from 
 using mozilla::Telemetry::Accumulation from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedAccumulation from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
 using mozilla::gfx::Feature from "gfxFeature.h";
 using mozilla::gfx::Fallback from "gfxFallback.h";
+using mozilla::HangDetails from "mozilla/HangDetails.h";
 
 namespace mozilla {
 namespace gfx {
 
 union GfxPrefValue {
   bool;
   int32_t;
   uint32_t;
@@ -119,12 +120,14 @@ child:
   async FinishMemoryReport(uint32_t aGeneration);
 
   // Update the UI process after a feature's status has changed. This is used
   // outside of the normal startup flow.
   async UpdateFeature(Feature aFeature, FeatureFailure aChange);
 
   // Notify about:support/Telemetry that a fallback occurred.
   async UsedFallback(Fallback aFallback, nsCString message);
+
+  async BHRThreadHang(HangDetails aDetails);
 };
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -460,20 +460,17 @@ WebRenderBridgeChild::InForwarderThread(
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeChild::RecvWrUpdated(const wr::IdNamespace& aNewIdNamespace)
 {
   // Update mIdNamespace to identify obsolete keys and messages by WebRenderBridgeParent.
   // Since usage of invalid keys could cause crash in webrender.
   mIdNamespace = aNewIdNamespace;
-  // Remove all FontKeys since they are removed by WebRenderBridgeParent
-  for (auto iter = mFontKeys.Iter(); !iter.Done(); iter.Next()) {
-    SendDeleteFont(iter.Data());
-  }
+  // Just clear FontKeys, they are removed during WebRenderAPI destruction.
   mFontKeys.Clear();
   GetCompositorBridgeChild()->RecvInvalidateLayers(wr::AsUint64(mPipelineId));
   return IPC_OK();
 }
 
 void
 WebRenderBridgeChild::BeginClearCachedResources()
 {
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -1298,27 +1298,20 @@ WebRenderBridgeParent::ClearResources()
   if (!mApi) {
     return;
   }
 
   uint32_t wrEpoch = GetNextWrEpoch();
   mApi->ClearRootDisplayList(wr::NewEpoch(wrEpoch), mPipelineId);
   // Schedule composition to clean up Pipeline
   mCompositorScheduler->ScheduleComposition();
-  // XXX webrender does not hava a way to delete a group of resources/keys,
-  // then delete keys one by one.
-  for (std::unordered_set<uint64_t>::iterator iter = mFontKeys.begin(); iter != mFontKeys.end(); iter++) {
-    mApi->DeleteFont(wr::AsFontKey(*iter));
-  }
+  // WrFontKeys and WrImageKeys are deleted during WebRenderAPI destruction.
   mFontKeys.clear();
-  for (std::unordered_set<uint64_t>::iterator iter = mActiveImageKeys.begin(); iter != mActiveImageKeys.end(); iter++) {
-    mKeysToDelete.push_back(wr::AsImageKey(*iter));
-  }
   mActiveImageKeys.clear();
-  DeleteOldImages();
+  mKeysToDelete.clear();
   for (auto iter = mExternalImageIds.Iter(); !iter.Done(); iter.Next()) {
     iter.Data()->ClearWrBridge();
   }
   mExternalImageIds.Clear();
   for (auto iter = mAsyncCompositables.Iter(); !iter.Done(); iter.Next()) {
     wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
     RefPtr<WebRenderImageHost> host = iter.Data();
     host->ClearWrBridge();
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -93,18 +93,20 @@ WebRenderLayerManager::DoDestroy(bool aI
     return;
   }
 
   mWidget->CleanupWebRenderWindowOverlay(WrBridge());
 
   LayerManager::Destroy();
 
   if (WrBridge()) {
-    DiscardImages();
-    DiscardCompositorAnimations();
+    // Just clear ImageKeys, they are deleted during WebRenderAPI destruction.
+    mImageKeysToDelete.clear();
+    // CompositorAnimations are cleared by WebRenderBridgeParent.
+    mDiscardedCompositorAnimationsIds.Clear();
     WrBridge()->Destroy(aIsSync);
   }
 
   if (mTransactionIdAllocator) {
     // Make sure to notify the refresh driver just in case it's waiting on a
     // pending transaction. Do this at the top of the event loop so we don't
     // cause a paint to occur during compositor shutdown.
     RefPtr<TransactionIdAllocator> allocator = mTransactionIdAllocator;
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -728,16 +728,24 @@ gfxPlatform::Init()
     gPlatform->InitWebRenderConfig();
     gPlatform->InitOMTPConfig();
 
     if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
       GPUProcessManager* gpu = GPUProcessManager::Get();
       gpu->LaunchGPUProcess();
     }
 
+    if (XRE_IsParentProcess()) {
+      if (gfxPlatform::ForceSoftwareVsync()) {
+        gPlatform->mVsyncSource = (gPlatform)->gfxPlatform::CreateHardwareVsyncSource();
+      } else {
+        gPlatform->mVsyncSource = gPlatform->CreateHardwareVsyncSource();
+      }
+    }
+
 #ifdef USE_SKIA
     SkGraphics::Init();
 #  ifdef MOZ_ENABLE_FREETYPE
     SkInitCairoFT(gPlatform->FontHintingEnabled());
 #  endif
 #endif
 
 #ifdef MOZ_GL_DEBUG
@@ -808,24 +816,16 @@ gfxPlatform::Init()
     nsCOMPtr<imgITools> imgTools = do_GetService("@mozilla.org/image/tools;1");
     if (!imgTools) {
       MOZ_CRASH("Could not initialize ImageLib");
     }
 
     RegisterStrongMemoryReporter(new GfxMemoryImageReporter());
     mlg::InitializeMemoryReporters();
 
-    if (XRE_IsParentProcess()) {
-      if (gfxPlatform::ForceSoftwareVsync()) {
-        gPlatform->mVsyncSource = (gPlatform)->gfxPlatform::CreateHardwareVsyncSource();
-      } else {
-        gPlatform->mVsyncSource = gPlatform->CreateHardwareVsyncSource();
-      }
-    }
-
 #ifdef USE_SKIA
     uint32_t skiaCacheSize = GetSkiaGlyphCacheSize();
     if (skiaCacheSize != kDefaultGlyphCacheSize) {
       SkGraphics::SetFontCacheLimit(skiaCacheSize);
     }
 #endif
 
     InitNullMetadata();
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -596,17 +596,17 @@ private:
   DECL_GFX_PREF(Once, "layers.mlgpu.enable-clear-view",        AdvancedLayersEnableClearView, bool, true);
   DECL_GFX_PREF(Once, "layers.mlgpu.enable-cpu-occlusion",     AdvancedLayersEnableCPUOcclusion, bool, true);
   DECL_GFX_PREF(Once, "layers.mlgpu.enable-depth-buffer",      AdvancedLayersEnableDepthBuffer, bool, false);
   DECL_GFX_PREF(Live, "layers.mlgpu.enable-invalidation",      AdvancedLayersUseInvalidation, bool, true);
   DECL_GFX_PREF(Once, "layers.mlgpu.enable-on-windows7",       AdvancedLayersEnableOnWindows7, bool, false);
   DECL_GFX_PREF(Once, "layers.mlgpu.enable-container-resizing", AdvancedLayersEnableContainerResizing, bool, true);
   DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.force-disabled", LayersOffMainThreadCompositionForceDisabled, bool, false);
   DECL_GFX_PREF(Live, "layers.offmainthreadcomposition.frame-rate", LayersCompositionFrameRate, int32_t,-1);
-  DECL_GFX_PREF(Live, "layers.omtp.force-sync",                LayersOMTPForceSync, bool, true);
+  DECL_GFX_PREF(Live, "layers.omtp.force-sync",                LayersOMTPForceSync, bool, false);
   DECL_GFX_PREF(Live, "layers.orientation.sync.timeout",       OrientationSyncMillis, uint32_t, (uint32_t)0);
   DECL_GFX_PREF(Once, "layers.prefer-opengl",                  LayersPreferOpenGL, bool, false);
   DECL_GFX_PREF(Live, "layers.progressive-paint",              ProgressivePaint, bool, false);
   DECL_GFX_PREF(Live, "layers.shared-buffer-provider.enabled", PersistentBufferProviderSharedEnabled, bool, false);
   DECL_GFX_PREF(Live, "layers.single-tile.enabled",            LayersSingleTileEnabled, bool, true);
   DECL_GFX_PREF(Once, "layers.stereo-video.enabled",           StereoVideoEnabled, bool, false);
   DECL_GFX_PREF(Live, "layers.force-synchronous-resize",       LayersForceSynchronousResize, bool, false);
 
@@ -691,16 +691,18 @@ private:
   DECL_GFX_PREF(Live, "test.mousescroll",                      MouseScrollTestingEnabled, bool, false);
 
   DECL_GFX_PREF(Live, "toolkit.scrollbox.horizontalScrollDistance", ToolkitHorizontalScrollDistance, int32_t, 5);
   DECL_GFX_PREF(Live, "toolkit.scrollbox.verticalScrollDistance",   ToolkitVerticalScrollDistance, int32_t, 3);
 
   DECL_GFX_PREF(Live, "ui.click_hold_context_menus.delay",     UiClickHoldContextMenusDelay, int32_t, 500);
 
   // WebGL (for pref access from Worker threads)
+  DECL_GFX_PREF(Live, "webgl.1.allow-core-profiles",           WebGL1AllowCoreProfile, bool, false);
+
   DECL_GFX_PREF(Live, "webgl.all-angle-options",               WebGLAllANGLEOptions, bool, false);
   DECL_GFX_PREF(Live, "webgl.angle.force-d3d11",               WebGLANGLEForceD3D11, bool, false);
   DECL_GFX_PREF(Live, "webgl.angle.try-d3d11",                 WebGLANGLETryD3D11, bool, false);
   DECL_GFX_PREF(Live, "webgl.angle.force-warp",                WebGLANGLEForceWARP, bool, false);
   DECL_GFX_PREF(Live, "webgl.bypass-shader-validation",        WebGLBypassShaderValidator, bool, true);
   DECL_GFX_PREF(Live, "webgl.can-lose-context-in-foreground",  WebGLCanLoseContextInForeground, bool, true);
   DECL_GFX_PREF(Live, "webgl.default-no-alpha",                WebGLDefaultNoAlpha, bool, false);
   DECL_GFX_PREF(Live, "webgl.disable-angle",                   WebGLDisableANGLE, bool, false);
--- a/image/StreamingLexer.h
+++ b/image/StreamingLexer.h
@@ -396,18 +396,18 @@ public:
   /**
    * From the given SourceBufferIterator, aIterator, create a new iterator at
    * the same position, with the given read limit, aReadLimit. The read limit
    * applies after adjusting for the position. If the given iterator has been
    * advanced, but required buffering inside StreamingLexer, the position
    * of the cloned iterator will be at the beginning of buffered data; this
    * should match the perspective of the caller.
    */
-  SourceBufferIterator Clone(SourceBufferIterator& aIterator,
-                             size_t aReadLimit) const
+  Maybe<SourceBufferIterator> Clone(SourceBufferIterator& aIterator,
+                                    size_t aReadLimit) const
   {
     // In order to advance to the current position of the iterator from the
     // perspective of the caller, we need to take into account if we are
     // buffering data.
     size_t pos = aIterator.Position();
     if (!mBuffer.empty()) {
       pos += aIterator.Length();
       MOZ_ASSERT(pos > mBuffer.length());
@@ -420,28 +420,36 @@ public:
     }
 
     SourceBufferIterator other = aIterator.Owner()->Iterator(readLimit);
 
     // Since the current iterator has already advanced to this point, we
     // know that the state can only be READY or COMPLETE. That does not mean
     // everything is stored in a single chunk, and may require multiple Advance
     // calls to get where we want to be.
-    DebugOnly<SourceBufferIterator::State> state;
+    SourceBufferIterator::State state;
     do {
       state = other.Advance(pos);
-      MOZ_ASSERT(state != SourceBufferIterator::WAITING);
+      if (state != SourceBufferIterator::READY) {
+        MOZ_ASSERT_UNREACHABLE("Cannot advance to existing position");
+        return Nothing();
+      }
       MOZ_ASSERT(pos >= other.Length());
       pos -= other.Length();
     } while (pos > 0);
 
     // Force the data pointer to be where we expect it to be.
     state = other.Advance(0);
-    MOZ_ASSERT(state != SourceBufferIterator::WAITING);
-    return other;
+    if (state != SourceBufferIterator::READY) {
+      // The current position could be the end of the buffer, in which case
+      // there is no point cloning with no more data to read.
+      MOZ_ASSERT(state == SourceBufferIterator::COMPLETE);
+      return Nothing();
+    }
+    return Some(Move(other));
   }
 
   template <typename Func>
   LexerResult Lex(SourceBufferIterator& aIterator,
                   IResumable* aOnResume,
                   Func aFunc)
   {
     if (mTransition.NextStateIsTerminal()) {
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -189,33 +189,41 @@ LexerTransition<ICOState>
 nsICODecoder::IterateUnsizedDirEntry()
 {
   MOZ_ASSERT(!mUnsizedDirEntries.IsEmpty());
 
   if (!mDirEntry) {
     // The first time we are here, there is no entry selected. We must prepare a
     // new iterator for the contained decoder to advance as it wills. Cloning at
     // this point ensures it will begin at the end of the dir entries.
-    mReturnIterator.emplace(mLexer.Clone(*mIterator, SIZE_MAX));
+    mReturnIterator = Move(mLexer.Clone(*mIterator, SIZE_MAX));
+    if (mReturnIterator.isNothing()) {
+      // If we cannot read further than this point, then there is no resource
+      // data to read.
+      return Transition::TerminateFailure();
+    }
   } else {
     // We have already selected an entry which means a metadata decoder has
     // finished. Verify the size is valid and if so, add to the discovered
     // resources.
     if (mDirEntry->mSize.width > 0 && mDirEntry->mSize.height > 0) {
       mDirEntries.AppendElement(*mDirEntry);
     }
 
     // Remove the entry from the unsized list either way.
     mDirEntry = nullptr;
     mUnsizedDirEntries.RemoveElementAt(0);
 
     // Our iterator is at an unknown point, so reset it to the point that we
     // saved.
-    mIterator.reset();
-    mIterator.emplace(mLexer.Clone(*mReturnIterator, SIZE_MAX));
+    mIterator = Move(mLexer.Clone(*mReturnIterator, SIZE_MAX));
+    if (mIterator.isNothing()) {
+      MOZ_ASSERT_UNREACHABLE("Cannot re-clone return iterator");
+      return Transition::TerminateFailure();
+    }
   }
 
   // There are no more unsized entries, so we can finally decide which entry to
   // select for decoding.
   if (mUnsizedDirEntries.IsEmpty()) {
     mReturnIterator.reset();
     return Transition::To(ICOState::FINISHED_DIR_ENTRY, 0);
   }
@@ -339,26 +347,29 @@ nsICODecoder::SniffResource(const char* 
                        PNGSIGNATURESIZE);
   if (isPNG) {
     if (mDirEntry->mBytesInRes <= BITMAPINFOSIZE) {
       return Transition::TerminateFailure();
     }
 
     // Prepare a new iterator for the contained decoder to advance as it wills.
     // Cloning at the point ensures it will begin at the resource offset.
-    SourceBufferIterator containedIterator
+    Maybe<SourceBufferIterator> containedIterator
       = mLexer.Clone(*mIterator, mDirEntry->mBytesInRes);
+    if (containedIterator.isNothing()) {
+      return Transition::TerminateFailure();
+    }
 
     // Create a PNG decoder which will do the rest of the work for us.
     bool metadataDecode = mReturnIterator.isSome();
     Maybe<IntSize> expectedSize = metadataDecode ? Nothing()
                                                  : Some(mDirEntry->mSize);
     mContainedDecoder =
       DecoderFactory::CreateDecoderForICOResource(DecoderType::PNG,
-                                                  Move(containedIterator),
+                                                  Move(containedIterator.ref()),
                                                   WrapNotNull(this),
                                                   metadataDecode,
                                                   expectedSize);
 
     // Read in the rest of the PNG unbuffered.
     size_t toRead = mDirEntry->mBytesInRes - BITMAPINFOSIZE;
     return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
                                     ICOState::READ_RESOURCE,
@@ -406,27 +417,30 @@ nsICODecoder::ReadBIH(const char* aData)
   // The ICO format when containing a BMP does not include the 14 byte
   // bitmap file header. So we create the BMP decoder via the constructor that
   // tells it to skip this, and pass in the required data (dataOffset) that
   // would have been present in the header.
   uint32_t dataOffset = bmp::FILE_HEADER_LENGTH + BITMAPINFOSIZE + 4 * numColors;
 
   // Prepare a new iterator for the contained decoder to advance as it wills.
   // Cloning at the point ensures it will begin at the resource offset.
-  SourceBufferIterator containedIterator
+  Maybe<SourceBufferIterator> containedIterator
     = mLexer.Clone(*mIterator, mDirEntry->mBytesInRes);
+  if (containedIterator.isNothing()) {
+    return Transition::TerminateFailure();
+  }
 
   // Create a BMP decoder which will do most of the work for us; the exception
   // is the AND mask, which isn't present in standalone BMPs.
   bool metadataDecode = mReturnIterator.isSome();
   Maybe<IntSize> expectedSize = metadataDecode ? Nothing()
                                                : Some(mDirEntry->mSize);
   mContainedDecoder =
     DecoderFactory::CreateDecoderForICOResource(DecoderType::BMP,
-                                                Move(containedIterator),
+                                                Move(containedIterator.ref()),
                                                 WrapNotNull(this),
                                                 metadataDecode,
                                                 expectedSize,
                                                 Some(dataOffset));
 
   RefPtr<nsBMPDecoder> bmpDecoder =
     static_cast<nsBMPDecoder*>(mContainedDecoder.get());
 
--- a/js/src/tests/ecma_3/Date/15.9.5.5.js
+++ b/js/src/tests/ecma_3/Date/15.9.5.5.js
@@ -1,9 +1,9 @@
-// |reftest| random-if(xulRuntime.OS=="Linux")
+// |reftest| random-if(xulRuntime.OS=="Linux") skip-if(xulRuntime.OS=="WINNT")
 /* -*- 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/. */
 
 
 /**
    File Name:          15.9.5.5.js
--- a/js/src/tests/ecma_3/Date/15.9.5.6.js
+++ b/js/src/tests/ecma_3/Date/15.9.5.6.js
@@ -1,8 +1,9 @@
+// |reftest| skip-if(xulRuntime.OS=="WINNT")
 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
 
 /**
    File Name:          15.9.5.6.js
--- a/js/src/tests/ecma_3/Date/15.9.5.7.js
+++ b/js/src/tests/ecma_3/Date/15.9.5.7.js
@@ -1,8 +1,9 @@
+// |reftest| skip-if(xulRuntime.OS=="WINNT")
 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
 /**
    File Name:    15.9.5.7.js
    ECMA Section: 15.9.5.7 Date.prototype.toLocaleTimeString()
--- a/js/src/tests/js1_5/extensions/toLocaleFormat-02.js
+++ b/js/src/tests/js1_5/extensions/toLocaleFormat-02.js
@@ -1,9 +1,9 @@
-// |reftest| fails-if(xulRuntime.OS=="WINNT") skip-if(xulRuntime.OS=="WINNT"&&isDebugBuild)
+// |reftest| skip-if(xulRuntime.OS=="WINNT")
 /* -*- 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/. */
 
 //-----------------------------------------------------------------------------
 var BUGNUMBER = 291494;
 var summary = 'Date.prototype.toLocaleFormat extension';
--- a/js/xpconnect/idl/nsIXPCScriptable.idl
+++ b/js/xpconnect/idl/nsIXPCScriptable.idl
@@ -42,17 +42,17 @@ interface nsIXPConnectWrappedNative;
     #define XPC_SCRIPTABLE_WANT_NEWENUMERATE                (1 <<  4)
     #define XPC_SCRIPTABLE_WANT_RESOLVE                     (1 <<  5)
     #define XPC_SCRIPTABLE_WANT_FINALIZE                    (1 <<  6)
     #define XPC_SCRIPTABLE_WANT_CALL                        (1 <<  7)
     #define XPC_SCRIPTABLE_WANT_CONSTRUCT                   (1 <<  8)
     #define XPC_SCRIPTABLE_WANT_HASINSTANCE                 (1 <<  9)
     #define XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY       (1 << 10)
     #define XPC_SCRIPTABLE_USE_JSSTUB_FOR_DELPROPERTY       (1 << 11)
-    #define XPC_SCRIPTABLE_USE_JSSTUB_FOR_SETPROPERTY       (1 << 12)
+    // (1 << 12) is unused
     #define XPC_SCRIPTABLE_DONT_ENUM_QUERY_INTERFACE        (1 << 13)
     #define XPC_SCRIPTABLE_DONT_ASK_INSTANCE_FOR_SCRIPTABLE (1 << 14)
     #define XPC_SCRIPTABLE_CLASSINFO_INTERFACES_ONLY        (1 << 15)
     #define XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE   (1 << 16)
     #define XPC_SCRIPTABLE_ALLOW_PROP_MODS_TO_PROTOTYPE     (1 << 17)
     #define XPC_SCRIPTABLE_IS_GLOBAL_OBJECT                 (1 << 18)
     #define XPC_SCRIPTABLE_DONT_REFLECT_INTERFACE_NAMES     (1 << 19)
 %}
@@ -123,17 +123,16 @@ interface nsIXPCScriptable : nsISupports
     GET_IT(WantNewEnumerate,             WANT_NEWENUMERATE)
     GET_IT(WantResolve,                  WANT_RESOLVE)
     GET_IT(WantFinalize,                 WANT_FINALIZE)
     GET_IT(WantCall,                     WANT_CALL)
     GET_IT(WantConstruct,                WANT_CONSTRUCT)
     GET_IT(WantHasInstance,              WANT_HASINSTANCE)
     GET_IT(UseJSStubForAddProperty,      USE_JSSTUB_FOR_ADDPROPERTY)
     GET_IT(UseJSStubForDelProperty,      USE_JSSTUB_FOR_DELPROPERTY)
-    GET_IT(UseJSStubForSetProperty,      USE_JSSTUB_FOR_SETPROPERTY)
     GET_IT(DontEnumQueryInterface,       DONT_ENUM_QUERY_INTERFACE)
     GET_IT(DontAskInstanceForScriptable, DONT_ASK_INSTANCE_FOR_SCRIPTABLE)
     GET_IT(ClassInfoInterfacesOnly,      CLASSINFO_INTERFACES_ONLY)
     GET_IT(AllowPropModsDuringResolve,   ALLOW_PROP_MODS_DURING_RESOLVE)
     GET_IT(AllowPropModsToPrototype,     ALLOW_PROP_MODS_TO_PROTOTYPE)
     GET_IT(IsGlobalObject,               IS_GLOBAL_OBJECT)
     GET_IT(DontReflectInterfaceNames,    DONT_REFLECT_INTERFACE_NAMES)
 
--- a/js/xpconnect/public/xpc_make_class.h
+++ b/js/xpconnect/public/xpc_make_class.h
@@ -31,24 +31,16 @@ XPC_WN_CannotDeletePropertyStub(JSContex
 bool
 XPC_WN_Helper_GetProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                           JS::MutableHandleValue vp);
 
 bool
 XPC_WN_Helper_SetProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                           JS::MutableHandleValue vp,
                           JS::ObjectOpResult& result);
-bool
-XPC_WN_MaybeResolvingSetPropertyStub(JSContext* cx, JS::HandleObject obj,
-                                     JS::HandleId id, JS::MutableHandleValue vp,
-                                     JS::ObjectOpResult& result);
-bool
-XPC_WN_CannotModifySetPropertyStub(JSContext* cx, JS::HandleObject obj,
-                                   JS::HandleId id, JS::MutableHandleValue vp,
-                                   JS::ObjectOpResult& result);
 
 bool
 XPC_WN_Helper_Enumerate(JSContext* cx, JS::HandleObject obj);
 bool
 XPC_WN_Shared_Enumerate(JSContext* cx, JS::HandleObject obj);
 
 bool
 XPC_WN_NewEnumerate(JSContext* cx, JS::HandleObject obj, JS::AutoIdVector& properties,
@@ -96,21 +88,17 @@ extern const js::ClassExtension XPC_WN_J
     /* getProperty */ \
     ((_flags) & XPC_SCRIPTABLE_WANT_GETPROPERTY) \
     ? XPC_WN_Helper_GetProperty \
     : nullptr, \
     \
     /* setProperty */ \
     ((_flags) & XPC_SCRIPTABLE_WANT_SETPROPERTY) \
     ? XPC_WN_Helper_SetProperty \
-    : ((_flags) & XPC_SCRIPTABLE_USE_JSSTUB_FOR_SETPROPERTY) \
-      ? nullptr \
-      : ((_flags) & XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) \
-        ? XPC_WN_MaybeResolvingSetPropertyStub \
-        : XPC_WN_CannotModifySetPropertyStub, \
+    : nullptr, \
     \
     /* enumerate */ \
     ((_flags) & XPC_SCRIPTABLE_WANT_NEWENUMERATE) \
     ? nullptr /* We will use newEnumerate set below in this case */ \
     : ((_flags) & XPC_SCRIPTABLE_WANT_ENUMERATE) \
       ? XPC_WN_Helper_Enumerate \
       : XPC_WN_Shared_Enumerate, \
     \
--- a/js/xpconnect/src/XPCRuntimeService.cpp
+++ b/js/xpconnect/src/XPCRuntimeService.cpp
@@ -28,17 +28,16 @@ NS_IMPL_RELEASE(BackstagePass)
 #define XPC_MAP_CLASSNAME         BackstagePass
 #define XPC_MAP_QUOTED_CLASSNAME "BackstagePass"
 #define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_RESOLVE | \
                        XPC_SCRIPTABLE_WANT_ENUMERATE | \
                        XPC_SCRIPTABLE_WANT_FINALIZE | \
                        XPC_SCRIPTABLE_WANT_PRECREATE | \
                        XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY |  \
                        XPC_SCRIPTABLE_USE_JSSTUB_FOR_DELPROPERTY |  \
-                       XPC_SCRIPTABLE_USE_JSSTUB_FOR_SETPROPERTY |  \
                        XPC_SCRIPTABLE_DONT_ENUM_QUERY_INTERFACE |  \
                        XPC_SCRIPTABLE_IS_GLOBAL_OBJECT |  \
                        XPC_SCRIPTABLE_DONT_REFLECT_INTERFACE_NAMES)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 
 JSObject*
 BackstagePass::GetGlobalJSObject()
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -677,144 +677,16 @@ static const JSFunctionSpec glob_functio
     JS_FS("simulateActivityCallback", SimulateActivityCallback, 1,0),
     JS_FS("registerAppManifest", RegisterAppManifest, 1, 0),
 #ifdef ENABLE_TESTS
     JS_FS("registerXPCTestComponents", RegisterXPCTestComponents, 0, 0),
 #endif
     JS_FS_END
 };
 
-static bool
-env_setProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
-                ObjectOpResult& result)
-{
-/* XXX porting may be easy, but these don't seem to supply setenv by default */
-#if !defined SOLARIS
-    RootedString valstr(cx);
-    RootedString idstr(cx);
-    int rv;
-
-    RootedValue idval(cx);
-    if (!JS_IdToValue(cx, id, &idval))
-        return false;
-
-    idstr = ToString(cx, idval);
-    valstr = ToString(cx, vp);
-    if (!idstr || !valstr)
-        return false;
-    JSAutoByteString name(cx, idstr);
-    if (!name)
-        return false;
-    JSAutoByteString value(cx, valstr);
-    if (!value)
-        return false;
-#if defined XP_WIN || defined HPUX || defined OSF1 || defined SCO
-    {
-        char* waste = JS_smprintf("%s=%s", name.ptr(), value.ptr()).release();
-        if (!waste) {
-            JS_ReportOutOfMemory(cx);
-            return false;
-        }
-        rv = putenv(waste);
-#ifdef XP_WIN
-        /*
-         * HPUX9 at least still has the bad old non-copying putenv.
-         *
-         * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
-         * that will crash if you pass it an auto char array (so it must place
-         * its argument directly in the char* environ[] array).
-         */
-        free(waste);
-#endif
-    }
-#else
-    rv = setenv(name.ptr(), value.ptr(), 1);
-#endif
-    if (rv < 0) {
-        name.clear();
-        value.clear();
-        if (!name.encodeUtf8(cx, idstr))
-            return false;
-        if (!value.encodeUtf8(cx, valstr))
-            return false;
-        JS_ReportErrorUTF8(cx, "can't set envariable %s to %s", name.ptr(), value.ptr());
-        return false;
-    }
-    vp.setString(valstr);
-#endif /* !defined SOLARIS */
-    return result.succeed();
-}
-
-static bool
-env_enumerate(JSContext* cx, HandleObject obj)
-{
-    static bool reflected;
-    char** evp;
-    char* name;
-    char* value;
-    RootedString valstr(cx);
-    bool ok;
-
-    if (reflected)
-        return true;
-
-    for (evp = (char**)JS_GetPrivate(obj); (name = *evp) != nullptr; evp++) {
-        value = strchr(name, '=');
-        if (!value)
-            continue;
-        *value++ = '\0';
-        valstr = JS_NewStringCopyZ(cx, value);
-        ok = valstr ? JS_DefineProperty(cx, obj, name, valstr, JSPROP_ENUMERATE) : false;
-        value[-1] = '=';
-        if (!ok)
-            return false;
-    }
-
-    reflected = true;
-    return true;
-}
-
-static bool
-env_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
-{
-    JSString* idstr;
-
-    RootedValue idval(cx);
-    if (!JS_IdToValue(cx, id, &idval))
-        return false;
-
-    idstr = ToString(cx, idval);
-    if (!idstr)
-        return false;
-    JSAutoByteString name(cx, idstr);
-    if (!name)
-        return false;
-    const char* value = getenv(name.ptr());
-    if (value) {
-        RootedString valstr(cx, JS_NewStringCopyZ(cx, value));
-        if (!valstr)
-            return false;
-        if (!JS_DefinePropertyById(cx, obj, id, valstr, JSPROP_ENUMERATE)) {
-            return false;
-        }
-        *resolvedp = true;
-    }
-    return true;
-}
-
-static const JSClassOps env_classOps = {
-    nullptr, nullptr, nullptr, env_setProperty,
-    env_enumerate, nullptr, env_resolve
-};
-
-static const JSClass env_class = {
-    "environment", JSCLASS_HAS_PRIVATE,
-    &env_classOps
-};
-
 /***************************************************************************/
 
 typedef enum JSShellErrNum {
 #define MSG_DEF(name, number, count, exception, format) \
     name = number,
 #include "jsshell.msg"
 #undef MSG_DEF
     JSShellErr_Limit
@@ -1489,24 +1361,16 @@ XRE_XPCShellMain(int argc, char** argv, 
                 return 1;
             }
 
             if (!JS_DefineFunctions(cx, glob, glob_functions) ||
                 !JS_DefineProfilingFunctions(cx, glob)) {
                 return 1;
             }
 
-            JS::Rooted<JSObject*> envobj(cx);
-            envobj = JS_DefineObject(cx, glob, "environment", &env_class);
-            if (!envobj) {
-                return 1;
-            }
-
-            JS_SetPrivate(envobj, envp);
-
             nsAutoString workingDirectory;
             if (GetCurrentWorkingDirectory(workingDirectory))
                 gWorkingDirectory = &workingDirectory;
 
             JS_DefineProperty(cx, glob, "__LOCATION__", JS::UndefinedHandleValue,
                               JSPROP_SHARED,
                               GetLocationProperty,
                               nullptr);
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -479,23 +479,16 @@ XPC_WN_CannotModifyPropertyStub(JSContex
 bool
 XPC_WN_CannotDeletePropertyStub(JSContext* cx, HandleObject obj, HandleId id,
                                 ObjectOpResult& result)
 {
     return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
 }
 
 bool
-XPC_WN_CannotModifySetPropertyStub(JSContext* cx, HandleObject obj, HandleId id,
-                                   MutableHandleValue vp, ObjectOpResult& result)
-{
-    return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
-}
-
-bool
 XPC_WN_Shared_Enumerate(JSContext* cx, HandleObject obj)
 {
     XPCCallContext ccx(cx, obj);
     XPCWrappedNative* wrapper = ccx.GetWrapper();
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
     // Since we aren't going to enumerate tearoff names and the prototype
     // handles non-mutated members, we can do this potential short-circuit.
@@ -664,24 +657,16 @@ XPC_WN_MaybeResolvingPropertyStub(JSCont
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
     if (ccx.GetResolvingWrapper() == wrapper)
         return true;
     return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
 }
 
 bool
-XPC_WN_MaybeResolvingSetPropertyStub(JSContext* cx, HandleObject obj, HandleId id,
-                                     MutableHandleValue vp, ObjectOpResult& result)
-{
-    result.succeed();
-    return XPC_WN_MaybeResolvingPropertyStub(cx, obj, id, vp);
-}
-
-bool
 XPC_WN_MaybeResolvingDeletePropertyStub(JSContext* cx, HandleObject obj, HandleId id,
                                         ObjectOpResult& result)
 {
     XPCCallContext ccx(cx, obj);
     XPCWrappedNative* wrapper = ccx.GetWrapper();
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
     if (ccx.GetResolvingWrapper() == wrapper) {
--- a/js/xpconnect/tests/chrome/chrome.ini
+++ b/js/xpconnect/tests/chrome/chrome.ini
@@ -83,16 +83,17 @@ skip-if = os == 'win' || os == 'mac' || 
 [test_bug1042436.xul]
 [test_bug1050049.html]
 [test_bug1065185.html]
 [test_bug1074863.html]
 [test_bug1092477.xul]
 [test_bug1124898.html]
 [test_bug1126911.html]
 [test_bug1281071.xul]
+[test_bug1390159.xul]
 [test_chrometoSource.xul]
 [test_cloneInto.xul]
 [test_cows.xul]
 [test_discardSystemSource.xul]
 [test_documentdomain.xul]
 [test_doublewrappedcompartments.xul]
 [test_evalInSandbox.xul]
 [test_evalInWindow.xul]
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug1390159.xul
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1390159
+-->
+<window title="Mozilla Bug 1390159"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <!-- test code goes here -->
+  <script type="application/javascript"><![CDATA[
+    function test() {
+      var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+                           .getService(Components.interfaces.nsIXULRuntime);
+
+      // Make sure it has an inSafeMode property. This is just an arbitrary
+      // readonly property.
+      var oldValue = xulRuntime.inSafeMode;
+      is(typeof oldValue, "boolean", "Expected an inSafeMode property");
+
+      // Changing a readonly property doesn't throw, but shouldn't change
+      // the value.
+      xulRuntime.inSafeMode = !oldValue;
+      is(xulRuntime.inSafeMode, oldValue, "Should not have modified prop");
+
+      // Adding a new property should throw.
+      ex = null;
+      try {
+        xulRuntime.foobar = 1;
+      } catch (e) {
+        ex = e;
+      }
+      is(ex.toString().includes("Cannot modify"), true, "Expected an error");
+      is(xulRuntime.foobar, undefined, "Property not defined");
+    }
+    test();
+  ]]></script>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  </body>
+
+</window>
--- a/layout/base/nsCaret.cpp
+++ b/layout/base/nsCaret.cpp
@@ -28,17 +28,16 @@
 #include "nsBlockFrame.h"
 #include "nsISelectionController.h"
 #include "nsTextFrame.h"
 #include "nsXULPopupManager.h"
 #include "nsMenuPopupFrame.h"
 #include "nsTextFragment.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/LookAndFeel.h"
-#include "mozilla/dom/Selection.h"
 #include "nsIBidiKeyboard.h"
 #include "nsContentUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
 // The bidi indicator hangs off the caret to one side, to show which
@@ -236,51 +235,27 @@ nsISelection* nsCaret::GetSelection()
   return sel;
 }
 
 void nsCaret::SetSelection(nsISelection *aDOMSel)
 {
   MOZ_ASSERT(aDOMSel);
   mDomSelectionWeak = do_GetWeakReference(aDOMSel);   // weak reference to pres shell
   ResetBlinking();
-  SchedulePaint();
+  SchedulePaint(aDOMSel);
 }
 
 void nsCaret::SetVisible(bool inMakeVisible)
 {
   mVisible = inMakeVisible;
   mIgnoreUserModify = mVisible;
   ResetBlinking();
   SchedulePaint();
 }
 
-bool nsCaret::IsVisible()
-{
-  if (!mVisible || mHideCount) {
-    return false;
-  }
-
-  if (!mShowDuringSelection) {
-    Selection* selection = GetSelectionInternal();
-    if (!selection) {
-      return false;
-    }
-    bool isCollapsed;
-    if (NS_FAILED(selection->GetIsCollapsed(&isCollapsed)) || !isCollapsed) {
-      return false;
-    }
-  }
-
-  if (IsMenuPopupHidingCaret()) {
-    return false;
-  }
-
-  return true;
-}
-
 void nsCaret::AddForceHide()
 {
   MOZ_ASSERT(mHideCount < UINT32_MAX);
   if (++mHideCount > 1) {
     return;
   }
   ResetBlinking();
   SchedulePaint();
@@ -459,19 +434,24 @@ nsCaret::GetGeometry(nsISelection* aSele
 
 Selection*
 nsCaret::GetSelectionInternal()
 {
   nsISelection* domSelection = GetSelection();
   return domSelection ? domSelection->AsSelection() : nullptr;
 }
 
-void nsCaret::SchedulePaint()
+void nsCaret::SchedulePaint(nsISelection* aSelection)
 {
-  Selection* selection = GetSelectionInternal();
+  Selection* selection;
+  if (aSelection) {
+    selection = aSelection->AsSelection();
+  } else {
+    selection = GetSelectionInternal();
+  }
   nsINode* focusNode;
   if (mOverrideContent) {
     focusNode = mOverrideContent;
   } else if (selection) {
     focusNode = selection->GetFocusNode();
   } else {
     return;
   }
@@ -604,34 +584,38 @@ void nsCaret::PaintCaret(DrawTarget& aDr
     aDrawTarget.FillRect(devPxHookRect, color);
   }
 }
 
 NS_IMETHODIMP
 nsCaret::NotifySelectionChanged(nsIDOMDocument *, nsISelection *aDomSel,
                                 int16_t aReason)
 {
-  if ((aReason & nsISelectionListener::MOUSEUP_REASON) || !IsVisible())//this wont do
+  // Note that aDomSel, per the comment below may not be the same as our
+  // selection, but that's OK since if that is the case, it wouldn't have
+  // mattered what IsVisible() returns here, so we just opt for checking
+  // the selection later down below.
+  if ((aReason & nsISelectionListener::MOUSEUP_REASON) || !IsVisible(aDomSel))//this wont do
     return NS_OK;
 
   nsCOMPtr<nsISelection> domSel(do_QueryReferent(mDomSelectionWeak));
 
   // The same caret is shared amongst the document and any text widgets it
   // may contain. This means that the caret could get notifications from
   // multiple selections.
   //
   // If this notification is for a selection that is not the one the
   // the caret is currently interested in (mDomSelectionWeak), then there
   // is nothing to do!
 
   if (domSel != aDomSel)
     return NS_OK;
 
   ResetBlinking();
-  SchedulePaint();
+  SchedulePaint(aDomSel);
 
   return NS_OK;
 }
 
 void nsCaret::ResetBlinking()
 {
   mIsBlinkOn = true;
 
--- a/layout/base/nsCaret.h
+++ b/layout/base/nsCaret.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* the caret is the text cursor used, e.g., when editing */
 
 #ifndef nsCaret_h__
 #define nsCaret_h__
 
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/dom/Selection.h"
 #include "nsCoord.h"
 #include "nsISelectionListener.h"
 #include "nsIWeakReferenceUtils.h"
 #include "CaretAssociationHint.h"
 #include "nsPoint.h"
 #include "nsRect.h"
 
 class nsDisplayListBuilder;
@@ -22,19 +23,16 @@ class nsFrameSelection;
 class nsIContent;
 class nsIDOMNode;
 class nsIFrame;
 class nsINode;
 class nsIPresShell;
 class nsITimer;
 
 namespace mozilla {
-namespace dom {
-class Selection;
-} // namespace dom
 namespace gfx {
 class DrawTarget;
 } // namespace gfx
 } // namespace mozilla
 
 //-----------------------------------------------------------------------------
 class nsCaret final : public nsISelectionListener
 {
@@ -72,17 +70,40 @@ class nsCaret final : public nsISelectio
     void SetVisible(bool intMakeVisible);
     /** IsVisible will get the visibility of the caret.
      *  This returns false if the caret is hidden because it was set
      *  to not be visible, or because the selection is not collapsed, or
      *  because an open popup is hiding the caret.
      *  It does not take account of blinking or the caret being hidden
      *  because we're in non-editable/disabled content.
      */
-    bool IsVisible();
+    bool IsVisible(nsISelection* aSelection = nullptr)
+    {
+      if (!mVisible || mHideCount) {
+        return false;
+      }
+
+      if (!mShowDuringSelection) {
+        mozilla::dom::Selection* selection;
+        if (aSelection) {
+          selection = static_cast<mozilla::dom::Selection*>(aSelection);
+        } else {
+          selection = GetSelectionInternal();
+        }
+        if (!selection || !selection->IsCollapsed()) {
+          return false;
+        }
+      }
+
+      if (IsMenuPopupHidingCaret()) {
+        return false;
+      }
+
+      return true;
+    }
     /**
      * AddForceHide() increases mHideCount and hide the caret even if
      * SetVisible(true) has been or will be called.  This is useful when the
      * caller wants to hide caret temporarily and it needs to cancel later.
      * Especially, in the latter case, it's too difficult to decide if the
      * caret should be actually visible or not because caret visible state
      * is set from a lot of event handlers.  So, it's very stateful.
      */
@@ -110,17 +131,17 @@ class nsCaret final : public nsISelectio
      * Passing null for aNode would set the caret to track its selection again.
      **/
     void SetCaretPosition(nsIDOMNode* aNode, int32_t aOffset);
 
     /**
      * Schedule a repaint for the frame where the caret would appear.
      * Does not check visibility etc.
      */
-    void SchedulePaint();
+    void SchedulePaint(nsISelection* aSelection = nullptr);
 
     /**
      * Returns a frame to paint in, and the bounds of the painted caret
      * relative to that frame.
      * The rectangle includes bidi decorations.
      * Returns null if the caret should not be drawn (including if it's blinked
      * off).
      */
--- a/layout/forms/nsGfxButtonControlFrame.cpp
+++ b/layout/forms/nsGfxButtonControlFrame.cpp
@@ -45,18 +45,19 @@ nsGfxButtonControlFrame::GetFrameName(ns
 }
 #endif
 
 // Create the text content used as label for the button.
 // The frame will be generated by the frame constructor.
 nsresult
 nsGfxButtonControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
 {
-  nsXPIDLString label;
-  GetLabel(label);
+  nsAutoString label;
+  nsresult rv = GetLabel(label);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // Add a child text content node for the label
   mTextContent = new nsTextNode(mContent->NodeInfo()->NodeInfoManager());
 
   // set the value of the text node and add it to the child list
   mTextContent->SetText(label, false);
   aElements.AppendElement(mTextContent);
 
@@ -78,17 +79,17 @@ NS_QUERYFRAME_TAIL_INHERITING(nsHTMLButt
 
 // Initially we hardcoded the default strings here.
 // Next, we used html.css to store the default label for various types
 // of buttons. (nsGfxButtonControlFrame::DoNavQuirksReflow rev 1.20)
 // However, since html.css is not internationalized, we now grab the default
 // label from a string bundle as is done for all other UI strings.
 // See bug 16999 for further details.
 nsresult
-nsGfxButtonControlFrame::GetDefaultLabel(nsXPIDLString& aString) const
+nsGfxButtonControlFrame::GetDefaultLabel(nsAString& aString) const
 {
   nsCOMPtr<nsIFormControl> form = do_QueryInterface(mContent);
   NS_ENSURE_TRUE(form, NS_ERROR_UNEXPECTED);
 
   int32_t type = form->ControlType();
   const char *prop;
   if (type == NS_FORM_INPUT_RESET) {
     prop = "Reset";
@@ -101,17 +102,17 @@ nsGfxButtonControlFrame::GetDefaultLabel
     return NS_OK;
   }
 
   return nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
                                             prop, aString);
 }
 
 nsresult
-nsGfxButtonControlFrame::GetLabel(nsXPIDLString& aLabel)
+nsGfxButtonControlFrame::GetLabel(nsString& aLabel)
 {
   // Get the text from the "value" property on our content if there is
   // one; otherwise set it to a default value (localized).
   dom::HTMLInputElement* elt = dom::HTMLInputElement::FromContent(mContent);
   if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::value) && elt) {
     elt->GetValue(aLabel, dom::CallerType::System);
   } else {
     // Generate localized label.
@@ -155,17 +156,17 @@ nsGfxButtonControlFrame::AttributeChange
                                           nsIAtom*        aAttribute,
                                           int32_t         aModType)
 {
   nsresult rv = NS_OK;
 
   // If the value attribute is set, update the text of the label
   if (nsGkAtoms::value == aAttribute) {
     if (mTextContent && mContent) {
-      nsXPIDLString label;
+      nsAutoString label;
       rv = GetLabel(label);
       NS_ENSURE_SUCCESS(rv, rv);
 
       mTextContent->SetText(label, true);
     } else {
       rv = NS_ERROR_UNEXPECTED;
     }
 
--- a/layout/forms/nsGfxButtonControlFrame.h
+++ b/layout/forms/nsGfxButtonControlFrame.h
@@ -44,19 +44,19 @@ public:
 
   virtual nsresult AttributeChanged(int32_t         aNameSpaceID,
                                     nsIAtom*        aAttribute,
                                     int32_t         aModType) override;
 
   virtual nsContainerFrame* GetContentInsertionFrame() override;
 
 protected:
-  nsresult GetDefaultLabel(nsXPIDLString& aLabel) const;
+  nsresult GetDefaultLabel(nsAString& aLabel) const;
 
-  nsresult GetLabel(nsXPIDLString& aLabel);
+  nsresult GetLabel(nsString& aLabel);
 
   virtual bool IsInput() override { return true; }
 private:
   nsCOMPtr<nsIContent> mTextContent;
 };
 
 
 #endif
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -6392,33 +6392,34 @@ nsIFrame::GetTransformMatrix(const nsIFr
   nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
   int32_t scaleFactor = (aInCSSUnits ? PresContext()->AppUnitsPerCSSPixel()
                                      : PresContext()->AppUnitsPerDevPixel());
   return Matrix4x4::Translation(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
                                 NSAppUnitsToFloatPixels(delta.y, scaleFactor),
                                 0.0f);
 }
 
-static void InvalidateRenderingObservers(nsIFrame* aFrame)
-{
+static void InvalidateRenderingObservers(nsIFrame* aDisplayRoot, nsIFrame* aFrame)
+{
+  MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame));
   nsSVGEffects::InvalidateDirectRenderingObservers(aFrame);
-  nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
   nsIFrame* parent = aFrame;
-  while (parent != displayRoot &&
+  while (parent != aDisplayRoot &&
          (parent = nsLayoutUtils::GetCrossDocParentFrame(parent)) &&
          !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
     nsSVGEffects::InvalidateDirectRenderingObservers(parent);
   }
 }
 
 void
-SchedulePaintInternal(nsIFrame* aFrame, nsIFrame::PaintType aType = nsIFrame::PAINT_DEFAULT)
-{
-  nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
-  nsPresContext* pres = displayRoot->PresContext()->GetRootPresContext();
+SchedulePaintInternal(nsIFrame* aDisplayRoot, nsIFrame* aFrame,
+                      nsIFrame::PaintType aType = nsIFrame::PAINT_DEFAULT)
+{
+  MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame));
+  nsPresContext* pres = aDisplayRoot->PresContext()->GetRootPresContext();
 
   // No need to schedule a paint for an external document since they aren't
   // painted directly.
   if (!pres || (pres->Document() && pres->Document()->IsResourceDoc())) {
     return;
   }
   if (!pres->GetContainerWeak()) {
     NS_WARNING("Shouldn't call SchedulePaint in a detached pres context");
@@ -6429,17 +6430,17 @@ SchedulePaintInternal(nsIFrame* aFrame, 
                                               nsIPresShell::PAINT_DELAYED_COMPRESS :
                                               nsIPresShell::PAINT_DEFAULT);
 
   if (aType == nsIFrame::PAINT_DELAYED_COMPRESS) {
     return;
   }
 
   if (aType == nsIFrame::PAINT_DEFAULT) {
-    displayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
+    aDisplayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
   }
 }
 
 static void InvalidateFrameInternal(nsIFrame *aFrame, bool aHasDisplayItem = true)
 {
   if (aHasDisplayItem) {
     aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT);
   }
@@ -6467,17 +6468,18 @@ static void InvalidateFrameInternal(nsIF
     if (!parent) {
       needsSchedulePaint = true;
     }
   }
   if (!aHasDisplayItem) {
     return;
   }
   if (needsSchedulePaint) {
-    SchedulePaintInternal(aFrame);
+    nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
+    SchedulePaintInternal(displayRoot, aFrame);
   }
   if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
     aFrame->DeleteProperty(nsIFrame::InvalidationRect());
     aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT);
   }
 }
 
 void
@@ -6667,31 +6669,33 @@ nsIFrame::IsInvalid(nsRect& aRect)
     aRect.SetEmpty();
   }
   return true;
 }
 
 void
 nsIFrame::SchedulePaint(PaintType aType)
 {
-  InvalidateRenderingObservers(this);
-  SchedulePaintInternal(this, aType);
+  nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
+  InvalidateRenderingObservers(displayRoot, this);
+  SchedulePaintInternal(displayRoot, this, aType);
 }
 
 Layer*
 nsIFrame::InvalidateLayer(DisplayItemType aDisplayItemKey,
                           const nsIntRect* aDamageRect,
                           const nsRect* aFrameDamageRect,
                           uint32_t aFlags /* = 0 */)
 {
   NS_ASSERTION(aDisplayItemKey > DisplayItemType::TYPE_ZERO, "Need a key");
 
   Layer* layer = FrameLayerBuilder::GetDedicatedLayer(this, aDisplayItemKey);
 
-  InvalidateRenderingObservers(this);
+  nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
+  InvalidateRenderingObservers(displayRoot, this);
 
   // If the layer is being updated asynchronously, and it's being forwarded
   // to a compositor, then we don't need to invalidate.
   if ((aFlags & UPDATE_IS_ASYNC) && layer && layer->SupportsAsyncUpdate()) {
     return layer;
   }
 
   if (!layer) {
@@ -6727,17 +6731,17 @@ nsIFrame::InvalidateLayer(DisplayItemTyp
   }
 
   if (aDamageRect) {
     layer->AddInvalidRect(*aDamageRect);
   } else {
     layer->SetInvalidRectToVisibleRegion();
   }
 
-  SchedulePaintInternal(this, PAINT_COMPOSITE_ONLY);
+  SchedulePaintInternal(displayRoot, this, PAINT_COMPOSITE_ONLY);
   return layer;
 }
 
 static nsRect
 ComputeEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect,
                    const nsSize& aNewSize)
 {
   nsRect r = aOverflowRect;
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -558,8 +558,18 @@
 @BINPATH@/chrome/marionette.manifest
 @BINPATH@/components/marionette.manifest
 @BINPATH@/components/marionette.js
 #endif
 
 #ifdef PKG_LOCALE_MANIFEST
 #include @PKG_LOCALE_MANIFEST@
 #endif
+
+; Background Hang Monitor
+@BINPATH@/components/backgroundhangmonitor.xpt
+
+; NOTE: This must match the config checks in
+; /toolkit/components/backgroundhangmonitor/moz.build.
+#if defined(NIGHTLY_BUILD) && !defined(MOZ_DEBUG) && !defined(MOZ_TSAN)
+@BINPATH@/components/BHRTelemetryService.js
+@BINPATH@/components/BHRTelemetryService.manifest
+#endif
--- a/mobile/android/tests/browser/robocop/robocop_input.html
+++ b/mobile/android/tests/browser/robocop/robocop_input.html
@@ -21,21 +21,16 @@
       let designMode = document.getElementById("design-mode");
       try {
         designMode.contentDocument.designMode = "on";
       } catch (e) {
         // Setting designMode above sometimes fails, so try again later.
         setTimeout(function() { designMode.contentDocument.designMode = "on" }, 0);
       }
 
-      // Spatial navigation interferes with design-mode key event tests.
-      SpecialPowers.setBoolPref("snav.enabled", false);
-      // Enable "selectionchange" events for input/textarea.
-      SpecialPowers.setBoolPref("dom.select_events.textcontrols.enabled", true);
-
       // An input that resets the editor on every input by resetting the value property.
       let resetting_input = document.getElementById("resetting-input");
       resetting_input.addEventListener("input", function() {
         this.value = this.value;
       });
 
       // An input that hides on input.
       let hiding_input = document.getElementById("hiding-input");
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java
@@ -25,16 +25,22 @@ public class testInputConnection extends
 
     private static final String INITIAL_TEXT = "foo";
 
     private String mEventsLog;
 
     public void testInputConnection() throws InterruptedException {
         GeckoHelper.blockForReady();
 
+        // Spatial navigation interferes with design-mode key event tests.
+        mActions.setPref("snav.enabled", false, /* flush */ false);
+        // Enable "selectionchange" events for input/textarea.
+        mActions.setPref("dom.select_events.enabled", true, /* flush */ false);
+        mActions.setPref("dom.select_events.textcontrols.enabled", true, /* flush */ false);
+
         final String url = mStringHelper.ROBOCOP_INPUT_URL;
         NavigationHelper.enterAndLoadUrl(url);
         mToolbar.assertTitle(url);
 
         // First run tests inside the normal input field.
         getJS().syncCall("focus_input", INITIAL_TEXT);
         mGeckoView.mTextInput
             .waitForInputConnection()
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4645,16 +4645,21 @@ pref("gl.msaa-level", 2);
 #endif
 pref("gl.require-hardware", false);
 #ifdef XP_MACOSX
 pref("gl.multithreaded", true);
 #endif
 pref("gl.ignore-dx-interop2-blacklist", false);
 pref("gl.use-tls-is-current", 0);
 
+#ifdef XP_MACOSX
+pref("webgl.1.allow-core-profiles", true);
+#else
+pref("webgl.1.allow-core-profiles", false);
+#endif
 pref("webgl.force-enabled", false);
 pref("webgl.disabled", false);
 pref("webgl.disable-angle", false);
 pref("webgl.disable-wgl", false);
 pref("webgl.min_capability_mode", false);
 pref("webgl.disable-extensions", false);
 pref("webgl.msaa-force", false);
 pref("webgl.prefer-16bpp", false);
--- a/netwerk/dns/nsIDNService.h
+++ b/netwerk/dns/nsIDNService.h
@@ -174,17 +174,17 @@ private:
   // |mIDNUseWhitelist|.
   //
   // These members can only be updated on the main thread and
   // read on any thread. Therefore, acquiring the mutex is required
   // only for threads other than the main thread.
   mozilla::Mutex mLock;
 
   // guarded by mLock
-  nsXPIDLString mIDNBlacklist;
+  nsString mIDNBlacklist;
 
   /**
    * Flag set by the pref network.IDN_show_punycode. When it is true,
    * IDNs containing non-ASCII characters are always displayed to the
    * user in punycode
    *
    * guarded by mLock
    */
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -7389,17 +7389,19 @@ nsHttpChannel::OnStopRequest(nsIRequest 
         kHttpsNetOK = 10,
         kHttpsNetEarlyFail = 11,
         kHttpsNetLateFail = 12
     } chanDisposition = kHttpCanceled;
 
     // HTTP 0.9 is more likely to be an error than really 0.9, so count it that way
     if (mCanceled) {
         chanDisposition  = kHttpCanceled;
-    } else if (!mUsedNetwork) {
+    } else if (!mUsedNetwork ||
+               (mRaceCacheWithNetwork &&
+                mFirstResponseSource == RESPONSE_FROM_CACHE)) {
         chanDisposition = kHttpDisk;
     } else if (NS_SUCCEEDED(status) &&
                mResponseHead &&
                mResponseHead->Version() != NS_HTTP_VERSION_0_9) {
         chanDisposition = kHttpNetOK;
     } else if (!mTransferSize) {
         chanDisposition = kHttpNetEarlyFail;
     } else {
--- a/netwerk/streamconv/converters/nsIndexedToHTML.cpp
+++ b/netwerk/streamconv/converters/nsIndexedToHTML.cpp
@@ -506,17 +506,17 @@ nsIndexedToHTML::DoOnStartRequest(nsIReq
         nsAutoCString charset;
         rv = platformCharset->GetCharset(kPlatformCharsetSel_FileName, charset);
         NS_ENSURE_SUCCESS(rv, rv);
 
         rv = mTextToSubURI->UnEscapeAndConvert(charset, titleUri, unEscapeSpec);
     }
     if (NS_FAILED(rv)) return rv;
 
-    nsXPIDLString htmlEscSpec;
+    nsString htmlEscSpec;
     htmlEscSpec.Adopt(nsEscapeHTML2(unEscapeSpec.get(),
                                     unEscapeSpec.Length()));
 
     nsAutoString title;
     const char16_t* formatTitle[] = {
         htmlEscSpec.get()
     };
 
--- a/security/manager/ssl/nsCertTree.cpp
+++ b/security/manager/ssl/nsCertTree.cpp
@@ -54,16 +54,17 @@ CompareCacheHashEntryPtr::~CompareCacheH
   delete entry;
 }
 
 CompareCacheHashEntry::CompareCacheHashEntry()
 :key(nullptr)
 {
   for (int i = 0; i < max_criterions; ++i) {
     mCritInit[i] = false;
+    mCrit[i].SetIsVoid(true);
   }
 }
 
 static bool
 CompareCacheMatchEntry(const PLDHashEntryHdr *hdr, const void *key)
 {
   const CompareCacheHashEntryPtr *entryPtr = static_cast<const CompareCacheHashEntryPtr*>(hdr);
   return entryPtr->entry->key == key;
@@ -1276,17 +1277,17 @@ NS_IMETHODIMP nsCertTree::IsSorted(bool 
 
 void
 nsCertTree::CmpInitCriterion(nsIX509Cert *cert, CompareCacheHashEntry *entry,
                              sortCriterion crit, int32_t level)
 {
   NS_ENSURE_TRUE(cert && entry, RETURN_NOTHING);
 
   entry->mCritInit[level] = true;
-  nsXPIDLString &str = entry->mCrit[level];
+  nsString& str = entry->mCrit[level];
 
   switch (crit) {
     case sort_IssuerOrg:
       cert->GetIssuerOrganization(str);
       if (str.IsEmpty())
         cert->GetCommonName(str);
       break;
     case sort_Org:
@@ -1338,24 +1339,24 @@ nsCertTree::CmpByCrit(nsIX509Cert *a, Co
   if (!ace->mCritInit[level]) {
     CmpInitCriterion(a, ace, crit, level);
   }
 
   if (!bce->mCritInit[level]) {
     CmpInitCriterion(b, bce, crit, level);
   }
 
-  nsXPIDLString &str_a = ace->mCrit[level];
-  nsXPIDLString &str_b = bce->mCrit[level];
+  nsString& str_a = ace->mCrit[level];
+  nsString& str_b = bce->mCrit[level];
 
   int32_t result;
-  if (str_a && str_b)
+  if (!str_a.IsVoid() && !str_b.IsVoid())
     result = Compare(str_a, str_b, nsCaseInsensitiveStringComparator());
   else
-    result = !str_a ? (!str_b ? 0 : -1) : 1;
+    result = str_a.IsVoid() ? (str_b.IsVoid() ? 0 : -1) : 1;
 
   if (sort_IssuedDateDescending == crit)
     result *= -1; // reverse compare order
 
   return result;
 }
 
 int32_t
--- a/security/manager/ssl/nsCertTree.h
+++ b/security/manager/ssl/nsCertTree.h
@@ -22,17 +22,17 @@
 typedef struct treeArrayElStr treeArrayEl;
 
 struct CompareCacheHashEntry {
   enum { max_criterions = 3 };
   CompareCacheHashEntry();
 
   void *key; // no ownership
   bool mCritInit[max_criterions];
-  nsXPIDLString mCrit[max_criterions];
+  nsString mCrit[max_criterions];
 };
 
 struct CompareCacheHashEntryPtr : PLDHashEntryHdr {
   CompareCacheHashEntryPtr();
   ~CompareCacheHashEntryPtr();
   CompareCacheHashEntry *entry;
 };
 
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -249,16 +249,19 @@ user_pref("browser.contentHandlers.types
 user_pref("browser.contentHandlers.types.4.uri", "http://test1.example.org/rss?url=%%s")
 user_pref("browser.contentHandlers.types.5.uri", "http://test1.example.org/rss?url=%%s")
 
 // We want to collect telemetry, but we don't want to send in the results.
 user_pref("toolkit.telemetry.server", "https://%(server)s/telemetry-dummy/");
 // Don't send 'new-profile' ping on new profiles during tests, otherwise the testing framework
 // might wait on the pingsender to finish and slow down tests.
 user_pref("toolkit.telemetry.newProfilePing.enabled", false);
+// Don't send 'bhr' ping during tests, otherwise the testing framework might
+// wait on the pingsender to finish and slow down tests.
+user_pref("toolkit.telemetry.bhrPing.enabled", false);
 // Don't send the 'shutdown' ping using the pingsender on the first session using
 // the 'pingsender' process. Valgrind marks the process as leaky (e.g. see bug 1364068
 // for the 'new-profile' ping) but does not provide enough information
 // to suppress the leak. Running locally does not reproduce the issue,
 // so disable this until we rewrite the pingsender in Rust (bug 1339035).
 user_pref("toolkit.telemetry.shutdownPingSender.enabledFirstSession", false);
 
 // A couple of preferences with default values to test that telemetry preference
new file mode 100644
--- /dev/null
+++ b/toolkit/components/backgroundhangmonitor/BHRTelemetryService.js
@@ -0,0 +1,119 @@
+/* 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/. */
+
+"use strict";
+
+const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+Cu.import("resource://gre/modules/Services.jsm", this);
+
+XPCOMUtils.defineLazyModuleGetter(this, "TelemetryController",
+                                  "resource://gre/modules/TelemetryController.jsm");
+
+function BHRTelemetryService() {
+  Services.obs.addObserver(this, "profile-before-change");
+  Services.obs.addObserver(this, "bhr-thread-hang");
+
+  this.resetPayload();
+}
+
+BHRTelemetryService.prototype = Object.freeze({
+  classID: Components.ID("{117c8cdf-69e6-4f31-a439-b8a654c67127}"),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+
+  TRANSMIT_HANG_COUNT: 50,
+
+  resetPayload() {
+    this.payload = {
+      modules: [],
+      hangs: [],
+    };
+  },
+
+  recordHang({duration, thread, runnableName, process, stack,
+              modules, annotations, pseudoStack}) {
+    if (!Services.telemetry.canRecordExtended) {
+      return;
+    }
+
+    // Create a mapping from module indicies in the original nsIHangDetails
+    // object to this.payload.modules indicies.
+    let moduleIdxs = modules.map(module => {
+      let idx = this.payload.modules.findIndex(m => {
+        return m[0] === module[0] && m[1] === module[1];
+      });
+      if (idx === -1) {
+        idx = this.payload.modules.length;
+        this.payload.modules.push(module);
+      }
+      return idx;
+    });
+
+    // Native stack frames are [modIdx, offset] arrays. If we have a valid
+    // module index, we want to map it to the this.payload.modules array.
+    for (let i = 0; i < stack.length; ++i) {
+      if (Array.isArray(stack[i]) && stack[i][0] !== -1) {
+        stack[i][0] = moduleIdxs[stack[i][0]];
+      }
+    }
+
+    // Create the hang object to record in the payload.
+    this.payload.hangs.push({
+      duration,
+      thread,
+      runnableName,
+      process,
+      annotations,
+      pseudoStack,
+      stack,
+    });
+
+    // If we have collected enough hangs, we can submit the hangs we have
+    // collected to telemetry.
+    if (this.payload.hangs.length > this.TRANSMIT_HANG_COUNT) {
+      this.submit();
+    }
+  },
+
+  submit() {
+    if (!Services.telemetry.canRecordExtended) {
+      return;
+    }
+
+    // NOTE: We check a separate bhrPing.enabled pref here. This pref is unset
+    // when running tests so that we run as much of BHR as possible (to catch
+    // errors) while avoiding timeouts caused by invoking `pingsender` during
+    // testing.
+    if (Services.prefs.getBoolPref("toolkit.telemetry.bhrPing.enabled", false) &&
+        this.payload.hangs.length > 0) {
+      TelemetryController.submitExternalPing("bhr", this.payload, {
+        addEnvironment: true,
+      });
+    }
+    this.resetPayload();
+  },
+
+  shutdown() {
+    Services.obs.removeObserver(this, "profile-before-change");
+    Services.obs.removeObserver(this, "bhr-thread-hang");
+    this.submit();
+  },
+
+  observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
+    case "profile-after-change":
+      this.resetPayload();
+      break;
+    case "bhr-thread-hang":
+      this.recordHang(aSubject.QueryInterface(Ci.nsIHangDetails));
+      break;
+    case "profile-before-change":
+      this.shutdown();
+      break;
+    }
+  },
+});
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([BHRTelemetryService]);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/backgroundhangmonitor/BHRTelemetryService.manifest
@@ -0,0 +1,3 @@
+component {117c8cdf-69e6-4f31-a439-b8a654c67127} BHRTelemetryService.js process=main
+contract @mozilla.org/bhr-telemetry-service;1 {117c8cdf-69e6-4f31-a439-b8a654c67127} process=main
+category profile-after-change BHRTelemetryService @mozilla.org/bhr-telemetry-service;1 process=main
\ No newline at end of file
rename from xpcom/threads/BackgroundHangMonitor.cpp
rename to toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
--- a/xpcom/threads/BackgroundHangMonitor.cpp
+++ b/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
@@ -7,55 +7,53 @@
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Move.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
-#include "mozilla/ThreadHangStats.h"
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/SystemGroup.h"
 
 #include "prinrval.h"
 #include "prthread.h"
 #include "ThreadStackHelper.h"
 #include "nsIObserverService.h"
 #include "nsIObserver.h"
 #include "mozilla/Services.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "GeckoProfiler.h"
 #include "nsNetCID.h"
-#include "nsIHangDetails.h"
+#include "HangDetails.h"
 
 #include <algorithm>
 
 // Activate BHR only for one every BHR_BETA_MOD users.
-// This is now 100% of Beta population for the Beta 45/46 e10s A/B trials
-// It can be scaled back again in the future
-#define BHR_BETA_MOD 1;
+// We're doing experimentation with collecting a lot more data from BHR, and
+// don't want to enable it for beta users at the moment. We can scale this up in
+// the future.
+#define BHR_BETA_MOD INT32_MAX;
 
 // Maximum depth of the call stack in the reported thread hangs. This value represents
 // the 99.9th percentile of the thread hangs stack depths reported by Telemetry.
 static const size_t kMaxThreadHangStackDepth = 30;
 
 // An utility comparator function used by std::unique to collapse "(* script)" entries in
 // a vector representing a call stack.
 bool StackScriptEntriesCollapser(const char* aStackEntry, const char *aAnotherStackEntry)
 {
   return !strcmp(aStackEntry, aAnotherStackEntry) &&
          (!strcmp(aStackEntry, "(chrome script)") || !strcmp(aStackEntry, "(content script)"));
 }
 
 namespace mozilla {
 
-class ProcessHangRunnable;
-
 /**
  * BackgroundHangManager is the global object that
  * manages all instances of BackgroundHangThread.
  */
 class BackgroundHangManager : public nsIObserver
 {
 private:
   // Background hang monitor thread function
@@ -183,30 +181,27 @@ public:
   bool mHanging;
   // Is the thread in a waiting state
   bool mWaiting;
   // Is the thread dedicated to a single BackgroundHangMonitor
   BackgroundHangMonitor::ThreadType mThreadType;
   // Platform-specific helper to get hang stacks
   ThreadStackHelper mStackHelper;
   // Stack of current hang
-  Telemetry::HangStack mHangStack;
+  HangStack mHangStack;
   // Native stack of current hang
-  Telemetry::NativeHangStack mNativeHangStack;
-  // Statistics for telemetry
-  Telemetry::ThreadHangStats mStats;
+  NativeHangStack mNativeHangStack;
   // Annotations for the current hang
-  UniquePtr<HangMonitor::HangAnnotations> mAnnotations;
+  HangMonitor::HangAnnotations mAnnotations;
   // Annotators registered for this thread
   HangMonitor::Observer::Annotators mAnnotators;
-  // List of runnables which can hold a reference to us which need to be
-  // canceled before we can go away.
-  LinkedList<RefPtr<ProcessHangRunnable>> mProcessHangRunnables;
   // 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,
                        BackgroundHangMonitor::ThreadType aThreadType = BackgroundHangMonitor::THREAD_SHARED);
 
   // Report a hang; aManager->mLock IS locked. The hang will be processed
   // off-main-thread, and will then be submitted back.
@@ -234,38 +229,16 @@ public:
 
   // Returns true if this thread is (or might be) shared between other
   // BackgroundHangMonitors for the monitored thread.
   bool IsShared() {
     return mThreadType == BackgroundHangMonitor::THREAD_SHARED;
   }
 };
 
-/**
- * HangDetails is the concrete implementaion of nsIHangDetails, and contains the
- * infromation which we want to expose to observers of the bhr-thread-hang
- * observer notification.
- */
-class HangDetails : public nsIHangDetails
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIHANGDETAILS
-
-  HangDetails(uint32_t aDuration, const nsACString& aName)
-    : mDuration(aDuration)
-    , mName(aName)
-    {}
-private:
-  virtual ~HangDetails() {}
-
-  uint32_t mDuration;
-  nsCString mName;
-};
-
 StaticRefPtr<BackgroundHangManager> BackgroundHangManager::sInstance;
 bool BackgroundHangManager::sDisabled = false;
 
 MOZ_THREAD_LOCAL(BackgroundHangThread*) BackgroundHangThread::sTlsKey;
 bool BackgroundHangThread::sTlsKeyInitialized;
 
 BackgroundHangManager::BackgroundHangManager()
   : mShutdown(false)
@@ -368,28 +341,22 @@ BackgroundHangManager::RunMonitorThread(
         currentThread->ReportPermaHang();
         continue;
       }
 
       if (MOZ_LIKELY(!currentThread->mHanging)) {
         if (MOZ_UNLIKELY(hangTime >= currentThread->mTimeout)) {
           // A hang started
 #ifdef NIGHTLY_BUILD
-          if (currentThread->mStats.mNativeStackCnt < Telemetry::kMaximumNativeHangStacks) {
-            // NOTE: In nightly builds of firefox we want to collect native stacks
-            // for all hangs, not just permahangs.
-            currentThread->mStats.mNativeStackCnt += 1;
-            currentThread->mStackHelper.GetPseudoAndNativeStack(
-              currentThread->mHangStack,
-              currentThread->mNativeHangStack,
-              currentThread->mRunnableName);
-          } else {
-            currentThread->mStackHelper.GetPseudoStack(currentThread->mHangStack,
-                                                       currentThread->mRunnableName);
-          }
+          // NOTE: In nightly builds of firefox we want to collect native stacks
+          // for all hangs, not just permahangs.
+          currentThread->mStackHelper.GetPseudoAndNativeStack(
+            currentThread->mHangStack,
+            currentThread->mNativeHangStack,
+            currentThread->mRunnableName);
 #else
           currentThread->mStackHelper.GetPseudoStack(currentThread->mHangStack,
                                                      currentThread->mRunnableName);
 #endif
           currentThread->mHangStart = interval;
           currentThread->mHanging = true;
           currentThread->mAnnotations =
             currentThread->mAnnotators.GatherAnnotations();
@@ -441,140 +408,42 @@ BackgroundHangThread::BackgroundHangThre
   , mMaxTimeout(aMaxTimeoutMs == BackgroundHangMonitor::kNoTimeout
                 ? PR_INTERVAL_NO_TIMEOUT
                 : PR_MillisecondsToInterval(aMaxTimeoutMs))
   , mInterval(mManager->mIntervalNow)
   , mHangStart(mInterval)
   , mHanging(false)
   , mWaiting(true)
   , mThreadType(aThreadType)
-  , mStats(aName)
+  , mThreadName(aName)
 {
   if (sTlsKeyInitialized && IsShared()) {
     sTlsKey.set(this);
   }
   // Lock here because LinkedList is not thread-safe
   MonitorAutoLock autoLock(mManager->mLock);
   // Add to thread list
   mManager->mHangThreads.insertBack(this);
   // Wake up monitor thread to process new thread
   autoLock.Notify();
 }
 
-// This runnable is used to pre-process a hang, performing any expensive
-// operations on it, before submitting it into the BackgroundHangThread object
-// for Telemetry.
-//
-// If this object is canceled, it will submit its payload to the
-// BackgroundHangThread without performing the processing.
-class ProcessHangRunnable final
-  : public CancelableRunnable
-  , public LinkedListElement<RefPtr<ProcessHangRunnable>>
-{
-public:
-  ProcessHangRunnable(BackgroundHangManager* aManager,
-                      BackgroundHangThread* aThread,
-                      Telemetry::HangHistogram&& aHistogram,
-                      Telemetry::NativeHangStack&& aNativeStack)
-    : CancelableRunnable("ProcessHangRunnable")
-    , mManager(aManager)
-    , mNativeStack(mozilla::Move(aNativeStack))
-    , mThread(aThread)
-    , mHistogram(mozilla::Move(aHistogram))
-  {
-    MOZ_ASSERT(mThread);
-  }
-
-  NS_IMETHOD
-  Run() override
-  {
-    // Start processing this histogram's native hang stack before we try to lock
-    // anything, as we can do this without any locks held. This is the expensive
-    // part of the operation.
-    Telemetry::ProcessedStack processed;
-    if (!mNativeStack.empty()) {
-       processed = Telemetry::GetStackAndModules(mNativeStack);
-    }
-
-    // Lock the manager's lock, so that we can take a look at our mThread
-    {
-      MonitorAutoLock autoLock(mManager->mLock);
-      if (NS_WARN_IF(!mThread)) {
-        return NS_OK;
-      }
-
-      // If we have a stack, check if we can add it to combined stacks. This is
-      // a relatively cheap operation, and must occur with the lock held.
-      if (!mNativeStack.empty() &&
-          mThread->mStats.mCombinedStacks.GetStackCount() < Telemetry::kMaximumNativeHangStacks) {
-        mHistogram.SetNativeStackIndex(mThread->mStats.mCombinedStacks.AddStack(processed));
-      }
-
-      // Submit, remove ourselves from the list, and clear out mThread so we
-      // don't run again.
-      MOZ_ALWAYS_TRUE(mThread->mStats.mHangs.append(Move(mHistogram)));
-      remove();
-      mThread = nullptr;
-    }
-
-    return NS_OK;
-  }
-
-  // Submits hang, and removes from list.
-  nsresult
-  Cancel() override
-  {
-    mManager->mLock.AssertCurrentThreadOwns();
-    if (NS_WARN_IF(!mThread)) {
-      return NS_OK;
-    }
-
-    // Submit, remove ourselves from the list, and clear out mThread so we
-    // don't run again.
-    MOZ_ALWAYS_TRUE(mThread->mStats.mHangs.append(Move(mHistogram)));
-    if (isInList()) {
-      remove();
-    }
-    mThread = nullptr;
-    return NS_OK;
-  }
-
-private:
-  // These variables are constant after initialization, and do not need
-  // synchronization.
-  RefPtr<BackgroundHangManager> mManager;
-  const Telemetry::NativeHangStack mNativeStack;
-  // These variables are guarded by mManager->mLock.
-  BackgroundHangThread* MOZ_NON_OWNING_REF mThread; // Will Cancel us before it dies
-  Telemetry::HangHistogram mHistogram;
-};
-
 BackgroundHangThread::~BackgroundHangThread()
 {
   // Lock here because LinkedList is not thread-safe
   MonitorAutoLock autoLock(mManager->mLock);
   // Remove from thread list
   remove();
   // Wake up monitor thread to process removed thread
   autoLock.Notify();
 
   // We no longer have a thread
   if (sTlsKeyInitialized && IsShared()) {
     sTlsKey.set(nullptr);
   }
-
-  // Cancel any remaining process hang runnables, as they hold a weak reference
-  // into our mStats variable, which we're about to move.
-  while (RefPtr<ProcessHangRunnable> runnable = mProcessHangRunnables.popFirst()) {
-    runnable->Cancel();
-  }
-
-  // Record the ThreadHangStats for this thread before we go away. All stats
-  // should be in this method now, as we canceled any pending runnables.
-  Telemetry::RecordThreadHangStats(Move(mStats));
 }
 
 void
 BackgroundHangThread::ReportHang(PRIntervalTime aHangTime)
 {
   // Recovered from a hang; called on the monitor thread
   // mManager->mLock IS locked
 
@@ -597,70 +466,33 @@ BackgroundHangThread::ReportHang(PRInter
   if (mHangStack.length() > kMaxThreadHangStackDepth) {
     const int elementsToRemove = mHangStack.length() - kMaxThreadHangStackDepth;
     // Replace the oldest frame with a known label so that we can tell this stack
     // was limited.
     mHangStack[0] = "(reduced stack)";
     mHangStack.erase(mHangStack.begin() + 1, mHangStack.begin() + elementsToRemove);
   }
 
-  Telemetry::HangHistogram newHistogram(Move(mHangStack), mRunnableName);
-  for (Telemetry::HangHistogram* oldHistogram = mStats.mHangs.begin();
-       oldHistogram != mStats.mHangs.end(); oldHistogram++) {
-    if (newHistogram == *oldHistogram) {
-      // New histogram matches old one
-      oldHistogram->Add(aHangTime, Move(mAnnotations));
-      return;
-    }
-  }
-  newHistogram.Add(aHangTime, Move(mAnnotations));
-
-  // Notify any observers of the "bhr-thread-hang" topic that a thread has hung.
-  nsCString name;
-  name.AssignASCII(mStats.GetName());
-  nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction("NotifyBHRHangObservers", [=] {
-    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-    if (os) {
-      // NOTE: Make sure to construct this on the main thread.
-      nsCOMPtr<nsIHangDetails> hangDetails = new HangDetails(aHangTime, name);
-      os->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr);
-    }
-  });
-  if (SystemGroup::Initialized()) {
-    // XXX(HACK): This is really sketchy. We need to keep a reference to the
-    // runnable in case the dispatch fails. If it fails, the already_AddRefed
-    // runnable which we passed in has been leaked, and we need to free it
-    // ourselves. The only time when this should fail is if we're shutting down.
-    //
-    // Most components just avoid dispatching runnables during shutdown, but BHR
-    // is not shut down until way too late, so we cannot do that. Instead, we
-    // just detect that the dispatch failed and manually unleak the leaked
-    // nsIRunnable in that situation.
-    nsresult rv = SystemGroup::Dispatch(TaskCategory::Other,
-                                        do_AddRef(runnable.get()));
-    if (NS_FAILED(rv)) {
-      // NOTE: We go through `get()` here in order to avoid the
-      // MOZ_NO_ADDREF_RELEASE_ON_RETURN static analysis.
-      nsrefcnt refcnt = runnable.get()->Release();
-      MOZ_RELEASE_ASSERT(refcnt == 1, "runnable should have had 1 reference leaked");
-    }
-  }
-
-  // Process the hang off-main thread. We record a reference to the runnable in
-  // mProcessHangRunnables so we can abort this preprocessing and just submit
-  // the message if the processing takes too long and our thread is going away.
-  RefPtr<ProcessHangRunnable> processHang =
-    new ProcessHangRunnable(mManager, this, Move(newHistogram), Move(mNativeHangStack));
-  mProcessHangRunnables.insertFront(processHang);
-
-  // Try to dispatch the runnable to the StreamTransportService threadpool. If
-  // we fail, cancel our runnable.
-  if (!mManager->mSTS || NS_FAILED(mManager->mSTS->Dispatch(processHang.forget()))) {
-    RefPtr<ProcessHangRunnable> runnable = mProcessHangRunnables.popFirst();
-    runnable->Cancel();
+  HangDetails hangDetails(aHangTime,
+                          XRE_GetProcessType(),
+                          mThreadName,
+                          mRunnableName,
+                          Move(mHangStack),
+                          Move(mAnnotations));
+  // If we have the stream transport service avaliable, 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->mSTS) {
+    nsCOMPtr<nsIRunnable> processHangStackRunnable =
+      new ProcessHangStackRunnable(Move(hangDetails), Move(mNativeHangStack));
+    mManager->mSTS->Dispatch(processHangStackRunnable.forget());
+  } else {
+    NS_WARNING("Unable to report native stack without a StreamTransportService");
+    RefPtr<nsHangDetails> hd = new nsHangDetails(Move(hangDetails));
+    hd->Submit();
   }
 }
 
 void
 BackgroundHangThread::ReportPermaHang()
 {
   // Permanently hanged; called on the monitor thread
   // mManager->mLock IS locked
@@ -681,17 +513,16 @@ BackgroundHangThread::Update()
   if (mWaiting) {
     mInterval = intervalNow;
     mWaiting = false;
     /* We have to wake up the manager thread because when all threads
        are waiting, the manager thread waits indefinitely as well. */
     mManager->Wakeup();
   } else {
     PRIntervalTime duration = intervalNow - mInterval;
-    mStats.mActivity.Add(duration);
     if (MOZ_UNLIKELY(duration >= mTimeout)) {
       /* Wake up the manager thread to tell it that a hang ended */
       mManager->Wakeup();
     }
     mInterval = intervalNow;
   }
 }
 
@@ -892,52 +723,9 @@ BackgroundHangMonitor::UnregisterAnnotat
     return false;
   }
   return thisThread->mAnnotators.Unregister(aAnnotator);
 #else
   return false;
 #endif
 }
 
-/* Because we are iterating through the BackgroundHangThread linked list,
-   we need to take a lock. Using MonitorAutoLock as a base class makes
-   sure all of that is taken care of for us. */
-BackgroundHangMonitor::ThreadHangStatsIterator::ThreadHangStatsIterator()
-  : MonitorAutoLock(BackgroundHangManager::sInstance->mLock)
-  , mThread(BackgroundHangManager::sInstance ?
-            BackgroundHangManager::sInstance->mHangThreads.getFirst() :
-            nullptr)
-{
-#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
-  MOZ_ASSERT(BackgroundHangManager::sInstance ||
-             BackgroundHangManager::sDisabled,
-             "Inconsistent state");
-#endif
-}
-
-Telemetry::ThreadHangStats*
-BackgroundHangMonitor::ThreadHangStatsIterator::GetNext()
-{
-  if (!mThread) {
-    return nullptr;
-  }
-  Telemetry::ThreadHangStats* stats = &mThread->mStats;
-  mThread = mThread->getNext();
-  return stats;
-}
-
-NS_IMETHODIMP
-HangDetails::GetDuration(uint32_t* aDuration)
-{
-  *aDuration = mDuration;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-HangDetails::GetThreadName(nsACString& aName)
-{
-  aName.Assign(mName);
-  return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS(HangDetails, nsIHangDetails)
-
 } // namespace mozilla
rename from xpcom/threads/BackgroundHangMonitor.h
rename to toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.h
--- a/xpcom/threads/BackgroundHangMonitor.h
+++ b/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.h
@@ -12,20 +12,16 @@
 #include "mozilla/RefPtr.h"
 
 #include "nsString.h"
 
 #include <stdint.h>
 
 namespace mozilla {
 
-namespace Telemetry {
-class ThreadHangStats;
-} // namespace Telemetry
-
 class BackgroundHangThread;
 class BackgroundHangManager;
 
 /**
  * The background hang monitor is responsible for detecting and reporting
  * hangs in main and background threads. A thread registers itself using
  * the BackgroundHangMonitor object and periodically calls its methods to
  * inform the hang monitor of the thread's activity. Each thread is given
@@ -123,54 +119,16 @@ public:
     THREAD_SHARED,
     // For a new BackgroundHangMonitor for thread T, create a new
     // monitoring thread for T even if there are other, pre-existing
     // monitoring threads for T.
     THREAD_PRIVATE
   };
 
   /**
-   * ThreadHangStatsIterator is used to iterate through the ThreadHangStats
-   * associated with each active monitored thread. Because of an internal
-   * lock while this object is alive, a thread must use only one instance
-   * of this class at a time and must iterate through the list as fast as
-   * possible. The following example shows using the iterator:
-   *
-   * {
-   *   // Scope the iter variable so it's destroyed as soon as we're done
-   *   BackgroundHangMonitor::ThreadHangStatsIterator iter;
-   *   for (ThreadHangStats* histogram = iter.GetNext();
-   *        histogram; histogram = iter.GetNext()) {
-   *     // Process histogram
-   *   }
-   * }
-   */
-  class ThreadHangStatsIterator : public MonitorAutoLock
-  {
-  private:
-    BackgroundHangThread* mThread;
-
-    ThreadHangStatsIterator(const ThreadHangStatsIterator&);
-    ThreadHangStatsIterator& operator=(const ThreadHangStatsIterator&);
-
-  public:
-    /**
-     * Create an ThreadHangStatsIterator instance and take the internal lock.
-     * Internal lock is released on destruction.
-     */
-    ThreadHangStatsIterator();
-
-    /**
-     * Get the next item in the list; the first call returns the first item.
-     * Returns nullptr at the end of the list.
-     */
-    Telemetry::ThreadHangStats* GetNext();
-  };
-
-  /**
    * Enable hang monitoring.
    * Must return before using BackgroundHangMonitor.
    */
   static void Startup();
 
   /**
    * Disable hang monitoring.
    * Can be called without destroying all BackgroundHangMonitors first.
new file mode 100644
--- /dev/null
+++ b/toolkit/components/backgroundhangmonitor/HangDetails.cpp
@@ -0,0 +1,342 @@
+#include "HangDetails.h"
+#include "nsIHangDetails.h"
+#include "nsPrintfCString.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/Unused.h"
+#include "mozilla/GfxMessageUtils.h" // For ParamTraits<GeckoProcessType>
+
+namespace mozilla {
+
+NS_IMETHODIMP
+nsHangDetails::GetDuration(uint32_t* aDuration)
+{
+  *aDuration = mDetails.mDuration;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHangDetails::GetThread(nsACString& aName)
+{
+  aName.Assign(mDetails.mThreadName);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHangDetails::GetRunnableName(nsACString& aRunnableName)
+{
+  aRunnableName.Assign(mDetails.mRunnableName);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHangDetails::GetProcess(nsACString& aName)
+{
+  aName.AssignASCII(XRE_ChildProcessTypeToString(mDetails.mProcess));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHangDetails::GetStack(JSContext* aCx, JS::MutableHandleValue aVal)
+{
+  size_t length = mDetails.mStack.GetStackSize();
+  JS::RootedObject retObj(aCx, JS_NewArrayObject(aCx, length));
+  if (!retObj) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  for (size_t i = 0; i < length; ++i) {
+    auto& frame = mDetails.mStack.GetFrame(i);
+    JS::RootedObject jsFrame(aCx, JS_NewArrayObject(aCx, 2));
+    if (!jsFrame) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    if (!JS_DefineElement(aCx, jsFrame, 0, frame.mModIndex, JSPROP_ENUMERATE)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    nsPrintfCString hexString("%" PRIxPTR, frame.mOffset);
+    JS::RootedString hex(aCx, JS_NewStringCopyZ(aCx, hexString.get()));
+    if (!hex || !JS_DefineElement(aCx, jsFrame, 1, hex, JSPROP_ENUMERATE)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    if (!JS_DefineElement(aCx, retObj, i, jsFrame, JSPROP_ENUMERATE)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
+
+  aVal.setObject(*retObj);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHangDetails::GetModules(JSContext* aCx, JS::MutableHandleValue aVal)
+{
+  size_t length = mDetails.mStack.GetNumModules();
+  JS::RootedObject retObj(aCx, JS_NewArrayObject(aCx, length));
+  if (!retObj) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  for (size_t i = 0; i < length; ++i) {
+    auto& module = mDetails.mStack.GetModule(i);
+    JS::RootedObject jsModule(aCx, JS_NewArrayObject(aCx, 2));
+    if (!jsModule) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    JS::RootedString name(aCx, JS_NewUCStringCopyZ(aCx, module.mName.get()));
+    if (!name || !JS_DefineElement(aCx, jsModule, 0, name, JSPROP_ENUMERATE)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    JS::RootedString id(aCx, JS_NewStringCopyZ(aCx, module.mBreakpadId.c_str()));
+    if (!id || !JS_DefineElement(aCx, jsModule, 1, id, JSPROP_ENUMERATE)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    if (!JS_DefineElement(aCx, retObj, i, jsModule, JSPROP_ENUMERATE)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
+
+  aVal.setObject(*retObj);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHangDetails::GetAnnotations(JSContext* aCx, JS::MutableHandleValue aVal)
+{
+  // We create an object with { "key" : "value" } string pairs for each item in
+  // our annotations object.
+  JS::RootedObject jsAnnotation(aCx, JS_NewPlainObject(aCx));
+  if (!jsAnnotation) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  for (auto& annot : mDetails.mAnnotations) {
+    JSString* jsString = JS_NewUCStringCopyN(aCx, annot.mValue.get(), annot.mValue.Length());
+    if (!jsString) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    JS::RootedValue jsValue(aCx);
+    jsValue.setString(jsString);
+    if (!JS_DefineUCProperty(aCx, jsAnnotation, annot.mName.get(), annot.mName.Length(),
+                             jsValue, JSPROP_ENUMERATE)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
+
+  aVal.setObject(*jsAnnotation);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHangDetails::GetPseudoStack(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
+{
+  JS::RootedObject ret(aCx, JS_NewArrayObject(aCx, mDetails.mPseudoStack.length()));
+  if (!ret) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  for (size_t i = 0; i < mDetails.mPseudoStack.length(); ++i) {
+    JSString* jsString = JS_NewStringCopyZ(aCx, mDetails.mPseudoStack[i]);
+    if (!jsString) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    JS::RootedString string(aCx, jsString);
+    if (!string) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    if (!JS_DefineElement(aCx, ret, i, string, JSPROP_ENUMERATE)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
+
+  aVal.setObject(*ret);
+  return NS_OK;
+}
+
+// Processing and submitting the stack as an observer notification.
+
+void
+nsHangDetails::Submit()
+{
+  if (NS_WARN_IF(!SystemGroup::Initialized())) {
+    return;
+  }
+
+  RefPtr<nsHangDetails> hangDetails = this;
+  nsCOMPtr<nsIRunnable> notifyObservers = NS_NewRunnableFunction("NotifyBHRHangObservers", [hangDetails] {
+    // The place we need to report the hang to varies depending on process.
+    //
+    // In child processes, we report the hang to our parent process, while if
+    // we're in the parent process, we report a bhr-thread-hang observer
+    // notification.
+    switch (XRE_GetProcessType()) {
+    case GeckoProcessType_Content: {
+      auto cc = dom::ContentChild::GetSingleton();
+      if (cc) {
+        Unused << cc->SendBHRThreadHang(hangDetails->mDetails);
+      }
+      break;
+    }
+    case GeckoProcessType_GPU: {
+      auto gp = gfx::GPUParent::GetSingleton();
+      if (gp) {
+        Unused << gp->SendBHRThreadHang(hangDetails->mDetails);
+      }
+      break;
+    }
+    case GeckoProcessType_Default: {
+      nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+      if (os) {
+        os->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr);
+      }
+      break;
+    }
+    default:
+      // XXX: Consider handling GeckoProcessType_GMPlugin and
+      // GeckoProcessType_Plugin?
+      NS_WARNING("Unsupported BHR process type - discarding hang.");
+      break;
+    }
+  });
+
+  nsresult rv = SystemGroup::Dispatch(TaskCategory::Other,
+                                      notifyObservers.forget());
+  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+}
+
+NS_IMPL_ISUPPORTS(nsHangDetails, nsIHangDetails)
+
+NS_IMETHODIMP
+ProcessHangStackRunnable::Run()
+{
+  // NOTE: This is an expensive operation on some platforms, so we do it off of
+  // any other critical path, moving it onto the StreamTransportService.
+  mHangDetails.mStack = Telemetry::GetStackAndModules(mNativeStack);
+
+  RefPtr<nsHangDetails> hangDetails = new nsHangDetails(Move(mHangDetails));
+  hangDetails->Submit();
+
+  return NS_OK;
+}
+
+} // namespace mozilla
+
+
+/**
+ * IPC Serialization / Deserialization logic
+ */
+namespace IPC {
+
+void
+ParamTraits<mozilla::HangDetails>::Write(Message* aMsg, const mozilla::HangDetails& aParam)
+{
+  WriteParam(aMsg, aParam.mDuration);
+  WriteParam(aMsg, aParam.mProcess);
+  WriteParam(aMsg, aParam.mThreadName);
+  WriteParam(aMsg, aParam.mRunnableName);
+  WriteParam(aMsg, aParam.mPseudoStack);
+
+  WriteParam(aMsg, aParam.mAnnotations);
+
+  // NOTE: ProcessedStack will stop being used for BHR in bug 1367406, so this
+  // inline serialization will survive until then.
+
+  // Write out the native stack module information
+  {
+    size_t length = aParam.mStack.GetNumModules();
+    WriteParam(aMsg, length);
+    for (size_t i = 0; i < length; ++i) {
+      auto& module = aParam.mStack.GetModule(i);
+      WriteParam(aMsg, module.mName);
+      WriteParam(aMsg, module.mBreakpadId);
+    }
+  }
+
+  // Native stack frame information
+  {
+    size_t length = aParam.mStack.GetStackSize();
+    WriteParam(aMsg, length);
+    for (size_t i = 0; i < length; ++i) {
+      auto& frame = aParam.mStack.GetFrame(i);
+      WriteParam(aMsg, frame.mOffset);
+      WriteParam(aMsg, frame.mModIndex);
+    }
+  }
+}
+
+bool
+ParamTraits<mozilla::HangDetails>::Read(const Message* aMsg,
+                                        PickleIterator* aIter,
+                                        mozilla::HangDetails* aResult)
+{
+  if (!ReadParam(aMsg, aIter, &aResult->mDuration)) {
+    return false;
+  }
+  if (!ReadParam(aMsg, aIter, &aResult->mProcess)) {
+    return false;
+  }
+  if (!ReadParam(aMsg, aIter, &aResult->mThreadName)) {
+    return false;
+  }
+  if (!ReadParam(aMsg, aIter, &aResult->mRunnableName)) {
+    return false;
+  }
+  if (!ReadParam(aMsg, aIter, &aResult->mPseudoStack)) {
+    return false;
+  }
+
+  // Annotation information
+  if (!ReadParam(aMsg, aIter, &aResult->mAnnotations)) {
+    return false;
+  }
+
+  // NOTE: ProcessedStack will stop being used for BHR in bug 1367406, so this
+  // inline serialization will survive until then.
+
+  // Native Stack Module Information
+  {
+    size_t length;
+    if (!ReadParam(aMsg, aIter, &length)) {
+      return false;
+    }
+
+    for (size_t i = 0; i < length; ++i) {
+      mozilla::Telemetry::ProcessedStack::Module module;
+      if (!ReadParam(aMsg, aIter, &module.mName)) {
+        return false;
+      }
+      if (!ReadParam(aMsg, aIter, &module.mBreakpadId)) {
+        return false;
+      }
+      aResult->mStack.AddModule(module);
+    }
+  }
+
+  // Native stack frame information
+  {
+    size_t length;
+    if (!ReadParam(aMsg, aIter, &length)) {
+      return false;
+    }
+
+    for (size_t i = 0; i < length; ++i) {
+      mozilla::Telemetry::ProcessedStack::Frame frame;
+      if (!ReadParam(aMsg, aIter, &frame.mOffset)) {
+        return false;
+      }
+      if (!ReadParam(aMsg, aIter, &frame.mModIndex)) {
+        return false;
+      }
+      aResult->mStack.AddFrame(frame);
+    }
+  }
+
+  return true;
+}
+
+} // namespace IPC
new file mode 100644
--- /dev/null
+++ b/toolkit/components/backgroundhangmonitor/HangDetails.h
@@ -0,0 +1,138 @@
+/* -*- 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_HangDetails_h
+#define mozilla_HangDetails_h
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/ProcessedStack.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Move.h"
+#include "mozilla/HangStack.h"
+#include "mozilla/HangAnnotations.h"
+#include "nsTArray.h"
+#include "nsIHangDetails.h"
+
+namespace mozilla {
+
+/**
+ * HangDetails is a POD struct which contains the information collected from the
+ * hang. It can be wrapped in a nsHangDetails to provide an XPCOM interface for
+ * extracting information from it easily.
+ *
+ * This type is separate, as it can be sent over IPC while nsHangDetails is an
+ * XPCOM interface which is harder to serialize over IPC.
+ */
+class HangDetails
+{
+public:
+  HangDetails()
+    : mDuration(0)
+    , mProcess(GeckoProcessType_Invalid)
+  {}
+
+  HangDetails(const HangDetails& aOther) = default;
+  HangDetails(HangDetails&& aOther) = default;
+  HangDetails(uint32_t aDuration,
+              GeckoProcessType aProcess,
+              const nsACString& aThreadName,
+              const nsACString& aRunnableName,
+              HangStack&& aPseudoStack,
+              HangMonitor::HangAnnotations&& aAnnotations)
+    : mDuration(aDuration)
+    , mProcess(aProcess)
+    , mThreadName(aThreadName)
+    , mRunnableName(aRunnableName)
+    , mPseudoStack(Move(aPseudoStack))
+    , mAnnotations(Move(aAnnotations))
+  {}
+
+  // NOTE: Once we get merged stacks, we will move ProcessedStack into HangStack
+  // and simplify everything. (bug 1367406).
+  uint32_t mDuration;
+  GeckoProcessType mProcess;
+  nsCString mThreadName;
+  nsCString mRunnableName;
+  HangStack mPseudoStack;
+  HangMonitor::HangAnnotations mAnnotations;
+
+  // NOTE: Initialized by ProcessHangStackRunnable.
+  Telemetry::ProcessedStack mStack;
+};
+
+/**
+ * HangDetails is the concrete implementaion of nsIHangDetails, and contains the
+ * infromation which we want to expose to observers of the bhr-thread-hang
+ * observer notification.
+ */
+class nsHangDetails : public nsIHangDetails
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIHANGDETAILS
+
+  explicit nsHangDetails(HangDetails&& aDetails)
+    : mDetails(Move(aDetails))
+  {}
+
+  // Submit these HangDetails to the main thread. This will dispatch a runnable
+  // to the main thread which will fire off the bhr-thread-hang observer
+  // notification with this HangDetails as the subject.
+  void Submit();
+
+private:
+  virtual ~nsHangDetails() {}
+
+  HangDetails mDetails;
+};
+
+/**
+ * This runnable is run on the StreamTransportService threadpool in order to
+ * process the stack off main thread before submitting it to the main thread as
+ * an observer notification.
+ *
+ * This object should have the only remaining reference to aHangDetails, as it
+ * will access its fields without synchronization.
+ */
+class ProcessHangStackRunnable final : public Runnable
+{
+public:
+  ProcessHangStackRunnable(HangDetails&& aHangDetails,
+                           std::vector<uintptr_t>&& aNativeStack)
+    : Runnable("ProcessHangStackRunnable")
+    , mHangDetails(Move(aHangDetails))
+    , mNativeStack(Move(aNativeStack))
+  {}
+
+  NS_IMETHOD Run() override;
+
+private:
+  HangDetails mHangDetails;
+  std::vector<uintptr_t> mNativeStack;
+};
+
+} // namespace mozilla
+
+// We implement the ability to send the HangDetails object over IPC. We need to
+// do this rather than rely on StructuredClone of the objects created by the
+// XPCOM getters on nsHangDetails because we want to run BHR in the GPU process
+// which doesn't run any JS.
+namespace IPC {
+
+template<>
+class ParamTraits<mozilla::HangDetails>
+{
+public:
+  typedef mozilla::HangDetails paramType;
+  static void Write(Message* aMsg, const paramType& aParam);
+  static bool Read(const Message* aMsg,
+                   PickleIterator* aIter,
+                   paramType* aResult);
+};
+
+} // namespace IPC
+
+#endif // mozilla_HangDetails_h
new file mode 100644
--- /dev/null
+++ b/toolkit/components/backgroundhangmonitor/HangStack.cpp
@@ -0,0 +1,103 @@
+#include "HangStack.h"
+
+namespace mozilla {
+
+HangStack::HangStack(const HangStack& aOther)
+{
+  if (NS_WARN_IF(!mBuffer.reserve(aOther.mBuffer.length()) ||
+                 !mImpl.reserve(aOther.mImpl.length()))) {
+    return;
+  }
+  // XXX: I should be able to just memcpy the other stack's mImpl and mBuffer,
+  // and then just re-offset pointers.
+  for (size_t i = 0; i < aOther.length(); ++i) {
+    const char* s = aOther[i];
+    if (aOther.IsInBuffer(s)) {
+      InfallibleAppendViaBuffer(s, strlen(s));
+    } else {
+      infallibleAppend(s);
+    }
+  }
+  MOZ_ASSERT(mImpl.length() == aOther.mImpl.length());
+  MOZ_ASSERT(mBuffer.length() == aOther.mBuffer.length());
+}
+
+const char*
+HangStack::InfallibleAppendViaBuffer(const char* aText, size_t aLength)
+{
+  MOZ_ASSERT(this->canAppendWithoutRealloc(1));
+  // Include null-terminator in length count.
+  MOZ_ASSERT(mBuffer.canAppendWithoutRealloc(aLength + 1));
+
+  const char* const entry = mBuffer.end();
+  mBuffer.infallibleAppend(aText, aLength);
+  mBuffer.infallibleAppend('\0'); // Explicitly append null-terminator
+  this->infallibleAppend(entry);
+  return entry;
+}
+
+const char*
+HangStack::AppendViaBuffer(const char* aText, size_t aLength)
+{
+  if (!this->reserve(this->length() + 1)) {
+    return nullptr;
+  }
+
+  // Keep track of the previous buffer in case we need to adjust pointers later.
+  const char* const prevStart = mBuffer.begin();
+  const char* const prevEnd = mBuffer.end();
+
+  // Include null-terminator in length count.
+  if (!mBuffer.reserve(mBuffer.length() + aLength + 1)) {
+    return nullptr;
+  }
+
+  if (prevStart != mBuffer.begin()) {
+    // The buffer has moved; we have to adjust pointers in the stack.
+    for (auto & entry : *this) {
+      if (entry >= prevStart && entry < prevEnd) {
+        // Move from old buffer to new buffer.
+        entry += mBuffer.begin() - prevStart;
+      }
+    }
+  }
+
+  return InfallibleAppendViaBuffer(aText, aLength);
+}
+
+} // namespace mozilla
+
+namespace IPC {
+
+void
+ParamTraits<mozilla::HangStack>::Write(Message* aMsg, const mozilla::HangStack& aParam)
+{
+  size_t length = aParam.length();
+  WriteParam(aMsg, length);
+  for (size_t i = 0; i < length; ++i) {
+    nsDependentCString str(aParam[i]);
+    WriteParam(aMsg, static_cast<nsACString&>(str));
+  }
+}
+
+bool
+ParamTraits<mozilla::HangStack>::Read(const Message* aMsg,
+                                      PickleIterator* aIter,
+                                      mozilla::HangStack* aResult)
+{
+  size_t length;
+  if (!ReadParam(aMsg, aIter, &length)) {
+    return false;
+  }
+
+  for (size_t i = 0; i < length; ++i) {
+    nsAutoCString str;
+    if (!ReadParam(aMsg, aIter, &str)) {
+      return false;
+    }
+    aResult->AppendViaBuffer(str.get(), str.Length());
+  }
+  return true;
+}
+
+} // namespace IPC
new file mode 100644
--- /dev/null
+++ b/toolkit/components/backgroundhangmonitor/HangStack.h
@@ -0,0 +1,144 @@
+/* -*- 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_HangStack_h
+#define mozilla_HangStack_h
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/ProcessedStack.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Move.h"
+#include "nsTArray.h"
+#include "nsIHangDetails.h"
+
+namespace mozilla {
+
+/* A native stack is a simple list of pointers, so rather than building a
+   wrapper type, we typdef the type here. */
+typedef std::vector<uintptr_t> NativeHangStack;
+
+/* HangStack stores an array of const char pointers,
+   with optional internal storage for strings. */
+class HangStack
+{
+public:
+  static const size_t sMaxInlineStorage = 8;
+
+  // The maximum depth for the native stack frames that we might collect.
+  // XXX: Consider moving this to a different object?
+  static const size_t sMaxNativeFrames = 150;
+
+private:
+  typedef mozilla::Vector<const char*, sMaxInlineStorage> Impl;
+  Impl mImpl;
+
+  // Stack entries can either be a static const char*
+  // or a pointer to within this buffer.
+  mozilla::Vector<char, 0> mBuffer;
+
+public:
+  HangStack() {}
+
+  HangStack(const HangStack& aOther);
+  HangStack(HangStack&& aOther)
+    : mImpl(mozilla::Move(aOther.mImpl))
+    , mBuffer(mozilla::Move(aOther.mBuffer))
+  {
+  }
+
+  HangStack& operator=(HangStack&& aOther) {
+    mImpl = mozilla::Move(aOther.mImpl);
+    mBuffer = mozilla::Move(aOther.mBuffer);
+    return *this;
+  }
+
+  bool operator==(const HangStack& aOther) const {
+    for (size_t i = 0; i < length(); i++) {
+      if (!IsSameAsEntry(operator[](i), aOther[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  bool operator!=(const HangStack& aOther) const {
+    return !operator==(aOther);
+  }
+
+  const char*& operator[](size_t aIndex) {
+    return mImpl[aIndex];
+  }
+
+  const char* const& operator[](size_t aIndex) const {
+    return mImpl[aIndex];
+  }
+
+  size_t capacity() const { return mImpl.capacity(); }
+  size_t length() const { return mImpl.length(); }
+  bool empty() const { return mImpl.empty(); }
+  bool canAppendWithoutRealloc(size_t aNeeded) const {
+    return mImpl.canAppendWithoutRealloc(aNeeded);
+  }
+  void infallibleAppend(const char* aEntry) { mImpl.infallibleAppend(aEntry); }
+  bool reserve(size_t aRequest) { return mImpl.reserve(aRequest); }
+  const char** begin() { return mImpl.begin(); }
+  const char* const* begin() const { return mImpl.begin(); }
+  const char** end() { return mImpl.end(); }
+  const char* const* end() const { return mImpl.end(); }
+  const char*& back() { return mImpl.back(); }
+  void erase(const char** aEntry) { mImpl.erase(aEntry); }
+  void erase(const char** aBegin, const char** aEnd) {
+    mImpl.erase(aBegin, aEnd);
+  }
+
+  void clear() {
+    mImpl.clear();
+    mBuffer.clear();
+  }
+
+  bool IsInBuffer(const char* aEntry) const {
+    return aEntry >= mBuffer.begin() && aEntry < mBuffer.end();
+  }
+
+  bool IsSameAsEntry(const char* aEntry, const char* aOther) const {
+    // If the entry came from the buffer, we need to compare its content;
+    // otherwise we only need to compare its pointer.
+    return IsInBuffer(aEntry) ? !strcmp(aEntry, aOther) : (aEntry == aOther);
+  }
+
+  size_t AvailableBufferSize() const {
+    return mBuffer.capacity() - mBuffer.length();
+  }
+
+  bool EnsureBufferCapacity(size_t aCapacity) {
+    // aCapacity is the minimal capacity and Vector may make the actual
+    // capacity larger, in which case we want to use up all the space.
+    return mBuffer.reserve(aCapacity) &&
+           mBuffer.reserve(mBuffer.capacity());
+  }
+
+  const char* InfallibleAppendViaBuffer(const char* aText, size_t aLength);
+  const char* AppendViaBuffer(const char* aText, size_t aLength);
+};
+
+} // namespace mozilla
+
+namespace IPC {
+
+template<>
+class ParamTraits<mozilla::HangStack>
+{
+public:
+  typedef mozilla::HangStack paramType;
+  static void Write(Message* aMsg, const paramType& aParam);
+  static bool Read(const Message* aMsg,
+                   PickleIterator* aIter,
+                   paramType* aResult);
+};
+
+} // namespace IPC
+
+#endif // mozilla_HangStack_h
rename from xpcom/threads/ThreadStackHelper.cpp
rename to toolkit/components/backgroundhangmonitor/ThreadStackHelper.cpp
--- a/xpcom/threads/ThreadStackHelper.cpp
+++ b/toolkit/components/backgroundhangmonitor/ThreadStackHelper.cpp
@@ -123,17 +123,17 @@ ThreadStackHelper::GetStacksInternal(Sta
   if (aStack && !PrepareStackBuffer(*aStack)) {
     // Skip and return empty aStack
     return;
   }
 
   // Prepare the native stack
   if (aNativeStack) {
     aNativeStack->clear();
-    aNativeStack->reserve(Telemetry::HangStack::sMaxNativeFrames);
+    aNativeStack->reserve(HangStack::sMaxNativeFrames);
   }
 
 #ifdef MOZ_THREADSTACKHELPER_PSEUDO
   ScopedSetPtr<Stack> stackPtr(mStackToFill, aStack);
 #endif
 #ifdef MOZ_THREADSTACKHELPER_NATIVE
   ScopedSetPtr<NativeStack> nativeStackPtr(mNativeStackToFill, aNativeStack);
 #endif
rename from xpcom/threads/ThreadStackHelper.h
rename to toolkit/components/backgroundhangmonitor/ThreadStackHelper.h
--- a/xpcom/threads/ThreadStackHelper.h
+++ b/toolkit/components/backgroundhangmonitor/ThreadStackHelper.h
@@ -2,18 +2,18 @@
 /* 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_ThreadStackHelper_h
 #define mozilla_ThreadStackHelper_h
 
-#include "mozilla/ThreadHangStats.h"
 #include "js/ProfilingStack.h"
+#include "HangDetails.h"
 
 #include <stddef.h>
 
 #if defined(XP_LINUX)
 #include <signal.h>
 #include <semaphore.h>
 #include <sys/types.h>
 #elif defined(XP_WIN)
@@ -48,23 +48,23 @@ namespace mozilla {
  * the pseudo-stack of the target thread at that instant.
  *
  * Only non-copying labels are included in the stack, which means labels
  * with custom text and markers are not included.
  */
 class ThreadStackHelper
 {
 public:
-  typedef Telemetry::HangStack Stack;
+  typedef HangStack Stack;
 
   // When a native stack is gathered, this vector holds the raw program counter
   // values that FramePointerStackWalk will return to us after it walks the
   // stack. When gathering the Telemetry payload, Telemetry will take care of
   // mapping these program counters to proper addresses within modules.
-  typedef Telemetry::NativeHangStack NativeStack;
+  typedef NativeHangStack NativeStack;
 
 private:
 #ifdef MOZ_THREADSTACKHELPER_PSEUDO
   Stack* mStackToFill;
   const PseudoStack* const mPseudoStack;
   size_t mMaxStackSize;
   size_t mMaxBufferSize;
 #endif
new file mode 100644
--- /dev/null
+++ b/toolkit/components/backgroundhangmonitor/moz.build
@@ -0,0 +1,51 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# NOTE: These config options must match the ones in both android/'s and
+# browser/'s package-manifest.in.
+
+# BHR disabled outside of Nightly builds due to expected high ping frequency.
+# BHR disabled for Release builds because of bug 965392.
+# BHR disabled for debug builds because of bug 979069.
+# BHR disabled for TSan builds because of bug 1121216.
+if CONFIG['NIGHTLY_BUILD'] and \
+   not CONFIG['MOZ_DEBUG'] and \
+   not CONFIG['MOZ_TSAN']:
+    DEFINES['MOZ_ENABLE_BACKGROUND_HANG_MONITOR'] = 1
+
+    EXTRA_COMPONENTS += [
+        'BHRTelemetryService.js',
+        'BHRTelemetryService.manifest',
+    ]
+
+    XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']
+
+XPIDL_SOURCES += [
+    'nsIHangDetails.idl',
+]
+
+XPIDL_MODULE = 'backgroundhangmonitor'
+
+EXPORTS.mozilla += [
+    'BackgroundHangMonitor.h',
+    'HangDetails.h',
+    'HangStack.h',
+]
+
+UNIFIED_SOURCES += [
+    'BackgroundHangMonitor.cpp',
+    'HangDetails.cpp',
+    'HangStack.cpp',
+    'ThreadStackHelper.cpp',
+]
+
+LOCAL_INCLUDES += [
+    '/caps', # For nsScriptSecurityManager.h
+]
+
+FINAL_LIBRARY = 'xul'
+
+include('/ipc/chromium/chromium-config.mozbuild')
rename from xpcom/threads/nsIHangDetails.idl
rename to toolkit/components/backgroundhangmonitor/nsIHangDetails.idl
--- a/xpcom/threads/nsIHangDetails.idl
+++ b/toolkit/components/backgroundhangmonitor/nsIHangDetails.idl
@@ -1,25 +1,71 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "nsISupports.idl"
 
+%{ C++
+namespace mozilla {
+class HangDetails;
+}
+%}
+
+[ref] native HangDetailsRef(mozilla::HangDetails);
+
 /**
  * A scriptable interface for getting information about a BHR detected hang.
  * This is the type of the subject of the "bhr-thread-hang" observer topic.
  */
 [scriptable, uuid(23d63fff-38d6-4003-9c57-2c90aca1180a)]
 interface nsIHangDetails : nsISupports
 {
   /**
    * The detected duration of the hang.
    */
   readonly attribute uint32_t duration;
 
   /**
-   * The name of the thread which hung
+   * The name of the thread which hung.
+   */
+  readonly attribute ACString thread;
+
+  /**
+   * The name of the runnable which hung if it hung on the main thread.
+   */
+  readonly attribute ACString runnableName;
+
+  /**
+   * The type of process which produced the hang. This should be either:
+   * "default", "content", or "gpu".
+   */
+  readonly attribute ACString process;
+
+  /**
+   * Returns the stack which was captured by BHR. The offset is encoded as a hex
+   * string, as it can contain numbers larger than JS can hold losslessly.
+   *
+   * This value takes the following form:
+   * [ [moduleIndex, offset], ... ]
    */
-  readonly attribute ACString threadName;
+  [implicit_jscontext] readonly attribute jsval stack;
+
+  /**
+   * Returns the modules which were captured by BHR.
+   *
+   * This value takes the following form:
+   * [ ["fileName", "BreakpadId"], ... ]
+   */
+  [implicit_jscontext] readonly attribute jsval modules;
+
+  /**
+   * The hang annotations which were captured when the hang occured. This
+   * attribute is a JS object of key-value pairs.
+   */
+  [implicit_jscontext] readonly attribute jsval annotations;
+
+  /**
+   * A getter for the pseudoStack. This attribute is a JS array of strings.
+   */
+  [implicit_jscontext] readonly attribute jsval pseudoStack;
 };
-
new file mode 100644
--- /dev/null
+++ b/toolkit/components/backgroundhangmonitor/tests/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+  "extends": [
+    "plugin:mozilla/xpcshell-test"
+  ]
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/components/backgroundhangmonitor/tests/child_cause_hang.js
@@ -0,0 +1,32 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let { classes: Cc, utils: Cu, interfaces: Ci, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+function ensureProfilerInitialized() {
+  // Starting and stopping the profiler with the "stackwalk" flag will cause the
+  // profiler's stackwalking features to be synchronously initialized. This
+  // should prevent us from not initializing BHR quickly enough.
+  if (!Services.profiler.CanProfile()) {
+    return false;
+  }
+  let features = ["stackwalk"];
+  Services.profiler.StartProfiler(1000, 10, features, features.length);
+  Services.profiler.StopProfiler();
+  return true;
+}
+
+add_task(async function childCauseHang() {
+  if (!ensureProfilerInitialized()) {
+    return;
+  }
+
+  do_execute_soon(() => {
+    let startTime = Date.now();
+    while ((Date.now() - startTime) < 2000);
+  });
+
+  await do_await_remote_message("bhr_hangs_detected");
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/backgroundhangmonitor/tests/test_BHRObserver.js
@@ -0,0 +1,120 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let { classes: Cc, utils: Cu, interfaces: Ci, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+function ensureProfilerInitialized() {
+  // Starting and stopping the profiler with the "stackwalk" flag will cause the
+  // profiler's stackwalking features to be synchronously initialized. This
+  // should prevent us from not initializing BHR quickly enough.
+  if (!Services.profiler.CanProfile()) {
+    return false;
+  }
+  let features = ["stackwalk"];
+  Services.profiler.StartProfiler(1000, 10, features, features.length);
+  Services.profiler.StopProfiler();
+  return true;
+}
+
+add_task(async function test_BHRObserver() {
+  if (!Services.telemetry.canRecordExtended) {
+    ok("Hang reporting not enabled.");
+    return;
+  }
+
+  if (!ensureProfilerInitialized()) {
+    return;
+  }
+
+  if (Services.appinfo.OS === "Linux" || Services.appinfo.OS === "Android") {
+    // We use the rt_tgsigqueueinfo syscall on Linux which requires a
+    // certain kernel version. It's not an error if the system running
+    // the test is older than that.
+    let kernel = Services.sysinfo.get("kernel_version") ||
+          Services.sysinfo.get("version");
+    if (Services.vc.compare(kernel, "2.6.31") < 0) {
+      ok("Hang reporting not supported for old kernel.");
+      return;
+    }
+  }
+
+  let hangsPromise = new Promise(resolve => {
+    let hangs = [];
+    const onThreadHang = subject => {
+      let hang = subject.QueryInterface(Ci.nsIHangDetails);
+      if (hang.thread.startsWith("Gecko")) {
+        hangs.push(hang);
+        if (hangs.length >= 3) {
+          Services.obs.removeObserver(onThreadHang, "bhr-thread-hang");
+          resolve(hangs);
+        }
+      }
+    };
+    Services.obs.addObserver(onThreadHang, "bhr-thread-hang");
+  });
+
+  // We're going to trigger two hangs, of various lengths. One should be a
+  // transient hang, and the other a permanent hang. We'll wait for the hangs to
+  // be recorded.
+
+  do_execute_soon(() => {
+    let startTime = Date.now();
+    while ((Date.now() - startTime) < 10000);
+  });
+
+  do_execute_soon(() => {
+    let startTime = Date.now();
+    while ((Date.now() - startTime) < 1000);
+  });
+
+  let childDone = run_test_in_child("child_cause_hang.js");
+
+  // Now we wait for the hangs to have their bhr-thread-hang message fired for
+  // them, collect them, and analyize the response.
+  let hangs = await hangsPromise;
+  equal(hangs.length, 3);
+  hangs.forEach(hang => {
+    ok(hang.duration > 0);
+    ok(hang.thread == "Gecko" || hang.thread == "Gecko_Child");
+    equal(typeof hang.runnableName, "string");
+
+    // hang.stack
+    ok(Array.isArray(hang.stack));
+    hang.stack.forEach(entry => {
+      // XXX: Once we get merged stacks, we might get pseudostack or js frames
+      // in here too.
+      ok(Array.isArray(entry));
+      equal(entry.length, 2);
+      equal(typeof entry[0], "number");
+      equal(typeof entry[1], "string");
+    });
+
+    // hang.modules
+    ok(Array.isArray(hang.modules));
+    hang.modules.forEach(module => {
+      ok(Array.isArray(module));
+      equal(module.length, 2);
+      equal(typeof module[0], "string");
+      equal(typeof module[1], "string");
+    });
+
+    // hang.annotations
+    equal(typeof hang.annotations, "object");
+    Object.keys(hang.annotations).forEach(key => {
+      equal(typeof hang.annotations[key], "string");
+    });
+
+    // hang.pseudoStack
+    // XXX: This will go away once we get merged stacks
+    ok(Array.isArray(hang.pseudoStack));
+    ok(hang.pseudoStack.length > 0);
+    hang.pseudoStack.forEach(entry => {
+      equal(typeof entry, "string");
+    });
+  });
+
+  do_send_remote_message("bhr_hangs_detected");
+  await childDone;
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/backgroundhangmonitor/tests/xpcshell.ini
@@ -0,0 +1,4 @@
+[test_BHRObserver.js]
+# BHR is disabled on android and outside of nightly
+skip-if = debug || os == "android" || release_or_beta
+support-files = child_cause_hang.js
--- a/toolkit/components/moz.build
+++ b/toolkit/components/moz.build
@@ -15,16 +15,17 @@ DIRS += [
     'aboutcache',
     'aboutcheckerboard',
     'aboutmemory',
     'aboutperformance',
     'addoncompat',
     'alerts',
     'apppicker',
     'asyncshutdown',
+    'backgroundhangmonitor',
     'browser',
     'cloudstorage',
     'commandlines',
     'contentprefs',
     'contextualidentity',
     'crashmonitor',
     'diskspacewatcher',
     'downloads',
@@ -109,10 +110,10 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wind
     DIRS += ['gfx']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
     EXTRA_COMPONENTS += [
         'nsDefaultCLH.js',
         'nsDefaultCLH.manifest',
     ]
 
-if CONFIG['MOZ_BUILD_APP'] == 'browser':
+if CONFIG['NIGHTLY_BUILD'] and CONFIG['MOZ_BUILD_APP'] == 'browser':
     DIRS += ['payments']
--- a/toolkit/components/telemetry/HangReports.cpp
+++ b/toolkit/components/telemetry/HangReports.cpp
@@ -9,61 +9,52 @@
 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 HangAnnotationsPtr& aAnnotations, nsAString& aKeyOut)
+ComputeAnnotationsKey(const HangAnnotations& aAnnotations, nsAString& aKeyOut)
 {
-  UniquePtr<HangAnnotations::Enumerator> annotationsEnum = aAnnotations->GetEnumerator();
-  if (!annotationsEnum) {
+  if (aAnnotations.IsEmpty()) {
     return NS_ERROR_FAILURE;
   }
 
-  // Append all the attributes to the key, to uniquely identify this annotation.
-  nsAutoString  key;
-  nsAutoString  value;
-  while (annotationsEnum->Next(key, value)) {
-    aKeyOut.Append(key);
-    aKeyOut.Append(value);
+  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,
-                     HangAnnotationsPtr aAnnotations) {
+                     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);
   }
 
-  if (!aAnnotations) {
-    return;
-  }
-
   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);
@@ -117,17 +108,18 @@ HangReports::SizeOfExcludingThis(mozilla
   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);
-    n += iter.Data()->mAnnotations->SizeOfIncludingThis(aMallocSizeOf);
+    auto& annotations = iter.Data()->mAnnotations;
+    n += annotations.ShallowSizeOfExcludingThis(aMallocSizeOf);
   }
   return n;
 }
 
 const CombinedStacks&
 HangReports::GetStacks() const {
   return mStacks;
 }
--- a/toolkit/components/telemetry/HangReports.h
+++ b/toolkit/components/telemetry/HangReports.h
@@ -13,27 +13,27 @@
 #include "nsString.h"
 #include "nsClassHashtable.h"
 #include "CombinedStacks.h"
 
 namespace mozilla {
 namespace Telemetry {
 
 nsresult
-ComputeAnnotationsKey(const HangMonitor::HangAnnotationsPtr& aAnnotations, nsAString& aKeyOut);
+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::HangAnnotationsPtr aAnnotations)
+                   HangMonitor::HangAnnotations&& aAnnotations)
       : mAnnotations(Move(aAnnotations))
     {
       mHangIndices.AppendElement(aHangIndex);
     }
     AnnotationInfo(AnnotationInfo&& aOther)
       : mHangIndices(aOther.mHangIndices)
       , mAnnotations(Move(aOther.mAnnotations))
     {}
@@ -42,28 +42,28 @@ public:
     {
       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::HangAnnotationsPtr mAnnotations;
+    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::HangAnnotationsPtr aAnnotations);
+               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:
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -68,17 +68,16 @@
 #endif
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsJSUtils.h"
 #include "nsReadableUtils.h"
 #include "plstr.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "mozilla/BackgroundHangMonitor.h"
-#include "mozilla/ThreadHangStats.h"
 #include "mozilla/ProcessedStack.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/IOInterposer.h"
 #include "mozilla/PoisonIOInterposer.h"
 #include "mozilla/StartupTimeline.h"
@@ -138,22 +137,21 @@ public:
   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,
-                               HangAnnotationsPtr aAnnotations);
+                               HangAnnotations&& aAnnotations);
 #endif
 #if defined(MOZ_GECKO_PROFILER)
   static void DoStackCapture(const nsACString& aKey);
 #endif
-  static void RecordThreadHangStats(Telemetry::ThreadHangStats&& aStats);
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
   struct Stat {
     uint32_t hitCount;
     uint32_t totalTime;
   };
   struct StmtStats {
     struct Stat mainThread;
     struct Stat otherThreads;
@@ -198,20 +196,16 @@ private:
   Atomic<bool> mCanRecordBase;
   Atomic<bool> mCanRecordExtended;
 
 #if defined(MOZ_GECKO_PROFILER)
   // Stores data about stacks captured on demand.
   KeyedStackCapturer mStackCapturer;
 #endif
 
-  // mThreadHangStats stores recorded, inactive thread hang stats
-  Vector<Telemetry::ThreadHangStats> mThreadHangStats;
-  Mutex mThreadHangStatsMutex;
-
   CombinedStacks mLateWritesStacks; // This is collected out of the main thread.
   bool mCachedTelemetryData;
   uint32_t mLastShutdownTime;
   uint32_t mFailedLockCount;
   nsCOMArray<nsIFetchTelemetryDataCallback> mCallbacks;
   friend class nsFetchTelemetryData;
 
   WebrtcTelemetry mWebrtcTelemetry;
@@ -480,34 +474,32 @@ TelemetryImpl::AsyncFetchTelemetryData(n
   return NS_OK;
 }
 
 TelemetryImpl::TelemetryImpl()
   : mHashMutex("Telemetry::mHashMutex")
   , mHangReportsMutex("Telemetry::mHangReportsMutex")
   , mCanRecordBase(false)
   , mCanRecordExtended(false)
-  , mThreadHangStatsMutex("Telemetry::mThreadHangStatsMutex")
   , mCachedTelemetryData(false)
   , mLastShutdownTime(0)
   , mFailedLockCount(0)
 {
   // We expect TelemetryHistogram::InitializeGlobalState() to have been
   // called before we get to this point.
   MOZ_ASSERT(TelemetryHistogram::GlobalStateHasBeenInitialized());
 }
 
 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);
-  MutexAutoLock threadHangsLock(mThreadHangStatsMutex);
 }
 
 void
 TelemetryImpl::InitMemoryReporter() {
   RegisterWeakMemoryReporter(this);
 }
 
 bool
@@ -748,29 +740,21 @@ TelemetryImpl::GetChromeHangs(JSContext 
         return NS_ERROR_FAILURE;
       }
 
       // Create the annotations object...
       JS::Rooted<JSObject*> jsAnnotation(cx, JS_NewPlainObject(cx));
       if (!jsAnnotation) {
         return NS_ERROR_FAILURE;
       }
-      UniquePtr<HangAnnotations::Enumerator> annotationsEnum =
-        info->mAnnotations->GetEnumerator();
-      if (!annotationsEnum) {
-        return NS_ERROR_FAILURE;
-      }
 
-      // ... fill it with key:value pairs...
-      nsAutoString key;
-      nsAutoString value;
-      while (annotationsEnum->Next(key, value)) {
+      for (auto& annot : info->mAnnotations) {
         JS::RootedValue jsValue(cx);
-        jsValue.setString(JS_NewUCStringCopyN(cx, value.get(), value.Length()));
-        if (!JS_DefineUCProperty(cx, jsAnnotation, key.get(), key.Length(),
+        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;
@@ -1060,53 +1044,16 @@ ReadStack(const char *aFileName, Telemet
       index
     };
     stack.AddFrame(frame);
   }
 
   aStack = stack;
 }
 
-NS_IMETHODIMP
-TelemetryImpl::GetThreadHangStats(JSContext* cx, JS::MutableHandle<JS::Value> ret)
-{
-  JS::RootedObject retObj(cx, JS_NewArrayObject(cx, 0));
-  if (!retObj) {
-    return NS_ERROR_FAILURE;
-  }
-  size_t threadIndex = 0;
-
-  if (!BackgroundHangMonitor::IsDisabled()) {
-    /* First add active threads; we need to hold |iter| (and its lock)
-       throughout this method to avoid a race condition where a thread can
-       be recorded twice if the thread is destroyed while this method is
-       running */
-    BackgroundHangMonitor::ThreadHangStatsIterator iter;
-    for (Telemetry::ThreadHangStats* histogram = iter.GetNext();
-         histogram; histogram = iter.GetNext()) {
-      JS::RootedObject obj(cx, CreateJSThreadHangStats(cx, *histogram));
-      if (!JS_DefineElement(cx, retObj, threadIndex++, obj, JSPROP_ENUMERATE)) {
-        return NS_ERROR_FAILURE;
-      }
-    }
-  }
-
-  // Add saved threads next
-  MutexAutoLock autoLock(mThreadHangStatsMutex);
-  for (auto & stat : mThreadHangStats) {
-    JS::RootedObject obj(cx,
-      CreateJSThreadHangStats(cx, stat));
-    if (!JS_DefineElement(cx, retObj, threadIndex++, obj, JSPROP_ENUMERATE)) {
-      return NS_ERROR_FAILURE;
-    }
-  }
-  ret.setObject(*retObj);
-  return NS_OK;
-}
-
 void
 TelemetryImpl::ReadLateWritesStacks(nsIFile* aProfileDir)
 {
   nsAutoCString nativePath;
   nsresult rv = aProfileDir->GetNativePath(nativePath);
   if (NS_FAILED(rv)) {
     return;
   }
@@ -1591,32 +1538,26 @@ TelemetryImpl::RecordIceCandidates(const
 }
 
 #if defined(MOZ_GECKO_PROFILER)
 void
 TelemetryImpl::RecordChromeHang(uint32_t aDuration,
                                 Telemetry::ProcessedStack &aStack,
                                 int32_t aSystemUptime,
                                 int32_t aFirefoxUptime,
-                                HangAnnotationsPtr aAnnotations)
+                                HangAnnotations&& aAnnotations)
 {
   if (!sTelemetry || !TelemetryHistogram::CanRecordExtended())
     return;
 
-  HangAnnotationsPtr annotations;
-  // We only pass aAnnotations if it is not empty.
-  if (aAnnotations && !aAnnotations->IsEmpty()) {
-    annotations = Move(aAnnotations);
-  }
-
   MutexAutoLock hangReportMutex(sTelemetry->mHangReportsMutex);
 
   sTelemetry->mHangReports.AddHang(aStack, aDuration,
                                    aSystemUptime, aFirefoxUptime,
-                                   Move(annotations));
+                                   Move(aAnnotations));
 }
 
 void
 TelemetryImpl::DoStackCapture(const nsACString& aKey) {
   if (Telemetry::CanRecordExtended() && XRE_IsParentProcess()) {
     sTelemetry->mStackCapturer.Capture(aKey);
   }
 }
@@ -1625,28 +1566,16 @@ TelemetryImpl::DoStackCapture(const nsAC
 nsresult
 TelemetryImpl::CaptureStack(const nsACString& aKey) {
 #if defined(MOZ_GECKO_PROFILER)
   TelemetryImpl::DoStackCapture(aKey);
 #endif
   return NS_OK;
 }
 
-void
-TelemetryImpl::RecordThreadHangStats(Telemetry::ThreadHangStats&& aStats)
-{
-  if (!sTelemetry || !TelemetryHistogram::CanRecordExtended())
-    return;
-
-  MutexAutoLock autoLock(sTelemetry->mThreadHangStatsMutex);
-
-  // Ignore OOM.
-  mozilla::Unused << sTelemetry->mThreadHangStats.append(Move(aStats));
-}
-
 bool
 TelemetryImpl::CanRecordBase()
 {
   if (!sTelemetry) {
     return false;
   }
   bool canRecordBase;
   nsresult rv = sTelemetry->GetCanRecordBase(&canRecordBase);
@@ -1854,20 +1783,16 @@ TelemetryImpl::SizeOfIncludingThis(mozil
     MutexAutoLock lock(mHashMutex);
     n += mPrivateSQL.SizeOfExcludingThis(aMallocSizeOf);
     n += mSanitizedSQL.SizeOfExcludingThis(aMallocSizeOf);
   }
   { // Scope for mHangReportsMutex lock
     MutexAutoLock lock(mHangReportsMutex);
     n += mHangReports.SizeOfExcludingThis(aMallocSizeOf);
   }
-  { // Scope for mThreadHangStatsMutex lock
-    MutexAutoLock lock(mThreadHangStatsMutex);
-    n += mThreadHangStats.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);
@@ -2069,37 +1994,31 @@ void Init()
   MOZ_ASSERT(telemetryService);
 }
 
 #if defined(MOZ_GECKO_PROFILER)
 void RecordChromeHang(uint32_t duration,
                       ProcessedStack &aStack,
                       int32_t aSystemUptime,
                       int32_t aFirefoxUptime,
-                      HangAnnotationsPtr aAnnotations)
+                      HangAnnotations&& aAnnotations)
 {
   TelemetryImpl::RecordChromeHang(duration, aStack,
                                   aSystemUptime, aFirefoxUptime,
                                   Move(aAnnotations));
 }
 
 void CaptureStack(const nsACString& aKey)
 {
 #if defined(MOZ_GECKO_PROFILER)
   TelemetryImpl::DoStackCapture(aKey);
 #endif
 }
 #endif
 
-void RecordThreadHangStats(ThreadHangStats&& aStats)
-{
-  TelemetryImpl::RecordThreadHangStats(Move(aStats));
-}
-
-
 void
 WriteFailedProfileLock(nsIFile* aProfileDir)
 {
   nsCOMPtr<nsIFile> file;
   nsresult rv = GetFailedProfileLockFile(getter_AddRefs(file), aProfileDir);
   NS_ENSURE_SUCCESS_VOID(rv);
   int64_t fileSize = 0;
   rv = file->GetFileSize(&fileSize);
--- a/toolkit/components/telemetry/Telemetry.h
+++ b/toolkit/components/telemetry/Telemetry.h
@@ -321,45 +321,30 @@ class ProcessedStack;
  * @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::UniquePtr<mozilla::HangMonitor::HangAnnotations>
-                              aAnnotations);
+                      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.
  */
 void CaptureStack(const nsCString& aKey);
 #endif
 
-class ThreadHangStats;
-
-/**
- * Move a ThreadHangStats to Telemetry storage. Normally Telemetry queries
- * for active ThreadHangStats through BackgroundHangMonitor, but once a
- * thread exits, the thread's copy of ThreadHangStats needs to be moved to
- * inside Telemetry using this function.
- *
- * @param aStats ThreadHangStats to save; the data inside aStats
- *               will be moved and aStats should be treated as
- *               invalid after this function returns
- */
-void RecordThreadHangStats(ThreadHangStats&& aStats);
-
 /**
  * Record a failed attempt at locking the user's profile.
  *
  * @param aProfileDir The profile directory whose lock attempt failed
  */
 void WriteFailedProfileLock(nsIFile* aProfileDir);
 
 /**
--- a/toolkit/components/telemetry/TelemetryEnvironment.jsm
+++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm
@@ -215,16 +215,17 @@ const DEFAULT_ENVIRONMENT_PREFS = new Ma
   ["experiments.manifest.uri", {what: RECORD_PREF_VALUE}],
   ["extensions.allow-non-mpc-extensions", {what: RECORD_PREF_VALUE}],
   ["extensions.autoDisableScopes", {what: RECORD_PREF_VALUE}],
   ["extensions.enabledScopes", {what: RECORD_PREF_VALUE}],
   ["extensions.blocklist.enabled", {what: RECORD_PREF_VALUE}],
   ["extensions.blocklist.url", {what: RECORD_PREF_VALUE}],
   ["extensions.formautofill.addresses.enabled", {what: RECORD_PREF_VALUE}],
   ["extensions.formautofill.creditCards.enabled", {what: RECORD_PREF_VALUE}],
+  ["extensions.legacy.enabled", {what: RECORD_PREF_VALUE}],
   ["extensions.strictCompatibility", {what: RECORD_PREF_VALUE}],
   ["extensions.update.enabled", {what: RECORD_PREF_VALUE}],
   ["extensions.update.url", {what: RECORD_PREF_VALUE}],
   ["extensions.update.background.url", {what: RECORD_PREF_VALUE}],
   ["extensions.screenshots.disabled", {what: RECORD_PREF_VALUE}],
   ["extensions.screenshots.system-disabled", {what: RECORD_PREF_VALUE}],
   ["general.smoothScroll", {what: RECORD_PREF_VALUE}],
   ["gfx.direct2d.disabled", {what: RECORD_PREF_VALUE}],
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -54,18 +54,16 @@ const ENVIRONMENT_CHANGE_LISTENER = "Tel
 
 const MS_IN_ONE_HOUR  = 60 * 60 * 1000;
 const MIN_SUBSESSION_LENGTH_MS = Services.prefs.getIntPref("toolkit.telemetry.minSubsessionLength", 5 * 60) * 1000;
 
 const LOGGER_NAME = "Toolkit.Telemetry";
 const LOGGER_PREFIX = "TelemetrySession" + (Utils.isContentProcess ? "#content::" : "::");
 
 const MESSAGE_TELEMETRY_PAYLOAD = "Telemetry:Payload";
-const MESSAGE_TELEMETRY_THREAD_HANGS = "Telemetry:ChildThreadHangs";
-const MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS = "Telemetry:GetChildThreadHangs";
 const MESSAGE_TELEMETRY_USS = "Telemetry:USS";
 const MESSAGE_TELEMETRY_GET_CHILD_USS = "Telemetry:GetChildUSS";
 
 const DATAREPORTING_DIRECTORY = "datareporting";
 const ABORTED_SESSION_FILE_NAME = "aborted-session-ping";
 
 // Whether the FHR/Telemetry unification features are enabled.
 // Changing this pref requires a restart.
@@ -515,27 +513,16 @@ this.TelemetrySession = Object.freeze({
    * @param reason Optional, the reason to trigger the payload.
    * @param clearSubsession Optional, whether to clear subsession specific data.
    * @returns Object
    */
   getPayload(reason, clearSubsession = false) {
     return Impl.getPayload(reason, clearSubsession);
   },
   /**
-   * Returns a promise that resolves to an array of thread hang stats from content processes, one entry per process.
-   * The structure of each entry is identical to that of "threadHangStats" in nsITelemetry.
-   * While thread hang stats are also part of the child payloads, this function is useful for cheaply getting this information,
-   * which is useful for realtime hang monitoring.
-   * Child processes that do not respond, or spawn/die during execution of this function are excluded from the result.
-   * @returns Promise
-   */
-  getChildThreadHangs() {
-    return Impl.getChildThreadHangs();
-  },
-  /**
    * Save the session state to a pending file.
    * Used only for testing purposes.
    */
   testSavePendingPing() {
     return Impl.testSavePendingPing();
   },
   /**
    * Collect and store information about startup.
@@ -654,27 +641,16 @@ var Impl = {
   // The previous build ID, if this is the first run with a new build.
   // Null if this is the first run, or the previous build ID is unknown.
   _previousBuildId: null,
   // Telemetry payloads sent by child processes.
   // Each element is in the format {source: <weak-ref>, payload: <object>},
   // where source is a weak reference to the child process,
   // and payload is the telemetry payload from that child process.
   _childTelemetry: [],
-  // Thread hangs from child processes.
-  // Used for TelemetrySession.getChildThreadHangs(); not sent with Telemetry pings.
-  // TelemetrySession.getChildThreadHangs() is used by extensions such as Statuser (https://github.com/chutten/statuser).
-  // Each element is in the format {source: <weak-ref>, payload: <object>},
-  // where source is a weak reference to the child process,
-  // and payload contains the thread hang stats from that child process.
-  _childThreadHangs: [],
-  // Array of the resolve functions of all the promises that are waiting for the child thread hang stats to arrive, used to resolve all those promises at once.
-  _childThreadHangsResolveFunctions: [],
-  // Timeout function for child thread hang stats retrieval.
-  _childThreadHangsTimeout: null,
   // Unique id that identifies this session so the server can cope with duplicate
   // submissions, orphaning and other oddities. The id is shared across subsessions.
   _sessionId: null,
   // Random subsession id.
   _subsessionId: null,
   // Session id of the previous session, null on first run.
   _previousSessionId: null,
   // Subsession id of the previous subsession (even if it was in a different session),
@@ -996,26 +972,16 @@ var Impl = {
       for (let proc of Object.keys(snapshot)) {
         snapshot[proc] = snapshot[proc].filter(e => !e[1].startsWith("telemetry.test"));
       }
     }
 
     return snapshot;
   },
 
-  getThreadHangStats: function getThreadHangStats(stats) {
-    stats.forEach((thread) => {
-      thread.activity = this.packHistogram(thread.activity);
-      thread.hangs.forEach((hang) => {
-        hang.histogram = this.packHistogram(hang.histogram);
-      });
-    });
-    return stats;
-  },
-
   /**
    * Descriptive metadata
    *
    * @param  reason
    *         The reason for the telemetry ping, this will be included in the
    *         returned metadata,
    * @return The metadata as a JS object
    */
@@ -1259,17 +1225,16 @@ var Impl = {
     let payloadObj = {
       ver: PAYLOAD_VERSION,
       simpleMeasurements,
     };
 
     // Add extended set measurements common to chrome & content processes
     if (Telemetry.canRecordExtended) {
       payloadObj.chromeHangs = protect(() => Telemetry.chromeHangs);
-      payloadObj.threadHangStats = protect(() => this.getThreadHangStats(Telemetry.threadHangStats));
       payloadObj.log = protect(() => TelemetryLog.entries());
       payloadObj.webrtc = protect(() => Telemetry.webrtcStats);
     }
 
     if (Utils.isContentProcess) {
       return payloadObj;
     }
 
@@ -1491,17 +1456,16 @@ var Impl = {
     if (previousBuildId != thisBuildID) {
       this._previousBuildId = previousBuildId;
       Services.prefs.setStringPref(TelemetryUtils.Preferences.PreviousBuildID, thisBuildID);
     }
 
     this.attachEarlyObservers();
 
     ppml.addMessageListener(MESSAGE_TELEMETRY_PAYLOAD, this);
-    ppml.addMessageListener(MESSAGE_TELEMETRY_THREAD_HANGS, this);
     ppml.addMessageListener(MESSAGE_TELEMETRY_USS, this);
   },
 
   /**
    * Does the "heavy" Telemetry initialization later on, so we
    * don't impact startup performance.
    * @return {Promise} Resolved when the initialization completes.
    */
@@ -1578,17 +1542,16 @@ var Impl = {
     this._testing = testing;
 
     if (!Telemetry.canRecordBase) {
       this._log.trace("setupContentProcess - base recording is disabled, not initializing");
       return;
     }
 
     this.addObserver("content-child-shutdown");
-    cpml.addMessageListener(MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS, this);
     cpml.addMessageListener(MESSAGE_TELEMETRY_GET_CHILD_USS, this);
 
     let delayedTask = new DeferredTask(() => {
       this._initialized = true;
 
       this.attachObservers();
       this.gatherMemory();
 
@@ -1628,45 +1591,16 @@ var Impl = {
 
       if (this._childTelemetry.length == MAX_NUM_CONTENT_PAYLOADS + 1) {
         this._childTelemetry.shift();
         Telemetry.getHistogramById("TELEMETRY_DISCARDED_CONTENT_PINGS_COUNT").add();
       }
 
       break;
     }
-    case MESSAGE_TELEMETRY_THREAD_HANGS:
-    {
-      // Accumulate child thread hang stats from this child
-      this._childThreadHangs.push(message.data);
-
-      // Check if we've got data from all the children, accounting for child processes dying
-      // if it happens before the last response is received and no new child processes are spawned at the exact same time
-      // If that happens, we can resolve the promise earlier rather than having to wait for the timeout to expire
-      // Basically, the number of replies is at most the number of messages sent out, this._childCount,
-      // and also at most the number of child processes that currently exist
-      if (this._childThreadHangs.length === Math.min(this._childCount, ppmm.childCount)) {
-        clearTimeout(this._childThreadHangsTimeout);
-
-        // Resolve all the promises that are waiting on these thread hang stats
-        // We resolve here instead of rejecting because
-        for (let resolve of this._childThreadHangsResolveFunctions) {
-          resolve(this._childThreadHangs);
-        }
-        this._childThreadHangsResolveFunctions = [];
-      }
-
-      break;
-    }
-    case MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS:
-    {
-      // In child process, send the requested child thread hangs
-      this.sendContentProcessThreadHangs();
-      break;
-    }
     case MESSAGE_TELEMETRY_USS:
     {
       // In parent process, receive the USS report from the child
       if (this._totalMemoryTimeout && this._childrenToHearFrom.delete(message.data.id)) {
         let uss = message.data.bytes;
         this._totalMemory += uss;
         this._USSFromChildProcesses.push(uss);
         if (this._childrenToHearFrom.size == 0) {
@@ -1746,25 +1680,16 @@ var Impl = {
   sendContentProcessPing: function sendContentProcessPing(reason) {
     this._log.trace("sendContentProcessPing - Reason " + reason);
     const isSubsession = !this._isClassicReason(reason);
     let payload = this.getSessionPayload(reason, isSubsession);
     payload.childUUID = this._processUUID;
     cpmm.sendAsyncMessage(MESSAGE_TELEMETRY_PAYLOAD, payload);
   },
 
-  sendContentProcessThreadHangs: function sendContentProcessThreadHangs() {
-    this._log.trace("sendContentProcessThreadHangs");
-    let payload = {
-      childUUID: this._processUUID,
-      hangs: Telemetry.threadHangStats,
-    };
-    cpmm.sendAsyncMessage(MESSAGE_TELEMETRY_THREAD_HANGS, payload);
-  },
-
    /**
     * Save both the "saved-session" and the "shutdown" pings to disk.
     * This needs to be called after TelemetrySend shuts down otherwise pings
     * would be sent instead of getting persisted to disk.
     */
   saveShutdownPings() {
     this._log.trace("saveShutdownPings");
 
@@ -1844,60 +1769,16 @@ var Impl = {
     // We only gather startup info once.
     if (Object.keys(this._slowSQLStartup).length == 0) {
       this._slowSQLStartup = Telemetry.slowSQL;
     }
     this.gatherMemory();
     return this.getSessionPayload(reason, clearSubsession);
   },
 
-  getChildThreadHangs: function getChildThreadHangs() {
-    return new Promise((resolve) => {
-      // Return immediately if there are no child processes to get stats from
-      if (ppmm.childCount === 0) {
-        resolve([]);
-        return;
-      }
-
-      // Register our promise so it will be resolved when we receive the child thread hang stats on the parent process
-      // The resolve functions will all be called from "receiveMessage" when a MESSAGE_TELEMETRY_THREAD_HANGS message comes in
-      this._childThreadHangsResolveFunctions.push((threadHangStats) => {
-        let hangs = threadHangStats.map(child => child.hangs);
-        return resolve(hangs);
-      });
-
-      // If we (the parent) are not currently in the process of requesting child thread hangs, request them
-      // If we are, then the resolve function we registered above will receive the results without needing to request them again
-      if (this._childThreadHangsResolveFunctions.length === 1) {
-        // We have to cache the number of children we send messages to, in case the child count changes while waiting for messages to arrive
-        // This handles the case where the child count increases later on, in which case the new processes won't respond since we never sent messages to them
-        this._childCount = ppmm.childCount;
-
-        this._childThreadHangs = []; // Clear the child hangs
-        for (let i = 0; i < this._childCount; i++) {
-          // If a child dies at exactly while we're running this loop, the message sending will fail but we won't get an exception
-          // In this case, since we won't know this has happened, we will simply rely on the timeout to handle it
-          ppmm.getChildAt(i).sendAsyncMessage(MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS);
-        }
-
-        // Set up a timeout in case one or more of the content processes never responds
-        this._childThreadHangsTimeout = setTimeout(() => {
-          // Resolve all the promises that are waiting on these thread hang stats
-          // We resolve here instead of rejecting because the purpose of this function is
-          // to retrieve the BHR stats from all processes that will give us stats
-          // As a result, one process failing simply means it doesn't get included in the result.
-          for (let resolve of this._childThreadHangsResolveFunctions) {
-            resolve(this._childThreadHangs);
-          }
-          this._childThreadHangsResolveFunctions = [];
-        }, 200);
-      }
-    });
-  },
-
   gatherStartup: function gatherStartup() {
     this._log.trace("gatherStartup");
     let counters = processInfo.getCounters();
     if (counters) {
       [this._startupIO.startupSessionRestoreReadBytes,
         this._startupIO.startupSessionRestoreWriteBytes] = counters;
     }
     this._slowSQLStartup = Telemetry.slowSQL;
deleted file mode 100644
--- a/toolkit/components/telemetry/ThreadHangStats.cpp
+++ /dev/null
@@ -1,322 +0,0 @@
-/* -*- 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/. */
-
-#include "mozilla/HangAnnotations.h"
-#include "ThreadHangStats.h"
-#include "nsITelemetry.h"
-#include "HangReports.h"
-#include "jsapi.h"
-
-namespace {
-
-using namespace mozilla;
-using namespace mozilla::HangMonitor;
-using namespace mozilla::Telemetry;
-
-static JSObject*
-CreateJSTimeHistogram(JSContext* cx, const Telemetry::TimeHistogram& time)
-{
-  /* Create JS representation of TimeHistogram,
-     in the format of Chromium-style histograms. */
-  JS::RootedObject ret(cx, JS_NewPlainObject(cx));
-  if (!ret) {
-    return nullptr;
-  }
-
-  if (!JS_DefineProperty(cx, ret, "min", time.GetBucketMin(0),
-                         JSPROP_ENUMERATE) ||
-      !JS_DefineProperty(cx, ret, "max",
-                         time.GetBucketMax(ArrayLength(time) - 1),
-                         JSPROP_ENUMERATE) ||
-      !JS_DefineProperty(cx, ret, "histogram_type",
-                         nsITelemetry::HISTOGRAM_EXPONENTIAL,
-                         JSPROP_ENUMERATE)) {
-    return nullptr;
-  }
-  // TODO: calculate "sum"
-  if (!JS_DefineProperty(cx, ret, "sum", 0, JSPROP_ENUMERATE)) {
-    return nullptr;
-  }
-
-  JS::RootedObject ranges(
-    cx, JS_NewArrayObject(cx, ArrayLength(time) + 1));
-  JS::RootedObject counts(
-    cx, JS_NewArrayObject(cx, ArrayLength(time) + 1));
-  if (!ranges || !counts) {
-    return nullptr;
-  }
-  /* In a Chromium-style histogram, the first bucket is an "under" bucket
-     that represents all values below the histogram's range. */
-  if (!JS_DefineElement(cx, ranges, 0, time.GetBucketMin(0), JSPROP_ENUMERATE) ||
-      !JS_DefineElement(cx, counts, 0, 0, JSPROP_ENUMERATE)) {
-    return nullptr;
-  }
-  for (size_t i = 0; i < ArrayLength(time); i++) {
-    if (!JS_DefineElement(cx, ranges, i + 1, time.GetBucketMax(i),
-                          JSPROP_ENUMERATE) ||
-        !JS_DefineElement(cx, counts, i + 1, time[i], JSPROP_ENUMERATE)) {
-      return nullptr;
-    }
-  }
-  if (!JS_DefineProperty(cx, ret, "ranges", ranges, JSPROP_ENUMERATE) ||
-      !JS_DefineProperty(cx, ret, "counts", counts, JSPROP_ENUMERATE)) {
-    return nullptr;
-  }
-  return ret;
-}
-
-static JSObject*
-CreateJSHangStack(JSContext* cx, const Telemetry::HangStack& stack)
-{
-  JS::RootedObject ret(cx, JS_NewArrayObject(cx, stack.length()));
-  if (!ret) {
-    return nullptr;
-  }
-  for (size_t i = 0; i < stack.length(); i++) {
-    JS::RootedString string(cx, JS_NewStringCopyZ(cx, stack[i]));
-    if (!JS_DefineElement(cx, ret, i, string, JSPROP_ENUMERATE)) {
-      return nullptr;
-    }
-  }
-  return ret;
-}
-
-static void
-CreateJSHangAnnotations(JSContext* cx, const HangAnnotationsVector& annotations,
-                        JS::MutableHandleObject returnedObject)
-{
-  JS::RootedObject annotationsArray(cx, JS_NewArrayObject(cx, 0));
-  if (!annotationsArray) {
-    returnedObject.set(nullptr);
-    return;
-  }
-  // We keep track of the annotations we reported in this hash set, so we can
-  // discard duplicated ones.
-  nsTHashtable<nsStringHashKey> reportedAnnotations;
-  size_t annotationIndex = 0;
-  for (const auto & curAnnotations : annotations) {
-    JS::RootedObject jsAnnotation(cx, JS_NewPlainObject(cx));
-    if (!jsAnnotation) {
-      continue;
-    }
-    // Build a key to index the current annotations in our hash set.
-    nsAutoString annotationsKey;
-    nsresult rv = ComputeAnnotationsKey(curAnnotations, annotationsKey);
-    if (NS_FAILED(rv)) {
-      continue;
-    }
-    // Check if the annotations are in the set. If that's the case, don't double report.
-    if (reportedAnnotations.GetEntry(annotationsKey)) {
-      continue;
-    }
-    // If not, report them.
-    reportedAnnotations.PutEntry(annotationsKey);
-    UniquePtr<HangAnnotations::Enumerator> annotationsEnum =
-      curAnnotations->GetEnumerator();
-    if (!annotationsEnum) {
-      continue;
-    }
-    nsAutoString key;
-    nsAutoString value;
-    while (annotationsEnum->Next(key, value)) {
-      JS::RootedValue jsValue(cx);
-      jsValue.setString(JS_NewUCStringCopyN(cx, value.get(), value.Length()));
-      if (!JS_DefineUCProperty(cx, jsAnnotation, key.get(), key.Length(),
-                               jsValue, JSPROP_ENUMERATE)) {
-        returnedObject.set(nullptr);
-        return;
-      }
-    }
-    if (!JS_SetElement(cx, annotationsArray, annotationIndex, jsAnnotation)) {
-      continue;
-    }
-    ++annotationIndex;
-  }
-  // Return the array using a |MutableHandleObject| to avoid triggering a false
-  // positive rooting issue in the hazard analysis build.
-  returnedObject.set(annotationsArray);
-}
-
-static JSObject*
-CreateJSHangHistogram(JSContext* cx, const Telemetry::HangHistogram& hang)
-{
-  JS::RootedObject ret(cx, JS_NewPlainObject(cx));
-  if (!ret) {
-    return nullptr;
-  }
-
-  JS::RootedObject stack(cx, CreateJSHangStack(cx, hang.GetStack()));
-  JS::RootedObject time(cx, CreateJSTimeHistogram(cx, hang));
-  auto& hangAnnotations = hang.GetAnnotations();
-  JS::RootedObject annotations(cx);
-  CreateJSHangAnnotations(cx, hangAnnotations, &annotations);
-
-  if (!stack ||
-      !time ||
-      !annotations ||
-      !JS_DefineProperty(cx, ret, "stack", stack, JSPROP_ENUMERATE) ||
-      !JS_DefineProperty(cx, ret, "histogram", time, JSPROP_ENUMERATE) ||
-      (!hangAnnotations.empty() && // <-- Only define annotations when nonempty
-        !JS_DefineProperty(cx, ret, "annotations", annotations, JSPROP_ENUMERATE))) {
-    return nullptr;
-  }
-
-  return ret;
-}
-
-} // namespace
-
-namespace mozilla {
-namespace Telemetry {
-
-JSObject*
-CreateJSThreadHangStats(JSContext* cx, const Telemetry::ThreadHangStats& thread)
-{
-  JS::RootedObject ret(cx, JS_NewPlainObject(cx));
-  if (!ret) {
-    return nullptr;
-  }
-  JS::RootedString name(cx, JS_NewStringCopyZ(cx, thread.GetName()));
-  if (!name ||
-      !JS_DefineProperty(cx, ret, "name", name, JSPROP_ENUMERATE)) {
-    return nullptr;
-  }
-
-  JS::RootedObject activity(cx, CreateJSTimeHistogram(cx, thread.mActivity));
-  if (!activity ||
-      !JS_DefineProperty(cx, ret, "activity", activity, JSPROP_ENUMERATE)) {
-    return nullptr;
-  }
-
-  // Process the hangs into a hangs object.
-  JS::RootedObject hangs(cx, JS_NewArrayObject(cx, 0));
-  if (!hangs) {
-    return nullptr;
-  }
-  for (size_t i = 0; i < thread.mHangs.length(); i++) {
-    JS::RootedObject obj(cx, CreateJSHangHistogram(cx, thread.mHangs[i]));
-    if (!ret) {
-      return nullptr;
-    }
-
-    JS::RootedString runnableName(cx, JS_NewStringCopyZ(cx, thread.mHangs[i].GetRunnableName()));
-    if (!runnableName ||
-        !JS_DefineProperty(cx, ret, "runnableName", runnableName, JSPROP_ENUMERATE)) {
-      return nullptr;
-    }
-
-    // Check if we have a cached native stack index, and if we do record it.
-    uint32_t index = thread.mHangs[i].GetNativeStackIndex();
-    if (index != Telemetry::HangHistogram::NO_NATIVE_STACK_INDEX) {
-      if (!JS_DefineProperty(cx, obj, "nativeStack", index, JSPROP_ENUMERATE)) {
-        return nullptr;
-      }
-    }
-
-    if (!JS_DefineElement(cx, hangs, i, obj, JSPROP_ENUMERATE)) {
-      return nullptr;
-    }
-  }
-  if (!JS_DefineProperty(cx, ret, "hangs", hangs, JSPROP_ENUMERATE)) {
-    return nullptr;
-  }
-
-  // We should already have a CombinedStacks object on the ThreadHangStats, so
-  // add that one.
-  JS::RootedObject fullReportObj(cx, CreateJSStackObject(cx, thread.mCombinedStacks));
-  if (!fullReportObj) {
-    return nullptr;
-  }
-
-  if (!JS_DefineProperty(cx, ret, "nativeStacks", fullReportObj, JSPROP_ENUMERATE)) {
-    return nullptr;
-  }
-
-  return ret;
-}
-
-void
-TimeHistogram::Add(PRIntervalTime aTime)
-{
-  uint32_t timeMs = PR_IntervalToMilliseconds(aTime);
-  size_t index = mozilla::FloorLog2(timeMs);
-  operator[](index)++;
-}
-
-const char*
-HangStack::InfallibleAppendViaBuffer(const char* aText, size_t aLength)
-{
-  MOZ_ASSERT(this->canAppendWithoutRealloc(1));
-  // Include null-terminator in length count.
-  MOZ_ASSERT(mBuffer.canAppendWithoutRealloc(aLength + 1));
-
-  const char* const entry = mBuffer.end();
-  mBuffer.infallibleAppend(aText, aLength);
-  mBuffer.infallibleAppend('\0'); // Explicitly append null-terminator
-  this->infallibleAppend(entry);
-  return entry;
-}
-
-const char*
-HangStack::AppendViaBuffer(const char* aText, size_t aLength)
-{
-  if (!this->reserve(this->length() + 1)) {
-    return nullptr;
-  }
-
-  // Keep track of the previous buffer in case we need to adjust pointers later.
-  const char* const prevStart = mBuffer.begin();
-  const char* const prevEnd = mBuffer.end();
-
-  // Include null-terminator in length count.
-  if (!mBuffer.reserve(mBuffer.length() + aLength + 1)) {
-    return nullptr;
-  }
-
-  if (prevStart != mBuffer.begin()) {
-    // The buffer has moved; we have to adjust pointers in the stack.
-    for (auto & entry : *this) {
-      if (entry >= prevStart && entry < prevEnd) {
-        // Move from old buffer to new buffer.
-        entry += mBuffer.begin() - prevStart;
-      }
-    }
-  }
-
-  return InfallibleAppendViaBuffer(aText, aLength);
-}
-
-uint32_t
-HangHistogram::GetHash(const HangStack& aStack)
-{
-  uint32_t hash = 0;
-  for (const char* const* label = aStack.begin();
-       label != aStack.end(); label++) {
-    /* If the string is within our buffer, we need to hash its content.
-       Otherwise, the string is statically allocated, and we only need
-       to hash the pointer instead of the content. */
-    if (aStack.IsInBuffer(*label)) {
-      hash = AddToHash(hash, HashString(*label));
-    } else {
-      hash = AddToHash(hash, *label);
-    }
-  }
-  return hash;
-}
-
-bool
-HangHistogram::operator==(const HangHistogram& aOther) const
-{
-  if (mHash != aOther.mHash) {
-    return false;
-  }
-  if (mStack.length() != aOther.mStack.length()) {
-    return false;
-  }
-  return mStack == aOther.mStack;
-}
-
-} // namespace Telemetry
-} // namespace mozilla
deleted file mode 100644
--- a/toolkit/components/telemetry/ThreadHangStats.h
+++ /dev/null
@@ -1,289 +0,0 @@
-/* -*- 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 mozilla_BackgroundHangTelemetry_h
-#define mozilla_BackgroundHangTelemetry_h
-
-#include "mozilla/Array.h"
-#include "mozilla/Assertions.h"
-#include "mozilla/HangAnnotations.h"
-#include "mozilla/Move.h"
-#include "mozilla/Mutex.h"
-#include "mozilla/PodOperations.h"
-#include "mozilla/Vector.h"
-#include "mozilla/CombinedStacks.h"
-
-#include "nsString.h"
-#include "prinrval.h"
-#include "jsapi.h"
-
-namespace mozilla {
-namespace Telemetry {
-
-// This variable controls the maximum number of native hang stacks which may be
-// attached to a ping. This is due to how large native stacks can be. We want to
-// reduce the chance of a ping being discarded due to it exceeding the maximum
-// ping size.
-static const uint32_t kMaximumNativeHangStacks = 300;
-
-static const size_t kTimeHistogramBuckets = 8 * sizeof(PRIntervalTime);
-
-/* TimeHistogram is an efficient histogram that puts time durations into
-   exponential (base 2) buckets; times are accepted in PRIntervalTime and
-   stored in milliseconds. */
-class TimeHistogram : public mozilla::Array<uint32_t, kTimeHistogramBuckets>
-{
-public:
-  TimeHistogram()
-  {
-    mozilla::PodArrayZero(*this);
-  }
-  // Get minimum (inclusive) range of bucket in milliseconds
-  uint32_t GetBucketMin(size_t aBucket) const {
-    MOZ_ASSERT(aBucket < ArrayLength(*this));
-    return (1u << aBucket) & ~1u; // Bucket 0 starts at 0, not 1
-  }
-  // Get maximum (inclusive) range of bucket in milliseconds
-  uint32_t GetBucketMax(size_t aBucket) const {
-    MOZ_ASSERT(aBucket < ArrayLength(*this));
-    return (1u << (aBucket + 1u)) - 1u;
-  }
-  void Add(PRIntervalTime aTime);
-};
-
-/* A native stack is a simple list of pointers, so rather than building a
-   wrapper type, we typdef the type here. */
-typedef std::vector<uintptr_t> NativeHangStack;
-
-/* HangStack stores an array of const char pointers,
-   with optional internal storage for strings. */
-class HangStack
-{
-public:
-  static const size_t sMaxInlineStorage = 8;
-
-  // The maximum depth for the native stack frames that we might collect.
-  // XXX: Consider moving this to a different object?
-  static const size_t sMaxNativeFrames = 150;
-
-private:
-  typedef mozilla::Vector<const char*, sMaxInlineStorage> Impl;
-  Impl mImpl;
-
-  // Stack entries can either be a static const char*
-  // or a pointer to within this buffer.
-  mozilla::Vector<char, 0> mBuffer;
-
-public:
-  HangStack() {}
-
-  HangStack(HangStack&& aOther)
-    : mImpl(mozilla::Move(aOther.mImpl))
-    , mBuffer(mozilla::Move(aOther.mBuffer))
-  {
-  }
-
-  HangStack& operator=(HangStack&& aOther) {
-    mImpl = mozilla::Move(aOther.mImpl);
-    mBuffer = mozilla::Move(aOther.mBuffer);
-    return *this;
-  }
-
-  bool operator==(const HangStack& aOther) const {
-    for (size_t i = 0; i < length(); i++) {
-      if (!IsSameAsEntry(operator[](i), aOther[i])) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  bool operator!=(const HangStack& aOther) const {
-    return !operator==(aOther);
-  }
-
-  const char*& operator[](size_t aIndex) {
-    return mImpl[aIndex];
-  }
-
-  const char* const& operator[](size_t aIndex) const {
-    return mImpl[aIndex];
-  }
-
-  size_t capacity() const { return mImpl.capacity(); }
-  size_t length() const { return mImpl.length(); }
-  bool empty() const { return mImpl.empty(); }
-  bool canAppendWithoutRealloc(size_t aNeeded) const {
-    return mImpl.canAppendWithoutRealloc(aNeeded);
-  }
-  void infallibleAppend(const char* aEntry) { mImpl.infallibleAppend(aEntry); }
-  bool reserve(size_t aRequest) { return mImpl.reserve(aRequest); }
-  const char** begin() { return mImpl.begin(); }
-  const char* const* begin() const { return mImpl.begin(); }
-  const char** end() { return mImpl.end(); }
-  const char* const* end() const { return mImpl.end(); }
-  const char*& back() { return mImpl.back(); }
-  void erase(const char** aEntry) { mImpl.erase(aEntry); }
-  void erase(const char** aBegin, const char** aEnd) {
-    mImpl.erase(aBegin, aEnd);
-  }
-
-  void clear() {
-    mImpl.clear();
-    mBuffer.clear();
-  }
-
-  bool IsInBuffer(const char* aEntry) const {
-    return aEntry >= mBuffer.begin() && aEntry < mBuffer.end();
-  }
-
-  bool IsSameAsEntry(const char* aEntry, const char* aOther) const {
-    // If the entry came from the buffer, we need to compare its content;
-    // otherwise we only need to compare its pointer.
-    return IsInBuffer(aEntry) ? !strcmp(aEntry, aOther) : (aEntry == aOther);
-  }
-
-  size_t AvailableBufferSize() const {
-    return mBuffer.capacity() - mBuffer.length();
-  }
-
-  bool EnsureBufferCapacity(size_t aCapacity) {
-    // aCapacity is the minimal capacity and Vector may make the actual
-    // capacity larger, in which case we want to use up all the space.
-    return mBuffer.reserve(aCapacity) &&
-           mBuffer.reserve(mBuffer.capacity());
-  }
-
-  const char* InfallibleAppendViaBuffer(const char* aText, size_t aLength);
-  const char* AppendViaBuffer(const char* aText, size_t aLength);
-};
-
-/* A hang histogram consists of a stack associated with the
-   hang, along with a time histogram of the hang times. */
-class HangHistogram : public TimeHistogram
-{
-public:
-  // Value used for mNativeStackIndex to represent the absence of a cached
-  // native stack.
-  static const uint32_t NO_NATIVE_STACK_INDEX = UINT32_MAX;
-
-private:
-  static uint32_t GetHash(const HangStack& aStack);
-
-  HangStack mStack;
-  // Cached index of the native stack in the mCombinedStacks list in the owning
-  // ThreadHangStats object. A default value of NO_NATIVE_STACK_INDEX means that
-  // the ThreadHangStats object which owns this HangHistogram doesn't have a
-  // cached CombinedStacks with this HangHistogram in it.
-  uint32_t mNativeStackIndex;
-  // Use a hash to speed comparisons
-  const uint32_t mHash;
-  // Annotations attributed to this stack
-  HangMonitor::HangAnnotationsVector mAnnotations;
-  // The name of the runnable on the current thread.
-  nsCString mRunnableName;
-
-public:
-  explicit HangHistogram(HangStack&& aStack, const nsACString& aRunnableName)
-    : mStack(mozilla::Move(aStack))
-    , mNativeStackIndex(NO_NATIVE_STACK_INDEX)
-    , mHash(GetHash(mStack))
-    , mRunnableName(aRunnableName)
-  {
-  }
-
-  HangHistogram(HangHistogram&& aOther)
-    : TimeHistogram(mozilla::Move(aOther))
-    , mStack(mozilla::Move(aOther.mStack))
-    , mNativeStackIndex(mozilla::Move(aOther.mNativeStackIndex))
-    , mHash(mozilla::Move(aOther.mHash))
-    , mAnnotations(mozilla::Move(aOther.mAnnotations))
-    , mRunnableName(aOther.mRunnableName)
-  {
-  }
-  bool operator==(const HangHistogram& aOther) const;
-  bool operator!=(const HangHistogram& aOther) const
-  {
-    return !operator==(aOther);
-  }
-  const HangStack& GetStack() const {
-    return mStack;
-  }
-  uint32_t GetNativeStackIndex() const {
-    return mNativeStackIndex;
-  }
-  void SetNativeStackIndex(uint32_t aIndex) {
-    MOZ_ASSERT(aIndex != NO_NATIVE_STACK_INDEX);
-    mNativeStackIndex = aIndex;
-  }
-  const char* GetRunnableName() const {
-    return mRunnableName.get();
-  }
-  const HangMonitor::HangAnnotationsVector& GetAnnotations() const {
-    return mAnnotations;
-  }
-  void Add(PRIntervalTime aTime, HangMonitor::HangAnnotationsPtr aAnnotations) {
-    TimeHistogram::Add(aTime);
-    if (aAnnotations) {
-      if (!mAnnotations.append(Move(aAnnotations))) {
-        MOZ_CRASH();
-      }
-    }
-  }
-};
-
-/* Thread hang stats consist of
- - thread name
- - time histogram of all task run times
- - hang histograms of individual hangs
- - annotations for each hang
- - combined native stacks for all hangs
-*/
-class ThreadHangStats
-{
-private:
-  nsCString mName;
-
-public:
-  TimeHistogram mActivity;
-  mozilla::Vector<HangHistogram, 4> mHangs;
-  uint32_t mNativeStackCnt;
-  CombinedStacks mCombinedStacks;
-
-  explicit ThreadHangStats(const char* aName)
-    : mName(aName)
-    , mNativeStackCnt(0)
-    , mCombinedStacks(Telemetry::kMaximumNativeHangStacks)
-  {
-  }
-  ThreadHangStats(ThreadHangStats&& aOther)
-    : mName(mozilla::Move(aOther.mName))
-    , mActivity(mozilla::Move(aOther.mActivity))
-    , mHangs(mozilla::Move(aOther.mHangs))
-    , mNativeStackCnt(aOther.mNativeStackCnt)
-    , mCombinedStacks(mozilla::Move(aOther.mCombinedStacks))
-  {
-    aOther.mNativeStackCnt = 0;
-  }
-  const char* GetName() const {
-    return mName.get();
-  }
-};
-
-/**
- * Reflects thread hang stats object as a JS object.
- *
- * @param JSContext* cx javascript context.
- * @param JSContext* cx thread hang statistics.
- *
- * @return JSObject* Javascript reflection of the statistics.
- */
-JSObject*
-CreateJSThreadHangStats(JSContext* cx, const Telemetry::ThreadHangStats& thread);
-
-} // namespace Telemetry
-} // namespace mozilla
-
-#endif // mozilla_BackgroundHangTelemetry_h
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/docs/data/backgroundhangmonitor-ping.rst
@@ -0,0 +1,118 @@
+
+"backgroundhangmonitor" ping
+==========
+
+Whenever a thread monitored by the Background Hang Monitor hangs, a stack and
+some non-identifying information about the hang is captured. When 50 of these
+hangs are collected across all processes, or the browser exits, this ping is
+transmitted with the collected hang information.
+
+This ping is only collected in nightly builds, to avoid the high volume of pings
+which would be produced in Beta.
+
+Structure:
+
+.. code-block:: js
+
+    {
+      "type": "backgroundhangmonitor",
+      ... // common ping data
+      "environment": { ... },
+      "payload": {
+        "modules": [
+          [
+            <string>, // Name of the file holding the debug information.
+            <string> // Breakpad ID of this module.
+          ],
+          ...
+        ],
+        "hangs": [
+          {
+            "duration": <number>, // duration of the hang in milliseconds.
+            "thread": <string>, // name of the hanging thread.
+            "runnableName": <string>, // name of the runnable executing during the hang.
+                                      // Runnable names are only collected for the XPCOM main thread.
+            "process": <string>, // Type of process that hung, see below for a list of types.
+            "annotations": { ... }, // A set of annotations on the hang, see below.
+            "pseudoStack": [ ... ], // List of pseudostack frame names.
+            "stack": [ ... ], // interleaved hang stack, see below.
+          },
+          ...
+        ]
+      }
+    }
+
+.. note: :
+
+  Hangs are collected whenever the current runnable takes over 128ms.
+
+Process Types
+-------------
+
+The ``process`` field is a string denoting the kind of process that hung. Hangs
+are currently sent only for the processes below:
+
++---------------+---------------------------------------------------+
+| Kind          | Description                                       |
++===============+===================================================+
+| default       | Main process, also known as the browser process   |
++---------------+---------------------------------------------------+
+| tab           | Content process                                   |
++---------------+---------------------------------------------------+
+| gpu           | GPU process                                       |
++---------------+---------------------------------------------------+
+
+Stack Traces
+------------
+
+Each hang object contains a ``stack`` field which has been populated with an
+interleaved stack trace of the hung thread. An interleaved stack consists of a
+native backtrace with additional frames interleaved, representing chrome JS and
+pseudostack entries.
+
+Note that this field only contains native stack frames, pseudostack and chrome
+JS script frames. If native stacks can not be collected on the target platform,
+or stackwalking was not initialized, there will be no native frames present, and
+the stack will consist only of pseudostack and chrome JS script frames.
+
+A single frame in the stack is either a raw string, representing a pseudostack
+or chrome JS script frame, or a native stack frame:
+
+.. code-block:: js
+
+    [
+      <number>, // Index in the payload.modules list of the module description.
+                // -1 if this frame was not in a valid module.
+      <string> // Hex string (e.g. "FF0F") of the frame offset in the module.
+    ]
+
+Annotations
+-----------
+
+The annotations field is a map from key to string value, for example if the user
+was interacting during a hang the annotations field would look something like
+this:
+
+.. code-block:: js
+
+    {
+        "UserInteracting": "true"
+    }
+
+The following annotations are currently present in tree:
+
++-----------------+-------------------------------------------------+
+| Name            | Description                                     |
++=================+=================================================+
+| UserInteracting | "true" if the user was actively interacting     |
++-----------------+-------------------------------------------------+
+| pluginName      | Name of the currently running plugin            |
++-----------------+-------------------------------------------------+
+| pluginVersion   | Version of the currently running plugin         |
++-----------------+-------------------------------------------------+
+| HangUIShown     | "true" if the hang UI was shown                 |
++-----------------+-------------------------------------------------+
+| HangUIContinued | "true" if continue was selected in the hang UI  |
++-----------------+-------------------------------------------------+
+| HangUIDontShow  | "true" if the hang UI was not shown             |
++-----------------+-------------------------------------------------+
--- a/toolkit/components/telemetry/docs/data/index.rst
+++ b/toolkit/components/telemetry/docs/data/index.rst
@@ -7,12 +7,13 @@ Data documentation
    :titlesonly:
    :glob:
 
    common-ping
    environment
    main-ping
    deletion-ping
    crash-ping
+   backgroundhangmonitor-ping
    anonymous-ping
    *-ping
 
 The `mozilla-pipeline-schemas repository <https://github.com/mozilla-services/mozilla-pipeline-schemas/>`_ contains schemas for some of the pings.
--- a/toolkit/components/telemetry/docs/data/main-ping.rst
+++ b/toolkit/components/telemetry/docs/data/main-ping.rst
@@ -58,17 +58,17 @@ Structure:
       processes: {...},
       childPayloads: [...], // only present with e10s; reduced payloads from content processes, null on failure
       simpleMeasurements: {...},
 
       // The following properties may all be null if we fail to collect them.
       histograms: {...},
       keyedHistograms: {...},
       chromeHangs: {...},
-      threadHangStats: [...],
+      threadHangStats: [...], // obsolete in firefox 57, use the 'bhr' ping
       capturedStacks: {...},
       log: [...],
       webrtc: {...},
       gc: {...},
       fileIOReports: {...},
       lateWrites: {...},
       addonDetails: {...},
       UIMeasurements: [...],  // Android only
@@ -245,16 +245,18 @@ This section contains the histograms tha
 keyedHistograms
 ---------------
 This section contains the keyed histograms available for the current platform.
 
 As of Firefox 48, this section does not contain empty keyed histograms anymore.
 
 threadHangStats
 ---------------
+As of Firefox 57 this section is no longer present, and has been replaced with the 'bhr' ping.
+
 Contains the statistics about the hangs in main and background threads. Note that hangs in this section capture the `C++ pseudostack <https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Profiling_with_the_Built-in_Profiler#Native_stack_vs._Pseudo_stack>`_ and an incomplete JS stack, which is not 100% precise. For particularly egregious hangs, and on nightly, an unsymbolicated native stack is also captured. The amount of time that is considered "egregious" is different from thread to thread, and is set when the BackgroundHangMonitor is constructed for that thread. In general though, hangs from 5 - 10 seconds are generally considered egregious. Shorter hangs (1 - 2s) are considered egregious for other threads (the compositor thread, and the hang monitor that is only enabled during tab switch).
 
 To avoid submitting overly large payloads, some limits are applied:
 
 * Identical, adjacent "(chrome script)" or "(content script)" stack entries are collapsed together. If a stack is reduced, the "(reduced stack)" frame marker is added as the oldest frame.
 * The depth of the reported pseudostacks is limited to 11 entries. This value represents the 99.9th percentile of the thread hangs stack depths reported by Telemetry.
 * The native stacks are limited to a depth of 25 stack frames.
 
--- a/toolkit/components/telemetry/moz.build
+++ b/toolkit/components/telemetry/moz.build
@@ -45,32 +45,30 @@ EXPORTS.mozilla += [
     '!TelemetryHistogramEnums.h',
     '!TelemetryProcessEnums.h',
     '!TelemetryScalarEnums.h',
     'CombinedStacks.h',
     'ipc/TelemetryComms.h',
     'ipc/TelemetryIPC.h',
     'ProcessedStack.h',
     'Telemetry.h',
-    'ThreadHangStats.h',
 ]
 
 SOURCES += [
     'CombinedStacks.cpp',
     'HangReports.cpp',
     'ipc/TelemetryIPC.cpp',
     'ipc/TelemetryIPCAccumulator.cpp',
     'ProcessedStack.cpp',
     'Telemetry.cpp',
     'TelemetryCommon.cpp',
     'TelemetryEvent.cpp',
     'TelemetryHistogram.cpp',
     'TelemetryIOInterposeObserver.cpp',
     'TelemetryScalar.cpp',
-    'ThreadHangStats.cpp',
     'WebrtcTelemetry.cpp',
 ]
 
 # KeyedStackCapturer entirely relies on profiler to be enabled.
 if CONFIG['MOZ_GECKO_PROFILER']:
     SOURCES += [
       'KeyedStackCapturer.cpp'
     ]
--- a/toolkit/components/telemetry/nsITelemetry.idl
+++ b/toolkit/components/telemetry/nsITelemetry.idl
@@ -198,33 +198,16 @@ interface nsITelemetry : nsISupports
    * @return A promise that resolves to an array of modules or rejects with
              NS_ERROR_FAILURE on failure.
    * @throws NS_ERROR_NOT_IMPLEMENTED if the Gecko profiler is not enabled.
    */
   [implicit_jscontext]
   nsISupports getLoadedModules();
 
   /*
-   * An array of thread hang stats,
-   *   [<thread>, <thread>, ...]
-   * <thread> represents a single thread,
-   *   {"name": "<name>",
-   *    "activity": <time>,
-   *    "hangs": [<hang>, <hang>, ...]}
-   * <time> represents a histogram of time intervals in milliseconds,
-   *   with the same format as histogramSnapshots
-   * <hang> represents a particular hang,
-   *   {"stack": <stack>, "nativeStack": <stack>, "histogram": <time>}
-   * <stack> represents the hang's stack,
-   *   ["<frame_0>", "<frame_1>", ...]
-   */
-  [implicit_jscontext]
-  readonly attribute jsval threadHangStats;
-
-  /*
    * An object with two fields: memoryMap and stacks.
    * * memoryMap is a list of loaded libraries.
    * * stacks is a list of stacks. Each stack is a list of pairs of the form
    *   [moduleIndex, offset]. The moduleIndex is an index into the memoryMap and
    *   offset is an offset in the library at memoryMap[moduleIndex].
    * This format is used to make it easier to send the stacks to the
    * symbolication server.
    */
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
@@ -1975,17 +1975,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 = [
-    "chromeHangs", "threadHangStats", "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.
deleted file mode 100644
--- a/toolkit/components/telemetry/tests/unit/test_ThreadHangStats.js
+++ /dev/null
@@ -1,106 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-Cu.import("resource://gre/modules/Services.jsm");
-
-function getMainThreadHangStats() {
-  let threads = Services.telemetry.threadHangStats;
-  return threads.find((thread) => (thread.name === "Gecko"));
-}
-
-function run_test() {
-  let startHangs = getMainThreadHangStats();
-
-  // We disable hang reporting in several situations (e.g. debug builds,
-  // official releases). In those cases, we don't have hang stats available
-  // and should exit the test early.
-  if (!startHangs) {
-    ok("Hang reporting not enabled.");
-    return;
-  }
-
-  if (Services.appinfo.OS === "Linux" || Services.appinfo.OS === "Android") {
-    // We use the rt_tgsigqueueinfo syscall on Linux which requires a
-    // certain kernel version. It's not an error if the system running
-    // the test is older than that.
-    let kernel = Services.sysinfo.get("kernel_version") ||
-                 Services.sysinfo.get("version");
-    if (Services.vc.compare(kernel, "2.6.31") < 0) {
-      ok("Hang reporting not supported for old kernel.");
-      return;
-    }
-  }
-
-  // Run three events in the event loop:
-  // the first event causes a transient hang;
-  // the second event causes a permanent hang;
-  // the third event checks results from previous events.
-
-  do_execute_soon(() => {
-    // Cause a hang lasting 1 second (transient hang).
-    let startTime = Date.now();
-    while ((Date.now() - startTime) < 1000);
-  });
-
-  do_execute_soon(() => {
-    // Cause a hang lasting 10 seconds (permanent hang).
-    let startTime = Date.now();
-    while ((Date.now() - startTime) < 10000);
-  });
-
-  do_execute_soon(() => {
-    do_test_pending();
-
-    let check_results = () => {
-      let endHangs = getMainThreadHangStats();
-
-      // Because hangs are recorded asynchronously, if we don't see new hangs,
-      // we should wait for pending hangs to be recorded. On the other hand,
-      // if hang monitoring is broken, this test will time out.
-      if (endHangs.hangs.length === startHangs.hangs.length) {
-        do_timeout(100, check_results);
-        return;
-      }
-
-      let check_histogram = (histogram) => {
-        equal(typeof histogram, "object");
-        equal(histogram.histogram_type, 0);
-        equal(typeof histogram.min, "number");
-        equal(typeof histogram.max, "number");
-        equal(typeof histogram.sum, "number");
-        ok(Array.isArray(histogram.ranges));
-        ok(Array.isArray(histogram.counts));
-        equal(histogram.counts.length, histogram.ranges.length);
-      };
-
-      // Make sure the hang stats structure is what we expect.
-      equal(typeof endHangs, "object");
-      check_histogram(endHangs.activity);
-
-      ok(Array.isArray(endHangs.hangs));
-      notEqual(endHangs.hangs.length, 0);
-
-      ok(Array.isArray(endHangs.hangs[0].stack));
-      notEqual(endHangs.hangs[0].stack.length, 0);
-      equal(typeof endHangs.hangs[0].stack[0], "string");
-
-      // Make sure there is a nativeStacks field with native stacks in it.
-      ok(Array.isArray(endHangs.nativeStacks.memoryMap));
-      ok(Array.isArray(endHangs.nativeStacks.stacks));
-      ok(endHangs.nativeStacks.stacks.length > 0);
-
-      // Make sure that at least one of the hangs contains a native stack.
-      ok(endHangs.hangs.some((hang) => {
-        return typeof hang.nativeStack == "number" &&
-          hang.nativeStack < endHangs.nativeStacks.stacks.length &&
-          Array.isArray(endHangs.nativeStacks.stacks[hang.nativeStack]);
-      }));
-
-      check_histogram(endHangs.hangs[0].histogram);
-
-      do_test_finished();
-    };
-
-    check_results();
-  });
-}
--- a/toolkit/components/telemetry/tests/unit/xpcshell.ini
+++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini
@@ -47,19 +47,16 @@ tags = addons
 [test_TelemetryStopwatch.js]
 [test_TelemetryControllerBuildID.js]
 [test_TelemetrySendOldPings.js]
 skip-if = os == "android" # Disabled due to intermittent orange on Android
 tags = addons
 [test_TelemetrySession.js]
 tags = addons
 [test_TelemetrySession_activeTicks.js]
-[test_ThreadHangStats.js]
-skip-if = os == "android" || asan # BHR is disabled on android (bug 1368520). ASAN does not have frame pointers.
-run-sequentially = Bug 1046307, test can fail intermittently when CPU load is high
 [test_TelemetrySend.js]
 [test_ChildHistograms.js]
 skip-if = os == "android" # Disabled due to crashes (see bug 1331366)
 tags = addons
 [test_ChildScalars.js]
 skip-if = os == "android" # Disabled due to crashes (see bug 1331366)
 [test_TelemetryReportingPolicy.js]
 skip-if = os == "android" # Disabled due to crashes (see bug 1367762)
--- a/toolkit/content/aboutTelemetry.js
+++ b/toolkit/content/aboutTelemetry.js
@@ -1133,68 +1133,16 @@ var CapturedStacks = {
 
   renderCaptureHeader: function CaptureStacks_renderCaptureHeader(index, captures) {
     let key = captures[index][0];
     let cardinality = captures[index][2];
     StackRenderer.renderHeader("captured-stacks", [key, cardinality]);
   }
 };
 
-var ThreadHangStats = {
-
-  /**
-   * Renders raw thread hang stats data
-   */
-  render(aPayload) {
-    let div = document.getElementById("thread-hang-stats");
-    removeAllChildNodes(div);
-
-    let stats = aPayload.threadHangStats;
-    setHasData("thread-hang-stats-section", stats && (stats.length > 0));
-    if (!stats) {
-      return;
-    }
-
-    stats.forEach((thread) => {
-      div.appendChild(this.renderThread(thread));
-    });
-  },
-
-  /**
-   * Creates and fills data corresponding to a thread
-   */
-  renderThread(aThread) {
-    let div = document.createElement("div");
-
-    let title = document.createElement("h2");
-    title.textContent = aThread.name;
-    div.appendChild(title);
-    div.id = title;
-
-    // Don't localize the histogram name, because the
-    // name is also used as the div element's ID
-    Histogram.render(div, aThread.name + "-Activity",
-                     aThread.activity, {exponential: true}, true);
-    aThread.hangs.forEach((hang, index) => {
-      let hangName = aThread.name + "-Hang-" + (index + 1);
-      let hangDiv = Histogram.render(
-        div, hangName, hang.histogram, {exponential: true}, true);
-      let stackDiv = document.createElement("div");
-      hang.stack.forEach((frame) => {
-        stackDiv.appendChild(document.createTextNode(frame));
-        // Leave an extra <br> at the end of the stack listing
-        stackDiv.appendChild(document.createElement("br"));
-      });
-      // Insert stack after the histogram title
-      hangDiv.insertBefore(stackDiv, hangDiv.childNodes[1]);
-    });
-    return div;
-  },
-};
-
 var Histogram = {
 
   hgramSamplesCaption: bundle.GetStringFromName("histogramSamples"),
 
   hgramAverageCaption: bundle.GetStringFromName("histogramAverage"),
 
   hgramSumCaption: bundle.GetStringFromName("histogramSum"),
 
@@ -1203,21 +1151,20 @@ var Histogram = {
   /**
    * Renders a single Telemetry histogram
    *
    * @param aParent Parent element
    * @param aName Histogram name
    * @param aHgram Histogram information
    * @param aOptions Object with render options
    *                 * exponential: bars follow logarithmic scale
-   * @param aIsBHR whether or not requires fixing the labels for TimeHistogram
    */
-  render: function Histogram_render(aParent, aName, aHgram, aOptions, aIsBHR) {
+  render: function Histogram_render(aParent, aName, aHgram, aOptions) {
     let options = aOptions || {};
-    let hgram = this.processHistogram(aHgram, aName, aIsBHR);
+    let hgram = this.processHistogram(aHgram, aName);
 
     let outerDiv = document.createElement("div");
     outerDiv.className = "histogram";
     outerDiv.id = aName;
 
     let divTitle = document.createElement("div");
     divTitle.className = "histogram-title";
     divTitle.appendChild(document.createTextNode(aName));
@@ -1248,17 +1195,17 @@ var Histogram = {
                                                  .copyString(this.histogramText);
     });
     outerDiv.appendChild(copyButton);
 
     aParent.appendChild(outerDiv);
     return outerDiv;
   },
 
-  processHistogram(aHgram, aName, aIsBHR) {
+  processHistogram(aHgram, aName) {
     const values = Object.keys(aHgram.values).map(k => aHgram.values[k]);
     if (!values.length) {
       // If we have no values collected for this histogram, just return
       // zero values so we still render it.
       return {
         values: [],
         pretty_average: 0,
         max: 0,
@@ -1266,40 +1213,18 @@ var Histogram = {
         sum: 0
       };
     }
 
     const sample_count = values.reduceRight((a, b) => a + b);
     const average = Math.round(aHgram.sum * 10 / sample_count) / 10;
     const max_value = Math.max(...values);
 
-    function labelFunc(k) {
-      // - BHR histograms are TimeHistograms: Exactly power-of-two buckets (from 0)
-      //   (buckets: [0..1], [2..3], [4..7], [8..15], ... note the 0..1 anomaly - same bucket)
-      // - TimeHistogram's JS representation adds a dummy (empty) "0" bucket, and
-      //   the rest of the buckets have the label as the upper value of the
-      //   bucket (non TimeHistograms have the lower value of the bucket as label).
-      //   So JS TimeHistograms bucket labels are: 0 (dummy), 1, 3, 7, 15, ...
-      // - see toolkit/components/telemetry/Telemetry.cpp
-      //   (CreateJSTimeHistogram, CreateJSThreadHangStats, CreateJSHangHistogram)
-      // - see toolkit/components/telemetry/ThreadHangStats.h
-      // Fix BHR labels to the "standard" format for about:telemetry as follows:
-      //   - The dummy 0 label+bucket will be filtered before arriving here
-      //   - If it's 1 -> manually correct it to 0 (the 0..1 anomaly)
-      //   - For the rest, set the label as the bottom value instead of the upper.
-      //   --> so we'll end with the following (non dummy) labels: 0, 2, 4, 8, 16, ...
-      if (!aIsBHR) {
-        return k;
-      }
-      return k == 1 ? 0 : (k + 1) / 2;
-    }
-
     const labelledValues = Object.keys(aHgram.values)
-                           .filter(label => !aIsBHR || Number(label) != 0) // remove dummy 0 label for BHR
-                           .map(k => [labelFunc(Number(k)), aHgram.values[k]]);
+                           .map(k => [Number(k), aHgram.values[k]]);
 
     let result = {
       values: labelledValues,
       pretty_average: average,
       max: max_value,
       sample_count,
       sum: aHgram.sum
     };
@@ -2437,17 +2362,14 @@ function displayRichPingData(ping, updat
   }
 
   // Show chrome hang stacks
   ChromeHangs.render(payload);
 
   // Show telemetry log.
   TelLog.render(payload);
 
-  // Show thread hang stats
-  ThreadHangStats.render(payload);
-
   // Show simple measurements
   SimpleMeasurements.render(payload);
 
 }
 
 window.addEventListener("load", onLoad);
--- a/toolkit/content/aboutTelemetry.xhtml
+++ b/toolkit/content/aboutTelemetry.xhtml
@@ -67,19 +67,16 @@
         <span class="category-name">&aboutTelemetry.telemetryLogSection;</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="thread-hang-stats-section">
-        <span class="category-name">&aboutTelemetry.threadHangStatsSection;</span>
-      </div>
       <div class="category" value="late-writes-section">
         <span class="category-name">&aboutTelemetry.lateWritesSection;</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>
@@ -202,20 +199,16 @@
       </section>
 
       <section id="chrome-hangs-section">
         <a id="chrome-hangs-fetch-symbols" href="">&aboutTelemetry.fetchStackSymbols;</a>
         <a id="chrome-hangs-hide-symbols" class="hidden" href="">&aboutTelemetry.hideStackSymbols;</a>
         <div id="chrome-hangs" class="data"></div>
       </section>
 
-      <section id="thread-hang-stats-section">
-        <div id="thread-hang-stats" 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" class="hidden" 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/locales/en-US/chrome/global/aboutTelemetry.dtd
+++ b/toolkit/locales/en-US/chrome/global/aboutTelemetry.dtd
@@ -79,20 +79,16 @@ Older
 <!ENTITY aboutTelemetry.slowSqlSection "
   Slow SQL Statements
 ">
 
 <!ENTITY aboutTelemetry.chromeHangsSection "
   Browser Hangs
 ">
 
-<!ENTITY aboutTelemetry.threadHangStatsSection "
-  Thread Hangs
-">
-
 <!ENTITY aboutTelemetry.capturedStacksSection "
   Captured Stacks
 ">
 
 <!ENTITY aboutTelemetry.scalarsSection "
   Scalars
 ">
 
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -1399,33 +1399,19 @@ PuppetWidget::HasPendingInputEvent()
   if (!mTabChild) {
     return false;
   }
 
   bool ret = false;
 
   mTabChild->GetIPCChannel()->PeekMessages(
     [&ret](const IPC::Message& aMsg) -> bool {
-      if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart)
-          == mozilla::dom::PBrowser::PBrowserStart) {
-        switch (aMsg.type()) {
-          case mozilla::dom::PBrowser::Msg_RealMouseMoveEvent__ID:
-          case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
-          case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
-          case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
-          case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
-          case mozilla::dom::PBrowser::Msg_RealTouchMoveEvent__ID:
-          case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
-          case mozilla::dom::PBrowser::Msg_UpdateDimensions__ID:
-          case mozilla::dom::PBrowser::Msg_MouseEvent__ID:
-          case mozilla::dom::PBrowser::Msg_KeyEvent__ID:
-          case mozilla::dom::PBrowser::Msg_SetDocShellIsActive__ID:
-            ret = true;
-            return false;  // Stop peeking.
-        }
+      if (nsContentUtils::IsMessageInputEvent(aMsg)) {
+        ret = true;
+        return false; // Stop peeking.
       }
       return true;
     }
   );
 
   return ret;
 }
 
--- a/widget/nsPrintSettingsImpl.h
+++ b/widget/nsPrintSettingsImpl.h
@@ -86,17 +86,17 @@ protected:
   int16_t       mPaperSizeUnit;
 
   bool          mPrintReversed;
   bool          mPrintInColor; // a false means grayscale
   int32_t       mOrientation;  // see orientation consts
   int32_t       mResolution;
   int32_t       mDuplex;
   int32_t       mNumCopies;
-  nsXPIDLString mPrinter;
+  nsString      mPrinter;
   bool          mPrintToFile;
   nsString      mToFileName;
   int16_t       mOutputFormat;
   bool          mIsInitedFromPrinter;
   bool          mIsInitedFromPrefs;
 };
 
 #endif /* nsPrintSettings_h__ */
--- a/widget/tests/TestChromeMargin.cpp
+++ b/widget/tests/TestChromeMargin.cpp
@@ -21,17 +21,16 @@
 #define nsAString_h___
 #define nsString_h___
 #define nsStringFwd_h___
 #define nsReadableUtils_h___
 class nsACString;
 class nsAString;
 class nsString;
 class nsCString;
-class nsXPIDLString;
 template<class T> class nsReadingIterator;
 #endif
 
 #include "nscore.h"
 #include "nsContentUtils.h"
 
 #ifndef MOZILLA_INTERNAL_API
 #undef nsString_h___
--- a/xpcom/string/nsReadableUtils.cpp
+++ b/xpcom/string/nsReadableUtils.cpp
@@ -1273,17 +1273,17 @@ EmptyCString()
   static const nsDependentCString sEmpty((const char*)empty_buffer);
 
   return sEmpty;
 }
 
 const nsString&
 NullString()
 {
-  static const nsXPIDLString sNull;
+  static const nsString sNull(mozilla::detail::StringDataFlags::VOIDED);
 
   return sNull;
 }
 
 const nsCString&
 NullCString()
 {
   static const nsXPIDLCString sNull;
--- a/xpcom/string/nsStringFwd.h
+++ b/xpcom/string/nsStringFwd.h
@@ -29,23 +29,22 @@ class nsAString;
 class nsSubstringTuple;
 class nsString;
 class nsAutoString;
 class nsDependentString;
 class nsDependentSubstring;
 class nsPromiseFlatString;
 class nsStringComparator;
 class nsDefaultStringComparator;
-class nsXPIDLString;
 
 // Single-byte (char) string types.
 class nsACString;
 class nsCSubstringTuple;
 class nsCString;
 class nsAutoCString;
 class nsDependentCString;
 class nsDependentCSubstring;
 class nsPromiseFlatCString;
 class nsCStringComparator;
 class nsDefaultCStringComparator;
-class nsXPIDLCString;
+class nsXPIDLCString; // deprecated
 
 #endif /* !defined(nsStringFwd_h___) */
--- a/xpcom/string/nsTString.h
+++ b/xpcom/string/nsTString.h
@@ -457,16 +457,25 @@ protected:
   // allow subclasses to initialize fields directly
   nsTString_CharT(char_type* aData, size_type aLength, DataFlags aDataFlags,
                   ClassFlags aClassFlags)
     : substring_type(aData, aLength, aDataFlags,
                      aClassFlags | ClassFlags::NULL_TERMINATED)
   {
   }
 
+  friend const nsTString_CharT& TNullString_CharT();
+
+  // Used by Null[C]String.
+  explicit nsTString_CharT(DataFlags aDataFlags)
+    : substring_type(char_traits::sEmptyBuffer, 0,
+                     aDataFlags | DataFlags::TERMINATED,
+                     ClassFlags::NULL_TERMINATED)
+  {}
+
   struct Segment {
     uint32_t mBegin, mLength;
     Segment(uint32_t aBegin, uint32_t aLength)
       : mBegin(aBegin)
       , mLength(aLength)
     {}
   };
 };
@@ -692,18 +701,18 @@ public:
  * nsTXPIDLString extends nsTString such that:
  *
  *   (1) mData can be null
  *   (2) objects of this type can be automatically cast to |const CharT*|
  *   (3) getter_Copies method is supported to adopt data allocated with
  *       moz_xmalloc, such as "out string" parameters in XPIDL.
  *
  * NAMES:
- *   nsXPIDLString for wide characters
- *   nsXPIDLCString for narrow characters
+ *   nsXPIDLString for wide characters (no longer available)
+ *   nsXPIDLCString for narrow characters (deprecated)
  */
 class nsTXPIDLString_CharT : public nsTString_CharT
 {
 public:
 
   typedef nsTXPIDLString_CharT self_type;
 
 public:
--- a/xpcom/string/string-template-def-char.h
+++ b/xpcom/string/string-template-def-char.h
@@ -20,8 +20,9 @@
 #define nsTDependentString_CharT            nsDependentCString
 #define nsTDependentSubstring_CharT         nsDependentCSubstring
 #define nsTLiteralString_CharT              nsLiteralCString
 #define nsTXPIDLString_CharT                nsXPIDLCString
 #define nsTGetterCopies_CharT               nsCGetterCopies
 #define nsTPromiseFlatString_CharT          nsPromiseFlatCString
 #define TPromiseFlatString_CharT            PromiseFlatCString
 #define nsTSubstringSplitter_CharT          nsCSubstringSplitter
+#define TNullString_CharT                   NullCString
--- a/xpcom/string/string-template-def-unichar.h
+++ b/xpcom/string/string-template-def-unichar.h
@@ -15,13 +15,13 @@
 #define nsTSubstring_CharT                  nsAString
 #define PrintfAppend_CharT                  PrintfAppend_nsAString
 #define nsTSubstringTuple_CharT             nsSubstringTuple
 #define nsTStringComparator_CharT           nsStringComparator
 #define nsTDefaultStringComparator_CharT    nsDefaultStringComparator
 #define nsTDependentString_CharT            nsDependentString
 #define nsTDependentSubstring_CharT         nsDependentSubstring
 #define nsTLiteralString_CharT              nsLiteralString
-#define nsTXPIDLString_CharT                nsXPIDLString
 #define nsTGetterCopies_CharT               nsGetterCopies
 #define nsTPromiseFlatString_CharT          nsPromiseFlatString
 #define TPromiseFlatString_CharT            PromiseFlatString
 #define nsTSubstringSplitter_CharT          nsSubstringSplitter
+#define TNullString_CharT                   NullString
--- a/xpcom/string/string-template-undef.h
+++ b/xpcom/string/string-template-undef.h
@@ -21,8 +21,9 @@
 #undef nsTDependentString_CharT
 #undef nsTDependentSubstring_CharT
 #undef nsTLiteralString_CharT
 #undef nsTXPIDLString_CharT
 #undef nsTGetterCopies_CharT
 #undef nsTPromiseFlatString_CharT
 #undef TPromiseFlatString_CharT
 #undef nsTSubstringSplitter_CharT
+#undef TNullString_CharT
deleted file mode 100644
--- a/xpcom/tests/gtest/TestXPIDLString.cpp
+++ /dev/null
@@ -1,24 +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 "nsString.h"
-#include "nsReadableUtils.h"
-#include "nsXPIDLString.h"
-#include "gtest/gtest.h"
-
-static void
-nsXPIDLStringTest_Value(char16_t** aResult)
-{
-  *aResult = ToNewUnicode(NS_LITERAL_STRING("Hello, World"));
-}
-
-TEST(XPIDLString, Main)
-{
-  nsXPIDLString s1;
-  nsXPIDLStringTest_Value(getter_Copies(s1));
-  EXPECT_TRUE(s1.EqualsLiteral("Hello, World"));
-}
-
--- a/xpcom/tests/gtest/moz.build
+++ b/xpcom/tests/gtest/moz.build
@@ -50,17 +50,16 @@ UNIFIED_SOURCES += [
     'TestThreadPool.cpp',
     'TestThreadPoolListener.cpp',
     'TestThreads.cpp',
     'TestThreadUtils.cpp',
     'TestTimers.cpp',
     'TestTimeStamp.cpp',
     'TestTokenizer.cpp',
     'TestUTF.cpp',
-    'TestXPIDLString.cpp',
 ]
 
 if CONFIG['MOZ_DEBUG'] and CONFIG['OS_ARCH'] not in ('WINNT') and CONFIG['OS_TARGET'] != 'Android':
     # FIXME bug 523392: TestDeadlockDetector doesn't like Windows
     # Bug 1054249: Doesn't work on Android
     UNIFIED_SOURCES += [
         'TestDeadlockDetector.cpp',
         'TestDeadlockDetectorScalability.cpp',
--- a/xpcom/threads/HangAnnotations.cpp
+++ b/xpcom/threads/HangAnnotations.cpp
@@ -6,173 +6,62 @@
 
 #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;
 
-class BrowserHangAnnotations : public HangAnnotations
+void
+HangAnnotations::AddAnnotation(const nsAString& aName, const int32_t aData)
 {
-public:
-  BrowserHangAnnotations();
-  ~BrowserHangAnnotations();
-
-  void AddAnnotation(const nsAString& aName, const int32_t aData) override;
-  void AddAnnotation(const nsAString& aName, const double aData) override;
-  void AddAnnotation(const nsAString& aName, const nsAString& aData) override;
-  void AddAnnotation(const nsAString& aName, const nsACString& aData) override;
-  void AddAnnotation(const nsAString& aName, const bool aData) override;
-
-  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
-  bool IsEmpty() const override;
-  UniquePtr<Enumerator> GetEnumerator() override;
-
-  typedef std::pair<nsString, nsString> AnnotationType;
-  typedef std::vector<AnnotationType> VectorType;
-  typedef VectorType::const_iterator IteratorType;
-
-private:
-  VectorType  mAnnotations;
-};
-
-BrowserHangAnnotations::BrowserHangAnnotations()
-{
-  MOZ_COUNT_CTOR(BrowserHangAnnotations);
-}
-
-BrowserHangAnnotations::~BrowserHangAnnotations()
-{
-  MOZ_COUNT_DTOR(BrowserHangAnnotations);
+  nsAutoString dataString;
+  dataString.AppendInt(aData);
+  AppendElement(Annotation(aName, dataString));
 }
 
 void
-BrowserHangAnnotations::AddAnnotation(const nsAString& aName, const int32_t aData)
+HangAnnotations::AddAnnotation(const nsAString& aName, const double aData)
 {
-  nsString dataString;
-  dataString.AppendInt(aData);
-  AnnotationType annotation = std::make_pair(nsString(aName), dataString);
-  mAnnotations.push_back(annotation);
-}
-
-void
-BrowserHangAnnotations::AddAnnotation(const nsAString& aName, const double aData)
-{
-  nsString dataString;
+  nsAutoString dataString;
   dataString.AppendFloat(aData);
-  AnnotationType annotation = std::make_pair(nsString(aName), dataString);
-  mAnnotations.push_back(annotation);
-}
-
-void
-BrowserHangAnnotations::AddAnnotation(const nsAString& aName, const nsAString& aData)
-{
-  AnnotationType annotation = std::make_pair(nsString(aName), nsString(aData));
-  mAnnotations.push_back(annotation);
-}
-
-void
-BrowserHangAnnotations::AddAnnotation(const nsAString& aName, const nsACString& aData)
-{
-  nsString dataString;
-  AppendUTF8toUTF16(aData, dataString);
-  AnnotationType annotation = std::make_pair(nsString(aName), dataString);
-  mAnnotations.push_back(annotation);
+  AppendElement(Annotation(aName, dataString));
 }
 
 void
-BrowserHangAnnotations::AddAnnotation(const nsAString& aName, const bool aData)
-{
-  nsString dataString;
-  dataString += aData ? NS_LITERAL_STRING("true") : NS_LITERAL_STRING("false");
-  AnnotationType annotation = std::make_pair(nsString(aName), dataString);
-  mAnnotations.push_back(annotation);
-}
-
-/**
- * This class itself does not use synchronization but it (and its parent object)
- * should be protected by mutual exclusion in some way. In Telemetry the chrome
- * hang data is protected via TelemetryImpl::mHangReportsMutex.
- */
-class ChromeHangAnnotationEnumerator : public HangAnnotations::Enumerator
+HangAnnotations::AddAnnotation(const nsAString& aName, const nsAString& aData)
 {
-public:
-  explicit ChromeHangAnnotationEnumerator(const BrowserHangAnnotations::VectorType& aAnnotations);
-  ~ChromeHangAnnotationEnumerator();
-
-  virtual bool Next(nsAString& aOutName, nsAString& aOutValue);
-
-private:
-  BrowserHangAnnotations::IteratorType mIterator;
-  BrowserHangAnnotations::IteratorType mEnd;
-};
-
-ChromeHangAnnotationEnumerator::ChromeHangAnnotationEnumerator(
-                          const BrowserHangAnnotations::VectorType& aAnnotations)
-  : mIterator(aAnnotations.begin())
-  , mEnd(aAnnotations.end())
-{
-  MOZ_COUNT_CTOR(ChromeHangAnnotationEnumerator);
-}
-
-ChromeHangAnnotationEnumerator::~ChromeHangAnnotationEnumerator()
-{
-  MOZ_COUNT_DTOR(ChromeHangAnnotationEnumerator);
+  AppendElement(Annotation(aName, aData));
 }
 
-bool
-ChromeHangAnnotationEnumerator::Next(nsAString& aOutName, nsAString& aOutValue)
+void
+HangAnnotations::AddAnnotation(const nsAString& aName, const nsACString& aData)
 {
-  aOutName.Truncate();
-  aOutValue.Truncate();
-  if (mIterator == mEnd) {
-    return false;
-  }
-  aOutName = mIterator->first;
-  aOutValue = mIterator->second;
-  ++mIterator;
-  return true;
-}
-
-bool
-BrowserHangAnnotations::IsEmpty() const
-{
-  return mAnnotations.empty();
+  NS_ConvertUTF8toUTF16 dataString(aData);
+  AppendElement(Annotation(aName, dataString));
 }
 
-size_t
-BrowserHangAnnotations::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+void
+HangAnnotations::AddAnnotation(const nsAString& aName, const bool aData)
 {
-  size_t result = sizeof(mAnnotations) +
-                  mAnnotations.capacity() * sizeof(AnnotationType);
-  for (IteratorType i = mAnnotations.begin(), e = mAnnotations.end(); i != e;
-       ++i) {
-    result += i->first.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
-    result += i->second.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+  if (aData) {
+    AppendElement(Annotation(aName, NS_LITERAL_STRING("true")));
+  } else {
+    AppendElement(Annotation(aName, NS_LITERAL_STRING("false")));
   }
-
-  return result;
-}
-
-UniquePtr<HangAnnotations::Enumerator>
-BrowserHangAnnotations::GetEnumerator()
-{
-  if (mAnnotations.empty()) {
-    return nullptr;
-  }
-  return MakeUnique<ChromeHangAnnotationEnumerator>(mAnnotations);
 }
 
 namespace Observer {
 
 Annotators::Annotators()
   : mMutex("HangMonitor::Annotators::mMutex")
 {
   MOZ_COUNT_CTOR(Annotators);
@@ -197,32 +86,29 @@ Annotators::Unregister(Annotator& aAnnot
 {
   MutexAutoLock lock(mMutex);
   DebugOnly<std::set<Annotator*>::size_type> numErased;
   numErased = mAnnotators.erase(&aAnnotator);
   MOZ_ASSERT(numErased == 1);
   return mAnnotators.empty();
 }
 
-UniquePtr<HangAnnotations>
+HangAnnotations
 Annotators::GatherAnnotations()
 {
-  auto annotations = MakeUnique<BrowserHangAnnotations>();
+  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);
+      (*i)->AnnotateHang(annotations);
     }
   }
-  if (annotations->IsEmpty()) {
-    return nullptr;
-  }
-  return Move(annotations);
+  return annotations;
 }
 
 } // namespace Observer
 
 void
 RegisterAnnotator(Annotator& aAnnotator)
 {
   BackgroundHangMonitor::RegisterAnnotator(aAnnotator);
@@ -244,19 +130,46 @@ UnregisterAnnotator(Annotator& aAnnotato
   if (NS_IsMainThread() &&
       GeckoProcessType_Default == XRE_GetProcessType()) {
     if (gChromehangAnnotators->Unregister(aAnnotator)) {
       gChromehangAnnotators = nullptr;
     }
   }
 }
 
-UniquePtr<HangAnnotations>
+HangAnnotations
 ChromeHangAnnotatorCallout()
 {
   if (!gChromehangAnnotators) {
-    return nullptr;
+    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
--- a/xpcom/threads/HangAnnotations.h
+++ b/xpcom/threads/HangAnnotations.h
@@ -4,54 +4,54 @@
  * 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/UniquePtr.h"
 #include "mozilla/Vector.h"
 #include "nsString.h"
+#include "nsTArray.h"
 
 namespace mozilla {
 namespace HangMonitor {
 
 /**
- * This class declares an abstraction for a data type that encapsulates all
- * of the annotations being reported by a registered hang Annotator.
+ * This type represents an individual hang annotation.
  */
-class HangAnnotations
+class Annotation
 {
 public:
-  virtual ~HangAnnotations() {}
-
-  virtual void AddAnnotation(const nsAString& aName, const int32_t aData) = 0;
-  virtual void AddAnnotation(const nsAString& aName, const double aData) = 0;
-  virtual void AddAnnotation(const nsAString& aName, const nsAString& aData) = 0;
-  virtual void AddAnnotation(const nsAString& aName, const nsACString& aData) = 0;
-  virtual void AddAnnotation(const nsAString& aName, const bool aData) = 0;
+  Annotation() {}
+  Annotation(const nsAString& aName, const nsAString& aValue)
+    : mName(aName), mValue(aValue)
+  {}
 
-  class Enumerator
-  {
-  public:
-    virtual ~Enumerator() {}
-    virtual bool Next(nsAString& aOutName, nsAString& aOutValue) = 0;
-  };
-
-  virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
-  virtual bool IsEmpty() const = 0;
-  virtual UniquePtr<Enumerator> GetEnumerator() = 0;
+  nsString mName;
+  nsString mValue;
 };
 
-typedef UniquePtr<HangAnnotations> HangAnnotationsPtr;
-typedef Vector<HangAnnotationsPtr> HangAnnotationsVector;
+/**
+ * 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.
    */
@@ -69,36 +69,59 @@ void RegisterAnnotator(Annotator& aAnnot
  * 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 UniquePtr to HangAnnotations object or nullptr if none.
+ * @return HangAnnotations object.
  */
-HangAnnotationsPtr ChromeHangAnnotatorCallout();
+HangAnnotations ChromeHangAnnotatorCallout();
 
 namespace Observer {
 
 class Annotators
 {
 public:
   Annotators();
   ~Annotators();
 
   bool Register(Annotator& aAnnotator);
   bool Unregister(Annotator& aAnnotator);
 
-  HangAnnotationsPtr GatherAnnotations();
+  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
--- a/xpcom/threads/HangMonitor.cpp
+++ b/xpcom/threads/HangMonitor.cpp
@@ -206,17 +206,17 @@ ThreadMain(void*)
   // 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;
-  UniquePtr<HangAnnotations> annotations;
+  HangAnnotations annotations;
 #endif
 
   while (true) {
     if (gShutdown) {
       return; // Exit the thread
     }
 
     // avoid rereading the volatile value in this loop
--- a/xpcom/threads/moz.build
+++ b/xpcom/threads/moz.build
@@ -2,17 +2,16 @@
 # vim: set filetype=python:
 # 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/.
 
 XPIDL_SOURCES += [
     'nsIEnvironment.idl',
     'nsIEventTarget.idl',
-    'nsIHangDetails.idl',
     'nsIIdlePeriod.idl',
     'nsINamed.idl',
     'nsIProcess.idl',
     'nsIRunnable.idl',
     'nsISerialEventTarget.idl',
     'nsISupportsPriority.idl',
     'nsIThread.idl',
     'nsIThreadInternal.idl',
@@ -32,17 +31,16 @@ EXPORTS += [
     'nsProcess.h',
     'nsProxyRelease.h',
     'nsThread.h',
     'nsThreadUtils.h',
 ]
 
 EXPORTS.mozilla += [
     'AbstractThread.h',
-    'BackgroundHangMonitor.h',
     'BlockingResourceBase.h',
     'CondVar.h',
     'DeadlockDetector.h',
     'HangAnnotations.h',
     'HangMonitor.h',
     'IdleTaskRunner.h',
     'LazyIdleThread.h',
     'MainThreadIdlePeriod.h',
@@ -65,17 +63,16 @@ EXPORTS.mozilla += [
 ]
 
 SOURCES += [
     'IdleTaskRunner.cpp',
 ]
 
 UNIFIED_SOURCES += [
     'AbstractThread.cpp',
-    'BackgroundHangMonitor.cpp',
     'BlockingResourceBase.cpp',
     'HangAnnotations.cpp',
     'HangMonitor.cpp',
     'InputEventStatistics.cpp',
     'LazyIdleThread.cpp',
     'MainThreadIdlePeriod.cpp',
     'nsEnvironment.cpp',
     'nsEventQueue.cpp',
@@ -88,31 +85,21 @@ UNIFIED_SOURCES += [
     'nsThreadUtils.cpp',
     'nsTimerImpl.cpp',
     'RecursiveMutex.cpp',
     'RWLock.cpp',
     'SchedulerGroup.cpp',
     'SharedThreadPool.cpp',
     'SystemGroup.cpp',
     'TaskQueue.cpp',
-    'ThreadStackHelper.cpp',
     'ThrottledEventQueue.cpp',
     'TimerThread.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '../build',
     '/caps',
     '/tools/profiler',
 ]
 
-# BHR disabled in Beta because of bug 1389252.
-# BHR disabled for Release builds because of bug 965392.
-# BHR disabled for debug builds because of bug 979069.
-# BHR disabled for TSan builds because of bug 1121216.
-if CONFIG['NIGHTLY_BUILD'] and \
-   not CONFIG['MOZ_DEBUG'] and \
-   not CONFIG['MOZ_TSAN']:
-    DEFINES['MOZ_ENABLE_BACKGROUND_HANG_MONITOR'] = 1
-
 FINAL_LIBRARY = 'xul'
 
 include('/ipc/chromium/chromium-config.mozbuild')