Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 23 Feb 2016 16:56:58 -0800
changeset 321530 5b2baa5e9356644a7ed0b73e422eaff62e159ffb
parent 321484 7a2db11cc81934507f730ae24429a41be6202438 (current diff)
parent 321529 27126257aeddfe02274e52eb7c5d1c343ab596a4 (diff)
child 321598 d50ab673e9a16a34cee1124779d722225e9abbe7
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone47.0a1
first release with
nightly linux32
5b2baa5e9356 / 47.0a1 / 20160224030246 / files
nightly linux64
5b2baa5e9356 / 47.0a1 / 20160224030246 / files
nightly mac
5b2baa5e9356 / 47.0a1 / 20160224030246 / files
nightly win32
5b2baa5e9356 / 47.0a1 / 20160224030246 / files
nightly win64
5b2baa5e9356 / 47.0a1 / 20160224030246 / 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 inbound to central, a=merge
testing/taskcluster/tasks/b2g_e2e_tests_base_definition.yml
testing/taskcluster/tasks/tests/b2g_e2e_api_tests.yml
testing/taskcluster/tasks/tests/b2g_e2e_dsds_tests.yml
testing/taskcluster/tasks/tests/b2g_e2e_tests.yml
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -249,18 +249,36 @@ def bootstrap(topsrcdir, mozilla_dir=Non
         outgoing_dir = os.path.join(telemetry_dir, 'outgoing')
         try:
             os.mkdir(outgoing_dir)
         except OSError as e:
             if e.errno != errno.EEXIST:
                 raise
 
         # Add common metadata to help submit sorted data later on.
-        # For now, we'll just record the mach command that was invoked.
         data['argv'] = sys.argv
+        data.setdefault('system', {}).update(dict(
+            architecture=list(platform.architecture()),
+            machine=platform.machine(),
+            python_version=platform.python_version(),
+            release=platform.release(),
+            system=platform.system(),
+            version=platform.version(),
+        ))
+
+        if platform.system() == 'Linux':
+            dist = list(platform.linux_distribution())
+            data['system']['linux_distribution'] = dist
+        elif platform.system() == 'Windows':
+            win32_ver=list((platform.win32_ver())),
+            data['system']['win32_ver'] = win32_ver
+        elif platform.system() == 'Darwin':
+            # mac version is a special Cupertino snowflake
+            r, v, m = platform.mac_ver()
+            data['system']['mac_ver'] = [r, list(v), m]
 
         with open(os.path.join(outgoing_dir, str(uuid.uuid4()) + '.json'),
                   'w') as f:
             json.dump(data, f, sort_keys=True)
 
     def should_skip_dispatch(context, handler):
         # The user is performing a maintenance command.
         if handler.name in ('bootstrap', 'doctor', 'mach-commands', 'mercurial-setup'):
new file mode 100644
--- /dev/null
+++ b/build/telemetry-schema.json
@@ -0,0 +1,24 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "type": "object",
+  "properties": {
+    "argv": {"type": "array"},
+    "system": {
+      "type": "object",
+      "properties": {
+        "architecture": {"type": "array"},
+        "linux_distribution": {"type": "array"},
+        "mac_ver": {"type": "array"},
+        "machine": {"type": "string"},
+        "python_version": {"type": "string"},
+        "release": {"type": "string"},
+        "system": {"type": "string"},
+        "version": {"type": "string"},
+        "win_ver": {"type": "array"}
+      },
+      "required": ["architecture", "machine", "python_version",
+                   "release", "system", "version"]
+    }
+  },
+  "required": ["argv", "system"]
+}
--- a/configure.in
+++ b/configure.in
@@ -3846,28 +3846,16 @@ AC_SUBST(MOZ_INSTALL_TRACKING_ADJUST_SDK
 # Allow specifying a GCM sender ID key file that contains the sender ID used for
 # GCM requests.  Note that GCM sender IDs are not sensitive: see, for example,
 # http://stackoverflow.com/a/18216063.
 MOZ_ARG_WITH_STRING(gcm-senderid-keyfile,
 [  --with-gcm-senderid-keyfile=file GCM sender ID for GCM requests],
   MOZ_ANDROID_GCM_SENDERID=`cat $withval`)
 AC_SUBST(MOZ_ANDROID_GCM_SENDERID)
 
-# Whether to include optional-but-large font files in the final APK.
-# We want this in mobile/android/confvars.sh, so it goes early.
-MOZ_ARG_DISABLE_BOOL(android-include-fonts,
-[  --disable-android-include-fonts
-                          Disable the inclusion of fonts into the final APK],
-    MOZ_ANDROID_EXCLUDE_FONTS=1)
-
-if test -n "$MOZ_ANDROID_EXCLUDE_FONTS"; then
-    AC_DEFINE(MOZ_ANDROID_EXCLUDE_FONTS)
-fi
-AC_SUBST(MOZ_ANDROID_EXCLUDE_FONTS)
-
 # Whether this APK is destined for resource constrained devices.
 # We want this in mobile/android/confvars.sh, so it goes early.
 MOZ_ARG_ENABLE_BOOL(android-resource-constrained,
 [  --enable-android-resource-constrained
                           Exclude hi-res images and similar from the final APK],
     MOZ_ANDROID_RESOURCE_CONSTRAINED=1)
 
 if test -n "$MOZ_ANDROID_RESOURCE_CONSTRAINED"; then
@@ -4771,16 +4759,23 @@ fi
 dnl ========================================================
 dnl = Include Mozilla Location Service Stumbler on Android
 dnl ========================================================
 if test -n "$MOZ_ANDROID_MLS_STUMBLER"; then
     AC_DEFINE(MOZ_ANDROID_MLS_STUMBLER)
 fi
 
 dnl =========================================================
+dnl = Whether to exclude font files in the build
+dnl =========================================================
+if test -n "$MOZ_ANDROID_EXCLUDE_FONTS"; then
+    AC_DEFINE(MOZ_ANDROID_EXCLUDE_FONTS)
+fi
+
+dnl =========================================================
 dnl = Whether to exclude hyphenations files in the build
 dnl =========================================================
 if test -n "$MOZ_EXCLUDE_HYPHENATION_DICTIONARIES"; then
     AC_DEFINE(MOZ_EXCLUDE_HYPHENATION_DICTIONARIES)
 fi
 
 dnl =========================================================
 dnl = Background service for downloading additional content at runtime.
@@ -8452,16 +8447,17 @@ AC_SUBST(MOZ_ANDROID_GECKOLIBS_AAR)
 AC_SUBST(MOZ_ANDROID_SEARCH_ACTIVITY)
 AC_SUBST(MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER)
 AC_SUBST(MOZ_ANDROID_MLS_STUMBLER)
 AC_SUBST(MOZ_ANDROID_DOWNLOADS_INTEGRATION)
 AC_SUBST(MOZ_ANDROID_APPLICATION_CLASS)
 AC_SUBST(MOZ_ANDROID_BROWSER_INTENT_CLASS)
 AC_SUBST(MOZ_ANDROID_SEARCH_INTENT_CLASS)
 AC_SUBST(MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE)
+AC_SUBST(MOZ_ANDROID_EXCLUDE_FONTS)
 AC_SUBST(MOZ_EXCLUDE_HYPHENATION_DICTIONARIES)
 AC_SUBST(MOZ_INSTALL_TRACKING)
 AC_SUBST(MOZ_SWITCHBOARD)
 AC_SUBST(ENABLE_STRIP)
 AC_SUBST(PKG_SKIP_STRIP)
 AC_SUBST(STRIP_FLAGS)
 AC_SUBST(USE_ELF_HACK)
 AC_SUBST(INCREMENTAL_LINKER)
--- a/dom/animation/test/crashtests/crashtests.list
+++ b/dom/animation/test/crashtests/crashtests.list
@@ -1,8 +1,8 @@
-load 1239889-1.html
-load 1244595-1.html
-load 1216842-1.html
-load 1216842-2.html
-load 1216842-3.html
-load 1216842-4.html
-load 1216842-5.html
-load 1216842-6.html
+pref(dom.animations-api.core.enabled,true) load 1239889-1.html
+pref(dom.animations-api.core.enabled,true) load 1244595-1.html
+pref(dom.animations-api.core.enabled,true) load 1216842-1.html
+pref(dom.animations-api.core.enabled,true) load 1216842-2.html
+pref(dom.animations-api.core.enabled,true) load 1216842-3.html
+pref(dom.animations-api.core.enabled,true) load 1216842-4.html
+pref(dom.animations-api.core.enabled,true) load 1216842-5.html
+pref(dom.animations-api.core.enabled,true) load 1216842-6.html
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -481,17 +481,17 @@ skip-if = buildapp == 'mulet' || buildap
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #time out on b2g desktop specific
 [test_iframe_sandbox_navigation2.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #time out on b2g desktop specific
 [test_iframe_sandbox_plugins.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # b2g(plugins not supported) b2g-debug(plugins not supported) b2g-desktop(plugins not supported)
 [test_iframe_sandbox_popups.html]
 skip-if = buildapp == 'b2g' # b2g(multiple concurrent window.open()s fail on B2G) b2g-debug(multiple concurrent window.open()s fail on B2G) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
 [test_iframe_sandbox_popups_inheritance.html]
-skip-if = buildapp == 'b2g' || e10s || toolkit == 'android' # b2g(multiple concurrent window.open()s fail on B2G) b2g-debug(multiple concurrent window.open()s fail on B2G) b2g-desktop(Bug 931116, b2g desktop specific, initial triage) android(bug 939642)
+skip-if = buildapp == 'b2g' || toolkit == 'android' # b2g(multiple concurrent window.open()s fail on B2G) b2g-debug(multiple concurrent window.open()s fail on B2G) b2g-desktop(Bug 931116, b2g desktop specific, initial triage) android(bug 939642)
 [test_iframe_sandbox_redirect.html]
 [test_iframe_sandbox_refresh.html]
 [test_iframe_sandbox_same_origin.html]
 [test_iframe_sandbox_workers.html]
 [test_img_attributes_reflection.html]
 [test_imageSrcSet.html]
 [test_imports_basics.html]
 [test_imports_redirect.html]
@@ -577,17 +577,17 @@ skip-if = toolkit == 'android'
 [test_bug486741.html]
 [test_bug489532.html]
 [test_bug497242.xhtml]
 [test_bug499092.html]
 [test_bug512367.html]
 [test_bug677495.html]
 [test_bug677495-1.html]
 [test_bug741266.html]
-skip-if = buildapp == "mulet" || buildapp == "b2g" || toolkit == "android" || toolkit == "windows" || e10s # b2g(needs control of popup window size) b2g-debug(needs control of popup window size) b2g-desktop(needs control of popup window size) windows(bug 1234520)
+skip-if = buildapp == "mulet" || buildapp == "b2g" || toolkit == "android" || toolkit == "windows" # b2g(needs control of popup window size) b2g-debug(needs control of popup window size) b2g-desktop(needs control of popup window size) windows(bug 1234520)
 [test_non-ascii-cookie.html]
 skip-if = buildapp == 'b2g'
 support-files = file_cookiemanager.js
 [test_bug765780.html]
 [test_bug871161.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 support-files = file_bug871161-1.html file_bug871161-2.html
 [test_bug1013316.html]
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -2047,16 +2047,27 @@ MediaManager::GetUserMedia(nsPIDOMWindow
     if ((!IsOn(c.mAudio) || audioPerm == nsIPermissionManager::DENY_ACTION) &&
         (!IsOn(c.mVideo) || videoPerm == nsIPermissionManager::DENY_ACTION)) {
       RefPtr<MediaStreamError> error =
           new MediaStreamError(aWindow, NS_LITERAL_STRING("SecurityError"));
       onFailure->OnError(error);
       RemoveFromWindowList(windowID, listener);
       return NS_OK;
     }
+  } else if (loop) {
+    // Record that we gave Loop permission to use camera access.
+    nsCOMPtr<nsIPermissionManager> permManager =
+      do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = permManager->Add(docURI, "camera",
+                          nsIPermissionManager::ALLOW_ACTION,
+                          nsIPermissionManager::EXPIRE_SESSION,
+                          0);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
 #if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   if (mCameraManager == nullptr) {
     mCameraManager = nsDOMCameraManager::CreateInstance(aWindow);
   }
 #endif
 
new file mode 100644
--- /dev/null
+++ b/dom/presentation/tests/mochitest/file_presentation_non_receiver_inner_iframe_oop.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for B2G PresentationReceiver on a non-receiver inner iframe of the receiver page at receiver side (OOP)</title>
+</head>
+<body onload="testConnectionAvailable()">
+<div id="content"></div>
+<script type="application/javascript;version=1.7">
+
+"use strict";
+
+function ok(a, msg) {
+  alert((a ? 'OK ' : 'KO ') + msg);
+}
+
+function testConnectionAvailable() {
+  return new Promise(function(aResolve, aReject) {
+    ok(!navigator.presentation, "navigator.presentation shouldn't be available in inner iframes with different origins from receiving OOP pages.");
+    aResolve();
+  });
+}
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/presentation/tests/mochitest/file_presentation_receiver_inner_iframe_oop.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for B2G PresentationReceiver in an inner iframe of the receiver page at receiver side (OOP)</title>
+</head>
+<body onload="testConnectionAvailable()">
+<div id="content"></div>
+<script type="application/javascript;version=1.7">
+
+"use strict";
+
+function ok(a, msg) {
+  alert((a ? 'OK ' : 'KO ') + msg);
+}
+
+function testConnectionAvailable() {
+  return new Promise(function(aResolve, aReject) {
+    ok(navigator.presentation, "navigator.presentation should be available in same-origin inner iframes of receiving OOP pages.");
+    aResolve();
+  });
+}
+
+</script>
+</body>
+</html>
--- a/dom/presentation/tests/mochitest/file_presentation_receiver_oop.html
+++ b/dom/presentation/tests/mochitest/file_presentation_receiver_oop.html
@@ -49,26 +49,36 @@ function testConnectionAvailable() {
         ok(false, "Error occurred when getting the connection: " + aError);
         finish();
         aReject();
       }
     );
   });
 }
 
-function testConnectionAvailableInnerIframe() {
+function testConnectionAvailableSameOriginInnerIframe() {
   return new Promise(function(aResolve, aReject) {
     var iframe = document.createElement('iframe');
     iframe.setAttribute('src', './file_presentation_receiver_inner_iframe_oop.html');
     document.body.appendChild(iframe);
 
     aResolve();
   });
 }
 
+function testConnectionUnavailableDiffOriginInnerIframe() {
+  return new Promise(function(aResolve, aReject) {
+    var iframe = document.createElement('iframe');
+    iframe.setAttribute('src', 'http://example.com/tests/dom/presentation/tests/mochitest/file_presentation_non_receiver_inner_iframe_oop.html');
+    document.body.appendChild(iframe);
+
+    aResolve();
+  });
+}
+
 function testConnectionReady() {
   return new Promise(function(aResolve, aReject) {
     connection.onstatechange = function() {
       connection.onstatechange = null;
       is(connection.state, "connected", "Connection state should become connected.");
       aResolve();
     };
 
@@ -99,17 +109,21 @@ function testTerminateConnection() {
       aResolve();
     };
 
     connection.terminate();
   });
 }
 
 testConnectionAvailable().
-then(testConnectionAvailableInnerIframe).
+// TODO Bug 1234128 - Fix the timing issue for navigator.presentation.receiver.
+// This test fails intermittently under some edge cases. Disable it for now until
+// the issue gets fixed.
+// then(testConnectionAvailableSameOriginInnerIframe).
+then(testConnectionUnavailableDiffOriginInnerIframe).
 then(testConnectionReady).
 then(testIncomingMessage).
 then(testTerminateConnection).
 then(finish);
 
 </script>
 </body>
 </html>
--- a/dom/presentation/tests/mochitest/mochitest.ini
+++ b/dom/presentation/tests/mochitest/mochitest.ini
@@ -1,16 +1,18 @@
 [DEFAULT]
 support-files =
   PresentationDeviceInfoChromeScript.js
   PresentationSessionChromeScript.js
   file_presentation_receiver.html
   file_presentation_receiver_oop.html
   file_presentation_non_receiver_oop.html
   file_presentation_receiver_establish_connection_error.html
+  file_presentation_receiver_inner_iframe_oop.html
+  file_presentation_non_receiver_inner_iframe_oop.html
 
 [test_presentation_device_info.html]
 [test_presentation_device_info_permission.html]
 [test_presentation_sender_disconnect.html]
 skip-if = toolkit == 'android' # Bug 1129785
 [test_presentation_sender_establish_connection_error.html]
 skip-if = toolkit == 'android' # Bug 1129785
 [test_presentation_sender.html]
--- a/dom/workers/ServiceWorkerRegistrar.cpp
+++ b/dom/workers/ServiceWorkerRegistrar.cpp
@@ -35,16 +35,17 @@ using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 static const char* gSupportedRegistrarVersions[] = {
   SERVICEWORKERREGISTRAR_VERSION,
+  "3",
   "2"
 };
 
 StaticRefPtr<ServiceWorkerRegistrar> gServiceWorkerRegistrar;
 
 } // namespace
 
 NS_IMPL_ISUPPORTS(ServiceWorkerRegistrar,
@@ -325,67 +326,96 @@ ServiceWorkerRegistrar::ReadData()
     nsContentUtils::LogMessageToConsole(nsPrintfCString(
       "Unsupported service worker registrar version: %s", version.get()).get());
     return NS_ERROR_FAILURE;
   }
 
   nsTArray<ServiceWorkerRegistrationData> tmpData;
 
   bool overwrite = false;
+  bool dedupe = false;
   while (hasMoreLines) {
     ServiceWorkerRegistrationData* entry = tmpData.AppendElement();
 
 #define GET_LINE(x)                                   \
     rv = lineInputStream->ReadLine(x, &hasMoreLines); \
     if (NS_WARN_IF(NS_FAILED(rv))) {                  \
       return rv;                                      \
     }                                                 \
     if (NS_WARN_IF(!hasMoreLines)) {                  \
       return NS_ERROR_FAILURE;                        \
     }
 
     nsAutoCString line;
+    nsAutoCString unused;
     if (version.EqualsLiteral(SERVICEWORKERREGISTRAR_VERSION)) {
       nsAutoCString suffix;
       GET_LINE(suffix);
 
       PrincipalOriginAttributes attrs;
       if (!attrs.PopulateFromSuffix(suffix)) {
         return NS_ERROR_INVALID_ARG;
       }
 
-      GET_LINE(line);
+      GET_LINE(entry->scope());
+
       entry->principal() =
-        mozilla::ipc::ContentPrincipalInfo(attrs, line);
+        mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
 
-      GET_LINE(entry->scope());
       GET_LINE(entry->currentWorkerURL());
 
       nsAutoCString cacheName;
       GET_LINE(cacheName);
       CopyUTF8toUTF16(cacheName, entry->cacheName());
-    } else if (version.EqualsLiteral("2")) {
+    } else if (version.EqualsLiteral("3")) {
       overwrite = true;
+      dedupe = true;
 
       nsAutoCString suffix;
       GET_LINE(suffix);
 
       PrincipalOriginAttributes attrs;
       if (!attrs.PopulateFromSuffix(suffix)) {
         return NS_ERROR_INVALID_ARG;
       }
 
-      GET_LINE(line);
-      entry->principal() =
-        mozilla::ipc::ContentPrincipalInfo(attrs, line);
+      // principal spec is no longer used; we use scope directly instead
+      GET_LINE(unused);
 
       GET_LINE(entry->scope());
 
+      entry->principal() =
+        mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
+
+      GET_LINE(entry->currentWorkerURL());
+
+      nsAutoCString cacheName;
+      GET_LINE(cacheName);
+      CopyUTF8toUTF16(cacheName, entry->cacheName());
+    } else if (version.EqualsLiteral("2")) {
+      overwrite = true;
+      dedupe = true;
+
+      nsAutoCString suffix;
+      GET_LINE(suffix);
+
+      PrincipalOriginAttributes attrs;
+      if (!attrs.PopulateFromSuffix(suffix)) {
+        return NS_ERROR_INVALID_ARG;
+      }
+
+      // principal spec is no longer used; we use scope directly instead
+      GET_LINE(unused);
+
+      GET_LINE(entry->scope());
+
+      entry->principal() =
+        mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
+
       // scriptSpec is no more used in latest version.
-      nsAutoCString unused;
       GET_LINE(unused);
 
       GET_LINE(entry->currentWorkerURL());
 
       nsAutoCString cacheName;
       GET_LINE(cacheName);
       CopyUTF8toUTF16(cacheName, entry->cacheName());
 
@@ -404,32 +434,42 @@ ServiceWorkerRegistrar::ReadData()
 
     if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TERMINATOR)) {
       return NS_ERROR_FAILURE;
     }
   }
 
   stream->Close();
 
-  // Dedupe data in file.  Old profiles had many duplicates.  In theory
-  // we can remove this in the future. (Bug 1248449)
+  // Copy data over to mData.
   for (uint32_t i = 0; i < tmpData.Length(); ++i) {
     bool match = false;
-    for (uint32_t j = 0; j < mData.Length(); ++j) {
-      // Use same comparison as RegisterServiceWorker. Scope contains
-      // basic origin information.  Combine with any principal attributes.
-      if (Equivalent(tmpData[i], mData[j])) {
-        // Last match wins, just like legacy loading used to do in
-        // the ServiceWorkerManager.
-        mData[j] = tmpData[i];
-        // Dupe found, so overwrite file with reduced list.
-        overwrite = true;
-        match = true;
-        break;
+    if (dedupe) {
+      MOZ_ASSERT(overwrite);
+      // If this is an old profile, then we might need to deduplicate.  In
+      // theory this can be removed in the future (Bug 1248449)
+      for (uint32_t j = 0; j < mData.Length(); ++j) {
+        // Use same comparison as RegisterServiceWorker. Scope contains
+        // basic origin information.  Combine with any principal attributes.
+        if (Equivalent(tmpData[i], mData[j])) {
+          // Last match wins, just like legacy loading used to do in
+          // the ServiceWorkerManager.
+          mData[j] = tmpData[i];
+          // Dupe found, so overwrite file with reduced list.
+          match = true;
+          break;
+        }
       }
+    } else {
+#ifdef DEBUG
+      // Otherwise assert no duplications in debug builds.
+      for (uint32_t j = 0; j < mData.Length(); ++j) {
+        MOZ_ASSERT(!Equivalent(tmpData[i], mData[j]));
+      }
+#endif
     }
     if (!match) {
       mData.AppendElement(tmpData[i]);
     }
   }
 
   // Overwrite previous version.
   // Cannot call SaveData directly because gtest uses main-thread.
@@ -642,19 +682,16 @@ ServiceWorkerRegistrar::WriteData()
 
     nsAutoCString suffix;
     cInfo.attrs().CreateSuffix(suffix);
 
     buffer.Truncate();
     buffer.Append(suffix.get());
     buffer.Append('\n');
 
-    buffer.Append(cInfo.spec());
-    buffer.Append('\n');
-
     buffer.Append(data[i].scope());
     buffer.Append('\n');
 
     buffer.Append(data[i].currentWorkerURL());
     buffer.Append('\n');
 
     buffer.Append(NS_ConvertUTF16toUTF8(data[i].cacheName()));
     buffer.Append('\n');
--- a/dom/workers/ServiceWorkerRegistrar.h
+++ b/dom/workers/ServiceWorkerRegistrar.h
@@ -11,17 +11,17 @@
 #include "mozilla/Telemetry.h"
 #include "nsClassHashtable.h"
 #include "nsIObserver.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 #define SERVICEWORKERREGISTRAR_FILE "serviceworker.txt"
-#define SERVICEWORKERREGISTRAR_VERSION "3"
+#define SERVICEWORKERREGISTRAR_VERSION "4"
 #define SERVICEWORKERREGISTRAR_TERMINATOR "#"
 #define SERVICEWORKERREGISTRAR_TRUE "true"
 #define SERVICEWORKERREGISTRAR_FALSE "false"
 
 class nsIFile;
 
 namespace mozilla {
 
--- a/dom/workers/test/gtest/TestReadWrite.cpp
+++ b/dom/workers/test/gtest/TestReadWrite.cpp
@@ -137,21 +137,21 @@ TEST(ServiceWorkerRegistrar, TestWrongVe
   ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file";
 }
 
 TEST(ServiceWorkerRegistrar, TestReadData)
 {
   nsAutoCString buffer(SERVICEWORKERREGISTRAR_VERSION "\n");
 
   buffer.Append("^appId=123&inBrowser=1\n");
-  buffer.Append("spec 0\nscope 0\ncurrentWorkerURL 0\ncacheName 0\n");
+  buffer.Append("scope 0\ncurrentWorkerURL 0\ncacheName 0\n");
   buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
 
   buffer.Append("\n");
-  buffer.Append("spec 1\nscope 1\ncurrentWorkerURL 1\ncacheName 1\n");
+  buffer.Append("scope 1\ncurrentWorkerURL 1\ncacheName 1\n");
   buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
 
   ASSERT_TRUE(CreateFile(buffer)) << "CreateFile should not fail";
 
   RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
 
   nsresult rv = swr->TestReadData();
   ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
@@ -162,30 +162,30 @@ TEST(ServiceWorkerRegistrar, TestReadDat
   const mozilla::ipc::PrincipalInfo& info0 = data[0].principal();
   ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
   const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal();
 
   nsAutoCString suffix0;
   cInfo0.attrs().CreateSuffix(suffix0);
 
   ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get());
-  ASSERT_STREQ("spec 0", cInfo0.spec().get());
+  ASSERT_STREQ("scope 0", cInfo0.spec().get());
   ASSERT_STREQ("scope 0", data[0].scope().get());
   ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get());
   ASSERT_STREQ("cacheName 0", NS_ConvertUTF16toUTF8(data[0].cacheName()).get());
 
   const mozilla::ipc::PrincipalInfo& info1 = data[1].principal();
   ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
   const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal();
 
   nsAutoCString suffix1;
   cInfo1.attrs().CreateSuffix(suffix1);
 
   ASSERT_STREQ("", suffix1.get());
-  ASSERT_STREQ("spec 1", cInfo1.spec().get());
+  ASSERT_STREQ("scope 1", cInfo1.spec().get());
   ASSERT_STREQ("scope 1", data[1].scope().get());
   ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get());
   ASSERT_STREQ("cacheName 1", NS_ConvertUTF16toUTF8(data[1].cacheName()).get());
 }
 
 TEST(ServiceWorkerRegistrar, TestDeleteData)
 {
   ASSERT_TRUE(CreateFile(nsAutoCString("Foobar"))) << "CreateFile should not fail";
@@ -241,17 +241,17 @@ TEST(ServiceWorkerRegistrar, TestWriteDa
 
     mozilla::PrincipalOriginAttributes attrs(i, i % 2);
     nsAutoCString suffix, expectSuffix;
     attrs.CreateSuffix(expectSuffix);
     cInfo.attrs().CreateSuffix(suffix);
 
     ASSERT_STREQ(expectSuffix.get(), suffix.get());
 
-    test.AppendPrintf("spec write %d", i);
+    test.AppendPrintf("scope write %d", i);
     ASSERT_STREQ(test.get(), cInfo.spec().get());
 
     test.Truncate();
     test.AppendPrintf("scope write %d", i);
     ASSERT_STREQ(test.get(), data[i].scope().get());
 
     test.Truncate();
     test.AppendPrintf("currentWorkerURL write %d", i);
@@ -278,48 +278,97 @@ TEST(ServiceWorkerRegistrar, TestVersion
   ASSERT_TRUE(CreateFile(buffer)) << "CreateFile should not fail";
 
   RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
 
   nsresult rv = swr->TestReadData();
   ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
 
   const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
-  ASSERT_EQ((uint32_t)2, data.Length()) << "4 entries should be found";
+  ASSERT_EQ((uint32_t)2, data.Length()) << "2 entries should be found";
 
   const mozilla::ipc::PrincipalInfo& info0 = data[0].principal();
   ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
   const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal();
 
   nsAutoCString suffix0;
   cInfo0.attrs().CreateSuffix(suffix0);
 
   ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get());
-  ASSERT_STREQ("spec 0", cInfo0.spec().get());
+  ASSERT_STREQ("scope 0", cInfo0.spec().get());
   ASSERT_STREQ("scope 0", data[0].scope().get());
   ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get());
   ASSERT_STREQ("activeCache 0", NS_ConvertUTF16toUTF8(data[0].cacheName()).get());
 
   const mozilla::ipc::PrincipalInfo& info1 = data[1].principal();
   ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
   const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal();
 
   nsAutoCString suffix1;
   cInfo1.attrs().CreateSuffix(suffix1);
 
   ASSERT_STREQ("", suffix1.get());
-  ASSERT_STREQ("spec 1", cInfo1.spec().get());
+  ASSERT_STREQ("scope 1", cInfo1.spec().get());
   ASSERT_STREQ("scope 1", data[1].scope().get());
   ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get());
   ASSERT_STREQ("activeCache 1", NS_ConvertUTF16toUTF8(data[1].cacheName()).get());
 }
 
+TEST(ServiceWorkerRegistrar, TestVersion3Migration)
+{
+  nsAutoCString buffer("3" "\n");
+
+  buffer.Append("^appId=123&inBrowser=1\n");
+  buffer.Append("spec 0\nscope 0\ncurrentWorkerURL 0\ncacheName 0\n");
+  buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
+
+  buffer.Append("\n");
+  buffer.Append("spec 1\nscope 1\ncurrentWorkerURL 1\ncacheName 1\n");
+  buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
+
+  ASSERT_TRUE(CreateFile(buffer)) << "CreateFile should not fail";
+
+  RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  nsresult rv = swr->TestReadData();
+  ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
+
+  const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+  ASSERT_EQ((uint32_t)2, data.Length()) << "2 entries should be found";
+
+  const mozilla::ipc::PrincipalInfo& info0 = data[0].principal();
+  ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
+  const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal();
+
+  nsAutoCString suffix0;
+  cInfo0.attrs().CreateSuffix(suffix0);
+
+  ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get());
+  ASSERT_STREQ("scope 0", cInfo0.spec().get());
+  ASSERT_STREQ("scope 0", data[0].scope().get());
+  ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get());
+  ASSERT_STREQ("cacheName 0", NS_ConvertUTF16toUTF8(data[0].cacheName()).get());
+
+  const mozilla::ipc::PrincipalInfo& info1 = data[1].principal();
+  ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
+  const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal();
+
+  nsAutoCString suffix1;
+  cInfo1.attrs().CreateSuffix(suffix1);
+
+  ASSERT_STREQ("", suffix1.get());
+  ASSERT_STREQ("scope 1", cInfo1.spec().get());
+  ASSERT_STREQ("scope 1", data[1].scope().get());
+  ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get());
+  ASSERT_STREQ("cacheName 1", NS_ConvertUTF16toUTF8(data[1].cacheName()).get());
+}
+
 TEST(ServiceWorkerRegistrar, TestDedupe)
 {
-  nsAutoCString buffer(SERVICEWORKERREGISTRAR_VERSION "\n");
+  nsAutoCString buffer("3" "\n");
 
   // unique entries
   buffer.Append("^appId=123&inBrowser=1\n");
   buffer.Append("spec 0\nscope 0\ncurrentWorkerURL 0\ncacheName 0\n");
   buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
 
   buffer.Append("\n");
   buffer.Append("spec 1\nscope 1\ncurrentWorkerURL 1\ncacheName 1\n");
@@ -351,30 +400,30 @@ TEST(ServiceWorkerRegistrar, TestDedupe)
   const mozilla::ipc::PrincipalInfo& info0 = data[0].principal();
   ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
   const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal();
 
   nsAutoCString suffix0;
   cInfo0.attrs().CreateSuffix(suffix0);
 
   ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get());
-  ASSERT_STREQ("spec 2", cInfo0.spec().get());
+  ASSERT_STREQ("scope 0", cInfo0.spec().get());
   ASSERT_STREQ("scope 0", data[0].scope().get());
   ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get());
   ASSERT_STREQ("cacheName 0", NS_ConvertUTF16toUTF8(data[0].cacheName()).get());
 
   const mozilla::ipc::PrincipalInfo& info1 = data[1].principal();
   ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
   const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal();
 
   nsAutoCString suffix1;
   cInfo1.attrs().CreateSuffix(suffix1);
 
   ASSERT_STREQ("", suffix1.get());
-  ASSERT_STREQ("spec 3", cInfo1.spec().get());
+  ASSERT_STREQ("scope 1", cInfo1.spec().get());
   ASSERT_STREQ("scope 1", data[1].scope().get());
   ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get());
   ASSERT_STREQ("cacheName 1", NS_ConvertUTF16toUTF8(data[1].cacheName()).get());
 }
 
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
 
--- a/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
+++ b/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
@@ -165,8 +165,15 @@ function synthesizeNativeDrag(aElement, 
   for (var i = 1; i < steps; i++) {
     var dx = i * (aDeltaX / steps);
     var dy = i * (aDeltaY / steps);
     synthesizeNativeTouch(aElement, aX + dx, aY + dy, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId);
   }
   synthesizeNativeTouch(aElement, aX + aDeltaX, aY + aDeltaY, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId);
   return synthesizeNativeTouch(aElement, aX + aDeltaX, aY + aDeltaY, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, aObserver, aTouchId);
 }
+
+function synthesizeNativeTap(aElement, aX, aY, aObserver = null) {
+  var pt = coordinatesRelativeToWindow(aX, aY, aElement);
+  var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
+  utils.sendNativeTouchTap(pt.x, pt.y, false, aObserver);
+  return true;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_tap.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width; initial-scale=1.0">
+  <title>Sanity touch-tapping test</title>
+  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+  <script type="application/javascript" src="apz_test_utils.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+  <script type="application/javascript">
+
+function clickButton() {
+  if (!window.TouchEvent) {
+    window.opener.ok(true, "Touch events are not supported on this platform, sorry!\n");
+    window.opener.testDone();
+    return;
+  }
+
+  document.addEventListener('click', clicked, false);
+
+  synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
+    dump("Finished synthesizing tap, waiting for button to be clicked...\n");
+  });
+}
+
+function clicked(e) {
+  window.opener.is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
+  window.opener.testDone();
+}
+
+window.onload = function() {
+  waitForAllPaints(function() {
+    flushApzRepaints(clickButton);
+  });
+}
+
+  </script>
+</head>
+<body>
+ <button id="b" style="width: 10px; height: 10px"></button>
+</body>
+</html>
--- a/gfx/layers/apz/test/mochitest/mochitest.ini
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -5,16 +5,17 @@ support-files =
   helper_bug982141.html
   helper_bug1151663.html
   helper_iframe1.html
   helper_iframe2.html
   helper_subframe_style.css
   helper_basic_pan.html
   helper_div_pan.html
   helper_iframe_pan.html
+  helper_tap.html
 tags = apz
 [test_bug982141.html]
 skip-if = toolkit != 'gonk'  # bug 991198
 [test_bug1151663.html]
 skip-if = toolkit != 'gonk'  # bug 991198
 [test_wheel_scroll.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_wheel_transactions.html]
@@ -27,8 +28,12 @@ skip-if = (os == 'android') || (os == 'b
 skip-if = toolkit != 'gonk'
 [test_scroll_inactive_flattened_frame.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_scroll_inactive_bug1190112.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_scroll_subframe_scrollbar.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_frame_reconstruction.html]
+[test_tap.html]
+# Windows touch injection doesn't work in automation, but this test can be run locally on a windows touch device.
+# On OS X we don't support touch events at all.
+skip-if = (toolkit == 'windows') || (toolkit == 'cocoa')
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_tap.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Sanity panning test</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+// this page just serially loads each one of the following test helper pages in
+// a new window and waits for it to call testDone()
+var tests = [
+  'helper_tap.html',
+];
+
+var testIndex = -1;
+var w = null;
+
+function testDone() {
+  if (w) {
+    w.close();
+  }
+  testIndex++;
+  if (testIndex < tests.length) {
+    w = window.open(tests[testIndex], "_blank");
+  } else {
+    SimpleTest.finish();
+  }
+}
+
+window.onload = function() {
+  if (!SpecialPowers.getDOMWindowUtils(window).asyncPanZoomEnabled) {
+    ok(true, "APZ is not enabled, this test is not relevant, sorry!\n");
+    SimpleTest.finish();
+    return;
+  }
+  testDone();
+};
+
+  </script>
+</head>
+<body>
+</body>
+</html>
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -429,17 +429,17 @@ BasicCompositor::DrawQuad(const gfx::Rec
     EffectMask *effectMask = static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
     sourceMask = effectMask->mMaskTexture->AsSourceBasic()->GetSurface(dest);
     if (!sourceMask) {
       gfxWarning() << "Invalid sourceMask effect";
     }
     MOZ_ASSERT(effectMask->mMaskTransform.Is2D(), "How did we end up with a 3D transform here?!");
     MOZ_ASSERT(!effectMask->mIs3D);
     maskTransform = effectMask->mMaskTransform.As2D();
-    maskTransform.PreTranslate(-offset.x, -offset.y);
+    maskTransform.PostTranslate(-offset.x, -offset.y);
   }
 
   CompositionOp blendMode = CompositionOp::OP_OVER;
   if (Effect* effect = aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get()) {
     blendMode = static_cast<EffectBlendMode*>(effect)->mBlendMode;
   }
 
   switch (aEffectChain.mPrimaryEffect->mType) {
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -815,17 +815,18 @@ TextureClient::CreateForDrawing(Composit
 already_AddRefed<TextureClient>
 TextureClient::CreateForRawBufferAccess(ISurfaceAllocator* aAllocator,
                                         gfx::SurfaceFormat aFormat,
                                         gfx::IntSize aSize,
                                         gfx::BackendType aMoz2DBackend,
                                         TextureFlags aTextureFlags,
                                         TextureAllocationFlags aAllocFlags)
 {
-  MOZ_ASSERT(aAllocator->IPCOpen());
+  // also test the validity of aAllocator
+  MOZ_ASSERT(aAllocator && aAllocator->IPCOpen());
   if (!aAllocator || !aAllocator->IPCOpen()) {
     return nullptr;
   }
 
   if (aAllocFlags & ALLOC_DISALLOW_BUFFERTEXTURECLIENT) {
     return nullptr;
   }
 
--- a/js/public/UbiNodeShortestPaths.h
+++ b/js/public/UbiNodeShortestPaths.h
@@ -107,52 +107,57 @@ struct JS_PUBLIC_API(ShortestPaths)
         {
         }
 
         bool
         operator()(Traversal& traversal, JS::ubi::Node origin, JS::ubi::Edge& edge,
                    BackEdge* back, bool first)
         {
             MOZ_ASSERT(back);
-            MOZ_ASSERT(traversal.visited.has(origin));
+            MOZ_ASSERT(origin == shortestPaths.root_ || traversal.visited.has(origin));
             MOZ_ASSERT(totalPathsRecorded < totalMaxPathsToRecord);
 
             if (first && !back->init(origin, edge))
                 return false;
 
             if (!shortestPaths.targets_.has(edge.referent))
                 return true;
 
             // If `first` is true, then we moved the edge's name into `back` in
             // the above call to `init`. So clone that back edge to get the
             // correct edge name. If `first` is not true, then our edge name is
             // still in `edge`. This accounts for the asymmetry between
             // `back->clone()` in the first branch, and the `init` call in the
             // second branch.
 
-            auto ptr = shortestPaths.paths_.lookupForAdd(edge.referent);
             if (first) {
-                MOZ_ASSERT(!ptr);
                 BackEdgeVector paths;
                 if (!paths.reserve(shortestPaths.maxNumPaths_))
                     return false;
                 auto cloned = back->clone();
                 if (!cloned)
                     return false;
                 paths.infallibleAppend(mozilla::Move(cloned));
-                if (!shortestPaths.paths_.add(ptr, edge.referent, mozilla::Move(paths)))
+                if (!shortestPaths.paths_.putNew(edge.referent, mozilla::Move(paths)))
                     return false;
                 totalPathsRecorded++;
-            } else if (ptr->value().length() < shortestPaths.maxNumPaths_) {
-                MOZ_ASSERT(ptr);
-                BackEdge::Ptr thisBackEdge(js_new<BackEdge>());
-                if (!thisBackEdge || !thisBackEdge->init(origin, edge))
-                    return false;
-                ptr->value().infallibleAppend(mozilla::Move(thisBackEdge));
-                totalPathsRecorded++;
+            } else {
+                auto ptr = shortestPaths.paths_.lookup(edge.referent);
+                MOZ_ASSERT(ptr,
+                           "This isn't the first time we have seen the target node `edge.referent`. "
+                           "We should have inserted it into shortestPaths.paths_ the first time we "
+                           "saw it.");
+
+                if (ptr->value().length() < shortestPaths.maxNumPaths_) {
+                    BackEdge::Ptr thisBackEdge(js_new<BackEdge>());
+                    if (!thisBackEdge || !thisBackEdge->init(origin, edge))
+                        return false;
+                    ptr->value().infallibleAppend(mozilla::Move(thisBackEdge));
+                    totalPathsRecorded++;
+                }
             }
 
             MOZ_ASSERT(totalPathsRecorded <= totalMaxPathsToRecord);
             if (totalPathsRecorded == totalMaxPathsToRecord)
                 traversal.stop();
 
             return true;
         }
@@ -246,17 +251,17 @@ struct JS_PUBLIC_API(ShortestPaths)
         size_t count = targets.count();
         ShortestPaths paths(maxNumPaths, root, mozilla::Move(targets));
         if (!paths.paths_.init(count))
             return mozilla::Nothing();
 
         Handler handler(paths);
         Traversal traversal(rt, handler, noGC);
         traversal.wantNames = true;
-        if (!traversal.init() || !traversal.addStartVisited(root) || !traversal.traverse())
+        if (!traversal.init() || !traversal.addStart(root) || !traversal.traverse())
             return mozilla::Nothing();
 
         // Take ownership of the back edges we created while traversing the
         // graph so that we can follow them from `paths_` and don't
         // use-after-free.
         paths.backEdges_ = mozilla::Move(traversal.visited);
 
         MOZ_ASSERT(paths.initialized());
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -648,40 +648,16 @@ ComparisonLeft(ParseNode* pn)
 }
 
 static inline ParseNode*
 ComparisonRight(ParseNode* pn)
 {
     return BinaryOpRight(pn);
 }
 
-static inline ParseNode*
-AndOrLeft(ParseNode* pn)
-{
-    return BinaryOpLeft(pn);
-}
-
-static inline ParseNode*
-AndOrRight(ParseNode* pn)
-{
-    return BinaryOpRight(pn);
-}
-
-static inline ParseNode*
-RelationalLeft(ParseNode* pn)
-{
-    return BinaryOpLeft(pn);
-}
-
-static inline ParseNode*
-RelationalRight(ParseNode* pn)
-{
-    return BinaryOpRight(pn);
-}
-
 static inline bool
 IsExpressionStatement(ParseNode* pn)
 {
     return pn->isKind(PNK_SEMI);
 }
 
 static inline ParseNode*
 ExpressionStatementExpr(ParseNode* pn)
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -195,17 +195,16 @@ const Class SimdTypeDescr::class_ = {
 
 namespace {
 
 // Define classes (Int8x16Defn, Int16x8Defn, etc.) to group together various
 // properties and so on.
 #define DEFINE_DEFN_(TypeName)                                       \
 class TypeName##Defn {                                               \
   public:                                                            \
-    static const SimdType type = SimdType::TypeName;                 \
     static const JSFunctionSpec Methods[];                           \
 };
 
 FOR_EACH_SIMD(DEFINE_DEFN_)
 #undef DEFINE_DEFN_
 
 } // namespace
 
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -928,17 +928,20 @@ Statistics::endGC()
 
     runtime->addTelemetry(JS_TELEMETRY_GC_IS_COMPARTMENTAL, !zoneStats.isCollectingAllZones());
     runtime->addTelemetry(JS_TELEMETRY_GC_MS, t(total));
     runtime->addTelemetry(JS_TELEMETRY_GC_MAX_PAUSE_MS, t(longest));
     int64_t markTotal = SumPhase(PHASE_MARK, phaseTimes);
     int64_t markRootsTotal = SumPhase(PHASE_MARK_ROOTS, phaseTimes);
     runtime->addTelemetry(JS_TELEMETRY_GC_MARK_MS, t(markTotal));
     runtime->addTelemetry(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[PHASE_DAG_NONE][PHASE_SWEEP]));
-    runtime->addTelemetry(JS_TELEMETRY_GC_COMPACT_MS, t(phaseTimes[PHASE_DAG_NONE][PHASE_COMPACT]));
+    if (runtime->gc.isCompactingGc()) {
+        runtime->addTelemetry(JS_TELEMETRY_GC_COMPACT_MS,
+                              t(phaseTimes[PHASE_DAG_NONE][PHASE_COMPACT]));
+    }
     runtime->addTelemetry(JS_TELEMETRY_GC_MARK_ROOTS_MS, t(markRootsTotal));
     runtime->addTelemetry(JS_TELEMETRY_GC_MARK_GRAY_MS, t(phaseTimes[PHASE_DAG_NONE][PHASE_SWEEP_MARK_GRAY]));
     runtime->addTelemetry(JS_TELEMETRY_GC_NON_INCREMENTAL, !!nonincrementalReason_);
     runtime->addTelemetry(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gc.isIncrementalGCAllowed());
     runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, t(sccTotal));
     runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, t(sccLongest));
 
     if (!aborted) {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/heap-analysis/bug-1249107.js
@@ -0,0 +1,1 @@
+shortestPaths(this, [this], 5)
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -3301,17 +3301,17 @@ IonBuilder::inlineConstructSimdObject(Ca
         if (laneType == MIRType_Int32) {
             defVal = constant(Int32Value(0));
         } else if (laneType == MIRType_Boolean) {
             defVal = constant(BooleanValue(false));
         } else if (laneType == MIRType_Double) {
             defVal = constant(DoubleNaNValue());
         } else {
             MOZ_ASSERT(laneType == MIRType_Float32);
-            defVal = MConstant::NewFloat32(alloc(), GenericNaN());
+            defVal = MConstant::NewFloat32(alloc(), JS::GenericNaN());
             current->add(defVal);
         }
     }
 
     MDefinition* lane[4];
     for (unsigned i = 0; i < 4; i++)
         lane[i] = callInfo.getArgWithDefault(i, defVal);
 
--- a/js/src/jit/MacroAssembler-inl.h
+++ b/js/src/jit/MacroAssembler-inl.h
@@ -614,12 +614,27 @@ MacroAssembler::assertStackAlignment(uin
     branchTestStackPtr(Assembler::Zero, Imm32((alignment - 1) ^ offset), &ok);
 
     bind(&bad);
     breakpoint();
     bind(&ok);
 #endif
 }
 
+void
+MacroAssembler::storeCallResultValue(AnyRegister dest)
+{
+    unboxValue(JSReturnOperand, dest);
+}
+
+void
+MacroAssembler::storeCallResultValue(TypedOrValueRegister dest)
+{
+    if (dest.hasValue())
+        storeCallResultValue(dest.valueReg());
+    else
+        storeCallResultValue(dest.typedReg());
+}
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_MacroAssembler_inl_h */
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -819,30 +819,46 @@ class MacroAssembler : public MacroAssem
     inline void branchPtr(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label)
         DEFINED_ON(arm, arm64, mips_shared, x86, x64);
     inline void branchPtr(Condition cond, const AbsoluteAddress& lhs, ImmWord rhs, Label* label)
         DEFINED_ON(arm, arm64, mips_shared, x86, x64);
 
     inline void branchPtr(Condition cond, wasm::SymbolicAddress lhs, Register rhs, Label* label)
         DEFINED_ON(arm, arm64, mips_shared, x86, x64);
 
+    template <typename T>
+    inline CodeOffsetJump branchPtrWithPatch(Condition cond, Register lhs, T rhs, RepatchLabel* label) PER_SHARED_ARCH;
+    template <typename T>
+    inline CodeOffsetJump branchPtrWithPatch(Condition cond, Address lhs, T rhs, RepatchLabel* label) PER_SHARED_ARCH;
+
+    void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label)
+        DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+    void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label) PER_ARCH;
+
     // This function compares a Value (lhs) which is having a private pointer
     // boxed inside a js::Value, with a raw pointer (rhs).
     inline void branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label) PER_ARCH;
 
     inline void branchFloat(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                             Label* label) PER_SHARED_ARCH;
     inline void branchTruncateFloat32(FloatRegister src, Register dest, Label* fail)
         DEFINED_ON(arm, arm64, mips_shared, x86, x64);
 
     inline void branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                              Label* label) PER_SHARED_ARCH;
     inline void branchTruncateDouble(FloatRegister src, Register dest, Label* fail)
         DEFINED_ON(arm, arm64, mips_shared, x86, x64);
 
+    template <typename T>
+    inline void branchAdd32(Condition cond, T src, Register dest, Label* label) PER_SHARED_ARCH;
+    template <typename T>
+    inline void branchSub32(Condition cond, T src, Register dest, Label* label) PER_SHARED_ARCH;
+
+    inline void decBranchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
+
     template <class L>
     inline void branchTest32(Condition cond, Register lhs, Register rhs, L label) PER_SHARED_ARCH;
     template <class L>
     inline void branchTest32(Condition cond, Register lhs, Imm32 rhs, L label) PER_SHARED_ARCH;
     inline void branchTest32(Condition cond, const Address& lhs, Imm32 rhh, Label* label) PER_SHARED_ARCH;
     inline void branchTest32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, Label* label)
         DEFINED_ON(arm, arm64, mips_shared, x86, x64);
 
@@ -897,16 +913,27 @@ class MacroAssembler : public MacroAssem
     // might actually be that type.
     void branchEqualTypeIfNeeded(MIRType type, MDefinition* maybeDef, Register tag, Label* label);
 
     template <typename T>
     inline void branchKey(Condition cond, const T& length, const Int32Key& key, Label* label);
 
     inline void branchTestNeedsIncrementalBarrier(Condition cond, Label* label);
 
+    inline void branchTestInt32(Condition cond, Register tag, Label* label)
+        DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+    inline void branchTestInt32(Condition cond, const Address& address, Label* label)
+        DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+    inline void branchTestInt32(Condition cond, const BaseIndex& address, Label* label)
+        DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+    inline void branchTestInt32(Condition cond, const ValueOperand& src, Label* label) PER_ARCH;
+
+    inline void branchTestInt32Truthy(bool truthy, const ValueOperand& operand, Label* label)
+        DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+
     //}}} check_macroassembler_style
   public:
 
     // Emits a test of a value against all types in a TypeSet. A scratch
     // register is required.
     template <typename Source>
     void guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind, Register scratch, Label* miss);
 
@@ -1007,25 +1034,17 @@ class MacroAssembler : public MacroAssem
             mov(ReturnReg, reg);
     }
 
     void storeCallFloatResult(FloatRegister reg) {
         if (reg != ReturnDoubleReg)
             moveDouble(ReturnDoubleReg, reg);
     }
 
-    void storeCallResultValue(AnyRegister dest) {
-#if defined(JS_NUNBOX32)
-        unboxValue(ValueOperand(JSReturnReg_Type, JSReturnReg_Data), dest);
-#elif defined(JS_PUNBOX64)
-        unboxValue(ValueOperand(JSReturnReg), dest);
-#else
-#error "Bad architecture"
-#endif
-    }
+    inline void storeCallResultValue(AnyRegister dest);
 
     void storeCallResultValue(ValueOperand dest) {
 #if defined(JS_NUNBOX32)
         // reshuffle the return registers used for a call result to store into
         // dest, using ReturnReg as a scratch register if necessary. This must
         // only be called after returning from a call, at a point when the
         // return register is not live. XXX would be better to allow wrappers
         // to store the return value to different places.
@@ -1046,22 +1065,17 @@ class MacroAssembler : public MacroAssem
 #elif defined(JS_PUNBOX64)
         if (dest.valueReg() != JSReturnReg)
             mov(JSReturnReg, dest.valueReg());
 #else
 #error "Bad architecture"
 #endif
     }
 
-    void storeCallResultValue(TypedOrValueRegister dest) {
-        if (dest.hasValue())
-            storeCallResultValue(dest.valueReg());
-        else
-            storeCallResultValue(dest.typedReg());
-    }
+    inline void storeCallResultValue(TypedOrValueRegister dest);
 
     template <typename T>
     Register extractString(const T& source, Register scratch) {
         return extractObject(source, scratch);
     }
 
     inline void bumpKey(Int32Key* key, int diff);
 
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -580,16 +580,34 @@ MacroAssembler::branchPtr(Condition cond
 void
 MacroAssembler::branchPtr(Condition cond, wasm::SymbolicAddress lhs, Register rhs, Label* label)
 {
     ScratchRegisterScope scratch(*this);
     loadPtr(lhs, scratch);
     branchPtr(cond, scratch, rhs, label);
 }
 
+template <typename T>
+CodeOffsetJump
+MacroAssembler::branchPtrWithPatch(Condition cond, Register lhs, T rhs, RepatchLabel* label)
+{
+    ma_cmp(lhs, rhs);
+    return jumpWithPatch(label, cond);
+}
+
+template <typename T>
+CodeOffsetJump
+MacroAssembler::branchPtrWithPatch(Condition cond, Address lhs, T rhs, RepatchLabel* label)
+{
+    AutoRegisterScope scratch2(*this, secondScratchReg_);
+    ma_ldr(lhs, scratch2);
+    ma_cmp(scratch2, rhs);
+    return jumpWithPatch(label, cond);
+}
+
 void
 MacroAssembler::branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label)
 {
     branchPtr(cond, lhs, rhs, label);
 }
 
 void
 MacroAssembler::branchFloat(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
@@ -666,16 +684,39 @@ MacroAssembler::branchTruncateDouble(Flo
 
     ma_vcvt_F64_I32(src, scratchSIntReg);
     ma_vxfer(scratchSIntReg, dest);
     ma_cmp(dest, Imm32(0x7fffffff));
     ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
     ma_b(fail, Assembler::Equal);
 }
 
+template <typename T>
+void
+MacroAssembler::branchAdd32(Condition cond, T src, Register dest, Label* label)
+{
+    add32(src, dest);
+    j(cond, label);
+}
+
+template <typename T>
+void
+MacroAssembler::branchSub32(Condition cond, T src, Register dest, Label* label)
+{
+    ma_sub(src, dest, SetCC);
+    j(cond, label);
+}
+
+void
+MacroAssembler::decBranchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label)
+{
+    ma_sub(rhs, lhs, SetCC);
+    as_b(label, cond);
+}
+
 template <class L>
 void
 MacroAssembler::branchTest32(Condition cond, Register lhs, Register rhs, L label)
 {
     MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
     // x86 likes test foo, foo rather than cmp foo, #0.
     // Convert the former into the latter.
     if (lhs == rhs && (cond == Zero || cond == NonZero))
@@ -739,27 +780,50 @@ MacroAssembler::branchTest64(Condition c
         MOZ_ASSERT(lhs.high == rhs.high);
         ma_orr(lhs.low, lhs.high, ScratchRegister);
         branchTestPtr(cond, ScratchRegister, ScratchRegister, label);
     } else {
         MOZ_CRASH("Unsupported condition");
     }
 }
 
+void
+MacroAssembler::branchTestInt32(Condition cond, Register tag, Label* label)
+{
+    branchTestInt32Impl(cond, tag, label);
+}
+
+void
+MacroAssembler::branchTestInt32(Condition cond, const Address& address, Label* label)
+{
+    branchTestInt32Impl(cond, address, label);
+}
+
+void
+MacroAssembler::branchTestInt32(Condition cond, const BaseIndex& address, Label* label)
+{
+    branchTestInt32Impl(cond, address, label);
+}
+
+void
+MacroAssembler::branchTestInt32(Condition cond, const ValueOperand& src, Label* label)
+{
+    branchTestInt32Impl(cond, src, label);
+}
+
+void
+MacroAssembler::branchTestInt32Truthy(bool truthy, const ValueOperand& operand, Label* label)
+{
+    Condition c = testInt32Truthy(truthy, operand);
+    ma_b(label, c);
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
-template <typename T>
-void
-MacroAssemblerARMCompat::branchAdd32(Condition cond, T src, Register dest, Label* label)
-{
-    asMasm().add32(src, dest);
-    j(cond, label);
-}
-
 void
 MacroAssemblerARMCompat::incrementInt32Value(const Address& addr)
 {
     asMasm().add32(Imm32(1), ToPayload(addr));
 }
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -2937,17 +2937,17 @@ MacroAssemblerARMCompat::unboxDouble(con
     ma_vldr(src, dest);
 }
 
 void
 MacroAssemblerARMCompat::unboxValue(const ValueOperand& src, AnyRegister dest)
 {
     if (dest.isFloat()) {
         Label notInt32, end;
-        branchTestInt32(Assembler::NotEqual, src, &notInt32);
+        asMasm().branchTestInt32(Assembler::NotEqual, src, &notInt32);
         convertInt32ToDouble(src.payloadReg(), dest.fpu());
         ma_b(&end);
         bind(&notInt32);
         unboxDouble(src, dest.fpu());
         bind(&end);
     } else if (src.payloadReg() != dest.gpr()) {
         as_mov(dest.gpr(), O2Reg(src.payloadReg()));
     }
@@ -3026,17 +3026,17 @@ MacroAssemblerARMCompat::loadInt32OrDoub
 {
     Label notInt32, end;
 
     // If it's an int, convert to a double.
     {
         ScratchRegisterScope scratch(asMasm());
 
         ma_ldr(ToType(src), scratch);
-        branchTestInt32(Assembler::NotEqual, scratch, &notInt32);
+        asMasm().branchTestInt32(Assembler::NotEqual, scratch, &notInt32);
         ma_ldr(ToPayload(src), scratch);
         convertInt32ToDouble(scratch, dest);
         ma_b(&end);
     }
 
     // Not an int, just load as double.
     bind(&notInt32);
     ma_vldr(src, dest);
@@ -3054,17 +3054,17 @@ MacroAssemblerARMCompat::loadInt32OrDoub
     ScratchRegisterScope scratch(asMasm());
 
     // If it's an int, convert it to double.
     ma_alu(base, lsl(index, shift), scratch, OpAdd);
 
     // Since we only have one scratch register, we need to stomp over it with
     // the tag.
     ma_ldr(Address(scratch, NUNBOX32_TYPE_OFFSET), scratch);
-    branchTestInt32(Assembler::NotEqual, scratch, &notInt32);
+    asMasm().branchTestInt32(Assembler::NotEqual, scratch, &notInt32);
 
     // Implicitly requires NUNBOX32_PAYLOAD_OFFSET == 0: no offset provided
     ma_ldr(DTRAddr(base, DtrRegImmShift(index, LSL, shift)), scratch);
     convertInt32ToDouble(scratch, dest);
     ma_b(&end);
 
     // Not an int, just load as double.
     bind(&notInt32);
@@ -3444,17 +3444,17 @@ MacroAssemblerARMCompat::simulatorStop(c
 #endif
 }
 
 void
 MacroAssemblerARMCompat::ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure)
 {
     Label isDouble, done;
     branchTestDouble(Assembler::Equal, source.typeReg(), &isDouble);
-    branchTestInt32(Assembler::NotEqual, source.typeReg(), failure);
+    asMasm().branchTestInt32(Assembler::NotEqual, source.typeReg(), failure);
 
     convertInt32ToDouble(source.payloadReg(), dest);
     jump(&done);
 
     bind(&isDouble);
     unboxDouble(source, dest);
 
     bind(&done);
@@ -4006,49 +4006,16 @@ MacroAssemblerARMCompat::jumpWithPatch(R
     ARMBuffer::PoolEntry pe;
     BufferOffset bo = as_BranchPool(0xdeadbeef, label, &pe, cond, documentation);
     // Fill in a new CodeOffset with both the load and the pool entry that the
     // instruction loads from.
     CodeOffsetJump ret(bo.getOffset(), pe.index());
     return ret;
 }
 
-void
-MacroAssemblerARMCompat::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp,
-                                                 Label* label)
-{
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-
-    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
-    MOZ_ASSERT(ptr != temp);
-    MOZ_ASSERT(ptr != scratch2);
-
-    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
-    uintptr_t startChunk = nursery.start() >> Nursery::ChunkShift;
-
-    ma_mov(Imm32(startChunk), scratch2);
-    as_rsb(scratch2, scratch2, lsr(ptr, Nursery::ChunkShift));
-    asMasm().branch32(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
-                      scratch2, Imm32(nursery.numChunks()), label);
-}
-
-void
-MacroAssemblerARMCompat::branchValueIsNurseryObject(Condition cond, ValueOperand value,
-                                                    Register temp, Label* label)
-{
-    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
-
-    Label done;
-
-    branchTestObject(Assembler::NotEqual, value, cond == Assembler::Equal ? &done : label);
-    branchPtrInNurseryRange(cond, value.payloadReg(), temp, label);
-
-    bind(&done);
-}
-
 namespace js {
 namespace jit {
 
 template<>
 Register
 MacroAssemblerARMCompat::computePointer<BaseIndex>(const BaseIndex& src, Register r)
 {
     Register base = src.base;
@@ -5060,9 +5027,45 @@ MacroAssembler::pushFakeReturnAddress(Re
     ma_nop();
     uint32_t pseudoReturnOffset = currentOffset();
     leaveNoPool();
 
     MOZ_ASSERT_IF(!oom(), pseudoReturnOffset - offsetBeforePush == 8);
     return pseudoReturnOffset;
 }
 
+// ===============================================================
+// Branch functions
+
+void
+MacroAssembler::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp,
+                                        Label* label)
+{
+    AutoRegisterScope scratch2(*this, secondScratchReg_);
+
+    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+    MOZ_ASSERT(ptr != temp);
+    MOZ_ASSERT(ptr != scratch2);
+
+    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
+    uintptr_t startChunk = nursery.start() >> Nursery::ChunkShift;
+
+    ma_mov(Imm32(startChunk), scratch2);
+    as_rsb(scratch2, scratch2, lsr(ptr, Nursery::ChunkShift));
+    branch32(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
+             scratch2, Imm32(nursery.numChunks()), label);
+}
+
+void
+MacroAssembler::branchValueIsNurseryObject(Condition cond, ValueOperand value,
+                                           Register temp, Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+
+    Label done;
+
+    branchTestObject(Assembler::NotEqual, value, cond == Assembler::Equal ? &done : label);
+    branchPtrInNurseryRange(cond, value.payloadReg(), temp, label);
+
+    bind(&done);
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -758,17 +758,17 @@ class MacroAssemblerARMCompat : public M
     Condition testDoubleTruthy(bool truthy, FloatRegister reg);
     Condition testStringTruthy(bool truthy, const ValueOperand& value);
 
     void boolValueToFloat32(const ValueOperand& operand, FloatRegister dest);
     void int32ValueToFloat32(const ValueOperand& operand, FloatRegister dest);
     void loadConstantFloat32(float f, FloatRegister dest);
 
     template<typename T>
-    void branchTestInt32(Condition cond, const T & t, Label* label) {
+    void branchTestInt32Impl(Condition cond, const T & t, Label* label) {
         Condition c = testInt32(cond, t);
         ma_b(label, c);
     }
     template<typename T>
     void branchTestBoolean(Condition cond, const T & t, Label* label) {
         Condition c = testBoolean(cond, t);
         ma_b(label, c);
     }
@@ -813,55 +813,35 @@ class MacroAssemblerARMCompat : public M
         cond = testMagic(cond, t);
         ma_b(label, cond);
     }
     void branchTestMagicValue(Condition cond, const ValueOperand& val, JSWhyMagic why,
                               Label* label) {
         MOZ_ASSERT(cond == Equal || cond == NotEqual);
         branchTestValue(cond, val, MagicValue(why), label);
     }
-    void branchTestInt32Truthy(bool truthy, const ValueOperand& operand, Label* label) {
-        Condition c = testInt32Truthy(truthy, operand);
-        ma_b(label, c);
-    }
     void branchTestBooleanTruthy(bool truthy, const ValueOperand& operand, Label* label) {
         Condition c = testBooleanTruthy(truthy, operand);
         ma_b(label, c);
     }
     void branchTestDoubleTruthy(bool truthy, FloatRegister reg, Label* label) {
         Condition c = testDoubleTruthy(truthy, reg);
         ma_b(label, c);
     }
     void branchTestStringTruthy(bool truthy, const ValueOperand& value, Label* label) {
         Condition c = testStringTruthy(truthy, value);
         ma_b(label, c);
     }
-    void decBranchPtr(Condition cond, Register lhs, Imm32 imm, Label* label) {
-        ma_sub(imm, lhs, SetCC);
-        as_b(label, cond);
-    }
     void moveValue(const Value& val, Register type, Register data);
 
     CodeOffsetJump jumpWithPatch(RepatchLabel* label, Condition cond = Always,
                                  Label* documentation = nullptr);
     CodeOffsetJump backedgeJump(RepatchLabel* label, Label* documentation) {
         return jumpWithPatch(label, Always, documentation);
     }
-    template <typename T>
-    CodeOffsetJump branchPtrWithPatch(Condition cond, Register reg, T ptr, RepatchLabel* label) {
-        ma_cmp(reg, ptr);
-        return jumpWithPatch(label, cond);
-    }
-    template <typename T>
-    CodeOffsetJump branchPtrWithPatch(Condition cond, Address addr, T ptr, RepatchLabel* label) {
-        AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-        ma_ldr(addr, scratch2);
-        ma_cmp(scratch2, ptr);
-        return jumpWithPatch(label, cond);
-    }
 
     void loadUnboxedValue(Address address, MIRType type, AnyRegister dest) {
         if (dest.isFloat())
             loadInt32OrDouble(address, dest.fpu());
         else
             ma_ldr(address, dest.gpr());
     }
 
@@ -980,23 +960,16 @@ class MacroAssemblerARMCompat : public M
     void storeTypeTag(ImmTag tag, const BaseIndex& dest);
 
     void handleFailureWithHandlerTail(void* handler);
 
     /////////////////////////////////////////////////////////////////
     // Common interface.
     /////////////////////////////////////////////////////////////////
   public:
-    template <typename T> inline void branchAdd32(Condition cond, T src, Register dest, Label* label);
-    template <typename T>
-    void branchSub32(Condition cond, T src, Register dest, Label* label) {
-        ma_sub(src, dest, SetCC);
-        j(cond, label);
-    }
-
     void not32(Register reg);
 
     void move32(Imm32 imm, Register dest);
     void move32(Register src, Register dest);
 
     void movePtr(Register src, Register dest);
     void movePtr(ImmWord imm, Register dest);
     void movePtr(ImmPtr imm, Register dest);
@@ -1543,19 +1516,16 @@ class MacroAssemblerARMCompat : public M
     BufferOffset ma_BoundsCheck(Register bounded) {
         return as_cmp(bounded, Imm8(0));
     }
 
     void moveFloat32(FloatRegister src, FloatRegister dest) {
         as_vmov(VFPRegister(dest).singleOverlay(), VFPRegister(src).singleOverlay());
     }
 
-    void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label);
-    void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label);
-
     void loadWasmActivation(Register dest) {
         loadPtr(Address(GlobalReg, wasm::ActivationGlobalDataOffset - AsmJSGlobalRegBias), dest);
     }
     void loadAsmJSHeapRegisterFromGlobalData() {
         loadPtr(Address(GlobalReg, wasm::HeapGlobalDataOffset - AsmJSGlobalRegBias), HeapReg);
     }
     // Instrumentation for entering and leaving the profiler.
     void profilerEnterFrame(Register framePtr, Register scratch);
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -649,16 +649,39 @@ MacroAssembler::branchPtr(Condition cond
 {
     vixl::UseScratchRegisterScope temps(this);
     const Register scratch = temps.AcquireX().asUnsized();
     MOZ_ASSERT(scratch != rhs);
     loadPtr(lhs, scratch);
     branchPtr(cond, scratch, rhs, label);
 }
 
+template <typename T>
+CodeOffsetJump
+MacroAssembler::branchPtrWithPatch(Condition cond, Register lhs, T rhs, RepatchLabel* label)
+{
+    cmpPtr(lhs, rhs);
+    return jumpWithPatch(label, cond);
+}
+
+template <typename T>
+CodeOffsetJump
+MacroAssembler::branchPtrWithPatch(Condition cond, Address lhs, T rhs, RepatchLabel* label)
+{
+    // The scratch register is unused after the condition codes are set.
+    {
+        vixl::UseScratchRegisterScope temps(this);
+        const Register scratch = temps.AcquireX().asUnsized();
+        MOZ_ASSERT(scratch != lhs.base);
+        loadPtr(lhs, scratch);
+        cmpPtr(scratch, rhs);
+    }
+    return jumpWithPatch(label, cond);
+}
+
 void
 MacroAssembler::branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label)
 {
     vixl::UseScratchRegisterScope temps(this);
     const Register scratch = temps.AcquireX().asUnsized();
     if (rhs != scratch)
         movePtr(rhs, scratch);
     // Instead of unboxing lhs, box rhs and do direct comparison with lhs.
@@ -744,16 +767,39 @@ MacroAssembler::branchTruncateDouble(Flo
 
     Fcvtzs(dest64, src64);
     Add(scratch64, dest64, Operand(0x7fffffffffffffff));
     Cmn(scratch64, 3);
     B(fail, Assembler::Above);
     And(dest64, dest64, Operand(0xffffffff));
 }
 
+template <typename T>
+void
+MacroAssembler::branchAdd32(Condition cond, T src, Register dest, Label* label)
+{
+    adds32(src, dest);
+    branch(cond, label);
+}
+
+template <typename T>
+void
+MacroAssembler::branchSub32(Condition cond, T src, Register dest, Label* label)
+{
+    subs32(src, dest);
+    branch(cond, label);
+}
+
+void
+MacroAssembler::decBranchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label)
+{
+    Subs(ARMRegister(lhs, 64), ARMRegister(lhs, 64), Operand(rhs.value));
+    B(cond, label);
+}
+
 template <class L>
 void
 MacroAssembler::branchTest32(Condition cond, Register lhs, Register rhs, L label)
 {
     MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
     // x86 prefers |test foo, foo| to |cmp foo, #0|.
     // Convert the former to the latter for ARM.
     if (lhs == rhs && (cond == Zero || cond == NonZero))
@@ -817,16 +863,47 @@ MacroAssembler::branchTestPtr(Condition 
 
 void
 MacroAssembler::branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp,
                              Label* label)
 {
     branchTestPtr(cond, lhs.reg, rhs.reg, label);
 }
 
+void
+MacroAssembler::branchTestInt32(Condition cond, Register tag, Label* label)
+{
+    branchTestInt32Impl(cond, tag, label);
+}
+
+void
+MacroAssembler::branchTestInt32(Condition cond, const Address& address, Label* label)
+{
+    branchTestInt32Impl(cond, address, label);
+}
+
+void
+MacroAssembler::branchTestInt32(Condition cond, const BaseIndex& address, Label* label)
+{
+    branchTestInt32Impl(cond, address, label);
+}
+
+void
+MacroAssembler::branchTestInt32(Condition cond, const ValueOperand& src, Label* label)
+{
+    branchTestInt32Impl(cond, src, label);
+}
+
+void
+MacroAssembler::branchTestInt32Truthy(bool truthy, const ValueOperand& operand, Label* label)
+{
+    Condition c = testInt32Truthy(truthy, operand);
+    B(label, c);
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 template <typename T>
 void
 MacroAssemblerCompat::addToStackPtr(T t)
 {
     asMasm().addPtr(t, getStackPointer());
@@ -884,12 +961,55 @@ MacroAssemblerCompat::branchStackPtrRhs(
 
 template <typename T>
 void
 MacroAssemblerCompat::branchTestStackPtr(Condition cond, T t, Label* label)
 {
     asMasm().branchTestPtr(cond, getStackPointer(), t, label);
 }
 
+// If source is a double, load into dest.
+// If source is int32, convert to double and store in dest.
+// Else, branch to failure.
+void
+MacroAssemblerCompat::ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure)
+{
+    Label isDouble, done;
+
+    // TODO: splitTagForTest really should not leak a scratch register.
+    Register tag = splitTagForTest(source);
+    {
+        vixl::UseScratchRegisterScope temps(this);
+        temps.Exclude(ARMRegister(tag, 64));
+
+        branchTestDouble(Assembler::Equal, tag, &isDouble);
+        asMasm().branchTestInt32(Assembler::NotEqual, tag, failure);
+    }
+
+    convertInt32ToDouble(source.valueReg(), dest);
+    jump(&done);
+
+    bind(&isDouble);
+    unboxDouble(source, dest);
+
+    bind(&done);
+}
+
+void
+MacroAssemblerCompat::unboxValue(const ValueOperand& src, AnyRegister dest)
+{
+    if (dest.isFloat()) {
+        Label notInt32, end;
+        asMasm().branchTestInt32(Assembler::NotEqual, src, &notInt32);
+        convertInt32ToDouble(src.valueReg(), dest.fpu());
+        jump(&end);
+        bind(&notInt32);
+        unboxDouble(src, dest.fpu());
+        bind(&end);
+    } else {
+        unboxNonDouble(src, dest.gpr());
+    }
+}
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm64_MacroAssembler_arm64_inl_h */
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -213,54 +213,16 @@ MacroAssemblerCompat::handleFailureWithH
     bind(&bailout);
     Ldr(x2, MemOperand(GetStackPointer64(), offsetof(ResumeFromException, bailoutInfo)));
     Ldr(x1, MemOperand(GetStackPointer64(), offsetof(ResumeFromException, target)));
     Mov(x0, BAILOUT_RETURN_OK);
     Br(x1);
 }
 
 void
-MacroAssemblerCompat::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp,
-                                              Label* label)
-{
-    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
-    MOZ_ASSERT(ptr != temp);
-    MOZ_ASSERT(ptr != ScratchReg && ptr != ScratchReg2); // Both may be used internally.
-    MOZ_ASSERT(temp != ScratchReg && temp != ScratchReg2);
-
-    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
-    movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
-    asMasm().addPtr(ptr, temp);
-    asMasm().branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
-                       temp, ImmWord(nursery.nurserySize()), label);
-}
-
-void
-MacroAssemblerCompat::branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp,
-                                                 Label* label)
-{
-    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
-    MOZ_ASSERT(temp != ScratchReg && temp != ScratchReg2); // Both may be used internally.
-
-    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
-
-    // Avoid creating a bogus ObjectValue below.
-    if (!nursery.exists())
-        return;
-
-    // 'Value' representing the start of the nursery tagged as a JSObject
-    Value start = ObjectValue(*reinterpret_cast<JSObject*>(nursery.start()));
-
-    movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), temp);
-    asMasm().addPtr(value.valueReg(), temp);
-    asMasm().branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
-                       temp, ImmWord(nursery.nurserySize()), label);
-}
-
-void
 MacroAssemblerCompat::breakpoint()
 {
     static int code = 0xA77;
     Brk((code++) & 0xffff);
 }
 
 template<typename T>
 void
@@ -715,12 +677,53 @@ MacroAssembler::pushFakeReturnAddress(Re
     Push(scratch);
     bind(&fakeCallsite);
     uint32_t pseudoReturnOffset = currentOffset();
 
     leaveNoPool();
     return pseudoReturnOffset;
 }
 
+// ===============================================================
+// Branch functions
+
+void
+MacroAssembler::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp,
+                                        Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+    MOZ_ASSERT(ptr != temp);
+    MOZ_ASSERT(ptr != ScratchReg && ptr != ScratchReg2); // Both may be used internally.
+    MOZ_ASSERT(temp != ScratchReg && temp != ScratchReg2);
+
+    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
+    movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
+    addPtr(ptr, temp);
+    branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
+              temp, ImmWord(nursery.nurserySize()), label);
+}
+
+void
+MacroAssembler::branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp,
+                                           Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+    MOZ_ASSERT(temp != ScratchReg && temp != ScratchReg2); // Both may be used internally.
+
+    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
+
+    // Avoid creating a bogus ObjectValue below.
+    if (!nursery.exists())
+        return;
+
+    // 'Value' representing the start of the nursery tagged as a JSObject
+    Value start = ObjectValue(*reinterpret_cast<JSObject*>(nursery.start()));
+
+    movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), temp);
+    addPtr(value.valueReg(), temp);
+    branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
+              temp, ImmWord(nursery.nurserySize()), label);
+}
+
 //}}} check_macroassembler_style
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/arm64/MacroAssembler-arm64.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64.h
@@ -434,40 +434,17 @@ class MacroAssemblerCompat : public vixl
         unboxInt32(value, scratch);
         return scratch;
     }
     Register extractBoolean(const ValueOperand& value, Register scratch) {
         unboxBoolean(value, scratch);
         return scratch;
     }
 
-    // If source is a double, load into dest.
-    // If source is int32, convert to double and store in dest.
-    // Else, branch to failure.
-    void ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure) {
-        Label isDouble, done;
-
-        // TODO: splitTagForTest really should not leak a scratch register.
-        Register tag = splitTagForTest(source);
-        {
-            vixl::UseScratchRegisterScope temps(this);
-            temps.Exclude(ARMRegister(tag, 64));
-
-            branchTestDouble(Assembler::Equal, tag, &isDouble);
-            branchTestInt32(Assembler::NotEqual, tag, failure);
-        }
-
-        convertInt32ToDouble(source.valueReg(), dest);
-        jump(&done);
-
-        bind(&isDouble);
-        unboxDouble(source, dest);
-
-        bind(&done);
-    }
+    inline void ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure);
 
     void emitSet(Condition cond, Register dest) {
         Cset(ARMRegister(dest, 64), cond);
     }
 
     template <typename T1, typename T2>
     void cmpPtrSet(Condition cond, T1 lhs, T2 rhs, Register dest) {
         cmpPtr(lhs, rhs);
@@ -1345,45 +1322,25 @@ class MacroAssemblerCompat : public vixl
             branch_bo = b(-1);
         }
         label->use(branch_bo.getOffset());
         return CodeOffsetJump(load_bo.getOffset(), pe.index());
     }
     CodeOffsetJump backedgeJump(RepatchLabel* label, Label* documentation = nullptr) {
         return jumpWithPatch(label, Always, documentation);
     }
-    template <typename T>
-    CodeOffsetJump branchPtrWithPatch(Condition cond, Register reg, T ptr, RepatchLabel* label) {
-        cmpPtr(reg, ptr);
-        return jumpWithPatch(label, cond);
-    }
-    template <typename T>
-    CodeOffsetJump branchPtrWithPatch(Condition cond, Address addr, T ptr, RepatchLabel* label) {
-        // The scratch register is unused after the condition codes are set.
-        {
-            vixl::UseScratchRegisterScope temps(this);
-            const Register scratch = temps.AcquireX().asUnsized();
-            MOZ_ASSERT(scratch != addr.base);
-            loadPtr(addr, scratch);
-            cmpPtr(scratch, ptr);
-        }
-        return jumpWithPatch(label, cond);
-    }
-
-    void decBranchPtr(Condition cond, Register lhs, Imm32 imm, Label* label) {
-        Subs(ARMRegister(lhs, 64), ARMRegister(lhs, 64), Operand(imm.value));
-        B(cond, label);
-    }
 
     void branchTestUndefined(Condition cond, Register tag, Label* label) {
         Condition c = testUndefined(cond, tag);
         B(label, c);
     }
-    void branchTestInt32(Condition cond, Register tag, Label* label) {
-        Condition c = testInt32(cond, tag);
+
+    template<typename T>
+    void branchTestInt32Impl(Condition cond, T& t, Label* label) {
+        Condition c = testInt32(cond, t);
         B(label, c);
     }
     void branchTestDouble(Condition cond, Register tag, Label* label) {
         Condition c = testDouble(cond, tag);
         B(label, c);
     }
     void branchTestBoolean(Condition cond, Register tag, Label* label) {
         Condition c = testBoolean(cond, tag);
@@ -1409,20 +1366,16 @@ class MacroAssemblerCompat : public vixl
         Condition c = testNumber(cond, tag);
         B(label, c);
     }
 
     void branchTestUndefined(Condition cond, const Address& address, Label* label) {
         Condition c = testUndefined(cond, address);
         B(label, c);
     }
-    void branchTestInt32(Condition cond, const Address& address, Label* label) {
-        Condition c = testInt32(cond, address);
-        B(label, c);
-    }
     void branchTestDouble(Condition cond, const Address& address, Label* label) {
         Condition c = testDouble(cond, address);
         B(label, c);
     }
     void branchTestBoolean(Condition cond, const Address& address, Label* label) {
         Condition c = testDouble(cond, address);
         B(label, c);
     }
@@ -1448,20 +1401,16 @@ class MacroAssemblerCompat : public vixl
     }
 
     // Perform a type-test on a full Value loaded into a register.
     // Clobbers the ScratchReg.
     void branchTestUndefined(Condition cond, const ValueOperand& src, Label* label) {
         Condition c = testUndefined(cond, src);
         B(label, c);
     }
-    void branchTestInt32(Condition cond, const ValueOperand& src, Label* label) {
-        Condition c = testInt32(cond, src);
-        B(label, c);
-    }
     void branchTestBoolean(Condition cond, const ValueOperand& src, Label* label) {
         Condition c = testBoolean(cond, src);
         B(label, c);
     }
     void branchTestDouble(Condition cond, const ValueOperand& src, Label* label) {
         Condition c = testDouble(cond, src);
         B(label, c);
     }
@@ -1487,20 +1436,16 @@ class MacroAssemblerCompat : public vixl
     }
 
     // Perform a type-test on a Value addressed by BaseIndex.
     // Clobbers the ScratchReg.
     void branchTestUndefined(Condition cond, const BaseIndex& address, Label* label) {
         Condition c = testUndefined(cond, address);
         B(label, c);
     }
-    void branchTestInt32(Condition cond, const BaseIndex& address, Label* label) {
-        Condition c = testInt32(cond, address);
-        B(label, c);
-    }
     void branchTestBoolean(Condition cond, const BaseIndex& address, Label* label) {
         Condition c = testBoolean(cond, address);
         B(label, c);
     }
     void branchTestDouble(Condition cond, const BaseIndex& address, Label* label) {
         Condition c = testDouble(cond, address);
         B(label, c);
     }
@@ -1644,30 +1589,18 @@ class MacroAssemblerCompat : public vixl
         loadPtr(src, dest);
         unboxNonDouble(dest, dest);
     }
     void unboxObject(const BaseIndex& src, Register dest) {
         doBaseIndex(ARMRegister(dest, 64), src, vixl::LDR_x);
         unboxNonDouble(dest, dest);
     }
 
-    void unboxValue(const ValueOperand& src, AnyRegister dest) {
-        if (dest.isFloat()) {
-            Label notInt32, end;
-            branchTestInt32(Assembler::NotEqual, src, &notInt32);
-            convertInt32ToDouble(src.valueReg(), dest.fpu());
-            jump(&end);
-            bind(&notInt32);
-            unboxDouble(src, dest.fpu());
-            bind(&end);
-        } else {
-            unboxNonDouble(src, dest.gpr());
-        }
+    inline void unboxValue(const ValueOperand& src, AnyRegister dest);
 
-    }
     void unboxString(const ValueOperand& operand, Register dest) {
         unboxNonDouble(operand, dest);
     }
     void unboxString(const Address& src, Register dest) {
         unboxNonDouble(src, dest);
     }
     void unboxSymbol(const ValueOperand& operand, Register dest) {
         unboxNonDouble(operand, dest);
@@ -2031,20 +1964,16 @@ class MacroAssemblerCompat : public vixl
         return testGCThing(cond, scratch);
     }
 
     Condition testInt32Truthy(bool truthy, const ValueOperand& operand) {
         ARMRegister payload32(operand.valueReg(), 32);
         Tst(payload32, payload32);
         return truthy ? NonZero : Zero;
     }
-    void branchTestInt32Truthy(bool truthy, const ValueOperand& operand, Label* label) {
-        Condition c = testInt32Truthy(truthy, operand);
-        B(label, c);
-    }
 
     void branchTestDoubleTruthy(bool truthy, FloatRegister reg, Label* label) {
         Fcmp(ARMFPRegister(reg, 64), 0.0);
         if (!truthy) {
             // falsy values are zero, and NaN.
             branch(Zero, label);
             branch(Overflow, label);
         } else {
@@ -2188,19 +2117,16 @@ class MacroAssemblerCompat : public vixl
         vixl::UseScratchRegisterScope temps(this);
         const Register scratch = temps.AcquireX().asUnsized();
         MOZ_ASSERT(scratch != src.base);
         MOZ_ASSERT(scratch != dest.base);
         load32(src, scratch);
         storeValue(JSVAL_TYPE_INT32, scratch, dest);
     }
 
-    void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label);
-    void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label);
-
     void profilerEnterFrame(Register framePtr, Register scratch) {
         AbsoluteAddress activation(GetJitContext()->runtime->addressOfProfilingActivation());
         loadPtr(activation, scratch);
         storePtr(framePtr, Address(scratch, JitActivation::offsetOfLastProfilingFrame()));
         storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
     }
     void profilerExitFrame() {
         branch(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail());
@@ -2553,27 +2479,16 @@ class MacroAssemblerCompat : public vixl
         syncStackPtr(); // SP is always used to transmit the stack between calls.
         vixl::MacroAssembler::Ret(vixl::lr);
     }
 
     void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest) {
         Ucvtf(ARMFPRegister(dest, 64), ARMRegister(src.reg, 64));
     }
 
-    template <typename T>
-    void branchAdd32(Condition cond, T src, Register dest, Label* label) {
-        adds32(src, dest);
-        branch(cond, label);
-    }
-
-    template <typename T>
-    void branchSub32(Condition cond, T src, Register dest, Label* label) {
-        subs32(src, dest);
-        branch(cond, label);
-    }
     void clampCheck(Register r, Label* handleNotAnInt) {
         MOZ_CRASH("clampCheck");
     }
 
     void stackCheck(ImmWord limitAddr, Label* label) {
         MOZ_CRASH("stackCheck");
     }
     void clampIntToUint8(Register reg) {
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
@@ -341,16 +341,41 @@ MacroAssembler::branchPtr(Condition cond
 
 void
 MacroAssembler::branchPtr(Condition cond, wasm::SymbolicAddress lhs, Register rhs, Label* label)
 {
     loadPtr(lhs, SecondScratchReg);
     branchPtr(cond, SecondScratchReg, rhs, label);
 }
 
+template <typename T>
+CodeOffsetJump
+MacroAssembler::branchPtrWithPatch(Condition cond, Register lhs, T rhs, RepatchLabel* label)
+{
+    movePtr(rhs, ScratchRegister);
+    Label skipJump;
+    ma_b(lhs, ScratchRegister, &skipJump, InvertCondition(cond), ShortJump);
+    CodeOffsetJump off = jumpWithPatch(label);
+    bind(&skipJump);
+    return off;
+}
+
+template <typename T>
+CodeOffsetJump
+MacroAssembler::branchPtrWithPatch(Condition cond, Address lhs, T rhs, RepatchLabel* label)
+{
+    loadPtr(lhs, SecondScratchReg);
+    movePtr(rhs, ScratchRegister);
+    Label skipJump;
+    ma_b(SecondScratchReg, ScratchRegister, &skipJump, InvertCondition(cond), ShortJump);
+    CodeOffsetJump off = jumpWithPatch(label);
+    bind(&skipJump);
+    return off;
+}
+
 void
 MacroAssembler::branchFloat(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                             Label* label)
 {
     ma_bc1s(lhs, rhs, label, cond);
 }
 
 void
@@ -380,16 +405,54 @@ MacroAssembler::branchTruncateDouble(Flo
     Label test, success;
     as_truncwd(ScratchDoubleReg, src);
     as_mfc1(dest, ScratchDoubleReg);
 
     ma_b(dest, Imm32(INT32_MAX), fail, Assembler::Equal);
     ma_b(dest, Imm32(INT32_MIN), fail, Assembler::Equal);
 }
 
+template <typename T>
+void
+MacroAssembler::branchAdd32(Condition cond, T src, Register dest, Label* overflow)
+{
+    switch (cond) {
+      case Overflow:
+        ma_addTestOverflow(dest, dest, src, overflow);
+        break;
+      default:
+        MOZ_CRASH("NYI");
+    }
+}
+
+template <typename T>
+void
+MacroAssembler::branchSub32(Condition cond, T src, Register dest, Label* overflow)
+{
+    switch (cond) {
+      case Overflow:
+        ma_subTestOverflow(dest, dest, src, overflow);
+        break;
+      case NonZero:
+      case Zero:
+        ma_subu(dest, src);
+        ma_b(dest, dest, overflow, cond);
+        break;
+      default:
+        MOZ_CRASH("NYI");
+    }
+}
+
+void
+MacroAssembler::decBranchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label)
+{
+    subPtr(rhs, lhs);
+    branchPtr(cond, lhs, Imm32(0), label);
+}
+
 template <class L>
 void
 MacroAssembler::branchTest32(Condition cond, Register lhs, Register rhs, L label)
 {
     MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
     if (lhs == rhs) {
         ma_b(lhs, rhs, label, cond);
     } else {
@@ -441,15 +504,38 @@ MacroAssembler::branchTestPtr(Condition 
 
 void
 MacroAssembler::branchTestPtr(Condition cond, const Address& lhs, Imm32 rhs, Label* label)
 {
     loadPtr(lhs, SecondScratchReg);
     branchTestPtr(cond, SecondScratchReg, rhs, label);
 }
 
+void
+MacroAssembler::branchTestInt32(Condition cond, Register tag, Label* label)
+{
+    MOZ_ASSERT(cond == Equal || cond == NotEqual);
+    ma_b(tag, ImmTag(JSVAL_TAG_INT32), label, cond);
+}
+
+void
+MacroAssembler::branchTestInt32(Condition cond, const Address& address, Label* label)
+{
+    MOZ_ASSERT(cond == Equal || cond == NotEqual);
+    extractTag(address, SecondScratchReg);
+    ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_INT32), label, cond);
+}
+
+void
+MacroAssembler::branchTestInt32(Condition cond, const BaseIndex& address, Label* label)
+{
+    MOZ_ASSERT(cond == Equal || cond == NotEqual);
+    extractTag(address, SecondScratchReg);
+    ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_INT32), label, cond);
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips_shared_MacroAssembler_mips_shared_inl_h */
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
@@ -1271,9 +1271,24 @@ MacroAssembler::pushFakeReturnAddress(Re
     Push(scratch);
     bind(cl.target());
     uint32_t retAddr = currentOffset();
 
     addCodeLabel(cl);
     return retAddr;
 }
 
+void
+MacroAssembler::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp,
+                                        Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+    MOZ_ASSERT(ptr != temp);
+    MOZ_ASSERT(ptr != SecondScratchReg);
+
+    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
+    movePtr(ImmWord(-ptrdiff_t(nursery.start())), SecondScratchReg);
+    addPtr(ptr, SecondScratchReg);
+    branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
+              SecondScratchReg, Imm32(nursery.nurserySize()), label);
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h
@@ -269,16 +269,31 @@ MacroAssembler::branchTest64(Condition c
         MOZ_ASSERT(lhs.high == rhs.high);
         as_or(ScratchRegister, lhs.low, lhs.high);
         branchTestPtr(cond, ScratchRegister, ScratchRegister, label);
     } else {
         MOZ_CRASH("Unsupported condition");
     }
 }
 
+void
+MacroAssembler::branchTestInt32(Condition cond, const ValueOperand& value, Label* label)
+{
+    MOZ_ASSERT(cond == Equal || cond == NotEqual);
+    ma_b(value.typeReg(), ImmType(JSVAL_TYPE_INT32), label, cond);
+}
+
+
+void
+MacroAssembler::branchTestInt32Truthy(bool b, const ValueOperand& value, Label* label)
+{
+    as_and(ScratchRegister, value.payloadReg(), value.payloadReg());
+    ma_b(ScratchRegister, ScratchRegister, label, b ? NonZero : Zero);
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 void
 MacroAssemblerMIPSCompat::incrementInt32Value(const Address& addr)
 {
     asMasm().add32(Imm32(1), ToPayload(addr));
 }
@@ -295,19 +310,12 @@ void
 MacroAssemblerMIPSCompat::retn(Imm32 n) {
     // pc <- [sp]; sp += n
     loadPtr(Address(StackPointer, 0), ra);
     asMasm().addPtr(n, StackPointer);
     as_jr(ra);
     as_nop();
 }
 
-void
-MacroAssemblerMIPSCompat::decBranchPtr(Condition cond, Register lhs, Imm32 imm, Label* label)
-{
-    asMasm().subPtr(imm, lhs);
-    asMasm().branchPtr(cond, lhs, Imm32(0), label);
-}
-
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips32_MacroAssembler_mips32_inl_h */
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -1122,46 +1122,16 @@ void
 MacroAssemblerMIPSCompat::branchTestPrimitive(Condition cond, Register tag, Label* label)
 {
     MOZ_ASSERT(cond == Equal || cond == NotEqual);
     ma_b(tag, ImmTag(JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET), label,
          (cond == Equal) ? Below : AboveOrEqual);
 }
 
 void
-MacroAssemblerMIPSCompat::branchTestInt32(Condition cond, const ValueOperand& value, Label* label)
-{
-    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
-    ma_b(value.typeReg(), ImmType(JSVAL_TYPE_INT32), label, cond);
-}
-
-void
-MacroAssemblerMIPSCompat::branchTestInt32(Condition cond, Register tag, Label* label)
-{
-    MOZ_ASSERT(cond == Equal || cond == NotEqual);
-    ma_b(tag, ImmTag(JSVAL_TAG_INT32), label, cond);
-}
-
-void
-MacroAssemblerMIPSCompat::branchTestInt32(Condition cond, const Address& address, Label* label)
-{
-    MOZ_ASSERT(cond == Equal || cond == NotEqual);
-    extractTag(address, SecondScratchReg);
-    ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_INT32), label, cond);
-}
-
-void
-MacroAssemblerMIPSCompat::branchTestInt32(Condition cond, const BaseIndex& src, Label* label)
-{
-    MOZ_ASSERT(cond == Equal || cond == NotEqual);
-    extractTag(src, SecondScratchReg);
-    ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_INT32), label, cond);
-}
-
-void
 MacroAssemblerMIPSCompat:: branchTestBoolean(Condition cond, const ValueOperand& value,
                                              Label* label)
 {
     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
     ma_b(value.typeReg(), ImmType(JSVAL_TYPE_BOOLEAN), label, cond);
 }
 
 void
@@ -1544,17 +1514,17 @@ MacroAssemblerMIPSCompat::unboxObject(co
     ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET));
 }
 
 void
 MacroAssemblerMIPSCompat::unboxValue(const ValueOperand& src, AnyRegister dest)
 {
     if (dest.isFloat()) {
         Label notInt32, end;
-        branchTestInt32(Assembler::NotEqual, src, &notInt32);
+        asMasm().branchTestInt32(Assembler::NotEqual, src, &notInt32);
         convertInt32ToDouble(src.payloadReg(), dest.fpu());
         ma_b(&end, ShortJump);
         bind(&notInt32);
         unboxDouble(src, dest.fpu());
         bind(&end);
     } else if (src.payloadReg() != dest.gpr()) {
         ma_move(dest.gpr(), src.payloadReg());
     }
@@ -1619,17 +1589,17 @@ MacroAssemblerMIPSCompat::loadConstantFl
 }
 
 void
 MacroAssemblerMIPSCompat::loadInt32OrDouble(const Address& src, FloatRegister dest)
 {
     Label notInt32, end;
     // If it's an int, convert it to double.
     ma_lw(SecondScratchReg, Address(src.base, src.offset + TAG_OFFSET));
-    branchTestInt32(Assembler::NotEqual, SecondScratchReg, &notInt32);
+    asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, &notInt32);
     ma_lw(SecondScratchReg, Address(src.base, src.offset + PAYLOAD_OFFSET));
     convertInt32ToDouble(SecondScratchReg, dest);
     ma_b(&end, ShortJump);
 
     // Not an int, just load as double.
     bind(&notInt32);
     ma_ld(dest, src);
     bind(&end);
@@ -1641,17 +1611,17 @@ MacroAssemblerMIPSCompat::loadInt32OrDou
 {
     Label notInt32, end;
 
     // If it's an int, convert it to double.
 
     computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg);
     // Since we only have one scratch, we need to stomp over it with the tag.
     load32(Address(SecondScratchReg, TAG_OFFSET), SecondScratchReg);
-    branchTestInt32(Assembler::NotEqual, SecondScratchReg, &notInt32);
+    asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, &notInt32);
 
     computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg);
     load32(Address(SecondScratchReg, PAYLOAD_OFFSET), SecondScratchReg);
     convertInt32ToDouble(SecondScratchReg, dest);
     ma_b(&end, ShortJump);
 
     // Not an int, just load as double.
     bind(&notInt32);
@@ -1664,23 +1634,16 @@ MacroAssemblerMIPSCompat::loadInt32OrDou
 
 void
 MacroAssemblerMIPSCompat::loadConstantDouble(double dp, FloatRegister dest)
 {
     ma_lid(dest, dp);
 }
 
 void
-MacroAssemblerMIPSCompat::branchTestInt32Truthy(bool b, const ValueOperand& value, Label* label)
-{
-    as_and(ScratchRegister, value.payloadReg(), value.payloadReg());
-    ma_b(ScratchRegister, ScratchRegister, label, b ? NonZero : Zero);
-}
-
-void
 MacroAssemblerMIPSCompat::branchTestStringTruthy(bool b, const ValueOperand& value, Label* label)
 {
     Register string = value.payloadReg();
     ma_lw(SecondScratchReg, Address(string, JSString::offsetOfLength()));
     ma_b(SecondScratchReg, Imm32(0), label, b ? NotEqual : Equal);
 }
 
 void
@@ -2047,17 +2010,17 @@ MacroAssemblerMIPSCompat::breakpoint()
 }
 
 void
 MacroAssemblerMIPSCompat::ensureDouble(const ValueOperand& source, FloatRegister dest,
                                        Label* failure)
 {
     Label isDouble, done;
     branchTestDouble(Assembler::Equal, source.typeReg(), &isDouble);
-    branchTestInt32(Assembler::NotEqual, source.typeReg(), failure);
+    asMasm().branchTestInt32(Assembler::NotEqual, source.typeReg(), failure);
 
     convertInt32ToDouble(source.payloadReg(), dest);
     jump(&done);
 
     bind(&isDouble);
     unboxDouble(source, dest);
 
     bind(&done);
@@ -2323,45 +2286,16 @@ MacroAssemblerMIPSCompat::toggledCall(Ji
         as_nop();
         as_nop();
     }
     MOZ_ASSERT_IF(!oom(), nextOffset().getOffset() - offset.offset() == ToggledCallSize(nullptr));
     return offset;
 }
 
 void
-MacroAssemblerMIPSCompat::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp,
-                                                  Label* label)
-{
-    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
-    MOZ_ASSERT(ptr != temp);
-    MOZ_ASSERT(ptr != SecondScratchReg);
-
-    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
-    movePtr(ImmWord(-ptrdiff_t(nursery.start())), SecondScratchReg);
-    asMasm().addPtr(ptr, SecondScratchReg);
-    asMasm().branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
-                       SecondScratchReg, Imm32(nursery.nurserySize()), label);
-}
-
-void
-MacroAssemblerMIPSCompat::branchValueIsNurseryObject(Condition cond, ValueOperand value,
-                                                     Register temp, Label* label)
-{
-    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
-
-    Label done;
-
-    branchTestObject(Assembler::NotEqual, value, cond == Assembler::Equal ? &done : label);
-    branchPtrInNurseryRange(cond, value.payloadReg(), temp, label);
-
-    bind(&done);
-}
-
-void
 MacroAssemblerMIPSCompat::profilerEnterFrame(Register framePtr, Register scratch)
 {
     AbsoluteAddress activation(GetJitContext()->runtime->addressOfProfilingActivation());
     loadPtr(activation, scratch);
     storePtr(framePtr, Address(scratch, JitActivation::offsetOfLastProfilingFrame()));
     storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
 }
 
@@ -2536,9 +2470,26 @@ MacroAssembler::callWithABINoProfiler(co
     // Load the callee in t9, as above.
     loadPtr(Address(fun.base, fun.offset), t9);
     uint32_t stackAdjust;
     callWithABIPre(&stackAdjust);
     call(t9);
     callWithABIPost(stackAdjust, result);
 }
 
+// ===============================================================
+// Branch functions
+
+void
+MacroAssembler::branchValueIsNurseryObject(Condition cond, ValueOperand value,
+                                           Register temp, Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+
+    Label done;
+
+    branchTestObject(Assembler::NotEqual, value, cond == Assembler::Equal ? &done : label);
+    branchPtrInNurseryRange(cond, value.payloadReg(), temp, label);
+
+    bind(&done);
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/mips32/MacroAssembler-mips32.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32.h
@@ -370,21 +370,16 @@ class MacroAssemblerMIPSCompat : public 
     void loadInt32OrDouble(Register base, Register index,
                            FloatRegister dest, int32_t shift = defaultShift);
     void loadConstantDouble(double dp, FloatRegister dest);
 
     void boolValueToFloat32(const ValueOperand& operand, FloatRegister dest);
     void int32ValueToFloat32(const ValueOperand& operand, FloatRegister dest);
     void loadConstantFloat32(float f, FloatRegister dest);
 
-    void branchTestInt32(Condition cond, const ValueOperand& value, Label* label);
-    void branchTestInt32(Condition cond, Register tag, Label* label);
-    void branchTestInt32(Condition cond, const Address& address, Label* label);
-    void branchTestInt32(Condition cond, const BaseIndex& src, Label* label);
-
     void branchTestBoolean(Condition cond, const ValueOperand& value, Label* label);
     void branchTestBoolean(Condition cond, Register tag, Label* label);
     void branchTestBoolean(Condition cond, const Address& address, Label* label);
     void branchTestBoolean(Condition cond, const BaseIndex& src, Label* label);
 
 
     void branchTestDouble(Condition cond, const ValueOperand& value, Label* label);
     void branchTestDouble(Condition cond, Register tag, Label* label);
@@ -427,26 +422,22 @@ class MacroAssemblerMIPSCompat : public 
     void branchTestMagic(Condition cond, const BaseIndex& src, Label* label);
 
     void branchTestMagicValue(Condition cond, const ValueOperand& val, JSWhyMagic why,
                               Label* label) {
         MOZ_ASSERT(cond == Equal || cond == NotEqual);
         branchTestValue(cond, val, MagicValue(why), label);
     }
 
-    void branchTestInt32Truthy(bool b, const ValueOperand& value, Label* label);
-
     void branchTestStringTruthy(bool b, const ValueOperand& value, Label* label);
 
     void branchTestDoubleTruthy(bool b, FloatRegister value, Label* label);
 
     void branchTestBooleanTruthy(bool b, const ValueOperand& operand, Label* label);
 
-    inline void decBranchPtr(Condition cond, Register lhs, Imm32 imm, Label* label);
-
     // higher level tag testing code
     Operand ToPayload(Operand base);
     Address ToPayload(Address base) {
         return ToPayload(Operand(base)).toAddress();
     }
 
   protected:
     Operand ToType(Operand base);
@@ -457,37 +448,16 @@ class MacroAssemblerMIPSCompat : public 
     uint32_t getType(const Value& val);
     void moveData(const Value& val, Register data);
   public:
     void moveValue(const Value& val, Register type, Register data);
 
     CodeOffsetJump backedgeJump(RepatchLabel* label, Label* documentation = nullptr);
     CodeOffsetJump jumpWithPatch(RepatchLabel* label, Label* documentation = nullptr);
 
-    template <typename T>
-    CodeOffsetJump branchPtrWithPatch(Condition cond, Register reg, T ptr, RepatchLabel* label) {
-        movePtr(ptr, ScratchRegister);
-        Label skipJump;
-        ma_b(reg, ScratchRegister, &skipJump, InvertCondition(cond), ShortJump);
-        CodeOffsetJump off = jumpWithPatch(label);
-        bind(&skipJump);
-        return off;
-    }
-
-    template <typename T>
-    CodeOffsetJump branchPtrWithPatch(Condition cond, Address addr, T ptr, RepatchLabel* label) {
-        loadPtr(addr, SecondScratchReg);
-        movePtr(ptr, ScratchRegister);
-        Label skipJump;
-        ma_b(SecondScratchReg, ScratchRegister, &skipJump, InvertCondition(cond), ShortJump);
-        CodeOffsetJump off = jumpWithPatch(label);
-        bind(&skipJump);
-        return off;
-    }
-
     void loadUnboxedValue(Address address, MIRType type, AnyRegister dest) {
         if (dest.isFloat())
             loadInt32OrDouble(address, dest.fpu());
         else
             ma_lw(dest.gpr(), address);
     }
 
     void loadUnboxedValue(BaseIndex address, MIRType type, AnyRegister dest) {
@@ -903,42 +873,16 @@ class MacroAssemblerMIPSCompat : public 
 
     template<typename T>
     void atomicExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem, Register value,
                                        Register temp, Register valueTemp, Register offsetTemp, Register maskTemp,
                                        AnyRegister output);
 
     inline void incrementInt32Value(const Address& addr);
 
-    template <typename T>
-    void branchAdd32(Condition cond, T src, Register dest, Label* overflow) {
-        switch (cond) {
-          case Overflow:
-            ma_addTestOverflow(dest, dest, src, overflow);
-            break;
-          default:
-            MOZ_CRASH("NYI");
-        }
-    }
-    template <typename T>
-    void branchSub32(Condition cond, T src, Register dest, Label* overflow) {
-        switch (cond) {
-          case Overflow:
-            ma_subTestOverflow(dest, dest, src, overflow);
-            break;
-          case NonZero:
-          case Zero:
-            ma_subu(dest, src);
-            ma_b(dest, dest, overflow, cond);
-            break;
-          default:
-            MOZ_CRASH("NYI");
-        }
-    }
-
     void move32(Imm32 imm, Register dest);
     void move32(Register src, Register dest);
 
     void movePtr(Register src, Register dest);
     void movePtr(ImmWord imm, Register dest);
     void movePtr(ImmPtr imm, Register dest);
     void movePtr(wasm::SymbolicAddress imm, Register dest);
     void movePtr(ImmGCPtr imm, Register dest);
@@ -1127,20 +1071,16 @@ class MacroAssemblerMIPSCompat : public 
         ma_liPatchable(bounded, ImmWord(0));
         return bo;
     }
 
     void moveFloat32(FloatRegister src, FloatRegister dest) {
         as_movs(dest, src);
     }
 
-    void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label);
-    void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp,
-                                    Label* label);
-
     void loadWasmActivation(Register dest) {
         loadPtr(Address(GlobalReg, wasm::ActivationGlobalDataOffset - AsmJSGlobalRegBias), dest);
     }
     void loadAsmJSHeapRegisterFromGlobalData() {
         MOZ_ASSERT(Imm16::IsInSignedRange(wasm::HeapGlobalDataOffset - AsmJSGlobalRegBias));
         loadPtr(Address(GlobalReg, wasm::HeapGlobalDataOffset - AsmJSGlobalRegBias), HeapReg);
     }
 
--- a/js/src/jit/mips64/MacroAssembler-mips64-inl.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64-inl.h
@@ -205,16 +205,31 @@ MacroAssembler::branchPrivatePtr(Conditi
 
 void
 MacroAssembler::branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp,
                              Label* label)
 {
     branchTestPtr(cond, lhs.reg, rhs.reg, label);
 }
 
+void
+MacroAssembler::branchTestInt32(Condition cond, const ValueOperand& value, Label* label)
+{
+    MOZ_ASSERT(cond == Equal || cond == NotEqual);
+    splitTag(value, SecondScratchReg);
+    ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_INT32), label, cond);
+}
+
+void
+MacroAssembler::branchTestInt32Truthy(bool b, const ValueOperand& value, Label* label)
+{
+    ma_dext(ScratchRegister, value.valueReg(), Imm32(0), Imm32(32));
+    ma_b(ScratchRegister, ScratchRegister, label, b ? NonZero : Zero);
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 void
 MacroAssemblerMIPS64Compat::incrementInt32Value(const Address& addr)
 {
     asMasm().add32(Imm32(1), addr);
 }
@@ -232,19 +247,12 @@ MacroAssemblerMIPS64Compat::retn(Imm32 n
 {
     // pc <- [sp]; sp += n
     loadPtr(Address(StackPointer, 0), ra);
     asMasm().addPtr(n, StackPointer);
     as_jr(ra);
     as_nop();
 }
 
-void
-MacroAssemblerMIPS64Compat::decBranchPtr(Condition cond, Register lhs, Imm32 imm, Label* label)
-{
-    asMasm().subPtr(imm, lhs);
-    asMasm().branchPtr(cond, lhs, Imm32(0), label);
-}
-
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips64_MacroAssembler_mips64_inl_h */
--- a/js/src/jit/mips64/MacroAssembler-mips64.cpp
+++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp
@@ -1250,47 +1250,16 @@ void
 MacroAssemblerMIPS64Compat::branchTestPrimitive(Condition cond, Register tag, Label* label)
 {
     MOZ_ASSERT(cond == Equal || cond == NotEqual);
     ma_b(tag, ImmTag(JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET), label,
          (cond == Equal) ? Below : AboveOrEqual);
 }
 
 void
-MacroAssemblerMIPS64Compat::branchTestInt32(Condition cond, const ValueOperand& value, Label* label)
-{
-    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
-    splitTag(value, SecondScratchReg);
-    ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_INT32), label, cond);
-}
-
-void
-MacroAssemblerMIPS64Compat::branchTestInt32(Condition cond, Register tag, Label* label)
-{
-    MOZ_ASSERT(cond == Equal || cond == NotEqual);
-    ma_b(tag, ImmTag(JSVAL_TAG_INT32), label, cond);
-}
-
-void
-MacroAssemblerMIPS64Compat::branchTestInt32(Condition cond, const Address& address, Label* label)
-{
-    MOZ_ASSERT(cond == Equal || cond == NotEqual);
-    extractTag(address, SecondScratchReg);
-    ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_INT32), label, cond);
-}
-
-void
-MacroAssemblerMIPS64Compat::branchTestInt32(Condition cond, const BaseIndex& src, Label* label)
-{
-    MOZ_ASSERT(cond == Equal || cond == NotEqual);
-    extractTag(src, SecondScratchReg);
-    ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_INT32), label, cond);
-}
-
-void
 MacroAssemblerMIPS64Compat:: branchTestBoolean(Condition cond, const ValueOperand& value,
                                                Label* label)
 {
     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
     splitTag(value, SecondScratchReg);
     ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_BOOLEAN), label, cond);
 }
 
@@ -1770,17 +1739,17 @@ MacroAssemblerMIPS64Compat::unboxObject(
     unboxNonDouble(src, dest);
 }
 
 void
 MacroAssemblerMIPS64Compat::unboxValue(const ValueOperand& src, AnyRegister dest)
 {
     if (dest.isFloat()) {
         Label notInt32, end;
-        branchTestInt32(Assembler::NotEqual, src, &notInt32);
+        asMasm().branchTestInt32(Assembler::NotEqual, src, &notInt32);
         convertInt32ToDouble(src.valueReg(), dest.fpu());
         ma_b(&end, ShortJump);
         bind(&notInt32);
         unboxDouble(src, dest.fpu());
         bind(&end);
     } else {
         unboxNonDouble(src, dest.gpr());
     }
@@ -1844,17 +1813,17 @@ MacroAssemblerMIPS64Compat::loadConstant
 
 void
 MacroAssemblerMIPS64Compat::loadInt32OrDouble(const Address& src, FloatRegister dest)
 {
     Label notInt32, end;
     // If it's an int, convert it to double.
     loadPtr(Address(src.base, src.offset), ScratchRegister);
     ma_dsrl(SecondScratchReg, ScratchRegister, Imm32(JSVAL_TAG_SHIFT));
-    branchTestInt32(Assembler::NotEqual, SecondScratchReg, &notInt32);
+    asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, &notInt32);
     loadPtr(Address(src.base, src.offset), SecondScratchReg);
     convertInt32ToDouble(SecondScratchReg, dest);
     ma_b(&end, ShortJump);
 
     // Not an int, just load as double.
     bind(&notInt32);
     ma_ld(dest, src);
     bind(&end);
@@ -1865,17 +1834,17 @@ MacroAssemblerMIPS64Compat::loadInt32OrD
 {
     Label notInt32, end;
 
     // If it's an int, convert it to double.
     computeScaledAddress(addr, SecondScratchReg);
     // Since we only have one scratch, we need to stomp over it with the tag.
     loadPtr(Address(SecondScratchReg, 0), ScratchRegister);
     ma_dsrl(SecondScratchReg, ScratchRegister, Imm32(JSVAL_TAG_SHIFT));
-    branchTestInt32(Assembler::NotEqual, SecondScratchReg, &notInt32);
+    asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, &notInt32);
 
     computeScaledAddress(addr, SecondScratchReg);
     loadPtr(Address(SecondScratchReg, 0), SecondScratchReg);
     convertInt32ToDouble(SecondScratchReg, dest);
     ma_b(&end, ShortJump);
 
     // Not an int, just load as double.
     bind(&notInt32);
@@ -1888,23 +1857,16 @@ MacroAssemblerMIPS64Compat::loadInt32OrD
 
 void
 MacroAssemblerMIPS64Compat::loadConstantDouble(double dp, FloatRegister dest)
 {
     ma_lid(dest, dp);
 }
 
 void
-MacroAssemblerMIPS64Compat::branchTestInt32Truthy(bool b, const ValueOperand& value, Label* label)
-{
-    ma_dext(ScratchRegister, value.valueReg(), Imm32(0), Imm32(32));
-    ma_b(ScratchRegister, ScratchRegister, label, b ? NonZero : Zero);
-}
-
-void
 MacroAssemblerMIPS64Compat::branchTestStringTruthy(bool b, const ValueOperand& value, Label* label)
 {
     unboxString(value, SecondScratchReg);
     load32(Address(SecondScratchReg, JSString::offsetOfLength()), SecondScratchReg);
     ma_b(SecondScratchReg, Imm32(0), label, b ? NotEqual : Equal);
 }
 
 void
@@ -2208,17 +2170,17 @@ MacroAssemblerMIPS64Compat::breakpoint()
 
 void
 MacroAssemblerMIPS64Compat::ensureDouble(const ValueOperand& source, FloatRegister dest,
                                          Label* failure)
 {
     Label isDouble, done;
     Register tag = splitTagForTest(source);
     branchTestDouble(Assembler::Equal, tag, &isDouble);
-    branchTestInt32(Assembler::NotEqual, tag, failure);
+    asMasm().branchTestInt32(Assembler::NotEqual, tag, failure);
 
     unboxInt32(source, ScratchRegister);
     convertInt32ToDouble(ScratchRegister, dest);
     jump(&done);
 
     bind(&isDouble);
     unboxDouble(source, dest);
 
@@ -2495,47 +2457,16 @@ MacroAssemblerMIPS64Compat::toggledCall(
         as_nop();
         as_nop();
     }
     MOZ_ASSERT_IF(!oom(), nextOffset().getOffset() - offset.offset() == ToggledCallSize(nullptr));
     return offset;
 }
 
 void
-MacroAssemblerMIPS64Compat::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp,
-                                                    Label* label)
-{
-    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
-    MOZ_ASSERT(ptr != temp);
-    MOZ_ASSERT(ptr != SecondScratchReg);
-
-    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
-    movePtr(ImmWord(-ptrdiff_t(nursery.start())), SecondScratchReg);
-    asMasm().addPtr(ptr, SecondScratchReg);
-    asMasm().branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
-                       SecondScratchReg, Imm32(nursery.nurserySize()), label);
-}
-
-void
-MacroAssemblerMIPS64Compat::branchValueIsNurseryObject(Condition cond, ValueOperand value,
-                                                       Register temp, Label* label)
-{
-    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
-
-    // 'Value' representing the start of the nursery tagged as a JSObject
-    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
-    Value start = ObjectValue(*reinterpret_cast<JSObject *>(nursery.start()));
-
-    movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), SecondScratchReg);
-    asMasm().addPtr(value.valueReg(), SecondScratchReg);
-    asMasm().branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
-                       SecondScratchReg, Imm32(nursery.nurserySize()), label);
-}
-
-void
 MacroAssemblerMIPS64Compat::profilerEnterFrame(Register framePtr, Register scratch)
 {
     AbsoluteAddress activation(GetJitContext()->runtime->addressOfProfilingActivation());
     loadPtr(activation, scratch);
     storePtr(framePtr, Address(scratch, JitActivation::offsetOfLastProfilingFrame()));
     storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
 }
 
@@ -2693,9 +2624,28 @@ MacroAssembler::callWithABINoProfiler(co
     // Load the callee in t9, as above.
     loadPtr(Address(fun.base, fun.offset), t9);
     uint32_t stackAdjust;
     callWithABIPre(&stackAdjust);
     call(t9);
     callWithABIPost(stackAdjust, result);
 }
 
+// ===============================================================
+// Branch functions
+
+void
+MacroAssembler::branchValueIsNurseryObject(Condition cond, ValueOperand value,
+                                           Register temp, Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+
+    // 'Value' representing the start of the nursery tagged as a JSObject
+    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
+    Value start = ObjectValue(*reinterpret_cast<JSObject *>(nursery.start()));
+
+    movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), SecondScratchReg);
+    addPtr(value.valueReg(), SecondScratchReg);
+    branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
+              SecondScratchReg, Imm32(nursery.nurserySize()), label);
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/mips64/MacroAssembler-mips64.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64.h
@@ -411,21 +411,16 @@ class MacroAssemblerMIPS64Compat : publi
     void loadInt32OrDouble(const Address& src, FloatRegister dest);
     void loadInt32OrDouble(const BaseIndex& addr, FloatRegister dest);
     void loadConstantDouble(double dp, FloatRegister dest);
 
     void boolValueToFloat32(const ValueOperand& operand, FloatRegister dest);
     void int32ValueToFloat32(const ValueOperand& operand, FloatRegister dest);
     void loadConstantFloat32(float f, FloatRegister dest);
 
-    void branchTestInt32(Condition cond, const ValueOperand& value, Label* label);
-    void branchTestInt32(Condition cond, Register tag, Label* label);
-    void branchTestInt32(Condition cond, const Address& address, Label* label);
-    void branchTestInt32(Condition cond, const BaseIndex& src, Label* label);
-
     void branchTestBoolean(Condition cond, const ValueOperand& value, Label* label);
     void branchTestBoolean(Condition cond, Register tag, Label* label);
     void branchTestBoolean(Condition cond, const Address& address, Label* label);
     void branchTestBoolean(Condition cond, const BaseIndex& src, Label* label);
 
 
     void branchTestDouble(Condition cond, const ValueOperand& value, Label* label);
     void branchTestDouble(Condition cond, Register tag, Label* label);
@@ -468,58 +463,33 @@ class MacroAssemblerMIPS64Compat : publi
     void branchTestMagic(Condition cond, const BaseIndex& src, Label* label);
 
     void branchTestMagicValue(Condition cond, const ValueOperand& val, JSWhyMagic why,
                               Label* label) {
         MOZ_ASSERT(cond == Equal || cond == NotEqual);
         branchTestValue(cond, val, MagicValue(why), label);
     }
 
-    void branchTestInt32Truthy(bool b, const ValueOperand& value, Label* label);
-
     void branchTestStringTruthy(bool b, const ValueOperand& value, Label* label);
 
     void branchTestDoubleTruthy(bool b, FloatRegister value, Label* label);
 
     void branchTestBooleanTruthy(bool b, const ValueOperand& operand, Label* label);
 
-    inline void decBranchPtr(Condition cond, Register lhs, Imm32 imm, Label* label);
-
     // higher level tag testing code
     Address ToPayload(Address value) {
         return value;
     }
 
     void moveValue(const Value& val, Register dest);
 
     CodeOffsetJump backedgeJump(RepatchLabel* label, Label* documentation = nullptr);
     CodeOffsetJump jumpWithPatch(RepatchLabel* label, Label* documentation = nullptr);
 
     template <typename T>
-    CodeOffsetJump branchPtrWithPatch(Condition cond, Register reg, T ptr, RepatchLabel* label) {
-        movePtr(ptr, ScratchRegister);
-        Label skipJump;
-        ma_b(reg, ScratchRegister, &skipJump, InvertCondition(cond), ShortJump);
-        CodeOffsetJump off = jumpWithPatch(label);
-        bind(&skipJump);
-        return off;
-    }
-
-    template <typename T>
-    CodeOffsetJump branchPtrWithPatch(Condition cond, Address addr, T ptr, RepatchLabel* label) {
-        loadPtr(addr, SecondScratchReg);
-        movePtr(ptr, ScratchRegister);
-        Label skipJump;
-        ma_b(SecondScratchReg, ScratchRegister, &skipJump, InvertCondition(cond), ShortJump);
-        CodeOffsetJump off = jumpWithPatch(label);
-        bind(&skipJump);
-        return off;
-    }
-
-    template <typename T>
     void loadUnboxedValue(const T& address, MIRType type, AnyRegister dest) {
         if (dest.isFloat())
             loadInt32OrDouble(address, dest.fpu());
         else if (type == MIRType_Int32)
             unboxInt32(address, dest.gpr());
         else if (type == MIRType_Boolean)
             unboxBoolean(address, dest.gpr());
         else
@@ -917,42 +887,16 @@ class MacroAssemblerMIPS64Compat : publi
 
     template<typename T>
     void atomicExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem, Register value,
                                        Register temp, Register valueTemp, Register offsetTemp, Register maskTemp,
                                        AnyRegister output);
 
     inline void incrementInt32Value(const Address& addr);
 
-    template <typename T>
-    void branchAdd32(Condition cond, T src, Register dest, Label* overflow) {
-        switch (cond) {
-          case Overflow:
-            ma_addTestOverflow(dest, dest, src, overflow);
-            break;
-          default:
-            MOZ_CRASH("NYI");
-        }
-    }
-    template <typename T>
-    void branchSub32(Condition cond, T src, Register dest, Label* overflow) {
-        switch (cond) {
-          case Overflow:
-            ma_subTestOverflow(dest, dest, src, overflow);
-            break;
-          case NonZero:
-          case Zero:
-            ma_subu(dest, src);
-            ma_b(dest, dest, overflow, cond);
-            break;
-          default:
-            MOZ_CRASH("NYI");
-        }
-    }
-
     void move32(Imm32 imm, Register dest);
     void move32(Register src, Register dest);
 
     void movePtr(Register src, Register dest);
     void movePtr(ImmWord imm, Register dest);
     void movePtr(ImmPtr imm, Register dest);
     void movePtr(wasm::SymbolicAddress imm, Register dest);
     void movePtr(ImmGCPtr imm, Register dest);
@@ -1135,20 +1079,16 @@ class MacroAssemblerMIPS64Compat : publi
         ma_liPatchable(bounded, ImmWord(0));
         return bo;
     }
 
     void moveFloat32(FloatRegister src, FloatRegister dest) {
         as_movs(dest, src);
     }
 
-    void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label);
-    void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp,
-                                    Label* label);
-
     void loadWasmActivation(Register dest) {
         loadPtr(Address(GlobalReg, wasm::ActivationGlobalDataOffset - AsmJSGlobalRegBias), dest);
     }
     void loadAsmJSHeapRegisterFromGlobalData() {
         MOZ_ASSERT(Imm16::IsInSignedRange(wasm::HeapGlobalDataOffset - AsmJSGlobalRegBias));
         loadPtr(Address(GlobalReg, wasm::HeapGlobalDataOffset - AsmJSGlobalRegBias), HeapReg);
     }
 
--- a/js/src/jit/mips64/Simulator-mips64.cpp
+++ b/js/src/jit/mips64/Simulator-mips64.cpp
@@ -42,17 +42,17 @@
 #define I8(v)   static_cast<int8_t>(v)
 #define I16(v)  static_cast<int16_t>(v)
 #define U16(v)  static_cast<uint16_t>(v)
 #define I32(v)  static_cast<int32_t>(v)
 #define U32(v)  static_cast<uint32_t>(v)
 #define I64(v)  static_cast<int64_t>(v)
 #define U64(v)  static_cast<uint64_t>(v)
 #define I128(v) static_cast<__int128_t>(v)
-#define U128(v) static_cast<unsigned __int128_t>(v)
+#define U128(v) static_cast<__uint128_t>(v)
 
 namespace js {
 namespace jit {
 
 static const Instr kCallRedirInstr = op_special | MAX_BREAK_CODE << FunctionBits | ff_break;
 
 // Utils functions.
 static uint32_t
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -228,30 +228,25 @@ class MacroAssemblerNone : public Assemb
     template <typename T> void Push(T) { MOZ_CRASH(); }
     template <typename T> void pop(T) { MOZ_CRASH(); }
     template <typename T> void Pop(T) { MOZ_CRASH(); }
     template <typename T> CodeOffset pushWithPatch(T) { MOZ_CRASH(); }
 
     CodeOffsetJump jumpWithPatch(RepatchLabel*, Label* doc = nullptr) { MOZ_CRASH(); }
     CodeOffsetJump jumpWithPatch(RepatchLabel*, Condition, Label* doc = nullptr) { MOZ_CRASH(); }
     CodeOffsetJump backedgeJump(RepatchLabel* label, Label* doc = nullptr) { MOZ_CRASH(); }
-    template <typename T, typename S>
-    CodeOffsetJump branchPtrWithPatch(Condition, T, S, RepatchLabel*) { MOZ_CRASH(); }
 
     template <typename T, typename S> void branchTestValue(Condition, T, S, Label*) { MOZ_CRASH(); }
     void testNullSet(Condition, ValueOperand, Register) { MOZ_CRASH(); }
     void testObjectSet(Condition, ValueOperand, Register) { MOZ_CRASH(); }
     void testUndefinedSet(Condition, ValueOperand, Register) { MOZ_CRASH(); }
 
     template <typename T, typename S> void cmpPtrSet(Condition, T, S, Register) { MOZ_CRASH(); }
     template <typename T, typename S> void cmp32Set(Condition, T, S, Register) { MOZ_CRASH(); }
 
-    template <typename T, typename S> void branchAdd32(Condition, T, S, Label*) { MOZ_CRASH(); }
-    template <typename T, typename S> void branchSub32(Condition, T, S, Label*) { MOZ_CRASH(); }
-    template <typename T, typename S> void decBranchPtr(Condition, T, S, Label*) { MOZ_CRASH(); }
     template <typename T, typename S> void mov(T, S) { MOZ_CRASH(); }
     template <typename T, typename S> void movq(T, S) { MOZ_CRASH(); }
     template <typename T, typename S> void movePtr(T, S) { MOZ_CRASH(); }
     template <typename T, typename S> void move32(T, S) { MOZ_CRASH(); }
     template <typename T, typename S> void moveFloat32(T, S) { MOZ_CRASH(); }
     template <typename T, typename S> void moveDouble(T, S) { MOZ_CRASH(); }
     template <typename T, typename S> void move64(T, S) { MOZ_CRASH(); }
     template <typename T> CodeOffset movWithPatch(T, Register) { MOZ_CRASH(); }
@@ -346,17 +341,16 @@ class MacroAssemblerNone : public Assemb
     template <typename T, typename S> void atomicXor16(const T& value, const S& mem) { MOZ_CRASH(); }
     template <typename T, typename S> void atomicXor32(const T& value, const S& mem) { MOZ_CRASH(); }
 
     void clampIntToUint8(Register) { MOZ_CRASH(); }
 
     Register splitTagForTest(ValueOperand) { MOZ_CRASH(); }
 
     template <typename T> void branchTestUndefined(Condition, T, Label*) { MOZ_CRASH(); }
-    template <typename T> void branchTestInt32(Condition, T, Label*) { MOZ_CRASH(); }
     template <typename T> void branchTestBoolean(Condition, T, Label*) { MOZ_CRASH(); }
     template <typename T> void branchTestDouble(Condition, T, Label*) { MOZ_CRASH(); }
     template <typename T> void branchTestNull(Condition, T, Label*) { MOZ_CRASH(); }
     template <typename T> void branchTestString(Condition, T, Label*) { MOZ_CRASH(); }
     template <typename T> void branchTestSymbol(Condition, T, Label*) { MOZ_CRASH(); }
     template <typename T> void branchTestObject(Condition, T, Label*) { MOZ_CRASH(); }
     template <typename T> void branchTestNumber(Condition, T, Label*) { MOZ_CRASH(); }
     template <typename T> void branchTestGCThing(Condition, T, Label*) { MOZ_CRASH(); }
@@ -395,34 +389,30 @@ class MacroAssemblerNone : public Assemb
     void boolValueToFloat32(ValueOperand, FloatRegister) { MOZ_CRASH(); }
     void int32ValueToDouble(ValueOperand, FloatRegister) { MOZ_CRASH(); }
     void int32ValueToFloat32(ValueOperand, FloatRegister) { MOZ_CRASH(); }
 
     void loadConstantDouble(double, FloatRegister) { MOZ_CRASH(); }
     void loadConstantFloat32(float, FloatRegister) { MOZ_CRASH(); }
     Condition testInt32Truthy(bool, ValueOperand) { MOZ_CRASH(); }
     Condition testStringTruthy(bool, ValueOperand) { MOZ_CRASH(); }
-    void branchTestInt32Truthy(bool, ValueOperand, Label*) { MOZ_CRASH(); }
     void branchTestBooleanTruthy(bool, ValueOperand, Label*) { MOZ_CRASH(); }
     void branchTestStringTruthy(bool, ValueOperand, Label*) { MOZ_CRASH(); }
     void branchTestDoubleTruthy(bool, FloatRegister, Label*) { MOZ_CRASH(); }
 
     template <typename T> void loadUnboxedValue(T, MIRType, AnyRegister) { MOZ_CRASH(); }
     template <typename T> void storeUnboxedValue(ConstantOrRegister, MIRType, T, MIRType) { MOZ_CRASH(); }
     template <typename T> void storeUnboxedPayload(ValueOperand value, T, size_t) { MOZ_CRASH(); }
 
     void convertUInt32ToDouble(Register, FloatRegister) { MOZ_CRASH(); }
     void convertUInt32ToFloat32(Register, FloatRegister) { MOZ_CRASH(); }
     void incrementInt32Value(Address) { MOZ_CRASH(); }
     void ensureDouble(ValueOperand, FloatRegister, Label*) { MOZ_CRASH(); }
     void handleFailureWithHandlerTail(void*) { MOZ_CRASH(); }
 
-    void branchPtrInNurseryRange(Condition, Register, Register, Label*) { MOZ_CRASH(); }
-    void branchValueIsNurseryObject(Condition, ValueOperand, Register, Label*) { MOZ_CRASH(); }
-
     void buildFakeExitFrame(Register, uint32_t*) { MOZ_CRASH(); }
     bool buildOOLFakeExitFrame(void*) { MOZ_CRASH(); }
     void loadWasmActivation(Register) { MOZ_CRASH(); }
     void loadAsmJSHeapRegisterFromGlobalData() { MOZ_CRASH(); }
     void memIntToValue(Address, Address) { MOZ_CRASH(); }
 
     void setPrinter(Sprinter*) { MOZ_CRASH(); }
     Operand ToPayload(Operand base) { MOZ_CRASH(); }
--- a/js/src/jit/x64/MacroAssembler-x64-inl.h
+++ b/js/src/jit/x64/MacroAssembler-x64-inl.h
@@ -351,16 +351,46 @@ MacroAssembler::branchTest32(Condition c
 
 void
 MacroAssembler::branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp,
                              Label* label)
 {
     branchTestPtr(cond, lhs.reg, rhs.reg, label);
 }
 
+void
+MacroAssembler::branchTestInt32(Condition cond, Register tag, Label* label)
+{
+    cond = testInt32(cond, tag);
+    j(cond, label);
+}
+
+void
+MacroAssembler::branchTestInt32(Condition cond, const Address& address, Label* label)
+{
+    MOZ_ASSERT(cond == Equal || cond == NotEqual);
+    branchTestInt32Impl(cond, Operand(address), label);
+}
+
+void
+MacroAssembler::branchTestInt32(Condition cond, const BaseIndex& address, Label* label)
+{
+    ScratchRegisterScope scratch(*this);
+    splitTag(address, scratch);
+    branchTestInt32(cond, scratch, label);
+}
+
+void
+MacroAssembler::branchTestInt32(Condition cond, const ValueOperand& src, Label* label)
+{
+    ScratchRegisterScope scratch(*this);
+    splitTag(src, scratch);
+    branchTestInt32(cond, scratch, label);
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 void
 MacroAssemblerX64::incrementInt32Value(const Address& addr)
 {
     asMasm().addPtr(Imm32(1), addr);
 }
@@ -376,12 +406,61 @@ MacroAssemblerX64::branchTestValue(Condi
 template <typename T, typename S>
 void
 MacroAssemblerX64::branchPtrImpl(Condition cond, const T& lhs, const S& rhs, Label* label)
 {
     cmpPtr(Operand(lhs), rhs);
     j(cond, label);
 }
 
+void
+MacroAssemblerX64::unboxValue(const ValueOperand& src, AnyRegister dest)
+{
+    if (dest.isFloat()) {
+        Label notInt32, end;
+        asMasm().branchTestInt32(Assembler::NotEqual, src, &notInt32);
+        convertInt32ToDouble(src.valueReg(), dest.fpu());
+        jump(&end);
+        bind(&notInt32);
+        unboxDouble(src, dest.fpu());
+        bind(&end);
+    } else {
+        unboxNonDouble(src, dest.gpr());
+    }
+}
+
+void
+MacroAssemblerX64::loadInt32OrDouble(const Operand& operand, FloatRegister dest)
+{
+    Label notInt32, end;
+    branchTestInt32Impl(Assembler::NotEqual, operand, &notInt32);
+    convertInt32ToDouble(operand, dest);
+    jump(&end);
+    bind(&notInt32);
+    loadDouble(operand, dest);
+    bind(&end);
+}
+
+// If source is a double, load it into dest. If source is int32,
+// convert it to double. Else, branch to failure.
+void
+MacroAssemblerX64::ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure)
+{
+    Label isDouble, done;
+    Register tag = splitTagForTest(source);
+    branchTestDouble(Assembler::Equal, tag, &isDouble);
+    asMasm().branchTestInt32(Assembler::NotEqual, tag, failure);
+
+    ScratchRegisterScope scratch(asMasm());
+    unboxInt32(source, scratch);
+    convertInt32ToDouble(scratch, dest);
+    jump(&done);
+
+    bind(&isDouble);
+    unboxDouble(source, dest);
+
+    bind(&done);
+}
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_MacroAssembler_x64_inl_h */
--- a/js/src/jit/x64/MacroAssembler-x64.cpp
+++ b/js/src/jit/x64/MacroAssembler-x64.cpp
@@ -260,54 +260,16 @@ template void
 MacroAssemblerX64::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest,
                                      MIRType slotType);
 
 template void
 MacroAssemblerX64::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest,
                                      MIRType slotType);
 
 void
-MacroAssemblerX64::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label)
-{
-    ScratchRegisterScope scratch(asMasm());
-
-    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
-    MOZ_ASSERT(ptr != temp);
-    MOZ_ASSERT(ptr != scratch);
-
-    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
-    movePtr(ImmWord(-ptrdiff_t(nursery.start())), scratch);
-    asMasm().addPtr(ptr, scratch);
-    asMasm().branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
-                       scratch, Imm32(nursery.nurserySize()), label);
-}
-
-void
-MacroAssemblerX64::branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp,
-                                              Label* label)
-{
-    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
-
-    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
-
-    // Avoid creating a bogus ObjectValue below.
-    if (!nursery.exists())
-        return;
-
-    // 'Value' representing the start of the nursery tagged as a JSObject
-    Value start = ObjectValue(*reinterpret_cast<JSObject*>(nursery.start()));
-
-    ScratchRegisterScope scratch(asMasm());
-    movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), scratch);
-    asMasm().addPtr(value.valueReg(), scratch);
-    asMasm().branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
-                       scratch, Imm32(nursery.nurserySize()), label);
-}
-
-void
 MacroAssemblerX64::profilerEnterFrame(Register framePtr, Register scratch)
 {
     AbsoluteAddress activation(GetJitContext()->runtime->addressOfProfilingActivation());
     loadPtr(activation, scratch);
     storePtr(framePtr, Address(scratch, JitActivation::offsetOfLastProfilingFrame()));
     storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
 }
 
@@ -459,9 +421,50 @@ MacroAssembler::callWithABINoProfiler(co
     MOZ_ASSERT(!IsIntArgReg(safeFun.base));
 
     uint32_t stackAdjust;
     callWithABIPre(&stackAdjust);
     call(safeFun);
     callWithABIPost(stackAdjust, result);
 }
 
+// ===============================================================
+// Branch functions
+
+void
+MacroAssembler::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label)
+{
+    ScratchRegisterScope scratch(*this);
+
+    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+    MOZ_ASSERT(ptr != temp);
+    MOZ_ASSERT(ptr != scratch);
+
+    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
+    movePtr(ImmWord(-ptrdiff_t(nursery.start())), scratch);
+    addPtr(ptr, scratch);
+    branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
+              scratch, Imm32(nursery.nurserySize()), label);
+}
+
+void
+MacroAssembler::branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp,
+                                           Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+
+    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
+
+    // Avoid creating a bogus ObjectValue below.
+    if (!nursery.exists())
+        return;
+
+    // 'Value' representing the start of the nursery tagged as a JSObject
+    Value start = ObjectValue(*reinterpret_cast<JSObject*>(nursery.start()));
+
+    ScratchRegisterScope scratch(*this);
+    movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), scratch);
+    addPtr(value.valueReg(), scratch);
+    branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
+              scratch, Imm32(nursery.nurserySize()), label);
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -540,26 +540,16 @@ class MacroAssemblerX64 : public MacroAs
         JmpSrc src = jSrc(cond, label);
         return CodeOffsetJump(size(), addPatchableJump(src, Relocation::HARDCODED));
     }
 
     CodeOffsetJump backedgeJump(RepatchLabel* label, Label* documentation = nullptr) {
         return jumpWithPatch(label);
     }
 
-    template <typename S, typename T>
-    CodeOffsetJump branchPtrWithPatch(Condition cond, S lhs, T ptr, RepatchLabel* label) {
-        cmpPtr(lhs, ptr);
-        return jumpWithPatch(label, cond);
-    }
-    void decBranchPtr(Condition cond, Register lhs, Imm32 imm, Label* label) {
-        subq(imm, lhs);
-        j(cond, label);
-    }
-
     void movePtr(Register src, Register dest) {
         movq(src, dest);
     }
     void movePtr(Register src, const Operand& dest) {
         movq(src, dest);
     }
     void movePtr(ImmWord imm, Register dest) {
         mov(imm, dest);
@@ -686,20 +676,16 @@ class MacroAssemblerX64 : public MacroAs
         Register reg = splitTagForTest(operand);
         cmp32(reg, tag);
     }
 
     void branchTestUndefined(Condition cond, Register tag, Label* label) {
         cond = testUndefined(cond, tag);
         j(cond, label);
     }
-    void branchTestInt32(Condition cond, Register tag, Label* label) {
-        cond = testInt32(cond, tag);
-        j(cond, label);
-    }
     void branchTestDouble(Condition cond, Register tag, Label* label) {
         cond = testDouble(cond, tag);
         j(cond, label);
     }
     void branchTestBoolean(Condition cond, Register tag, Label* label) {
         cond = testBoolean(cond, tag);
         j(cond, label);
     }
@@ -731,25 +717,21 @@ class MacroAssemblerX64 : public MacroAs
         MOZ_ASSERT(cond == Equal || cond == NotEqual);
         cmp32(ToUpper32(operand), Imm32(Upper32Of(GetShiftedTag(JSVAL_TYPE_UNDEFINED))));
         j(cond, label);
     }
     void branchTestUndefined(Condition cond, const Address& address, Label* label) {
         MOZ_ASSERT(cond == Equal || cond == NotEqual);
         branchTestUndefined(cond, Operand(address), label);
     }
-    void branchTestInt32(Condition cond, const Operand& operand, Label* label) {
+    void branchTestInt32Impl(Condition cond, const Operand& operand, Label* label) {
         MOZ_ASSERT(cond == Equal || cond == NotEqual);
         cmp32(ToUpper32(operand), Imm32(Upper32Of(GetShiftedTag(JSVAL_TYPE_INT32))));
         j(cond, label);
     }
-    void branchTestInt32(Condition cond, const Address& address, Label* label) {
-        MOZ_ASSERT(cond == Equal || cond == NotEqual);
-        branchTestInt32(cond, Operand(address), label);
-    }
     void branchTestDouble(Condition cond, const Operand& operand, Label* label) {
         MOZ_ASSERT(cond == Equal || cond == NotEqual);
         ScratchRegisterScope scratch(asMasm());
         splitTag(operand, scratch);
         branchTestDouble(cond, scratch, label);
     }
     void branchTestDouble(Condition cond, const Address& address, Label* label) {
         MOZ_ASSERT(cond == Equal || cond == NotEqual);
@@ -781,21 +763,16 @@ class MacroAssemblerX64 : public MacroAs
     }
 
     // Perform a type-test on a full Value loaded into a register.
     // Clobbers the ScratchReg.
     void branchTestUndefined(Condition cond, const ValueOperand& src, Label* label) {
         cond = testUndefined(cond, src);
         j(cond, label);
     }
-    void branchTestInt32(Condition cond, const ValueOperand& src, Label* label) {
-        ScratchRegisterScope scratch(asMasm());
-        splitTag(src, scratch);
-        branchTestInt32(cond, scratch, label);
-    }
     void branchTestBoolean(Condition cond, const ValueOperand& src, Label* label) {
         ScratchRegisterScope scratch(asMasm());
         splitTag(src, scratch);
         branchTestBoolean(cond, scratch, label);
     }
     void branchTestDouble(Condition cond, const ValueOperand& src, Label* label) {
         cond = testDouble(cond, src);
         j(cond, label);
@@ -822,21 +799,16 @@ class MacroAssemblerX64 : public MacroAs
     }
 
     // Perform a type-test on a Value addressed by BaseIndex.
     // Clobbers the ScratchReg.
     void branchTestUndefined(Condition cond, const BaseIndex& address, Label* label) {
         cond = testUndefined(cond, address);
         j(cond, label);
     }
-    void branchTestInt32(Condition cond, const BaseIndex& address, Label* label) {
-        ScratchRegisterScope scratch(asMasm());
-        splitTag(address, scratch);
-        branchTestInt32(cond, scratch, label);
-    }
     void branchTestBoolean(Condition cond, const BaseIndex& address, Label* label) {
         ScratchRegisterScope scratch(asMasm());
         splitTag(address, scratch);
         branchTestBoolean(cond, scratch, label);
     }
     void branchTestDouble(Condition cond, const BaseIndex& address, Label* label) {
         cond = testDouble(cond, address);
         j(cond, label);
@@ -1041,29 +1013,17 @@ class MacroAssemblerX64 : public MacroAs
         return scratch;
     }
     Register extractTag(const ValueOperand& value, Register scratch) {
         MOZ_ASSERT(scratch != ScratchReg);
         splitTag(value, scratch);
         return scratch;
     }
 
-    void unboxValue(const ValueOperand& src, AnyRegister dest) {
-        if (dest.isFloat()) {
-            Label notInt32, end;
-            branchTestInt32(Assembler::NotEqual, src, &notInt32);
-            convertInt32ToDouble(src.valueReg(), dest.fpu());
-            jump(&end);
-            bind(&notInt32);
-            unboxDouble(src, dest.fpu());
-            bind(&end);
-        } else {
-            unboxNonDouble(src, dest.gpr());
-        }
-    }
+    inline void unboxValue(const ValueOperand& src, AnyRegister dest);
 
     // These two functions use the low 32-bits of the full value register.
     void boolValueToDouble(const ValueOperand& operand, FloatRegister dest) {
         convertInt32ToDouble(operand.valueReg(), dest);
     }
     void int32ValueToDouble(const ValueOperand& operand, FloatRegister dest) {
         convertInt32ToDouble(operand.valueReg(), dest);
     }
@@ -1079,44 +1039,32 @@ class MacroAssemblerX64 : public MacroAs
     void loadConstantFloat32(float f, FloatRegister dest);
     void loadConstantInt32x4(const SimdConstant& v, FloatRegister dest);
     void loadConstantFloat32x4(const SimdConstant& v, FloatRegister dest);
 
     Condition testInt32Truthy(bool truthy, const ValueOperand& operand) {
         test32(operand.valueReg(), operand.valueReg());
         return truthy ? NonZero : Zero;
     }
-    void branchTestInt32Truthy(bool truthy, const ValueOperand& operand, Label* label) {
-        Condition cond = testInt32Truthy(truthy, operand);
-        j(cond, label);
-    }
     void branchTestBooleanTruthy(bool truthy, const ValueOperand& operand, Label* label) {
         test32(operand.valueReg(), operand.valueReg());
         j(truthy ? NonZero : Zero, label);
     }
     Condition testStringTruthy(bool truthy, const ValueOperand& value) {
         ScratchRegisterScope scratch(asMasm());
         unboxString(value, scratch);
         cmp32(Operand(scratch, JSString::offsetOfLength()), Imm32(0));
         return truthy ? Assembler::NotEqual : Assembler::Equal;
     }
     void branchTestStringTruthy(bool truthy, const ValueOperand& value, Label* label) {
         Condition cond = testStringTruthy(truthy, value);
         j(cond, label);
     }
 
-    void loadInt32OrDouble(const Operand& operand, FloatRegister dest) {
-        Label notInt32, end;
-        branchTestInt32(Assembler::NotEqual, operand, &notInt32);
-        convertInt32ToDouble(operand, dest);
-        jump(&end);
-        bind(&notInt32);
-        loadDouble(operand, dest);
-        bind(&end);
-    }
+    inline void loadInt32OrDouble(const Operand& operand, FloatRegister dest);
 
     template <typename T>
     void loadUnboxedValue(const T& src, MIRType type, AnyRegister dest) {
         if (dest.isFloat())
             loadInt32OrDouble(Operand(src), dest.fpu());
         else if (type == MIRType_Int32 || type == MIRType_Boolean)
             movl(Operand(src), dest.gpr());
         else
@@ -1158,34 +1106,17 @@ class MacroAssemblerX64 : public MacroAs
     }
 
     void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest) {
         vcvtsi2sdq(src.reg, dest);
     }
 
     inline void incrementInt32Value(const Address& addr);
 
-    // If source is a double, load it into dest. If source is int32,
-    // convert it to double. Else, branch to failure.
-    void ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure) {
-        Label isDouble, done;
-        Register tag = splitTagForTest(source);
-        branchTestDouble(Assembler::Equal, tag, &isDouble);
-        branchTestInt32(Assembler::NotEqual, tag, failure);
-
-        ScratchRegisterScope scratch(asMasm());
-        unboxInt32(source, scratch);
-        convertInt32ToDouble(scratch, dest);
-        jump(&done);
-
-        bind(&isDouble);
-        unboxDouble(source, dest);
-
-        bind(&done);
-    }
+    inline void ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure);
 
   public:
     void handleFailureWithHandlerTail(void* handler);
 
     // See CodeGeneratorX64 calls to noteAsmJSGlobalAccess.
     void patchAsmJSGlobalAccess(CodeOffset patchAt, uint8_t* code, uint8_t* globalData,
                                 unsigned globalDataOffset)
     {
@@ -1195,19 +1126,16 @@ class MacroAssemblerX64 : public MacroAs
         ((int32_t*)nextInsn)[-1] = target - nextInsn;
     }
     void memIntToValue(Address Source, Address Dest) {
         ScratchRegisterScope scratch(asMasm());
         load32(Source, scratch);
         storeValue(JSVAL_TYPE_INT32, scratch, Dest);
     }
 
-    void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label);
-    void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label);
-
     // Instrumentation for entering and leaving the profiler.
     void profilerEnterFrame(Register framePtr, Register scratch);
     void profilerExitFrame();
 };
 
 typedef MacroAssemblerX64 MacroAssemblerSpecific;
 
 } // namespace jit
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
@@ -290,16 +290,32 @@ MacroAssembler::branchPtr(Condition cond
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmWord rhs, Label* label)
 {
     branchPtrImpl(cond, lhs, rhs, label);
 }
 
+template <typename T>
+CodeOffsetJump
+MacroAssembler::branchPtrWithPatch(Condition cond, Register lhs, T rhs, RepatchLabel* label)
+{
+    cmpPtr(lhs, rhs);
+    return jumpWithPatch(label, cond);
+}
+
+template <typename T>
+CodeOffsetJump
+MacroAssembler::branchPtrWithPatch(Condition cond, Address lhs, T rhs, RepatchLabel* label)
+{
+    cmpPtr(lhs, rhs);
+    return jumpWithPatch(label, cond);
+}
+
 void
 MacroAssembler::branchFloat(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                             Label* label)
 {
     compareFloat(cond, lhs, rhs);
 
     if (cond == DoubleEqual) {
         Label unordered;
@@ -337,16 +353,39 @@ MacroAssembler::branchDouble(DoubleCondi
         j(Parity, label);
         return;
     }
 
     MOZ_ASSERT(!(cond & DoubleConditionBitSpecial));
     j(ConditionFromDoubleCondition(cond), label);
 }
 
+template <typename T>
+void
+MacroAssembler::branchAdd32(Condition cond, T src, Register dest, Label* label)
+{
+    addl(src, dest);
+    j(cond, label);
+}
+
+template <typename T>
+void
+MacroAssembler::branchSub32(Condition cond, T src, Register dest, Label* label)
+{
+    subl(src, dest);
+    j(cond, label);
+}
+
+void
+MacroAssembler::decBranchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label)
+{
+    subPtr(rhs, lhs);
+    j(cond, label);
+}
+
 template <class L>
 void
 MacroAssembler::branchTest32(Condition cond, Register lhs, Register rhs, L label)
 {
     MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
     test32(lhs, rhs);
     j(cond, label);
 }
@@ -384,16 +423,23 @@ MacroAssembler::branchTestPtr(Condition 
 
 void
 MacroAssembler::branchTestPtr(Condition cond, const Address& lhs, Imm32 rhs, Label* label)
 {
     testPtr(Operand(lhs), rhs);
     j(cond, label);
 }
 
+void
+MacroAssembler::branchTestInt32Truthy(bool truthy, const ValueOperand& operand, Label* label)
+{
+    Condition cond = testInt32Truthy(truthy, operand);
+    j(cond, label);
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 void
 MacroAssemblerX86Shared::clampIntToUint8(Register reg)
 {
     Label inRange;
     asMasm().branchTest32(Assembler::Zero, reg, Imm32(0xffffff00), &inRange);
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h
@@ -160,26 +160,16 @@ class MacroAssemblerX86Shared : public A
         cmpl(rhs, lhs);
     }
     void cmp32(Register lhs, const Operand& rhs) {
         cmpl(rhs, lhs);
     }
     CodeOffset cmp32WithPatch(Register lhs, Imm32 rhs) {
         return cmplWithPatch(rhs, lhs);
     }
-    template <typename T>
-    void branchAdd32(Condition cond, T src, Register dest, Label* label) {
-        addl(src, dest);
-        j(cond, label);
-    }
-    template <typename T>
-    void branchSub32(Condition cond, T src, Register dest, Label* label) {
-        subl(src, dest);
-        j(cond, label);
-    }
     void atomic_inc32(const Operand& addr) {
         lock_incl(addr);
     }
     void atomic_dec32(const Operand& addr) {
         lock_decl(addr);
     }
 
     template <typename T>
--- a/js/src/jit/x86/MacroAssembler-x86-inl.h
+++ b/js/src/jit/x86/MacroAssembler-x86-inl.h
@@ -360,16 +360,40 @@ MacroAssembler::branchTest64(Condition c
         movl(lhs.low, temp);
         orl(lhs.high, temp);
         branchTestPtr(cond, temp, temp, label);
     } else {
         MOZ_CRASH("Unsupported condition");
     }
 }
 
+void
+MacroAssembler::branchTestInt32(Condition cond, Register tag, Label* label)
+{
+    branchTestInt32Impl(cond, tag, label);
+}
+
+void
+MacroAssembler::branchTestInt32(Condition cond, const Address& address, Label* label)
+{
+    branchTestInt32Impl(cond, address, label);
+}
+
+void
+MacroAssembler::branchTestInt32(Condition cond, const BaseIndex& address, Label* label)
+{
+    branchTestInt32Impl(cond, address, label);
+}
+
+void
+MacroAssembler::branchTestInt32(Condition cond, const ValueOperand& src, Label* label)
+{
+    branchTestInt32Impl(cond, src, label);
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 // Note: this function clobbers the source register.
 void
 MacroAssemblerX86::convertUInt32ToDouble(Register src, FloatRegister dest)
 {
     // src is [0, 2^32-1]
@@ -411,12 +435,70 @@ MacroAssemblerX86::branchTestValue(Condi
 template <typename T, typename S>
 void
 MacroAssemblerX86::branchPtrImpl(Condition cond, const T& lhs, const S& rhs, Label* label)
 {
     cmpPtr(Operand(lhs), rhs);
     j(cond, label);
 }
 
+void
+MacroAssemblerX86::unboxValue(const ValueOperand& src, AnyRegister dest)
+{
+    if (dest.isFloat()) {
+        Label notInt32, end;
+        asMasm().branchTestInt32(Assembler::NotEqual, src, &notInt32);
+        convertInt32ToDouble(src.payloadReg(), dest.fpu());
+        jump(&end);
+        bind(&notInt32);
+        unboxDouble(src, dest.fpu());
+        bind(&end);
+    } else {
+        if (src.payloadReg() != dest.gpr())
+            movl(src.payloadReg(), dest.gpr());
+    }
+}
+
+template <typename T>
+void
+MacroAssemblerX86::loadInt32OrDouble(const T& src, FloatRegister dest)
+{
+    Label notInt32, end;
+    asMasm().branchTestInt32(Assembler::NotEqual, src, &notInt32);
+    convertInt32ToDouble(ToPayload(src), dest);
+    jump(&end);
+    bind(&notInt32);
+    loadDouble(src, dest);
+    bind(&end);
+}
+
+template <typename T>
+void
+MacroAssemblerX86::loadUnboxedValue(const T& src, MIRType type, AnyRegister dest)
+{
+    if (dest.isFloat())
+        loadInt32OrDouble(src, dest.fpu());
+    else
+        movl(Operand(src), dest.gpr());
+}
+
+// If source is a double, load it into dest. If source is int32,
+// convert it to double. Else, branch to failure.
+void
+MacroAssemblerX86::ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure)
+{
+    Label isDouble, done;
+    branchTestDouble(Assembler::Equal, source.typeReg(), &isDouble);
+    asMasm().branchTestInt32(Assembler::NotEqual, source.typeReg(), failure);
+
+    convertInt32ToDouble(source.payloadReg(), dest);
+    jump(&done);
+
+    bind(&isDouble);
+    unboxDouble(source, dest);
+
+    bind(&done);
+}
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x86_MacroAssembler_x86_inl_h */
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -324,45 +324,16 @@ template void
 MacroAssemblerX86::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest,
                                      MIRType slotType);
 
 template void
 MacroAssemblerX86::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest,
                                      MIRType slotType);
 
 void
-MacroAssemblerX86::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp,
-                                           Label* label)
-{
-    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
-    MOZ_ASSERT(ptr != temp);
-    MOZ_ASSERT(temp != InvalidReg);  // A temp register is required for x86.
-
-    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
-    movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
-    asMasm().addPtr(ptr, temp);
-    asMasm().branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
-                       temp, Imm32(nursery.nurserySize()), label);
-}
-
-void
-MacroAssemblerX86::branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp,
-                                              Label* label)
-{
-    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
-
-    Label done;
-
-    branchTestObject(Assembler::NotEqual, value, cond == Assembler::Equal ? &done : label);
-    branchPtrInNurseryRange(cond, value.payloadReg(), temp, label);
-
-    bind(&done);
-}
-
-void
 MacroAssemblerX86::profilerEnterFrame(Register framePtr, Register scratch)
 {
     AbsoluteAddress activation(GetJitContext()->runtime->addressOfProfilingActivation());
     loadPtr(activation, scratch);
     storePtr(framePtr, Address(scratch, JitActivation::offsetOfLastProfilingFrame()));
     storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
 }
 
@@ -493,9 +464,41 @@ void
 MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result)
 {
     uint32_t stackAdjust;
     callWithABIPre(&stackAdjust);
     call(fun);
     callWithABIPost(stackAdjust, result);
 }
 
+// ===============================================================
+// Branch functions
+
+void
+MacroAssembler::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp,
+                                        Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+    MOZ_ASSERT(ptr != temp);
+    MOZ_ASSERT(temp != InvalidReg);  // A temp register is required for x86.
+
+    const Nursery& nursery = GetJitContext()->runtime->gcNursery();
+    movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
+    addPtr(ptr, temp);
+    branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
+              temp, Imm32(nursery.nurserySize()), label);
+}
+
+void
+MacroAssembler::branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp,
+                                           Label* label)
+{
+    MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+
+    Label done;
+
+    branchTestObject(Assembler::NotEqual, value, cond == Assembler::Equal ? &done : label);
+    branchPtrInNurseryRange(cond, value.payloadReg(), temp, label);
+
+    bind(&done);
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -67,16 +67,19 @@ class MacroAssemblerX86 : public MacroAs
     /////////////////////////////////////////////////////////////////
 
     Operand ToPayload(Operand base) {
         return base;
     }
     Address ToPayload(Address base) {
         return base;
     }
+    BaseIndex ToPayload(BaseIndex base) {
+        return base;
+    }
     Operand ToType(Operand base) {
         switch (base.kind()) {
           case Operand::MEM_REG_DISP:
             return Operand(Register::FromCode(base.base()), base.disp() + sizeof(void*));
 
           case Operand::MEM_SCALE:
             return Operand(Register::FromCode(base.base()), Register::FromCode(base.index()),
                            base.scale(), base.disp() + sizeof(void*));
@@ -570,29 +573,20 @@ class MacroAssemblerX86 : public MacroAs
         j(cond, label);
         return CodeOffsetJump(size());
     }
 
     CodeOffsetJump backedgeJump(RepatchLabel* label, Label* documentation = nullptr) {
         return jumpWithPatch(label);
     }
 
-    template <typename S, typename T>
-    CodeOffsetJump branchPtrWithPatch(Condition cond, S lhs, T ptr, RepatchLabel* label) {
-        branchPtr(cond, lhs, ptr, label);
-        return CodeOffsetJump(size());
-    }
     void branchPtr(Condition cond, Register lhs, Register rhs, RepatchLabel* label) {
         cmpPtr(lhs, rhs);
         j(cond, label);
     }
-    void decBranchPtr(Condition cond, Register lhs, Imm32 imm, Label* label) {
-        subl(imm, lhs);
-        j(cond, label);
-    }
 
     void movePtr(ImmWord imm, Register dest) {
         movl(Imm32(imm.value), dest);
     }
     void movePtr(ImmPtr imm, Register dest) {
         movl(imm, dest);
     }
     void movePtr(wasm::SymbolicAddress imm, Register dest) {
@@ -662,17 +656,17 @@ class MacroAssemblerX86 : public MacroAs
     // Type testing instructions can take a tag in a register or a
     // ValueOperand.
     template <typename T>
     void branchTestUndefined(Condition cond, const T& t, Label* label) {
         cond = testUndefined(cond, t);
         j(cond, label);
     }
     template <typename T>
-    void branchTestInt32(Condition cond, const T& t, Label* label) {
+    void branchTestInt32Impl(Condition cond, const T& t, Label* label) {
         cond = testInt32(cond, t);
         j(cond, label);
     }
     template <typename T>
     void branchTestBoolean(Condition cond, const T& t, Label* label) {
         cond = testBoolean(cond, t);
         j(cond, label);
     }
@@ -791,30 +785,17 @@ class MacroAssemblerX86 : public MacroAs
         } else {
             movl(payload, scratch);
             vmovd(scratch, dest);
             movl(type, scratch);
             vmovd(scratch, ScratchDoubleReg);
             vunpcklps(ScratchDoubleReg, dest, dest);
         }
     }
-    void unboxValue(const ValueOperand& src, AnyRegister dest) {
-        if (dest.isFloat()) {
-            Label notInt32, end;
-            branchTestInt32(Assembler::NotEqual, src, &notInt32);
-            convertInt32ToDouble(src.payloadReg(), dest.fpu());
-            jump(&end);
-            bind(&notInt32);
-            unboxDouble(src, dest.fpu());
-            bind(&end);
-        } else {
-            if (src.payloadReg() != dest.gpr())
-                movl(src.payloadReg(), dest.gpr());
-        }
-    }
+    inline void unboxValue(const ValueOperand& src, AnyRegister dest);
     void unboxPrivate(const ValueOperand& src, Register dest) {
         if (src.payloadReg() != dest)
             movl(src.payloadReg(), dest);
     }
 
     void notBoolean(const ValueOperand& val) {
         xorl(Imm32(1), val.payloadReg());
     }
@@ -860,51 +841,35 @@ class MacroAssemblerX86 : public MacroAs
     void loadConstantFloat32(float f, FloatRegister dest);
     void loadConstantInt32x4(const SimdConstant& v, FloatRegister dest);
     void loadConstantFloat32x4(const SimdConstant& v, FloatRegister dest);
 
     Condition testInt32Truthy(bool truthy, const ValueOperand& operand) {
         test32(operand.payloadReg(), operand.payloadReg());
         return truthy ? NonZero : Zero;
     }
-    void branchTestInt32Truthy(bool truthy, const ValueOperand& operand, Label* label) {
-        Condition cond = testInt32Truthy(truthy, operand);
-        j(cond, label);
-    }
     void branchTestBooleanTruthy(bool truthy, const ValueOperand& operand, Label* label) {
         test32(operand.payloadReg(), operand.payloadReg());
         j(truthy ? NonZero : Zero, label);
     }
     Condition testStringTruthy(bool truthy, const ValueOperand& value) {
         Register string = value.payloadReg();
         cmp32(Operand(string, JSString::offsetOfLength()), Imm32(0));
         return truthy ? Assembler::NotEqual : Assembler::Equal;
     }
     void branchTestStringTruthy(bool truthy, const ValueOperand& value, Label* label) {
         Condition cond = testStringTruthy(truthy, value);
         j(cond, label);
     }
 
-    void loadInt32OrDouble(const Operand& operand, FloatRegister dest) {
-        Label notInt32, end;
-        branchTestInt32(Assembler::NotEqual, operand, &notInt32);
-        convertInt32ToDouble(ToPayload(operand), dest);
-        jump(&end);
-        bind(&notInt32);
-        loadDouble(operand, dest);
-        bind(&end);
-    }
+    template <typename T>
+    inline void loadInt32OrDouble(const T& src, FloatRegister dest);
 
     template <typename T>
-    void loadUnboxedValue(const T& src, MIRType type, AnyRegister dest) {
-        if (dest.isFloat())
-            loadInt32OrDouble(Operand(src), dest.fpu());
-        else
-            movl(Operand(src), dest.gpr());
-    }
+    inline void loadUnboxedValue(const T& src, MIRType type, AnyRegister dest);
 
     template <typename T>
     void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest,
                            MIRType slotType);
 
     template <typename T>
     void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes) {
         switch (nbytes) {
@@ -929,39 +894,22 @@ class MacroAssemblerX86 : public MacroAs
     inline void convertUInt32ToFloat32(Register src, FloatRegister dest);
 
     void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest);
 
     void incrementInt32Value(const Address& addr) {
         addl(Imm32(1), payloadOf(addr));
     }
 
-    // If source is a double, load it into dest. If source is int32,
-    // convert it to double. Else, branch to failure.
-    void ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure) {
-        Label isDouble, done;
-        branchTestDouble(Assembler::Equal, source.typeReg(), &isDouble);
-        branchTestInt32(Assembler::NotEqual, source.typeReg(), failure);
-
-        convertInt32ToDouble(source.payloadReg(), dest);
-        jump(&done);
-
-        bind(&isDouble);
-        unboxDouble(source, dest);
-
-        bind(&done);
-    }
+    inline void ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure);
 
   public:
     // Used from within an Exit frame to handle a pending exception.
     void handleFailureWithHandlerTail(void* handler);
 
-    void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label);
-    void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label);
-
     // Instrumentation for entering and leaving the profiler.
     void profilerEnterFrame(Register framePtr, Register scratch);
     void profilerExitFrame();
 };
 
 typedef MacroAssemblerX86 MacroAssemblerSpecific;
 
 } // namespace jit
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -825,16 +825,17 @@ js::Disassemble1(JSContext* cx, HandleSc
     }
     const JSCodeSpec* cs = &CodeSpec[op];
     ptrdiff_t len = (ptrdiff_t) cs->length;
     Sprint(sp, "%05u:", loc);
     if (lines)
         Sprint(sp, "%4u", PCToLineNumber(script, pc));
     Sprint(sp, "  %s", CodeName[op]);
 
+    int i;
     switch (JOF_TYPE(cs->format)) {
       case JOF_BYTE:
           // Scan the trynotes to find the associated catch block
           // and make the try opcode look like a jump instruction
           // with an offset. This simplifies code coverage analysis
           // based on this disassembled output.
           if (op == JSOP_TRY) {
               TryNoteArray* trynotes = script->trynotes();
@@ -941,19 +942,16 @@ js::Disassemble1(JSContext* cx, HandleSc
       case JOF_LOCAL:
         Sprint(sp, " %u", GET_LOCALNO(pc));
         break;
 
       case JOF_UINT32:
         Sprint(sp, " %u", GET_UINT32(pc));
         break;
 
-      {
-        int i;
-
       case JOF_UINT16:
         i = (int)GET_UINT16(pc);
         goto print_int;
 
       case JOF_UINT24:
         MOZ_ASSERT(len == 4);
         i = (int)GET_UINT24(pc);
         goto print_int;
@@ -967,17 +965,16 @@ js::Disassemble1(JSContext* cx, HandleSc
         goto print_int;
 
       case JOF_INT32:
         MOZ_ASSERT(op == JSOP_INT32);
         i = GET_INT32(pc);
       print_int:
         Sprint(sp, " %d", i);
         break;
-      }
 
       default: {
         char numBuf[12];
         JS_snprintf(numBuf, sizeof numBuf, "%x", cs->format);
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                              JSMSG_UNKNOWN_FORMAT, numBuf);
         return 0;
       }
--- a/js/src/proxy/ScriptedDirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp
@@ -123,27 +123,16 @@ ValidatePropertyDescriptor(JSContext* cx
 // Get the [[ProxyHandler]] of a scripted direct proxy.
 static JSObject*
 GetDirectProxyHandlerObject(JSObject* proxy)
 {
     MOZ_ASSERT(proxy->as<ProxyObject>().handler() == &ScriptedDirectProxyHandler::singleton);
     return proxy->as<ProxyObject>().extra(ScriptedDirectProxyHandler::HANDLER_EXTRA).toObjectOrNull();
 }
 
-static inline void
-ReportInvalidTrapResult(JSContext* cx, JSObject* proxy, JSAtom* atom)
-{
-    RootedValue v(cx, ObjectOrNullValue(proxy));
-    JSAutoByteString bytes;
-    if (!AtomToPrintableString(cx, atom, &bytes))
-        return;
-    ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_IGNORE_STACK, v,
-                      nullptr, bytes.ptr());
-}
-
 // ES6 implements both getPrototype and setPrototype traps. We don't have them yet (see bug
 // 888969). For now, use these, to account for proxy revocation.
 bool
 ScriptedDirectProxyHandler::getPrototype(JSContext* cx, HandleObject proxy,
                                          MutableHandleObject protop) const
 {
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     // Though handler is used elsewhere, spec mandates that both get set to null.
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -70,16 +70,17 @@
 #include "jit/OptimizationTracking.h"
 #include "js/Debug.h"
 #include "js/GCAPI.h"
 #include "js/Initialization.h"
 #include "js/StructuredClone.h"
 #include "js/TrackedOptimizationInfo.h"
 #include "perf/jsperf.h"
 #include "shell/jsoptparse.h"
+#include "shell/jsshell.h"
 #include "shell/OSObject.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Compression.h"
 #include "vm/Debugger.h"
 #include "vm/HelperThreads.h"
 #include "vm/Monitor.h"
 #include "vm/Shape.h"
 #include "vm/SharedArrayObject.h"
@@ -354,17 +355,17 @@ GetLine(FILE* file, const char * prompt)
     }
 
     size_t size = 80;
     char* buffer = static_cast<char*>(malloc(size));
     if (!buffer)
         return nullptr;
 
     char* current = buffer;
-    while (true) {
+    do {
         while (true) {
             if (fgets(current, size - len, file))
                 break;
             if (errno != EINTR) {
                 free(buffer);
                 return nullptr;
             }
         }
@@ -382,21 +383,17 @@ GetLine(FILE* file, const char * prompt)
             char* tmp = static_cast<char*>(realloc(buffer, size));
             if (!tmp) {
                 free(buffer);
                 return nullptr;
             }
             buffer = tmp;
         }
         current = buffer + len;
-    }
-
-    if (len && !ferror(file))
-        return buffer;
-    free(buffer);
+    } while (true);
     return nullptr;
 }
 
 /* State to store as JSContext private. */
 struct JSShellContextData {
     /* Creation timestamp, used by the elapsed() shell builtin. */
     int64_t startTime;
 };
@@ -4402,36 +4399,41 @@ SingleStepCallback(void* arg, jit::Simul
 #elif defined(JS_SIMULATOR_MIPS64)
     state.sp = (void*)sim->getRegister(jit::Simulator::sp);
     state.lr = (void*)sim->getRegister(jit::Simulator::ra);
 #endif
 
     mozilla::DebugOnly<void*> lastStackAddress = nullptr;
     StackChars stack;
     uint32_t frameNo = 0;
+    AutoEnterOOMUnsafeRegion oomUnsafe;
     for (JS::ProfilingFrameIterator i(rt, state); !i.done(); ++i) {
         MOZ_ASSERT(i.stackAddress() != nullptr);
         MOZ_ASSERT(lastStackAddress <= i.stackAddress());
         lastStackAddress = i.stackAddress();
         JS::ProfilingFrameIterator::Frame frames[16];
         uint32_t nframes = i.extractStack(frames, 0, 16);
         for (uint32_t i = 0; i < nframes; i++) {
-            if (frameNo > 0)
-                stack.append(",", 1);
-            stack.append(frames[i].label, strlen(frames[i].label));
+            if (frameNo > 0) {
+                if (!stack.append(",", 1))
+                    oomUnsafe.crash("stack.append");
+            }
+            if (!stack.append(frames[i].label, strlen(frames[i].label)))
+                oomUnsafe.crash("stack.append");
             frameNo++;
         }
     }
 
     // Only append the stack if it differs from the last stack.
     if (stacks.empty() ||
         stacks.back().length() != stack.length() ||
         !PodEqual(stacks.back().begin(), stack.begin(), stack.length()))
     {
-        stacks.append(Move(stack));
+        if (!stacks.append(Move(stack)))
+            oomUnsafe.crash("stacks.append");
     }
 }
 #endif
 
 static bool
 EnableSingleStepProfiling(JSContext* cx, unsigned argc, Value* vp)
 {
 #if defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS64)
--- a/js/src/shell/jsshell.h
+++ b/js/src/shell/jsshell.h
@@ -18,17 +18,17 @@ enum JSShellErrNum {
 #include "jsshell.msg"
 #undef MSG_DEF
     JSShellErr_Limit
 };
 
 const JSErrorFormatString*
 my_GetErrorMessage(void* userRef, const unsigned errorNumber);
 
-static void
+void
 my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report);
 
 JSString*
 FileAsString(JSContext* cx, const char* pathname);
 
 class AutoCloseFile
 {
   private:
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/DebugOnly.h"
 
 #include "jsapi.h"
 #include "jscompartment.h"
 #include "jsgc.h"
 #include "jsobj.h"
 #include "jsscript.h"
 
+#include "gc/Heap.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "vm/ArrayObject.h"
 #include "vm/Runtime.h"
 #include "vm/Shape.h"
 #include "vm/String.h"
 #include "vm/Symbol.h"
 #include "vm/WrapperObject.h"
@@ -351,17 +352,17 @@ StatsCompartmentCallback(JSRuntime* rt, 
 static void
 StatsArenaCallback(JSRuntime* rt, void* data, gc::Arena* arena,
                    JS::TraceKind traceKind, size_t thingSize)
 {
     RuntimeStats* rtStats = static_cast<StatsClosure*>(data)->rtStats;
 
     // The admin space includes (a) the header and (b) the padding between the
     // end of the header and the start of the first GC thing.
-    size_t allocationSpace = Arena::thingsSpan(arena->aheader.getAllocKind());
+    size_t allocationSpace = gc::Arena::thingsSpan(arena->aheader.getAllocKind());
     rtStats->currZoneStats->gcHeapArenaAdmin += gc::ArenaSize - allocationSpace;
 
     // We don't call the callback on unused things.  So we compute the
     // unused space like this:  arenaUnused = maxArenaUnused - arenaUsed.
     // We do this by setting arenaUnused to maxArenaUnused here, and then
     // subtracting thingSize for every used cell, in StatsCellCallback().
     rtStats->currZoneStats->unusedGCThings.addToKind(traceKind, allocationSpace);
 }
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -600,16 +600,17 @@ nsDisplayListBuilder::nsDisplayListBuild
     : mReferenceFrame(aReferenceFrame),
       mIgnoreScrollFrame(nullptr),
       mLayerEventRegions(nullptr),
       mCurrentTableItem(nullptr),
       mCurrentFrame(aReferenceFrame),
       mCurrentReferenceFrame(aReferenceFrame),
       mCurrentAGR(&mRootAGR),
       mRootAGR(aReferenceFrame, nullptr),
+      mUsedAGRBudget(0),
       mDirtyRect(-1,-1,-1,-1),
       mGlassDisplayItem(nullptr),
       mPendingScrollInfoItems(nullptr),
       mCommittedScrollInfoItems(nullptr),
       mMode(aMode),
       mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID),
       mCurrentScrollbarTarget(FrameMetrics::NULL_SCROLL_ID),
       mCurrentScrollbarFlags(0),
@@ -1082,18 +1083,22 @@ nsDisplayListBuilder::IsAnimatedGeometry
     if (aParent) {
       *aParent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
     }
     return false;
   }
 
   if (nsLayoutUtils::IsPopup(aFrame))
     return true;
-  if (ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(aFrame))
-    return true;
+  if (ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(aFrame)) {
+    const bool inBudget = AddToAGRBudget(aFrame);
+    if (inBudget) {
+      return true;
+    }
+  }
   if (!aFrame->GetParent() &&
       nsLayoutUtils::ViewportHasDisplayPort(aFrame->PresContext())) {
     // Viewport frames in a display port need to be animated geometry roots
     // for background-attachment:fixed elements.
     return true;
   }
   if (aFrame->IsTransformed()) {
     return true;
@@ -1250,70 +1255,70 @@ LayoutDeviceIntRegion
 nsDisplayListBuilder::GetWindowDraggingRegion() const
 {
   LayoutDeviceIntRegion result;
   result.Sub(mWindowDraggingRegion, mWindowNoDraggingRegion);;
   return result;
 }
 
 const uint32_t gWillChangeAreaMultiplier = 3;
-static uint32_t GetWillChangeCost(const nsSize& aSize) {
+static uint32_t GetLayerizationCost(const nsSize& aSize) {
   // There's significant overhead for each layer created from Gecko
   // (IPC+Shared Objects) and from the backend (like an OpenGL texture).
   // Therefore we set a minimum cost threshold of a 64x64 area.
   int minBudgetCost = 64 * 64;
 
   uint32_t budgetCost =
     std::max(minBudgetCost,
       nsPresContext::AppUnitsToIntCSSPixels(aSize.width) *
       nsPresContext::AppUnitsToIntCSSPixels(aSize.height));
 
   return budgetCost;
 }
 
 bool
 nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame,
                                             const nsSize& aSize) {
-  if (mBudgetSet.Contains(aFrame)) {
+  if (mWillChangeBudgetSet.Contains(aFrame)) {
     return true; // Already accounted
   }
 
   nsPresContext* key = aFrame->PresContext();
   if (!mWillChangeBudget.Contains(key)) {
     mWillChangeBudget.Put(key, DocumentWillChangeBudget());
   }
 
   DocumentWillChangeBudget budget;
   mWillChangeBudget.Get(key, &budget);
 
   nsRect area = aFrame->PresContext()->GetVisibleArea();
   uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) *
     nsPresContext::AppUnitsToIntCSSPixels(area.height);
 
-  uint32_t cost = GetWillChangeCost(aSize);
+  uint32_t cost = GetLayerizationCost(aSize);
   bool onBudget = (budget.mBudget + cost) /
                     gWillChangeAreaMultiplier < budgetLimit;
 
   if (onBudget) {
     budget.mBudget += cost;
     mWillChangeBudget.Put(key, budget);
-    mBudgetSet.PutEntry(aFrame);
+    mWillChangeBudgetSet.PutEntry(aFrame);
   }
 
   return onBudget;
 }
 
 bool
 nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame,
                                            const nsSize& aSize) {
   bool onBudget = AddToWillChangeBudget(aFrame, aSize);
 
   if (!onBudget) {
     nsString usageStr;
-    usageStr.AppendInt(GetWillChangeCost(aSize));
+    usageStr.AppendInt(GetLayerizationCost(aSize));
 
     nsString multiplierStr;
     multiplierStr.AppendInt(gWillChangeAreaMultiplier);
 
     nsString limitStr;
     nsRect area = aFrame->PresContext()->GetVisibleArea();
     uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) *
       nsPresContext::AppUnitsToIntCSSPixels(area.height);
@@ -1322,16 +1327,50 @@ nsDisplayListBuilder::IsInWillChangeBudg
     const char16_t* params[] = { multiplierStr.get(), limitStr.get() };
     aFrame->PresContext()->Document()->WarnOnceAbout(
       nsIDocument::eIgnoringWillChangeOverBudget, false,
       params, ArrayLength(params));
   }
   return onBudget;
 }
 
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+const float gAGRBudgetAreaMultiplier = 0.3;
+#else
+const float gAGRBudgetAreaMultiplier = 3.0;
+#endif
+
+bool
+nsDisplayListBuilder::AddToAGRBudget(nsIFrame* aFrame)
+{
+  if (mAGRBudgetSet.Contains(aFrame)) {
+    return true;
+  }
+
+  const nsPresContext* presContext = aFrame->PresContext()->GetRootPresContext();
+  if (!presContext) {
+    return false;
+  }
+
+  const nsRect area = presContext->GetVisibleArea();
+  const uint32_t budgetLimit = gAGRBudgetAreaMultiplier *
+    nsPresContext::AppUnitsToIntCSSPixels(area.width) *
+    nsPresContext::AppUnitsToIntCSSPixels(area.height);
+
+  const uint32_t cost = GetLayerizationCost(aFrame->GetSize());
+  const bool onBudget = mUsedAGRBudget + cost < budgetLimit;
+
+  if (onBudget) {
+    mUsedAGRBudget += cost;
+    mAGRBudgetSet.PutEntry(aFrame);
+  }
+
+  return onBudget;
+}
+
 nsDisplayList*
 nsDisplayListBuilder::EnterScrollInfoItemHoisting(nsDisplayList* aScrollInfoItemStorage)
 {
   MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
   nsDisplayList* old = mPendingScrollInfoItems;
   mPendingScrollInfoItems = aScrollInfoItemStorage;
   return old;
 }
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1159,16 +1159,23 @@ private:
   AnimatedGeometryRoot* WrapAGRForFrame(nsIFrame* aAnimatedGeometryRoot,
                                         AnimatedGeometryRoot* aParent = nullptr);
 
   friend class nsDisplayItem;
   AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsIFrame* aFrame);
 
   nsDataHashtable<nsPtrHashKey<nsIFrame>, AnimatedGeometryRoot*> mFrameToAnimatedGeometryRootMap;
 
+  /**
+   * Add the current frame to the AGR budget if possible and remember
+   * the outcome. Subsequent calls will return the same value as
+   * returned here.
+   */
+  bool AddToAGRBudget(nsIFrame* aFrame);
+
   struct PresShellState {
     nsIPresShell* mPresShell;
     nsIFrame*     mCaretFrame;
     nsRect        mCaretRect;
     uint32_t      mFirstFrameMarkedForDisplay;
     bool          mIsBackgroundOnly;
     // This is a per-document flag turning off event handling for all content
     // in the document, and is set when we enter a subdocument for a pointer-
@@ -1212,17 +1219,22 @@ private:
   AnimatedGeometryRoot           mRootAGR;
 
   // will-change budget tracker
   nsDataHashtable<nsPtrHashKey<nsPresContext>, DocumentWillChangeBudget>
                                  mWillChangeBudget;
 
   // Any frame listed in this set is already counted in the budget
   // and thus is in-budget.
-  nsTHashtable<nsPtrHashKey<nsIFrame> > mBudgetSet;
+  nsTHashtable<nsPtrHashKey<nsIFrame> > mWillChangeBudgetSet;
+
+  // Area of animated geometry root budget already allocated
+  uint32_t mUsedAGRBudget;
+  // Set of frames already counted in budget
+  nsTHashtable<nsPtrHashKey<nsIFrame> > mAGRBudgetSet;
 
   // rects are relative to the frame's reference frame
   nsDataHashtable<nsPtrHashKey<nsIFrame>, nsRect> mDirtyRectForScrolledContents;
 
   // Relative to mCurrentFrame.
   nsRect                         mDirtyRect;
   nsRegion                       mWindowExcludeGlassRegion;
   nsRegion                       mWindowOpaqueRegion;
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -450,17 +450,17 @@ public:
   virtual void Detach();
 
   /**
    * Get the visible area associated with this presentation context.
    * This is the size of the visible area that is used for
    * presenting the document. The returned value is in the standard
    * nscoord units (as scaled by the device context).
    */
-  nsRect GetVisibleArea() { return mVisibleArea; }
+  nsRect GetVisibleArea() const { return mVisibleArea; }
 
   /**
    * Set the currently visible area. The units for r are standard
    * nscoord units (as scaled by the device context).
    */
   void SetVisibleArea(const nsRect& r) {
     if (!r.IsEqualEdges(mVisibleArea)) {
       mVisibleArea = r;
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1239564-ref.html
@@ -0,0 +1,21 @@
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Testcase for bug 1239564</title>
+<style>
+#circle {
+  border-radius: 300px;
+  height: 80px;
+  width: 80px;
+  background-color: red;
+  margin: auto;
+  position: absolute;
+  left: 0;
+  right: 0;
+  top:0;
+  bottom: 0;
+  transform: scale(3);
+}
+</style>
+<body>
+<div id="circle"></div>
+</body></html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1239564.html
@@ -0,0 +1,29 @@
+<html class="reftest-wait"><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Testcase for bug 1239564</title>
+<style>
+#circle {
+  border-radius: 300px;
+  height: 80px;
+  width: 80px;
+  background-color: red;
+  margin: auto;
+  position: absolute;
+  left: 0;
+  right: 0;
+  top:0;
+  bottom: 0;
+  will-change: transform;
+}
+</style>
+<body>
+<div id="circle"></div>
+<script>
+var c = document.getElementById('circle');
+function doTest() {
+  c.style.transform = "translateZ(1px) scale(3)";
+  document.documentElement.removeAttribute('class');
+}
+document.addEventListener("MozReftestInvalidate", doTest, false);
+</script>
+</body></html>
\ No newline at end of file
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1939,8 +1939,9 @@ fuzzy(1,74) fuzzy-if(gtkWidget,6,79) == 
 == 1209603-1.html 1209603-1-ref.html
 == 1209994-1.html 1209994-1-ref.html
 == 1209994-2.html 1209994-2-ref.html
 == 1209994-3.html 1209994-3-ref.html
 == 1209994-4.html 1209994-4-ref.html
 == 1222226-1.html 1222226-1-ref.html
 pref(layout.css.overflow-clip-box.enabled,true) == 1226278.html 1226278-ref.html
 == 1230466.html about:blank
+fuzzy(100,2000) == 1239564.html 1239564-ref.html
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
@@ -18,16 +18,18 @@ import org.mozilla.gecko.util.FloatUtils
 import org.mozilla.gecko.AppConstants;
 
 import android.content.Context;
 import android.graphics.PointF;
 import android.graphics.RectF;
 import android.os.SystemClock;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.InputDevice;
+import android.view.MotionEvent;
 import org.json.JSONObject;
 
 import java.util.ArrayList;
 import java.util.List;
 
 class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
 {
     private static final String LOGTAG = "GeckoLayerClient";
@@ -101,16 +103,18 @@ class GeckoLayerClient implements LayerV
      * to the time that we receive the first-paint composite notification from the compositor.
      * Note that there is a small race condition with this; if there are two paints that both
      * have the first-paint flag set, and the second paint happens concurrently with the
      * composite for the first paint, then this flag may be set to true prematurely. Fixing this
      * is possible but risky; see https://bugzilla.mozilla.org/show_bug.cgi?id=797615#c751
      */
     private volatile boolean mContentDocumentIsDisplayed;
 
+    private SynthesizedEventState mPointerState;
+
     public GeckoLayerClient(Context context, LayerView view, EventDispatcher eventDispatcher) {
         // we can fill these in with dummy values because they are always written
         // to before being read
         mContext = context;
         mScreenSize = new IntSize(0, 0);
         mWindowSize = new IntSize(0, 0);
         mDisplayPort = new DisplayPortMetrics();
         mRecordDrawTimes = true;
@@ -704,16 +708,151 @@ class GeckoLayerClient implements LayerV
             mViewportMetrics = mViewportMetrics.setViewportOrigin(scrollX, scrollY)
                 .setZoomFactor(zoom)
                 .setPageRect(RectUtils.scale(cssPageRect, zoom), cssPageRect);
         }
         return syncViewportInfo(dpX, dpY, dpWidth, dpHeight, paintedResolution,
                     layersUpdated, paintSyncId);
     }
 
+    class PointerInfo {
+        public int pointerId;
+        public int screenX;
+        public int screenY;
+        public double pressure;
+        public int orientation;
+
+        public MotionEvent.PointerCoords getCoords() {
+            MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
+            coords.orientation = orientation;
+            coords.pressure = (float)pressure;
+            coords.x = screenX;
+            coords.y = screenY;
+            return coords;
+        }
+    }
+
+    class SynthesizedEventState {
+        public final ArrayList<PointerInfo> pointers;
+        public long downTime;
+
+        SynthesizedEventState() {
+            pointers = new ArrayList<PointerInfo>();
+        }
+
+        int getPointerIndex(int pointerId) {
+            for (int i = 0; i < pointers.size(); i++) {
+                if (pointers.get(i).pointerId == pointerId) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        int addPointer(int pointerId) {
+            PointerInfo info = new PointerInfo();
+            info.pointerId = pointerId;
+            pointers.add(info);
+            return pointers.size() - 1;
+        }
+
+        int[] getPointerIds() {
+            int[] ids = new int[pointers.size()];
+            for (int i = 0; i < ids.length; i++) {
+                ids[i] = pointers.get(i).pointerId;
+            }
+            return ids;
+        }
+
+        MotionEvent.PointerCoords[] getPointerCoords() {
+            MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[pointers.size()];
+            for (int i = 0; i < coords.length; i++) {
+                coords[i] = pointers.get(i).getCoords();
+            }
+            return coords;
+        }
+    }
+
+    @WrapForJNI
+    public void synthesizeNativeTouchPoint(int pointerId, int eventType, int screenX,
+            int screenY, double pressure, int orientation)
+    {
+        if (mPointerState == null) {
+            mPointerState = new SynthesizedEventState();
+        }
+
+        // Find the pointer if it already exists
+        int pointerIndex = mPointerState.getPointerIndex(pointerId);
+
+        // Event-specific handling
+        switch (eventType) {
+            case MotionEvent.ACTION_POINTER_UP:
+                if (pointerIndex < 0) {
+                    Log.d(LOGTAG, "Requested synthesis of a pointer-up for a pointer that doesn't exist!");
+                    return;
+                }
+                if (mPointerState.pointers.size() == 1) {
+                    // Last pointer is going up
+                    eventType = MotionEvent.ACTION_UP;
+                }
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                if (pointerIndex < 0) {
+                    Log.d(LOGTAG, "Requested synthesis of a pointer-cancel for a pointer that doesn't exist!");
+                    return;
+                }
+                break;
+            case MotionEvent.ACTION_POINTER_DOWN:
+                if (pointerIndex < 0) {
+                    // Adding a new pointer
+                    pointerIndex = mPointerState.addPointer(pointerId);
+                    if (pointerIndex == 0) {
+                        // first pointer
+                        eventType = MotionEvent.ACTION_DOWN;
+                        mPointerState.downTime = SystemClock.uptimeMillis();
+                    }
+                } else {
+                    // We're moving an existing pointer
+                    eventType = MotionEvent.ACTION_MOVE;
+                }
+                break;
+        }
+
+        // Update the pointer with the new info
+        PointerInfo info = mPointerState.pointers.get(pointerIndex);
+        info.screenX = screenX;
+        info.screenY = screenY;
+        info.pressure = pressure;
+        info.orientation = orientation;
+
+        // Dispatch the event
+        int action = (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
+        action &= MotionEvent.ACTION_POINTER_INDEX_MASK;
+        action |= (eventType & MotionEvent.ACTION_MASK);
+        final MotionEvent event = MotionEvent.obtain(mPointerState.downTime,
+            SystemClock.uptimeMillis(), action, mPointerState.pointers.size(),
+            mPointerState.getPointerIds(), mPointerState.getPointerCoords(),
+            0, 0, 0, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
+        mView.post(new Runnable() {
+            @Override
+            public void run() {
+                event.offsetLocation(0, mView.getSurfaceTranslation());
+                mView.dispatchTouchEvent(event);
+            }
+        });
+
+        // Forget about removed pointers
+        if (eventType == MotionEvent.ACTION_POINTER_UP ||
+            eventType == MotionEvent.ACTION_UP ||
+            eventType == MotionEvent.ACTION_CANCEL)
+        {
+            mPointerState.pointers.remove(pointerIndex);
+        }
+    }
+
     @WrapForJNI(allowMultithread = true)
     public LayerRenderer.Frame createFrame() {
         // Create the shaders and textures if necessary.
         if (!mLayerRendererInitialized) {
             if (mLayerRenderer == null) {
                 return null;
             }
             mLayerRenderer.checkMonitoringEnabled();
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -1310,17 +1310,17 @@ static nsresult pref_InitInitialObjects(
   rv = pref_LoadPrefsInDirList(NS_APP_PREFS_DEFAULTS_DIR_LIST);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Set up the correct default for toolkit.telemetry.enabled.
   // If this build has MOZ_TELEMETRY_ON_BY_DEFAULT *or* we're on the beta
   // channel, telemetry is on by default, otherwise not. This is necessary
   // so that beta users who are testing final release builds don't flipflop
   // defaults.
-  if (Preferences::GetDefaultType(kTelemetryPref) == PREF_INVALID) {
+  if (Preferences::GetDefaultType(kTelemetryPref) == nsIPrefBranch::PREF_INVALID) {
     bool prerelease = false;
 #ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
     prerelease = true;
 #else
     if (Preferences::GetDefaultCString(kChannelPref).EqualsLiteral("beta")) {
       prerelease = true;
     }
 #endif
--- a/modules/libpref/nsPrefBranch.cpp
+++ b/modules/libpref/nsPrefBranch.cpp
@@ -122,17 +122,31 @@ NS_IMETHODIMP nsPrefBranch::GetRoot(char
   *aRoot = ToNewCString(mPrefRoot);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsPrefBranch::GetPrefType(const char *aPrefName, int32_t *_retval)
 {
   NS_ENSURE_ARG(aPrefName);
   const char *pref = getPrefName(aPrefName);
-  *_retval = PREF_GetPrefType(pref);
+  switch (PREF_GetPrefType(pref)) {
+    case PrefType::String:
+      *_retval = PREF_STRING;
+      break;
+    case PrefType::Int:
+      *_retval = PREF_INT;
+      break;
+    case PrefType::Bool:
+      *_retval = PREF_BOOL;
+        break;
+    case PrefType::Invalid:
+    default:
+      *_retval = PREF_INVALID;
+      break;
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP nsPrefBranch::GetBoolPref(const char *aPrefName, bool *_retval)
 {
   NS_ENSURE_ARG(aPrefName);
   const char *pref = getPrefName(aPrefName);
   return PREF_GetBoolPref(pref, _retval, mIsDefault);
--- a/modules/libpref/prefapi.cpp
+++ b/modules/libpref/prefapi.cpp
@@ -35,17 +35,17 @@
 #include "prlink.h"
 
 using namespace mozilla;
 
 static void
 clearPrefEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
 {
     PrefHashEntry *pref = static_cast<PrefHashEntry *>(entry);
-    if (pref->flags & PREF_STRING)
+    if (pref->prefFlags.IsTypeString())
     {
         if (pref->defaultPref.stringVal)
             PL_strfree(pref->defaultPref.stringVal);
         if (pref->userPref.stringVal)
             PL_strfree(pref->userPref.stringVal);
     }
     // don't need to free this as it's allocated in memory owned by
     // gPrefNameArena
@@ -112,23 +112,17 @@ static char *ArenaStrDup(const char* str
     PL_ARENA_ALLOCATE(mem, aArena, len+1);
     if (mem)
         memcpy(mem, str, len+1);
     return static_cast<char*>(mem);
 }
 
 /*---------------------------------------------------------------------------*/
 
-#define PREF_IS_LOCKED(pref)            ((pref)->flags & PREF_LOCKED)
-#define PREF_HAS_DEFAULT_VALUE(pref)    ((pref)->flags & PREF_HAS_DEFAULT)
-#define PREF_HAS_USER_VALUE(pref)       ((pref)->flags & PREF_USERSET)
-#define PREF_TYPE(pref)                 (PrefType)((pref)->flags & PREF_VALUETYPE_MASK)
-
 static bool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType type);
-
 /* -- Privates */
 struct CallbackNode {
     char*                   domain;
     // If someone attempts to remove the node from the callback list while
     // pref_DoCallback is running, |func| is set to nullptr. Such nodes will
     // be removed at the end of pref_DoCallback.
     PrefChangedFunc         func;
     void*                   data;
@@ -244,35 +238,35 @@ PREF_SetCharPref(const char *pref_name, 
 {
     if ((uint32_t)strlen(value) > MAX_PREF_LENGTH) {
         return NS_ERROR_ILLEGAL_VALUE;
     }
 
     PrefValue pref;
     pref.stringVal = (char*)value;
 
-    return pref_HashPref(pref_name, pref, PREF_STRING, set_default ? kPrefSetDefault : 0);
+    return pref_HashPref(pref_name, pref, PrefType::String, set_default ? kPrefSetDefault : 0);
 }
 
 nsresult
 PREF_SetIntPref(const char *pref_name, int32_t value, bool set_default)
 {
     PrefValue pref;
     pref.intVal = value;
 
-    return pref_HashPref(pref_name, pref, PREF_INT, set_default ? kPrefSetDefault : 0);
+    return pref_HashPref(pref_name, pref, PrefType::Int, set_default ? kPrefSetDefault : 0);
 }
 
 nsresult
 PREF_SetBoolPref(const char *pref_name, bool value, bool set_default)
 {
     PrefValue pref;
     pref.boolVal = value;
 
-    return pref_HashPref(pref_name, pref, PREF_BOOL, set_default ? kPrefSetDefault : 0);
+    return pref_HashPref(pref_name, pref, PrefType::Bool, set_default ? kPrefSetDefault : 0);
 }
 
 enum WhichValue { DEFAULT_VALUE, USER_VALUE };
 static nsresult
 SetPrefValue(const char* aPrefName, const dom::PrefValue& aValue,
              WhichValue aWhich)
 {
     bool setDefault = (aWhich == DEFAULT_VALUE);
@@ -330,38 +324,38 @@ pref_savePrefs(PLDHashTable* aTable)
 
         nsAutoCString prefValue;
         nsAutoCString prefPrefix;
         prefPrefix.AssignLiteral("user_pref(\"");
 
         // where we're getting our pref from
         PrefValue* sourcePref;
 
-        if (PREF_HAS_USER_VALUE(pref) &&
+        if (pref->prefFlags.HasUserValue() &&
             (pref_ValueChanged(pref->defaultPref,
                                pref->userPref,
-                               (PrefType) PREF_TYPE(pref)) ||
-             !(pref->flags & PREF_HAS_DEFAULT) ||
-             pref->flags & PREF_STICKY_DEFAULT)) {
+                               pref->prefFlags.GetPrefType()) ||
+             !(pref->prefFlags.HasDefault()) ||
+             pref->prefFlags.HasStickyDefault())) {
             sourcePref = &pref->userPref;
         } else {
             // do not save default prefs that haven't changed
             continue;
         }
 
         // strings are in quotes!
-        if (pref->flags & PREF_STRING) {
+        if (pref->prefFlags.IsTypeString()) {
             prefValue = '\"';
             str_escape(sourcePref->stringVal, prefValue);
             prefValue += '\"';
 
-        } else if (pref->flags & PREF_INT) {
+        } else if (pref->prefFlags.IsTypeInt()) {
             prefValue.AppendInt(sourcePref->intVal);
 
-        } else if (pref->flags & PREF_BOOL) {
+        } else if (pref->prefFlags.IsTypeBool()) {
             prefValue = (sourcePref->boolVal) ? "true" : "false";
         }
 
         nsAutoCString prefName;
         str_escape(pref->key, prefName);
 
         savedPrefs[j++] = ToNewCString(prefPrefix +
                                        prefName +
@@ -384,41 +378,41 @@ GetPrefValueFromEntry(PrefHashEntry *aHa
         aPref->userValue() = dom::PrefValue();
         settingValue = &aPref->userValue().get_PrefValue();
     } else {
         value = &aHashEntry->defaultPref;
         aPref->defaultValue() = dom::PrefValue();
         settingValue = &aPref->defaultValue().get_PrefValue();
     }
 
-    switch (aHashEntry->flags & PREF_VALUETYPE_MASK) {
-    case PREF_STRING:
+    switch (aHashEntry->prefFlags.GetPrefType()) {
+      case PrefType::String:
         *settingValue = nsDependentCString(value->stringVal);
         return;
-    case PREF_INT:
+      case PrefType::Int:
         *settingValue = value->intVal;
         return;
-    case PREF_BOOL:
+      case PrefType::Bool:
         *settingValue = !!value->boolVal;
         return;
     default:
         MOZ_CRASH();
     }
 }
 
 void
 pref_GetPrefFromEntry(PrefHashEntry *aHashEntry, dom::PrefSetting* aPref)
 {
     aPref->name() = aHashEntry->key;
-    if (PREF_HAS_DEFAULT_VALUE(aHashEntry)) {
+    if (aHashEntry->prefFlags.HasDefault()) {
         GetPrefValueFromEntry(aHashEntry, aPref, DEFAULT_VALUE);
     } else {
         aPref->defaultValue() = null_t();
     }
-    if (PREF_HAS_USER_VALUE(aHashEntry)) {
+    if (aHashEntry->prefFlags.HasUserValue()) {
         GetPrefValueFromEntry(aHashEntry, aPref, USER_VALUE);
     } else {
         aPref->userValue() = null_t();
     }
 
     MOZ_ASSERT(aPref->defaultValue().type() == dom::MaybePrefValue::Tnull_t ||
                aPref->userValue().type() == dom::MaybePrefValue::Tnull_t ||
                (aPref->defaultValue().get_PrefValue().type() ==
@@ -446,92 +440,84 @@ pref_CompareStrings(const void *v1, cons
 }
 
 bool PREF_HasUserPref(const char *pref_name)
 {
     if (!gHashTable)
         return false;
 
     PrefHashEntry *pref = pref_HashTableLookup(pref_name);
-    if (!pref) return false;
-
-    /* convert PREF_HAS_USER_VALUE to bool */
-    return (PREF_HAS_USER_VALUE(pref) != 0);
-
+    return pref && pref->prefFlags.HasUserValue();
 }
 
 nsresult
 PREF_CopyCharPref(const char *pref_name, char ** return_buffer, bool get_default)
 {
     if (!gHashTable)
         return NS_ERROR_NOT_INITIALIZED;
 
     nsresult rv = NS_ERROR_UNEXPECTED;
     char* stringVal;
     PrefHashEntry* pref = pref_HashTableLookup(pref_name);
 
-    if (pref && (pref->flags & PREF_STRING))
-    {
-        if (get_default || PREF_IS_LOCKED(pref) || !PREF_HAS_USER_VALUE(pref))
+    if (pref && (pref->prefFlags.IsTypeString())) {
+        if (get_default || pref->prefFlags.IsLocked() || !pref->prefFlags.HasUserValue()) {
             stringVal = pref->defaultPref.stringVal;
-        else
+        } else {
             stringVal = pref->userPref.stringVal;
+        }
 
         if (stringVal) {
             *return_buffer = NS_strdup(stringVal);
             rv = NS_OK;
         }
     }
     return rv;
 }
 
 nsresult PREF_GetIntPref(const char *pref_name,int32_t * return_int, bool get_default)
 {
     if (!gHashTable)
         return NS_ERROR_NOT_INITIALIZED;
 
     nsresult rv = NS_ERROR_UNEXPECTED;
     PrefHashEntry* pref = pref_HashTableLookup(pref_name);
-    if (pref && (pref->flags & PREF_INT))
-    {
-        if (get_default || PREF_IS_LOCKED(pref) || !PREF_HAS_USER_VALUE(pref))
-        {
+    if (pref && (pref->prefFlags.IsTypeInt())) {
+        if (get_default || pref->prefFlags.IsLocked() || !pref->prefFlags.HasUserValue()) {
             int32_t tempInt = pref->defaultPref.intVal;
             /* check to see if we even had a default */
-            if (!(pref->flags & PREF_HAS_DEFAULT))
+            if (!pref->prefFlags.HasDefault()) {
                 return NS_ERROR_UNEXPECTED;
+            }
             *return_int = tempInt;
+        } else {
+            *return_int = pref->userPref.intVal;
         }
-        else
-            *return_int = pref->userPref.intVal;
         rv = NS_OK;
     }
     return rv;
 }
 
 nsresult PREF_GetBoolPref(const char *pref_name, bool * return_value, bool get_default)
 {
     if (!gHashTable)
         return NS_ERROR_NOT_INITIALIZED;
 
     nsresult rv = NS_ERROR_UNEXPECTED;
     PrefHashEntry* pref = pref_HashTableLookup(pref_name);
     //NS_ASSERTION(pref, pref_name);
-    if (pref && (pref->flags & PREF_BOOL))
-    {
-        if (get_default || PREF_IS_LOCKED(pref) || !PREF_HAS_USER_VALUE(pref))
-        {
+    if (pref && (pref->prefFlags.IsTypeBool())) {
+        if (get_default || pref->prefFlags.IsLocked() || !pref->prefFlags.HasUserValue()) {
             bool tempBool = pref->defaultPref.boolVal;
             /* check to see if we even had a default */
-            if (pref->flags & PREF_HAS_DEFAULT) {
+            if (pref->prefFlags.HasDefault()) {
                 *return_value = tempBool;
                 rv = NS_OK;
             }
-        }
-        else {
+        } else {
             *return_value = pref->userPref.boolVal;
             rv = NS_OK;
         }
     }
     return rv;
 }
 
 nsresult
@@ -579,21 +565,20 @@ PREF_DeleteBranch(const char *branch_nam
 
 nsresult
 PREF_ClearUserPref(const char *pref_name)
 {
     if (!gHashTable)
         return NS_ERROR_NOT_INITIALIZED;
 
     PrefHashEntry* pref = pref_HashTableLookup(pref_name);
-    if (pref && PREF_HAS_USER_VALUE(pref))
-    {
-        pref->flags &= ~PREF_USERSET;
+    if (pref && pref->prefFlags.HasUserValue()) {
+        pref->prefFlags.SetHasUserValue(false);
 
-        if (!(pref->flags & PREF_HAS_DEFAULT)) {
+        if (!pref->prefFlags.HasDefault()) {
             gHashTable->RemoveEntry(pref);
         }
 
         pref_DoCallback(pref_name);
         gDirty = true;
     }
     return NS_OK;
 }
@@ -607,21 +592,21 @@ PREF_ClearAllUserPrefs()
 
     if (!gHashTable)
         return NS_ERROR_NOT_INITIALIZED;
 
     std::vector<std::string> prefStrings;
     for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
         auto pref = static_cast<PrefHashEntry*>(iter.Get());
 
-        if (PREF_HAS_USER_VALUE(pref)) {
+        if (pref->prefFlags.HasUserValue()) {
             prefStrings.push_back(std::string(pref->key));
 
-            pref->flags &= ~PREF_USERSET;
-            if (!(pref->flags & PREF_HAS_DEFAULT)) {
+            pref->prefFlags.SetHasUserValue(false);
+            if (!pref->prefFlags.HasDefault()) {
                 iter.Remove();
             }
         }
     }
 
     for (std::string& prefString : prefStrings) {
         pref_DoCallback(prefString.c_str());
     }
@@ -635,71 +620,76 @@ nsresult PREF_LockPref(const char *key, 
     if (!gHashTable)
         return NS_ERROR_NOT_INITIALIZED;
 
     PrefHashEntry* pref = pref_HashTableLookup(key);
     if (!pref)
         return NS_ERROR_UNEXPECTED;
 
     if (lockit) {
-        if (!PREF_IS_LOCKED(pref))
-        {
-            pref->flags |= PREF_LOCKED;
+        if (!pref->prefFlags.IsLocked()) {
+            pref->prefFlags.SetLocked(true);
             gIsAnyPrefLocked = true;
             pref_DoCallback(key);
         }
-    }
-    else
-    {
-        if (PREF_IS_LOCKED(pref))
-        {
-            pref->flags &= ~PREF_LOCKED;
+    } else {
+        if (pref->prefFlags.IsLocked()) {
+            pref->prefFlags.SetLocked(false);
             pref_DoCallback(key);
         }
     }
     return NS_OK;
 }
 
 /*
  * Hash table functions
  */
 static bool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType type)
 {
     bool changed = true;
-    if (type & PREF_STRING)
-    {
-        if (oldValue.stringVal && newValue.stringVal)
+    switch(type) {
+      case PrefType::String:
+        if (oldValue.stringVal && newValue.stringVal) {
             changed = (strcmp(oldValue.stringVal, newValue.stringVal) != 0);
+        }
+        break;
+      case PrefType::Int:
+        changed = oldValue.intVal != newValue.intVal;
+        break;
+      case PrefType::Bool:
+        changed = oldValue.boolVal != newValue.boolVal;
+        break;
+      case PrefType::Invalid:
+      default:
+        changed = false;
+        break;
     }
-    else if (type & PREF_INT)
-        changed = oldValue.intVal != newValue.intVal;
-    else if (type & PREF_BOOL)
-        changed = oldValue.boolVal != newValue.boolVal;
     return changed;
 }
 
 /*
  * Overwrite the type and value of an existing preference. Caller must
  * ensure that they are not changing the type of a preference that has
  * a default value.
  */
-static void pref_SetValue(PrefValue* existingValue, uint16_t *existingFlags,
-                          PrefValue newValue, PrefType newType)
+static PrefTypeFlags pref_SetValue(PrefValue* existingValue, PrefTypeFlags flags,
+                                   PrefValue newValue, PrefType newType)
 {
-    if ((*existingFlags & PREF_STRING) && existingValue->stringVal) {
+    if (flags.IsTypeString() && existingValue->stringVal) {
         PL_strfree(existingValue->stringVal);
     }
-    *existingFlags = (*existingFlags & ~PREF_VALUETYPE_MASK) | newType;
-    if (newType & PREF_STRING) {
+    flags.SetPrefType(newType);
+    if (flags.IsTypeString()) {
         PR_ASSERT(newValue.stringVal);
         existingValue->stringVal = newValue.stringVal ? PL_strdup(newValue.stringVal) : nullptr;
     }
     else {
         *existingValue = newValue;
     }
+    return flags;
 }
 
 PrefHashEntry* pref_HashTableLookup(const char *key)
 {
 #ifndef MOZ_B2G
     MOZ_ASSERT(NS_IsMainThread());
 #endif
 
@@ -718,73 +708,63 @@ nsresult pref_HashPref(const char *key, 
     auto pref = static_cast<PrefHashEntry*>(gHashTable->Add(key, fallible));
     if (!pref)
         return NS_ERROR_OUT_OF_MEMORY;
 
     // new entry, better initialize
     if (!pref->key) {
 
         // initialize the pref entry
-        pref->flags = type;
+        pref->prefFlags.Reset().SetPrefType(type);
         pref->key = ArenaStrDup(key, &gPrefNameArena);
         memset(&pref->defaultPref, 0, sizeof(pref->defaultPref));
         memset(&pref->userPref, 0, sizeof(pref->userPref));
-    }
-    else if ((pref->flags & PREF_HAS_DEFAULT) && PREF_TYPE(pref) != type)
-    {
+    } else if (pref->prefFlags.HasDefault() && !pref->prefFlags.IsPrefType(type)) {
         NS_WARNING(nsPrintfCString("Trying to overwrite value of default pref %s with the wrong type!", key).get());
         return NS_ERROR_UNEXPECTED;
     }
 
     bool valueChanged = false;
-    if (flags & kPrefSetDefault)
-    {
-        if (!PREF_IS_LOCKED(pref))
-        {       /* ?? change of semantics? */
+    if (flags & kPrefSetDefault) {
+        if (!pref->prefFlags.IsLocked()) {
+            /* ?? change of semantics? */
             if (pref_ValueChanged(pref->defaultPref, value, type) ||
-                !(pref->flags & PREF_HAS_DEFAULT))
-            {
-                pref_SetValue(&pref->defaultPref, &pref->flags, value, type);
-                pref->flags |= PREF_HAS_DEFAULT;
-                if (flags & kPrefStickyDefault)
-                    pref->flags |= PREF_STICKY_DEFAULT;
-                if (!PREF_HAS_USER_VALUE(pref))
+                !pref->prefFlags.HasDefault()) {
+                pref->prefFlags = pref_SetValue(&pref->defaultPref, pref->prefFlags, value, type).SetHasDefault(true);
+                if (flags & kPrefStickyDefault) {
+                    pref->prefFlags.SetHasStickyDefault(true);
+                }
+                if (!pref->prefFlags.HasUserValue()) {
                     valueChanged = true;
+                }
             }
             // What if we change the default to be the same as the user value?
             // Should we clear the user value?
         }
-    }
-    else
-    {
+    } else {
         /* If new value is same as the default value and it's not a "sticky"
            pref, then un-set the user value.
            Otherwise, set the user value only if it has changed */
-        if ((pref->flags & PREF_HAS_DEFAULT) &&
-            !(pref->flags & PREF_STICKY_DEFAULT) &&
+        if ((pref->prefFlags.HasDefault()) &&
+            !(pref->prefFlags.HasStickyDefault()) &&
             !pref_ValueChanged(pref->defaultPref, value, type) &&
-            !(flags & kPrefForceSet))
-        {
-            if (PREF_HAS_USER_VALUE(pref))
-            {
+            !(flags & kPrefForceSet)) {
+            if (pref->prefFlags.HasUserValue()) {
                 /* XXX should we free a user-set string value if there is one? */
-                pref->flags &= ~PREF_USERSET;
-                if (!PREF_IS_LOCKED(pref)) {
+                pref->prefFlags.SetHasUserValue(false);
+                if (!pref->prefFlags.IsLocked()) {
                     gDirty = true;
                     valueChanged = true;
                 }
             }
-        }
-        else if (!PREF_HAS_USER_VALUE(pref) ||
-                 PREF_TYPE(pref) != type ||
-                 pref_ValueChanged(pref->userPref, value, type) )
-        {
-            pref_SetValue(&pref->userPref, &pref->flags, value, type);
-            pref->flags |= PREF_USERSET;
-            if (!PREF_IS_LOCKED(pref)) {
+        } else if (!pref->prefFlags.HasUserValue() ||
+                 !pref->prefFlags.IsPrefType(type) ||
+                 pref_ValueChanged(pref->userPref, value, type) ) {
+            pref->prefFlags = pref_SetValue(&pref->userPref, pref->prefFlags, value, type).SetHasUserValue(true);
+            if (!pref->prefFlags.IsLocked()) {
                 gDirty = true;
                 valueChanged = true;
             }
         }
     }
 
     if (valueChanged) {
         return pref_DoCallback(key);
@@ -803,39 +783,34 @@ pref_SizeOfPrivateData(MallocSizeOf aMal
     return n;
 }
 
 PrefType
 PREF_GetPrefType(const char *pref_name)
 {
     if (gHashTable) {
         PrefHashEntry* pref = pref_HashTableLookup(pref_name);
-        if (pref)
-        {
-            if (pref->flags & PREF_STRING)
-                return PREF_STRING;
-            else if (pref->flags & PREF_INT)
-                return PREF_INT;
-            else if (pref->flags & PREF_BOOL)
-                return PREF_BOOL;
+        if (pref) {
+            return pref->prefFlags.GetPrefType();
         }
     }
-    return PREF_INVALID;
+    return PrefType::Invalid;
 }
 
 /* -- */
 
 bool
 PREF_PrefIsLocked(const char *pref_name)
 {
     bool result = false;
     if (gIsAnyPrefLocked && gHashTable) {
         PrefHashEntry* pref = pref_HashTableLookup(pref_name);
-        if (pref && PREF_IS_LOCKED(pref))
+        if (pref && pref->prefFlags.IsLocked()) {
             result = true;
+        }
     }
 
     return result;
 }
 
 /* Adds a node to the beginning of the callback list. */
 void
 PREF_RegisterCallback(const char *pref_node,
@@ -968,15 +943,21 @@ static nsresult pref_DoCallback(const ch
 }
 
 void PREF_ReaderCallback(void       *closure,
                          const char *pref,
                          PrefValue   value,
                          PrefType    type,
                          bool        isDefault,
                          bool        isStickyDefault)
+
 {
-    uint32_t flags = isDefault ? kPrefSetDefault : kPrefForceSet;
-    if (isDefault && isStickyDefault) {
-        flags |= kPrefStickyDefault;
+    uint32_t flags = 0;
+    if (isDefault) {
+        flags |= kPrefSetDefault;
+        if (isStickyDefault) {
+            flags |= kPrefStickyDefault;
+        }
+    } else {
+        flags |= kPrefForceSet;
     }
     pref_HashPref(pref, value, type, flags);
 }
--- a/modules/libpref/prefapi.h
+++ b/modules/libpref/prefapi.h
@@ -23,58 +23,115 @@ static const uint32_t MAX_ADVISABLE_PREF
 
 typedef union
 {
     char*       stringVal;
     int32_t     intVal;
     bool        boolVal;
 } PrefValue;
 
-struct PrefHashEntry : PLDHashEntryHdr
-{
-    uint16_t flags; // This field goes first to minimize struct size on 64-bit.
-    const char *key;
-    PrefValue defaultPref;
-    PrefValue userPref;
-};
-
 /*
 // <font color=blue>
 // The Init function initializes the preference context and creates
 // the preference hashtable.
 // </font>
 */
 void        PREF_Init();
 
 /*
-// Cleanup should be called at program exit to free the 
+// Cleanup should be called at program exit to free the
 // list of registered callbacks.
 */
 void        PREF_Cleanup();
 void        PREF_CleanupPrefs();
 
 /*
 // <font color=blue>
-// Preference flags, including the native type of the preference
+// Preference flags, including the native type of the preference. Changing any of these
+// values will require modifying the code inside of PrefTypeFlags class.
 // </font>
 */
 
-typedef enum { PREF_INVALID = 0,
-               PREF_LOCKED = 1, PREF_USERSET = 2, PREF_CONFIG = 4, PREF_REMOTE = 8,
-               PREF_LILOCAL = 16, PREF_STRING = 32, PREF_INT = 64, PREF_BOOL = 128,
-               PREF_HAS_DEFAULT = 256,
-               // pref is default pref with "sticky" semantics
-               PREF_STICKY_DEFAULT = 512,
-               PREF_VALUETYPE_MASK = (PREF_STRING | PREF_INT | PREF_BOOL)
-             } PrefType;
+enum class PrefType {
+  Invalid = 0,
+  String = 1,
+  Int = 2,
+  Bool = 3,
+};
+
+// Keep the type of the preference, as well as the flags guiding its behaviour.
+class PrefTypeFlags
+{
+public:
+  PrefTypeFlags() : mValue(AsInt(PrefType::Invalid)) {}
+  explicit PrefTypeFlags(PrefType aType) : mValue(AsInt(aType)) {}
+  PrefTypeFlags& Reset() { mValue = AsInt(PrefType::Invalid); return *this; }
+
+  bool IsTypeValid() const { return !IsPrefType(PrefType::Invalid); }
+  bool IsTypeString() const { return IsPrefType(PrefType::String); }
+  bool IsTypeInt() const { return IsPrefType(PrefType::Int); }
+  bool IsTypeBool() const { return IsPrefType(PrefType::Bool); }
+  bool IsPrefType(PrefType type) const { return GetPrefType() == type; }
+
+  PrefTypeFlags& SetPrefType(PrefType aType) {
+    mValue = mValue - AsInt(GetPrefType()) + AsInt(aType);
+    return *this;
+  }
+  PrefType GetPrefType() const {
+    return (PrefType)(mValue & (AsInt(PrefType::String) |
+                                AsInt(PrefType::Int) |
+                                AsInt(PrefType::Bool)));
+  }
+
+  bool HasDefault() const { return mValue & PREF_FLAG_HAS_DEFAULT; }
+  PrefTypeFlags& SetHasDefault(bool aSetOrUnset) { return SetFlag(PREF_FLAG_HAS_DEFAULT, aSetOrUnset); }
+
+  bool HasStickyDefault() const { return mValue & PREF_FLAG_STICKY_DEFAULT; }
+  PrefTypeFlags& SetHasStickyDefault(bool aSetOrUnset) { return SetFlag(PREF_FLAG_STICKY_DEFAULT, aSetOrUnset); }
+
+  bool IsLocked() const { return mValue & PREF_FLAG_LOCKED; }
+  PrefTypeFlags& SetLocked(bool aSetOrUnset) { return SetFlag(PREF_FLAG_LOCKED, aSetOrUnset); }
+
+  bool HasUserValue() const { return mValue & PREF_FLAG_USERSET; }
+  PrefTypeFlags& SetHasUserValue(bool aSetOrUnset) { return SetFlag(PREF_FLAG_USERSET, aSetOrUnset); }
+
+private:
+  static uint16_t AsInt(PrefType aType) { return (uint16_t)aType; }
+
+  PrefTypeFlags& SetFlag(uint16_t aFlag, bool aSetOrUnset) {
+    mValue = aSetOrUnset ? mValue | aFlag : mValue & ~aFlag;
+    return *this;
+  }
+
+  // Pack both the value of type (PrefType) and flags into the same int.  This is why
+  // the flag enum starts at 4, as PrefType occupies the bottom two bits.
+  enum {
+    PREF_FLAG_LOCKED = 4,
+    PREF_FLAG_USERSET = 8,
+    PREF_FLAG_CONFIG = 16,
+    PREF_FLAG_REMOTE = 32,
+    PREF_FLAG_LILOCAL = 64,
+    PREF_FLAG_HAS_DEFAULT = 128,
+    PREF_FLAG_STICKY_DEFAULT = 256,
+  };
+  uint16_t mValue;
+};
+
+struct PrefHashEntry : PLDHashEntryHdr
+{
+    PrefTypeFlags prefFlags; // This field goes first to minimize struct size on 64-bit.
+    const char *key;
+    PrefValue defaultPref;
+    PrefValue userPref;
+};
 
 /*
 // <font color=blue>
 // Set the various types of preferences.  These functions take a dotted
-// notation of the preference name (e.g. "browser.startup.homepage").  
+// notation of the preference name (e.g. "browser.startup.homepage").
 // Note that this will cause the preference to be saved to the file if
 // it is different from the default.  In other words, these are used
 // to set the _user_ preferences.
 //
 // If set_default is set to true however, it sets the default value.
 // This will only affect the program behavior if the user does not have a value
 // saved over it for the particular preference.  In addition, these will never
 // be saved out to disk.
@@ -98,18 +155,18 @@ bool     PREF_HasUserPref(const char* pr
 // error value.  At the moment, this is simply an int but it may
 // be converted to an enum once the global error strategy is worked out.
 //
 // They will perform conversion if the type doesn't match what was requested.
 // (if it is reasonably possible)
 // </font>
 */
 nsresult PREF_GetIntPref(const char *pref,
-                           int32_t * return_int, bool get_default);	
-nsresult PREF_GetBoolPref(const char *pref, bool * return_val, bool get_default);	
+                           int32_t * return_int, bool get_default);
+nsresult PREF_GetBoolPref(const char *pref, bool * return_val, bool get_default);
 /*
 // <font color=blue>
 // These functions are similar to the above "Get" version with the significant
 // difference that the preference module will alloc the memory (e.g. XP_STRDUP) and
 // the caller will need to be responsible for freeing it...
 // </font>
 */
 nsresult PREF_CopyCharPref(const char *pref, char ** return_buf, bool get_default);
@@ -168,20 +225,20 @@ typedef void (*PrefChangedFunc) (const c
 /*
 // <font color=blue>
 // Register a callback.  This takes a node in the preference tree and will
 // call the callback function if anything below that node is modified.
 // Unregister returns PREF_NOERROR if a callback was found that
 // matched all the parameters; otherwise it returns PREF_ERROR.
 // </font>
 */
-void PREF_RegisterCallback( const char* domain,
-								PrefChangedFunc callback, void* instance_data );
-nsresult PREF_UnregisterCallback( const char* domain,
-								PrefChangedFunc callback, void* instance_data );
+void PREF_RegisterCallback(const char* domain,
+                           PrefChangedFunc callback, void* instance_data );
+nsresult PREF_UnregisterCallback(const char* domain,
+                                 PrefChangedFunc callback, void* instance_data );
 
 /*
  * Used by nsPrefService as the callback function of the 'pref' parser
  */
 void PREF_ReaderCallback( void *closure,
                           const char *pref,
                           PrefValue   value,
                           PrefType    type,
--- a/modules/libpref/prefread.cpp
+++ b/modules/libpref/prefread.cpp
@@ -109,27 +109,27 @@ pref_GrowBuf(PrefParseState *ps)
  * @return false to indicate a fatal error.
  */
 static bool
 pref_DoCallback(PrefParseState *ps)
 {
     PrefValue  value;
 
     switch (ps->vtype) {
-    case PREF_STRING:
+    case PrefType::String:
         value.stringVal = ps->vb;
         break;
-    case PREF_INT:
+    case PrefType::Int:
         if ((ps->vb[0] == '-' || ps->vb[0] == '+') && ps->vb[1] == '\0') {
             NS_WARNING("malformed integer value");
             return false;
         }
         value.intVal = atoi(ps->vb);
         break;
-    case PREF_BOOL:
+    case PrefType::Bool:
         value.boolVal = (ps->vb == kTrue);
         break;
     default:
         break;
     }
     (*ps->reader)(ps->closure, ps->lb, value, ps->vtype, ps->fdefault,
                   ps->fstickydefault);
     return true;
@@ -149,17 +149,17 @@ PREF_FinalizeParseState(PrefParseState *
     if (ps->lb)
         free(ps->lb);
 }
 
 /**
  * Pseudo-BNF
  * ----------
  * function      = LJUNK function-name JUNK function-args
- * function-name = "user_pref" | "pref"
+ * function-name = "user_pref" | "pref" | "sticky_pref"
  * function-args = "(" JUNK pref-name JUNK "," JUNK pref-value JUNK ")" JUNK ";"
  * pref-name     = quoted-string
  * pref-value    = quoted-string | "true" | "false" | integer-value
  * JUNK          = *(WS | comment-block | comment-line)
  * LJUNK         = *(WS | comment-block | comment-line | bcomment-line)
  * WS            = SP | HT | LF | VT | FF | CR
  * SP            = <US-ASCII SP, space (32)>
  * HT            = <US-ASCII HT, horizontal-tab (9)>
@@ -183,32 +183,37 @@ PREF_ParseBuf(PrefParseState *ps, const 
     for (end = buf + bufLen; buf != end; ++buf) {
         c = *buf;
         switch (state) {
         /* initial state */
         case PREF_PARSE_INIT:
             if (ps->lbcur != ps->lb) { /* reset state */
                 ps->lbcur = ps->lb;
                 ps->vb    = nullptr;
-                ps->vtype = PREF_INVALID;
+                ps->vtype = PrefType::Invalid;
                 ps->fdefault = false;
                 ps->fstickydefault = false;
             }
             switch (c) {
             case '/':       /* begin comment block or line? */
                 state = PREF_PARSE_COMMENT_MAYBE_START;
                 break;
             case '#':       /* accept shell style comments */
                 state = PREF_PARSE_UNTIL_EOL;
                 break;
             case 'u':       /* indicating user_pref */
+            case 's':       /* indicating sticky_pref */
             case 'p':       /* indicating pref */
-            case 's':       /* indicating sticky_pref */
-                ps->smatch = (c == 'u' ? kUserPref :
-                             (c == 's' ? kPrefSticky : kPref));
+                if (c == 'u') {
+                  ps->smatch = kUserPref;
+                } else if (c == 's') {
+                  ps->smatch = kPrefSticky;
+                } else {
+                  ps->smatch = kPref;
+                }
                 ps->sindex = 1;
                 ps->nextstate = PREF_PARSE_UNTIL_OPEN_PAREN;
                 state = PREF_PARSE_MATCH_STRING;
                 break;
             /* else skip char */
             }
             break;
 
@@ -243,16 +248,18 @@ PREF_ParseBuf(PrefParseState *ps, const 
             else
                 *ps->lbcur++ = c;
             break;
 
         /* name parsing */
         case PREF_PARSE_UNTIL_NAME:
             if (c == '\"' || c == '\'') {
                 ps->fdefault = (ps->smatch == kPref || ps->smatch == kPrefSticky);
+                ps->fdefault = (ps->smatch == kPref ||
+                                ps->smatch == kPrefSticky);
                 ps->fstickydefault = (ps->smatch == kPrefSticky);
                 ps->quotechar = c;
                 ps->nextstate = PREF_PARSE_UNTIL_COMMA; /* return here when done */
                 state = PREF_PARSE_QUOTED_STRING;
             }
             else if (c == '/') {       /* allow embedded comment */
                 ps->nextstate = state; /* return here when done with comment */
                 state = PREF_PARSE_COMMENT_MAYBE_START;
@@ -279,31 +286,31 @@ PREF_ParseBuf(PrefParseState *ps, const 
             }
             break;
 
         /* value parsing */
         case PREF_PARSE_UNTIL_VALUE:
             /* the pref value type is unknown.  so, we scan for the first
              * character of the value, and determine the type from that. */
             if (c == '\"' || c == '\'') {
-                ps->vtype = PREF_STRING;
+                ps->vtype = PrefType::String;
                 ps->quotechar = c;
                 ps->nextstate = PREF_PARSE_UNTIL_CLOSE_PAREN;
                 state = PREF_PARSE_QUOTED_STRING;
             }
             else if (c == 't' || c == 'f') {
                 ps->vb = (char *) (c == 't' ? kTrue : kFalse);
-                ps->vtype = PREF_BOOL;
+                ps->vtype = PrefType::Bool;
                 ps->smatch = ps->vb;
                 ps->sindex = 1;
                 ps->nextstate = PREF_PARSE_UNTIL_CLOSE_PAREN;
                 state = PREF_PARSE_MATCH_STRING;
             }
             else if (isdigit(c) || (c == '-') || (c == '+')) {
-                ps->vtype = PREF_INT;
+                ps->vtype = PrefType::Int;
                 /* write c to line buffer... */
                 if (ps->lbcur == ps->lbend && !pref_GrowBuf(ps))
                     return false; /* out of memory */
                 *ps->lbcur++ = c;
                 state = PREF_PARSE_INT_VALUE;
             }
             else if (c == '/') {       /* allow embedded comment */
                 ps->nextstate = state; /* return here when done with comment */
@@ -506,23 +513,22 @@ PREF_ParseBuf(PrefParseState *ps, const 
             }
             else if (!isspace(c)) {
                 NS_WARNING("malformed pref file");
                 return false;
             }
             break;
         case PREF_PARSE_UNTIL_CLOSE_PAREN:
             /* tolerate only whitespace and embedded comments  */
-            if (c == ')')
+            if (c == ')') {
                 state = PREF_PARSE_UNTIL_SEMICOLON;
-            else if (c == '/') {
+            } else if (c == '/') {
                 ps->nextstate = state; /* return here when done with comment */
                 state = PREF_PARSE_COMMENT_MAYBE_START;
-            }
-            else if (!isspace(c)) {
+            } else if (!isspace(c)) {
                 NS_WARNING("malformed pref file");
                 return false;
             }
             break;
 
         /* function terminator ';' parsing */
         case PREF_PARSE_UNTIL_SEMICOLON:
             /* tolerate only whitespace and embedded comments */
--- a/modules/libpref/prefread.h
+++ b/modules/libpref/prefread.h
@@ -49,17 +49,17 @@ typedef struct PrefParseState {
     int         esclen;     /* length in esctmp              */
     char        esctmp[6];  /* raw escape to put back if err */
     char        quotechar;  /* char delimiter for quotations */
     char       *lb;         /* line buffer (only allocation) */
     char       *lbcur;      /* line buffer cursor            */
     char       *lbend;      /* line buffer end               */
     char       *vb;         /* value buffer (ptr into lb)    */
     PrefType    vtype;      /* PREF_STRING,INT,BOOL          */
-    bool        fdefault;   /* true if (default) pref     */
+    bool        fdefault;   /* true if (default) pref        */
     bool        fstickydefault; /* true if (sticky) pref     */
 } PrefParseState;
 
 /**
  * PREF_InitParseState
  *
  * Called to initialize a PrefParseState instance.
  * 
--- a/modules/woff2/src/store_bytes.h
+++ b/modules/woff2/src/store_bytes.h
@@ -32,17 +32,17 @@ inline size_t StoreU32(uint8_t* dst, siz
   return offset + 4;
 }
 
 inline size_t Store16(uint8_t* dst, size_t offset, int x) {
 #if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
   *reinterpret_cast<uint16_t*>(dst + offset) =
       ((x & 0xFF) << 8) | ((x & 0xFF00) >> 8);
 #elif (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
-  *reinterpret_cast<uint16_t*>(dst + offset) = reinterpret_cast<uint16_t>(x);
+  *reinterpret_cast<uint16_t*>(dst + offset) = static_cast<uint16_t>(x);
 #else
   dst[offset] = x >> 8;
   dst[offset + 1] = x;
 #endif
   return offset + 2;
 }
 
 inline void StoreU32(uint32_t val, size_t* offset, uint8_t* dst) {
@@ -54,17 +54,17 @@ inline void StoreU32(uint32_t val, size_
 
 inline void Store16(int val, size_t* offset, uint8_t* dst) {
 #if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
   *reinterpret_cast<uint16_t*>(dst + *offset) =
       ((val & 0xFF) << 8) | ((val & 0xFF00) >> 8);
   *offset += 2;
 #elif (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
   *reinterpret_cast<uint16_t*>(dst + *offset) =
-      reinterpret_cast<uint16_t>(val);
+      static_cast<uint16_t>(val);
   *offset += 2;
 #else
   dst[(*offset)++] = val >> 8;
   dst[(*offset)++] = val;
 #endif
 }
 
 inline void StoreBytes(const uint8_t* data, size_t len,
--- a/netwerk/cache2/CacheEntry.cpp
+++ b/netwerk/cache2/CacheEntry.cpp
@@ -540,17 +540,16 @@ already_AddRefed<CacheEntryHandle> Cache
     mozilla::MutexAutoUnlock unlock(mLock);
 
     // The following call dooms this entry (calls DoomAlreadyRemoved on us)
     nsresult rv = CacheStorageService::Self()->AddStorageEntry(
       GetStorageID(), GetURI(), GetEnhanceID(),
       mUseDisk && !aMemoryOnly,
       mSkipSizeCheck,
       mPinned,
-      true, // always create
       true, // truncate existing (this one)
       getter_AddRefs(handle));
 
     if (NS_SUCCEEDED(rv)) {
       newEntry = handle->Entry();
       LOG(("  exchanged entry %p by entry %p, rv=0x%08x", this, newEntry.get(), rv));
       newEntry->AsyncOpen(aCallback, nsICacheStorage::OPEN_TRUNCATE);
     } else {
@@ -1067,39 +1066,38 @@ NS_IMETHODIMP CacheEntry::GetIsForcedVal
   MOZ_ASSERT(mState > LOADING);
 
   if (mPinned) {
     *aIsForcedValid = true;
     return NS_OK;
   }
 
   nsAutoCString key;
-
-  nsresult rv = HashingKeyWithStorage(key);
+  nsresult rv = HashingKey(key);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  *aIsForcedValid = CacheStorageService::Self()->IsForcedValidEntry(key);
+  *aIsForcedValid = CacheStorageService::Self()->IsForcedValidEntry(mStorageID, key);
   LOG(("CacheEntry::GetIsForcedValid [this=%p, IsForcedValid=%d]", this, *aIsForcedValid));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP CacheEntry::ForceValidFor(uint32_t aSecondsToTheFuture)
 {
   LOG(("CacheEntry::ForceValidFor [this=%p, aSecondsToTheFuture=%d]", this, aSecondsToTheFuture));
 
   nsAutoCString key;
-  nsresult rv = HashingKeyWithStorage(key);
+  nsresult rv = HashingKey(key);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  CacheStorageService::Self()->ForceEntryValidFor(key, aSecondsToTheFuture);
+  CacheStorageService::Self()->ForceEntryValidFor(mStorageID, key, aSecondsToTheFuture);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP CacheEntry::SetExpirationTime(uint32_t aExpirationTime)
 {
   NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
 
@@ -1320,16 +1318,18 @@ NS_IMETHODIMP CacheEntry::AsyncDoom(nsIC
   LOG(("CacheEntry::AsyncDoom [this=%p]", this));
 
   {
     mozilla::MutexAutoLock lock(mLock);
 
     if (mIsDoomed || mDoomCallback)
       return NS_ERROR_IN_PROGRESS; // to aggregate have DOOMING state
 
+    RemoveForcedValidity();
+
     mIsDoomed = true;
     mDoomCallback = aCallback;
   }
 
   // This immediately removes the entry from the master hashtable and also
   // immediately dooms the file.  This way we make sure that any consumer
   // after this point asking for the same entry won't get
   //   a) this entry
@@ -1618,16 +1618,18 @@ void CacheEntry::PurgeAndDoom()
 }
 
 void CacheEntry::DoomAlreadyRemoved()
 {
   LOG(("CacheEntry::DoomAlreadyRemoved [this=%p]", this));
 
   mozilla::MutexAutoLock lock(mLock);
 
+  RemoveForcedValidity();
+
   mIsDoomed = true;
 
   // Pretend pinning is know.  This entry is now doomed for good, so don't
   // bother with defering doom because of unknown pinning state any more.
   mPinningKnown = true;
 
   // This schedules dooming of the file, dooming is ensured to happen
   // sooner than demand to open the same file made after this point
@@ -1661,16 +1663,35 @@ void CacheEntry::DoomFile()
       rv = NS_OK;
     }
   }
 
   // Always posts to the main thread.
   OnFileDoomed(rv);
 }
 
+void CacheEntry::RemoveForcedValidity()
+{
+  mLock.AssertCurrentThreadOwns();
+
+  nsresult rv;
+
+  if (mIsDoomed) {
+    return;
+  }
+
+  nsAutoCString entryKey;
+  rv = HashingKey(entryKey);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  CacheStorageService::Self()->RemoveEntryForceValid(mStorageID, entryKey);
+}
+
 void CacheEntry::BackgroundOp(uint32_t aOperations, bool aForceAsync)
 {
   mLock.AssertCurrentThreadOwns();
 
   if (!CacheStorageService::IsOnManagementThread() || aForceAsync) {
     if (mBackgroundOperations.Set(aOperations))
       CacheStorageService::Self()->Dispatch(this);
 
--- a/netwerk/cache2/CacheEntry.h
+++ b/netwerk/cache2/CacheEntry.h
@@ -261,16 +261,19 @@ private:
   // Schedules a background operation on the management thread.
   // When executed on the management thread directly, the operation(s)
   // is (are) executed immediately.
   void BackgroundOp(uint32_t aOperation, bool aForceAsync = false);
   void StoreFrecency(double aFrecency);
 
   // Called only from DoomAlreadyRemoved()
   void DoomFile();
+  // When this entry is doomed the first time, this method removes
+  // any force-valid timing info for this entry.
+  void RemoveForcedValidity();
 
   already_AddRefed<CacheEntryHandle> ReopenTruncated(bool aMemoryOnly,
                                                      nsICacheEntryOpenCallback* aCallback);
   void TransferCallbacks(CacheEntry & aFromEntry);
 
   mozilla::Mutex mLock;
 
   // Reflects the number of existing handles for this entry
--- a/netwerk/cache2/CacheStorage.cpp
+++ b/netwerk/cache2/CacheStorage.cpp
@@ -99,17 +99,16 @@ NS_IMETHODIMP CacheStorage::AsyncOpenURI
 
     LOG(("CacheStorage::AsyncOpenURI loading from appcache"));
     return NS_OK;
   }
 
   RefPtr<CacheEntryHandle> entry;
   rv = CacheStorageService::Self()->AddStorageEntry(
     this, noRefURI, aIdExtension,
-    true, // create always
     truncate, // replace any existing one?
     getter_AddRefs(entry));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // May invoke the callback synchronously
   entry->Entry()->AsyncOpen(aCallback, aFlags);
 
   return NS_OK;
@@ -126,17 +125,16 @@ NS_IMETHODIMP CacheStorage::OpenTruncate
 
   nsCOMPtr<nsIURI> noRefURI;
   rv = aURI->CloneIgnoringRef(getter_AddRefs(noRefURI));
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<CacheEntryHandle> handle;
   rv = CacheStorageService::Self()->AddStorageEntry(
     this, noRefURI, aIdExtension,
-    true, // create always
     true, // replace any existing one
     getter_AddRefs(handle));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Just open w/o callback, similar to nsICacheEntry.recreate().
   handle->Entry()->AsyncOpen(nullptr, OPEN_TRUNCATE);
 
   // Return a write handler, consumer is supposed to fill in the entry.
--- a/netwerk/cache2/CacheStorageService.cpp
+++ b/netwerk/cache2/CacheStorageService.cpp
@@ -764,16 +764,21 @@ NS_IMETHODIMP CacheStorageService::Synth
 NS_IMETHODIMP CacheStorageService::Clear()
 {
   nsresult rv;
 
   if (CacheObserver::UseNewCache()) {
     {
       mozilla::MutexAutoLock lock(mLock);
 
+      {
+        mozilla::MutexAutoLock forcedValidEntriesLock(mForcedValidEntriesLock);
+        mForcedValidEntries.Clear();
+      }
+
       NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED);
 
       nsTArray<nsCString> keys;
       for (auto iter = sGlobalEntryTables->Iter(); !iter.Done(); iter.Next()) {
         keys.AppendElement(iter.Key());
       }
 
       for (uint32_t i = 0; i < keys.Length(); ++i) {
@@ -1016,17 +1021,17 @@ CacheStorageService::RemoveEntry(CacheEn
   }
 
   if (aOnlyUnreferenced) {
     if (aEntry->IsReferenced()) {
       LOG(("  still referenced, not removing"));
       return false;
     }
 
-    if (!aEntry->IsUsingDisk() && IsForcedValidEntry(entryKey)) {
+    if (!aEntry->IsUsingDisk() && IsForcedValidEntry(aEntry->GetStorageID(), entryKey)) {
       LOG(("  forced valid, not removing"));
       return false;
     }
   }
 
   CacheEntryTable* entries;
   if (sGlobalEntryTables->Get(aEntry->GetStorageID(), &entries))
     RemoveExactEntry(entries, entryKey, aEntry, false /* don't overwrite */);
@@ -1089,54 +1094,69 @@ CacheStorageService::RecordMemoryOnlyEnt
   }
   else {
     RemoveExactEntry(entries, entryKey, aEntry, aOverwrite);
   }
 }
 
 // Checks if a cache entry is forced valid (will be loaded directly from cache
 // without further validation) - see nsICacheEntry.idl for further details
-bool CacheStorageService::IsForcedValidEntry(nsACString &aCacheEntryKey)
+bool CacheStorageService::IsForcedValidEntry(nsACString const &aContextKey,
+                                             nsACString const &aEntryKey)
+{
+  return IsForcedValidEntry(aContextKey + aEntryKey);
+}
+
+bool CacheStorageService::IsForcedValidEntry(nsACString const &aContextEntryKey)
 {
   mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
 
   TimeStamp validUntil;
 
-  if (!mForcedValidEntries.Get(aCacheEntryKey, &validUntil)) {
+  if (!mForcedValidEntries.Get(aContextEntryKey, &validUntil)) {
     return false;
   }
 
   if (validUntil.IsNull()) {
     return false;
   }
 
   // Entry timeout not reached yet
   if (TimeStamp::NowLoRes() <= validUntil) {
     return true;
   }
 
   // Entry timeout has been reached
-  mForcedValidEntries.Remove(aCacheEntryKey);
+  mForcedValidEntries.Remove(aContextEntryKey);
   return false;
 }
 
 // Allows a cache entry to be loaded directly from cache without further
 // validation - see nsICacheEntry.idl for further details
-void CacheStorageService::ForceEntryValidFor(nsACString &aCacheEntryKey,
+void CacheStorageService::ForceEntryValidFor(nsACString const &aContextKey,
+                                             nsACString const &aEntryKey,
                                              uint32_t aSecondsToTheFuture)
 {
   mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
 
   TimeStamp now = TimeStamp::NowLoRes();
   ForcedValidEntriesPrune(now);
 
   // This will be the timeout
   TimeStamp validUntil = now + TimeDuration::FromSeconds(aSecondsToTheFuture);
 
-  mForcedValidEntries.Put(aCacheEntryKey, validUntil);
+  mForcedValidEntries.Put(aContextKey + aEntryKey, validUntil);
+}
+
+void CacheStorageService::RemoveEntryForceValid(nsACString const &aContextKey,
+                                                nsACString const &aEntryKey)
+{
+  mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
+
+  mForcedValidEntries.Remove(aContextKey + aEntryKey);
 }
 
 // Cleans out the old entries in mForcedValidEntries
 void CacheStorageService::ForcedValidEntriesPrune(TimeStamp &now)
 {
   static TimeDuration const oneMinute = TimeDuration::FromSeconds(60);
   static TimeStamp dontPruneUntil = now + oneMinute;
   if (now < dontPruneUntil)
@@ -1373,43 +1393,41 @@ CacheStorageService::MemoryPool::PurgeAl
 }
 
 // Methods exposed to and used by CacheStorage.
 
 nsresult
 CacheStorageService::AddStorageEntry(CacheStorage const* aStorage,
                                      nsIURI* aURI,
                                      const nsACString & aIdExtension,
-                                     bool aCreateIfNotExist,
                                      bool aReplace,
                                      CacheEntryHandle** aResult)
 {
   NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
 
   NS_ENSURE_ARG(aStorage);
 
   nsAutoCString contextKey;
   CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
 
   return AddStorageEntry(contextKey, aURI, aIdExtension,
                          aStorage->WriteToDisk(),
                          aStorage->SkipSizeCheck(),
                          aStorage->Pinning(),
-                         aCreateIfNotExist, aReplace,
+                         aReplace,
                          aResult);
 }
 
 nsresult
 CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey,
                                      nsIURI* aURI,
                                      const nsACString & aIdExtension,
                                      bool aWriteToDisk,
                                      bool aSkipSizeCheck,
                                      bool aPin,
-                                     bool aCreateIfNotExist,
                                      bool aReplace,
                                      CacheEntryHandle** aResult)
 {
   NS_ENSURE_ARG(aURI);
 
   nsresult rv;
 
   nsAutoCString entryKey;
@@ -1435,37 +1453,47 @@ CacheStorageService::AddStorageEntry(nsC
       LOG(("  new storage entries table for context '%s'", aContextKey.BeginReading()));
     }
 
     bool entryExists = entries->Get(entryKey, getter_AddRefs(entry));
 
     if (entryExists && !aReplace) {
       // check whether we want to turn this entry to a memory-only.
       if (MOZ_UNLIKELY(!aWriteToDisk) && MOZ_LIKELY(entry->IsUsingDisk())) {
-        LOG(("  entry is persistnet but we want mem-only, replacing it"));
+        LOG(("  entry is persistent but we want mem-only, replacing it"));
         aReplace = true;
       }
     }
 
     // If truncate is demanded, delete and doom the current entry
     if (entryExists && aReplace) {
       entries->Remove(entryKey);
 
       LOG(("  dooming entry %p for %s because of OPEN_TRUNCATE", entry.get(), entryKey.get()));
       // On purpose called under the lock to prevent races of doom and open on I/O thread
       // No need to remove from both memory-only and all-entries tables.  The new entry
       // will overwrite the shadow entry in its ctor.
       entry->DoomAlreadyRemoved();
 
       entry = nullptr;
       entryExists = false;
+
+      // Would only lead to deleting force-valid timestamp again.  We don't need the
+      // replace information anymore after this point anyway.
+      aReplace = false;
     }
 
-    // Ensure entry for the particular URL, if not read/only
-    if (!entryExists && (aCreateIfNotExist || aReplace)) {
+    // Ensure entry for the particular URL
+    if (!entryExists) {
+      // When replacing with a new entry, always remove the current force-valid timestamp,
+      // this is the only place to do it.
+      if (aReplace) {
+        RemoveEntryForceValid(aContextKey, entryKey);
+      }
+
       // Entry is not in the hashtable or has just been truncated...
       entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk, aSkipSizeCheck, aPin);
       entries->Put(entryKey, entry);
       LOG(("  new entry %p for %s", entry.get(), entryKey.get()));
     }
 
     if (entry) {
       // Here, if this entry was not for a long time referenced by any consumer,
@@ -1634,16 +1662,20 @@ CacheStorageService::DoomStorageEntry(Ca
         else {
           // Otherwise, leave it
           LOG(("  leaving entry %p for %s [storage use disk=%d, entry use disk=%d]",
             entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDisk()));
           entry = nullptr;
         }
       }
     }
+
+    if (!entry) {
+      RemoveEntryForceValid(contextKey, entryKey);
+    }
   }
 
   if (entry) {
     LOG(("  dooming entry %p for %s", entry.get(), entryKey.get()));
     return entry->AsyncDoom(aCallback);
   }
 
   LOG(("  no entry loaded for %s", entryKey.get()));
@@ -1759,16 +1791,31 @@ CacheStorageService::DoomStorageEntries(
     if (memoryEntries && sGlobalEntryTables->Get(aContextKey, &diskEntries)) {
       for (auto iter = memoryEntries->Iter(); !iter.Done(); iter.Next()) {
         auto entry = iter.Data();
         RemoveExactEntry(diskEntries, iter.Key(), entry, false);
       }
     }
   }
 
+  {
+    mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
+
+    for (auto iter = mForcedValidEntries.Iter(); !iter.Done(); iter.Next()) {
+      bool matches;
+      DebugOnly<nsresult> rv = CacheFileUtils::KeyMatchesLoadContextInfo(
+        iter.Key(), aContext, &matches);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+      if (matches) {
+        iter.Remove();
+      }
+    }
+  }
+
   // An artificial callback.  This is a candidate for removal tho.  In the new
   // cache any 'doom' or 'evict' function ensures that the entry or entries
   // being doomed is/are not accessible after the function returns.  So there is
   // probably no need for a callback - has no meaning.  But for compatibility
   // with the old cache that is still in the tree we keep the API similar to be
   // able to make tests as well as other consumers work for now.
   class Callback : public nsRunnable
   {
@@ -1819,34 +1866,38 @@ CacheStorageService::CacheFileDoomed(nsI
   nsAutoCString contextKey;
   CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, contextKey);
 
   nsAutoCString entryKey;
   CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURISpec, entryKey);
 
   mozilla::MutexAutoLock lock(mLock);
 
-  if (mShutdown)
+  if (mShutdown) {
     return;
+  }
 
   CacheEntryTable* entries;
-  if (!sGlobalEntryTables->Get(contextKey, &entries))
-    return;
-
   RefPtr<CacheEntry> entry;
-  if (!entries->Get(entryKey, getter_AddRefs(entry)))
-    return;
 
-  if (!entry->IsFileDoomed())
-    return;
+  if (sGlobalEntryTables->Get(contextKey, &entries) &&
+      entries->Get(entryKey, getter_AddRefs(entry))) {
+    if (entry->IsFileDoomed()) {
+      // Need to remove under the lock to avoid possible race leading
+      // to duplication of the entry per its key.
+      RemoveExactEntry(entries, entryKey, entry, false);
+      entry->DoomAlreadyRemoved();
+    }
 
-  // Need to remove under the lock to avoid possible race leading
-  // to duplication of the entry per its key.
-  RemoveExactEntry(entries, entryKey, entry, false);
-  entry->DoomAlreadyRemoved();
+    // Entry found, but it's not the entry that has been found doomed
+    // by the lower eviction layer.  Just leave everything unchanged.
+    return;
+  }
+
+  RemoveEntryForceValid(contextKey, entryKey);
 }
 
 bool
 CacheStorageService::GetCacheEntryInfo(nsILoadContextInfo* aLoadContextInfo,
                                        const nsACString & aIdExtension,
                                        const nsACString & aURISpec,
                                        EntryInfoCallback *aCallback)
 {
--- a/netwerk/cache2/CacheStorageService.h
+++ b/netwerk/cache2/CacheStorageService.h
@@ -153,49 +153,60 @@ private:
                              bool aOnlyInMemory,
                              bool aOverwrite);
 
   /**
    * Sets a cache entry valid (overrides the default loading behavior by loading
    * directly from cache) for the given number of seconds
    * See nsICacheEntry.idl for more details
    */
-  void ForceEntryValidFor(nsACString &aCacheEntryKey,
+  void ForceEntryValidFor(nsACString const &aContextKey,
+                          nsACString const &aEntryKey,
                           uint32_t aSecondsToTheFuture);
 
+  /**
+   * Remove the validity info
+   */
+  void RemoveEntryForceValid(nsACString const &aContextKey,
+                             nsACString const &aEntryKey);
+
+  /**
+   * Retrieves the status of the cache entry to see if it has been forced valid
+   * (so it will loaded directly from cache without further validation)
+   */
+  bool IsForcedValidEntry(nsACString const &aContextKey,
+                          nsACString const &aEntryKey);
+
 private:
   friend class CacheIndex;
 
   /**
-   * Retrieves the status of the cache entry to see if it has been forced valid
-   * (so it will loaded directly from cache without further validation)
    * CacheIndex uses this to prevent a cache entry from being prememptively
    * thrown away when forced valid
    * See nsICacheEntry.idl for more details
    */
-  bool IsForcedValidEntry(nsACString &aCacheEntryKey);
+  bool IsForcedValidEntry(nsACString const &aEntryKeyWithContext);
 
 private:
-  // These are helpers for telemetry monitorying of the memory pools.
+  // These are helpers for telemetry monitoring of the memory pools.
   void TelemetryPrune(TimeStamp &now);
   void TelemetryRecordEntryCreation(CacheEntry const* entry);
   void TelemetryRecordEntryRemoval(CacheEntry const* entry);
 
 private:
   // Following methods are thread safe to call.
   friend class CacheStorage;
 
   /**
    * Get, or create when not existing and demanded, an entry for the storage
    * and uri+id extension.
    */
   nsresult AddStorageEntry(CacheStorage const* aStorage,
                            nsIURI* aURI,
                            const nsACString & aIdExtension,
-                           bool aCreateIfNotExist,
                            bool aReplace,
                            CacheEntryHandle** aResult);
 
   /**
    * Check existance of an entry.  This may throw NS_ERROR_NOT_AVAILABLE
    * when the information cannot be obtained synchronously w/o blocking.
    */
   nsresult CheckStorageEntry(CacheStorage const* aStorage,
@@ -281,17 +292,16 @@ private:
                               bool aPin,
                               nsICacheEntryDoomCallback* aCallback);
   nsresult AddStorageEntry(nsCSubstring const& aContextKey,
                            nsIURI* aURI,
                            const nsACString & aIdExtension,
                            bool aWriteToDisk,
                            bool aSkipSizeCheck,
                            bool aPin,
-                           bool aCreateIfNotExist,
                            bool aReplace,
                            CacheEntryHandle** aResult);
 
   static CacheStorageService* sSelf;
 
   mozilla::Mutex mLock;
   mozilla::Mutex mForcedValidEntriesLock;
 
--- a/python/mozbuild/mozbuild/controller/building.py
+++ b/python/mozbuild/mozbuild/controller/building.py
@@ -372,17 +372,17 @@ class BuildMonitor(MozbuildObject):
         if excessive is not None and (sin or sout):
             sin /= 1048576
             sout /= 1048576
             self.log(logging.WARNING, 'swap_activity',
                 {'sin': sin, 'sout': sout},
                 'Swap in/out (MB): {sin}/{sout}')
 
         o = dict(
-            version=2,
+            version=3,
             argv=sys.argv,
             start=self.start_time,
             end=self.end_time,
             duration=self.end_time - self.start_time,
             resources=[],
             cpu_percent=cpu_percent,
             cpu_times=cpu_times,
             io=io,
@@ -406,51 +406,33 @@ class BuildMonitor(MozbuildObject):
                 swap=list(usage.swap),
             )
 
             self.tiers.add_resources_to_dict(entry, start=usage.start,
                     end=usage.end)
 
             o['resources'].append(entry)
 
-        # TODO: it would be nice to collect data on the storage device as well
-        o['system'] = dict(
-            architecture=list(platform.architecture()),
-            machine=platform.machine(),
-            python_version=platform.python_version(),
-            release=platform.release(),
-            system=platform.system(),
-            version=platform.version(),
-        )
 
         # If the imports for this file ran before the in-tree virtualenv
         # was bootstrapped (for instance, for a clobber build in automation),
         # psutil might not be available.
         #
         # Treat psutil as optional to avoid an outright failure to log resources
+        # TODO: it would be nice to collect data on the storage device as well
         # in this case.
+        o['system'] = {}
         if psutil:
             o['system'].update(dict(
                 logical_cpu_count=psutil.cpu_count(),
                 physical_cpu_count=psutil.cpu_count(logical=False),
                 swap_total=psutil.swap_memory()[0],
                 vmem_total=psutil.virtual_memory()[0],
             ))
 
-        if platform.system() == 'Linux':
-            dist = list(platform.linux_distribution())
-            o['system']['linux_distribution'] = dist
-        elif platform.system() == 'Windows':
-            win32_ver=list((platform.win32_ver())),
-            o['system']['win32_ver'] = win32_ver
-        elif platform.system() == 'Darwin':
-            # mac version is a special Cupertino snowflake
-            r, v, m = platform.mac_ver()
-            o['system']['mac_ver'] = [r, list(v), m]
-
         return o
 
     def _log_resource_usage(self, prefix, m_type, duration, cpu_percent,
         cpu_times, io, extra_params={}):
 
         params = dict(
             duration=duration,
             cpu_percent=cpu_percent,
--- a/python/mozbuild/mozbuild/resources/html-build-viewer/index.html
+++ b/python/mozbuild/mozbuild/resources/html-build-viewer/index.html
@@ -51,17 +51,17 @@ svg {
   <body>
     <script>
 var currentResources;
 
 /**
  * Interface for a build resources JSON file.
  */
 function BuildResources(data) {
-  if (data.version != 1 && data.version != 2) {
+  if (data.version < 1 || data.version > 3) {
     throw new Error("Unsupported version of the JSON format: " + data.version);
   }
 
   this.resources = [];
 
   var cpu_fields = data.cpu_times_fields;
   var io_fields = data.io_fields;
   var virt_fields = data.virt_fields;
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -77,18 +77,17 @@ IsCertChainRootBuiltInRoot(CERTCertList*
   }
   return Success;
 }
 
 SECStatus
 IsCertBuiltInRoot(CERTCertificate* cert, bool& result)
 {
   result = false;
-  ScopedPK11SlotList slots;
-  slots = PK11_GetAllSlotsForCert(cert, nullptr);
+  UniquePK11SlotList slots(PK11_GetAllSlotsForCert(cert, nullptr));
   if (!slots) {
     if (PORT_GetError() == SEC_ERROR_NO_TOKEN) {
       // no list
       return SECSuccess;
     }
     return SECFailure;
   }
   for (PK11SlotListElement* le = slots->head; le; le = le->next) {
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -1012,17 +1012,17 @@ LoadLoadableRoots(/*optional*/ const cha
   UniquePtr<char, void(&)(char*)>
     pkcs11ModuleSpec(PR_smprintf("name=\"%s\" library=\"%s\"", modNameUTF8,
                                  escaped_fullLibraryPath.get()),
                      PR_smprintf_free);
   if (!pkcs11ModuleSpec) {
     return SECFailure;
   }
 
-  ScopedSECMODModule rootsModule(SECMOD_LoadUserModule(pkcs11ModuleSpec.get(),
+  UniqueSECMODModule rootsModule(SECMOD_LoadUserModule(pkcs11ModuleSpec.get(),
                                                        nullptr, false));
   if (!rootsModule) {
     return SECFailure;
   }
 
   if (!rootsModule->loaded) {
     PR_SetError(PR_INVALID_STATE_ERROR, 0);
     return SECFailure;
@@ -1030,17 +1030,17 @@ LoadLoadableRoots(/*optional*/ const cha
 
   return SECSuccess;
 }
 
 void
 UnloadLoadableRoots(const char* modNameUTF8)
 {
   PR_ASSERT(modNameUTF8);
-  ScopedSECMODModule rootsModule(SECMOD_FindModule(modNameUTF8));
+  UniqueSECMODModule rootsModule(SECMOD_FindModule(modNameUTF8));
 
   if (rootsModule) {
     SECMOD_UnloadUserModule(rootsModule.get());
   }
 }
 
 char*
 DefaultServerNicknameForCert(CERTCertificate* cert)
--- a/security/manager/.eslintrc
+++ b/security/manager/.eslintrc
@@ -1,16 +1,19 @@
 {
   "rules": {
     // Braces only needed for multi-line arrow function blocks
     "arrow-body-style": [2, "as-needed"],
 
     // Require spacing around =>
     "arrow-spacing": 2,
 
+    // No space before always a space after a comma
+    "comma-spacing": [2, {"before": false, "after": true}],
+
     // Commas at the end of the line not the start
     "comma-style": 2,
 
     // Functions must always return something or nothing
     "consistent-return": 2,
 
     // Require braces around blocks that start a new line
     "curly": [2, "multi-line"],
@@ -100,16 +103,19 @@
     "no-unreachable": 2,
 
     // No expressions where a statement is expected
     "no-unused-expressions": 2,
 
     // No using with
     "no-with": 2,
 
+    // Always require semicolon at end of statement
+    "semi": [2, "always"],
+
     // Require space before blocks
     "space-before-blocks": 2,
 
     // Require spaces before finally, catch, etc.
     "space-before-keywords": [2, "always"],
 
     // No space padding in parentheses
     "space-in-parens": [2, "never"],
--- a/security/manager/pki/resources/content/certpicker.js
+++ b/security/manager/pki/resources/content/certpicker.js
@@ -50,19 +50,23 @@ function setDetails()
 
 function onCertSelected()
 {
   setDetails();
 }
 
 function doOK()
 {
-  dialogParams.SetInt(0,1);
-  var index = parseInt(document.getElementById("nicknames").value);
+  // Signal that the user accepted.
+  dialogParams.SetInt(0, 1);
+
+  // Signal the index of the selected cert in the list of cert nicknames
+  // provided.
+  let index = parseInt(document.getElementById("nicknames").value);
   dialogParams.SetInt(1, index);
   return true;
 }
 
 function doCancel()
 {
-  dialogParams.SetInt(0,0);
+  dialogParams.SetInt(0, 0); // Signal that the user cancelled.
   return true;
 }
--- a/security/manager/pki/resources/content/choosetoken.js
+++ b/security/manager/pki/resources/content/choosetoken.js
@@ -23,20 +23,21 @@ function onLoad()
     if (i == 0) {
       selectElement.selectedItem = menuItemNode;
     }
   }
 }
 
 function doOK()
 {
-  var tokenList = document.getElementById("tokens");
-  var token = tokenList.value;
-  dialogParams.SetInt(0,1);
-  dialogParams.SetString(0, token);
+  let tokenList = document.getElementById("tokens");
+  // Signal that the user accepted.
+  dialogParams.SetInt(0, 1);
+  // Signal the name of the token the user chose.
+  dialogParams.SetString(0, tokenList.value);
   return true;
 }
 
 function doCancel()
 {
-  dialogParams.SetInt(0,0);
+  dialogParams.SetInt(0, 0); // Signal that the user cancelled.
   return true;
 }
--- a/security/manager/pki/resources/content/clientauthask.js
+++ b/security/manager/pki/resources/content/clientauthask.js
@@ -75,22 +75,29 @@ function setDetails()
 
 function onCertSelected()
 {
   setDetails();
 }
 
 function doOK()
 {
-  dialogParams.SetInt(0,1);
-  var index = parseInt(document.getElementById("nicknames").value);
+  // Signal that the user accepted.
+  dialogParams.SetInt(0, 1);
+  let index = parseInt(document.getElementById("nicknames").value);
+  // Signal the index of the selected cert in the list of cert nicknames
+  // provided.
   dialogParams.SetInt(1, index);
+  // Signal whether the user wanted to remember the selection.
   dialogParams.SetInt(2, rememberBox.checked);
   return true;
 }
 
 function doCancel()
 {
-  dialogParams.SetInt(0,0);
+  // Signal that the user cancelled.
+  dialogParams.SetInt(0, 0);
+  // Signal some invalid index value since a cert hasn't actually been chosen.
   dialogParams.SetInt(1, -1); // invalid value
+  // Signal whether the user wanted to remember the "selection".
   dialogParams.SetInt(2, rememberBox.checked);
   return true;
 }
--- a/security/manager/pki/resources/content/deletecert.js
+++ b/security/manager/pki/resources/content/deletecert.js
@@ -55,28 +55,28 @@ function setWindowName()
   else
   {
      return;
   }
   var confirReference = document.getElementById('confirm');
   var impactReference = document.getElementById('impact');
   document.title = title;
 
-  setText("confirm",confirm);
+  setText("confirm", confirm);
 
   let box = document.getElementById("certlist");
   for (let x = 0; x < numberOfCerts; x++) {
     var listItem = document.createElement("richlistitem");
     var label = document.createElement("label");
     label.setAttribute("value", gParams.GetString(x + 1));
     listItem.appendChild(label);
     box.appendChild(listItem);
   }
 
-  setText("impact",impact);
+  setText("impact", impact);
 }
 
 function doOK()
 {
   gParams.SetInt(1, 1); // means OK
   return true;
 }
 
--- a/security/manager/pki/resources/content/device_manager.js
+++ b/security/manager/pki/resources/content/device_manager.js
@@ -463,17 +463,17 @@ function doBrowseFiles()
   }
 }
 
 function doLoadDevice()
 {
   var name_box = document.getElementById("device_name");
   var path_box = document.getElementById("device_path");
   try {
-    getPKCS11().addModule(name_box.value, path_box.value, 0,0);
+    getPKCS11().addModule(name_box.value, path_box.value, 0, 0);
   } catch (e) {
     if (e.result == Components.results.NS_ERROR_ILLEGAL_VALUE) {
       doPrompt(getNSSString("AddModuleDup"));
     } else {
       doPrompt(getNSSString("AddModuleFailure"));
     }
 
     return false;
--- a/security/manager/pki/resources/content/downloadcert.js
+++ b/security/manager/pki/resources/content/downloadcert.js
@@ -43,11 +43,11 @@ function doOK()
 
   // Signal that the user accepted.
   params.SetInt(1, 1);
   return true;
 }
 
 function doCancel()
 {
-  params.SetInt(1,0);
+  params.SetInt(1, 0); // Signal that the user cancelled.
   return true;
 }
--- a/security/manager/pki/resources/content/exceptionDialog.js
+++ b/security/manager/pki/resources/content/exceptionDialog.js
@@ -36,17 +36,17 @@ badCertListener.prototype = {
     }
   },
   notifyCertProblem: function MSR_notifyCertProblem(socketInfo, sslStatus, targetHost) {
     gBroken = true;
     gSSLStatus = sslStatus;
     this.handle_test_result();
     return true; // suppress error UI
   }
-}
+};
 
 function initExceptionDialog() {
   gNeedReset = false;
   gDialog = document.documentElement;
   gBundleBrand = document.getElementById("brand_bundle");
   gPKIBundle = document.getElementById("pippki_bundle");
   gSecHistogram = Components.classes["@mozilla.org/base/telemetry;1"].
                     getService(Components.interfaces.nsITelemetry).
--- a/security/manager/pki/resources/content/password.js
+++ b/security/manager/pki/resources/content/password.js
@@ -60,17 +60,17 @@ function onLoad()
               i++;
            }
         }
      } catch (exception) {}
   } else {
     var sel = document.getElementById("tokenMenu");
     sel.setAttribute("hidden", "true");
     var tag = document.getElementById("tokenName");
-    tag.setAttribute("value",tokenName);
+    tag.setAttribute("value", tokenName);
   }
 
   process();
 }
 
 function onMenuChange()
 {
    //get the selected token
--- a/security/manager/pki/resources/content/pippki.js
+++ b/security/manager/pki/resources/content/pippki.js
@@ -16,17 +16,17 @@ function setText(id, value) {
   }
   if (element.hasChildNodes()) {
     element.removeChild(element.firstChild);
   }
   element.appendChild(document.createTextNode(value));
 }
 
 const nsICertificateDialogs = Components.interfaces.nsICertificateDialogs;
-const nsCertificateDialogs = "@mozilla.org/nsCertificateDialogs;1"
+const nsCertificateDialogs = "@mozilla.org/nsCertificateDialogs;1";
 
 function viewCertHelper(parent, cert) {
   if (!cert) {
     return;
   }
 
   var cd = Components.classes[nsCertificateDialogs].getService(nsICertificateDialogs);
   cd.viewCert(parent, cert);
--- a/security/manager/pki/resources/content/viewCertDetails.js
+++ b/security/manager/pki/resources/content/viewCertDetails.js
@@ -6,53 +6,64 @@ const nsIX509Cert = Components.interface
 const nsX509CertDB = "@mozilla.org/security/x509certdb;1";
 const nsIX509CertDB = Components.interfaces.nsIX509CertDB;
 const nsPK11TokenDB = "@mozilla.org/security/pk11tokendb;1";
 const nsIPK11TokenDB = Components.interfaces.nsIPK11TokenDB;
 const nsIASN1Object = Components.interfaces.nsIASN1Object;
 const nsIASN1Sequence = Components.interfaces.nsIASN1Sequence;
 const nsIASN1PrintableItem = Components.interfaces.nsIASN1PrintableItem;
 const nsIASN1Tree = Components.interfaces.nsIASN1Tree;
-const nsASN1Tree = "@mozilla.org/security/nsASN1Tree;1"
+const nsASN1Tree = "@mozilla.org/security/nsASN1Tree;1";
 const nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
 
 var bundle;
 
 function doPrompt(msg)
 {
   let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
     getService(Components.interfaces.nsIPromptService);
   prompts.alert(window, null, msg);
 }
 
-function AddCertChain(node, chain, idPrefix)
+/**
+ * Fills out the "Certificate Hierarchy" tree of the cert viewer "Details" tab.
+ *
+ * @param {tree} node
+ *        Parent tree node to append to.
+ * @param {nsIArray<nsIX509Cert>} chain
+ *        Chain where cert element n is issued by cert element n + 1.
+ */
+function AddCertChain(node, chain)
 {
   var child = document.getElementById(node);
   var currCert;
   var displayVal;
-  var addTwistie;
   for (let i = chain.length - 1; i >= 0; i--) {
     currCert = chain.queryElementAt(i, nsIX509Cert);
     if (currCert.commonName) {
       displayVal = currCert.commonName;
     } else {
       displayVal = currCert.windowTitle;
     }
-    if (0 == i) {
-      addTwistie = false;
-    } else {
-      addTwistie = true;
-    }
-    child = addChildrenToTree(child, displayVal, currCert.dbKey,addTwistie);
+    let addTwistie = i != 0;
+    child = addChildrenToTree(child, displayVal, currCert.dbKey, addTwistie);
   }
 }
 
-function AddUsage(usage,verifyInfoBox)
+/**
+ * Adds a "verified usage" of a cert to the "General" tab of the cert viewer.
+ *
+ * @param {String} usage
+ *        Verified usage to add.
+ * @param {Node} verifyInfoBox
+ *        Parent node to append to.
+ */
+function AddUsage(usage, verifyInfoBox)
 {
-  var text  = document.createElement("textbox");
+  let text = document.createElement("textbox");
   text.setAttribute("value", usage);
   text.setAttribute("style", "margin: 2px 5px");
   text.setAttribute("readonly", "true");
   text.setAttribute("class", "scrollfield");
   verifyInfoBox.appendChild(text);
 }
 
 function setWindowName()
@@ -81,43 +92,43 @@ function setWindowName()
     document.title = certDetails + '"' + cert.windowTitle + '"'; // XXX l10n?
   }
 
   //
   //  Set the cert attributes for viewing
   //
 
   //  The chain of trust
-  var chain = cert.getChain();
-  AddCertChain("treesetDump", chain, "dump_");
+  AddCertChain("treesetDump", cert.getChain());
   DisplayGeneralDataFromCert(cert);
   BuildPrettyPrint(cert);
   cert.requestUsagesArrayAsync(new listener());
 }
 
-function addChildrenToTree(parentTree,label,value,addTwistie)
+function addChildrenToTree(parentTree, label, value, addTwistie)
 {
-  var treeChild1 = document.createElement("treechildren");
-  var treeElement = addTreeItemToTreeChild(treeChild1,label,value,addTwistie);
+  let treeChild1 = document.createElement("treechildren");
+  let treeElement = addTreeItemToTreeChild(treeChild1, label, value,
+                                           addTwistie);
   parentTree.appendChild(treeChild1);
   return treeElement;
 }
 
-function addTreeItemToTreeChild(treeChild,label,value,addTwistie)
+function addTreeItemToTreeChild(treeChild, label, value, addTwistie)
 {
-  var treeElem1 = document.createElement("treeitem");
+  let treeElem1 = document.createElement("treeitem");
   if (addTwistie) {
-    treeElem1.setAttribute("container","true");
-    treeElem1.setAttribute("open","true");
+    treeElem1.setAttribute("container", "true");
+    treeElem1.setAttribute("open", "true");
   }
-  var treeRow = document.createElement("treerow");
-  var treeCell = document.createElement("treecell");
-  treeCell.setAttribute("label",label);
+  let treeRow = document.createElement("treerow");
+  let treeCell = document.createElement("treecell");
+  treeCell.setAttribute("label", label);
   if (value) {
-    treeCell.setAttribute("display",value);
+    treeCell.setAttribute("display", value);
   }
   treeRow.appendChild(treeCell);
   treeElem1.appendChild(treeRow);
   treeChild.appendChild(treeElem1);
   return treeElem1;
 }
 
 function displaySelected() {
@@ -158,22 +169,22 @@ function listener() {
 listener.prototype.QueryInterface =
   function(iid) {
     if (iid.equals(Components.interfaces.nsISupports) ||
         iid.equals(Components.interfaces.nsICertVerificationListener)) {
       return this;
     }
 
     throw Components.results.NS_ERROR_NO_INTERFACE;
-  }
+  };
 
 listener.prototype.notify =
   function(cert, result) {
     DisplayVerificationData(cert, result);
-  }
+  };
 
 function DisplayVerificationData(cert, result)
 {
   document.getElementById("verify_pending").setAttribute("hidden", "true");
 
   if (!result || !cert) {
     return; // no results could be produced
   }
@@ -216,48 +227,41 @@ function DisplayVerificationData(cert, r
   } else { /* if (verifystate == cert.NOT_VERIFIED_UNKNOWN || == USAGE_NOT_ALLOWED) */
     verifystr = bundle.getString('certNotVerified_Unknown');
   }
   let verified = document.getElementById("verified");
   verified.textContent = verifystr;
   if (count > 0) {
     var verifyInfoBox = document.getElementById('verify_info_box');
     for (let i = 0; i < count; i++) {
-      AddUsage(usageList[i],verifyInfoBox);
+      AddUsage(usageList[i], verifyInfoBox);
     }
   }
 }
 
+/**
+ * Displays information about a cert in the "General" tab of the cert viewer.
+ *
+ * @param {nsIX509Cert} cert
+ *        Cert to display information about.
+ */
 function DisplayGeneralDataFromCert(cert)
 {
-  //  Common Name
-  addAttributeFromCert('commonname', cert.commonName);
-  //  Organization
-  addAttributeFromCert('organization', cert.organization);
-  //  Organizational Unit
-  addAttributeFromCert('orgunit', cert.organizationalUnit);
-  //  Serial Number
-  addAttributeFromCert('serialnumber',cert.serialNumber);
-  // SHA-256 Fingerprint
-  addAttributeFromCert('sha256fingerprint', cert.sha256Fingerprint);
-  //  SHA1 Fingerprint
-  addAttributeFromCert('sha1fingerprint',cert.sha1Fingerprint);
-  // Validity start
-  addAttributeFromCert('validitystart', cert.validity.notBeforeLocalDay);
-  // Validity end
-  addAttributeFromCert('validityend', cert.validity.notAfterLocalDay);
+  addAttributeFromCert("commonname", cert.commonName);
+  addAttributeFromCert("organization", cert.organization);
+  addAttributeFromCert("orgunit", cert.organizationalUnit);
+  addAttributeFromCert("serialnumber", cert.serialNumber);
+  addAttributeFromCert("sha256fingerprint", cert.sha256Fingerprint);
+  addAttributeFromCert("sha1fingerprint", cert.sha1Fingerprint);
+  addAttributeFromCert("validitystart", cert.validity.notBeforeLocalDay);
+  addAttributeFromCert("validityend", cert.validity.notAfterLocalDay);
 
-  //Now to populate the fields that correspond to the issuer.
-  var issuerCommonname, issuerOrg, issuerOrgUnit;
-  issuerCommonname = cert.issuerCommonName;
-  issuerOrg = cert.issuerOrganization;
-  issuerOrgUnit = cert.issuerOrganizationUnit;
-  addAttributeFromCert('issuercommonname', issuerCommonname);
-  addAttributeFromCert('issuerorganization', issuerOrg);
-  addAttributeFromCert('issuerorgunit', issuerOrgUnit);
+  addAttributeFromCert("issuercommonname", cert.issuerCommonName);
+  addAttributeFromCert("issuerorganization", cert.issuerOrganization);
+  addAttributeFromCert("issuerorgunit", cert.issuerOrganizationUnit);
 }
 
 function updateCertDump()
 {
   var asn1Tree = document.getElementById('prettyDumpTree')
           .view.QueryInterface(nsIASN1Tree);
 
   var tree = document.getElementById('treesetDump');
--- a/security/manager/ssl/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/SSLServerCertVerification.cpp
@@ -1083,17 +1083,17 @@ GatherEKUTelemetry(const ScopedCERTCertL
   }
 
   if (!foundEKU) {
     Telemetry::Accumulate(Telemetry::SSL_SERVER_AUTH_EKU, 0);
     return;
   }
 
   // Parse the EKU extension
-  ScopedCERTOidSequence ekuSequence(
+  UniqueCERTOidSequence ekuSequence(
     CERT_DecodeOidSequence(&ekuExtension->value));
   if (!ekuSequence) {
     return;
   }
 
   // Search through the available EKUs
   bool foundServerAuth = false;
   bool foundOther = false;
--- a/security/manager/ssl/ScopedNSSTypes.h
+++ b/security/manager/ssl/ScopedNSSTypes.h
@@ -1,14 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=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/. */
 
+// This header provides smart pointers and various helpers for code that needs
+// to interact with NSS.
+
 #ifndef mozilla_ScopedNSSTypes_h
 #define mozilla_ScopedNSSTypes_h
 
 #include <limits>
 
 #include "cert.h"
 #include "cms.h"
 #include "cryptohi.h"
@@ -65,16 +68,17 @@ MapSECStatus(SECStatus rv)
   if (rv == SECSuccess) {
     return NS_OK;
   }
 
   return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
 }
 
 // Alphabetical order by NSS type
+// Deprecated: use the equivalent UniquePtr templates instead.
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc,
                                           PRFileDesc,
                                           PR_Close)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTCertificate,
                                           CERTCertificate,
                                           CERT_DestroyCertificate)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTCertificateList,
                                           CERTCertificateList,
@@ -83,67 +87,40 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLAT
                                           CERTCertificateRequest,
                                           CERT_DestroyCertificateRequest)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTCertList,
                                           CERTCertList,
                                           CERT_DestroyCertList)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTName,
                                           CERTName,
                                           CERT_DestroyName)
-MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTOidSequence,
-                                          CERTOidSequence,
-                                          CERT_DestroyOidSequence)
-MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTCertNicknames,
-                                          CERTCertNicknames,
-                                          CERT_FreeNicknames)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTSubjectPublicKeyInfo,
                                           CERTSubjectPublicKeyInfo,
                                           SECKEY_DestroySubjectPublicKeyInfo)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTValidity,
                                           CERTValidity,
                                           CERT_DestroyValidity)
-
-MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedNSSCMSMessage,
-                                          NSSCMSMessage,
-                                          NSS_CMSMessage_Destroy)
-MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedNSSCMSSignedData,
-                                          NSSCMSSignedData,
-                                          NSS_CMSSignedData_Destroy)
+// Deprecated: use the equivalent UniquePtr templates instead.
 
 namespace psm {
 
 inline void
 PK11_DestroyContext_true(PK11Context * ctx) {
   PK11_DestroyContext(ctx, true);
 }
 
-inline void
-SGN_DestroyContext_true(SGNContext* ctx) {
-  SGN_DestroyContext(ctx, true);
-}
-
-inline void
-VFY_DestroyContext_true(VFYContext * ctx) {
-  VFY_DestroyContext(ctx, true);
-}
-
 } // namespace mozilla::psm
 
+// Deprecated: use the equivalent UniquePtr templates instead.
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11Context,
                                           PK11Context,
                                           mozilla::psm::PK11_DestroyContext_true)
-MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSGNContext,
-                                          SGNContext,
-                                          mozilla::psm::SGN_DestroyContext_true)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSGNDigestInfo,
                                           SGNDigestInfo,
                                           SGN_DestroyDigestInfo)
-MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedVFYContext,
-                                          VFYContext,
-                                          mozilla::psm::VFY_DestroyContext_true)
 
 /** A more convenient way of dealing with digests calculated into
  *  stack-allocated buffers. NSS must be initialized on the main thread before
  *  use, and the caller must ensure NSS isn't shut down, typically by
  *  subclassing nsNSSShutDownObject, while Digest is in use.
  *
  * Typical usage, for digesting a buffer in memory:
  *
@@ -226,48 +203,42 @@ private:
 
     return NS_OK;
   }
 
   uint8_t buf[HASH_LENGTH_MAX];
   SECItem item;
 };
 
+// Deprecated: use the equivalent UniquePtr templates instead.
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11SlotInfo,
                                           PK11SlotInfo,
                                           PK11_FreeSlot)
-MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11SlotList,
-                                          PK11SlotList,
-                                          PK11_FreeSlotList)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11SymKey,
                                           PK11SymKey,
                                           PK11_FreeSymKey)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11GenericObject,
                                           PK11GenericObject,
                                           PK11_DestroyGenericObject)
-
-MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSEC_PKCS7ContentInfo,
-                                          SEC_PKCS7ContentInfo,
-                                          SEC_PKCS7DestroyContentInfo)
-
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSEC_PKCS12DecoderContext,
                                           SEC_PKCS12DecoderContext,
                                           SEC_PKCS12DecoderFinish)
 namespace internal {
 
 inline void
 PORT_FreeArena_false(PLArenaPool* arena)
 {
   // PL_FreeArenaPool can't be used because it doesn't actually free the
   // memory, which doesn't work well with memory analysis tools.
   return PORT_FreeArena(arena, false);
 }
 
 } // namespace internal
 
+// Deprecated: use the equivalent UniquePtr templates instead.
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPLArenaPool,
                                           PLArenaPool,
                                           internal::PORT_FreeArena_false)
 
 // Wrapper around NSS's SECItem_AllocItem that handles OOM the same way as
 // other allocators.
 inline void
 SECITEM_AllocItem(SECItem & item, uint32_t len)
@@ -320,56 +291,73 @@ inline void SECOID_DestroyAlgorithmID_tr
 
 inline void SECKEYEncryptedPrivateKeyInfo_true(SECKEYEncryptedPrivateKeyInfo * epki)
 {
   return SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
 }
 
 } // namespace internal
 
+// Deprecated: use the equivalent UniquePtr templates instead.
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECItem,
                                           SECItem,
                                           internal::SECITEM_FreeItem_true)
-
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECKEYPrivateKey,
                                           SECKEYPrivateKey,
                                           SECKEY_DestroyPrivateKey)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECKEYEncryptedPrivateKeyInfo,
                                           SECKEYEncryptedPrivateKeyInfo,
                                           internal::SECKEYEncryptedPrivateKeyInfo_true)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECKEYPublicKey,
                                           SECKEYPublicKey,
                                           SECKEY_DestroyPublicKey)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECAlgorithmID,
                                           SECAlgorithmID,
                                           internal::SECOID_DestroyAlgorithmID_true)
-MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECMODModule, SECMODModule,
-                                          SECMOD_DestroyModule)
 
 // Emulates MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE, but for UniquePtrs.
 #define MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(name, Type, Deleter) \
 struct name##DeletePolicy \
 { \
   void operator()(Type* aValue) { Deleter(aValue); } \
 }; \
 typedef UniquePtr<Type, name##DeletePolicy> name;
 
 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificatePolicies,
                                       CERTCertificatePolicies,
                                       CERT_DestroyCertificatePoliciesExtension)
+MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertNicknames,
+                                      CERTCertNicknames,
+                                      CERT_FreeNicknames)
 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTOidSequence,
                                       CERTOidSequence,
                                       CERT_DestroyOidSequence)
 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTUserNotice,
                                       CERTUserNotice,
                                       CERT_DestroyUserNotice)
+
+MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueNSSCMSMessage,
+                                      NSSCMSMessage,
+                                      NSS_CMSMessage_Destroy)
+MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueNSSCMSSignedData,
+                                      NSSCMSSignedData,
+                                      NSS_CMSSignedData_Destroy)
+
+MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11SlotList,
+                                      PK11SlotList,
+                                      PK11_FreeSlotList)
+
 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePLArenaPool,
                                       PLArenaPool,
                                       internal::PORT_FreeArena_false)
+
 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECItem,
                                       SECItem,
                                       internal::SECITEM_FreeItem_true)
 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPublicKey,
                                       SECKEYPublicKey,
                                       SECKEY_DestroyPublicKey)
+MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECMODModule,
+                                      SECMODModule,
+                                      SECMOD_DestroyModule)
 } // namespace mozilla
 
 #endif // mozilla_ScopedNSSTypes_h
--- a/security/manager/ssl/nsCertPicker.cpp
+++ b/security/manager/ssl/nsCertPicker.cpp
@@ -98,17 +98,18 @@ NS_IMETHODIMP nsCertPicker::PickByUsage(
           CERT_RemoveCertListNode(freenode);
           continue;
         }
       }
       node = CERT_LIST_NEXT(node);
     }
   }
 
-  ScopedCERTCertNicknames nicknames(getNSSCertNicknamesFromCertList(certList.get()));
+  UniqueCERTCertNicknames nicknames(
+    getNSSCertNicknamesFromCertList(certList.get()));
   if (!nicknames) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   certNicknameList = (char16_t **)moz_xmalloc(sizeof(char16_t *) * nicknames->numnicknames);
   certDetailsList = (char16_t **)moz_xmalloc(sizeof(char16_t *) * nicknames->numnicknames);
 
   if (!certNicknameList || !certDetailsList) {
--- a/security/manager/ssl/nsCrypto.cpp
+++ b/security/manager/ssl/nsCrypto.cpp
@@ -46,17 +46,17 @@ nsPkcs11::DeleteModule(const nsAString& 
     return NS_ERROR_INVALID_ARG;
   }
 
   NS_ConvertUTF16toUTF8 moduleName(aModuleName);
   // Introduce additional scope for module so all references to it are released
   // before we call SECMOD_DeleteModule, below.
 #ifndef MOZ_NO_SMART_CARDS
   {
-    mozilla::ScopedSECMODModule module(SECMOD_FindModule(moduleName.get()));
+    mozilla::UniqueSECMODModule module(SECMOD_FindModule(moduleName.get()));
     if (!module) {
       return NS_ERROR_FAILURE;
     }
     nsCOMPtr<nsINSSComponent> nssComponent(
       do_GetService(PSM_COMPONENT_CONTRACTID));
     nssComponent->ShutdownSmartCardThread(module.get());
   }
 #endif
@@ -95,17 +95,17 @@ nsPkcs11::AddModule(const nsAString& aMo
   uint32_t cipherFlags = SECMOD_PubCipherFlagstoInternal(aCipherFlags);
   SECStatus srv = SECMOD_AddNewModule(moduleName.get(), fullPath.get(),
                                       mechFlags, cipherFlags);
   if (srv != SECSuccess) {
     return NS_ERROR_FAILURE;
   }
 
 #ifndef MOZ_NO_SMART_CARDS
-  mozilla::ScopedSECMODModule module(SECMOD_FindModule(moduleName.get()));
+  mozilla::UniqueSECMODModule module(SECMOD_FindModule(moduleName.get()));
   if (!module) {
     return NS_ERROR_FAILURE;
   }
   nsCOMPtr<nsINSSComponent> nssComponent(
     do_GetService(PSM_COMPONENT_CONTRACTID));
   nssComponent->LaunchSmartCardThread(module.get());
 #endif
 
--- a/security/manager/ssl/nsDataSignatureVerifier.cpp
+++ b/security/manager/ssl/nsDataSignatureVerifier.cpp
@@ -124,17 +124,17 @@ VerifyCMSDetachedSignatureIncludingCerti
   // XXX: missing pinArg is tolerated.
   if (NS_WARN_IF(!buffer.data && buffer.len > 0) ||
       NS_WARN_IF(!detachedDigest.data && detachedDigest.len > 0) ||
       (!verifyCertificate) ||
       NS_WARN_IF(!verifyCertificateContext)) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  ScopedNSSCMSMessage
+  UniqueNSSCMSMessage
     cmsMsg(NSS_CMSMessage_CreateFromDER(const_cast<SECItem*>(&buffer), nullptr,
                                         nullptr, nullptr, nullptr, nullptr,
                                         nullptr));
   if (!cmsMsg) {
     return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
   }
 
   if (!NSS_CMSMessage_IsSigned(cmsMsg.get())) {
--- a/security/manager/ssl/nsNSSCertificate.cpp
+++ b/security/manager/ssl/nsNSSCertificate.cpp
@@ -6,16 +6,17 @@
 #include "nsNSSCertificate.h"
 
 #include "prmem.h"
 #include "prerror.h"
 #include "prprf.h"
 #include "CertVerifier.h"
 #include "ExtendedValidation.h"
 #include "mozilla/UniquePtr.h"
+#include "mozilla/unused.h"
 #include "pkix/pkixnss.h"
 #include "pkix/pkixtypes.h"
 #include "nsNSSComponent.h" // for PIPNSS string bundle calls.
 #include "nsCOMPtr.h"
 #include "nsIMutableArray.h"
 #include "nsNSSCertValidity.h"
 #include "nsPKCS12Blob.h"
 #include "nsPK11TokenDB.h"
@@ -918,24 +919,23 @@ nsNSSCertificate::GetAllTokenNames(uint3
     return NS_ERROR_NOT_AVAILABLE;
 
   NS_ENSURE_ARG(aLength);
   NS_ENSURE_ARG(aTokenNames);
   *aLength = 0;
   *aTokenNames = nullptr;
 
   // Get the slots from NSS
-  ScopedPK11SlotList slots;
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Getting slots for \"%s\"\n", mCert->nickname));
-  slots = PK11_GetAllSlotsForCert(mCert.get(), nullptr);
+  UniquePK11SlotList slots(PK11_GetAllSlotsForCert(mCert.get(), nullptr));
   if (!slots) {
-    if (PORT_GetError() == SEC_ERROR_NO_TOKEN)
+    if (PORT_GetError() == SEC_ERROR_NO_TOKEN) {
       return NS_OK; // List of slots is empty, return empty array
-    else
-      return NS_ERROR_FAILURE;
+    }
+    return NS_ERROR_FAILURE;
   }
 
   // read the token names from slots
   PK11SlotListElement* le;
 
   for (le = slots->head; le; le = le->next) {
     ++(*aLength);
   }
@@ -1144,26 +1144,26 @@ nsNSSCertificate::ExportAsCMS(uint32_t c
     case nsIX509Cert::CMS_CHAIN_MODE_CertOnly:
     case nsIX509Cert::CMS_CHAIN_MODE_CertChain:
     case nsIX509Cert::CMS_CHAIN_MODE_CertChainWithRoot:
       break;
     default:
       return NS_ERROR_INVALID_ARG;
   }
 
-  ScopedNSSCMSMessage cmsg(NSS_CMSMessage_Create(nullptr));
+  UniqueNSSCMSMessage cmsg(NSS_CMSMessage_Create(nullptr));
   if (!cmsg) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("nsNSSCertificate::ExportAsCMS - can't create CMS message\n"));
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   // first, create SignedData with the certificate only (no chain)
-  ScopedNSSCMSSignedData sigd(
-    NSS_CMSSignedData_CreateCertsOnly(cmsg, mCert.get(), false));
+  UniqueNSSCMSSignedData sigd(
+    NSS_CMSSignedData_CreateCertsOnly(cmsg.get(), mCert.get(), false));
   if (!sigd) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("nsNSSCertificate::ExportAsCMS - can't create SignedData\n"));
     return NS_ERROR_FAILURE;
   }
 
   // Calling NSS_CMSSignedData_CreateCertsOnly() will not allow us
   // to specify the inclusion of the root, but CERT_CertChainFromCert() does.
@@ -1177,60 +1177,60 @@ nsNSSCertificate::ExportAsCMS(uint32_t c
     // the issuerCert of a self signed root is the cert itself,
     // so make sure we're not adding duplicates, again
     if (issuerCert && issuerCert != mCert.get()) {
       bool includeRoot =
         (chainMode == nsIX509Cert::CMS_CHAIN_MODE_CertChainWithRoot);
       ScopedCERTCertificateList certChain(
           CERT_CertChainFromCert(issuerCert, certUsageAnyCA, includeRoot));
       if (certChain) {
-        if (NSS_CMSSignedData_AddCertList(sigd, certChain) == SECSuccess) {
+        if (NSS_CMSSignedData_AddCertList(sigd.get(), certChain) == SECSuccess) {
           certChain.forget();
         }
         else {
           MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
                  ("nsNSSCertificate::ExportAsCMS - can't add chain\n"));
           return NS_ERROR_FAILURE;
         }
       }
       else {
         // try to add the issuerCert, at least
-        if (NSS_CMSSignedData_AddCertificate(sigd, issuerCert)
+        if (NSS_CMSSignedData_AddCertificate(sigd.get(), issuerCert)
             == SECSuccess) {
           issuerCert.forget();
         }
         else {
           MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
                  ("nsNSSCertificate::ExportAsCMS - can't add issuer cert\n"));
           return NS_ERROR_FAILURE;
         }
       }
     }
   }
 
-  NSSCMSContentInfo* cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
-  if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd)
+  NSSCMSContentInfo* cinfo = NSS_CMSMessage_GetContentInfo(cmsg.get());
+  if (NSS_CMSContentInfo_SetContent_SignedData(cmsg.get(), cinfo, sigd.get())
        == SECSuccess) {
-    sigd.forget();
+    Unused << sigd.release();
   }
   else {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("nsNSSCertificate::ExportAsCMS - can't attach SignedData\n"));
     return NS_ERROR_FAILURE;
   }
 
   ScopedPLArenaPool arena(PORT_NewArena(1024));
   if (!arena) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("nsNSSCertificate::ExportAsCMS - out of memory\n"));
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   SECItem certP7 = { siBuffer, nullptr, 0 };
-  NSSCMSEncoderContext* ecx = NSS_CMSEncoder_Start(cmsg, nullptr, nullptr,
+  NSSCMSEncoderContext* ecx = NSS_CMSEncoder_Start(cmsg.get(), nullptr, nullptr,
                                                    &certP7, arena, nullptr,
                                                    nullptr, nullptr, nullptr,
                                                    nullptr, nullptr);
   if (!ecx) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("nsNSSCertificate::ExportAsCMS - can't create encoder context\n"));
     return NS_ERROR_FAILURE;
   }
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -2087,17 +2087,17 @@ void
 ClientAuthDataRunnable::RunOnTargetThread()
 {
   PLArenaPool* arena = nullptr;
   char** caNameStrings;
   ScopedCERTCertificate cert;
   ScopedSECKEYPrivateKey privKey;
   ScopedCERTCertList certList;
   CERTCertListNode* node;
-  ScopedCERTCertNicknames nicknames;
+  UniqueCERTCertNicknames nicknames;
   int keyError = 0; // used for private key retrieval error
   SSM_UserCertChoice certChoice;
   int32_t NumberOfCerts = 0;
   void* wincx = mSocketInfo;
   nsresult rv;
 
   nsCOMPtr<nsIX509Cert> socketClientCert;
   mSocketInfo->GetClientCert(getter_AddRefs(socketClientCert));
@@ -2291,17 +2291,17 @@ ClientAuthDataRunnable::RunOnTargetThrea
       while (!CERT_LIST_END(node, certList.get())) {
         ++NumberOfCerts;
         node = CERT_LIST_NEXT(node);
       }
       if (CERT_LIST_END(CERT_LIST_HEAD(certList.get()), certList.get())) {
         goto noCert;
       }
 
-      nicknames = getNSSCertNicknamesFromCertList(certList.get());
+      nicknames.reset(getNSSCertNicknamesFromCertList(certList.get()));
 
       if (!nicknames) {
         goto loser;
       }
 
       NS_ASSERTION(nicknames->numnicknames == NumberOfCerts, "nicknames->numnicknames != NumberOfCerts");
 
       // Get CN and O of the subject and O of the issuer
--- a/security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js
+++ b/security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js
@@ -18,17 +18,17 @@ FakeSSLStatus.prototype = {
   },
   QueryInterface: function(aIID) {
     if (aIID.equals(Ci.nsISSLStatus) ||
         aIID.equals(Ci.nsISupports)) {
       return this;
     }
     throw Components.results.NS_ERROR_NO_INTERFACE;
   },
-}
+};
 
 // This is a template to help porting global private browsing tests
 // to per-window private browsing tests
 function test() {
   // initialization
   waitForExplicitFinish();
   let windowsToClose = [];
   let testURI = "about:blank";
--- a/security/manager/ssl/tests/mochitest/mixedcontent/backward.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/backward.html
@@ -6,13 +6,13 @@
   window.onload = function()
   {
     window.setTimeout(function()
     {
       SpecialPowers.wrap(window).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
         .getInterface(SpecialPowers.Ci.nsIWebNavigation)
         .goBack();
     }, 100);
-  }
-  
+  };
+
   </script>
 </head>
 </html>
--- a/security/manager/ssl/tests/mochitest/mixedcontent/bug383369step2.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/bug383369step2.html
@@ -8,17 +8,17 @@
 
   <script class="testbody" type="text/javascript">
 
   window.onload = function runTest() {
     window.setTimeout(function () {
       window.location =
         "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/bug383369step3.html?runtest";
     }, 0);
-  }
+  };
 
   function afterNavigationTest()
   {
   }
 
   </script>
 </head>
 
--- a/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js
@@ -42,17 +42,17 @@ window.onload = function onLoad() {
       ok(false, "Exception thrown during test: " + ex);
       finish();
     }
   } else {
     window.addEventListener("message", onMessageReceived, false);
 
     let secureTestLocation = loadAsInsecure ? "http://example.com"
                                             : "https://example.com";
-    secureTestLocation += location.pathname
+    secureTestLocation += location.pathname;
     if (testPage != "") {
       array = secureTestLocation.split("/");
       array.pop();
       array.push(testPage);
       secureTestLocation = array.join("/");
     }
     secureTestLocation += "?runtest";
 
@@ -65,17 +65,17 @@ window.onload = function onLoad() {
       _windowCount = 2;
       window.open(secureTestLocation, "_new1", "");
       window.open(secureTestLocation, "_new2", "");
     } else {
       _windowCount = 1;
       window.open(secureTestLocation);
     }
   }
-}
+};
 
 function onMessageReceived(event)
 {
   switch (event.data) {
     // Indication of all test parts finish (from any of the frames)
     case "done":
       if (--_windowCount == 0) {
         if (testCleanUp) {
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug383369.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug383369.html
@@ -80,16 +80,16 @@
 
     var prefKeys = ["dir", "useDownloadDir", "folderList",
                     "manager.closeWhenDone", "manager.showWhenStarting"];
     for (let prefKey of prefKeys) {
       if (prefs.prefHasUserValue(prefKey)) {
         prefs.clearUserPref(prefKey);
       }
     }
-  }
+  };
 
   </script>
 </head>
 
 <body>
 </body>
 </html>
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug472986.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug472986.html
@@ -17,29 +17,29 @@
   // Clear the default onload assigned to test start because we must
   // wait for replaced image to load and only after that test the security state
   var onLoadFunction = window.onload;
   window.onload = function()
   {
     var img1 = document.getElementById("img1");
     img1.addEventListener("load", onLoadFunction, false);
     img1.src = "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg";
-  }
+  };
 
   function runTest()
   {
     isSecurityState("secure", "secure");
     finish();
   }
 
   function afterNavigationTest()
   {
     isSecurityState("secure", "secure after navigation");
     finish();
   }
-  
+
   </script>
 </head>
 
 <body>
   <img id="img1" src="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/hugebmp.sjs" />
 </body>
 </html>
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html
@@ -17,17 +17,17 @@
   function runTest()
   {
     isSecurityState("secure");
     var self = window;
     var iframe = document.getElementById("iframe1");
     iframe.onload = function() {
       self.isSecurityState("broken", "src='redirect to unsecure' changed to broken");
       self.finish();
-    }
+    };
 
     iframe.src =
       "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs";
   }
 
   function afterNavigationTest()
   {
     isSecurityState("broken", "security still broken after navigation");
--- a/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_sts_privatebrowsing_perwindowpb.html
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_sts_privatebrowsing_perwindowpb.html
@@ -190,26 +190,26 @@
       dump_STSState(false);
       startRound(win, false, 'nosts');
     });
   }
 
   function clean_up_sts_state(isPrivate) {
     // erase all signs that this test ran.
     SimpleTest.info("Cleaning up STS data");
-    var flags = isPrivate ? Ci.nsISocketProvider.NO_PERMANENT_STORAGE : 0
+    let flags = isPrivate ? Ci.nsISocketProvider.NO_PERMANENT_STORAGE : 0;
     SpecialPowers.cleanUpSTSData("http://example.com", flags);
     dump_STSState(isPrivate);
   }
 
   function dump_STSState(isPrivate) {
     var sss =
       Cc["@mozilla.org/ssservice;1"].
         getService(Ci.nsISiteSecurityService);
-    var flags = isPrivate ? Ci.nsISocketProvider.NO_PERMANENT_STORAGE : 0
+    let flags = isPrivate ? Ci.nsISocketProvider.NO_PERMANENT_STORAGE : 0;
     SimpleTest.info("State of example.com: " + sss.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, "example.com", flags));
   }
 
   // These are executed in the order presented.
   // 0.  test that STS works before entering private browsing mode.
   //     (load sts-bootstrapped "plain" tests)
   //  ... clear any STS data ...
   // 1.  test that STS works in private browsing mode
@@ -219,17 +219,17 @@
   //  ... clear any STS data ...
   var tests = [
     test_sts_before_private_mode,
     test_sts_in_private_mode,
     test_sts_after_exiting_private_mode
   ];
 
   function finish() {
-    SpecialPowers.Services.prefs.clearUserPref("browser.startup.page")
+    SpecialPowers.Services.prefs.clearUserPref("browser.startup.page");
     SimpleTest.finish();
   }
   function nextTest() {
     SimpleTest.executeSoon(tests.length ? tests.shift() : finish);
   }
   window.addEventListener('load', nextTest, false);
   </script>
 </head>
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -560,17 +560,17 @@ function startOCSPResponder(serverPort, 
   );
   let ocspResponses = generateOCSPResponses(ocspResponseGenerationArgs,
                                             nssDBLocation);
   let httpServer = new HttpServer();
   httpServer.registerPrefixHandler("/",
     function handleServerCallback(aRequest, aResponse) {
       invalidIdentities.forEach(function(identity) {
         Assert.notEqual(aRequest.host, identity,
-                        "Request host and invalid identity should not match")
+                        "Request host and invalid identity should not match");
       });
       do_print("got request for: " + aRequest.path);
       let basePath = aRequest.path.slice(1).split("/")[0];
       if (expectedBasePaths.length >= 1) {
         Assert.equal(basePath, expectedBasePaths.shift(),
                      "Actual and expected base path should match");
       }
       Assert.ok(expectedCertNames.length >= 1,
@@ -628,17 +628,17 @@ FakeSSLStatus.prototype = {
   },
   QueryInterface: function(aIID) {
     if (aIID.equals(Ci.nsISSLStatus) ||
         aIID.equals(Ci.nsISupports)) {
       return this;
     }
     throw Components.results.NS_ERROR_NO_INTERFACE;
   },
-}
+};
 
 // Utility functions for adding tests relating to certificate error overrides
 
 // Helper function for add_cert_override_test. Probably doesn't need to be
 // called directly.
 function add_cert_override(aHost, aExpectedBits, aSecurityInfo) {
   let sslstatus = aSecurityInfo.QueryInterface(Ci.nsISSLStatusProvider)
                                .SSLStatus;
--- a/security/manager/ssl/tests/unit/test_cert_blocklist.js
+++ b/security/manager/ssl/tests/unit/test_cert_blocklist.js
@@ -85,17 +85,17 @@ const XULAPPINFO_CONTRACTID = "@mozilla.
 const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}");
 registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo",
                           XULAPPINFO_CONTRACTID, XULAppInfoFactory);
 
 var revocations = profile.clone();
 revocations.append("revocations.txt");
 if (!revocations.exists()) {
   let existing = do_get_file("test_onecrl/sample_revocations.txt", false);
-  existing.copyTo(profile,"revocations.txt");
+  existing.copyTo(profile, "revocations.txt");
 }
 
 var certDB = Cc["@mozilla.org/security/x509certdb;1"]
                .getService(Ci.nsIX509CertDB);
 
 // set up a test server to serve the blocklist.xml
 var testserver = new HttpServer();
 
@@ -134,23 +134,23 @@ var initialBlocklist = "<?xml version=\"
     " pubKeyHash='VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8='>" +
     "</certItem></certItems></blocklist>";
 
 var updatedBlocklist = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
     "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">" +
     "<certItems>" +
     "<certItem issuerName='something new in both the issuer'>" +
     "<serialNumber>and the serial number</serialNumber></certItem>" +
-    "</certItems></blocklist>"
+    "</certItems></blocklist>";
 
 
 var blocklists = {
   "/initialBlocklist/" : initialBlocklist,
   "/updatedBlocklist/" : updatedBlocklist
-}
+};
 
 function serveResponse(request, response) {
   do_print("Serving for path " + request.path + "\n");
   response.write(blocklists[request.path]);
 }
 
 for (var path in blocklists) {
   testserver.registerPathHandler(path, serveResponse);
@@ -206,34 +206,34 @@ function test_is_revoked(certList, issue
 
 function fetch_blocklist(blocklistPath) {
   do_print("path is " + blocklistPath + "\n");
   let certblockObserver = {
     observe: function(aSubject, aTopic, aData) {
       Services.obs.removeObserver(this, "blocklist-updated");
       run_next_test();
     }
-  }
+  };
 
   Services.obs.addObserver(certblockObserver, "blocklist-updated", false);
   Services.prefs.setCharPref("extensions.blocklist.url",
                               `http://localhost:${port}/${blocklistPath}`);
   let blocklist = Cc["@mozilla.org/extensions/blocklist;1"]
                     .getService(Ci.nsITimerCallback);
   blocklist.notify(null);
 }
 
 function check_revocations_txt_contents(expected) {
   let profile = do_get_profile();
   let revocations = profile.clone();
   revocations.append("revocations.txt");
   ok(revocations.exists(), "the revocations file should exist");
   let inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
                       .createInstance(Ci.nsIFileInputStream);
-  inputStream.init(revocations,-1, -1, 0);
+  inputStream.init(revocations, -1, -1, 0);
   inputStream.QueryInterface(Ci.nsILineInputStream);
   let contents = "";
   let hasmore = false;
   do {
     var line = {};
     hasmore = inputStream.readLine(line);
     contents = contents + (contents.length == 0 ? "" : "\n") + line.value;
   } while (hasmore);
--- a/security/manager/ssl/tests/unit/test_cert_overrides.js
+++ b/security/manager/ssl/tests/unit/test_cert_overrides.js
@@ -68,17 +68,17 @@ function check_telemetry() {
 
 // Internally, specifying "port" -1 is the same as port 443. This tests that.
 function run_port_equivalency_test(inPort, outPort) {
   Assert.ok((inPort == 443 && outPort == -1) || (inPort == -1 && outPort == 443),
             "The two specified ports must be -1 and 443 (in any order)");
   let certOverrideService = Cc["@mozilla.org/security/certoverride;1"]
                               .getService(Ci.nsICertOverrideService);
   let cert = constructCertFromFile("bad_certs/default-ee.pem");
-  let expectedBits = Ci.nsICertOverrideService.ERROR_UNTRUSTED
+  let expectedBits = Ci.nsICertOverrideService.ERROR_UNTRUSTED;
   let expectedTemporary = true;
   certOverrideService.rememberValidityOverride("example.com", inPort, cert,
                                                expectedBits, expectedTemporary);
   let actualBits = {};
   let actualTemporary = {};
   Assert.ok(certOverrideService.hasMatchingOverride("example.com", outPort,
                                                     cert, actualBits,
                                                     actualTemporary),
--- a/security/manager/ssl/tests/unit/test_constructX509FromBase64.js
+++ b/security/manager/ssl/tests/unit/test_constructX509FromBase64.js
@@ -24,17 +24,17 @@ function excMessage(e) {
 }
 
 function testGood(data) {
   try {
     let cert = certDB.constructX509FromBase64(data.cert);
     equal(cert.commonName, data.cn,
           "Actual and expected commonName should match");
   } catch (e) {
-    do_print(`Exception: ${excMessage(e)}`)
+    do_print(`Exception: ${excMessage(e)}`);
     ok(false, `Should not have gotten an exception for "CN=${data.cn}"`);
   }
 }
 
 function testBad(data) {
   throws(() => certDB.constructX509FromBase64(data.input), data.result,
          `Should get "${data.result}" for "${data.input}"`);
 }
--- a/security/manager/ssl/tests/unit/test_name_constraints.js
+++ b/security/manager/ssl/tests/unit/test_name_constraints.js
@@ -130,17 +130,17 @@ function run_test() {
   check_ok(certFromFile('cn-www.foo.org_o-bar_c-us-alt-foo.com-int-nc-foo.com-int-nc-c-us-ca-nc'));
   check_ok(certFromFile('cn-www.foo.com_o-bar_c-us-alt-foo.com-int-nc-foo.com-int-nc-c-us-ca-nc'));
   check_fail(certFromFile('cn-www.foo.org_o-bar_c-us-alt-foo.org-int-nc-foo.com-int-nc-c-us-ca-nc'));
   check_fail(certFromFile('cn-www.foo.com_o-bar_c-us-alt-foo.com-a.a.us-b.a.us-int-nc-foo.com-int-nc-c-us-ca-nc'));
 
   // Testing on a non constrainted root an intermediate name contrainted to
   // permited dirNAME:C=US and  permited DNS:foo.com
   // checks for compostability of different name constraints with same cert
-  check_ok_ca(load_cert('int-nc-perm-foo.com_c-us-ca-nc' , ',,'));
+  check_ok_ca(load_cert('int-nc-perm-foo.com_c-us-ca-nc', ',,'));
   check_fail(certFromFile('cn-www.foo.com-int-nc-perm-foo.com_c-us-ca-nc'));
   check_fail(certFromFile('cn-www.foo.org-int-nc-perm-foo.com_c-us-ca-nc'));
   check_fail(certFromFile('cn-www.foo.com-alt-foo.org-int-nc-perm-foo.com_c-us-ca-nc'));
   check_fail(certFromFile('cn-www.foo.org-alt-foo.com-int-nc-perm-foo.com_c-us-ca-nc'));
   check_fail(certFromFile('cn-www.foo.com-alt-foo.com-int-nc-perm-foo.com_c-us-ca-nc'));
   check_fail(certFromFile('cn-www.foo.org-alt-foo.org-int-nc-perm-foo.com_c-us-ca-nc'));
   check_fail(certFromFile('cn-www.foo.com-alt-foo.com-a.a.us-b.a.us-int-nc-perm-foo.com_c-us-ca-nc'));
   check_ok(certFromFile('cn-www.foo.com_o-bar_c-us-int-nc-perm-foo.com_c-us-ca-nc'));
--- a/security/manager/ssl/tests/unit/test_pinning_header_parsing.js
+++ b/security/manager/ssl/tests/unit/test_pinning_header_parsing.js
@@ -70,17 +70,17 @@ function checkPassSettingPin(pinValue) {
 
 function checkPassRemovingPin(pinValue) {
   return checkPassValidPin(pinValue, false);
 }
 
 const NON_ISSUED_KEY_HASH1 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
 const NON_ISSUED_KEY_HASH2 = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ=";
 const PINNING_ROOT_KEY_HASH = "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=";
-const MAX_AGE_ZERO = "max-age=0;"
+const MAX_AGE_ZERO = "max-age=0;";
 const VALID_PIN1 = `pin-sha256="${PINNING_ROOT_KEY_HASH}";`;
 const BACKUP_PIN1 = `pin-sha256="${NON_ISSUED_KEY_HASH1}";`;
 const BACKUP_PIN2 = `pin-sha256="${NON_ISSUED_KEY_HASH2}";`;
 const BROKEN_PIN1 = "pin-sha256=\"jdjsjsjs\";";
 const GOOD_MAX_AGE = "max-age=69403;";
 const INCLUDE_SUBDOMAINS = "includeSubdomains;";
 const REPORT_URI = "report-uri=\"https://www.example.com/report/\";";
 const UNRECOGNIZED_DIRECTIVE = "unreconized-dir=12343;";
--- a/security/manager/ssl/tests/unit/test_signed_apps.js
+++ b/security/manager/ssl/tests/unit/test_signed_apps.js
@@ -104,17 +104,17 @@ function truncateEntry(entry, entryInput
   if (entryInput.available() == 0) {
     throw "Truncating already-zero length entry will result in identical entry.";
   }
 
   var content = Cc["@mozilla.org/io/string-input-stream;1"]
                   .createInstance(Ci.nsIStringInputStream);
   content.data = "";
 
-  return [entry, content]
+  return [entry, content];
 }
 
 function run_test() {
   run_next_test();
 }
 
 function check_open_result(name, expectedRv) {
   return function openSignedAppFileCallback(rv, aZipReader, aSignerCert) {
--- a/security/manager/ssl/tests/unit/test_signed_dir.js
+++ b/security/manager/ssl/tests/unit/test_signed_dir.js
@@ -89,17 +89,17 @@ add_test(function() {
   verifyDirAsync("'empty meta dir'", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED,
                  {delete: ["META-INF/mozilla.rsa",
                            "META-INF/mozilla.sf",
                            "META-INF/manifest.mf"]});
 });
 
 add_test(function() {
   verifyDirAsync("'two rsa files'", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
-                 {copy: [["META-INF/mozilla.rsa","extra.rsa"]]});
+                 {copy: [["META-INF/mozilla.rsa", "extra.rsa"]]});
 });
 
 add_test(function() {
   verifyDirAsync("'corrupt rsa file'", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
                  {corrupt: ["META-INF/mozilla.rsa"]});
 });
 
 add_test(function() {
@@ -109,22 +109,22 @@ add_test(function() {
 
 add_test(function() {
   verifyDirAsync("'corrupt sf file'", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
                  {corrupt: ["META-INF/mozilla.sf"]});
 });
 
 add_test(function() {
   verifyDirAsync("'extra .sf file (invalid)'", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY,
-                 {copy: [["META-INF/mozilla.rsa","extra.sf"]]});
+                 {copy: [["META-INF/mozilla.rsa", "extra.sf"]]});
 });
 
 add_test(function() {
   verifyDirAsync("'extra .sf file (valid)'", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY,
-                 {copy: [["META-INF/mozilla.sf","extra.sf"]]});
+                 {copy: [["META-INF/mozilla.sf", "extra.sf"]]});
 });
 
 add_test(function() {
   verifyDirAsync("'missing manifest'", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
                  {delete: ["META-INF/manifest.mf"]});
 });
 
 add_test(function() {
@@ -139,32 +139,32 @@ add_test(function() {
 
 add_test(function() {
   verifyDirAsync("'corrupt file'", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY,
                  {corrupt: ["bootstrap.js"]});
 });
 
 add_test(function() {
   verifyDirAsync("'extra file'", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY,
-                 {copy: [["bootstrap.js","extra"]]});
+                 {copy: [["bootstrap.js", "extra"]]});
 });
 
 add_test(function() {
   verifyDirAsync("'missing file in dir'", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING,
                  {delete: ["content/options.xul"]});
 });
 
 add_test(function() {
   verifyDirAsync("'corrupt file in dir'", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY,
                  {corrupt: ["content/options.xul"]});
 });
 
 add_test(function() {
   verifyDirAsync("'extra file in dir'", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY,
-                 {copy: [["content/options.xul","extra"]]});
+                 {copy: [["content/options.xul", "extra"]]});
 });
 
 do_register_cleanup(function() {
   if (gTarget.exists()) {
     gTarget.remove(true);
   }
 });
 
--- a/security/manager/ssl/tests/unit/test_sts_ipv4_ipv6.js
+++ b/security/manager/ssl/tests/unit/test_sts_ipv4_ipv6.js
@@ -12,17 +12,17 @@ function check_ip(s, v, ip) {
   }
   str += "/";
 
   let uri = Services.io.newURI(str, null, null);
 
   let parsedMaxAge = {};
   let parsedIncludeSubdomains = {};
   s.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
-                  "max-age=1000;includeSubdomains", sslStatus , 0,
+                  "max-age=1000;includeSubdomains", sslStatus, 0,
                   parsedMaxAge, parsedIncludeSubdomains);
 
   /* Test that processHeader will ignore headers for an uri, if the uri
    * contains an IP address not a hostname.
    * If processHeader indeed ignore the header, then the output parameters will
    * remain empty, and we shouldn't see the values passed as the header.
    */
   notEqual(parsedMaxAge.value, 1000);
--- a/security/manager/ssl/tests/unit/test_toolkit_securityreporter.js
+++ b/security/manager/ssl/tests/unit/test_toolkit_securityreporter.js
@@ -47,17 +47,17 @@ function getReportCheck(expectReport, ex
         response.setStatusLine(null, 201, "Created");
         response.write("Created");
       } else {
         do_throw("No report should have been received");
       }
     });
 
     reporter.reportTLSError(transportSecurityInfo, "example.com", -1);
-  }
+  };
 }
 
 // read the request body from a request
 function readDataFromRequest(aRequest) {
   if (aRequest.method == "POST" || aRequest.method == "PUT") {
     if (aRequest.bodyInputStream) {
       let inputStream = new BinaryInputStream(aRequest.bodyInputStream);
       let bytes = [];
--- a/security/manager/tools/genHPKPStaticPins.js
+++ b/security/manager/tools/genHPKPStaticPins.js
@@ -423,17 +423,17 @@ function parseJson(filename) {
   return JSON.parse(json);
 }
 
 function nameToAlias(certName) {
   // change the name to a string valid as a c identifier
   // remove  non-ascii characters
   certName = certName.replace(/[^[:ascii:]]/g, "_");
   // replace non word characters
-  certName = certName.replace(/[^A-Za-z0-9]/g ,"_");
+  certName = certName.replace(/[^A-Za-z0-9]/g, "_");
 
   return "k" + certName + "Fingerprint";
 }
 
 function compareByName (a, b) {
   return a.name.localeCompare(b.name);
 }
 
--- a/security/manager/tools/genRootCAHashes.js
+++ b/security/manager/tools/genRootCAHashes.js
@@ -233,17 +233,17 @@ insertTrustAnchorsFromDatabase();
 // Update known hashes before we sort
 writeTrustAnchors(trustAnchorsFile);
 
 // Sort all trust anchors before writing, as AccumulateRootCA.cpp
 // will perform binary searches
 gTrustAnchors.roots.sort(function(a, b) {
   // We need to work from the binary values, not the base64 values.
   let aBin = atob(a.sha256Fingerprint);
-  let bBin = atob(b.sha256Fingerprint)
+  let bBin = atob(b.sha256Fingerprint);
 
   if (aBin < bBin) {
     return -1;
   }
   if (aBin > bBin) {
     return 1;
   }
   return 0;
deleted file mode 100644
--- a/testing/taskcluster/tasks/b2g_e2e_tests_base_definition.yml
+++ /dev/null
@@ -1,103 +0,0 @@
-task:
-  created: '{{now}}'
-  deadline: '{{#from_now}}24 hours{{/from_now}}'
-  metadata:
-    source: '{{source}}'
-    owner: mozilla-taskcluster-maintenance@mozilla.com
-  tags:
-    createdForUser: {{owner}}
-  workerType: b2gtest
-  provisionerId: aws-provisioner-v1
-  schedulerId: task-graph-scheduler
-
-  scopes:
-    - 'docker-worker:image:{{#docker_image}}tester-device{{/docker_image}}'
-    - 'queue:create-task:aws-provisioner-v1/testdroid-device'
-    - 'docker-worker:cache:level-{{level}}-{{project}}-tc-vcs'
-    - 'docker-worker:capability:device:phone'
-
-  payload:
-    image: '{{#docker_image}}tester-device{{/docker_image}}'
-    maxRunTime: 7200
-    cache:
-      level-{{level}}-{{project}}-tc-vcs: '/home/worker/.tc-vcs'
-    env:
-      MOZ_BUILD_DATE: '{{pushdate}}'
-      GAIA_HEAD_REPOSITORY: '{{{gaia_head_repository}}}'
-      GAIA_BASE_REPOSITORY: '{{{gaia_base_repository}}}'
-      GAIA_REF: '{{{gaia_ref}}}'
-      GAIA_REV: '{{{gaia_rev}}}'
-      TESTS_TIMEOUT_IN_MS: 30000
-      MANIFEST_PATH: gaia/source/tests/python/gaia-ui-tests/gaiatest/tests/functional/manifest.ini
-      MANIFEST_INCLUDE_EXCLUDE: b2g-dsds
-
-    log: 'private/b2g/logs/live.log'
-
-    # All builds share a common artifact directory for ease of uploading.
-    artifacts:
-      'private/b2g/device.json':
-        type: file
-        path: '/home/worker/data/device.json'
-        expires: '{{#from_now}}1 year{{/from_now}}'
-
-      'private/b2g/logs':
-        type: directory
-        path: '/home/worker/upload/logs/'
-        expires: '{{#from_now}}1 year{{/from_now}}'
-
-      'private/b2g/videos':
-        type: directory
-        path: '/home/worker/upload/videos/'
-        expires: '{{#from_now}}3 months{{/from_now}}'
-
-    features:
-      testdroidProxy: true
-
-    capabilities:
-      devices:
-        phone:
-          type: 'flame'
-          memory: '512'
-          sims: '0'
-          build: '{{{img_url}}}'
-
-    command:
-      - >
-        adb
-        -H $PROXY_HOST
-        -P $ADB_PORT
-        -s $SERIAL_ID
-        logcat
-        -v threadtime
-        &> /home/worker/upload/logs/logcat.log
-        &
-
-      - >
-        gaiatest
-        --total-chunks={{total_chunks}}
-        --this-chunk={{chunk}}
-        --testvars=/home/worker/data/gaia_testvars.json
-        --testvars=/home/worker/data/device.json
-        --adb-host=$PROXY_HOST
-        --adb-port=$ADB_PORT
-        --address=$PROXY_HOST:$MARIONETTE_PORT
-        --device $SERIAL_ID
-        --xml-output=/home/worker/upload/logs/xml_output.xml
-        --log-html=/home/worker/upload/logs/index.html
-        --log-mach=-
-        --log-raw=/home/worker/upload/logs/raw.log
-        --capture=whenfail
-        --capturefolder=/home/worker/upload/videos/
-        --restart
-        --timeout=$TESTS_TIMEOUT_IN_MS
-        --type=$MANIFEST_INCLUDE_EXCLUDE
-        $MANIFEST_PATH
-
-  extra:
-    treeherder:
-      groupName: B2G end-to-end tests (Python)
-      groupSymbol: e2e-py
-      productName: b2g
-    chunks:
-      total: 1
-      current: {{chunk}}
--- a/testing/taskcluster/tasks/branches/b2g-inbound/job_flags.yml
+++ b/testing/taskcluster/tasks/branches/b2g-inbound/job_flags.yml
@@ -1,20 +1,6 @@
 ---
 # For complete sample of all build and test jobs,
 # see <gecko>/testing/taskcluster/tasks/branches/base_job_flags.yml
 
 $inherits:
   from: tasks/branches/base_jobs.yml
-
-tests:
-  gaia-ui-test-functional:
-    allowed_build_tasks:
-      tasks/builds/b2g_flame_kk_eng.yml:
-        task: tasks/tests/b2g_e2e_tests.yml
-  gaia-ui-test-functional-dsds:
-    allowed_build_tasks:
-      tasks/builds/b2g_flame_kk_eng.yml:
-        task: tasks/tests/b2g_e2e_dsds_tests.yml
-  gaia-ui-test-unit:
-    allowed_build_tasks:
-      tasks/builds/b2g_flame_kk_eng.yml:
-        task: tasks/tests/b2g_e2e_api_tests.yml
--- a/testing/taskcluster/tasks/branches/base_job_flags.yml
+++ b/testing/taskcluster/tasks/branches/base_job_flags.yml
@@ -26,17 +26,16 @@ flags:
     # with "foobar-7".  Note that a few aliases allowed chunks to be specified
     # without a leading `-`, for example 'mochitest-dt1'. That's no longer
     # supported.
     cppunit: /cppunit.*/
     crashtest: /crashtest.*/
     crashtest-e10s: /crashtest-e10s.*/
     e10s: /.*e10s.*/
     gaia-js-integration: /.*gaia-js-integration.*/
-    gaia-ui-test: /.*gaia-ui-test.*/
     gtest: /gtest.*/
     jittest: /jittest.*/
     jittests: /jittest.*/
     jsreftest: /jsreftest.*/
     jsreftest-e10s: /jsreftest-e10s.*/
     luciddream: /luciddream.*/
     marionette: /marionette.*/
     marionette-e10s: /marionette-e10s.*/
@@ -116,20 +115,16 @@ flags:
     - crashtest-e10s
     - crashtest-ipc
     - gaia-build
     - gaia-build-unit
     - gaia-js-integration
     - gaia-linter
     - gaia-unit
     - gaia-unit-oop
-    - gaia-ui-test-sanity
-    - gaia-ui-test-functional
-    - gaia-ui-test-functional-dsds
-    - gaia-ui-test-unit
     - gtest
     - jetpack
     - jittests
     - jsreftest
     - jsreftest-e10s
     - luciddream
     - marionette
     - marionette-e10s
@@ -154,9 +149,8 @@ flags:
     - reftest-ipc
     - reftest-no-accel
     - reftest-sanity-oop
     - web-platform-tests
     - web-platform-tests-e10s
     - web-platform-tests-reftests
     - web-platform-tests-reftests-e10s
     - xpcshell
-
deleted file mode 100644
--- a/testing/taskcluster/tasks/tests/b2g_e2e_api_tests.yml
+++ /dev/null
@@ -1,16 +0,0 @@
----
-$inherits:
-  from: tasks/b2g_e2e_tests_base_definition.yml
-task:
-  metadata:
-    name: B2G gecko APIs tests (on device)
-    description: Tests which verifies the APIs needed for setting up the e2e tests
-  workerType: flame-kk-0-sim
-
-  payload:
-    env:
-      MANIFEST_PATH: gaia/source/tests/python/gaia-ui-tests/gaiatest/tests/unit/manifest.ini
-
-  extra:
-    treeherder:
-      symbol: 'a'
deleted file mode 100644
--- a/testing/taskcluster/tasks/tests/b2g_e2e_dsds_tests.yml
+++ /dev/null
@@ -1,21 +0,0 @@
----
-$inherits:
-  from: tasks/b2g_e2e_tests_base_definition.yml
-task:
-  metadata:
-    name: B2G end-to-end DSDS tests
-    description: Tests which require 2 SIM cards
-  workerType: flame-kk-2-sim
-
-  payload:
-    env:
-      MANIFEST_INCLUDE_EXCLUDE: b2g+dsds
-
-    capabilities:
-      devices:
-        phone:
-          sims: '2'
-
-  extra:
-    treeherder:
-      symbol: 'dsds'
deleted file mode 100644
--- a/testing/taskcluster/tasks/tests/b2g_e2e_tests.yml
+++ /dev/null
@@ -1,21 +0,0 @@
----
-$inherits:
-  from: tasks/b2g_e2e_tests_base_definition.yml
-task:
-  metadata:
-    name: B2G end-to-end tests
-    description: Regular end-to-end tests
-  workerType: flame-kk-1-sim
-
-  payload:
-    capabilities:
-      devices:
-        phone:
-          sims: '1'
-
-  extra:
-    chunks:
-      total: 6
-
-    treeherder:
-      symbol: '{{chunk}}'
--- a/widget/TouchEvents.h
+++ b/widget/TouchEvents.h
@@ -78,17 +78,17 @@ public:
     AssignGUIEventData(aEvent, aCopyTargets);
 
     panDirection = aEvent.panDirection;
     displayPanFeedback = aEvent.displayPanFeedback;
   }
 };
 
 /******************************************************************************
- * mozilla::WidgetTouchEvent
+ * mozilla::WidgetSimpleGestureEvent
  ******************************************************************************/
 
 class WidgetSimpleGestureEvent : public WidgetMouseEventBase
 {
 public:
   virtual WidgetSimpleGestureEvent* AsSimpleGestureEvent() override
   {
     return this;
@@ -158,29 +158,30 @@ class WidgetTouchEvent : public WidgetIn
 public:
   typedef nsTArray<RefPtr<mozilla::dom::Touch>> TouchArray;
   typedef AutoTArray<RefPtr<mozilla::dom::Touch>, 10> AutoTouchArray;
 
   virtual WidgetTouchEvent* AsTouchEvent() override { return this; }
 
   WidgetTouchEvent()
   {
+    MOZ_COUNT_CTOR(WidgetTouchEvent);
   }
 
   WidgetTouchEvent(const WidgetTouchEvent& aOther)
     : WidgetInputEvent(aOther.mFlags.mIsTrusted, aOther.mMessage, aOther.widget,
                        eTouchEventClass)
   {
+    MOZ_COUNT_CTOR(WidgetTouchEvent);
     modifiers = aOther.modifiers;
     time = aOther.time;
     timeStamp = aOther.timeStamp;
     touches.AppendElements(aOther.touches);
     mFlags.mCancelable = mMessage != eTouchCancel;
     mFlags.mHandledByAPZ = aOther.mFlags.mHandledByAPZ;
-    MOZ_COUNT_CTOR(WidgetTouchEvent);
   }
 
   WidgetTouchEvent(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget)
     : WidgetInputEvent(aIsTrusted, aMessage, aWidget, eTouchEventClass)
   {
     MOZ_COUNT_CTOR(WidgetTouchEvent);
     mFlags.mCancelable = mMessage != eTouchCancel;
   }
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -1432,16 +1432,24 @@ auto GeckoLayerClient::SyncFrameMetrics(
 constexpr char GeckoLayerClient::SyncViewportInfo_t::name[];
 constexpr char GeckoLayerClient::SyncViewportInfo_t::signature[];
 
 auto GeckoLayerClient::SyncViewportInfo(int32_t a0, int32_t a1, int32_t a2, int32_t a3, float a4, bool a5, int32_t a6) const -> mozilla::jni::Object::LocalRef
 {
     return mozilla::jni::Method<SyncViewportInfo_t>::Call(GeckoLayerClient::mCtx, nullptr, a0, a1, a2, a3, a4, a5, a6);
 }
 
+constexpr char GeckoLayerClient::SynthesizeNativeTouchPoint_t::name[];
+constexpr char GeckoLayerClient::SynthesizeNativeTouchPoint_t::signature[];
+
+auto GeckoLayerClient::SynthesizeNativeTouchPoint(int32_t a0, int32_t a1, int32_t a2, int32_t a3, double a4, int32_t a5) const -> void
+{
+    return mozilla::jni::Method<SynthesizeNativeTouchPoint_t>::Call(GeckoLayerClient::mCtx, nullptr, a0, a1, a2, a3, a4, a5);
+}
+
 template<> const char mozilla::jni::Context<ImmutableViewportMetrics, jobject>::name[] =
         "org/mozilla/gecko/gfx/ImmutableViewportMetrics";
 
 constexpr char ImmutableViewportMetrics::New_t::name[];
 constexpr char ImmutableViewportMetrics::New_t::signature[];
 
 auto ImmutableViewportMetrics::New(float a0, float a1, float a2, float a3, float a4, float a5, float a6, float a7, float a8, float a9, int32_t a10, int32_t a11, float a12) -> ImmutableViewportMetrics::LocalRef
 {
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -3365,16 +3365,37 @@ public:
                 "(IIIIFZI)Lorg/mozilla/gecko/gfx/ViewTransform;";
         static const bool isStatic = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
     auto SyncViewportInfo(int32_t, int32_t, int32_t, int32_t, float, bool, int32_t) const -> mozilla::jni::Object::LocalRef;
 
+    struct SynthesizeNativeTouchPoint_t {
+        typedef GeckoLayerClient Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int32_t,
+                int32_t,
+                int32_t,
+                int32_t,
+                double,
+                int32_t> Args;
+        static constexpr char name[] = "synthesizeNativeTouchPoint";
+        static constexpr char signature[] =
+                "(IIIIDI)V";
+        static const bool isStatic = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+    auto SynthesizeNativeTouchPoint(int32_t, int32_t, int32_t, int32_t, double, int32_t) const -> void;
+
     static const bool isMultithreaded = true;
 
 };
 
 class ImmutableViewportMetrics : public mozilla::jni::ObjectBase<ImmutableViewportMetrics, jobject>
 {
 public:
     explicit ImmutableViewportMetrics(const Context& ctx) : ObjectBase<ImmutableViewportMetrics, jobject>(ctx) {}
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -3258,16 +3258,55 @@ nsWindow::GetIMEUpdatePreference()
     if (GetInputContext().mIMEState.mEnabled == IMEState::PLUGIN) {
       return nsIMEUpdatePreference();
     }
     return nsIMEUpdatePreference(
         nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE |
         nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE);
 }
 
+nsresult
+nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId,
+                                     TouchPointerState aPointerState,
+                                     ScreenIntPoint aPointerScreenPoint,
+                                     double aPointerPressure,
+                                     uint32_t aPointerOrientation,
+                                     nsIObserver* aObserver)
+{
+    mozilla::widget::AutoObserverNotifier notifier(aObserver, "touchpoint");
+
+    int eventType;
+    switch (aPointerState) {
+    case TOUCH_CONTACT:
+        // This could be a ACTION_DOWN or ACTION_MOVE depending on the
+        // existing state; it is mapped to the right thing in Java.
+        eventType = sdk::MotionEvent::ACTION_POINTER_DOWN;
+        break;
+    case TOUCH_REMOVE:
+        // This could be turned into a ACTION_UP in Java
+        eventType = sdk::MotionEvent::ACTION_POINTER_UP;
+        break;
+    case TOUCH_CANCEL:
+        eventType = sdk::MotionEvent::ACTION_CANCEL;
+        break;
+    case TOUCH_HOVER:   // not supported for now
+    default:
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    MOZ_ASSERT(mGLControllerSupport);
+    GeckoLayerClient::LocalRef client = mGLControllerSupport->GetLayerClient();
+    client->SynthesizeNativeTouchPoint(aPointerId, eventType,
+        aPointerScreenPoint.x, aPointerScreenPoint.y, aPointerPressure,
+        aPointerOrientation);
+
+    return NS_OK;
+}
+
+
 void
 nsWindow::DrawWindowUnderlay(LayerManagerComposite* aManager,
                              LayoutDeviceIntRect aRect)
 {
     if (Destroyed()) {
         return;
     }
     MOZ_ASSERT(mGLControllerSupport);
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -181,16 +181,23 @@ public:
     virtual bool WidgetPaintsBackground() override;
 
     virtual uint32_t GetMaxTouchPoints() const override;
 
     void UpdateZoomConstraints(const uint32_t& aPresShellId,
                                const FrameMetrics::ViewID& aViewId,
                                const mozilla::Maybe<ZoomConstraints>& aConstraints) override;
 
+    nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId,
+                                        TouchPointerState aPointerState,
+                                        ScreenIntPoint aPointerScreenPoint,
+                                        double aPointerPressure,
+                                        uint32_t aPointerOrientation,
+                                        nsIObserver* aObserver) override;
+
 protected:
     void BringToFront();
     nsWindow *FindTopLevel();
     bool IsTopLevel();
 
     RefPtr<mozilla::TextComposition> GetIMEComposition();
     void RemoveIMEComposition();
 
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -6838,13 +6838,95 @@ nsWindow::SynthesizeNativeMouseScrollEve
   }
 #endif
 
   gdk_event_put(&event);
 
   return NS_OK;
 }
 
+#if GTK_CHECK_VERSION(3,4,0)
+nsresult
+nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId,
+                                     TouchPointerState aPointerState,
+                                     ScreenIntPoint aPointerScreenPoint,
+                                     double aPointerPressure,
+                                     uint32_t aPointerOrientation,
+                                     nsIObserver* aObserver)
+{
+  AutoObserverNotifier notifier(aObserver, "touchpoint");
+
+  if (!mGdkWindow) {
+    return NS_OK;
+  }
+
+  GdkEvent event;
+  memset(&event, 0, sizeof(GdkEvent));
+
+  static std::map<uint32_t, GdkEventSequence*> sKnownPointers;
+
+  auto result = sKnownPointers.find(aPointerId);
+  switch (aPointerState) {
+  case TOUCH_CONTACT:
+    if (result == sKnownPointers.end()) {
+      // GdkEventSequence isn't a thing we can instantiate, and never gets
+      // dereferenced in the gtk code. It's an opaque pointer, the only
+      // requirement is that it be distinct from other instances of
+      // GdkEventSequence*.
+      event.touch.sequence = (GdkEventSequence*)((uintptr_t)aPointerId);
+      sKnownPointers[aPointerId] = event.touch.sequence;
+      event.type = GDK_TOUCH_BEGIN;
+    } else {
+      event.touch.sequence = result->second;
+      event.type = GDK_TOUCH_UPDATE;
+    }
+    break;
+  case TOUCH_REMOVE:
+    event.type = GDK_TOUCH_END;
+    if (result == sKnownPointers.end()) {
+      NS_WARNING("Tried to synthesize touch-end for unknown pointer!");
+      return NS_ERROR_UNEXPECTED;
+    }
+    event.touch.sequence = result->second;
+    sKnownPointers.erase(result);
+    break;
+  case TOUCH_CANCEL:
+    event.type = GDK_TOUCH_CANCEL;
+    if (result == sKnownPointers.end()) {
+      NS_WARNING("Tried to synthesize touch-cancel for unknown pointer!");
+      return NS_ERROR_UNEXPECTED;
+    }
+    event.touch.sequence = result->second;
+    sKnownPointers.erase(result);
+    break;
+  case TOUCH_HOVER:
+  default:
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  event.touch.window = mGdkWindow;
+  event.touch.time = GDK_CURRENT_TIME;
+
+  GdkDisplay* display = gdk_window_get_display(mGdkWindow);
+  GdkDeviceManager* device_manager = gdk_display_get_device_manager(display);
+  event.touch.device = gdk_device_manager_get_client_pointer(device_manager);
+
+  event.touch.x_root = DevicePixelsToGdkCoordRoundDown(aPointerScreenPoint.x);
+  event.touch.y_root = DevicePixelsToGdkCoordRoundDown(aPointerScreenPoint.y);
+
+  LayoutDeviceIntPoint pointInWindow =
+    ViewAs<LayoutDevicePixel>(aPointerScreenPoint,
+                              PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent)
+    - WidgetToScreenOffset();
+  event.touch.x = DevicePixelsToGdkCoordRoundDown(pointInWindow.x);
+  event.touch.y = DevicePixelsToGdkCoordRoundDown(pointInWindow.y);
+
+  gdk_event_put(&event);
+
+  return NS_OK;
+}
+#endif
+
 int32_t
 nsWindow::RoundsWidgetCoordinatesTo()
 {
     return GdkScaleFactor();
 }
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -333,16 +333,25 @@ public:
                                                       uint32_t aNativeMessage,
                                                       double aDeltaX,
                                                       double aDeltaY,
                                                       double aDeltaZ,
                                                       uint32_t aModifierFlags,
                                                       uint32_t aAdditionalFlags,
                                                       nsIObserver* aObserver) override;
 
+#if GTK_CHECK_VERSION(3,4,0)
+    virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId,
+                                                TouchPointerState aPointerState,
+                                                ScreenIntPoint aPointerScreenPoint,
+                                                double aPointerPressure,
+                                                uint32_t aPointerOrientation,
+                                                nsIObserver* aObserver) override;
+#endif
+
     // HiDPI scale conversion
     gint GdkScaleFactor();
 
     // To GDK
     gint DevicePixelsToGdkCoordRoundUp(int pixels);
     gint DevicePixelsToGdkCoordRoundDown(int pixels);
     GdkPoint DevicePixelsToGdkPointRoundDown(LayoutDeviceIntPoint point);
     GdkRectangle DevicePixelsToGdkSizeRoundUp(LayoutDeviceIntSize pixelSize);
--- a/widget/windows/nsWindowBase.cpp
+++ b/widget/windows/nsWindowBase.cpp
@@ -85,18 +85,18 @@ nsWindowBase::InjectTouchPoint(uint32_t 
   info.touchFlags = TOUCH_FLAG_NONE;
   info.touchMask = TOUCH_MASK_CONTACTAREA|TOUCH_MASK_ORIENTATION|TOUCH_MASK_PRESSURE;
   info.pressure = aPressure;
   info.orientation = aOrientation;
   
   info.pointerInfo.pointerFlags = aFlags;
   info.pointerInfo.pointerType =  PT_TOUCH;
   info.pointerInfo.pointerId = aId;
-  info.pointerInfo.ptPixelLocation.x = LogToPhys(aPointerScreenPoint.x);
-  info.pointerInfo.ptPixelLocation.y = LogToPhys(aPointerScreenPoint.y);
+  info.pointerInfo.ptPixelLocation.x = aPointerScreenPoint.x;
+  info.pointerInfo.ptPixelLocation.y = aPointerScreenPoint.y;
 
   info.rcContact.top = info.pointerInfo.ptPixelLocation.y - 2;
   info.rcContact.bottom = info.pointerInfo.ptPixelLocation.y + 2;
   info.rcContact.left = info.pointerInfo.ptPixelLocation.x - 2;
   info.rcContact.right = info.pointerInfo.ptPixelLocation.x + 2;
   
   if (!sInjectTouchFuncPtr(1, &info)) {
     WinUtils::Log("InjectTouchInput failure. GetLastError=%d", GetLastError());