Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 06 Feb 2017 17:45:21 -0800
changeset 341005 af8a2573d0f1e9cc6f2ba0ab67d7a702a197f177
parent 340964 4f8b2d44eaa0c67bc7e7f5bac4e41e9a4b2e27c7 (current diff)
parent 341004 88c906492f0eaaf9cbdf8da5b85611da7a360631 (diff)
child 341025 330ab4695793c499110152a6582afca8e87ae46c
child 341097 f0453084d86e87070b2894eab61a2b58f1964768
push id31322
push userkwierso@gmail.com
push dateTue, 07 Feb 2017 01:45:28 +0000
treeherdermozilla-central@af8a2573d0f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.0a1
first release with
nightly linux32
af8a2573d0f1 / 54.0a1 / 20170207110148 / files
nightly linux64
af8a2573d0f1 / 54.0a1 / 20170207110148 / files
nightly mac
af8a2573d0f1 / 54.0a1 / 20170207030214 / files
nightly win32
af8a2573d0f1 / 54.0a1 / 20170207030214 / files
nightly win64
af8a2573d0f1 / 54.0a1 / 20170207030214 / 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 MozReview-Commit-ID: 2yQjOIMCCiO
security/nss/coreconf/sanitizers.mk
--- a/.cron.yml
+++ b/.cron.yml
@@ -20,8 +20,18 @@ jobs:
           treeherder-symbol: Na
           triggered-by: nightly
           target-tasks-method: nightly_fennec
       run-on-projects:
           - mozilla-central
           - date
       when: []  # never (temporary)
 
+    - name: nightly-mochitest-valgrind
+      job:
+          type: decision-task
+          treeherder-symbol: Vg
+          target-tasks-method: mochitest_valgrind
+      run-on-projects:
+          - mozilla-central
+      when:
+          - {hour: 16, minute: 0}
+          - {hour: 4, minute: 0}
--- a/.eslintignore
+++ b/.eslintignore
@@ -110,17 +110,16 @@ devtools/server/actors/object.js
 devtools/server/actors/script.js
 devtools/server/actors/styleeditor.js
 devtools/server/actors/stylesheets.js
 devtools/server/tests/browser/**
 !devtools/server/tests/browser/browser_webextension_inspected_window.js
 devtools/server/tests/mochitest/**
 devtools/server/tests/unit/**
 devtools/shared/heapsnapshot/**
-devtools/shared/transport/tests/unit/**
 devtools/shared/webconsole/test/**
 
 # Ignore devtools pre-processed files
 devtools/client/framework/toolbox-process-window.js
 devtools/client/performance/system.js
 devtools/client/webide/webide-prefs.js
 devtools/client/preferences/**
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -698,16 +698,17 @@
                    aria-label="&urlbar.viewSiteInfo.label;"
                    onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
                    onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
                    ondragstart="gIdentityHandler.onDragStart(event);">
                 <image id="identity-icon"
                        consumeanchor="identity-box"
                        onclick="PageProxyClickHandler(event);"/>
                 <image id="sharing-icon" mousethrough="always"/>
+                <image id="tracking-protection-icon"/>
                 <box id="blocked-permissions-container" align="center">
                   <image data-permission-id="geo" class="blocked-permission-icon geo-icon" role="button"
                          tooltiptext="&urlbar.geolocationBlocked.tooltip;"/>
                   <image data-permission-id="desktop-notification" class="blocked-permission-icon desktop-notification-icon" role="button"
                          tooltiptext="&urlbar.webNotificationsBlocked.tooltip;"/>
                   <image data-permission-id="camera" class="blocked-permission-icon camera-icon" role="button"
                          tooltiptext="&urlbar.cameraBlocked.tooltip;"/>
                   <image data-permission-id="indexedDB" class="blocked-permission-icon indexedDB-icon" role="button"
@@ -746,17 +747,16 @@
                          tooltiptext="&urlbar.servicesNotificationAnchor.tooltip;"/>
                   <image id="translate-notification-icon" class="notification-anchor-icon translation-icon" role="button"
                          tooltiptext="&urlbar.translateNotificationAnchor.tooltip;"/>
                   <image id="translated-notification-icon" class="notification-anchor-icon translation-icon in-use" role="button"
                          tooltiptext="&urlbar.translatedNotificationAnchor.tooltip;"/>
                   <image id="eme-notification-icon" class="notification-anchor-icon drm-icon" role="button"
                          tooltiptext="&urlbar.emeNotificationAnchor.tooltip;"/>
                 </box>
-                <image id="tracking-protection-icon"/>
                 <image id="connection-icon"/>
                 <hbox id="identity-icon-labels">
                   <label id="identity-icon-label" class="plain" flex="1"/>
                   <label id="identity-icon-country-label" class="plain"/>
                 </hbox>
               </box>
               <box id="urlbar-display-box" align="center">
                 <label id="switchtab" class="urlbar-display urlbar-display-switchtab" value="&urlbar.switchToTab.label;"/>
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_screen.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_screen.js
@@ -316,17 +316,17 @@ var gTests = [
     yield checkSharingUI({audio: true, screen: "Screen"});
     yield closeStream();
   }
 },
 
 
 {
   desc: "getUserMedia screen: clicking through without selecting a screen denies",
-  run: function* checkReloading() {
+  run: function* checkClickThroughDenies() {
     let promise = promisePopupNotificationShown("webRTC-shareDevices");
     yield promiseRequestDevice(false, true, null, "screen");
     yield promise;
     yield expectObserverCalled("getUserMedia:request");
     checkDeviceSelectors(false, false, true);
 
     yield promiseMessage(permissionError, () => {
       PopupNotifications.panel.firstChild.button.click();
--- a/browser/base/content/test/webrtc/head.js
+++ b/browser/base/content/test/webrtc/head.js
@@ -402,22 +402,24 @@ function* closeStream(aAlreadyClosed, aF
   if (promises)
     yield Promise.all(promises);
 
   yield* assertWebRTCIndicatorStatus(null);
 }
 
 function* reloadAndAssertClosedStreams() {
   info("reloading the web page");
-  let promise = promiseObserverCalled("recording-device-events");
+  let promises = [
+    promiseObserverCalled("recording-device-events"),
+    promiseObserverCalled("recording-window-ended")
+  ];
   yield ContentTask.spawn(gBrowser.selectedBrowser, null,
                           "() => content.location.reload()");
-  yield promise;
+  yield Promise.all(promises);
 
-  yield expectObserverCalled("recording-window-ended");
   yield expectNoObserverCalled();
   yield checkNotSharing();
 }
 
 function checkDeviceSelectors(aAudio, aVideo, aScreen) {
   let micSelector = document.getElementById("webRTC-selectMicrophone");
   if (aAudio)
     ok(!micSelector.hidden, "microphone selector visible");
--- a/browser/modules/E10SUtils.jsm
+++ b/browser/modules/E10SUtils.jsm
@@ -8,18 +8,22 @@ this.EXPORTED_SYMBOLS = ["E10SUtils"];
 
 const {interfaces: Ci, utils: Cu, classes: Cc} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "useRemoteWebExtensions",
                                       "extensions.webextensions.remote", false);
+XPCOMUtils.defineLazyPreferenceGetter(this, "useSeparateFileUriProcess",
+                                      "browser.tabs.remote.separateFileUriProcess", false);
 XPCOMUtils.defineLazyModuleGetter(this, "Utils",
                                   "resource://gre/modules/sessionstore/Utils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "console",
+                                  "resource://gre/modules/Console.jsm");
 
 function getAboutModule(aURL) {
   // Needs to match NS_GetAboutModuleName
   let moduleName = aURL.path.replace(/[#?].*/, "").toLowerCase();
   let contract = "@mozilla.org/network/protocol/about;1?what=" + moduleName;
   try {
     return Cc[contract].getService(Ci.nsIAboutModule);
   } catch (e) {
@@ -51,112 +55,125 @@ this.E10SUtils = {
 
   canLoadURIInProcess(aURL, aProcess) {
     let remoteType = aProcess == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT
                      ? DEFAULT_REMOTE_TYPE : NOT_REMOTE;
     return remoteType == this.getRemoteTypeForURI(aURL, true, remoteType);
   },
 
   getRemoteTypeForURI(aURL, aMultiProcess,
-                                aPreferredRemoteType = DEFAULT_REMOTE_TYPE) {
+                      aPreferredRemoteType = DEFAULT_REMOTE_TYPE) {
     if (!aMultiProcess) {
       return NOT_REMOTE;
     }
 
     // loadURI in browser.xml treats null as about:blank
     if (!aURL) {
       aURL = "about:blank";
     }
 
-    // Javascript urls can load in any process, they apply to the current document
-    if (aURL.startsWith("javascript:")) {
-      return aPreferredRemoteType;
-    }
-
-    // We need data: URI's to load in a remote process, because some of our
-    // tests rely on this. For blob: URI's, load them in their originating
-    // process unless it is non-remote. In that case, favor a remote (sandboxed)
-    // process with fewer privileges to limit exposure.
-    if (aURL.startsWith("data:") || aURL.startsWith("blob:")) {
-      return aPreferredRemoteType == NOT_REMOTE ? DEFAULT_REMOTE_TYPE
-                                                : aPreferredRemoteType;
-    }
-
-    if (aURL.startsWith("file:")) {
-      return Services.prefs.getBoolPref("browser.tabs.remote.separateFileUriProcess")
-             ? FILE_REMOTE_TYPE : DEFAULT_REMOTE_TYPE;
+    let uri;
+    try {
+      uri = Services.io.newURI(aURL);
+    } catch (e) {
+      // If we have an invalid URI, it's still possible that it might get
+      // fixed-up into a valid URI later on. However, we don't want to return
+      // aPreferredRemoteType here, in case the URI gets fixed-up into
+      // something that wouldn't normally run in that process.
+      return DEFAULT_REMOTE_TYPE;
     }
 
-    if (aURL.startsWith("about:")) {
-      // We need to special case about:blank because it needs to load in any.
-      if (aURL == "about:blank") {
-        return aPreferredRemoteType;
-      }
+    return this.getRemoteTypeForURIObject(uri, aMultiProcess,
+                                          aPreferredRemoteType);
+  },
 
-      let url = Services.io.newURI(aURL);
-      let module = getAboutModule(url);
-      // If the module doesn't exist then an error page will be loading, that
-      // should be ok to load in any process
-      if (!module) {
-        return aPreferredRemoteType;
-      }
-
-      let flags = module.getURIFlags(url);
-      if (flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD) {
-        return DEFAULT_REMOTE_TYPE;
-      }
-
-      // If the about page can load in parent or child, it should be safe to
-      // load in any remote type.
-      if (flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD) {
-        return aPreferredRemoteType;
-      }
-
+  getRemoteTypeForURIObject(aURI, aMultiProcess,
+                            aPreferredRemoteType = DEFAULT_REMOTE_TYPE) {
+    if (!aMultiProcess) {
       return NOT_REMOTE;
     }
 
-    if (aURL.startsWith("chrome:")) {
-      let url;
-      try {
-        // This can fail for invalid Chrome URIs, in which case we will end up
-        // not loading anything anyway.
-        url = Services.io.newURI(aURL);
-      } catch (ex) {
+    switch (aURI.scheme) {
+      case "javascript":
+        // javascript URIs can load in any, they apply to the current document.
         return aPreferredRemoteType;
-      }
+
+      case "data":
+      case "blob":
+        // We need data: and blob: URIs to load in any remote process, because
+        // they need to be able to load in whatever is the current process
+        // unless it is non-remote. In that case we don't want to load them in
+        // the parent process, so we load them in the default remote process,
+        // which is sandboxed and limits any risk.
+        return aPreferredRemoteType == NOT_REMOTE ? DEFAULT_REMOTE_TYPE
+                                                  : aPreferredRemoteType;
 
-      let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
-                      getService(Ci.nsIXULChromeRegistry);
-      if (chromeReg.mustLoadURLRemotely(url)) {
-        return DEFAULT_REMOTE_TYPE;
-      }
+      case "file":
+        return useSeparateFileUriProcess ? FILE_REMOTE_TYPE
+                                         : DEFAULT_REMOTE_TYPE;
+
+      case "about":
+        let module = getAboutModule(aURI);
+        // If the module doesn't exist then an error page will be loading, that
+        // should be ok to load in any process
+        if (!module) {
+          return aPreferredRemoteType;
+        }
+
+        let flags = module.getURIFlags(aURI);
+        if (flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD) {
+          return DEFAULT_REMOTE_TYPE;
+        }
 
-      if (chromeReg.canLoadURLRemotely(url) &&
-          aPreferredRemoteType != NOT_REMOTE) {
-        return DEFAULT_REMOTE_TYPE;
-      }
+        // If the about page can load in parent or child, it should be safe to
+        // load in any remote type.
+        if (flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD) {
+          return aPreferredRemoteType;
+        }
+
+        return NOT_REMOTE;
 
-      return NOT_REMOTE;
-    }
+      case "chrome":
+        let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
+                        getService(Ci.nsIXULChromeRegistry);
+        if (chromeReg.mustLoadURLRemotely(aURI)) {
+          return DEFAULT_REMOTE_TYPE;
+        }
+
+        if (chromeReg.canLoadURLRemotely(aURI) &&
+            aPreferredRemoteType != NOT_REMOTE) {
+          return DEFAULT_REMOTE_TYPE;
+        }
 
-    if (aURL.startsWith("moz-extension:")) {
-      return useRemoteWebExtensions ? EXTENSION_REMOTE_TYPE : NOT_REMOTE;
-    }
+        return NOT_REMOTE;
+
+      case "moz-extension":
+        return useRemoteWebExtensions ? EXTENSION_REMOTE_TYPE : NOT_REMOTE;
 
-    if (aURL.startsWith("view-source:")) {
-      return this.getRemoteTypeForURI(aURL.substr("view-source:".length),
-                                      aMultiProcess, aPreferredRemoteType);
+      default:
+        // For any other nested URIs, we use the innerURI to determine the
+        // remote type. In theory we should use the innermost URI, but some URIs
+        // have fake inner URIs (e.g. about URIs with inner moz-safe-about) and
+        // if such URIs are wrapped in other nested schemes like view-source:,
+        // we don't want to "skip" past "about:" by going straight to the
+        // innermost URI. Any URIs like this will need to be handled in the
+        // cases above, so we don't still end up using the fake inner URI here.
+        if (aURI instanceof Ci.nsINestedURI) {
+          let innerURI = aURI.QueryInterface(Ci.nsINestedURI).innerURI;
+          return this.getRemoteTypeForURIObject(innerURI, aMultiProcess,
+                                                aPreferredRemoteType);
+        }
+
+        return validatedWebRemoteType(aPreferredRemoteType);
     }
-
-    return validatedWebRemoteType(aPreferredRemoteType);
   },
 
   shouldLoadURIInThisProcess(aURI) {
     let remoteType = Services.appinfo.remoteType;
-    return remoteType == this.getRemoteTypeForURI(aURI.spec, true, remoteType);
+    return remoteType == this.getRemoteTypeForURIObject(aURI, true, remoteType);
   },
 
   shouldLoadURI(aDocShell, aURI, aReferrer) {
     // Inner frames should always load in the current process
     if (aDocShell.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeParent)
       return true;
 
     // If we are in a Large-Allocation process, and it wouldn't be content visible
new file mode 100644
--- /dev/null
+++ b/browser/modules/test/xpcshell/test_E10SUtils_nested_URIs.js
@@ -0,0 +1,94 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+
+const {utils: Cu, interfaces: Ci} = Components;
+
+Cu.import("resource:///modules/E10SUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+var TEST_PREFERRED_REMOTE_TYPES = [
+  E10SUtils.WEB_REMOTE_TYPE,
+  E10SUtils.NOT_REMOTE,
+  "fakeRemoteType",
+]
+
+// These test cases give a nestedURL and a plainURL that should always load in
+// the same remote type. By making these tests comparisons, they should work
+// with any pref combination.
+var TEST_CASES = [
+  {
+    nestedURL: "jar:file:///some.file!/",
+    plainURL: "file:///some.file",
+  },
+  {
+    nestedURL: "jar:jar:file:///some.file!/!/",
+    plainURL: "file:///some.file",
+  },
+  {
+    nestedURL: "jar:http://some.site/file!/",
+    plainURL: "http://some.site/file",
+  },
+  {
+    nestedURL: "feed:http://some.site",
+    plainURL: "http://some.site",
+  },
+  {
+    nestedURL: "pcast:http://some.site",
+    plainURL: "http://some.site",
+  },
+  {
+    nestedURL: "view-source:http://some.site",
+    plainURL: "http://some.site",
+  },
+  {
+    nestedURL: "view-source:file:///some.file",
+    plainURL: "file:///some.file",
+  },
+  {
+    nestedURL: "view-source:about:home",
+    plainURL: "about:home",
+  },
+  {
+    nestedURL: "view-source:about:robots",
+    plainURL: "about:robots",
+  },
+  {
+    nestedURL: "view-source:feed:http://some.site",
+    plainURL: "http://some.site",
+  },
+  {
+    nestedURL: "view-source:pcast:http://some.site",
+    plainURL: "http://some.site",
+  },
+]
+
+function run_test() {
+  for (let testCase of TEST_CASES) {
+    for (let preferredRemoteType of TEST_PREFERRED_REMOTE_TYPES) {
+      let plainUri = Services.io.newURI(testCase.plainURL);
+      let plainRemoteType =
+        E10SUtils.getRemoteTypeForURIObject(plainUri, true, preferredRemoteType);
+
+      let nestedUri = Services.io.newURI(testCase.nestedURL);
+      let nestedRemoteType =
+        E10SUtils.getRemoteTypeForURIObject(nestedUri, true, preferredRemoteType);
+
+      let nestedStr = nestedUri.scheme + ":";
+      do {
+        nestedUri = nestedUri.QueryInterface(Ci.nsINestedURI).innerURI;
+        if (nestedUri.scheme == "about") {
+          nestedStr += nestedUri.spec;
+          break;
+        }
+
+        nestedStr += nestedUri.scheme + ":";
+      } while (nestedUri instanceof Ci.nsINestedURI);
+
+      let plainStr = plainUri.scheme == "about" ? plainUri.spec
+                                                : plainUri.scheme + ":";
+      equal(nestedRemoteType, plainRemoteType,
+            `Check that ${nestedStr} loads in same remote type as ${plainStr}`
+            + ` with preferred remote type: ${preferredRemoteType}`);
+    }
+  }
+}
--- a/browser/modules/test/xpcshell/xpcshell.ini
+++ b/browser/modules/test/xpcshell/xpcshell.ini
@@ -1,10 +1,11 @@
 [DEFAULT]
 head =
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 [test_AttributionCode.js]
 skip-if = os != 'win'
 [test_DirectoryLinksProvider.js]
+[test_E10SUtils_nested_URIs.js]
 [test_SitePermissions.js]
 [test_LaterRun.js]
--- a/config/system-headers
+++ b/config/system-headers
@@ -484,16 +484,17 @@ ft2build.h
 fts.h
 gconf/gconf-client.h
 Gdiplus.h
 gdk/gdk.h
 gdk/gdkkeysyms.h
 gdk/gdkprivate.h
 gdk/gdkx.h
 gdk/gdkdirectfb.h
+gdk/gdkwayland.h
 gdk-pixbuf/gdk-pixbuf.h
 Gestalt.h
 getopt.h
 glibconfig.h
 glib.h
 glib-object.h
 gmodule.h
 gnome.h
--- a/devtools/shared/transport/tests/unit/head_dbg.js
+++ b/devtools/shared/transport/tests/unit/head_dbg.js
@@ -1,262 +1,157 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+/* exported Cr, CC, NetUtil, defer, errorCount, initTestDebuggerServer,
+            writeTestTempFile, socket_transport, local_transport, really_long
+*/
+
 var Cc = Components.classes;
 var Ci = Components.interfaces;
 var Cu = Components.utils;
 var Cr = Components.results;
 var CC = Components.Constructor;
 
 const { require } =
   Cu.import("resource://devtools/shared/Loader.jsm", {});
 const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const { Task } = require("devtools/shared/task");
 
 const Services = require("Services");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 
 // We do not want to log packets by default, because in some tests,
 // we can be sending large amounts of data. The test harness has
 // trouble dealing with logging all the data, and we end up with
 // intermittent time outs (e.g. bug 775924).
 // Services.prefs.setBoolPref("devtools.debugger.log", true);
 // Services.prefs.setBoolPref("devtools.debugger.log.verbose", true);
 // Enable remote debugging for the relevant tests.
 Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true);
 
 const { DebuggerServer } = require("devtools/server/main");
 const { DebuggerClient } = require("devtools/shared/client/main");
 
-function testExceptionHook(ex) {
-  try {
-    do_report_unexpected_exception(ex);
-  } catch (ex) {
-    return {throw: ex};
+// Convert an nsIScriptError 'flags' value into an appropriate string.
+function scriptErrorFlagsToKind(flags) {
+  let kind;
+  if (flags & Ci.nsIScriptError.warningFlag) {
+    kind = "warning";
   }
-  return undefined;
-}
+  if (flags & Ci.nsIScriptError.exceptionFlag) {
+    kind = "exception";
+  } else {
+    kind = "error";
+  }
 
-// Convert an nsIScriptError 'aFlags' value into an appropriate string.
-function scriptErrorFlagsToKind(aFlags) {
-  var kind;
-  if (aFlags & Ci.nsIScriptError.warningFlag)
-    kind = "warning";
-  if (aFlags & Ci.nsIScriptError.exceptionFlag)
-    kind = "exception";
-  else
-    kind = "error";
-
-  if (aFlags & Ci.nsIScriptError.strictFlag)
+  if (flags & Ci.nsIScriptError.strictFlag) {
     kind = "strict " + kind;
+  }
 
   return kind;
 }
 
 // Register a console listener, so console messages don't just disappear
 // into the ether.
 var errorCount = 0;
 var listener = {
-  observe: function (aMessage) {
+  observe: function (message) {
     errorCount++;
+    let string = "";
     try {
       // If we've been given an nsIScriptError, then we can print out
       // something nicely formatted, for tools like Emacs to pick up.
-      var scriptError = aMessage.QueryInterface(Ci.nsIScriptError);
-      dump(aMessage.sourceName + ":" + aMessage.lineNumber + ": " +
-           scriptErrorFlagsToKind(aMessage.flags) + ": " +
-           aMessage.errorMessage + "\n");
-      var string = aMessage.errorMessage;
+      message.QueryInterface(Ci.nsIScriptError);
+      dump(message.sourceName + ":" + message.lineNumber + ": " +
+           scriptErrorFlagsToKind(message.flags) + ": " +
+           message.errorMessage + "\n");
+      string = message.errorMessage;
     } catch (x) {
       // Be a little paranoid with message, as the whole goal here is to lose
       // no information.
       try {
-        var string = "" + aMessage.message;
-      } catch (x) {
-        var string = "<error converting error message to string>";
+        string = message.message;
+      } catch (e) {
+        string = "<error converting error message to string>";
       }
     }
 
     // Make sure we exit all nested event loops so that the test can finish.
     while (DebuggerServer.xpcInspector.eventLoopNestLevel > 0) {
       DebuggerServer.xpcInspector.exitNestedEventLoop();
     }
 
     // Throw in most cases, but ignore the "strict" messages
-    if (!(aMessage.flags & Ci.nsIScriptError.strictFlag)) {
+    if (!(message.flags & Ci.nsIScriptError.strictFlag)) {
       do_throw("head_dbg.js got console message: " + string + "\n");
     }
   }
 };
 
 var consoleService = Cc["@mozilla.org/consoleservice;1"]
                      .getService(Ci.nsIConsoleService);
 consoleService.registerListener(listener);
 
-function check_except(func) {
-  try {
-    func();
-  } catch (e) {
-    do_check_true(true);
-    return;
-  }
-  dump("Should have thrown an exception: " + func.toString());
-  do_check_true(false);
-}
-
-function testGlobal(aName) {
-  let systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
-    .createInstance(Ci.nsIPrincipal);
-
-  let sandbox = Cu.Sandbox(systemPrincipal);
-  sandbox.__name = aName;
-  return sandbox;
-}
-
-function addTestGlobal(aName)
-{
-  let global = testGlobal(aName);
-  DebuggerServer.addTestGlobal(global);
-  return global;
-}
-
-// List the DebuggerClient |aClient|'s tabs, look for one whose title is
-// |aTitle|, and apply |aCallback| to the packet's entry for that tab.
-function getTestTab(aClient, aTitle, aCallback) {
-  aClient.listTabs(function (aResponse) {
-    for (let tab of aResponse.tabs) {
-      if (tab.title === aTitle) {
-        aCallback(tab);
-        return;
-      }
-    }
-    aCallback(null);
-  });
-}
-
-// Attach to |aClient|'s tab whose title is |aTitle|; pass |aCallback| the
-// response packet and a TabClient instance referring to that tab.
-function attachTestTab(aClient, aTitle, aCallback) {
-  getTestTab(aClient, aTitle, function (aTab) {
-    aClient.attachTab(aTab.actor, aCallback);
-  });
-}
-
-// Attach to |aClient|'s tab whose title is |aTitle|, and then attach to
-// that tab's thread. Pass |aCallback| the thread attach response packet, a
-// TabClient referring to the tab, and a ThreadClient referring to the
-// thread.
-function attachTestThread(aClient, aTitle, aCallback) {
-  attachTestTab(aClient, aTitle, function (aResponse, aTabClient) {
-    function onAttach(aResponse, aThreadClient) {
-      aCallback(aResponse, aTabClient, aThreadClient);
-    }
-    aTabClient.attachThread({ useSourceMaps: true }, onAttach);
-  });
-}
-
-// Attach to |aClient|'s tab whose title is |aTitle|, attach to the tab's
-// thread, and then resume it. Pass |aCallback| the thread's response to
-// the 'resume' packet, a TabClient for the tab, and a ThreadClient for the
-// thread.
-function attachTestTabAndResume(aClient, aTitle, aCallback) {
-  attachTestThread(aClient, aTitle, function (aResponse, aTabClient, aThreadClient) {
-    aThreadClient.resume(function (aResponse) {
-      aCallback(aResponse, aTabClient, aThreadClient);
-    });
-  });
-}
-
 /**
  * Initialize the testing debugger server.
  */
 function initTestDebuggerServer() {
   DebuggerServer.registerModule("devtools/server/actors/script", {
     prefix: "script",
     constructor: "ScriptActor",
     type: { global: true, tab: true }
   });
   DebuggerServer.registerModule("xpcshell-test/testactors");
   // Allow incoming connections.
   DebuggerServer.init();
 }
 
-function finishClient(aClient) {
-  aClient.close().then(function () {
-    do_test_finished();
-  });
-}
-
-/**
- * Takes a relative file path and returns the absolute file url for it.
- */
-function getFileUrl(aName, aAllowMissing = false) {
-  let file = do_get_file(aName, aAllowMissing);
-  return Services.io.newFileURI(file).spec;
-}
-
-/**
- * Returns the full path of the file with the specified name in a
- * platform-independent and URL-like form.
- */
-function getFilePath(aName, aAllowMissing = false) {
-  let file = do_get_file(aName, aAllowMissing);
-  let path = Services.io.newFileURI(file).spec;
-  let filePrePath = "file://";
-  if ("nsILocalFileWin" in Ci &&
-      file instanceof Ci.nsILocalFileWin) {
-    filePrePath += "/";
-  }
-  return path.slice(filePrePath.length);
-}
-
 /**
  * Wrapper around do_get_file to prefix files with the name of current test to
  * avoid collisions when running in parallel.
  */
 function getTestTempFile(fileName, allowMissing) {
   let thisTest = _TEST_FILE.toString().replace(/\\/g, "/");
   thisTest = thisTest.substring(thisTest.lastIndexOf("/") + 1);
   thisTest = thisTest.replace(/\..*$/, "");
   return do_get_file(fileName + "-" + thisTest, allowMissing);
 }
 
-function writeTestTempFile(aFileName, aContent) {
-  let file = getTestTempFile(aFileName, true);
+function writeTestTempFile(fileName, content) {
+  let file = getTestTempFile(fileName, true);
   let stream = Cc["@mozilla.org/network/file-output-stream;1"]
     .createInstance(Ci.nsIFileOutputStream);
   stream.init(file, -1, -1, 0);
   try {
     do {
-      let numWritten = stream.write(aContent, aContent.length);
-      aContent = aContent.slice(numWritten);
-    } while (aContent.length > 0);
+      let numWritten = stream.write(content, content.length);
+      content = content.slice(numWritten);
+    } while (content.length > 0);
   } finally {
     stream.close();
   }
 }
 
 /** * Transport Factories ***/
 
 var socket_transport = Task.async(function* () {
   if (!DebuggerServer.listeningSockets) {
     let AuthenticatorType = DebuggerServer.Authenticators.get("PROMPT");
     let authenticator = new AuthenticatorType.Server();
     authenticator.allowConnection = () => {
       return DebuggerServer.AuthenticationResult.ALLOW;
     };
-    let listener = DebuggerServer.createListener();
-    listener.portOrPath = -1;
-    listener.authenticator = authenticator;
-    yield listener.open();
+    let debuggerListener = DebuggerServer.createListener();
+    debuggerListener.portOrPath = -1;
+    debuggerListener.authenticator = authenticator;
+    yield debuggerListener.open();
   }
   let port = DebuggerServer._listeners[0].port;
   do_print("Debugger server port is " + port);
   return DebuggerClient.socketConnect({ host: "127.0.0.1", port });
 });
 
 function local_transport() {
   return promise.resolve(DebuggerServer.connectPipe());
--- a/devtools/shared/transport/tests/unit/test_bulk_error.js
+++ b/devtools/shared/transport/tests/unit/test_bulk_error.js
@@ -68,18 +68,18 @@ function json_reply(client, response) {
     actor: response.testBulk,
     type: "jsonReply",
     length: reallyLong.length
   });
 
   // Send bulk data to server
   let copyDeferred = defer();
   request.on("bulk-send-ready", ({writer, done}) => {
-    let input = Cc["@mozilla.org/io/string-input-stream;1"].
-                  createInstance(Ci.nsIStringInputStream);
+    let input = Cc["@mozilla.org/io/string-input-stream;1"]
+                  .createInstance(Ci.nsIStringInputStream);
     input.setData(reallyLong, reallyLong.length);
     try {
       writer.copyFrom(input, () => {
         input.close();
         done();
       });
       do_throw(new Error("Copying should fail, the stream is not async."));
     } catch (e) {
--- a/devtools/shared/transport/tests/unit/test_client_server_bulk.js
+++ b/devtools/shared/transport/tests/unit/test_client_server_bulk.js
@@ -145,35 +145,37 @@ var test_bulk_request_cs = Task.async(fu
   let transport = yield transportFactory();
 
   let client = new DebuggerClient(transport);
   client.connect().then(([app, traits]) => {
     do_check_eq(traits.bulk, true);
     client.listTabs(clientDeferred.resolve);
   });
 
+  function bulkSendReadyCallback({copyFrom}) {
+    NetUtil.asyncFetch({
+      uri: NetUtil.newURI(getTestTempFile("bulk-input")),
+      loadUsingSystemPrincipal: true
+    }, input => {
+      copyFrom(input).then(() => {
+        input.close();
+        bulkCopyDeferred.resolve();
+      });
+    });
+  }
+
   clientDeferred.promise.then(response => {
     let request = client.startBulkRequest({
       actor: response.testBulk,
       type: actorType,
       length: really_long().length
     });
 
     // Send bulk data to server
-    request.on("bulk-send-ready", ({copyFrom}) => {
-      NetUtil.asyncFetch({
-        uri: NetUtil.newURI(getTestTempFile("bulk-input")),
-        loadUsingSystemPrincipal: true
-      }, input => {
-        copyFrom(input).then(() => {
-          input.close();
-          bulkCopyDeferred.resolve();
-        });
-      });
-    });
+    request.on("bulk-send-ready", bulkSendReadyCallback);
 
     // Set up reply handling for this type
     replyHandlers[replyType](request).then(() => {
       client.close();
       transport.close();
     });
   }).then(null, do_throw);
 
--- a/devtools/shared/transport/tests/unit/test_dbgsocket.js
+++ b/devtools/shared/transport/tests/unit/test_dbgsocket.js
@@ -1,28 +1,27 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
 
 var gPort;
 var gExtraListener;
 
-function run_test()
-{
+function run_test() {
   do_print("Starting test at " + new Date().toTimeString());
   initTestDebuggerServer();
 
   add_task(test_socket_conn);
   add_task(test_socket_shutdown);
   add_test(test_pipe_conn);
 
   run_next_test();
 }
 
-function* test_socket_conn()
-{
+function* test_socket_conn() {
   do_check_eq(DebuggerServer.listeningSockets, 0);
   let AuthenticatorType = DebuggerServer.Authenticators.get("PROMPT");
   let authenticator = new AuthenticatorType.Server();
   authenticator.allowConnection = () => {
     return DebuggerServer.AuthenticationResult.ALLOW;
   };
   let listener = DebuggerServer.createListener();
   do_check_true(listener);
@@ -48,77 +47,74 @@ function* test_socket_conn()
 
   // Assert that connection settings are available on transport object
   let settings = transport.connectionSettings;
   do_check_eq(settings.host, "127.0.0.1");
   do_check_eq(settings.port, gPort);
 
   let closedDeferred = defer();
   transport.hooks = {
-    onPacket: function (aPacket) {
-      this.onPacket = function (aPacket) {
-        do_check_eq(aPacket.unicode, unicodeString);
+    onPacket: function (packet) {
+      this.onPacket = function ({unicode}) {
+        do_check_eq(unicode, unicodeString);
         transport.close();
       };
       // Verify that things work correctly when bigger than the output
       // transport buffers and when transporting unicode...
       transport.send({to: "root",
                       type: "echo",
                       reallylong: really_long(),
                       unicode: unicodeString});
-      do_check_eq(aPacket.from, "root");
+      do_check_eq(packet.from, "root");
     },
-    onClosed: function (aStatus) {
+    onClosed: function (status) {
       closedDeferred.resolve();
     },
   };
   transport.ready();
   return closedDeferred.promise;
 }
 
-function* test_socket_shutdown()
-{
+function* test_socket_shutdown() {
   do_check_eq(DebuggerServer.listeningSockets, 2);
   gExtraListener.close();
   do_check_eq(DebuggerServer.listeningSockets, 1);
   do_check_true(DebuggerServer.closeAllListeners());
   do_check_eq(DebuggerServer.listeningSockets, 0);
   // Make sure closing the listener twice does nothing.
   do_check_false(DebuggerServer.closeAllListeners());
   do_check_eq(DebuggerServer.listeningSockets, 0);
 
   do_print("Connecting to a server socket at " + new Date().toTimeString());
   try {
-    let transport = yield DebuggerClient.socketConnect({
+    yield DebuggerClient.socketConnect({
       host: "127.0.0.1",
       port: gPort
     });
   } catch (e) {
     if (e.result == Cr.NS_ERROR_CONNECTION_REFUSED ||
         e.result == Cr.NS_ERROR_NET_TIMEOUT) {
       // The connection should be refused here, but on slow or overloaded
       // machines it may just time out.
       do_check_true(true);
       return;
-    } else {
-      throw e;
     }
+    throw e;
   }
 
   // Shouldn't reach this, should never connect.
   do_check_true(false);
 }
 
-function test_pipe_conn()
-{
+function test_pipe_conn() {
   let transport = DebuggerServer.connectPipe();
   transport.hooks = {
-    onPacket: function (aPacket) {
-      do_check_eq(aPacket.from, "root");
+    onPacket: function (packet) {
+      do_check_eq(packet.from, "root");
       transport.close();
     },
-    onClosed: function (aStatus) {
+    onClosed: function (status) {
       run_next_test();
     }
   };
 
   transport.ready();
 }
--- a/devtools/shared/transport/tests/unit/test_dbgsocket_connection_drop.js
+++ b/devtools/shared/transport/tests/unit/test_dbgsocket_connection_drop.js
@@ -2,16 +2,17 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 /**
  * Bug 755412 - checks if the server drops the connection on an improperly
  * framed packet, i.e. when the length header is invalid.
  */
+"use strict";
 
 const { RawPacket } = require("devtools/shared/transport/packets");
 
 function run_test() {
   do_print("Starting test at " + new Date().toTimeString());
   initTestDebuggerServer();
 
   add_task(test_socket_conn_drops_after_invalid_header);
@@ -56,26 +57,26 @@ var test_helper = Task.async(function* (
   listener.open();
 
   let transport = yield DebuggerClient.socketConnect({
     host: "127.0.0.1",
     port: listener.port
   });
   let closedDeferred = defer();
   transport.hooks = {
-    onPacket: function (aPacket) {
-      this.onPacket = function (aPacket) {
+    onPacket: function (packet) {
+      this.onPacket = function () {
         do_throw(new Error("This connection should be dropped."));
         transport.close();
       };
 
       // Inject the payload directly into the stream.
       transport._outgoing.push(new RawPacket(transport, payload));
       transport._flushOutgoing();
     },
-    onClosed: function (aStatus) {
+    onClosed: function (status) {
       do_check_true(true);
       closedDeferred.resolve();
     },
   };
   transport.ready();
   return closedDeferred.promise;
 });
--- a/devtools/shared/transport/tests/unit/test_delimited_read.js
+++ b/devtools/shared/transport/tests/unit/test_delimited_read.js
@@ -1,10 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
 
 const StreamUtils = require("devtools/shared/transport/stream-utils");
 
 const StringInputStream = CC("@mozilla.org/io/string-input-stream;1",
                              "nsIStringInputStream", "setData");
 
 function run_test() {
   add_task(function* () {
--- a/devtools/shared/transport/tests/unit/test_no_bulk.js
+++ b/devtools/shared/transport/tests/unit/test_no_bulk.js
@@ -15,24 +15,22 @@ function run_test() {
   });
 
   run_next_test();
 }
 
 /** * Tests ***/
 
 var test_bulk_send_error = Task.async(function* (transportFactory) {
-  let deferred = defer();
   let transport = yield transportFactory();
 
   let client = new DebuggerClient(transport);
   return client.connect().then(([app, traits]) => {
     do_check_false(traits.bulk);
 
     try {
       client.startBulkRequest();
       do_throw(new Error("Can't use bulk since server doesn't support it"));
     } catch (e) {
       do_check_true(true);
     }
-
   });
 });
--- a/devtools/shared/transport/tests/unit/test_packet.js
+++ b/devtools/shared/transport/tests/unit/test_packet.js
@@ -1,10 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
 
 const { JSONPacket, BulkPacket } =
   require("devtools/shared/transport/packets");
 
 function run_test() {
   add_test(test_packet_done);
   run_next_test();
 }
--- a/devtools/shared/transport/tests/unit/test_transport_bulk.js
+++ b/devtools/shared/transport/tests/unit/test_transport_bulk.js
@@ -69,19 +69,19 @@ var test_bulk_transfer_transport = Task.
         clientDeferred.resolve();
       };
       transport.close();
     });
   }
 
   // Client
   transport.hooks = {
-    onPacket: function (aPacket) {
+    onPacket: function (packet) {
       // We've received the initial start up packet
-      do_check_eq(aPacket.from, "root");
+      do_check_eq(packet.from, "root");
 
       // Server
       do_check_eq(Object.keys(DebuggerServer._connections).length, 1);
       do_print(Object.keys(DebuggerServer._connections));
       for (let connId in DebuggerServer._connections) {
         DebuggerServer._connections[connId].onBulkPacket = on_bulk_packet;
       }
 
--- a/devtools/shared/transport/tests/unit/testactors-no-bulk.js
+++ b/devtools/shared/transport/tests/unit/testactors-no-bulk.js
@@ -1,19 +1,20 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
 
 const { RootActor } = require("devtools/server/actors/root");
 const { DebuggerServer } = require("devtools/server/main");
 
 /**
  * Root actor that doesn't have the bulk trait.
  */
-function createRootActor(aConnection) {
-  let root = new RootActor(aConnection, {
+function createRootActor(connection) {
+  let root = new RootActor(connection, {
     globalActorFactories: DebuggerServer.globalActorFactories
   });
   root.applicationType = "xpcshell-tests";
   root.traits = {
     bulk: false
   };
   return root;
 }
--- a/devtools/shared/transport/tests/unit/testactors.js
+++ b/devtools/shared/transport/tests/unit/testactors.js
@@ -1,72 +1,73 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
 
 const { ActorPool, appendExtraActors, createExtraActors } =
   require("devtools/server/actors/common");
 const { RootActor } = require("devtools/server/actors/root");
 const { ThreadActor } = require("devtools/server/actors/script");
 const { DebuggerServer } = require("devtools/server/main");
 const promise = require("promise");
 
 var gTestGlobals = [];
-DebuggerServer.addTestGlobal = function (aGlobal) {
-  gTestGlobals.push(aGlobal);
+DebuggerServer.addTestGlobal = function (global) {
+  gTestGlobals.push(global);
 };
 
 // A mock tab list, for use by tests. This simply presents each global in
 // gTestGlobals as a tab, and the list is fixed: it never calls its
 // onListChanged handler.
 //
 // As implemented now, we consult gTestGlobals when we're constructed, not
 // when we're iterated over, so tests have to add their globals before the
 // root actor is created.
-function TestTabList(aConnection) {
-  this.conn = aConnection;
+function TestTabList(connection) {
+  this.conn = connection;
 
   // An array of actors for each global added with
   // DebuggerServer.addTestGlobal.
   this._tabActors = [];
 
   // A pool mapping those actors' names to the actors.
-  this._tabActorPool = new ActorPool(aConnection);
+  this._tabActorPool = new ActorPool(connection);
 
   for (let global of gTestGlobals) {
-    let actor = new TestTabActor(aConnection, global);
+    let actor = new TestTabActor(connection, global);
     actor.selected = false;
     this._tabActors.push(actor);
     this._tabActorPool.addActor(actor);
   }
   if (this._tabActors.length > 0) {
     this._tabActors[0].selected = true;
   }
 
-  aConnection.addActorPool(this._tabActorPool);
+  connection.addActorPool(this._tabActorPool);
 }
 
 TestTabList.prototype = {
   constructor: TestTabList,
   getList: function () {
     return promise.resolve([...this._tabActors]);
   }
 };
 
-function createRootActor(aConnection) {
-  let root = new RootActor(aConnection, {
-    tabList: new TestTabList(aConnection),
+function createRootActor(connection) {
+  let root = new RootActor(connection, {
+    tabList: new TestTabList(connection),
     globalActorFactories: DebuggerServer.globalActorFactories
   });
   root.applicationType = "xpcshell-tests";
   return root;
 }
 
-function TestTabActor(aConnection, aGlobal) {
-  this.conn = aConnection;
-  this._global = aGlobal;
+function TestTabActor(connection, global) {
+  this.conn = connection;
+  this._global = global;
   this._threadActor = new ThreadActor(this, this._global);
   this.conn.addActor(this._threadActor);
   this._attached = false;
   this._extraActors = {};
 }
 
 TestTabActor.prototype = {
   constructor: TestTabActor,
@@ -91,28 +92,28 @@ TestTabActor.prototype = {
       this.conn.addActorPool(this._tabActorPool);
     }
 
     this._appendExtraActors(response);
 
     return response;
   },
 
-  onAttach: function (aRequest) {
+  onAttach: function (request) {
     this._attached = true;
 
     let response = { type: "tabAttached", threadActor: this._threadActor.actorID };
     this._appendExtraActors(response);
 
     return response;
   },
 
-  onDetach: function (aRequest) {
+  onDetach: function (request) {
     if (!this._attached) {
-      return { "error":"wrongState" };
+      return { "error": "wrongState" };
     }
     return { type: "detached" };
   },
 
   /* Support for DebuggerServer.addTabActor. */
   _createExtraActors: createExtraActors,
   _appendExtraActors: appendExtraActors
 };
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -7567,17 +7567,23 @@ nsDocShell::EndPageLoad(nsIWebProgress* 
                         nsIChannel* aChannel, nsresult aStatus)
 {
   if (!aChannel) {
     return NS_ERROR_NULL_POINTER;
   }
 
   nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
   if (reporter) {
-    reporter->FlushConsoleReports(GetDocument());
+    nsCOMPtr<nsILoadGroup> loadGroup;
+    aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
+    if (loadGroup) {
+      reporter->FlushConsoleReports(loadGroup);
+    } else {
+      reporter->FlushConsoleReports(GetDocument());
+    }
   }
 
   nsCOMPtr<nsIURI> url;
   nsresult rv = aChannel->GetURI(getter_AddRefs(url));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -928,28 +928,38 @@ TimeoutManager::Timeouts::ResetTimersFor
 
       // Since we reset When() we need to move |timeout| to the right
       // place in the list so that it remains sorted by When().
 
       // Get the pointer to the next timeout now, before we move the
       // current timeout in the list.
       Timeout* nextTimeout = timeout->getNext();
 
-      // It is safe to remove and re-insert because When() is now
-      // strictly smaller than it used to be, so we know we'll insert
-      // |timeout| before nextTimeout.
-      NS_ASSERTION(!nextTimeout ||
-                   timeout->When() < nextTimeout->When(), "How did that happen?");
-      timeout->remove();
-      // Insert() will addref |timeout| and reset mFiringDepth.  Make sure to
-      // undo that after calling it.
-      uint32_t firingDepth = timeout->mFiringDepth;
-      Insert(timeout, aSortBy);
-      timeout->mFiringDepth = firingDepth;
-      timeout->Release();
+      // Since we are only reducing intervals in this method we can
+      // make an optimization here.  If the reduction does not cause us
+      // to fall before our previous timeout then we do not have to
+      // remove and re-insert the current timeout.  This is important
+      // because re-insertion makes this algorithm O(n^2).  Since we
+      // will typically be shifting a lot of timers at once this
+      // optimization saves us a lot of work.
+      Timeout* prevTimeout = timeout->getPrevious();
+      if (prevTimeout && prevTimeout->When() > timeout->When()) {
+        // It is safe to remove and re-insert because When() is now
+        // strictly smaller than it used to be, so we know we'll insert
+        // |timeout| before nextTimeout.
+        NS_ASSERTION(!nextTimeout ||
+                     timeout->When() < nextTimeout->When(), "How did that happen?");
+        timeout->remove();
+        // Insert() will addref |timeout| and reset mFiringDepth.  Make sure to
+        // undo that after calling it.
+        uint32_t firingDepth = timeout->mFiringDepth;
+        Insert(timeout, aSortBy);
+        timeout->mFiringDepth = firingDepth;
+        timeout->Release();
+      }
 
       nsresult rv = timeout->InitTimer(aQueue, delay.ToMilliseconds());
 
       if (NS_FAILED(rv)) {
         NS_WARNING("Error resetting non background timer for DOM timeout!");
         return rv;
       }
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -1792,18 +1792,17 @@ nsContentUtils::ParseLegacyFontSize(cons
 
   return clamped(value, 1, 7);
 }
 
 /* static */
 bool
 nsContentUtils::IsControlledByServiceWorker(nsIDocument* aDocument)
 {
-  if (aDocument &&
-      aDocument->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId) {
+  if (nsContentUtils::IsInPrivateBrowsing(aDocument)) {
     return false;
   }
 
   RefPtr<workers::ServiceWorkerManager> swm =
     workers::ServiceWorkerManager::GetInstance();
   MOZ_ASSERT(swm);
 
   ErrorResult rv;
@@ -3197,16 +3196,50 @@ nsContentUtils::GetOriginAttributes(nsIL
     nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
     if (loadContext && loadContext->GetOriginAttributes(attrs)) {
       attrs.StripAttributes(OriginAttributes::STRIP_ADDON_ID);
     }
   }
   return attrs;
 }
 
+// static
+bool
+nsContentUtils::IsInPrivateBrowsing(nsIDocument* aDoc)
+{
+  if (!aDoc) {
+    return false;
+  }
+
+  nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
+  if (loadGroup) {
+    return IsInPrivateBrowsing(loadGroup);
+  }
+
+  nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
+  return channel && NS_UsePrivateBrowsing(channel);
+}
+
+// static
+bool
+nsContentUtils::IsInPrivateBrowsing(nsILoadGroup* aLoadGroup)
+{
+  if (!aLoadGroup) {
+    return false;
+  }
+  bool isPrivate = false;
+  nsCOMPtr<nsIInterfaceRequestor> callbacks;
+  aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
+  if (callbacks) {
+    nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
+    isPrivate = loadContext && loadContext->UsePrivateBrowsing();
+  }
+  return isPrivate;
+}
+
 bool
 nsContentUtils::DocumentInactiveForImageLoads(nsIDocument* aDocument)
 {
   if (aDocument && !IsChromeDoc(aDocument) && !aDocument->IsResourceDoc()) {
     nsCOMPtr<nsPIDOMWindowInner> win =
       do_QueryInterface(aDocument->GetScopeObject());
     return !win || !win->GetDocShell();
   }
@@ -3216,36 +3249,19 @@ nsContentUtils::DocumentInactiveForImage
 imgLoader*
 nsContentUtils::GetImgLoaderForDocument(nsIDocument* aDoc)
 {
   NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aDoc), nullptr);
 
   if (!aDoc) {
     return imgLoader::NormalLoader();
   }
-
-  nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
-  if (loadGroup) {
-    nsCOMPtr<nsIInterfaceRequestor> callbacks;
-    loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
-    if (callbacks) {
-      nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
-      if (loadContext && loadContext->UsePrivateBrowsing()) {
-        return imgLoader::PrivateBrowsingLoader();
-      }
-    }
-    return imgLoader::NormalLoader();
-  }
-
-  nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
-  if (channel && NS_UsePrivateBrowsing(channel)) {
-    return imgLoader::PrivateBrowsingLoader();
-  }
-
-  return imgLoader::NormalLoader();
+  bool isPrivate = IsInPrivateBrowsing(aDoc);
+  return isPrivate ? imgLoader::PrivateBrowsingLoader()
+                   : imgLoader::NormalLoader();
 }
 
 // static
 imgLoader*
 nsContentUtils::GetImgLoaderForChannel(nsIChannel* aChannel,
                                        nsIDocument* aContext)
 {
   NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aContext), nullptr);
@@ -3667,16 +3683,32 @@ nsContentUtils::ReportToConsoleNonLocali
   uint64_t innerWindowID = 0;
   if (aDocument) {
     if (!aURI) {
       aURI = aDocument->GetDocumentURI();
     }
     innerWindowID = aDocument->InnerWindowID();
   }
 
+  return ReportToConsoleByWindowID(aErrorText, aErrorFlags, aCategory,
+                                   innerWindowID, aURI, aSourceLine,
+                                   aLineNumber, aColumnNumber, aLocationMode);
+}
+
+/* static */ nsresult
+nsContentUtils::ReportToConsoleByWindowID(const nsAString& aErrorText,
+                                          uint32_t aErrorFlags,
+                                          const nsACString& aCategory,
+                                          uint64_t aInnerWindowID,
+                                          nsIURI* aURI,
+                                          const nsAFlatString& aSourceLine,
+                                          uint32_t aLineNumber,
+                                          uint32_t aColumnNumber,
+                                          MissingErrorLocationMode aLocationMode)
+{
   nsresult rv;
   if (!sConsoleService) { // only need to bother null-checking here
     rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsAutoCString spec;
   if (!aLineNumber && aLocationMode == eUSE_CALLING_LOCATION) {
@@ -3693,17 +3725,17 @@ nsContentUtils::ReportToConsoleNonLocali
       do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = errorObject->InitWithWindowID(aErrorText,
                                      NS_ConvertUTF8toUTF16(spec), // file name
                                      aSourceLine,
                                      aLineNumber, aColumnNumber,
                                      aErrorFlags, aCategory,
-                                     innerWindowID);
+                                     aInnerWindowID);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return sConsoleService->LogMessage(errorObject);
 }
 
 void
 nsContentUtils::LogMessageToConsole(const char* aMsg)
 {
@@ -7332,18 +7364,28 @@ nsContentUtils::GetInnerWindowID(nsIRequ
 
   nsCOMPtr<nsILoadGroup> loadGroup;
   nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
 
   if (NS_FAILED(rv) || !loadGroup) {
     return 0;
   }
 
+  return GetInnerWindowID(loadGroup);
+}
+
+uint64_t
+nsContentUtils::GetInnerWindowID(nsILoadGroup* aLoadGroup)
+{
+  if (!aLoadGroup) {
+    return 0;
+  }
+
   nsCOMPtr<nsIInterfaceRequestor> callbacks;
-  rv = loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
+  nsresult rv = aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
   if (NS_FAILED(rv) || !callbacks) {
     return 0;
   }
 
   nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
   if (!loadContext) {
     return 0;
   }
@@ -8616,17 +8658,17 @@ nsContentUtils::InternalStorageAllowedFo
   if (aWindow) {
     // If the document is sandboxed, then it is not permitted to use storage
     nsIDocument* document = aWindow->GetExtantDoc();
     if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
       return StorageAccess::eDeny;
     }
 
     // Check if we are in private browsing, and record that fact
-    if (document->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId) {
+    if (IsInPrivateBrowsing(document)) {
       access = StorageAccess::ePrivateBrowsing;
     }
   }
 
   nsCOMPtr<nsIPermissionManager> permissionManager =
     services::GetPermissionManager();
   if (!permissionManager) {
     return StorageAccess::eDeny;
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -813,16 +813,26 @@ public:
 
   /**
    * Returns origin attributes of the load group.
    **/
   static mozilla::OriginAttributes
   GetOriginAttributes(nsILoadGroup* aLoadGroup);
 
   /**
+   * Returns true if this document is in a Private Browsing window.
+   */
+  static bool IsInPrivateBrowsing(nsIDocument* aDoc);
+
+  /**
+   * Returns true if this loadGroup uses Private Browsing.
+   */
+  static bool IsInPrivateBrowsing(nsILoadGroup* aLoadGroup);
+
+  /**
    * If aNode is not an element, return true exactly when aContent's binding
    * parent is null.
    *
    * If aNode is an element, return true exactly when aContent's binding parent
    * is the same as aNode's.
    *
    * This method is particularly useful for callers who are trying to ensure
    * that they are working with a non-anonymous descendant of a given node.  If
@@ -880,16 +890,47 @@ public:
                                               const nsAFlatString& aSourceLine
                                                 = EmptyString(),
                                               uint32_t aLineNumber = 0,
                                               uint32_t aColumnNumber = 0,
                                               MissingErrorLocationMode aLocationMode
                                                 = eUSE_CALLING_LOCATION);
 
   /**
+   * Report a non-localized error message to the error console base on the
+   * innerWindowID.
+   *   @param aErrorText the error message
+   *   @param aErrorFlags See nsIScriptError.
+   *   @param aCategory Name of module reporting error.
+   *   @param [aInnerWindowID] Inner window ID for document which triggered the
+   *          message.
+   *   @param [aURI=nullptr] (Optional) URI of resource containing error.
+   *   @param [aSourceLine=EmptyString()] (Optional) The text of the line that
+              contains the error (may be empty).
+   *   @param [aLineNumber=0] (Optional) Line number within resource
+              containing error.
+   *   @param [aColumnNumber=0] (Optional) Column number within resource
+              containing error.
+              If aURI is null, then aDocument->GetDocumentURI() is used.
+   *   @param [aLocationMode] (Optional) Specifies the behavior if
+              error location information is omitted.
+   */
+  static nsresult ReportToConsoleByWindowID(const nsAString& aErrorText,
+                                            uint32_t aErrorFlags,
+                                            const nsACString& aCategory,
+                                            uint64_t aInnerWindowID,
+                                            nsIURI* aURI = nullptr,
+                                            const nsAFlatString& aSourceLine
+                                              = EmptyString(),
+                                            uint32_t aLineNumber = 0,
+                                            uint32_t aColumnNumber = 0,
+                                            MissingErrorLocationMode aLocationMode
+                                              = eUSE_CALLING_LOCATION);
+
+  /**
    * Report a localized error message to the error console.
    *   @param aErrorFlags See nsIScriptError.
    *   @param aCategory Name of module reporting error.
    *   @param aDocument Reference to the document which triggered the message.
    *   @param aFile Properties file containing localized message.
    *   @param aMessageName Name of localized message.
    *   @param [aParams=nullptr] (Optional) Parameters to be substituted into
               localized message.
@@ -2442,21 +2483,26 @@ public:
 
   /**
    * Returns whether a given header is forbidden for an XHR or fetch
    * response.
    */
   static bool IsForbiddenResponseHeader(const nsACString& aHeader);
 
   /**
-   * Returns the inner window ID for the window associated with a request,
+   * Returns the inner window ID for the window associated with a request.
    */
   static uint64_t GetInnerWindowID(nsIRequest* aRequest);
 
   /**
+   * Returns the inner window ID for the window associated with a load group.
+   */
+  static uint64_t GetInnerWindowID(nsILoadGroup* aLoadGroup);
+
+  /**
    * If the hostname for aURI is an IPv6 it encloses it in brackets,
    * otherwise it just outputs the hostname in aHost.
    */
   static nsresult GetHostOrIPv6WithBrackets(nsIURI* aURI, nsAString& aHost);
   static nsresult GetHostOrIPv6WithBrackets(nsIURI* aURI, nsCString& aHost);
 
   /*
    * Call the given callback on all remote children of the given top-level
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -2483,17 +2483,22 @@ nsScriptLoader::OnStreamComplete(nsIIncr
     MOZ_ASSERT(mReporter);
 
     nsAutoCString sourceUri;
     if (mDocument && mDocument->GetDocumentURI()) {
       mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
     }
     rv = aSRIDataVerifier->Verify(aRequest->mIntegrity, channel, sourceUri,
                                   mReporter);
-    mReporter->FlushConsoleReports(mDocument);
+    if (channelRequest) {
+      mReporter->FlushReportsToConsole(
+        nsContentUtils::GetInnerWindowID(channelRequest));
+    } else {
+      mReporter->FlushConsoleReports(mDocument);
+    }
     if (NS_FAILED(rv)) {
       rv = NS_ERROR_SRI_CORRUPT;
     }
   } else {
     nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
 
     if (loadInfo->GetEnforceSRI()) {
       MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
--- a/dom/console/ConsoleReportCollector.cpp
+++ b/dom/console/ConsoleReportCollector.cpp
@@ -34,64 +34,85 @@ ConsoleReportCollector::AddConsoleReport
 
   mPendingReports.AppendElement(PendingReport(aErrorFlags, aCategory,
                                               aPropertiesFile, aSourceFileURI,
                                               aLineNumber, aColumnNumber,
                                               aMessageName, aStringParams));
 }
 
 void
-ConsoleReportCollector::FlushConsoleReports(nsIDocument* aDocument,
-                                            ReportAction aAction)
+ConsoleReportCollector::FlushReportsToConsole(uint64_t aInnerWindowID,
+                                              ReportAction aAction)
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
   nsTArray<PendingReport> reports;
 
   {
     MutexAutoLock lock(mMutex);
     if (aAction == ReportAction::Forget) {
       mPendingReports.SwapElements(reports);
     } else {
       reports = mPendingReports;
     }
   }
 
   for (uint32_t i = 0; i < reports.Length(); ++i) {
     PendingReport& report = reports[i];
 
+    nsXPIDLString errorText;
+    nsresult rv;
+    if (!report.mStringParams.IsEmpty()) {
+      rv = nsContentUtils::FormatLocalizedString(report.mPropertiesFile,
+                                                 report.mMessageName.get(),
+                                                 report.mStringParams,
+                                                 errorText);
+    } else {
+      rv = nsContentUtils::GetLocalizedString(report.mPropertiesFile,
+                                              report.mMessageName.get(),
+                                              errorText);
+    }
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      continue;
+    }
+
     // It would be nice if we did not have to do this since ReportToConsole()
     // just turns around and converts it back to a spec.
     nsCOMPtr<nsIURI> uri;
     if (!report.mSourceFileURI.IsEmpty()) {
       nsresult rv = NS_NewURI(getter_AddRefs(uri), report.mSourceFileURI);
       MOZ_ALWAYS_SUCCEEDS(rv);
       if (NS_FAILED(rv)) {
         continue;
       }
     }
 
-    // Convert back from nsTArray<nsString> to the char16_t** format required
-    // by our l10n libraries and ReportToConsole. (bug 1219762)
-    UniquePtr<const char16_t*[]> params;
-    uint32_t paramsLength = report.mStringParams.Length();
-    if (paramsLength > 0) {
-      params = MakeUnique<const char16_t*[]>(paramsLength);
-      for (uint32_t j = 0; j < paramsLength; ++j) {
-        params[j] = report.mStringParams[j].get();
-      }
-    }
+    nsContentUtils::ReportToConsoleByWindowID(errorText,
+                                              report.mErrorFlags,
+                                              report.mCategory,
+                                              aInnerWindowID,
+                                              uri,
+                                              EmptyString(),
+                                              report.mLineNumber,
+                                              report.mColumnNumber);
+  }
+}
 
-    nsContentUtils::ReportToConsole(report.mErrorFlags, report.mCategory,
-                                    aDocument, report.mPropertiesFile,
-                                    report.mMessageName.get(),
-                                    params.get(),
-                                    paramsLength, uri, EmptyString(),
-                                    report.mLineNumber, report.mColumnNumber);
-  }
+void
+ConsoleReportCollector::FlushConsoleReports(nsIDocument* aDocument,
+                                            ReportAction aAction)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  FlushReportsToConsole(aDocument ? aDocument->InnerWindowID() : 0, aAction);
+}
+
+void
+ConsoleReportCollector::FlushConsoleReports(nsILoadGroup* aLoadGroup,
+                                            ReportAction aAction)
+{
+  FlushReportsToConsole(nsContentUtils::GetInnerWindowID(aLoadGroup), aAction);
 }
 
 void
 ConsoleReportCollector::FlushConsoleReports(nsIConsoleReportCollector* aCollector)
 {
   MOZ_ASSERT(aCollector);
 
   nsTArray<PendingReport> reports;
@@ -106,81 +127,16 @@ ConsoleReportCollector::FlushConsoleRepo
     aCollector->AddConsoleReport(report.mErrorFlags, report.mCategory,
                                  report.mPropertiesFile, report.mSourceFileURI,
                                  report.mLineNumber, report.mColumnNumber,
                                  report.mMessageName, report.mStringParams);
   }
 }
 
 void
-ConsoleReportCollector::FlushReportsByWindowId(uint64_t aWindowId,
-                                               ReportAction aAction)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsTArray<PendingReport> reports;
-
-  {
-    MutexAutoLock lock(mMutex);
-    if (aAction == ReportAction::Forget) {
-      mPendingReports.SwapElements(reports);
-    } else {
-      reports = mPendingReports;
-    }
-  }
-
-  nsCOMPtr<nsIConsoleService> consoleService =
-    do_GetService(NS_CONSOLESERVICE_CONTRACTID);
-  if (!consoleService) {
-    NS_WARNING("GetConsoleService failed");
-    return;
-  }
-
-  nsresult rv;
-  for (uint32_t i = 0; i < reports.Length(); ++i) {
-    PendingReport& report = reports[i];
-
-    nsXPIDLString errorText;
-    if (!report.mStringParams.IsEmpty()) {
-      rv = nsContentUtils::FormatLocalizedString(report.mPropertiesFile,
-                                                 report.mMessageName.get(),
-                                                 report.mStringParams,
-                                                 errorText);
-    } else {
-      rv = nsContentUtils::GetLocalizedString(report.mPropertiesFile,
-                                              report.mMessageName.get(),
-                                              errorText);
-    }
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      continue;
-    }
-
-    nsCOMPtr<nsIScriptError> errorObject =
-    do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      continue;
-    }
-
-    rv = errorObject->InitWithWindowID(errorText,
-                                       NS_ConvertUTF8toUTF16(report.mSourceFileURI),
-                                       EmptyString(),
-                                       report.mLineNumber,
-                                       report.mColumnNumber,
-                                       report.mErrorFlags,
-                                       report.mCategory,
-                                       aWindowId);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      continue;
-    }
-
-    consoleService->LogMessage(errorObject);
-  }
-}
-
-void
 ConsoleReportCollector::ClearConsoleReports()
 {
   MutexAutoLock lock(mMutex);
 
   mPendingReports.Clear();
 }
 
 ConsoleReportCollector::~ConsoleReportCollector()
--- a/dom/console/ConsoleReportCollector.h
+++ b/dom/console/ConsoleReportCollector.h
@@ -22,27 +22,31 @@ public:
   AddConsoleReport(uint32_t aErrorFlags, const nsACString& aCategory,
                    nsContentUtils::PropertiesFile aPropertiesFile,
                    const nsACString& aSourceFileURI,
                    uint32_t aLineNumber, uint32_t aColumnNumber,
                    const nsACString& aMessageName,
                    const nsTArray<nsString>& aStringParams) override;
 
   void
+  FlushReportsToConsole(uint64_t aInnerWindowID,
+                        ReportAction aAction = ReportAction::Forget) override;
+
+  void
   FlushConsoleReports(nsIDocument* aDocument,
                       ReportAction aAction = ReportAction::Forget) override;
 
   void
+  FlushConsoleReports(nsILoadGroup* aLoadGroup,
+                      ReportAction aAction = ReportAction::Forget) override;
+
+  void
   FlushConsoleReports(nsIConsoleReportCollector* aCollector) override;
 
   void
-  FlushReportsByWindowId(uint64_t aWindowId,
-                         ReportAction aAction = ReportAction::Forget) override;
-
-  void
   ClearConsoleReports() override;
 
 private:
   ~ConsoleReportCollector();
 
   struct PendingReport
   {
     PendingReport(uint32_t aErrorFlags, const nsACString& aCategory,
--- a/dom/console/nsIConsoleReportCollector.h
+++ b/dom/console/nsIConsoleReportCollector.h
@@ -69,47 +69,58 @@ public:
   // An enum calss to indicate whether should free the pending reports or not.
   // Forget        Free the pending reports.
   // Save          Keep the pending reports.
   enum class ReportAction {
     Forget,
     Save
   };
 
+  // Flush all pending reports to the console.  May be called from any thread.
+  //
+  // aInnerWindowID A inner window ID representing where to flush the reports.
+  // aAction        An action to determine whether to reserve the pending
+  //                reports. Defalut action is to forget the report.
+  virtual void
+  FlushReportsToConsole(uint64_t aInnerWindowID,
+                        ReportAction aAction = ReportAction::Forget) = 0;
+
   // Flush all pending reports to the console.  Main thread only.
   //
   // aDocument      An optional document representing where to flush the
   //                reports.  If provided, then the corresponding window's
   //                web console will get the reports.  Otherwise the reports
   //                go to the browser console.
   // aAction        An action to determine whether to reserve the pending
   //                reports. Defalut action is to forget the report.
   virtual void
   FlushConsoleReports(nsIDocument* aDocument,
                       ReportAction aAction = ReportAction::Forget) = 0;
 
+  // Flush all pending reports to the console.  May be called from any thread.
+  //
+  // aLoadGroup     An optional loadGroup representing where to flush the
+  //                reports.  If provided, then the corresponding window's
+  //                web console will get the reports.  Otherwise the reports
+  //                go to the browser console.
+  // aAction        An action to determine whether to reserve the pending
+  //                reports. Defalut action is to forget the report.
+  virtual void
+  FlushConsoleReports(nsILoadGroup* aLoadGroup,
+                      ReportAction aAction = ReportAction::Forget) = 0;
+
+
   // Flush all pending reports to another collector.  May be called from any
   // thread.
   //
   // aCollector     A required collector object that will effectively take
   //                ownership of our currently console reports.
   virtual void
   FlushConsoleReports(nsIConsoleReportCollector* aCollector) = 0;
 
-  // Flush all pending reports to the console accroding to window ID. Main
-  // thread only.
-  //
-  // aWindowId      A window ID representing where to flush the reports and it's
-  //                typically the inner window ID.
-  //
-  // aAction        An action to decide whether free the pending reports or not.
-  virtual void
-  FlushReportsByWindowId(uint64_t aWindowId,
-                         ReportAction aAction = ReportAction::Forget) = 0;
-
   // Clear all pending reports.
   virtual void
   ClearConsoleReports() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIConsoleReportCollector, NS_NSICONSOLEREPORTCOLLECTOR_IID)
 
 #endif // nsIConsoleReportCollector_h
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -95,41 +95,41 @@ private:
   FlushConsoleReport() override;
 };
 
 class MainThreadFetchResolver final : public FetchDriverObserver
 {
   RefPtr<Promise> mPromise;
   RefPtr<Response> mResponse;
 
-  nsCOMPtr<nsIDocument> mDocument;
+  nsCOMPtr<nsILoadGroup> mLoadGroup;
 
   NS_DECL_OWNINGTHREAD
 public:
   explicit MainThreadFetchResolver(Promise* aPromise);
 
   void
   OnResponseAvailableInternal(InternalResponse* aResponse) override;
 
-  void SetDocument(nsIDocument* aDocument)
+  void SetLoadGroup(nsILoadGroup* aLoadGroup)
   {
-    mDocument = aDocument;
+    mLoadGroup = aLoadGroup;
   }
 
   virtual void OnResponseEnd() override
   {
     FlushConsoleReport();
   }
 
 private:
   ~MainThreadFetchResolver();
 
   void FlushConsoleReport() override
   {
-    mReporter->FlushConsoleReports(mDocument);
+    mReporter->FlushConsoleReports(mLoadGroup);
   }
 };
 
 class MainThreadFetchRunnable : public Runnable
 {
   RefPtr<WorkerFetchResolver> mResolver;
   RefPtr<InternalRequest> mRequest;
 
@@ -233,17 +233,17 @@ FetchRequest(nsIGlobalObject* aGlobal, c
       }
     }
 
     Telemetry::Accumulate(Telemetry::FETCH_IS_MAINTHREAD, 1);
 
     RefPtr<MainThreadFetchResolver> resolver = new MainThreadFetchResolver(p);
     RefPtr<FetchDriver> fetch = new FetchDriver(r, principal, loadGroup);
     fetch->SetDocument(doc);
-    resolver->SetDocument(doc);
+    resolver->SetLoadGroup(loadGroup);
     aRv = fetch->Fetch(resolver);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
   } else {
     WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(worker);
 
@@ -448,40 +448,40 @@ WorkerFetchResolver::FlushConsoleReport(
   MOZ_ASSERT(mPromiseProxy);
 
   if(!mReporter) {
     return;
   }
 
   workers::WorkerPrivate* worker = mPromiseProxy->GetWorkerPrivate();
   if (!worker) {
-    mReporter->FlushConsoleReports((nsIDocument*)nullptr);
+    mReporter->FlushReportsToConsole(0);
     return;
   }
 
   if (worker->IsServiceWorker()) {
     // Flush to service worker
     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     if (!swm) {
-      mReporter->FlushConsoleReports((nsIDocument*)nullptr);
+      mReporter->FlushReportsToConsole(0);
       return;
     }
 
     swm->FlushReportsToAllClients(worker->WorkerName(), mReporter);
     return;
   }
 
   if (worker->IsSharedWorker()) {
     // Flush to shared worker
     worker->FlushReportsToSharedWorkers(mReporter);
     return;
   }
 
   // Flush to dedicated worker
-  mReporter->FlushConsoleReports(worker->GetDocument());
+  mReporter->FlushConsoleReports(worker->GetLoadGroup());
 }
 
 nsresult
 ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
                           nsIInputStream** aStream,
                           nsCString& aContentTypeWithCharset,
                           uint64_t& aContentLength)
 {
--- a/dom/file/File.cpp
+++ b/dom/file/File.cpp
@@ -31,16 +31,17 @@
 #include "nsPrintfCString.h"
 #include "mozilla/SHA1.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/FileBinding.h"
+#include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "nsThreadUtils.h"
 #include "nsStreamUtils.h"
 #include "SlicedInputStream.h"
 
 namespace mozilla {
 namespace dom {
@@ -465,25 +466,28 @@ File::WrapObject(JSContext* aCx, JS::Han
 
 void
 File::GetName(nsAString& aFileName) const
 {
   mImpl->GetName(aFileName);
 }
 
 void
-File::GetPath(nsAString& aPath) const
+File::GetRelativePath(nsAString& aPath) const
 {
-  mImpl->GetPath(aPath);
-}
+  aPath.Truncate();
 
-void
-File::SetPath(const nsAString& aPath)
-{
-  mImpl->SetPath(aPath);
+  nsAutoString path;
+  mImpl->GetDOMPath(path);
+
+  // WebkitRelativePath doesn't start with '/'
+  if (!path.IsEmpty()) {
+    MOZ_ASSERT(path[0] == FILESYSTEM_DOM_PATH_SEPARATOR_CHAR);
+    aPath.Assign(Substring(path, 1));
+  }
 }
 
 Date
 File::GetLastModifiedDate(ErrorResult& aRv)
 {
   int64_t value = GetLastModified(aRv);
   if (aRv.Failed()) {
     return Date();
@@ -665,24 +669,24 @@ NS_IMPL_ISUPPORTS_INHERITED0(BlobImplFil
 void
 BlobImplBase::GetName(nsAString& aName) const
 {
   MOZ_ASSERT(mIsFile, "Should only be called on files");
   aName = mName;
 }
 
 void
-BlobImplBase::GetPath(nsAString& aPath) const
+BlobImplBase::GetDOMPath(nsAString& aPath) const
 {
   MOZ_ASSERT(mIsFile, "Should only be called on files");
   aPath = mPath;
 }
 
 void
-BlobImplBase::SetPath(const nsAString& aPath)
+BlobImplBase::SetDOMPath(const nsAString& aPath)
 {
   MOZ_ASSERT(mIsFile, "Should only be called on files");
   mPath = aPath;
 }
 
 void
 BlobImplBase::GetMozFullPath(nsAString& aFileName,
                              SystemCallerGuarantee /* unused */,
--- a/dom/file/File.h
+++ b/dom/file/File.h
@@ -220,23 +220,17 @@ public:
                     ErrorResult& aRv);
 
   void GetName(nsAString& aName) const;
 
   int64_t GetLastModified(ErrorResult& aRv);
 
   Date GetLastModifiedDate(ErrorResult& aRv);
 
-  // GetPath and SetPath are currently used only for the webkitRelativePath
-  // attribute and they are only used when this File object is created from a
-  // Directory, generated by a Directory Picker.
-
-  void GetPath(nsAString& aName) const;
-
-  void SetPath(const nsAString& aName);
+  void GetRelativePath(nsAString& aPath) const;
 
   void GetMozFullPath(nsAString& aFilename, SystemCallerGuarantee aGuarantee,
                       ErrorResult& aRv) const;
 
   void GetMozFullPathInternal(nsAString& aName, ErrorResult& aRv) const;
 
 protected:
   virtual bool HasFileInterface() const override { return true; }
@@ -255,19 +249,19 @@ class BlobImpl : public nsISupports
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(BLOBIMPL_IID)
   NS_DECL_THREADSAFE_ISUPPORTS
 
   BlobImpl() {}
 
   virtual void GetName(nsAString& aName) const = 0;
 
-  virtual void GetPath(nsAString& aName) const = 0;
+  virtual void GetDOMPath(nsAString& aName) const = 0;
 
-  virtual void SetPath(const nsAString& aName) = 0;
+  virtual void SetDOMPath(const nsAString& aName) = 0;
 
   virtual int64_t GetLastModified(ErrorResult& aRv) = 0;
 
   virtual void SetLastModified(int64_t aLastModified) = 0;
 
   virtual void GetMozFullPath(nsAString& aName,
                               SystemCallerGuarantee /* unused */,
                               ErrorResult& aRv) const = 0;
@@ -401,19 +395,19 @@ public:
   {
     MOZ_ASSERT(aLength != UINT64_MAX, "Must know length when creating slice");
     // Ensure non-null mContentType by default
     mContentType.SetIsVoid(false);
   }
 
   virtual void GetName(nsAString& aName) const override;
 
-  virtual void GetPath(nsAString& aName) const override;
+  virtual void GetDOMPath(nsAString& aName) const override;
 
-  virtual void SetPath(const nsAString& aName) override;
+  virtual void SetDOMPath(const nsAString& aName) override;
 
   virtual int64_t GetLastModified(ErrorResult& aRv) override;
 
   virtual void SetLastModified(int64_t aLastModified) override;
 
   virtual void GetMozFullPath(nsAString& aName,
                               SystemCallerGuarantee /* unused */,
                               ErrorResult& aRv) const override;
--- a/dom/file/ipc/Blob.cpp
+++ b/dom/file/ipc/Blob.cpp
@@ -2066,20 +2066,20 @@ public:
   NoteDyingActor();
 
   NS_DECL_ISUPPORTS_INHERITED
 
   void
   GetName(nsAString& aName) const override;
 
   void
-  GetPath(nsAString& aPath) const override;
+  GetDOMPath(nsAString& aPath) const override;
 
   void
-  SetPath(const nsAString& aPath) override;
+  SetDOMPath(const nsAString& aPath) override;
 
   int64_t
   GetLastModified(ErrorResult& aRv) override;
 
   void
   SetLastModified(int64_t aLastModified) override;
 
   void
@@ -2170,27 +2170,27 @@ private:
  * BlobChild::RemoteBlobImpl
  ******************************************************************************/
 
 BlobChild::
 RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
                                BlobImpl* aRemoteBlobImpl,
                                const nsAString& aName,
                                const nsAString& aContentType,
-                               const nsAString& aPath,
+                               const nsAString& aDOMPath,
                                uint64_t aLength,
                                int64_t aModDate,
                                BlobImplIsDirectory aIsDirectory,
                                bool aIsSameProcessBlob)
   : BlobImplBase(aName, aContentType, aLength, aModDate)
   , mWorkerPrivate(nullptr)
   , mMutex("BlobChild::RemoteBlobImpl::mMutex")
   , mIsSlice(false), mIsDirectory(aIsDirectory == eDirectory)
 {
-  SetPath(aPath);
+  SetDOMPath(aDOMPath);
 
   if (aIsSameProcessBlob) {
     MOZ_ASSERT(aRemoteBlobImpl);
     mSameProcessBlobImpl = aRemoteBlobImpl;
     MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
   } else {
     mDifferentProcessBlobImpl = aRemoteBlobImpl;
   }
@@ -2838,26 +2838,26 @@ void
 BlobParent::
 RemoteBlobImpl::GetName(nsAString& aName) const
 {
   mBlobImpl->GetName(aName);
 }
 
 void
 BlobParent::
-RemoteBlobImpl::GetPath(nsAString& aPath) const
+RemoteBlobImpl::GetDOMPath(nsAString& aPath) const
 {
-  mBlobImpl->GetPath(aPath);
+  mBlobImpl->GetDOMPath(aPath);
 }
 
 void
 BlobParent::
-RemoteBlobImpl::SetPath(const nsAString& aPath)
+RemoteBlobImpl::SetDOMPath(const nsAString& aPath)
 {
-  mBlobImpl->SetPath(aPath);
+  mBlobImpl->SetDOMPath(aPath);
 }
 
 int64_t
 BlobParent::
 RemoteBlobImpl::GetLastModified(ErrorResult& aRv)
 {
   return mBlobImpl->GetLastModified(aRv);
 }
@@ -3191,28 +3191,28 @@ BlobChild::CommonInit(BlobChild* aOther,
   uint64_t length = otherImpl->GetSize(rv);
   MOZ_ASSERT(!rv.Failed());
 
   RemoteBlobImpl* remoteBlob = nullptr;
   if (otherImpl->IsFile()) {
     nsAutoString name;
     otherImpl->GetName(name);
 
-    nsAutoString path;
-    otherImpl->GetPath(path);
+    nsAutoString domPath;
+    otherImpl->GetDOMPath(domPath);
 
     int64_t modDate = otherImpl->GetLastModified(rv);
     MOZ_ASSERT(!rv.Failed());
 
     RemoteBlobImpl::BlobImplIsDirectory directory = otherImpl->IsDirectory() ?
       RemoteBlobImpl::BlobImplIsDirectory::eDirectory :
       RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory;
 
     remoteBlob =
-      new RemoteBlobImpl(this, otherImpl, name, contentType, path,
+      new RemoteBlobImpl(this, otherImpl, name, contentType, domPath,
                          length, modDate, directory,
                          false /* SameProcessBlobImpl */);
   } else {
     remoteBlob = new RemoteBlobImpl(this, otherImpl, contentType, length,
                                     false /* SameProcessBlobImpl */);
   }
 
   // This RemoteBlob must be kept alive untill RecvCreatedFromKnownBlob is
@@ -3286,33 +3286,33 @@ BlobChild::CommonInit(const ChildBlobCon
 
       nsString contentType;
       blobImpl->GetType(contentType);
 
       if (blobImpl->IsFile()) {
         nsAutoString name;
         blobImpl->GetName(name);
 
-        nsAutoString path;
-        blobImpl->GetPath(path);
+        nsAutoString domPath;
+        blobImpl->GetDOMPath(domPath);
 
         int64_t lastModifiedDate = blobImpl->GetLastModified(rv);
         MOZ_ASSERT(!rv.Failed());
 
         RemoteBlobImpl::BlobImplIsDirectory directory =
           blobImpl->IsDirectory() ?
             RemoteBlobImpl::BlobImplIsDirectory::eDirectory :
             RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory;
 
         remoteBlob =
           new RemoteBlobImpl(this,
                              blobImpl,
                              name,
                              contentType,
-                             path,
+                             domPath,
                              size,
                              lastModifiedDate,
                              directory,
                              true /* SameProcessBlobImpl */);
       } else {
         remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size,
                                         true /* SameProcessBlobImpl */);
       }
@@ -3485,24 +3485,24 @@ BlobChild::GetOrCreateFromImpl(ChildMana
     ErrorResult rv;
     uint64_t length = aBlobImpl->GetSize(rv);
     MOZ_ASSERT(!rv.Failed());
 
     if (aBlobImpl->IsFile()) {
       nsAutoString name;
       aBlobImpl->GetName(name);
 
-      nsAutoString path;
-      aBlobImpl->GetPath(path);
+      nsAutoString domPath;
+      aBlobImpl->GetDOMPath(domPath);
 
       int64_t modDate = aBlobImpl->GetLastModified(rv);
       MOZ_ASSERT(!rv.Failed());
 
       blobParams =
-        FileBlobConstructorParams(name, contentType, path, length, modDate,
+        FileBlobConstructorParams(name, contentType, domPath, length, modDate,
                                   aBlobImpl->IsDirectory(), blobData);
     } else {
       blobParams = NormalBlobConstructorParams(contentType, length, blobData);
     }
   }
 
   auto* actor = new BlobChild(aManager, aBlobImpl);
 
@@ -4038,24 +4038,24 @@ BlobParent::GetOrCreateFromImpl(ParentMa
       ErrorResult rv;
       uint64_t length = aBlobImpl->GetSize(rv);
       MOZ_ASSERT(!rv.Failed());
 
       if (aBlobImpl->IsFile()) {
         nsAutoString name;
         aBlobImpl->GetName(name);
 
-        nsAutoString path;
-        aBlobImpl->GetPath(path);
+        nsAutoString domPath;
+        aBlobImpl->GetDOMPath(domPath);
 
         int64_t modDate = aBlobImpl->GetLastModified(rv);
         MOZ_ASSERT(!rv.Failed());
 
         blobParams =
-          FileBlobConstructorParams(name, contentType, path, length, modDate,
+          FileBlobConstructorParams(name, contentType, domPath, length, modDate,
                                     aBlobImpl->IsDirectory(), void_t());
       } else {
         blobParams = NormalBlobConstructorParams(contentType, length, void_t());
       }
     }
   }
 
   nsID id;
--- a/dom/file/moz.build
+++ b/dom/file/moz.build
@@ -18,16 +18,17 @@ EXPORTS += [
     'nsHostObjectURI.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'BlobSet.h',
     'File.h',
     'FileList.h',
     'FileReader.h',
+    'MultipartBlobImpl.h',
     'MutableBlobStorage.h',
     'MutableBlobStreamListener.h',
 ]
 
 UNIFIED_SOURCES += [
     'BlobSet.cpp',
     'File.cpp',
     'FileList.cpp',
--- a/dom/filesystem/GetDirectoryListingTask.cpp
+++ b/dom/filesystem/GetDirectoryListingTask.cpp
@@ -247,17 +247,17 @@ GetDirectoryListingTaskParent::GetSucces
       // This is specific for unix root filesystem.
       if (!mDOMPath.EqualsLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL)) {
         filePath.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
       }
 
       nsAutoString name;
       blobImpl->GetName(name);
       filePath.Append(name);
-      blobImpl->SetPath(filePath);
+      blobImpl->SetDOMPath(filePath);
 
       fileData.blobParent() =
         BlobParent::GetOrCreate(mRequestParent->Manager(), blobImpl);
       inputs.AppendElement(fileData);
     } else {
       MOZ_ASSERT(mTargetData[i].mType == FileOrDirectoryPath::eDirectoryPath);
       FileSystemDirectoryListingResponseDirectory directoryData;
       directoryData.directoryRealPath() = mTargetData[i].mPath;
--- a/dom/filesystem/GetFilesHelper.cpp
+++ b/dom/filesystem/GetFilesHelper.cpp
@@ -272,27 +272,27 @@ GetFilesHelper::RunIO()
   MOZ_ASSERT(!mListingCompleted);
 
   nsCOMPtr<nsIFile> file;
   mErrorResult = NS_NewLocalFile(mDirectoryPath, true, getter_AddRefs(file));
   if (NS_WARN_IF(NS_FAILED(mErrorResult))) {
     return;
   }
 
-  nsAutoString path;
-  mErrorResult = file->GetLeafName(path);
+  nsAutoString leafName;
+  mErrorResult = file->GetLeafName(leafName);
   if (NS_WARN_IF(NS_FAILED(mErrorResult))) {
     return;
   }
 
-  if (path.IsEmpty()) {
-    path.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
-  }
+  nsAutoString domPath;
+  domPath.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
+  domPath.Append(leafName);
 
-  mErrorResult = ExploreDirectory(path, file);
+  mErrorResult = ExploreDirectory(domPath, file);
 }
 
 void
 GetFilesHelper::RunMainThread()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mDirectoryPath.IsEmpty());
   MOZ_ASSERT(!mListingCompleted);
@@ -379,17 +379,17 @@ GetFilesHelperBase::ExploreDirectory(con
     nsAutoString leafName;
     if (NS_WARN_IF(NS_FAILED(currFile->GetLeafName(leafName)))) {
       continue;
     }
     domPath.Append(leafName);
 
     if (isFile) {
       RefPtr<BlobImpl> blobImpl = new BlobImplFile(currFile);
-      blobImpl->SetPath(domPath);
+      blobImpl->SetDOMPath(domPath);
 
       if (!mTargetBlobImplArray.AppendElement(blobImpl, fallible)) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
 
       continue;
     }
 
--- a/dom/filesystem/compat/FileSystemFileEntry.cpp
+++ b/dom/filesystem/compat/FileSystemFileEntry.cpp
@@ -2,43 +2,74 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileSystemFileEntry.h"
 #include "CallbackRunnables.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/MultipartBlobImpl.h"
 #include "mozilla/dom/FileSystemFileEntryBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 class FileCallbackRunnable final : public Runnable
 {
 public:
-  FileCallbackRunnable(FileCallback* aCallback, File* aFile)
+  FileCallbackRunnable(FileCallback* aCallback, ErrorCallback* aErrorCallback,
+                       File* aFile)
     : mCallback(aCallback)
+    , mErrorCallback(aErrorCallback)
     , mFile(aFile)
   {
     MOZ_ASSERT(aCallback);
     MOZ_ASSERT(aFile);
   }
 
   NS_IMETHOD
   Run() override
   {
-    mCallback->HandleEvent(*mFile);
+    // Here we clone the File object.
+
+    nsAutoString name;
+    mFile->GetName(name);
+
+    nsAutoString type;
+    mFile->GetType(type);
+
+    nsTArray<RefPtr<BlobImpl>> blobImpls;
+    blobImpls.AppendElement(mFile->Impl());
+
+    ErrorResult rv;
+    RefPtr<BlobImpl> blobImpl =
+      MultipartBlobImpl::Create(Move(blobImpls), name, type, rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      if (mErrorCallback) {
+        RefPtr<DOMException> exception =
+          DOMException::Create(rv.StealNSResult());
+        mErrorCallback->HandleEvent(*exception);
+      }
+
+      return NS_OK;
+    }
+
+    RefPtr<File> file = File::Create(mFile->GetParentObject(), blobImpl);
+    MOZ_ASSERT(file);
+
+    mCallback->HandleEvent(*file);
     return NS_OK;
   }
 
 private:
   RefPtr<FileCallback> mCallback;
+  RefPtr<ErrorCallback> mErrorCallback;
   RefPtr<File> mFile;
 };
 
 } // anonymous namespace
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(FileSystemFileEntry, FileSystemEntry, mFile)
 
 NS_IMPL_ADDREF_INHERITED(FileSystemFileEntry, FileSystemEntry)
@@ -72,17 +103,17 @@ void
 FileSystemFileEntry::GetName(nsAString& aName, ErrorResult& aRv) const
 {
   mFile->GetName(aName);
 }
 
 void
 FileSystemFileEntry::GetFullPath(nsAString& aPath, ErrorResult& aRv) const
 {
-  mFile->GetPath(aPath);
+  mFile->Impl()->GetDOMPath(aPath);
   if (aPath.IsEmpty()) {
     // We're under the root directory. webkitRelativePath
     // (implemented as GetPath) is for cases when file is selected because its
     // ancestor directory is selected. But that is not the case here, so need to
     // manually prepend '/'.
     nsAutoString name;
     mFile->GetName(name);
     aPath.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
@@ -90,15 +121,18 @@ FileSystemFileEntry::GetFullPath(nsAStri
   }
 }
 
 void
 FileSystemFileEntry::GetFile(FileCallback& aSuccessCallback,
                              const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback) const
 {
   RefPtr<FileCallbackRunnable> runnable =
-    new FileCallbackRunnable(&aSuccessCallback, mFile);
+    new FileCallbackRunnable(&aSuccessCallback,
+                             aErrorCallback.WasPassed()
+                               ? &aErrorCallback.Value() : nullptr,
+                             mFile);
   DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/filesystem/compat/tests/test_basic.html
+++ b/dom/filesystem/compat/tests/test_basic.html
@@ -403,16 +403,35 @@ function test_getParent(entry, parentEnt
     } else {
       test_getParent(e, parentEntry, false);
     }
   }, function(e) {
     ok(false, "This should not happen.");
   });
 }
 
+function test_webkitRelativePath() {
+  fileEntry.file(function(file1) {
+    ok(file1, "We have a file here!");
+    ok(!file1.webkitRelativePath, "webkitRelativePath is an empty string");
+
+    fileEntry.file(function(file2) {
+      ok(file2, "We have a file here!");
+      ok(!file2.webkitRelativePath, "webkitRelativePath is an empty string");
+      isnot(file1, file2, "The 2 files are not the same");
+
+      next();
+    }, function() {
+      ok(false, "Something when wrong!");
+    });
+  }, function() {
+    ok(false, "Something when wrong!");
+  });
+}
+
 var tests = [
   setup_tests,
   populate_entries,
 
   test_entries,
 
   test_fileEntry,
   test_fileEntry_file,
@@ -447,16 +466,18 @@ var tests = [
 
   test_root_getDirectory_securityError,
   test_root_getDirectory_typeMismatchError,
   test_root_getDirectory_nonValidPath,
   test_root_getDirectory_nonExistingPath,
   test_root_getDirectory_simple,
   test_root_getDirectory_deep,
 
+  test_webkitRelativePath,
+
   cleanUpTestingFiles,
 ];
 
 function next() {
   if (!tests.length) {
     SimpleTest.finish();
     return;
   }
--- a/dom/filesystem/tests/filesystem_commons.js
+++ b/dom/filesystem/tests/filesystem_commons.js
@@ -1,12 +1,18 @@
 function createPath(parentDir, dirOrFile) {
   return parentDir.path + (parentDir.path == '/' ? '' : '/') + dirOrFile.name;
 }
 
+function createRelativePath(parentDir, dirOrFile) {
+  let path = createPath(parentDir, dirOrFile);
+  is(path[0], "/", "The full path should start with '/'");
+  return path.substring(1);
+}
+
 function setup_tests(aNext) {
   SimpleTest.requestLongerTimeout(2);
   SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
                                      ["dom.webkitBlink.dirPicker.enabled", true]]}, aNext);
 }
 
 function test_basic(aDirectory, aNext) {
   ok(aDirectory, "Directory exists.");
@@ -24,17 +30,17 @@ function test_getFilesAndDirectories(aDi
           if (data[i] instanceof Directory) {
             isnot(data[i].name, '/', "Subdirectory should be called with the leafname");
             isnot(data[i].path, '/', "Subdirectory path should be called with the leafname");
             isnot(data[i].path, dir.path, "Subdirectory path should contain the parent path.");
             is(data[i].path, createPath(dir, data[i]), "Subdirectory path should be called parentdir.path + '/' + leafname: " + data[i].path);
           }
 
           if (data[i] instanceof File) {
-            is(data[i].webkitRelativePath, createPath(dir, data[i]), "File.webkitRelativePath should be called: parentdir.path + '/' + file.name: " + data[i].webkitRelativePath);
+            is(data[i].webkitRelativePath, createRelativePath(dir, data[i]), "File.webkitRelativePath should be called: parentdir.path + '/' + file.name: " + data[i].webkitRelativePath);
           }
         }
       }
     );
   }
 
   aDirectory.getFilesAndDirectories().then(
     function(data) {
@@ -46,34 +52,35 @@ function test_getFilesAndDirectories(aDi
           isnot(data[i].name, '/', "Subdirectory should be called with the leafname");
           is(data[i].path, createPath(aDirectory, data[i]), "Subdirectory path should be called parentdir.path + '/' + leafname: " + data[i].path);
           if (aRecursive) {
             promises.push(checkSubDir(data[i]));
           }
         }
 
         if (data[i] instanceof File) {
-          is(data[i].webkitRelativePath, createPath(aDirectory, data[i]), "File.webkitRelativePath should be called '/' + file.name: " + data[i].webkitRelativePath);
+          is(data[i].webkitRelativePath, createRelativePath(aDirectory, data[i]), "File.webkitRelativePath should be called file.name: " + data[i].webkitRelativePath);
         }
       }
 
       return Promise.all(promises);
     },
     function() {
       ok(false, "Something when wrong");
     }
   ).then(aNext);
 }
 
 function test_getFiles(aDirectory, aRecursive, aNext) {
   aDirectory.getFiles(aRecursive).then(
     function(data) {
       for (var i = 0; i < data.length; ++i) {
         ok(data[i] instanceof File, "File: " + data[i].name);
-        ok(data[i].webkitRelativePath.indexOf(aDirectory.path) == 0 &&
+        is(aDirectory.path[0], '/', "Directory path must start with '/'");
+        ok(data[i].webkitRelativePath.indexOf(aDirectory.path.substring(1)) == 0 &&
            data[i].webkitRelativePath.indexOf('/' + data[i].name) + ('/' + data[i].name).length == data[i].webkitRelativePath.length,
            "File.webkitRelativePath should be called dir.path + '/' + file.name: " + data[i].webkitRelativePath);
       }
     },
     function() {
       ok(false, "Something when wrong");
     }
   ).then(aNext);
--- a/dom/html/HTMLFormSubmission.cpp
+++ b/dom/html/HTMLFormSubmission.cpp
@@ -506,21 +506,21 @@ FSMultipartFormData::AddNameBlobOrNullPa
   nsAutoCString contentType;
   nsCOMPtr<nsIInputStream> fileStream;
 
   if (aBlob) {
     nsAutoString filename16;
 
     RefPtr<File> file = aBlob->ToFile();
     if (file) {
-      nsAutoString path;
-      file->GetPath(path);
+      nsAutoString relativePath;
+      file->GetRelativePath(relativePath);
       if (Directory::WebkitBlinkDirectoryPickerEnabled(nullptr, nullptr) &&
-          !path.IsEmpty()) {
-        filename16 = path;
+          !relativePath.IsEmpty()) {
+        filename16 = relativePath;
       }
 
       if (filename16.IsEmpty()) {
         RetrieveFileName(aBlob, filename16);
       }
     }
 
     rv = EncodeVal(filename16, filename, true);
--- a/dom/indexedDB/FileSnapshot.h
+++ b/dom/indexedDB/FileSnapshot.h
@@ -55,25 +55,25 @@ private:
   // BlobImpl
   virtual void
   GetName(nsAString& aName) const override
   {
     mBlobImpl->GetName(aName);
   }
 
   virtual void
-  GetPath(nsAString& aPath) const override
+  GetDOMPath(nsAString& aPath) const override
   {
-    mBlobImpl->GetPath(aPath);
+    mBlobImpl->GetDOMPath(aPath);
   }
 
   virtual void
-  SetPath(const nsAString& aPath) override
+  SetDOMPath(const nsAString& aPath) override
   {
-    mBlobImpl->SetPath(aPath);
+    mBlobImpl->SetDOMPath(aPath);
   }
 
   virtual int64_t
   GetLastModified(ErrorResult& aRv) override
   {
     return mBlobImpl->GetLastModified(aRv);
   }
 
--- a/dom/security/test/contentverifier/browser.ini
+++ b/dom/security/test/contentverifier/browser.ini
@@ -11,11 +11,9 @@ support-files =
   file_about_newtab_sri.html
   file_about_newtab_sri_signature
   goodChain.pem
   head.js
   script.js
   style.css
 
 [browser_verify_content_about_newtab.js]
-skip-if = true # This started permafailing randomly (Bug 1336654)
 [browser_verify_content_about_newtab2.js]
-skip-if = true # This started permafailing randomly (Bug 1336654)
--- a/dom/security/test/contentverifier/file_about_newtab_good_signature
+++ b/dom/security/test/contentverifier/file_about_newtab_good_signature
@@ -1,1 +1,1 @@
--mqpvTYdZX4HYQDW1nScojL7ICw5yj8UF2gzxyLbSCx9UIfHH-gWZ40F_PFtqjHxoC1J3dHDb3VedVhOYczdaLrNKbRvPrlnkdGx7Rl8qEBrtZpF1py1Z9uAGoCrgUHa
\ No newline at end of file
+HUndgHvxHNMiAe1SXoeyOOraUJCdxHqWkAYTu0Cq1KpAHcWZEVelNTvyXGbTLWj8btsmqNLAm08UlyK43q_2oO9DQfez3Fo8DhsKvm7TqgSXCkhUoxsYNanxWXhqw-Jw
\ No newline at end of file
--- a/dom/security/test/contentverifier/goodChain.pem
+++ b/dom/security/test/contentverifier/goodChain.pem
@@ -1,51 +1,51 @@
 -----BEGIN CERTIFICATE-----
-MIICUzCCAT2gAwIBAgIUNy0IWlDRDL53zwvj1lq0GCpIe2EwCwYJKoZIhvcNAQEL
-MBExDzANBgNVBAMMBmludC1DQTAiGA8yMDE0MTEyNzAwMDAwMFoYDzIwMTcwMjA0
+MIICUzCCAT2gAwIBAgIUJ1BtYqWRwUsVaZCGPp9eTHIC04QwCwYJKoZIhvcNAQEL
+MBExDzANBgNVBAMMBmludC1DQTAiGA8yMDE1MTEyODAwMDAwMFoYDzIwMTgwMjA1
 MDAwMDAwWjAUMRIwEAYDVQQDDAllZS1pbnQtQ0EwdjAQBgcqhkjOPQIBBgUrgQQA
 IgNiAAShaHJDNitcexiJ83kVRhWhxz+0je6GPgIpFdtgjiUt5LcTLajOmOgxU05q
 nAwLCcjWOa3oMgbluoE0c6EfozDgXajJbkOD/ieHPalxA74oiM/wAvBa9xof3cyD
 dKpuqc6jTjBMMBMGA1UdJQQMMAoGCCsGAQUFBwMDMDUGA1UdEQQuMCyCKnJlbW90
 ZW5ld3RhYi5jb250ZW50LXNpZ25hdHVyZS5tb3ppbGxhLm9yZzALBgkqhkiG9w0B
-AQsDggEBAIeB4WKghknsrow+lj3qzDiHrPBc9AMlb4aZvS6yzazmXr80rXxnsKkb
-ZV1PW/cU6xXH5srWHpfJwypvvYS74btNtuacjKVH2AJdua4482WQIi9gCkXIufRx
-2nSS6pYgYZ4vD+yG8v+3SCChOCXnLjRaN9WxMi8tldbOW9pH44O3vrSSL70pQ2Ph
-8ncUbUbCNNtYhtOe2Z4XT9Cswmfkf4OIQ3gy9eYK2ySEUWP+lHs9KnnNXrLcA/ae
-cSUdI00i3C3OS9yldeyNHzVb8mSsZ5d1WkJrkf/hnXWGrMHRTtlJlG7t7cN8S0Oi
-tQoinJyxrZ+zabFIyl/euDc+Y/dijOU=
+AQsDggEBALiLck6k50ok9ahVq45P3feY1PeUXcIYZkJd8aPDYM+0kfg5+JyJBykA
+mtHWPE1QQjs7VRMfaLfu04E4UJMI2V1AON1qtgR9BQLctW85KFACg2omfiCKwJh0
+5Q8cxBFx9BpNMayqLJwHttB6oluxZFTB8CL/hfpbYpHz1bMEDCVSRP588YBrc8mV
+OLqzQK+k3ewwGvfD6SvXmTny37MxqwxdTPFJNnpqzKAsQIvz8Skic9BkA1NFk0Oq
+lsKKoiibbOCmwS9XY/laAkBaC3winuhciYAC0ImAopZ4PBCU0AOHGrNbhZXWYQxt
+uHBj34FqvIRCqgM06JCEwN0ULgix4kI=
 -----END CERTIFICATE-----
 -----BEGIN CERTIFICATE-----
-MIIC0TCCAbugAwIBAgIULYyr3v/0zZ+XiR22NH7hOcnj2FcwCwYJKoZIhvcNAQEL
-MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
+MIIC0TCCAbugAwIBAgIUPcKbBQpKwTzrrlqzM+d3z5DWiNUwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTUxMTI4MDAwMDAwWhgPMjAxODAyMDUwMDAw
 MDBaMBExDzANBgNVBAMMBmludC1DQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
 AQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72x
 nAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lM
 wmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF
 4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20
 yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xx
 j5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMlMCMwDAYDVR0TBAUwAwEB/zAT
-BgNVHSUEDDAKBggrBgEFBQcDAzALBgkqhkiG9w0BAQsDggEBADfRBKSM08JF6vqz
-0EA+KNc0XIEAWApuHuwX6XXWeLgo6QN4E/9qfrsaO+C366WT+JDsjDOi40wW46SA
-XbguxtZQeZasNDUWp/leZix4RSJoHB7OllG1rgZJfN76zKVaXRGUmyQObkMMOJZe
-wIA0OBURT8ik9Z89pD0IWrqscds71Edfjt0hHgg63wVvIaklReZXvFOD3VmSCPNn
-2wB6ZzECcbhJpnzxZdsoMSGH0C6apYnNNTjqZjO90JVm/Ph/7nbi/KncYXA6ccl6
-Jz2mfiAquWIua2+CzBGbqjZVSATTpWCp+cXQJE1xka+hWUaL5HPTq1bTULRFlauZ
-HGl5lJk=
+BgNVHSUEDDAKBggrBgEFBQcDAzALBgkqhkiG9w0BAQsDggEBADDPjITgz8joxLRW
+wpLxELKSgO/KQ6iAXztjMHq9ovT7Fy0fqBnQ1mMVFr+sBXLgtUCM45aip6PjhUXc
+zs5Dq5STg+kz7qtmAjEQvOPcyictbgdu/K7+uMhXQhlzhOgyW88Uk5vrAezNTc/e
+TvSmWp1FcgVAfaeMN/90nzD1KIHoUt7zqZIz9ub8jXPVzQNZq4vh33smZhmbdTdV
+DaHUyef5cR1VTEGB+L1qzUIQqpHmD4UkMNP1nYedWfauiQhRt6Ql3rJSCRuEvsOA
+iBTJlwai/EFwfyfHkOV2GNgv+A5wHHEjBtF5c4PCxQEL5Vw+mfZHLsDVqF3279ZY
+lQ6jQ9g=
 -----END CERTIFICATE-----
 -----BEGIN CERTIFICATE-----
-MIICzTCCAbegAwIBAgIUIVkGGA8HiO3RIKGjdOjVi+d6EVkwCwYJKoZIhvcNAQEL
-MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
+MIICzTCCAbegAwIBAgIUKRLJoCmk0A6PHrNc8CxFn//4BYcwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTUxMTI4MDAwMDAwWhgPMjAxODAyMDUwMDAw
 MDBaMA0xCzAJBgNVBAMMAmNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
 AQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptu
 Gobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO
 7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf
 qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/yt
 HSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcx
 uLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoyUwIzAMBgNVHRMEBTADAQH/MBMGA1Ud
-JQQMMAoGCCsGAQUFBwMDMAsGCSqGSIb3DQEBCwOCAQEAlpbRzRIPnf43AwGfMvKP
-zOtntRy2nE9GlmY9I00uioHUnUrPLs8aw3UDtyiDWMGqcYysXGx9EX2Vk0POS4gf
-G6PA95F6GxTtbzIEZmTPVuzA/cfc9HU3HXDPqh+dySJ8/Ta4c4vX1lgeGGAvstNe
-q+9DaCGXs8MqMF8KtXNmOm3eS9q622hKEvTVEoxqj1t365kwKHaNpbObddQ6Xcny
-akvfh2L+8QbJSflcm8fL/JTup/2/cRG1ytOsaiXEr9JBEITOtQO0Ot/4Qzq+MJjv
-weaJ3hZ0c+cTy3tEvt+I7+lnW4Q5dB7aLR2/BZfLubhxz1SUVMuHfLH64fc0Uf1Q
-Nw==
+JQQMMAoGCCsGAQUFBwMDMAsGCSqGSIb3DQEBCwOCAQEAABgMK6EyVIXTjD5qaxPO
+DWz6yREACmAQBcowKWvfhwgi27DPSXyFGDbzTPEo+7RrIcXJkVAhLouGT51fCwTZ
+zb6Sgf6ztX7VSppY9AT4utvlZKP1xQ5WhIYsMtdHCHLHIkRjeWyoBEfUx50UXNLK
+Snl+A02GKYWiX+TLLg2DPN2s7v/mm8NLMQNgUlL7KakB2FHFyPa8otPpL4llg7UJ
+iBTVQ0c3JoiVbwZaY1Z8QinfMXUrTK9egUC4BAcId1dE8glzA5RRlw1fTLWpGApt
+hUmbDnl9N2a9NhGX323ypNzIATexafipzWe7bc4u/+bFdrUqnKUoEka73pZBdHdA
+FQ==
 -----END CERTIFICATE-----
--- a/dom/security/test/contentverifier/head.js
+++ b/dom/security/test/contentverifier/head.js
@@ -78,17 +78,17 @@ function* doTest(aExpectedStrings, reloa
     aboutNewTabService.newTabURL = aNewTabPref;
   }
 
   // set prefs
   yield pushPrefs(
       ["browser.newtabpage.remote.content-signing-test", true],
       ["browser.newtabpage.remote", true],
       ["security.content.signature.root_hash",
-       "65:AE:D8:1E:B5:12:AE:B0:6B:38:58:BC:7C:47:35:3D:D4:EA:25:F1:63:DA:08:BB:86:3A:2E:97:39:66:8F:55"]);
+       "CC:BE:04:87:74:B2:98:24:4A:C6:7A:71:BC:6F:DB:D6:C0:48:17:29:57:51:96:47:38:CC:24:C8:E4:F9:DD:CB"]);
 
   if (aNewTabPref === URI_BAD_CSP) {
     // Use stricter CSP to test CSP violation.
     yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self'; style-src 'self'"]);
   } else {
     // Use weaker CSP to test normal content.
     yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self' 'unsafe-inline'; style-src 'self'"]);
   }
--- a/dom/webidl/File.webidl
+++ b/dom/webidl/File.webidl
@@ -28,17 +28,17 @@ dictionary ChromeFilePropertyBag : FileP
   DOMString name = "";
 };
 
 // Mozilla extensions
 partial interface File {
   [GetterThrows, Deprecated="FileLastModifiedDate"]
   readonly attribute Date lastModifiedDate;
 
-  [BinaryName="path", Func="mozilla::dom::Directory::WebkitBlinkDirectoryPickerEnabled"]
+  [BinaryName="relativePath", Func="mozilla::dom::Directory::WebkitBlinkDirectoryPickerEnabled"]
   readonly attribute USVString webkitRelativePath;
 
   [GetterThrows, ChromeOnly, NeedsCallerType]
   readonly attribute DOMString mozFullPath;
 
   [ChromeOnly, Throws, NeedsCallerType]
   static File createFromNsIFile(nsIFile file,
                                 optional ChromeFilePropertyBag options);
--- a/dom/workers/ServiceWorkerContainer.cpp
+++ b/dom/workers/ServiceWorkerContainer.cpp
@@ -44,17 +44,17 @@ ServiceWorkerContainer::IsEnabled(JSCont
 
   JS::Rooted<JSObject*> global(aCx, aGlobal);
   nsCOMPtr<nsPIDOMWindowInner> window = Navigator::GetWindowFromGlobal(global);
   if (!window) {
     return false;
   }
 
   nsIDocument* doc = window->GetExtantDoc();
-  if (!doc || doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId) {
+  if (!doc || nsContentUtils::IsInPrivateBrowsing(doc)) {
     return false;
   }
 
   return Preferences::GetBool("dom.serviceWorkers.enabled", false);
 }
 
 ServiceWorkerContainer::ServiceWorkerContainer(nsPIDOMWindowInner* aWindow)
   : DOMEventTargetHelper(aWindow)
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -1649,20 +1649,21 @@ ServiceWorkerManager::FlushReportsToAllC
       continue;
     }
 
     nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
     if (!doc || !doc->IsCurrentActiveDocument() || !doc->GetWindow()) {
       continue;
     }
 
-    windows.AppendElement(doc->InnerWindowID());
-
-    aReporter->FlushConsoleReports(doc,
-                                   nsIConsoleReportCollector::ReportAction::Save);
+    uint64_t innerWindowId = doc->InnerWindowID();
+    windows.AppendElement(innerWindowId);
+
+    aReporter->FlushReportsToConsole(
+      innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
   }
 
   // Report to any documents that have called .register() for this scope.  They
   // may not be controlled, but will still want to see error reports.
   WeakDocumentList* regList = mRegisteringDocuments.Get(aScope);
   if (regList) {
     for (int32_t i = regList->Length() - 1; i >= 0; --i) {
       nsCOMPtr<nsIDocument> doc = do_QueryReferent(regList->ElementAt(i));
@@ -1677,18 +1678,18 @@ ServiceWorkerManager::FlushReportsToAllC
 
       uint64_t innerWindowId = doc->InnerWindowID();
       if (windows.Contains(innerWindowId)) {
         continue;
       }
 
       windows.AppendElement(innerWindowId);
 
-      aReporter->FlushConsoleReports(doc,
-                                     nsIConsoleReportCollector::ReportAction::Save);
+      aReporter->FlushReportsToConsole(
+        innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
     }
 
     if (regList->IsEmpty()) {
       regList = nullptr;
       nsAutoPtr<WeakDocumentList> doomed;
       mRegisteringDocuments.RemoveAndForget(aScope, doomed);
     }
   }
@@ -1707,25 +1708,25 @@ ServiceWorkerManager::FlushReportsToAllC
 
       uint64_t innerWindowId = nsContentUtils::GetInnerWindowID(inner);
       if (innerWindowId == 0 || windows.Contains(innerWindowId)) {
         continue;
       }
 
       windows.AppendElement(innerWindowId);
 
-      aReporter->FlushReportsByWindowId(innerWindowId,
-                                        nsIConsoleReportCollector::ReportAction::Save);
+      aReporter->FlushReportsToConsole(
+        innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
     }
   }
 
   // If there are no documents to report to, at least report something to the
   // browser console.
   if (windows.IsEmpty()) {
-    aReporter->FlushConsoleReports((nsIDocument*)nullptr);
+    aReporter->FlushReportsToConsole(0);
     return;
   }
 
   aReporter->ClearConsoleReports();
 }
 
 void
 ServiceWorkerManager::HandleError(JSContext* aCx,
@@ -2555,17 +2556,17 @@ ServiceWorkerManager::IsAvailable(nsIPri
   return registration && registration->GetActive();
 }
 
 bool
 ServiceWorkerManager::IsControlled(nsIDocument* aDoc, ErrorResult& aRv)
 {
   MOZ_ASSERT(aDoc);
 
-  if (aDoc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId) {
+  if (nsContentUtils::IsInPrivateBrowsing(aDoc)) {
     // Handle the case where a service worker was previously registered in
     // a non-private window (bug 1255621).
     return false;
   }
 
   RefPtr<ServiceWorkerRegistrationInfo> registration;
   nsresult rv = GetDocumentRegistration(aDoc, getter_AddRefs(registration));
   if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE)) {
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -3730,25 +3730,26 @@ WorkerPrivateParent<Derived>::FlushRepor
   }
 
   bool reportErrorToBrowserConsole = true;
 
   // Flush the reports.
   for (uint32_t index = 0; index < windowActions.Length(); index++) {
     WindowAction& windowAction = windowActions[index];
 
-    aReporter->FlushConsoleReports(windowAction.mWindow->GetExtantDoc(),
-                                   nsIConsoleReportCollector::ReportAction::Save);
+    aReporter->FlushReportsToConsole(
+      windowAction.mWindow->WindowID(),
+      nsIConsoleReportCollector::ReportAction::Save);
     reportErrorToBrowserConsole = false;
   }
 
   // Finally report to broswer console if there is no any window or shared
   // worker.
   if (reportErrorToBrowserConsole) {
-    aReporter->FlushConsoleReports((nsIDocument*)nullptr);
+    aReporter->FlushReportsToConsole(0);
     return;
   }
 
   aReporter->ClearConsoleReports();
 }
 
 template <class Derived>
 NS_IMPL_ADDREF_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper)
new file mode 100644
--- /dev/null
+++ b/embedding/moz.build
@@ -0,0 +1,3 @@
+with Files("**"):
+    BUG_COMPONENT = ("Core", "General")
+
--- a/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp
+++ b/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp
@@ -677,20 +677,23 @@ static void bilevel_to_bw(const uint8_t*
 template<bool APPLY_PREBLEND>
 static void rgb_to_a8(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, const uint8_t* table8) {
     const size_t dstRB = glyph.rowBytes();
     const U16CPU width = glyph.fWidth;
     uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage);
 
     for (U16CPU y = 0; y < glyph.fHeight; y++) {
         for (U16CPU i = 0; i < width; i++) {
-            U8CPU r = *(src++);
-            U8CPU g = *(src++);
-            U8CPU b = *(src++);
-            dst[i] = sk_apply_lut_if<APPLY_PREBLEND>((r + g + b) / 3, table8);
+            U8CPU g = src[1];
+            src += 3;
+
+            // Ignore the R, B channels. It looks the closest to what
+            // D2D with grayscale AA has. But there's no way
+            // to just get a grayscale AA alpha texture from a glyph run.
+            dst[i] = sk_apply_lut_if<APPLY_PREBLEND>(g, table8);
         }
         dst = (uint8_t*)((char*)dst + dstRB);
     }
 }
 
 template<bool APPLY_PREBLEND, bool RGB>
 static void rgb_to_lcd16(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
                          const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -2068,25 +2068,25 @@ gfxFont::Draw(const gfxTextRun *aTextRun
         if (aTextRun->UseCenterBaseline()) {
             gfxPoint baseAdj(0, (metrics.emAscent - metrics.emDescent) / 2);
             mat.Translate(baseAdj);
         }
 
         aRunParams.context->SetMatrix(mat);
     }
 
-    UniquePtr<SVGContextPaint> contextPaint;
+    RefPtr<SVGContextPaint> contextPaint;
     if (fontParams.haveSVGGlyphs && !fontParams.contextPaint) {
         // If no pattern is specified for fill, use the current pattern
         NS_ASSERTION((int(aRunParams.drawMode) & int(DrawMode::GLYPH_STROKE)) == 0,
                      "no pattern supplied for stroking text");
         RefPtr<gfxPattern> fillPattern = aRunParams.context->GetPattern();
-        contextPaint.reset(
+        contextPaint =
             new SimpleTextContextPaint(fillPattern, nullptr,
-                                       aRunParams.context->CurrentMatrix()));
+                                       aRunParams.context->CurrentMatrix());
         fontParams.contextPaint = contextPaint.get();
     }
 
     // Synthetic-bold strikes are each offset one device pixel in run direction.
     // (these values are only needed if IsSyntheticBold() is true)
     if (IsSyntheticBold()) {
         double xscale = CalcXScale(aRunParams.context->GetDrawTarget());
         fontParams.synBoldOnePixelOffset = aRunParams.direction * xscale;
--- a/image/ClippedImage.cpp
+++ b/image/ClippedImage.cpp
@@ -467,18 +467,19 @@ ClippedImage::DrawSingleTile(gfxContext*
     // The size in pixels at which the output will ultimately be drawn is
     // irrelevant here since the purpose of the SVG viewport size is to
     // determine what *region* of the SVG document will be drawn.
     CSSIntSize vSize(aOldContext.GetViewportSize());
     vSize.width = ceil(vSize.width * double(innerSize.width) / mClip.width);
     vSize.height =
       ceil(vSize.height * double(innerSize.height) / mClip.height);
 
-    return SVGImageContext(vSize,
-                           aOldContext.GetPreserveAspectRatio());
+    SVGImageContext context(aOldContext);
+    context.SetViewportSize(vSize);
+    return context;
   };
 
   return InnerImage()->Draw(aContext, size, region,
                             aWhichFrame, aSamplingFilter,
                             aSVGContext.map(unclipViewport),
                             aFlags, aOpacity);
 }
 
--- a/image/OrientedImage.cpp
+++ b/image/OrientedImage.cpp
@@ -292,18 +292,19 @@ OrientedImage::Draw(gfxContext* aContext
   ImageRegion region(aRegion);
   region.TransformBoundsBy(inverseMatrix);
 
   auto orientViewport = [&](const SVGImageContext& aOldContext) {
     CSSIntSize viewportSize(aOldContext.GetViewportSize());
     if (mOrientation.SwapsWidthAndHeight()) {
       swap(viewportSize.width, viewportSize.height);
     }
-    return SVGImageContext(viewportSize,
-                           aOldContext.GetPreserveAspectRatio());
+    SVGImageContext context(aOldContext);
+    context.SetViewportSize(viewportSize);
+    return context;
   };
 
   return InnerImage()->Draw(aContext, size, region, aWhichFrame,
                             aSamplingFilter,
                             aSVGContext.map(orientViewport), aFlags,
                             aOpacity);
 }
 
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -850,19 +850,18 @@ VectorImage::Draw(gfxContext* aContext,
   // overwrite SVG preserveAspectRatio attibute of this image with none, and
   // always stretch this image to viewport non-uniformly.
   // And we can do this only if the caller pass in the the SVG viewport, via
   // aSVGContext.
   if ((aFlags & FLAG_FORCE_PRESERVEASPECTRATIO_NONE) && aSVGContext.isSome()) {
     Maybe<SVGPreserveAspectRatio> aspectRatio =
       Some(SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE,
                                   SVG_MEETORSLICE_UNKNOWN));
-    svgContext =
-      Some(SVGImageContext(aSVGContext->GetViewportSize(),
-                           aspectRatio));
+    svgContext = Some(SVGImageContext(*aSVGContext)); // copy
+    svgContext->SetPreserveAspectRatio(aspectRatio);
   } else {
     svgContext = aSVGContext;
   }
 
   float animTime =
     (aWhichFrame == FRAME_FIRST) ? 0.0f
                                  : mSVGDocumentWrapper->GetCurrentTime();
   AutoSVGRenderingState autoSVGState(svgContext, animTime,
@@ -875,16 +874,23 @@ VectorImage::Draw(gfxContext* aContext,
   // If we have an prerasterized version of this image that matches the
   // drawing parameters, use that.
   RefPtr<gfxDrawable> svgDrawable = LookupCachedSurface(params);
   if (svgDrawable) {
     Show(svgDrawable, params);
     return DrawResult::SUCCESS;
   }
 
+  Maybe<AutoSetRestoreSVGContextPaint> autoContextPaint;
+  if (aSVGContext &&
+      aSVGContext->GetContextPaint()) {
+    autoContextPaint.emplace(aSVGContext->GetContextPaint(),
+                             mSVGDocumentWrapper->GetDocument());
+  }
+
   // We didn't get a hit in the surface cache, so we'll need to rerasterize.
   CreateSurfaceAndShow(params, aContext->GetDrawTarget()->GetBackendType());
   return DrawResult::SUCCESS;
 }
 
 already_AddRefed<gfxDrawable>
 VectorImage::LookupCachedSurface(const SVGDrawingParameters& aParams)
 {
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -416,20 +416,21 @@ function ArrayReduce(callbackfn/*, initi
     return accumulator;
 }
 
 function ArrayStaticReduce(list, callbackfn) {
     if (arguments.length < 2)
         ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "Array.reduce");
     if (!IsCallable(callbackfn))
         ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn));
+
     if (arguments.length > 2)
         return callFunction(ArrayReduce, list, callbackfn, arguments[2]);
-    else
-        return callFunction(ArrayReduce, list, callbackfn);
+
+    return callFunction(ArrayReduce, list, callbackfn);
 }
 
 /* ES5 15.4.4.22. */
 function ArrayReduceRight(callbackfn/*, initialValue*/) {
     /* Step 1. */
     var O = ToObject(this);
 
     /* Steps 2-3. */
@@ -483,20 +484,21 @@ function ArrayReduceRight(callbackfn/*, 
     return accumulator;
 }
 
 function ArrayStaticReduceRight(list, callbackfn) {
     if (arguments.length < 2)
         ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "Array.reduceRight");
     if (!IsCallable(callbackfn))
         ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn));
+
     if (arguments.length > 2)
         return callFunction(ArrayReduceRight, list, callbackfn, arguments[2]);
-    else
-        return callFunction(ArrayReduceRight, list, callbackfn);
+
+    return callFunction(ArrayReduceRight, list, callbackfn);
 }
 
 /* ES6 draft 2013-05-14 15.4.3.23. */
 function ArrayFind(predicate/*, thisArg*/) {
     /* Steps 1-2. */
     var O = ToObject(this);
 
     /* Steps 3-5. */
@@ -821,17 +823,17 @@ function ArrayFrom(items, mapfn = undefi
             // We could hit this when |A| is a proxy and it ignores
             // |_DefineDataProperty|, but it happens only after too long loop.
             /*
             if (k >= 0x1fffffffffffff)
                 ThrowTypeError(JSMSG_TOO_LONG_ARRAY);
             */
 
             // Steps 5.e.vi-vii.
-            var mappedValue = mapping ? callContentFunction(mapfn, thisArg, nextValue, k) : nextValue;
+            var mappedValue = mapping ? callContentFunction(mapfn, T, nextValue, k) : nextValue;
 
             // Steps 5.e.ii (reordered), 5.e.viii.
             _DefineDataProperty(A, k++, mappedValue);
         }
 
         // Step 5.e.iv.
         A.length = k;
         return A;
@@ -850,17 +852,17 @@ function ArrayFrom(items, mapfn = undefi
     var A = IsConstructor(C) ? new C(len) : std_Array(len);
 
     // Steps 15-16.
     for (var k = 0; k < len; k++) {
         // Steps 16.a-c.
         var kValue = items[k];
 
         // Steps 16.d-e.
-        var mappedValue = mapping ? callContentFunction(mapfn, thisArg, kValue, k) : kValue;
+        var mappedValue = mapping ? callContentFunction(mapfn, T, kValue, k) : kValue;
 
         // Steps 16.f-g.
         _DefineDataProperty(A, k, mappedValue);
     }
 
     // Steps 17-18.
     A.length = len;
 
--- a/js/src/builtin/Function.js
+++ b/js/src/builtin/Function.js
@@ -28,17 +28,17 @@ function FunctionBind(thisArg, ...boundA
         F = bind_bindFunctionN(target, thisArg, boundArgs);
     }
 
     // Steps 5-11.
     _FinishBoundFunctionInit(F, target, argCount);
 
     // Ensure that the apply intrinsic has been cloned so it can be baked into
     // JIT code.
-    var funApply = std_Function_apply;
+    void std_Function_apply;
 
     // Step 12.
     return F;
 }
 /**
  * bind_bindFunction{0,1,2} are special cases of the generic bind_bindFunctionN
  * below. They avoid the need to merge the lists of bound arguments and call
  * arguments to the bound function into a new list which is then used in a
@@ -189,18 +189,18 @@ function bind_bindFunctionN(fun, thisArg
         if (_IsConstructing()) {
             newTarget = new.target;
             if (newTarget === bound)
                 newTarget = fun;
         }
         if (arguments.length === 0) {
             if (newTarget !== undefined)
                 return bind_constructFunctionN(fun, newTarget, boundArgs);
-            else
-                return bind_applyFunctionN(fun, thisArg, boundArgs);
+
+            return bind_applyFunctionN(fun, thisArg, boundArgs);
         }
         var callArgs = FUN_APPLY(bind_mapArguments, null, arguments);
         return bind_invokeFunctionN(fun, thisArg, newTarget, boundArgs, callArgs);
     };
 }
 
 function bind_mapArguments() {
     var len = arguments.length;
--- a/js/src/builtin/Generator.js
+++ b/js/src/builtin/Generator.js
@@ -123,17 +123,17 @@ function LegacyGeneratorCloseInternal() 
         CloseClosingLegacyGeneratorObject(this);
 }
 
 function LegacyGeneratorClose() {
     if (!IsObject(this) || !IsLegacyGeneratorObject(this))
         return callFunction(CallLegacyGeneratorMethodIfWrapped, this, "LegacyGeneratorClose");
 
     if (LegacyGeneratorObjectIsClosed(this))
-        return;
+        return undefined;
 
     callFunction(LegacyGeneratorCloseInternal, this);
 }
 
 function InterpretGeneratorResume(gen, val, kind) {
     // If we want to resume a generator in the interpreter, the script containing
     // the resumeGenerator/JSOP_RESUME also has to run in the interpreter. The
     // forceInterpreter() call below compiles to a bytecode op that prevents us
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -1738,18 +1738,18 @@ function resolveNumberFormatInternals(la
 
     var NumberFormat = numberFormatInternalProperties;
 
     // Step 9.
     var localeData = NumberFormat.localeData;
 
     // Step 10.
     var r = ResolveLocale(callFunction(NumberFormat.availableLocales, NumberFormat),
-                          lazyNumberFormatData.requestedLocales,
-                          lazyNumberFormatData.opt,
+                          requestedLocales,
+                          opt,
                           NumberFormat.relevantExtensionKeys,
                           localeData);
 
     // Steps 11-12.  (Step 13 is not relevant to our implementation.)
     internalProps.locale = r.locale;
     internalProps.numberingSystem = r.nu;
 
     // Compute formatting options.
@@ -2399,16 +2399,17 @@ function InitializeDateTimeFormat(dateTi
     //
     // For some reason (ICU not exposing enough interface?) we drop the
     // requested format matcher on the floor after this.  In any case, even if
     // doing so is justified, we have to do this work here in case it triggers
     // getters or similar. (bug 852837)
     var formatMatcher =
         GetOption(options, "formatMatcher", "string", ["basic", "best fit"],
                   "best fit");
+    void formatMatcher;
 
     // Steps 23-25 provided by ICU, more or less - see comment after this function.
 
     // Step 26.
     var hr12  = GetOption(options, "hour12", "boolean", undefined, undefined);
 
     // Pass hr12 on to ICU.
     if (hr12 !== undefined)
@@ -2926,18 +2927,16 @@ var pluralRulesInternalProperties = {
 /**
  * Compute an internal properties object from |lazyPluralRulesData|.
  */
 function resolvePluralRulesInternals(lazyPluralRulesData) {
     assert(IsObject(lazyPluralRulesData), "lazy data not an object?");
 
     var internalProps = std_Object_create(null);
 
-    var requestedLocales = lazyPluralRulesData.requestedLocales;
-
     var PluralRules = pluralRulesInternalProperties;
 
     // Step 13.
     const r = ResolveLocale(callFunction(PluralRules.availableLocales, PluralRules),
                           lazyPluralRulesData.requestedLocales,
                           lazyPluralRulesData.opt,
                           noRelevantExtensionKeys, undefined);
 
--- a/js/src/builtin/Module.js
+++ b/js/src/builtin/Module.js
@@ -226,17 +226,17 @@ function ModuleDeclarationInstantiation(
     if (!IsObject(this) || !IsModule(this))
         return callFunction(CallModuleMethodIfWrapped, this, "ModuleDeclarationInstantiation");
 
     // Step 1
     let module = this;
 
     // Step 5
     if (GetModuleEnvironment(module) !== undefined)
-        return;
+        return undefined;
 
     // Step 7
     CreateModuleEnvironment(module);
     let env = GetModuleEnvironment(module);
 
     SetModuleState(this, MODULE_STATE_INSTANTIATED);
 
     try {
--- a/js/src/builtin/Sorting.js
+++ b/js/src/builtin/Sorting.js
@@ -263,17 +263,17 @@ function MergeSort(array, len, comparefn
         MoveHoles(array, len, denseList, denseLen);
         return array;
     }
 
     // We do all of our allocating up front
     var lBuffer = new List();
     var rBuffer = new List();
 
-    var mid, end, endOne, endTwo;
+    var mid, end;
     for (var windowSize = 1; windowSize < denseLen; windowSize = 2 * windowSize) {
         for (var start = 0; start < denseLen - 1; start += 2 * windowSize) {
             assert(windowSize < denseLen, "The window size is larger than the array denseLength!");
             // The midpoint between the two subarrays.
             mid = start + windowSize - 1;
             // To keep from going over the edge.
             end = start + 2 * windowSize - 1;
             end = end < denseLen - 1 ? end : denseLen - 1;
@@ -332,17 +332,17 @@ function QuickSort(array, len, comparefn
 
     // Managing the stack ourselves seems to provide a small performance boost.
     var stack = new List();
     var top = 0;
 
     var start = 0;
     var end   = len - 1;
 
-    var pivotIndex, i, j, leftLen, rightLen;
+    var pivotIndex, leftLen, rightLen;
 
     for (;;) {
         // Insertion sort for the first N elements where N is some value
         // determined by performance testing.
         if (end - start <= 23) {
             InsertionSort(array, start, end, comparefn);
             if (top < 1)
                 break;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/setelem-id-guard.js
@@ -0,0 +1,91 @@
+function setSlot() {
+    var o = {x: 1, y: 2};
+    for (var i=0; i<100; i++) {
+        var p = (i & 1) ? "x" : "y";
+        o[p] = i;
+        assertEq((i & 1) ? o.x : o.y, i);
+    }
+}
+setSlot();
+
+function setUnboxed() {
+    var arr = [];
+    for (var i=0; i<100; i++)
+        arr.push({x: 1, y: 2});
+
+    for (var i=0; i<100; i++) {
+        var o = arr[i];
+        var p = (i & 1) ? "x" : "y";
+        o[p] = i;
+        assertEq((i & 1) ? o.x : o.y, i);
+    }
+}
+setUnboxed();
+
+function setUnboxedExpando() {
+    var arr = [];
+    for (var i=0; i<100; i++)
+        arr.push({x: 1, y: 2});
+
+    for (var i=0; i<100; i++) {
+        var o = arr[i];
+        o.a = 1;
+        o.b = 2;
+        var p = (i & 1) ? "a" : "b";
+        o[p] = i;
+        o[p] = o[p] + 4;
+        assertEq((i & 1) ? o.a : o.b, i + 4);
+    }
+}
+setUnboxedExpando();
+
+function setArrayLength() {
+    var arr = [];
+    for (var i=0; i<100; i++) {
+        var p = (i & 1) ? "length" : "x";
+        arr[p] = i;
+        assertEq((i & 1) ? arr.length : arr.x, i);
+    }
+}
+setArrayLength();
+
+function setter() {
+    var c = 0;
+    var o = {set x(i) { c += i; }, set y(i) { c += i + 2; }};
+    for (var i=0; i<100; i++) {
+        var p = (i & 1) ? "x" : "y";
+        o[p] = i;
+    }
+    assertEq(c, 5050);
+}
+setter();
+
+function addSlot() {
+    for (var i=0; i<100; i++) {
+        var o = {};
+        var p1 = (i & 1) ? "x" : "y";
+        var p2 = (i % 3) ? "a" : "b";
+        o[p1] = i;
+        o[p2] = i;
+        assertEq((i & 1) ? o.x : o.y, i);
+        assertEq((i % 3) ? o.a : o.b, i);
+    }
+}
+addSlot();
+
+function addExpandoSlot() {
+    var arr = [];
+    for (var i=0; i<100; i++)
+        arr.push({c: 1, d: 2});
+
+    for (var i=0; i<100; i++) {
+        var o = arr[i];
+        var p1 = (i & 1) ? "x" : "y";
+        var p2 = (i % 3) ? "a" : "b";
+        o[p1] = i;
+        o[p2] = i;
+        assertEq((i & 1) ? o.x : o.y, i);
+        assertEq((i % 3) ? o.a : o.b, i);
+    }
+}
+addExpandoSlot();
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -17,16 +17,23 @@
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::Maybe;
 
 class AutoStubFrame;
 
+Address
+CacheRegisterAllocator::addressOf(MacroAssembler& masm, BaselineFrameSlot slot) const
+{
+    uint32_t offset = stackPushed_ + ICStackValueOffset + slot.slot() * sizeof(JS::Value);
+    return Address(masm.getStackPointer(), offset);
+}
+
 // BaselineCacheIRCompiler compiles CacheIR to BaselineIC native code.
 class MOZ_RAII BaselineCacheIRCompiler : public CacheIRCompiler
 {
     // Some Baseline IC stubs can be used in IonMonkey through SharedStubs.
     // Those stubs have different machine code, so we need to track whether
     // we're compiling for Baseline or Ion.
     ICStubEngine engine_;
 
@@ -1254,29 +1261,39 @@ BaselineCacheIRCompiler::init(CacheKind 
 {
     if (!allocator.init())
         return false;
 
     // Baseline ICs monitor values when needed, so returning doubles is fine.
     allowDoubleResult_.emplace(true);
 
     size_t numInputs = writer_.numInputOperands();
-    AllocatableGeneralRegisterSet available(ICStubCompiler::availableGeneralRegs(numInputs));
+
+    // Baseline passes the first 2 inputs in R0/R1, other Values are stored on
+    // the stack.
+    size_t numInputsInRegs = std::min(numInputs, size_t(2));
+    AllocatableGeneralRegisterSet available(ICStubCompiler::availableGeneralRegs(numInputsInRegs));
 
     switch (kind) {
       case CacheKind::GetProp:
         MOZ_ASSERT(numInputs == 1);
         allocator.initInputLocation(0, R0);
         break;
       case CacheKind::GetElem:
       case CacheKind::SetProp:
         MOZ_ASSERT(numInputs == 2);
         allocator.initInputLocation(0, R0);
         allocator.initInputLocation(1, R1);
         break;
+      case CacheKind::SetElem:
+        MOZ_ASSERT(numInputs == 3);
+        allocator.initInputLocation(0, R0);
+        allocator.initInputLocation(1, R1);
+        allocator.initInputLocation(2, BaselineFrameSlot(0));
+        break;
       case CacheKind::GetName:
         MOZ_ASSERT(numInputs == 1);
         allocator.initInputLocation(0, R0.scratchReg(), JSVAL_TYPE_OBJECT);
 #if defined(JS_NUNBOX32)
         // availableGeneralRegs can't know that GetName is only using
         // the payloadReg and not typeReg on x86.
         available.add(R0.typeReg());
 #endif
@@ -1313,16 +1330,17 @@ jit::AttachBaselineCacheIRStub(JSContext
     switch (kind) {
       case CacheKind::GetProp:
       case CacheKind::GetElem:
       case CacheKind::GetName:
         stubDataOffset = sizeof(ICCacheIR_Monitored);
         stubKind = CacheIRStubKind::Monitored;
         break;
       case CacheKind::SetProp:
+      case CacheKind::SetElem:
         stubDataOffset = sizeof(ICCacheIR_Updated);
         stubKind = CacheIRStubKind::Updated;
         break;
     }
 
     JitCompartment* jitCompartment = cx->compartment()->jitCompartment();
 
     // Check if we already have JitCode for this stub.
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1125,17 +1125,54 @@ DoSetElemFallback(JSContext* cx, Baselin
                op == JSOP_INITHIDDENELEM ||
                op == JSOP_INITELEM_ARRAY ||
                op == JSOP_INITELEM_INC);
 
     RootedObject obj(cx, ToObjectFromStack(cx, objv));
     if (!obj)
         return false;
 
+    bool isTemporarilyUnoptimizable = false;
+
+    bool attached = false;
+    if (stub->numOptimizedStubs() < ICSetElem_Fallback::MAX_OPTIMIZED_STUBS &&
+        !JitOptions.disableCacheIR)
+    {
+        SetPropIRGenerator gen(cx, pc, CacheKind::SetElem, &isTemporarilyUnoptimizable,
+                               objv, index, rhs);
+        if (gen.tryAttachStub()) {
+            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
+                                                        ICStubEngine::Baseline, frame->script(), stub);
+            if (newStub) {
+                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                attached = true;
+
+                if (gen.needUpdateStub()) {
+                    newStub->toCacheIR_Updated()->updateStubGroup() = gen.updateStubGroup();
+                    newStub->toCacheIR_Updated()->updateStubId() = gen.updateStubId();
+                }
+
+                if (gen.shouldNotePreliminaryObjectStub())
+                    newStub->toCacheIR_Updated()->notePreliminaryObject();
+                else if (gen.shouldUnlinkPreliminaryObjectStubs())
+                    StripPreliminaryObjectStubs(cx, stub);
+            }
+        }
+    }
+
     RootedShape oldShape(cx, obj->maybeShape());
+    RootedObjectGroup oldGroup(cx, JSObject::getGroup(cx, obj));
+    if (!oldGroup)
+        return false;
+
+    if (obj->is<UnboxedPlainObject>()) {
+        MOZ_ASSERT(!oldShape);
+        if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
+            oldShape = expando->lastProperty();
+    }
 
     // Check the old capacity
     uint32_t oldCapacity = 0;
     uint32_t oldInitLength = 0;
     if (index.isInt32() && index.toInt32() >= 0) {
         oldCapacity = GetAnyBoxedOrUnboxedCapacity(obj);
         oldInitLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
     }
@@ -1167,22 +1204,41 @@ DoSetElemFallback(JSContext* cx, Baselin
     // Overwrite the object on the stack (pushed for the decompiler) with the rhs.
     MOZ_ASSERT(stack[2] == objv);
     stack[2] = rhs;
 
     // Check if debug mode toggling made the stub invalid.
     if (stub.invalid())
         return true;
 
+    if (attached)
+        return true;
+
     if (stub->numOptimizedStubs() >= ICSetElem_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
         // But for now we just bail.
         return true;
     }
 
+    if (!JitOptions.disableCacheIR) {
+        SetPropIRGenerator gen(cx, pc, CacheKind::SetElem, &isTemporarilyUnoptimizable,
+                               objv, index, rhs);
+        if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
+            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
+                                                        ICStubEngine::Baseline, frame->script(), stub);
+            if (newStub) {
+                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                attached = true;
+                newStub->toCacheIR_Updated()->updateStubGroup() = gen.updateStubGroup();
+                newStub->toCacheIR_Updated()->updateStubId() = gen.updateStubId();
+                return true;
+            }
+        }
+    }
+
     // Try to generate new stubs.
     if (IsNativeOrUnboxedDenseElementAccess(obj, index) && !rhs.isMagic(JS_ELEMENTS_HOLE)) {
         bool addingCase;
         size_t protoDepth;
 
         if (CanOptimizeDenseOrUnboxedArraySetElem(obj, index.toInt32(),
                                                   oldShape, oldCapacity, oldInitLength,
                                                   &addingCase, &protoDepth))
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -1298,35 +1298,52 @@ GetPropIRGenerator::tryAttachProxyElemen
     // so we don't attach a new stub for every id.
     MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
     writer.callProxyGetByValueResult(objId, getElemKeyValueId());
     writer.typeMonitorResult();
     return true;
 }
 
 void
+IRGenerator::emitIdGuard(ValOperandId valId, jsid id)
+{
+    if (JSID_IS_SYMBOL(id)) {
+        SymbolOperandId symId = writer.guardIsSymbol(valId);
+        writer.guardSpecificSymbol(symId, JSID_TO_SYMBOL(id));
+    } else {
+        MOZ_ASSERT(JSID_IS_ATOM(id));
+        StringOperandId strId = writer.guardIsString(valId);
+        writer.guardSpecificAtom(strId, JSID_TO_ATOM(id));
+    }
+}
+
+void
 GetPropIRGenerator::maybeEmitIdGuard(jsid id)
 {
     if (cacheKind_ == CacheKind::GetProp) {
         // Constant PropertyName, no guards necessary.
         MOZ_ASSERT(&idVal_.toString()->asAtom() == JSID_TO_ATOM(id));
         return;
     }
 
     MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
+    emitIdGuard(getElemKeyValueId(), id);
+}
 
-    ValOperandId valId = getElemKeyValueId();
-    if (JSID_IS_SYMBOL(id)) {
-        SymbolOperandId symId = writer.guardIsSymbol(valId);
-        writer.guardSpecificSymbol(symId, JSID_TO_SYMBOL(id));
-    } else {
-        MOZ_ASSERT(JSID_IS_ATOM(id));
-        StringOperandId strId = writer.guardIsString(valId);
-        writer.guardSpecificAtom(strId, JSID_TO_ATOM(id));
+void
+SetPropIRGenerator::maybeEmitIdGuard(jsid id)
+{
+    if (cacheKind_ == CacheKind::SetProp) {
+        // Constant PropertyName, no guards necessary.
+        MOZ_ASSERT(&idVal_.toString()->asAtom() == JSID_TO_ATOM(id));
+        return;
     }
+
+    MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
+    emitIdGuard(setElemKeyValueId(), id);
 }
 
 GetNameIRGenerator::GetNameIRGenerator(JSContext* cx, jsbytecode* pc, HandleScript script,
                                        HandleObject env, HandlePropertyName name)
   : IRGenerator(cx, pc, CacheKind::GetName),
     script_(script),
     env_(env),
     name_(name)
@@ -1598,32 +1615,40 @@ SetPropIRGenerator::SetPropIRGenerator(J
     needUpdateStub_(false)
 {}
 
 bool
 SetPropIRGenerator::tryAttachStub()
 {
     AutoAssertNoPendingException aanpe(cx_);
 
-    ValOperandId lhsValId(writer.setInputOperandId(0));
-    ValOperandId rhsValId(writer.setInputOperandId(1));
+    ValOperandId objValId(writer.setInputOperandId(0));
+    ValOperandId rhsValId;
+    if (cacheKind_ == CacheKind::SetProp) {
+        rhsValId = ValOperandId(writer.setInputOperandId(1));
+    } else {
+        MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
+        MOZ_ASSERT(setElemKeyValueId().id() == 1);
+        writer.setInputOperandId(1);
+        rhsValId = ValOperandId(writer.setInputOperandId(2));
+    }
 
     RootedId id(cx_);
     bool nameOrSymbol;
     if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
         cx_->clearPendingException();
         return false;
     }
 
     if (lhsVal_.isObject()) {
         RootedObject obj(cx_, &lhsVal_.toObject());
         if (obj->watched())
             return false;
 
-        ObjOperandId objId = writer.guardIsObject(lhsValId);
+        ObjOperandId objId = writer.guardIsObject(objValId);
         if (nameOrSymbol) {
             if (tryAttachNativeSetSlot(obj, objId, id, rhsValId))
                 return true;
             if (tryAttachUnboxedExpandoSetSlot(obj, objId, id, rhsValId))
                 return true;
             if (tryAttachUnboxedProperty(obj, objId, id, rhsValId))
                 return true;
             if (tryAttachSetter(obj, objId, id, rhsValId))
@@ -1684,16 +1709,18 @@ SetPropIRGenerator::tryAttachNativeSetSl
     // overwritten. Don't attach a stub in this case, so that we don't
     // execute another write to the property without TI seeing that write.
     EnsureTrackPropertyTypes(cx_, obj, id);
     if (!PropertyHasBeenMarkedNonConstant(obj, id)) {
         *isTemporarilyUnoptimizable_ = true;
         return false;
     }
 
+    maybeEmitIdGuard(id);
+
     // For Baseline, we have to guard on both the shape and group, because the
     // type update IC applies to a single group. When we port the Ion IC, we can
     // do a bit better and avoid the group guard if we don't have to guard on
     // the property types.
     NativeObject* nobj = &obj->as<NativeObject>();
     writer.guardGroup(objId, nobj->group());
     writer.guardShape(objId, nobj->lastProperty());
 
@@ -1717,16 +1744,17 @@ SetPropIRGenerator::tryAttachUnboxedExpa
     UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
     if (!expando)
         return false;
 
     Shape* propShape = LookupShapeForSetSlot(expando, id);
     if (!propShape)
         return false;
 
+    maybeEmitIdGuard(id);
     writer.guardGroup(objId, obj->group());
     ObjOperandId expandoId = writer.guardAndLoadUnboxedExpando(objId);
     writer.guardShape(expandoId, expando->lastProperty());
 
     // Property types must be added to the unboxed object's group, not the
     // expando's group (it has unknown properties).
     setUpdateStubInfo(obj->group(), id);
     EmitStoreSlotAndReturn(writer, expandoId, expando, propShape, rhsId);
@@ -1750,16 +1778,17 @@ SetPropIRGenerator::tryAttachUnboxedProp
 {
     if (!obj->is<UnboxedPlainObject>() || !cx_->runtime()->jitSupportsFloatingPoint)
         return false;
 
     const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
     if (!property)
         return false;
 
+    maybeEmitIdGuard(id);
     writer.guardGroup(objId, obj->group());
     EmitGuardUnboxedPropertyType(writer, property->type, rhsId);
     writer.storeUnboxedProperty(objId, property->type,
                                 UnboxedPlainObject::offsetOfData() + property->offset,
                                 rhsId);
     writer.returnFromIC();
 
     setUpdateStubInfo(obj->group(), id);
@@ -1787,16 +1816,17 @@ SetPropIRGenerator::tryAttachTypedObject
 
     TypeDescr* fieldDescr = &structDescr->fieldDescr(fieldIndex);
     if (!fieldDescr->is<SimpleTypeDescr>())
         return false;
 
     uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
     TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
 
+    maybeEmitIdGuard(id);
     writer.guardNoDetachedTypedObjects();
     writer.guardShape(objId, obj->as<TypedObject>().shape());
     writer.guardGroup(objId, obj->group());
 
     setUpdateStubInfo(obj->group(), id);
 
     // Scalar types can always be stored without a type update stub.
     if (fieldDescr->is<ScalarTypeDescr>()) {
@@ -1844,31 +1874,38 @@ EmitCallSetterResultNoGuards(CacheIRWrit
     writer.callScriptedSetter(objId, target, rhsId);
     writer.returnFromIC();
 }
 
 bool
 SetPropIRGenerator::tryAttachSetter(HandleObject obj, ObjOperandId objId, HandleId id,
                                     ValOperandId rhsId)
 {
+    // Don't attach a setter stub for ops like JSOP_INITELEM.
+    if (IsPropertyInitOp(JSOp(*pc_)))
+        return false;
+    MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
+
     PropertyResult prop;
     JSObject* holder;
     if (!LookupPropertyPure(cx_, obj, id, &holder, &prop))
         return false;
 
     if (prop.isNonNativeProperty())
         return false;
 
     Shape* shape = prop.maybeShape();
     if (!IsCacheableSetPropCallScripted(obj, holder, shape, isTemporarilyUnoptimizable_) &&
         !IsCacheableSetPropCallNative(obj, holder, shape))
     {
         return false;
     }
 
+    maybeEmitIdGuard(id);
+
     Maybe<ObjOperandId> expandoId;
     TestMatchingReceiver(writer, obj, shape, objId, &expandoId);
 
     if (obj != holder) {
         GeneratePrototypeGuards(writer, obj, holder, objId);
 
         // Guard on the holder's shape.
         ObjOperandId holderId = writer.loadObject(holder);
@@ -1881,29 +1918,38 @@ SetPropIRGenerator::tryAttachSetter(Hand
 
 bool
 SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj, ObjOperandId objId, HandleId id,
                                             ValOperandId rhsId)
 {
     if (!obj->is<ArrayObject>() || !JSID_IS_ATOM(id, cx_->names().length))
         return false;
 
+    maybeEmitIdGuard(id);
     writer.guardClass(objId, GuardClassKind::Array);
     writer.callSetArrayLength(objId, IsStrictSetPC(pc_), rhsId);
     writer.returnFromIC();
     return true;
 }
 
 bool
 SetPropIRGenerator::tryAttachAddSlotStub(HandleObjectGroup oldGroup, HandleShape oldShape)
 {
     AutoAssertNoPendingException aanpe(cx_);
 
-    ValOperandId lhsValId(writer.setInputOperandId(0));
-    ValOperandId rhsValId(writer.setInputOperandId(1));
+    ValOperandId objValId(writer.setInputOperandId(0));
+    ValOperandId rhsValId;
+    if (cacheKind_ == CacheKind::SetProp) {
+        rhsValId = ValOperandId(writer.setInputOperandId(1));
+    } else {
+        MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
+        MOZ_ASSERT(setElemKeyValueId().id() == 1);
+        writer.setInputOperandId(1);
+        rhsValId = ValOperandId(writer.setInputOperandId(2));
+    }
 
     RootedId id(cx_);
     bool nameOrSymbol;
     if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
         cx_->clearPendingException();
         return false;
     }
 
@@ -1987,17 +2033,19 @@ SetPropIRGenerator::tryAttachAddSlotStub
     // Don't attach if we are adding a property to an object which the new
     // script properties analysis hasn't been performed for yet, as there
     // may be a shape change required here afterwards.
     if (oldGroup->newScript() && !oldGroup->newScript()->analyzed()) {
         *isTemporarilyUnoptimizable_ = true;
         return false;
     }
 
-    ObjOperandId objId = writer.guardIsObject(lhsValId);
+    ObjOperandId objId = writer.guardIsObject(objValId);
+    maybeEmitIdGuard(id);
+
     writer.guardGroup(objId, oldGroup);
 
     // Shape guard the holder.
     ObjOperandId holderId = objId;
     if (!obj->isNative()) {
         MOZ_ASSERT(obj->as<UnboxedPlainObject>().maybeExpando());
         holderId = writer.guardAndLoadUnboxedExpando(objId);
     }
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -130,16 +130,17 @@ class TypedOperandId : public OperandId
 };
 
 enum class CacheKind : uint8_t
 {
     GetProp,
     GetElem,
     GetName,
     SetProp,
+    SetElem,
 };
 
 #define CACHE_IR_OPS(_)                   \
     _(GuardIsObject)                      \
     _(GuardIsObjectOrNull)                \
     _(GuardIsString)                      \
     _(GuardIsSymbol)                      \
     _(GuardIsInt32Index)                  \
@@ -836,16 +837,18 @@ class MOZ_RAII IRGenerator
     CacheKind cacheKind_;
 
     IRGenerator(const IRGenerator&) = delete;
     IRGenerator& operator=(const IRGenerator&) = delete;
 
     bool maybeGuardInt32Index(const Value& index, ValOperandId indexId,
                               uint32_t* int32Index, Int32OperandId* int32IndexId);
 
+    void emitIdGuard(ValOperandId valId, jsid id);
+
   public:
     explicit IRGenerator(JSContext* cx, jsbytecode* pc, CacheKind cacheKind);
 
     const CacheIRWriter& writerRef() const { return writer; }
     CacheKind cacheKind() const { return cacheKind_; }
 };
 
 enum class CanAttachGetter { Yes, No };
@@ -962,16 +965,31 @@ class MOZ_RAII SetPropIRGenerator : publ
 
     void setUpdateStubInfo(ObjectGroup* group, jsid id) {
         MOZ_ASSERT(!needUpdateStub_);
         needUpdateStub_ = true;
         updateStubGroup_ = group;
         updateStubId_ = id;
     }
 
+    ValOperandId setElemKeyValueId() const {
+        MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
+        return ValOperandId(1);
+    }
+    ValOperandId rhsValueId() const {
+        if (cacheKind_ == CacheKind::SetProp)
+            return ValOperandId(1);
+        MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
+        return ValOperandId(2);
+    }
+
+    // If this is a SetElem cache, emit instructions to guard the incoming Value
+    // matches |id|.
+    void maybeEmitIdGuard(jsid id);
+
     bool tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
                                  ValOperandId rhsId);
     bool tryAttachUnboxedExpandoSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
                                         ValOperandId rhsId);
     bool tryAttachUnboxedProperty(HandleObject obj, ObjOperandId objId, HandleId id,
                                   ValOperandId rhsId);
     bool tryAttachTypedObjectProperty(HandleObject obj, ObjOperandId objId, HandleId id,
                                       ValOperandId rhsId);
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -26,16 +26,24 @@ CacheRegisterAllocator::useValueRegister
         return loc.valueReg();
 
       case OperandLocation::ValueStack: {
         ValueOperand reg = allocateValueRegister(masm);
         popValue(masm, &loc, reg);
         return reg;
       }
 
+      case OperandLocation::BaselineFrame: {
+        ValueOperand reg = allocateValueRegister(masm);
+        Address addr = addressOf(masm, loc.baselineFrameSlot());
+        masm.loadValue(addr, reg);
+        loc.setValueReg(reg);
+        return reg;
+      }
+
       case OperandLocation::Constant: {
         ValueOperand reg = allocateValueRegister(masm);
         masm.moveValue(loc.constant(), reg);
         loc.setValueReg(reg);
         return reg;
       }
 
       case OperandLocation::PayloadReg: {
@@ -72,16 +80,21 @@ CacheRegisterAllocator::useFixedValueReg
       case OperandLocation::ValueReg:
         masm.moveValue(loc.valueReg(), reg);
         MOZ_ASSERT(!currentOpRegs_.aliases(loc.valueReg()), "Register shouldn't be in use");
         availableRegs_.add(loc.valueReg());
         break;
       case OperandLocation::ValueStack:
         popValue(masm, &loc, reg);
         break;
+      case OperandLocation::BaselineFrame: {
+        Address addr = addressOf(masm, loc.baselineFrameSlot());
+        masm.loadValue(addr, reg);
+        break;
+      }
       case OperandLocation::Constant:
         masm.moveValue(loc.constant(), reg);
         break;
       case OperandLocation::PayloadReg:
         masm.tagValue(loc.payloadType(), loc.payloadReg(), reg);
         MOZ_ASSERT(!currentOpRegs_.has(loc.payloadReg()), "Register shouldn't be in use");
         availableRegs_.add(loc.payloadReg());
         break;
@@ -138,16 +151,24 @@ CacheRegisterAllocator::useRegister(Macr
             MOZ_ASSERT(loc.valueStack() < stackPushed_);
             masm.unboxObject(Address(masm.getStackPointer(), stackPushed_ - loc.valueStack()),
                              reg);
         }
         loc.setPayloadReg(reg, typedId.type());
         return reg;
       }
 
+      case OperandLocation::BaselineFrame: {
+        Register reg = allocateRegister(masm);
+        Address addr = addressOf(masm, loc.baselineFrameSlot());
+        masm.unboxNonDouble(addr, reg);
+        loc.setPayloadReg(reg, typedId.type());
+        return reg;
+      };
+
       case OperandLocation::Constant: {
         Value v = loc.constant();
         Register reg = allocateRegister(masm);
         if (v.isString())
             masm.movePtr(ImmGCPtr(v.toString()), reg);
         else if (v.isSymbol())
             masm.movePtr(ImmGCPtr(v.toSymbol()), reg);
         else
@@ -201,16 +222,17 @@ CacheRegisterAllocator::freeDeadOperandR
             availableRegs_.add(loc.payloadReg());
             break;
           case OperandLocation::ValueReg:
             availableRegs_.add(loc.valueReg());
             break;
           case OperandLocation::Uninitialized:
           case OperandLocation::PayloadStack:
           case OperandLocation::ValueStack:
+          case OperandLocation::BaselineFrame:
           case OperandLocation::Constant:
             break;
         }
         loc.setUninitialized();
     }
 }
 
 void
@@ -379,16 +401,17 @@ CacheRegisterAllocator::inputRegisterSet
           case OperandLocation::PayloadReg:
             result.add(loc.payloadReg());
             continue;
           case OperandLocation::ValueReg:
             result.add(loc.valueReg());
             continue;
           case OperandLocation::PayloadStack:
           case OperandLocation::ValueStack:
+          case OperandLocation::BaselineFrame:
           case OperandLocation::Constant:
             continue;
           case OperandLocation::Uninitialized:
             break;
         }
         MOZ_CRASH("Invalid kind");
     }
 
@@ -398,16 +421,17 @@ CacheRegisterAllocator::inputRegisterSet
 JSValueType
 CacheRegisterAllocator::knownType(ValOperandId val) const
 {
     const OperandLocation& loc = operandLocations_[val.id()];
 
     switch (loc.kind()) {
       case OperandLocation::ValueReg:
       case OperandLocation::ValueStack:
+      case OperandLocation::BaselineFrame:
         return JSVAL_TYPE_UNKNOWN;
 
       case OperandLocation::PayloadStack:
       case OperandLocation::PayloadReg:
         return loc.payloadType();
 
       case OperandLocation::Constant:
         return loc.constant().isDouble()
@@ -533,16 +557,17 @@ OperandLocation::aliasesReg(const Operan
 
     switch (other.kind_) {
       case PayloadReg:
         return aliasesReg(other.payloadReg());
       case ValueReg:
         return aliasesReg(other.valueReg());
       case PayloadStack:
       case ValueStack:
+      case BaselineFrame:
       case Constant:
         return false;
       case Uninitialized:
         break;
     }
 
     MOZ_CRASH("Invalid kind");
 }
@@ -584,16 +609,17 @@ CacheRegisterAllocator::restoreInputStat
                 popPayload(masm, &cur, scratch);
                 masm.tagValue(cur.payloadType(), scratch, dest.valueReg());
                 continue;
               }
               case OperandLocation::ValueStack:
                 popValue(masm, &cur, dest.valueReg());
                 continue;
               case OperandLocation::Constant:
+              case OperandLocation::BaselineFrame:
               case OperandLocation::Uninitialized:
                 break;
             }
         } else if (dest.kind() == OperandLocation::PayloadReg) {
             // We have to restore a payload register.
             switch (cur.kind()) {
               case OperandLocation::ValueReg:
                 MOZ_ASSERT(dest.payloadType() != JSVAL_TYPE_DOUBLE);
@@ -611,20 +637,23 @@ CacheRegisterAllocator::restoreInputStat
               case OperandLocation::ValueStack:
                 MOZ_ASSERT(stackPushed_ >= sizeof(js::Value));
                 MOZ_ASSERT(cur.valueStack() <= stackPushed_);
                 MOZ_ASSERT(dest.payloadType() != JSVAL_TYPE_DOUBLE);
                 masm.unboxNonDouble(Address(masm.getStackPointer(), stackPushed_ - cur.valueStack()),
                                     dest.payloadReg());
                 continue;
               case OperandLocation::Constant:
+              case OperandLocation::BaselineFrame:
               case OperandLocation::Uninitialized:
                 break;
             }
-        } else if (dest.kind() == OperandLocation::Constant) {
+        } else if (dest.kind() == OperandLocation::Constant ||
+                   dest.kind() == OperandLocation::BaselineFrame)
+        {
             // Nothing to do.
             continue;
         }
 
         MOZ_CRASH("Invalid kind");
     }
 
     for (const SpilledRegister& spill : spilledRegs_) {
@@ -944,16 +973,18 @@ OperandLocation::operator==(const Operan
       case PayloadReg:
         return payloadReg() == other.payloadReg() && payloadType() == other.payloadType();
       case ValueReg:
         return valueReg() == other.valueReg();
       case PayloadStack:
         return payloadStack() == other.payloadStack() && payloadType() == other.payloadType();
       case ValueStack:
         return valueStack() == other.valueStack();
+      case BaselineFrame:
+        return baselineFrameSlot() == other.baselineFrameSlot();
       case Constant:
         return constant() == other.constant();
     }
 
     MOZ_CRASH("Invalid OperandLocation kind");
 }
 
 AutoOutputRegister::AutoOutputRegister(CacheIRCompiler& compiler)
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -42,27 +42,43 @@ namespace jit {
     _(LoadStringLengthResult)             \
     _(LoadStringCharResult)               \
     _(LoadArgumentsObjectArgResult)       \
     _(LoadDenseElementResult)             \
     _(LoadDenseElementHoleResult)         \
     _(LoadUnboxedArrayElementResult)      \
     _(LoadTypedElementResult)
 
+// Represents a Value on the Baseline frame's expression stack. Slot 0 is the
+// value on top of the stack (the most recently pushed value), slot 1 is the
+// value pushed before that, etc.
+class BaselineFrameSlot
+{
+    uint32_t slot_;
+
+  public:
+    explicit BaselineFrameSlot(uint32_t slot) : slot_(slot) {}
+    uint32_t slot() const { return slot_; }
+
+    bool operator==(const BaselineFrameSlot& other) const { return slot_ == other.slot_; }
+    bool operator!=(const BaselineFrameSlot& other) const { return slot_ != other.slot_; }
+};
+
 // OperandLocation represents the location of an OperandId. The operand is
 // either in a register or on the stack, and is either boxed or unboxed.
 class OperandLocation
 {
   public:
     enum Kind {
         Uninitialized = 0,
         PayloadReg,
         ValueReg,
         PayloadStack,
         ValueStack,
+        BaselineFrame,
         Constant,
     };
 
   private:
     Kind kind_;
 
     union Data {
         struct {
@@ -70,16 +86,17 @@ class OperandLocation
             JSValueType type;
         } payloadReg;
         ValueOperand valueReg;
         struct {
             uint32_t stackPushed;
             JSValueType type;
         } payloadStack;
         uint32_t valueStackPushed;
+        BaselineFrameSlot baselineFrameSlot;
         Value constant;
 
         Data() : valueStackPushed(0) {}
     };
     Data data_;
 
   public:
     OperandLocation() : kind_(Uninitialized) {}
@@ -111,16 +128,20 @@ class OperandLocation
             return data_.payloadReg.type;
         MOZ_ASSERT(kind_ == PayloadStack);
         return data_.payloadStack.type;
     }
     Value constant() const {
         MOZ_ASSERT(kind_ == Constant);
         return data_.constant;
     }
+    BaselineFrameSlot baselineFrameSlot() const {
+        MOZ_ASSERT(kind_ == BaselineFrame);
+        return data_.baselineFrameSlot;
+    }
 
     void setPayloadReg(Register reg, JSValueType type) {
         kind_ = PayloadReg;
         data_.payloadReg.reg = reg;
         data_.payloadReg.type = type;
     }
     void setValueReg(ValueOperand reg) {
         kind_ = ValueReg;
@@ -134,16 +155,20 @@ class OperandLocation
     void setValueStack(uint32_t stackPushed) {
         kind_ = ValueStack;
         data_.valueStackPushed = stackPushed;
     }
     void setConstant(const Value& v) {
         kind_ = Constant;
         data_.constant = v;
     }
+    void setBaselineFrame(BaselineFrameSlot slot) {
+        kind_ = BaselineFrame;
+        data_.baselineFrameSlot = slot;
+    }
 
     bool isInRegister() const { return kind_ == PayloadReg || kind_ == ValueReg; }
     bool isOnStack() const { return kind_ == PayloadStack || kind_ == ValueStack; }
 
     size_t stackPushed() const {
         if (kind_ == PayloadStack)
             return data_.payloadStack.stackPushed;
         MOZ_ASSERT(kind_ == ValueStack);
@@ -281,16 +306,20 @@ class MOZ_RAII CacheRegisterAllocator
     void initInputLocation(size_t i, Register reg, JSValueType type) {
         origInputLocations_[i].setPayloadReg(reg, type);
         operandLocations_[i].setPayloadReg(reg, type);
     }
     void initInputLocation(size_t i, const Value& v) {
         origInputLocations_[i].setConstant(v);
         operandLocations_[i].setConstant(v);
     }
+    void initInputLocation(size_t i, BaselineFrameSlot slot) {
+        origInputLocations_[i].setBaselineFrame(slot);
+        operandLocations_[i].setBaselineFrame(slot);
+    }
 
     void initInputLocation(size_t i, const TypedOrValueRegister& reg);
     void initInputLocation(size_t i, const ConstantOrRegister& value);
 
     const SpilledRegisterVector& spilledRegs() const { return spilledRegs_; }
 
     MOZ_MUST_USE bool setSpilledRegs(const SpilledRegisterVector& regs) {
         spilledRegs_.clear();
@@ -333,16 +362,18 @@ class MOZ_RAII CacheRegisterAllocator
         releaseRegister(reg.valueReg());
 #endif
     }
 
     // Removes spilled values from the native stack. This should only be
     // called after all registers have been allocated.
     void discardStack(MacroAssembler& masm);
 
+    Address addressOf(MacroAssembler& masm, BaselineFrameSlot slot) const;
+
     // Returns the register for the given operand. If the operand is currently
     // not in a register, it will load it into one.
     ValueOperand useValueRegister(MacroAssembler& masm, ValOperandId val);
     ValueOperand useFixedValueRegister(MacroAssembler& masm, ValOperandId valId, ValueOperand reg);
     Register useRegister(MacroAssembler& masm, TypedOperandId typedId);
 
     // Allocates an output register for the given operand.
     Register defineRegister(MacroAssembler& masm, TypedOperandId typedId);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -253,16 +253,17 @@ CodeGenerator::visitOutOfLineICFallback(
         StoreValueTo(getPropIC->output()).generate(this);
         restoreLiveIgnore(lir, StoreValueTo(getPropIC->output()).clobbered());
 
         masm.jump(ool->rejoin());
         return;
       }
       case CacheKind::GetName:
       case CacheKind::SetProp:
+      case CacheKind::SetElem:
         MOZ_CRASH("Baseline-specific for now");
     }
     MOZ_CRASH();
 }
 
 StringObject*
 MNewStringObject::templateObj() const
 {
--- a/js/src/jit/ExecutableAllocator.h
+++ b/js/src/jit/ExecutableAllocator.h
@@ -31,16 +31,19 @@
 #include "mozilla/Maybe.h"
 #include "mozilla/XorShift128PlusRNG.h"
 
 #include <limits>
 #include <stddef.h> // for ptrdiff_t
 
 #include "jsalloc.h"
 
+#ifdef JS_CODEGEN_ARM
+#include "jit/arm/Architecture-arm.h"
+#endif
 #include "jit/arm/Simulator-arm.h"
 #if defined(JS_CODEGEN_ARM64)
 #include "jit/arm64/vixl/Cpu-vixl.h"
 #endif
 #include "jit/mips32/Simulator-mips32.h"
 #include "jit/mips64/Simulator-mips64.h"
 #include "jit/ProcessExecutableMemory.h"
 #include "js/GCAPI.h"
@@ -259,28 +262,45 @@ class ExecutableAllocator
 #elif defined(JS_CODEGEN_ARM) && defined(XP_IOS)
     static void cacheFlush(void* code, size_t size)
     {
         sys_icache_invalidate(code, size);
     }
 #elif defined(JS_CODEGEN_ARM) && (defined(__linux__) || defined(ANDROID)) && defined(__GNUC__)
     static void cacheFlush(void* code, size_t size)
     {
+        void* end = (void*)(reinterpret_cast<char*>(code) + size);
         asm volatile (
             "push    {r7}\n"
             "mov     r0, %0\n"
             "mov     r1, %1\n"
             "mov     r7, #0xf0000\n"
             "add     r7, r7, #0x2\n"
             "mov     r2, #0x0\n"
             "svc     0x0\n"
             "pop     {r7}\n"
             :
-            : "r" (code), "r" (reinterpret_cast<char*>(code) + size)
+            : "r" (code), "r" (end)
             : "r0", "r1", "r2");
+
+        if (ForceDoubleCacheFlush()) {
+            void* start = (void*)((uintptr_t)code + 1);
+            asm volatile (
+                "push    {r7}\n"
+                "mov     r0, %0\n"
+                "mov     r1, %1\n"
+                "mov     r7, #0xf0000\n"
+                "add     r7, r7, #0x2\n"
+                "mov     r2, #0x0\n"
+                "svc     0x0\n"
+                "pop     {r7}\n"
+                :
+                : "r" (start), "r" (end)
+                : "r0", "r1", "r2");
+        }
     }
 #elif defined(JS_CODEGEN_ARM64) && (defined(__linux__) || defined(ANDROID)) && defined(__GNUC__)
     static void cacheFlush(void* code, size_t size)
     {
         vixl::CPU::EnsureIAndDCacheCoherency(code, size);
     }
 #elif defined(__sparc__)
     static void cacheFlush(void* code, size_t size)
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -9350,16 +9350,19 @@ IonBuilder::jsop_rest()
     MDefinition *array = current->peek(-1);
     MElements* elements = MElements::New(alloc(), array);
     current->add(elements);
 
     // Unroll the argument copy loop. We don't need to do any bounds or hole
     // checking here.
     MConstant* index = nullptr;
     for (unsigned i = numFormals; i < numActuals; i++) {
+        if (!alloc().ensureBallast())
+            return abort(AbortReason::Alloc);
+
         index = MConstant::New(alloc(), Int32Value(i - numFormals));
         current->add(index);
 
         MDefinition* arg = inlineCallInfo_->argv()[i];
         MStoreElement* store = MStoreElement::New(alloc(), elements, index, arg,
                                                   /* needsHoleCheck = */ false);
         current->add(store);
 
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -38,16 +38,17 @@ IonIC::scratchRegisterForEntryJump()
         Register temp = asGetPropertyIC()->maybeTemp();
         if (temp != InvalidReg)
             return temp;
         TypedOrValueRegister output = asGetPropertyIC()->output();
         return output.hasValue() ? output.valueReg().scratchReg() : output.typedReg().gpr();
       }
       case CacheKind::GetName:
       case CacheKind::SetProp:
+      case CacheKind::SetElem:
         MOZ_CRASH("Baseline-specific for now");
     }
 
     MOZ_CRASH("Invalid kind");
 }
 
 void
 IonIC::reset(Zone* zone)
--- a/js/src/jit/arm/Architecture-arm.cpp
+++ b/js/src/jit/arm/Architecture-arm.cpp
@@ -129,16 +129,23 @@ CanonicalizeARMHwCapFlags(uint32_t flags
 
     // Older kernels do not implement the HWCAP_VFPD32 flag.
     if ((flags & HWCAP_VFPv3) && !(flags & HWCAP_VFPv3D16))
         flags |= HWCAP_VFPD32;
 
     return flags;
 }
 
+volatile bool forceDoubleCacheFlush = false;
+
+bool
+ForceDoubleCacheFlush() {
+    return forceDoubleCacheFlush;
+}
+
 // The override flags parsed from the ARMHWCAP environment variable or from the
 // --arm-hwcap js shell argument.
 volatile uint32_t armHwCapFlags = HWCAP_UNINITIALIZED;
 
 bool
 ParseARMHwCapFlags(const char* armHwCap)
 {
     uint32_t flags = 0;
@@ -198,50 +205,58 @@ InitARMFlags()
 #ifdef JS_SIMULATOR_ARM
     // HWCAP_FIXUP_FAULT is on by default even if HWCAP_ALIGNMENT_FAULT is
     // not on by default, because some memory access instructions always fault.
     // Notably, this is true for floating point accesses.
     flags = HWCAP_ARMv7 | HWCAP_VFP | HWCAP_VFPv3 | HWCAP_VFPv4 | HWCAP_NEON | HWCAP_IDIVA
           | HWCAP_FIXUP_FAULT;
 #else
 
-#if defined(__linux__)
+#if defined(__linux__) || defined(ANDROID)
     // This includes Android and B2G.
     bool readAuxv = false;
     int fd = open("/proc/self/auxv", O_RDONLY);
     if (fd > 0) {
         struct { uint32_t a_type; uint32_t a_val; } aux;
         while (read(fd, &aux, sizeof(aux))) {
             if (aux.a_type == AT_HWCAP) {
                 flags = aux.a_val;
                 readAuxv = true;
                 break;
             }
         }
         close(fd);
     }
 
-    if (!readAuxv) {
+    FILE* fp = fopen("/proc/cpuinfo", "r");
+    if (fp) {
+        char buf[1024];
+        memset(buf, 0, sizeof(buf));
+        size_t len = fread(buf, sizeof(char), sizeof(buf) - 1, fp);
+        fclose(fp);
+        buf[len] = '\0';
+
         // Read the cpuinfo Features if the auxv is not available.
-        FILE* fp = fopen("/proc/cpuinfo", "r");
-        if (fp) {
-            char buf[1024];
-            memset(buf, 0, sizeof(buf));
-            size_t len = fread(buf, sizeof(char), sizeof(buf) - 1, fp);
-            fclose(fp);
-            buf[len] = '\0';
+        if (!readAuxv) {
             char* featureList = strstr(buf, "Features");
             if (featureList) {
                 if (char* featuresEnd = strstr(featureList, "\n"))
                     *featuresEnd = '\0';
                 flags = ParseARMCpuFeatures(featureList + 8);
             }
             if (strstr(buf, "ARMv7"))
                 flags |= HWCAP_ARMv7;
         }
+
+        // The exynos7420 cpu (EU galaxy S6 (Note)) has a bug where sometimes
+        // flushing doesn't invalidate the instruction cache. As a result we force
+        // it by calling the cacheFlush twice on different start addresses.
+        char* exynos7420 = strstr(buf, "Exynos7420");
+        if (exynos7420)
+            forceDoubleCacheFlush = true;
     }
 #endif
 
     // If compiled to use specialized features then these features can be
     // assumed to be present otherwise the compiler would fail to run.
 
 #ifdef JS_CODEGEN_ARM_HARDFP
     // Compiled to use the hardfp ABI.
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.h
@@ -772,16 +772,18 @@ static inline bool UseHardFpABI()
 #if defined(JS_CODEGEN_ARM_HARDFP)
     return true;
 #else
     return false;
 #endif
 }
 #endif
 
+bool ForceDoubleCacheFlush();
+
 // In order to handle SoftFp ABI calls, we need to be able to express that we
 // have ABIArg which are represented by pair of general purpose registers.
 #define JS_CODEGEN_REGISTER_PAIR 1
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_Architecture_arm_h */
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -57,18 +57,19 @@ enum {
     JOF_ATOMOBJECT      = 19,       /* uint16_t constant index + object index */
     JOF_SCOPE           = 20,       /* unsigned 32-bit scope index */
     JOF_ENVCOORD        = 21,       /* embedded ScopeCoordinate immediate */
     JOF_TYPEMASK        = 0x001f,   /* mask for above immediate types */
 
     JOF_NAME            = 1 << 5,   /* name operation */
     JOF_PROP            = 2 << 5,   /* obj.prop operation */
     JOF_ELEM            = 3 << 5,   /* obj[index] operation */
-    JOF_MODEMASK        = 7 << 5,   /* mask for above addressing modes */
-    JOF_SET             = 1 << 8,   /* set (i.e., assignment) operation */
+    JOF_MODEMASK        = 3 << 5,   /* mask for above addressing modes */
+    JOF_PROPSET         = 1 << 7,   /* property/element/name set operation */
+    JOF_PROPINIT        = 1 << 8,   /* property/element/name init operation */
     /* 1 << 9 is unused */
     /* 1 << 10 is unused */
     /* 1 << 11 is unused */
     /* 1 << 12 is unused */
     /* 1 << 13 is unused */
     JOF_DETECTING       = 1 << 14,  /* object detection for warning-quelling */
     /* 1 << 15 is unused */
     JOF_LEFTASSOC       = 1 << 16,  /* left-associative operator */
@@ -689,16 +690,28 @@ IsAliasedVarOp(JSOp op)
 
 inline bool
 IsGlobalOp(JSOp op)
 {
     return CodeSpec[op].format & JOF_GNAME;
 }
 
 inline bool
+IsPropertySetOp(JSOp op)
+{
+    return CodeSpec[op].format & JOF_PROPSET;
+}
+
+inline bool
+IsPropertyInitOp(JSOp op)
+{
+    return CodeSpec[op].format & JOF_PROPINIT;
+}
+
+inline bool
 IsEqualityOp(JSOp op)
 {
     return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
 }
 
 inline bool
 IsCheckStrictOp(JSOp op)
 {
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -461,27 +461,27 @@ 1234567890123456789012345678901234567890
      * Pops the top two values on the stack as 'val' and 'obj', and performs
      * 'obj.prop = val', pushing 'val' back onto the stack. Throws a TypeError
      * if the set-operation failed (per strict mode semantics).
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t nameIndex
      *   Stack: obj, val => val
      */ \
-    macro(JSOP_STRICTSETPROP,   48, "strict-setprop",    NULL,         5,  2,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING|JOF_CHECKSTRICT) \
+    macro(JSOP_STRICTSETPROP,   48, "strict-setprop",    NULL,         5,  2,  1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSTRICT) \
     /*
      * Pops a scope and value from the stack, assigns value to the given name,
      * and pushes the value back on the stack. If the set failed, then throw
      * a TypeError, per usual strict mode semantics.
      *   Category: Variables and Scopes
      *   Type: Variables
      *   Operands: uint32_t nameIndex
      *   Stack: scope, val => val
      */ \
-    macro(JSOP_STRICTSETNAME,   49, "strict-setname",    NULL,         5,  2,  1,  JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_CHECKSTRICT) \
+    macro(JSOP_STRICTSETNAME,   49, "strict-setname",    NULL,         5,  2,  1,  JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSTRICT) \
     /*
      * spreadcall variant of JSOP_EVAL
      *
      * Invokes 'eval' with 'args' and pushes the return value onto the stack.
      *
      * If 'eval' in global scope is not original one, invokes the function
      * with 'this' and 'args', and pushes return value onto the stack.
      *   Category: Statements
@@ -519,17 +519,17 @@ 1234567890123456789012345678901234567890
     /*
      * Pops the top two values on the stack as 'val' and 'obj' and performs
      * 'obj.prop = val', pushing 'val' back onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t nameIndex
      *   Stack: obj, val => val
      */ \
-    macro(JSOP_SETPROP,   54, "setprop",    NULL,         5,  2,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING|JOF_CHECKSLOPPY) \
+    macro(JSOP_SETPROP,   54, "setprop",    NULL,         5,  2,  1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSLOPPY) \
     /*
      * Pops the top two values on the stack as 'propval' and 'obj', pushes
      * 'propval' property of 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands:
      *   Stack: obj, propval => obj[propval]
      */ \
@@ -538,28 +538,28 @@ 1234567890123456789012345678901234567890
      * Pops the top three values on the stack as 'val', 'propval' and 'obj',
      * sets 'propval' property of 'obj' as 'val', pushes 'obj' onto the
      * stack.
      *   Category: Literals
      *   Type: Object
      *   Operands:
      *   Stack: obj, propval, val => val
      */ \
-    macro(JSOP_SETELEM,   56, "setelem",    NULL,         1,  3,  1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING|JOF_CHECKSLOPPY) \
+    macro(JSOP_SETELEM,   56, "setelem",    NULL,         1,  3,  1, JOF_BYTE |JOF_ELEM|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSLOPPY) \
     /*
      * Pops the top three values on the stack as 'val', 'propval' and 'obj',
      * sets 'propval' property of 'obj' as 'val', pushes 'obj' onto the
      * stack. Throws a TypeError if the set fails, per strict mode
      * semantics.
      *   Category: Literals
      *   Type: Object
      *   Operands:
      *   Stack: obj, propval, val => val
      */ \
-    macro(JSOP_STRICTSETELEM,   57, "strict-setelem",    NULL,         1,  3,  1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING|JOF_CHECKSTRICT) \
+    macro(JSOP_STRICTSETELEM,   57, "strict-setelem",    NULL,         1,  3,  1, JOF_BYTE |JOF_ELEM|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSTRICT) \
     /*
      * Invokes 'callee' with 'this' and 'args', pushes return value onto the
      * stack.
      *   Category: Statements
      *   Type: Function
      *   Operands: uint16_t argc
      *   Stack: callee, this, args[0], ..., args[argc-1] => rval
      *   nuses: (argc+2)
@@ -803,33 +803,33 @@ 1234567890123456789012345678901234567890
      * Fast set op for function arguments and local variables.
      *
      * Sets 'arguments[argno]' as the top of stack value.
      *   Category: Variables and Scopes
      *   Type: Arguments
      *   Operands: uint16_t argno
      *   Stack: v => v
      */ \
-    macro(JSOP_SETARG,    85, "setarg",     NULL,         3,  1,  1,  JOF_QARG |JOF_NAME|JOF_SET) \
+    macro(JSOP_SETARG,    85, "setarg",     NULL,         3,  1,  1,  JOF_QARG |JOF_NAME) \
     /*
      * Pushes the value of local variable onto the stack.
      *   Category: Variables and Scopes
      *   Type: Local Variables
      *   Operands: uint32_t localno
      *   Stack: => val
      */ \
     macro(JSOP_GETLOCAL,  86,"getlocal",    NULL,         4,  0,  1,  JOF_LOCAL|JOF_NAME) \
     /*
      * Stores the top stack value to the given local.
      *   Category: Variables and Scopes
      *   Type: Local Variables
      *   Operands: uint32_t localno
      *   Stack: v => v
      */ \
-    macro(JSOP_SETLOCAL,  87,"setlocal",    NULL,         4,  1,  1,  JOF_LOCAL|JOF_NAME|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_SETLOCAL,  87,"setlocal",    NULL,         4,  1,  1,  JOF_LOCAL|JOF_NAME|JOF_DETECTING) \
     \
     /*
      * Pushes unsigned 16-bit int immediate integer operand onto the stack.
      *   Category: Literals
      *   Type: Constants
      *   Operands: uint16_t val
      *   Stack: => val
      */ \
@@ -889,102 +889,102 @@ 1234567890123456789012345678901234567890
      *
      * Pops the top two values on the stack as 'val' and 'obj', defines
      * 'nameIndex' property of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t nameIndex
      *   Stack: obj, val => obj
      */ \
-    macro(JSOP_INITPROP,  93, "initprop",   NULL,         5,  2,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITPROP,  93, "initprop",   NULL,         5,  2,  1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_DETECTING) \
     \
     /*
      * Initialize a numeric property in an object literal, like '{1: x}'.
      *
      * Pops the top three values on the stack as 'val', 'id' and 'obj', defines
      * 'id' property of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands:
      *   Stack: obj, id, val => obj
      */ \
-    macro(JSOP_INITELEM,  94, "initelem",   NULL,         1,  3,  1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITELEM,  94, "initelem",   NULL,         1,  3,  1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_DETECTING) \
     \
     /*
      * Pops the top three values on the stack as 'val', 'index' and 'obj', sets
      * 'index' property of 'obj' as 'val', pushes 'obj' and 'index + 1' onto
      * the stack.
      *
      * This opcode is used in Array literals with spread and spreadcall
      * arguments.
      *   Category: Literals
      *   Type: Array
      *   Operands:
      *   Stack: obj, index, val => obj, (index + 1)
      */ \
-    macro(JSOP_INITELEM_INC,95, "initelem_inc", NULL,     1,  3,  2, JOF_BYTE|JOF_ELEM|JOF_SET) \
+    macro(JSOP_INITELEM_INC,95, "initelem_inc", NULL,     1,  3,  2, JOF_BYTE|JOF_ELEM|JOF_PROPINIT) \
     \
     /*
      * Initialize an array element.
      *
      * Pops the top two values on the stack as 'val' and 'obj', sets 'index'
      * property of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Array
      *   Operands: uint32_t index
      *   Stack: obj, val => obj
      */ \
-    macro(JSOP_INITELEM_ARRAY,96, "initelem_array", NULL, 5,  2,  1,  JOF_UINT32|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITELEM_ARRAY,96, "initelem_array", NULL, 5,  2,  1,  JOF_UINT32|JOF_ELEM|JOF_PROPINIT|JOF_DETECTING) \
     \
     /*
      * Initialize a getter in an object literal.
      *
      * Pops the top two values on the stack as 'val' and 'obj', defines getter
      * of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t nameIndex
      *   Stack: obj, val => obj
      */ \
-    macro(JSOP_INITPROP_GETTER,  97, "initprop_getter",   NULL, 5,  2,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITPROP_GETTER,  97, "initprop_getter",   NULL, 5,  2,  1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_DETECTING) \
     /*
      * Initialize a setter in an object literal.
      *
      * Pops the top two values on the stack as 'val' and 'obj', defines setter
      * of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t nameIndex
      *   Stack: obj, val => obj
      */ \
-    macro(JSOP_INITPROP_SETTER,  98, "initprop_setter",   NULL, 5,  2,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITPROP_SETTER,  98, "initprop_setter",   NULL, 5,  2,  1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_DETECTING) \
     /*
      * Initialize a numeric getter in an object literal like
      * '{get 2() {}}'.
      *
      * Pops the top three values on the stack as 'val', 'id' and 'obj', defines
      * 'id' getter of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands:
      *   Stack: obj, id, val => obj
      */ \
-    macro(JSOP_INITELEM_GETTER,  99, "initelem_getter",   NULL, 1,  3,  1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITELEM_GETTER,  99, "initelem_getter",   NULL, 1,  3,  1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_DETECTING) \
     /*
      * Initialize a numeric setter in an object literal like
      * '{set 2(v) {}}'.
      *
      * Pops the top three values on the stack as 'val', 'id' and 'obj', defines
      * 'id' setter of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands:
      *   Stack: obj, id, val => obj
      */ \
-    macro(JSOP_INITELEM_SETTER, 100, "initelem_setter",   NULL, 1,  3,  1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITELEM_SETTER, 100, "initelem_setter",   NULL, 1,  3,  1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_DETECTING) \
     /*
      * Pushes the call site object specified by objectIndex onto the stack. Defines the raw
      * property specified by objectIndex + 1 on the call site object and freezes both the call site
      * object as well as its raw property.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t objectIndex
      *   Stack: => obj
@@ -1024,17 +1024,17 @@ 1234567890123456789012345678901234567890
      * Pops the top three values on the stack as 'val' and 'obj', and 'receiver',
      * and performs 'obj.prop = val', pushing 'val' back onto the stack.
      * Throws a TypeError if the set-operation failed (per strict mode semantics).
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t nameIndex
      *   Stack: receiver, obj, val => val
      */ \
-    macro(JSOP_STRICTSETPROP_SUPER,   105, "strictsetprop-super",    NULL,         5,  3,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING|JOF_CHECKSTRICT) \
+    macro(JSOP_STRICTSETPROP_SUPER,   105, "strictsetprop-super",    NULL,         5,  3,  1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSTRICT) \
     \
     /*
      * This opcode precedes every labeled statement. It's a no-op.
      *
      * 'offset' is the offset to the next instruction after this statement,
      * the one 'break LABEL;' would jump to. IonMonkey uses this.
      *   Category: Statements
      *   Type: Jumps
@@ -1046,17 +1046,17 @@ 1234567890123456789012345678901234567890
     /*
      * Pops the top three values on the stack as 'val', 'obj' and 'receiver',
      * and performs 'obj.prop = val', pushing 'val' back onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t nameIndex
      *   Stack: receiver, obj, val => val
      */ \
-    macro(JSOP_SETPROP_SUPER,   107, "setprop-super",    NULL,         5,  3,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING|JOF_CHECKSLOPPY) \
+    macro(JSOP_SETPROP_SUPER,   107, "setprop-super",    NULL,         5,  3,  1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSLOPPY) \
     \
     /*
      * Invokes 'callee' with 'this' and 'args', pushes return value onto the
      * stack.
      *
      * If 'callee' is determined to be the canonical 'Function.prototype.call'
      * function, then this operation is optimized to directly call 'callee'
      * with 'args[0]' as 'this', and the remaining arguments as formal args
@@ -1087,26 +1087,26 @@ 1234567890123456789012345678901234567890
      * Looks up name on the scope chain and pushes the scope which contains
      * the name onto the stack. If not found, pushes global scope onto the
      * stack.
      *   Category: Variables and Scopes
      *   Type: Variables
      *   Operands: uint32_t nameIndex
      *   Stack: => scope
      */ \
-    macro(JSOP_BINDNAME,  110,"bindname",   NULL,         5,  0,  1,  JOF_ATOM|JOF_NAME|JOF_SET) \
+    macro(JSOP_BINDNAME,  110,"bindname",   NULL,         5,  0,  1,  JOF_ATOM|JOF_NAME) \
     /*
      * Pops a scope and value from the stack, assigns value to the given name,
      * and pushes the value back on the stack
      *   Category: Variables and Scopes
      *   Type: Variables
      *   Operands: uint32_t nameIndex
      *   Stack: scope, val => val
      */ \
-    macro(JSOP_SETNAME,   111,"setname",    NULL,         5,  2,  1,  JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_CHECKSLOPPY) \
+    macro(JSOP_SETNAME,   111,"setname",    NULL,         5,  2,  1,  JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSLOPPY) \
     \
     /* Exception handling ops. */ \
     /*
      * Pops the top of stack value as 'v', sets pending exception as 'v', then
      * raises error.
      *   Category: Statements
      *   Type: Exception Handling
      *   Operands:
@@ -1415,17 +1415,17 @@ 1234567890123456789012345678901234567890
     macro(JSOP_GETALIASEDVAR, 136,"getaliasedvar",NULL,      5,  0,  1,  JOF_ENVCOORD|JOF_NAME|JOF_TYPESET) \
     /*
      * Sets aliased variable as the top of stack value.
      *   Category: Variables and Scopes
      *   Type: Aliased Variables
      *   Operands: uint8_t hops, uint24_t slot
      *   Stack: v => v
      */ \
-    macro(JSOP_SETALIASEDVAR, 137,"setaliasedvar",NULL,      5,  1,  1,  JOF_ENVCOORD|JOF_NAME|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_SETALIASEDVAR, 137,"setaliasedvar",NULL,      5,  1,  1,  JOF_ENVCOORD|JOF_NAME|JOF_PROPSET|JOF_DETECTING) \
     \
     /*
      * Checks if the value of the local variable is the
      * JS_UNINITIALIZED_LEXICAL magic, throwing an error if so.
      *   Category: Variables and Scopes
      *   Type: Local Variables
      *   Operands: uint32_t localno
      *   Stack: =>
@@ -1434,17 +1434,17 @@ 1234567890123456789012345678901234567890
     /*
      * Initializes an uninitialized local lexical binding with the top of stack
      * value.
      *   Category: Variables and Scopes
      *   Type: Local Variables
      *   Operands: uint32_t localno
      *   Stack: v => v
      */ \
-    macro(JSOP_INITLEXICAL,   139, "initlexical",  NULL,      4,  1,  1, JOF_LOCAL|JOF_NAME|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITLEXICAL,   139, "initlexical",  NULL,      4,  1,  1, JOF_LOCAL|JOF_NAME|JOF_DETECTING) \
     /*
      * Checks if the value of the aliased variable is the
      * JS_UNINITIALIZED_LEXICAL magic, throwing an error if so.
      *   Category: Variables and Scopes
      *   Type: Aliased Variables
      *   Operands: uint8_t hops, uint24_t slot
      *   Stack: =>
      */ \
@@ -1452,17 +1452,17 @@ 1234567890123456789012345678901234567890
     /*
      * Initializes an uninitialized aliased lexical binding with the top of
      * stack value.
      *   Category: Variables and Scopes
      *   Type: Aliased Variables
      *   Operands: uint8_t hops, uint24_t slot
      *   Stack: v => v
      */ \
-    macro(JSOP_INITALIASEDLEXICAL,  141, "initaliasedlexical",  NULL, 5,  1,  1, JOF_ENVCOORD|JOF_NAME|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITALIASEDLEXICAL,  141, "initaliasedlexical",  NULL, 5,  1,  1, JOF_ENVCOORD|JOF_NAME|JOF_PROPINIT|JOF_DETECTING) \
     /*
      * Pushes a JS_UNINITIALIZED_LEXICAL value onto the stack, representing an
      * uninitialized lexical binding.
      *
      * This opcode is used with the JSOP_INITLET opcode.
      *   Category: Literals
      *   Type: Constants
      *   Operands:
@@ -1484,17 +1484,17 @@ 1234567890123456789012345678901234567890
     macro(JSOP_GETINTRINSIC,  143, "getintrinsic",  NULL, 5,  0,  1, JOF_ATOM|JOF_NAME|JOF_TYPESET) \
     /*
      * Stores the top stack value in the specified intrinsic.
      *   Category: Variables and Scopes
      *   Type: Intrinsics
      *   Operands: uint32_t nameIndex
      *   Stack: val => val
      */ \
-    macro(JSOP_SETINTRINSIC,  144, "setintrinsic",  NULL, 5,  1,  1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_SETINTRINSIC,  144, "setintrinsic",  NULL, 5,  1,  1, JOF_ATOM|JOF_NAME|JOF_DETECTING) \
     /*
      * Like JSOP_CALL, but used as part of for-of and destructuring bytecode
      * to provide better error messages.
      *   Category: Statements
      *   Type: Function
      *   Operands: uint16_t argc (must be 0)
      *   Stack: callee, this => rval
      *   nuses: 2
@@ -1505,28 +1505,28 @@ 1234567890123456789012345678901234567890
      *
      * Pops the top two values on the stack as 'val' and 'obj', defines
      * 'nameIndex' property of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t nameIndex
      *   Stack: obj, val => obj
      */ \
-    macro(JSOP_INITLOCKEDPROP, 146, "initlockedprop", NULL, 5,  2,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITLOCKEDPROP, 146, "initlockedprop", NULL, 5,  2,  1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_DETECTING) \
     /*
      * Initialize a non-enumerable data-property on an object.
      *
      * Pops the top two values on the stack as 'val' and 'obj', defines
      * 'nameIndex' property of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t nameIndex
      *   Stack: obj, val => obj
      */ \
-    macro(JSOP_INITHIDDENPROP, 147,"inithiddenprop", NULL, 5,  2,  1,  JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITHIDDENPROP, 147,"inithiddenprop", NULL, 5,  2,  1,  JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_DETECTING) \
     /*
      * Push "new.target"
      *
      *   Category: Variables and Scopes
      *   Type: Arguments
      *   Operands:
      *   Stack: => new.target
      */ \
@@ -1603,31 +1603,31 @@ 1234567890123456789012345678901234567890
      *
      * 'scope' should be the global scope unless the script has a non-syntactic
      * global scope, in which case acts like JSOP_SETNAME.
      *   Category: Variables and Scopes
      *   Type: Free Variables
      *   Operands: uint32_t nameIndex
      *   Stack: scope, val => val
      */ \
-    macro(JSOP_SETGNAME,      155,"setgname",  NULL,       5,  2,  1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME|JOF_CHECKSLOPPY) \
+    macro(JSOP_SETGNAME,      155,"setgname",  NULL,       5,  2,  1, JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_DETECTING|JOF_GNAME|JOF_CHECKSLOPPY) \
     \
     /*
      * Pops the top two values on the stack as 'val' and 'scope', sets property
      * of 'scope' as 'val' and pushes 'val' back on the stack. Throws a
      * TypeError if the set fails, per strict mode semantics.
      *
      * 'scope' should be the global scope unless the script has a non-syntactic
      * global scope, in which case acts like JSOP_STRICTSETNAME.
      *   Category: Variables and Scopes
      *   Type: Free Variables
      *   Operands: uint32_t nameIndex
      *   Stack: scope, val => val
      */ \
-    macro(JSOP_STRICTSETGNAME, 156, "strict-setgname",  NULL,       5,  2,  1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME|JOF_CHECKSTRICT) \
+    macro(JSOP_STRICTSETGNAME, 156, "strict-setgname",  NULL,       5,  2,  1, JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_DETECTING|JOF_GNAME|JOF_CHECKSTRICT) \
     /*
      * Pushes the implicit 'this' value for calls to the associated name onto
      * the stack; only used when we know this implicit this will be our first
      * non-syntactic scope.
      *
      *   Category: Variables and Scopes
      *   Type: This
      *   Operands: uint32_t nameIndex
@@ -1637,26 +1637,26 @@ 1234567890123456789012345678901234567890
     /*
      * LIKE JSOP_SETELEM, but takes receiver on the stack, and the propval is
      * evaluated before the base.
      *   Category: Literals
      *   Type: Object
      *   Operands:
      *   Stack: propval, receiver, obj, val => val
      */ \
-    macro(JSOP_SETELEM_SUPER,   158, "setelem-super", NULL, 1,  4,  1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING|JOF_CHECKSLOPPY) \
+    macro(JSOP_SETELEM_SUPER,   158, "setelem-super", NULL, 1,  4,  1, JOF_BYTE |JOF_ELEM|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSLOPPY) \
     /*
      * LIKE JSOP_STRICTSETELEM, but takes receiver on the stack, and the
      * propval is evaluated before the base.
      *   Category: Literals
      *   Type: Object
      *   Operands:
      *   Stack: propval, receiver, obj, val => val
      */ \
-    macro(JSOP_STRICTSETELEM_SUPER, 159, "strict-setelem-super", NULL, 1,  4, 1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING|JOF_CHECKSTRICT) \
+    macro(JSOP_STRICTSETELEM_SUPER, 159, "strict-setelem-super", NULL, 1,  4, 1, JOF_BYTE |JOF_ELEM|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSTRICT) \
     \
     /*
      * Pushes a regular expression literal onto the stack.
      * It requires special "clone on exec" handling.
      *   Category: Literals
      *   Type: RegExp
      *   Operands: uint32_t regexpIndex
      *   Stack: => regexp
@@ -1666,17 +1666,17 @@ 1234567890123456789012345678901234567890
     /*
      * Initializes an uninitialized global lexical binding with the top of
      * stack value.
      *   Category: Variables and Scopes
      *   Type: Free Variables
      *   Operands: uint32_t nameIndex
      *   Stack: val => val
      */ \
-    macro(JSOP_INITGLEXICAL,  161,"initglexical", NULL,   5,  1,  1,  JOF_ATOM|JOF_NAME|JOF_SET|JOF_GNAME) \
+    macro(JSOP_INITGLEXICAL,  161,"initglexical", NULL,   5,  1,  1,  JOF_ATOM|JOF_NAME|JOF_PROPINIT|JOF_GNAME) \
     \
     /* Defines the new mutable binding on global lexical scope.
      *
      * Throws if a binding with the same name already exists on the scope, or
      * if a var binding with the same name exists on the global.
      *   Category: Variables and Scopes
      *   Type: Variables
      *   Operands: uint32_t nameIndex
@@ -1743,84 +1743,84 @@ 1234567890123456789012345678901234567890
      * Throws a runtime TypeError for invalid assignment to 'const'. The
      * localno is used for better error messages.
      *
      *   Category: Variables and Scopes
      *   Type: Local Variables
      *   Operands: uint32_t localno
      *   Stack: v => v
      */ \
-    macro(JSOP_THROWSETCONST,        169, "throwsetconst",        NULL, 4,  1,  1, JOF_LOCAL|JOF_NAME|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_THROWSETCONST,        169, "throwsetconst",        NULL, 4,  1,  1, JOF_LOCAL|JOF_NAME|JOF_DETECTING) \
     /*
      * Throws a runtime TypeError for invalid assignment to 'const'. The
      * scope coordinate is used for better error messages.
      *
      *   Category: Variables and Scopes
      *   Type: Aliased Variables
      *   Operands: uint8_t hops, uint24_t slot
      *   Stack: v => v
      */ \
-    macro(JSOP_THROWSETALIASEDCONST, 170, "throwsetaliasedconst", NULL, 5,  1,  1, JOF_ENVCOORD|JOF_NAME|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_THROWSETALIASEDCONST, 170, "throwsetaliasedconst", NULL, 5,  1,  1, JOF_ENVCOORD|JOF_NAME|JOF_DETECTING) \
     /*
      * Initialize a non-enumerable getter in an object literal.
      *
      * Pops the top two values on the stack as 'val' and 'obj', defines
      * getter of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t nameIndex
      *   Stack: obj, val => obj
      */ \
-    macro(JSOP_INITHIDDENPROP_GETTER,  171, "inithiddenprop_getter",   NULL, 5,  2,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITHIDDENPROP_GETTER,  171, "inithiddenprop_getter",   NULL, 5,  2,  1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_DETECTING) \
     /*
      * Initialize a non-enumerable setter in an object literal.
      *
      * Pops the top two values on the stack as 'val' and 'obj', defines
      * setter of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t nameIndex
      *   Stack: obj, val => obj
      */ \
-    macro(JSOP_INITHIDDENPROP_SETTER,  172, "inithiddenprop_setter",   NULL, 5,  2,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITHIDDENPROP_SETTER,  172, "inithiddenprop_setter",   NULL, 5,  2,  1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_DETECTING) \
     /*
      * Initialize a non-enumerable numeric getter in an object literal like
      * '{get 2() {}}'.
      *
      * Pops the top three values on the stack as 'val', 'id' and 'obj', defines
      * 'id' getter of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands:
      *   Stack: obj, id, val => obj
      */ \
-    macro(JSOP_INITHIDDENELEM_GETTER,  173, "inithiddenelem_getter",   NULL, 1,  3,  1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITHIDDENELEM_GETTER,  173, "inithiddenelem_getter",   NULL, 1,  3,  1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_DETECTING) \
     /*
      * Initialize a non-enumerable numeric setter in an object literal like
      * '{set 2(v) {}}'.
      *
      * Pops the top three values on the stack as 'val', 'id' and 'obj', defines
      * 'id' setter of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands:
      *   Stack: obj, id, val => obj
      */ \
-    macro(JSOP_INITHIDDENELEM_SETTER, 174, "inithiddenelem_setter",   NULL, 1,  3,  1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITHIDDENELEM_SETTER, 174, "inithiddenelem_setter",   NULL, 1,  3,  1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_DETECTING) \
     /*
      * Initialize a non-enumerable numeric property in an object literal, like '{1: x}'.
      *
      * Pops the top three values on the stack as 'val', 'id' and 'obj', defines
      * 'id' property of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands:
      *   Stack: obj, id, val => obj
      */ \
-    macro(JSOP_INITHIDDENELEM,  175, "inithiddenelem",   NULL,         1,  3,  1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITHIDDENELEM,  175, "inithiddenelem",   NULL,         1,  3,  1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_DETECTING) \
     /*
      * Gets the value of a module import by name and pushes it onto the stack.
      *   Category: Variables and Scopes
      *   Type: Variables
      *   Operands: uint32_t nameIndex
      *   Stack: => val
      */ \
     macro(JSOP_GETIMPORT,     176,"getimport",  NULL,     5,  0,  1,  JOF_ATOM|JOF_NAME|JOF_TYPESET) \
@@ -1850,17 +1850,17 @@ 1234567890123456789012345678901234567890
      * closed over, does not have a frame slot to look up the name with for
      * the error message.
      *
      *   Category: Variables and Scopes
      *   Type: Local Variables
      *   Operands:
      *   Stack: v => v
      */ \
-    macro(JSOP_THROWSETCALLEE,     179, "throwsetcallee",        NULL, 1,  1,  1, JOF_SET|JOF_BYTE) \
+    macro(JSOP_THROWSETCALLEE,     179, "throwsetcallee",        NULL, 1,  1,  1, JOF_BYTE) \
     /*
      * Pushes a var environment onto the env chain.
      *   Category: Variables and Scopes
      *   Type: Var Scope
      *   Operands: uint32_t scopeIndex
      *   Stack: =>
      */ \
     macro(JSOP_PUSHVARENV,         180, "pushvarenv",           NULL, 5,  0,  0,  JOF_SCOPE) \
@@ -2157,17 +2157,17 @@ 1234567890123456789012345678901234567890
      * non-syntactic global scope.  Otherwise will act like JSOP_BINDNAME.
      *
      * 'nameIndex' is only used when acting like JSOP_BINDNAME.
      *   Category: Variables and Scopes
      *   Type: Free Variables
      *   Operands: uint32_t nameIndex
      *   Stack: => global
      */ \
-    macro(JSOP_BINDGNAME,     214, "bindgname",    NULL,  5,  0,  1,  JOF_ATOM|JOF_NAME|JOF_SET|JOF_GNAME) \
+    macro(JSOP_BINDGNAME,     214, "bindgname",    NULL,  5,  0,  1,  JOF_ATOM|JOF_NAME|JOF_GNAME) \
     \
     /*
      * Pushes 8-bit int immediate integer operand onto the stack.
      *   Category: Literals
      *   Type: Constants
      *   Operands: int8_t val
      *   Stack: => val
      */ \
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -359,31 +359,43 @@ class BaseCompiler
 
         MIRType  type_;              // Type of the value, or MIRType::None
         uint32_t offs_;              // Zero-based frame offset of value, or UINT32_MAX
 
         MIRType type() const { MOZ_ASSERT(type_ != MIRType::None); return type_; }
         uint32_t offs() const { MOZ_ASSERT(offs_ != UINT32_MAX); return offs_; }
     };
 
+    // Bit set used for simple bounds check elimination.  Capping this at 64
+    // locals makes sense; even 32 locals would probably be OK in practice.
+    //
+    // For more information about BCE, see the block comment above
+    // popMemoryAccess(), below.
+
+    typedef uint64_t BCESet;
+
     // Control node, representing labels and stack heights at join points.
 
     struct Control
     {
         Control()
             : framePushed(UINT32_MAX),
               stackSize(UINT32_MAX),
+              bceSafeOnEntry(0),
+              bceSafeOnExit(~BCESet(0)),
               deadOnArrival(false),
               deadThenBranch(false)
         {}
 
         NonAssertingLabel label;        // The "exit" label
         NonAssertingLabel otherLabel;   // Used for the "else" branch of if-then-else
         uint32_t framePushed;           // From masm
         uint32_t stackSize;             // Value stack height
+        BCESet bceSafeOnEntry;          // Bounds check info flowing into the item
+        BCESet bceSafeOnExit;           // Bounds check info flowing out of the item
         bool deadOnArrival;             // deadCode_ was set on entry to the region
         bool deadThenBranch;            // deadCode_ was set on exit from "then"
     };
 
     struct BaseCompilePolicy : OpIterPolicy
     {
         static const bool Output = true;
 
@@ -464,16 +476,17 @@ class BaseCompiler
     TempAllocator&              alloc_;
     const ValTypeVector&        locals_;         // Types of parameters and locals
     int32_t                     localSize_;      // Size of local area in bytes (stable after beginFunction)
     int32_t                     varLow_;         // Low byte offset of local area for true locals (not parameters)
     int32_t                     varHigh_;        // High byte offset + 1 of local area for true locals
     int32_t                     maxFramePushed_; // Max value of masm.framePushed() observed
     bool                        deadCode_;       // Flag indicating we should decode & discard the opcode
     bool                        debugEnabled_;
+    BCESet                      bceSafe_;        // Locals that have been bounds checked and not updated since
     ValTypeVector               SigI64I64_;
     ValTypeVector               SigD_;
     ValTypeVector               SigF_;
     ValTypeVector               SigI_;
     ValTypeVector               Sig_;
     Label                       returnLabel_;
     Label                       stackOverflowLabel_;
     TrapOffset                  prologueTrapOffset_;
@@ -1712,16 +1725,24 @@ class BaseCompiler
         c = v.i64val();
         if (c <= cutoff || !IsPowerOfTwo(static_cast<uint64_t>(c)))
             return false;
         power = FloorLog2(c);
         stk_.popBack();
         return true;
     }
 
+    MOZ_MUST_USE bool peekLocalI32(uint32_t* local) {
+        Stk& v = stk_.back();
+        if (v.kind() != Stk::LocalI32)
+            return false;
+        *local = v.slot();
+        return true;
+    }
+
     // TODO / OPTIMIZE (Bug 1316818): At the moment we use ReturnReg
     // for JoinReg.  It is possible other choices would lead to better
     // register allocation, as ReturnReg is often first in the
     // register set and will be heavily wanted by the register
     // allocator that uses takeFirst().
     //
     // Obvious options:
     //  - pick a register at the back of the register set
@@ -2014,16 +2035,17 @@ class BaseCompiler
     void initControl(Control& item)
     {
         // Make sure the constructor was run properly
         MOZ_ASSERT(item.framePushed == UINT32_MAX && item.stackSize == UINT32_MAX);
 
         item.framePushed = masm.framePushed();
         item.stackSize = stk_.length();
         item.deadOnArrival = deadCode_;
+        item.bceSafeOnEntry = bceSafe_;
     }
 
     Control& controlItem() {
         return iter_.controlItem();
     }
 
     Control& controlItem(uint32_t relativeDepth) {
         return iter_.controlItem(relativeDepth);
@@ -3182,16 +3204,34 @@ class BaseCompiler
         loadFromFramePtr(tmp, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
         masm.storeDouble(r, Address(tmp, globalToTlsOffset(globalDataOffset)));
     }
 
     //////////////////////////////////////////////////////////////////////
     //
     // Heap access.
 
+    void bceCheckLocal(MemoryAccessDesc* access, uint32_t local, bool* omitBoundsCheck) {
+        if (local >= sizeof(BCESet)*8)
+            return;
+
+        if ((bceSafe_ & (BCESet(1) << local)) && access->offset() < wasm::OffsetGuardLimit)
+            *omitBoundsCheck = true;
+
+        // The local becomes safe even if the offset is beyond the guard limit.
+        bceSafe_ |= (BCESet(1) << local);
+    }
+
+    void bceLocalIsUpdated(uint32_t local) {
+        if (local >= sizeof(BCESet)*8)
+            return;
+
+        bceSafe_ &= ~(BCESet(1) << local);
+    }
+
     void checkOffset(MemoryAccessDesc* access, RegI32 ptr) {
         if (access->offset() >= OffsetGuardLimit) {
             masm.branchAdd32(Assembler::CarrySet, Imm32(access->offset()), ptr,
                              trap(Trap::OutOfBounds));
             access->clearOffset();
         }
     }
 
@@ -5089,69 +5129,80 @@ BaseCompiler::emitBlock()
 
 void
 BaseCompiler::endBlock(ExprType type)
 {
     Control& block = controlItem();
 
     // Save the value.
     AnyReg r;
-    if (!deadCode_)
+    if (!deadCode_) {
         r = popJoinRegUnlessVoid(type);
+        block.bceSafeOnExit &= bceSafe_;
+    }
 
     // Leave the block.
     popStackOnBlockExit(block.framePushed);
     popValueStackTo(block.stackSize);
 
     // Bind after cleanup: branches out will have popped the stack.
     if (block.label.used()) {
         masm.bind(&block.label);
         // No value was provided by the fallthrough but the branch out will
         // have stored one in joinReg, so capture that.
         if (deadCode_)
             r = captureJoinRegUnlessVoid(type);
         deadCode_ = false;
     }
 
+    bceSafe_ = block.bceSafeOnExit;
+
     // Retain the value stored in joinReg by all paths, if there are any.
     if (!deadCode_)
         pushJoinRegUnlessVoid(r);
 }
 
 bool
 BaseCompiler::emitLoop()
 {
     if (!iter_.readLoop())
         return false;
 
     if (!deadCode_)
         sync();                    // Simplifies branching out from block
 
     initControl(controlItem());
+    bceSafe_ = 0;
 
     if (!deadCode_) {
         masm.bind(&controlItem(0).label);
         addInterruptCheck();
     }
 
     return true;
 }
 
 void
 BaseCompiler::endLoop(ExprType type)
 {
     Control& block = controlItem();
 
     AnyReg r;
-    if (!deadCode_)
+    if (!deadCode_) {
         r = popJoinRegUnlessVoid(type);
+        // block.bceSafeOnExit need not be updated because it won't be used for
+        // the fallthrough path.
+    }
 
     popStackOnBlockExit(block.framePushed);
     popValueStackTo(block.stackSize);
 
+    // bceSafe_ stays the same along the fallthrough path because branches to
+    // loops branch to the top.
+
     // Retain the value stored in joinReg by all paths.
     if (!deadCode_)
         pushJoinRegUnlessVoid(r);
 }
 
 // The bodies of the "then" and "else" arms can be arbitrary sequences
 // of expressions, they push control and increment the nesting and can
 // even be targeted by jumps.  A branch to the "if" block branches to
@@ -5198,17 +5249,22 @@ BaseCompiler::endIfThen()
     popValueStackTo(ifThen.stackSize);
 
     if (ifThen.otherLabel.used())
         masm.bind(&ifThen.otherLabel);
 
     if (ifThen.label.used())
         masm.bind(&ifThen.label);
 
+    if (!deadCode_)
+        ifThen.bceSafeOnExit &= bceSafe_;
+
     deadCode_ = ifThen.deadOnArrival;
+
+    bceSafe_ = ifThen.bceSafeOnExit & ifThen.bceSafeOnEntry;
 }
 
 bool
 BaseCompiler::emitElse()
 {
     ExprType thenType;
     Nothing unused_thenValue;
 
@@ -5233,20 +5289,23 @@ BaseCompiler::emitElse()
     if (!deadCode_)
         masm.jump(&ifThenElse.label);
 
     if (ifThenElse.otherLabel.used())
         masm.bind(&ifThenElse.otherLabel);
 
     // Reset to the "else" branch.
 
-    if (!deadCode_)
+    if (!deadCode_) {
         freeJoinRegUnlessVoid(r);
+        ifThenElse.bceSafeOnExit &= bceSafe_;
+    }
 
     deadCode_ = ifThenElse.deadOnArrival;
+    bceSafe_ = ifThenElse.bceSafeOnEntry;
 
     return true;
 }
 
 void
 BaseCompiler::endIfThenElse(ExprType type)
 {
     Control& ifThenElse = controlItem();
@@ -5254,18 +5313,20 @@ BaseCompiler::endIfThenElse(ExprType typ
     // The expression type is not a reliable guide to what we'll find
     // on the stack, we could have (if E (i32.const 1) (unreachable))
     // in which case the "else" arm is AnyType but the type of the
     // full expression is I32.  So restore whatever's there, not what
     // we want to find there.  The "then" arm has the same constraint.
 
     AnyReg r;
 
-    if (!deadCode_)
+    if (!deadCode_) {
         r = popJoinRegUnlessVoid(type);
+        ifThenElse.bceSafeOnExit &= bceSafe_;
+    }
 
     popStackOnBlockExit(ifThenElse.framePushed);
     popValueStackTo(ifThenElse.stackSize);
 
     if (ifThenElse.label.used())
         masm.bind(&ifThenElse.label);
 
     bool joinLive = !ifThenElse.deadOnArrival &&
@@ -5274,16 +5335,18 @@ BaseCompiler::endIfThenElse(ExprType typ
     if (joinLive) {
         // No value was provided by the "then" path but capture the one
         // provided by the "else" path.
         if (deadCode_)
             r = captureJoinRegUnlessVoid(type);
         deadCode_ = false;
     }
 
+    bceSafe_ = ifThenElse.bceSafeOnExit;
+
     if (!deadCode_)
         pushJoinRegUnlessVoid(r);
 }
 
 bool
 BaseCompiler::emitEnd()
 {
     LabelKind kind;
@@ -5314,16 +5377,17 @@ BaseCompiler::emitBr()
     Nothing unused_value;
     if (!iter_.readBr(&relativeDepth, &type, &unused_value))
         return false;
 
     if (deadCode_)
         return true;
 
     Control& target = controlItem(relativeDepth);
+    target.bceSafeOnExit &= bceSafe_;
 
     // Save any value in the designated join register, where the
     // normal block exit code will also leave it.
 
     AnyReg r = popJoinRegUnlessVoid(type);
 
     popStackBeforeBranch(target.framePushed);
     masm.jump(&target.label);
@@ -5348,16 +5412,17 @@ BaseCompiler::emitBrIf()
         return false;
 
     if (deadCode_) {
         resetLatentOp();
         return true;
     }
 
     Control& target = controlItem(relativeDepth);
+    target.bceSafeOnExit &= bceSafe_;
 
     BranchState b(&target.label, target.framePushed, InvertBranch(false), type);
     emitBranchSetup(&b);
     emitBranchPerform(&b);
 
     return true;
 }
 
@@ -5403,31 +5468,33 @@ BaseCompiler::emitBrTable()
     AnyReg r = popJoinRegUnlessVoid(type);
 
     Label dispatchCode;
     masm.branch32(Assembler::Below, rc, Imm32(tableLength), &dispatchCode);
 
     // This is the out-of-range stub.  rc is dead here but we don't need it.
 
     popStackBeforeBranch(controlItem(defaultDepth).framePushed);
+    controlItem(defaultDepth).bceSafeOnExit &= bceSafe_;
     masm.jump(&controlItem(defaultDepth).label);
 
     // Emit stubs.  rc is dead in all of these but we don't need it.
     //
     // The labels in the vector are in the TempAllocator and will
     // be freed by and by.
     //
     // TODO / OPTIMIZE (Bug 1316804): Branch directly to the case code if we
     // can, don't emit an intermediate stub.
 
     for (uint32_t i = 0; i < tableLength; i++) {
         stubs.infallibleEmplaceBack(NonAssertingLabel());
         masm.bind(&stubs.back());
         uint32_t k = depths[i];
         popStackBeforeBranch(controlItem(k).framePushed);
+        controlItem(k).bceSafeOnExit &= bceSafe_;
         masm.jump(&controlItem(k).label);
     }
 
     // Emit table.
 
     Label theTable;
     jumpTable(stubs, &theTable);
 
@@ -5886,16 +5953,17 @@ BaseCompiler::emitGetLocal()
 
 template<bool isSetLocal>
 bool
 BaseCompiler::emitSetOrTeeLocal(uint32_t slot)
 {
     if (deadCode_)
         return true;
 
+    bceLocalIsUpdated(slot);
     switch (locals_[slot]) {
       case ValType::I32: {
         RegI32 rv = popI32();
         syncLocal(slot);
         storeToFrameI32(rv, frameOffsetFromSlot(slot, MIRType::Int32));
         if (isSetLocal)
             freeI32(rv);
         else
@@ -6064,35 +6132,81 @@ BaseCompiler::emitSetGlobal()
       }
       default:
         MOZ_CRASH("Global variable type");
         break;
     }
     return true;
 }
 
-// See EffectiveAddressAnalysis::analyzeAsmJSHeapAccess() for comparable Ion code.
+// Bounds check elimination.
+//
+// We perform BCE on two kinds of address expressions: on constant heap pointers
+// that are known to be in the heap or will be handled by the out-of-bounds trap
+// handler; and on local variables that have been checked in dominating code
+// without being updated since.
+//
+// For an access through a constant heap pointer + an offset we can eliminate
+// the bounds check if the sum of the address and offset is below the sum of the
+// minimum memory length and the offset guard length.
+//
+// For an access through a local variable + an offset we can eliminate the
+// bounds check if the local variable has already been checked and has not been
+// updated since, and the offset is less than the guard limit.
+//
+// To track locals for which we can eliminate checks we use a bit vector
+// bceSafe_ that has a bit set for those locals whose bounds have been checked
+// and which have not subsequently been set.  Initially this vector is zero.
+//
+// In straight-line code a bit is set when we perform a bounds check on an
+// access via the local and is reset when the variable is updated.
 //
+// In control flow, the bit vector is manipulated as follows.  Each ControlItem
+// has a value bceSafeOnEntry, which is the value of bceSafe_ on entry to the
+// item, and a value bceSafeOnExit, which is initially ~0.  On a branch (br,
+// brIf, brTable), we always AND the branch target's bceSafeOnExit with the
+// value of bceSafe_ at the branch point.  On exiting an item by falling out of
+// it, provided we're not in dead code, we AND the current value of bceSafe_
+// into the item's bceSafeOnExit.  Additional processing depends on the item
+// type:
+//
+//  - After a block, set bceSafe_ to the block's bceSafeOnExit.
+//
+//  - On loop entry, after pushing the ControlItem, set bceSafe_ to zero; the
+//    back edges would otherwise require us to iterate to a fixedpoint.
+//
+//  - After a loop, the bceSafe_ is left unchanged, because only fallthrough
+//    control flow will reach that point and the bceSafe_ value represents the
+//    correct state of the fallthrough path.
+//
+//  - Set bceSafe_ to the ControlItem's bceSafeOnEntry at both the 'then' branch
+//    and the 'else' branch.
+//
+//  - After an if-then-else, set bceSafe_ to the if-then-else's bceSafeOnExit.
+//
+//  - After an if-then, set bceSafe_ to the if-then's bceSafeOnExit AND'ed with
+//    the if-then's bceSafeOnEntry.
+//
+// Finally, when the debugger allows locals to be mutated we must disable BCE
+// for references via a local, by returning immediately from bceCheckLocal if
+// debugEnabled_ is true.
+
 // TODO / OPTIMIZE (bug 1329576): There are opportunities to generate better
 // code by not moving a constant address with a zero offset into a register.
 
 BaseCompiler::RegI32
 BaseCompiler::popMemoryAccess(MemoryAccessDesc* access, bool* omitBoundsCheck)
 {
     // Caller must initialize.
     MOZ_ASSERT(!*omitBoundsCheck);
 
     int32_t addrTmp;
     if (popConstI32(addrTmp)) {
         uint32_t addr = addrTmp;
 
-        // We can eliminate the bounds check if the sum of the constant address
-        // and the known offset are below the sum of the minimum memory length
-        // and the offset guard length.
-
         uint64_t ea = uint64_t(addr) + uint64_t(access->offset());
         uint64_t limit = uint64_t(env_.minMemoryLength) + uint64_t(wasm::OffsetGuardLimit);
 
         *omitBoundsCheck = ea < limit;
 
         // Fold the offset into the pointer if we can, as this is always
         // beneficial.
 
@@ -6101,16 +6215,20 @@ BaseCompiler::popMemoryAccess(MemoryAcce
             access->clearOffset();
         }
 
         RegI32 r = needI32();
         loadConstI32(r, int32_t(addr));
         return r;
     }
 
+    uint32_t local;
+    if (peekLocalI32(&local))
+        bceCheckLocal(access, local, omitBoundsCheck);
+
     return popI32();
 }
 
 bool
 BaseCompiler::emitLoad(ValType type, Scalar::Type viewType)
 {
     LinearMemoryAddress<Nothing> addr;
     if (!iter_.readLoad(type, Scalar::byteSize(viewType), &addr))
@@ -7166,16 +7284,17 @@ BaseCompiler::BaseCompiler(const ModuleE
       alloc_(*alloc),
       locals_(locals),
       localSize_(0),
       varLow_(0),
       varHigh_(0),
       maxFramePushed_(0),
       deadCode_(false),
       debugEnabled_(debugEnabled),
+      bceSafe_(0),
       prologueTrapOffset_(trapOffset()),
       stackAddOffset_(0),
       latentOp_(LatentOp::None),
       latentType_(ValType::I32),
       latentIntCmp_(Assembler::Equal),
       latentDoubleCmp_(Assembler::DoubleEqual),
       masm(*masm),
       availGPR_(GeneralRegisterSet::All()),
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -6463,17 +6463,17 @@ static DrawResult
 DrawImageInternal(gfxContext&            aContext,
                   nsPresContext*         aPresContext,
                   imgIContainer*         aImage,
                   const SamplingFilter   aSamplingFilter,
                   const nsRect&          aDest,
                   const nsRect&          aFill,
                   const nsPoint&         aAnchor,
                   const nsRect&          aDirty,
-                  const SVGImageContext* aSVGContext,
+                  const Maybe<const SVGImageContext>& aSVGContext,
                   uint32_t               aImageFlags,
                   ExtendMode             aExtendMode = ExtendMode::CLAMP,
                   float                  aOpacity = 1.0)
 {
   DrawResult result = DrawResult::SUCCESS;
 
   aImageFlags |= imgIContainer::FLAG_ASYNC_NOTIFY;
 
@@ -6499,25 +6499,26 @@ DrawImageInternal(gfxContext&           
 
   {
     gfxContextMatrixAutoSaveRestore contextMatrixRestorer(&aContext);
 
     RefPtr<gfxContext> destCtx = &aContext;
 
     destCtx->SetMatrix(params.imageSpaceToDeviceSpace);
 
-    Maybe<SVGImageContext> svgContext = ToMaybe(aSVGContext);
-    if (!svgContext) {
+    Maybe<const SVGImageContext> fallbackContext;
+    if (!aSVGContext) {
       // Use the default viewport.
-      svgContext = Some(SVGImageContext(params.svgViewportSize, Nothing()));
+      fallbackContext.emplace(params.svgViewportSize);
     }
 
     result = aImage->Draw(destCtx, params.size, params.region,
                           imgIContainer::FRAME_CURRENT, aSamplingFilter,
-                          svgContext, aImageFlags, aOpacity);
+                          aSVGContext ? aSVGContext : fallbackContext,
+                          aImageFlags, aOpacity);
 
   }
 
   return result;
 }
 
 /* static */ DrawResult
 nsLayoutUtils::DrawSingleUnscaledImage(gfxContext&          aContext,
@@ -6549,27 +6550,27 @@ nsLayoutUtils::DrawSingleUnscaledImage(g
   nsRect fill(aDest, source.Size());
   // Ensure that only a single image tile is drawn. If aSourceArea extends
   // outside the image bounds, we want to honor the aSourceArea-to-aDest
   // translation but we don't want to actually tile the image.
   fill.IntersectRect(fill, dest);
   return DrawImageInternal(aContext, aPresContext,
                            aImage, aSamplingFilter,
                            dest, fill, aDest, aDirty ? *aDirty : dest,
-                           nullptr, aImageFlags);
+                           /* no SVGImageContext */ Nothing(), aImageFlags);
 }
 
 /* static */ DrawResult
 nsLayoutUtils::DrawSingleImage(gfxContext&            aContext,
                                nsPresContext*         aPresContext,
                                imgIContainer*         aImage,
                                const SamplingFilter   aSamplingFilter,
                                const nsRect&          aDest,
                                const nsRect&          aDirty,
-                               const SVGImageContext* aSVGContext,
+                               const Maybe<const SVGImageContext>& aSVGContext,
                                uint32_t               aImageFlags,
                                const nsPoint*         aAnchorPoint,
                                const nsRect*          aSourceArea)
 {
   nscoord appUnitsPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel();
   CSSIntSize pixelImageSize(ComputeSizeForDrawingWithFallback(aImage, aDest.Size()));
   if (pixelImageSize.width < 1 || pixelImageSize.height < 1) {
     NS_ASSERTION(pixelImageSize.width >= 0 && pixelImageSize.height >= 0,
@@ -6686,34 +6687,34 @@ nsLayoutUtils::DrawBackgroundImage(gfxCo
                                    const nsRect&       aDirty,
                                    uint32_t            aImageFlags,
                                    ExtendMode          aExtendMode,
                                    float               aOpacity)
 {
   PROFILER_LABEL("layout", "nsLayoutUtils::DrawBackgroundImage",
                  js::ProfileEntry::Category::GRAPHICS);
 
-  SVGImageContext svgContext(aImageSize, Nothing());
+  const Maybe<const SVGImageContext> svgContext(Some(SVGImageContext(aImageSize)));
 
   /* Fast path when there is no need for image spacing */
   if (aRepeatSize.width == aDest.width && aRepeatSize.height == aDest.height) {
     return DrawImageInternal(aContext, aPresContext, aImage,
                              aSamplingFilter, aDest, aFill, aAnchor,
-                             aDirty, &svgContext, aImageFlags, aExtendMode,
+                             aDirty, svgContext, aImageFlags, aExtendMode,
                              aOpacity);
   }
 
   nsPoint firstTilePos = aDest.TopLeft() +
                          nsPoint(NSToIntFloor(float(aFill.x - aDest.x) / aRepeatSize.width) * aRepeatSize.width,
                                  NSToIntFloor(float(aFill.y - aDest.y) / aRepeatSize.height) * aRepeatSize.height);
   for (int32_t i = firstTilePos.x; i < aFill.XMost(); i += aRepeatSize.width) {
     for (int32_t j = firstTilePos.y; j < aFill.YMost(); j += aRepeatSize.height) {
       nsRect dest(i, j, aDest.width, aDest.height);
       DrawResult result = DrawImageInternal(aContext, aPresContext, aImage, aSamplingFilter,
-                                            dest, dest, aAnchor, aDirty, &svgContext,
+                                            dest, dest, aAnchor, aDirty, svgContext,
                                             aImageFlags, ExtendMode::CLAMP,
                                             aOpacity);
       if (result != DrawResult::SUCCESS) {
         return result;
       }
     }
   }
 
@@ -6729,17 +6730,19 @@ nsLayoutUtils::DrawImage(gfxContext&    
                          const nsRect&       aFill,
                          const nsPoint&      aAnchor,
                          const nsRect&       aDirty,
                          uint32_t            aImageFlags,
                          float               aOpacity)
 {
   return DrawImageInternal(aContext, aPresContext, aImage,
                            aSamplingFilter, aDest, aFill, aAnchor,
-                           aDirty, nullptr, aImageFlags, ExtendMode::CLAMP,
+                           aDirty,
+                           /* no SVGImageContext */ Nothing(),
+                           aImageFlags, ExtendMode::CLAMP,
                            aOpacity);
 }
 
 /* static */ nsRect
 nsLayoutUtils::GetWholeImageDestination(const nsSize& aWholeImageSize,
                                         const nsRect& aImageSourceArea,
                                         const nsRect& aDestArea)
 {
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -26,16 +26,17 @@
 #include "imgIContainer.h"
 #include "mozilla/gfx/2D.h"
 #include "Units.h"
 #include "mozilla/ToString.h"
 #include "mozilla/ReflowOutput.h"
 #include "ImageContainer.h"
 #include "gfx2DGlue.h"
 #include "nsStyleConsts.h"
+#include "SVGImageContext.h"
 #include <limits>
 #include <algorithm>
 
 class nsPresContext;
 class nsIContent;
 class nsIAtom;
 class nsIScrollableFrame;
 class nsIDOMEvent;
@@ -60,17 +61,16 @@ class nsIDocument;
 struct gfxPoint;
 struct nsStyleFont;
 struct nsStyleImageOrientation;
 struct nsOverflowAreas;
 
 namespace mozilla {
 enum class CSSPseudoElementType : uint8_t;
 class EventListenerManager;
-class SVGImageContext;
 struct IntrinsicSize;
 struct ContainerLayerParameters;
 class WritingMode;
 namespace dom {
 class CanvasRenderingContext2D;
 class DOMRectList;
 class Element;
 class HTMLImageElement;
@@ -155,16 +155,17 @@ public:
   typedef FrameMetrics::ViewID ViewID;
   typedef mozilla::CSSPoint CSSPoint;
   typedef mozilla::CSSSize CSSSize;
   typedef mozilla::CSSIntSize CSSIntSize;
   typedef mozilla::CSSRect CSSRect;
   typedef mozilla::ScreenMargin ScreenMargin;
   typedef mozilla::LayoutDeviceIntSize LayoutDeviceIntSize;
   typedef mozilla::StyleGeometryBox StyleGeometryBox;
+  typedef mozilla::SVGImageContext SVGImageContext;
 
   /**
    * Finds previously assigned ViewID for the given content element, if any.
    * Returns whether a ViewID was previously assigned.
    */
   static bool FindIDFor(const nsIContent* aContent, ViewID* aOutViewId);
 
   /**
@@ -1859,36 +1860,40 @@ public:
    * Draw a whole image without tiling.
    *
    *   @param aRenderingContext Where to draw the image, set up with an
    *                            appropriate scale and transform for drawing in
    *                            app units.
    *   @param aImage            The image.
    *   @param aDest             The area that the image should fill.
    *   @param aDirty            Pixels outside this area may be skipped.
-   *   @param aSVGContext       If non-null, SVG-related rendering context
-   *                            such as overridden attributes on the image
-   *                            document's root <svg> node. Ignored for
-   *                            raster images.
+   *   @param aSVGContext       Optionally provides an SVGImageContext.
+   *                            Callers should pass an SVGImageContext with at
+   *                            least the viewport size set if aImage may be of
+   *                            type imgIContainer::TYPE_VECTOR, or pass
+   *                            Nothing() if it is of type
+   *                            imgIContainer::TYPE_RASTER (to save cycles
+   *                            constructing an SVGImageContext, since this
+   *                            argument will be ignored for raster images).
    *   @param aImageFlags       Image flags of the imgIContainer::FLAG_*
    *                            variety.
    *   @param aAnchor           If non-null, a point which we will ensure
    *                            is pixel-aligned in the output.
    *   @param aSourceArea       If non-null, this area is extracted from
    *                            the image and drawn in aDest. It's
    *                            in appunits. For best results it should
    *                            be aligned with image pixels.
    */
   static DrawResult DrawSingleImage(gfxContext&         aContext,
                                     nsPresContext*      aPresContext,
                                     imgIContainer*      aImage,
                                     const SamplingFilter aSamplingFilter,
                                     const nsRect&       aDest,
                                     const nsRect&       aDirty,
-                                    const mozilla::SVGImageContext* aSVGContext,
+                                    const mozilla::Maybe<const SVGImageContext>& aSVGContext,
                                     uint32_t            aImageFlags,
                                     const nsPoint*      aAnchorPoint = nullptr,
                                     const nsRect*       aSourceArea = nullptr);
 
   /**
    * Given an imgIContainer, this method attempts to obtain an intrinsic
    * px-valued height & width for it.  If the imgIContainer has a non-pixel
    * value for either height or width, this method tries to generate a pixel
--- a/layout/forms/nsRangeFrame.cpp
+++ b/layout/forms/nsRangeFrame.cpp
@@ -557,17 +557,24 @@ nsRangeFrame::GetValueAtEventPoint(Widge
     nsPresContext *presContext = PresContext();
     bool notUsedCanOverride;
     LayoutDeviceIntSize size;
     presContext->GetTheme()->
       GetMinimumWidgetSize(presContext, this, NS_THEME_RANGE_THUMB, &size,
                            &notUsedCanOverride);
     thumbSize.width = presContext->DevPixelsToAppUnits(size.width);
     thumbSize.height = presContext->DevPixelsToAppUnits(size.height);
-    MOZ_ASSERT(thumbSize.width > 0 && thumbSize.height > 0);
+    // For GTK, GetMinimumWidgetSize returns zero for the thumb dimension
+    // perpendicular to the orientation of the slider.  That's okay since we
+    // only care about the dimension in the direction of the slider when using
+    // |thumbSize| below, but it means this assertion need to check
+    // IsHorizontal().
+    MOZ_ASSERT((IsHorizontal() && thumbSize.width > 0) ||
+               (!IsHorizontal() && thumbSize.height > 0),
+               "The thumb is expected to take up some slider space");
   } else {
     nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame();
     if (thumbFrame) { // diplay:none?
       thumbSize = thumbFrame->GetSize();
     }
   }
 
   Decimal fraction;
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -27,16 +27,17 @@
 #include "nsCounterManager.h"
 #include "nsBidiUtils.h"
 #include "CounterStyleManager.h"
 
 #include "imgIContainer.h"
 #include "ImageLayers.h"
 #include "imgRequestProxy.h"
 #include "nsIURI.h"
+#include "SVGImageContext.h"
 
 #include <algorithm>
 
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
 
 using namespace mozilla;
@@ -253,17 +254,19 @@ public:
   Paint(nsRenderingContext& aRenderingContext, nsPoint aPt,
         const nsRect& aDirtyRect, uint32_t aFlags,
         bool aDisableSubpixelAA, nsIFrame* aFrame)
   {
     if (IsImageType()) {
       SamplingFilter filter = nsLayoutUtils::GetSamplingFilterForFrame(aFrame);
       return nsLayoutUtils::DrawSingleImage(*aRenderingContext.ThebesContext(),
                                             aFrame->PresContext(), mImage, filter,
-                                            mDest, aDirtyRect, nullptr, aFlags);
+                                            mDest, aDirtyRect,
+                                            /* no SVGImageContext */ Nothing(),
+                                            aFlags);
     }
 
     if (IsPathType()) {
       DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
       switch (mListStyleType) {
       case NS_STYLE_LIST_STYLE_CIRCLE:
         MOZ_ASSERT(mPath);
         drawTarget->Stroke(mPath, ColorPattern(ToDeviceColor(mColor)));
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -73,16 +73,17 @@
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
 #include "nsBlockFrame.h"
 #include "nsStyleStructInlines.h"
 
 #include "mozilla/Preferences.h"
 
 #include "mozilla/dom/Link.h"
+#include "SVGImageContext.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 using namespace mozilla::layers;
 
 // sizes (pixels) for image icon, padding and border frame
@@ -1418,17 +1419,17 @@ nsImageFrame::DisplayAltFeedback(nsRende
     if (imageStatus & imgIRequest::STATUS_LOAD_COMPLETE) {
       nsCOMPtr<imgIContainer> imgCon;
       request->GetImage(getter_AddRefs(imgCon));
       MOZ_ASSERT(imgCon, "Load complete, but no image container?");
       nsRect dest(flushRight ? inner.XMost() - size : inner.x,
                   inner.y, size, size);
       result = nsLayoutUtils::DrawSingleImage(*gfx, PresContext(), imgCon,
         nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
-        nullptr, aFlags);
+        /* no SVGImageContext */ Nothing(), aFlags);
     }
 
     // If we could not draw the icon, just draw some graffiti in the mean time.
     if (result == DrawResult::NOT_READY) {
       ColorPattern color(ToDeviceColor(Color(1.f, 0.f, 0.f, 1.f)));
 
       nscoord iconXPos = flushRight ? inner.XMost() - size : inner.x;
 
@@ -1695,17 +1696,17 @@ nsImageFrame::PaintImage(nsRenderingCont
   if (mForceSyncDecoding) {
     flags |= imgIContainer::FLAG_SYNC_DECODE;
   }
 
   DrawResult result =
     nsLayoutUtils::DrawSingleImage(*aRenderingContext.ThebesContext(),
       PresContext(), aImage,
       nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
-      nullptr, flags, &anchorPoint);
+      /* no SVGImageContext */ Nothing(), flags, &anchorPoint);
 
   nsImageMap* map = GetImageMap();
   if (map) {
     gfxPoint devPixelOffset =
       nsLayoutUtils::PointToGfxPoint(dest.TopLeft(),
                                      PresContext()->AppUnitsPerDevPixel());
     AutoRestoreTransform autoRestoreTransform(drawTarget);
     drawTarget->SetTransform(
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -55,16 +55,17 @@
 #include "mozilla/css/ImageLoader.h"
 #include "ImageContainer.h"
 #include "mozilla/Telemetry.h"
 #include "gfxUtils.h"
 #include "gfxGradientCache.h"
 #include "nsInlineFrame.h"
 #include "nsRubyTextContainerFrame.h"
 #include <algorithm>
+#include "SVGImageContext.h"
 
 using namespace mozilla;
 using namespace mozilla::css;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 using mozilla::CSSSizeOrRatio;
 
 static int gFrameTreeLockCount = 0;
@@ -5987,17 +5988,17 @@ nsImageRenderer::DrawBorderImageComponen
     SamplingFilter samplingFilter = nsLayoutUtils::GetSamplingFilterForFrame(mForFrame);
 
     if (!RequiresScaling(aFill, aHFill, aVFill, aUnitSize)) {
       return nsLayoutUtils::DrawSingleImage(*aRenderingContext.ThebesContext(),
                                             aPresContext,
                                             subImage,
                                             samplingFilter,
                                             aFill, aDirtyRect,
-                                            nullptr,
+                                            /* no SVGImageContext */ Nothing(),
                                             drawFlags);
     }
 
     nsSize repeatSize;
     nsRect fillRect(aFill);
     nsRect tile = ComputeTile(fillRect, aHFill, aVFill, aUnitSize, repeatSize);
     CSSIntSize imageSize(srcRect.width, srcRect.height);
     return nsLayoutUtils::DrawBackgroundImage(*aRenderingContext.ThebesContext(),
--- a/layout/reftests/forms/reftest.list
+++ b/layout/reftests/forms/reftest.list
@@ -1,10 +1,10 @@
 fuzzy-if(skiaContent,1,10) HTTP(..) == text-control-baseline-1.html text-control-baseline-1-ref.html
-fuzzy-if(cocoaWidget,16,64) fuzzy-if(Android,52,64) fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu),104,224) fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),57,400) == display-block-baselines-1.html display-block-baselines-1-ref.html # anti-aliasing issues
+fuzzy-if(cocoaWidget,16,64) fuzzy-if(Android,52,64) fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu),104,224) fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),88,400) == display-block-baselines-1.html display-block-baselines-1-ref.html # anti-aliasing issues
 == display-block-baselines-2.html display-block-baselines-2-ref.html
 == display-block-baselines-3.html display-block-baselines-3-ref.html
 == display-block-baselines-4.html display-block-baselines-4-ref.html
 fuzzy-if(Android,4,8) == display-block-baselines-5.html display-block-baselines-5-ref.html
 
 # button element
 include button/reftest.list
 
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -961,17 +961,25 @@ SheetLoadData::OnStreamComplete(nsIUnich
     }
   } else {
     nsAutoCString sourceUri;
     if (mLoader->mDocument && mLoader->mDocument->GetDocumentURI()) {
       mLoader->mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
     }
     nsresult rv = SRICheck::VerifyIntegrity(sriMetadata, aLoader, aBuffer,
                                             sourceUri, mLoader->mReporter);
-    mLoader->mReporter->FlushConsoleReports(mLoader->mDocument);
+
+    nsCOMPtr<nsILoadGroup> loadGroup;
+    channel->GetLoadGroup(getter_AddRefs(loadGroup));
+    if (loadGroup) {
+      mLoader->mReporter->FlushConsoleReports(loadGroup);
+    } else {
+      mLoader->mReporter->FlushConsoleReports(mLoader->mDocument);
+    }
+
     if (NS_FAILED(rv)) {
       LOG(("  Load was blocked by SRI"));
       MOZ_LOG(gSriPRLog, mozilla::LogLevel::Debug,
               ("css::Loader::OnStreamComplete, bad metadata"));
       mLoader->SheetComplete(this, NS_ERROR_SRI_CORRUPT);
       return NS_OK;
     }
   }
--- a/layout/svg/SVGContextPaint.cpp
+++ b/layout/svg/SVGContextPaint.cpp
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SVGContextPaint.h"
 
 #include "gfxContext.h"
+#include "gfxUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "nsIDocument.h"
 #include "nsSVGPaintServerFrame.h"
 #include "nsSVGEffects.h"
 #include "nsSVGPaintServerFrame.h"
 
 using namespace mozilla::gfx;
 
@@ -137,16 +138,27 @@ SVGContextPaint::InitStrokeGeometry(gfxC
 SVGContextPaint::GetContextPaint(nsIContent* aContent)
 {
   nsIDocument* ownerDoc = aContent->OwnerDoc();
 
   if (!ownerDoc->IsBeingUsedAsImage()) {
     return nullptr;
   }
 
+  // XXX The SVGContextPaint that was passed to SetProperty was const. Ideally
+  // we could and should re-apply that constness to the SVGContextPaint that
+  // we get here (SVGImageContext is never changed after it is initialized).
+  // Unfortunately lazy initialization of SVGContextPaint (which is a member of
+  // SVGImageContext, and also conceptually never changes after construction)
+  // prevents some of SVGContextPaint's conceptually const methods from being
+  // const.  Trying to fix SVGContextPaint (perhaps by using |mutable|) is a
+  // bit of a headache so for now we punt on that, don't reapply the constness
+  // to the SVGContextPaint here, and trust that no one will add code that
+  // actually modifies the object.
+
   return static_cast<SVGContextPaint*>(
            ownerDoc->GetProperty(nsGkAtoms::svgContextPaint));
 }
 
 already_AddRefed<gfxPattern>
 SVGContextPaintImpl::GetFillPattern(const DrawTarget* aDrawTarget,
                                     float aOpacity,
                                     const gfxMatrix& aCTM)
@@ -224,17 +236,17 @@ SVGContextPaintImpl::Paint::GetPattern(c
     return nullptr;
   }
 
   mPatternCache.Put(aOpacity, pattern);
   return pattern.forget();
 }
 
 AutoSetRestoreSVGContextPaint::AutoSetRestoreSVGContextPaint(
-                                 SVGContextPaint* aContextPaint,
+                                 const SVGContextPaint* aContextPaint,
                                  nsIDocument* aSVGDocument)
   : mSVGDocument(aSVGDocument)
   , mOuterContextPaint(aSVGDocument->GetProperty(nsGkAtoms::svgContextPaint))
 {
   // The way that we supply context paint is to temporarily set the context
   // paint on the owner document of the SVG that we're painting while it's
   // being painted.
 
@@ -242,25 +254,58 @@ AutoSetRestoreSVGContextPaint::AutoSetRe
   MOZ_ASSERT(aSVGDocument->IsBeingUsedAsImage(),
              "SVGContextPaint::GetContextPaint assumes this");
 
   if (mOuterContextPaint) {
     mSVGDocument->UnsetProperty(nsGkAtoms::svgContextPaint);
   }
 
   DebugOnly<nsresult> res =
-    mSVGDocument->SetProperty(nsGkAtoms::svgContextPaint, aContextPaint);
+    mSVGDocument->SetProperty(nsGkAtoms::svgContextPaint,
+                              const_cast<SVGContextPaint*>(aContextPaint));
 
   NS_WARNING_ASSERTION(NS_SUCCEEDED(res), "Failed to set context paint");
 }
 
 AutoSetRestoreSVGContextPaint::~AutoSetRestoreSVGContextPaint()
 {
   mSVGDocument->UnsetProperty(nsGkAtoms::svgContextPaint);
   if (mOuterContextPaint) {
     DebugOnly<nsresult> res =
       mSVGDocument->SetProperty(nsGkAtoms::svgContextPaint, mOuterContextPaint);
 
     NS_WARNING_ASSERTION(NS_SUCCEEDED(res), "Failed to restore context paint");
   }
 }
 
+
+// SVGEmbeddingContextPaint
+
+void
+SVGEmbeddingContextPaint::SetFill(nscolor aFill)
+{
+  mFill = new gfxPattern(ToDeviceColor(aFill));
+}
+
+void
+SVGEmbeddingContextPaint::SetStroke(nscolor aStroke)
+{
+  mStroke = new gfxPattern(ToDeviceColor(aStroke));
+}
+
+uint32_t
+SVGEmbeddingContextPaint::Hash() const
+{
+  uint32_t hash = 0;
+
+  Color color;
+
+  if (mFill && mFill->GetSolidColor(color)) {
+    hash = HashGeneric(hash, color.ToABGR());
+  }
+  if (mStroke && mStroke->GetSolidColor(color)) {
+    hash = HashGeneric(hash, color.ToABGR());
+  }
+
+  return hash;
+}
+
 } // namespace mozilla
--- a/layout/svg/SVGContextPaint.h
+++ b/layout/svg/SVGContextPaint.h
@@ -6,17 +6,19 @@
 #ifndef MOZILLA_SVGCONTEXTPAINT_H_
 #define MOZILLA_SVGCONTEXTPAINT_H_
 
 #include "DrawMode.h"
 #include "gfxMatrix.h"
 #include "gfxPattern.h"
 #include "gfxTypes.h"
 #include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/gfx/2D.h"
+#include "nsColor.h"
 #include "nsStyleStruct.h"
 #include "nsTArray.h"
 
 class gfxContext;
 class nsIDocument;
 class nsSVGPaintServerFrame;
 
 namespace mozilla {
@@ -27,25 +29,36 @@ namespace mozilla {
  * keywords. See:
  *
  *   https://www.w3.org/TR/SVG2/painting.html#context-paint
  *
  * This feature allows the color in an SVG-in-OpenType glyph to come from the
  * computed style for the text that is being drawn, for example, or for color
  * in an SVG embedded by an <img> element to come from the embedding <img>
  * element.
+ *
+ * This class is reference counted so that it can be shared among many similar
+ * SVGImageContext objects. (SVGImageContext objects are frequently
+ * copy-constructed with small modifications, and we'd like for those copies to
+ * be able to share their context-paint data cheaply.)  However, in most cases,
+ * SVGContextPaint instances are stored in a local RefPtr and only last for the
+ * duration of a function call.
+ * XXX Note: SVGImageContext doesn't actually have a SVGContextPaint member yet,
+ * but it will in a later patch in the patch series that added this comment.
  */
-class SVGContextPaint
+class SVGContextPaint : public RefCounted<SVGContextPaint>
 {
 protected:
   typedef mozilla::gfx::DrawTarget DrawTarget;
 
   SVGContextPaint() {}
 
 public:
+  MOZ_DECLARE_REFCOUNTED_TYPENAME(SVGContextPaint)
+
   virtual ~SVGContextPaint() {}
 
   virtual already_AddRefed<gfxPattern> GetFillPattern(const DrawTarget* aDrawTarget,
                                                       float aOpacity,
                                                       const gfxMatrix& aCTM) = 0;
   virtual already_AddRefed<gfxPattern> GetStrokePattern(const DrawTarget* aDrawTarget,
                                                         float aOpacity,
                                                         const gfxMatrix& aCTM) = 0;
@@ -76,16 +89,23 @@ public:
   gfxFloat GetStrokeDashOffset() {
     return mDashOffset;
   }
 
   gfxFloat GetStrokeWidth() {
     return mStrokeWidth;
   }
 
+  virtual uint32_t Hash() const {
+    MOZ_ASSERT_UNREACHABLE("Only VectorImage needs to hash, and that should "
+                           "only be operating on our SVGEmbeddingContextPaint "
+                           "subclass");
+    return 0;
+  }
+
 private:
   // Member-vars are initialized in InitStrokeGeometry.
   FallibleTArray<gfxFloat> mDashes;
   MOZ_INIT_OUTSIDE_CTOR gfxFloat mDashOffset;
   MOZ_INIT_OUTSIDE_CTOR gfxFloat mStrokeWidth;
 };
 
 /**
@@ -93,17 +113,17 @@ private:
  * piece of SVG is being painted.  The context paint is set on the SVG's owner
  * document, as expected by SVGContextPaint::GetContextPaint.  Any pre-existing
  * context paint is restored after this class removes the context paint that it
  * set.
  */
 class MOZ_RAII AutoSetRestoreSVGContextPaint
 {
 public:
-  AutoSetRestoreSVGContextPaint(SVGContextPaint* aContextPaint,
+  AutoSetRestoreSVGContextPaint(const SVGContextPaint* aContextPaint,
                                 nsIDocument* aSVGDocument);
   ~AutoSetRestoreSVGContextPaint();
 private:
   nsIDocument* mSVGDocument;
   // The context paint that needs to be restored by our dtor after it removes
   // aContextPaint:
   void* mOuterContextPaint;
 };
@@ -186,12 +206,56 @@ public:
 
   Paint mFillPaint;
   Paint mStrokePaint;
 
   float mFillOpacity;
   float mStrokeOpacity;
 };
 
+/**
+ * This class is used to pass context paint to an SVG image when an element
+ * references that image (e.g. via HTML <img> or SVG <image>, or by referencing
+ * it from a CSS property such as 'background-image').  In this case we only
+ * support context colors and not paint servers.
+ */
+class SVGEmbeddingContextPaint : public SVGContextPaint
+{
+public:
+  SVGEmbeddingContextPaint() {}
+
+  void SetFill(nscolor aFill);
+  void SetStroke(nscolor aStroke);
+
+  already_AddRefed<gfxPattern> GetFillPattern(const DrawTarget* aDrawTarget,
+                                              float aOpacity,
+                                              const gfxMatrix& aCTM) override {
+    return do_AddRef(mFill);
+  }
+
+  already_AddRefed<gfxPattern> GetStrokePattern(const DrawTarget* aDrawTarget,
+                                                float aOpacity,
+                                                const gfxMatrix& aCTM) override {
+    return do_AddRef(mStroke);
+  }
+
+  float GetFillOpacity() const override {
+    // Always 1.0f since we don't currently allow 'context-fill-opacity'
+    return 1.0f;
+  };
+
+  float GetStrokeOpacity() const override {
+    // Always 1.0f since we don't currently allow 'context-stroke-opacity'
+    return 1.0f;
+  };
+
+  uint32_t Hash() const override;
+
+private:
+  // Note: if these are set at all, they'll have type PatternType::COLOR.
+  RefPtr<gfxPattern> mFill;
+  RefPtr<gfxPattern> mStroke;
+};
+
 } // namespace mozilla
 
 #endif // MOZILLA_SVGCONTEXTPAINT_H_
 
--- a/layout/svg/SVGGeometryFrame.cpp
+++ b/layout/svg/SVGGeometryFrame.cpp
@@ -279,26 +279,19 @@ SVGGeometryFrame::BuildDisplayList(nsDis
 DrawResult
 SVGGeometryFrame::PaintSVG(gfxContext& aContext,
                            const gfxMatrix& aTransform,
                            const nsIntRect* aDirtyRect)
 {
   if (!StyleVisibility()->IsVisible())
     return DrawResult::SUCCESS;
 
-  gfxMatrix childToUserSpace = aTransform;
-  if (GetContent()->IsSVGElement()) {
-    childToUserSpace = static_cast<const nsSVGElement*>(GetContent())->
-                         PrependLocalTransformsTo(childToUserSpace,
-                                                  eChildToUserSpace);
-  }
-
   // Matrix to the geometry's user space:
   gfxMatrix newMatrix =
-    aContext.CurrentMatrix().PreMultiply(childToUserSpace).NudgeToIntegers();
+    aContext.CurrentMatrix().PreMultiply(aTransform).NudgeToIntegers();
   if (newMatrix.IsSingular()) {
     return DrawResult::BAD_ARGS;
   }
 
   uint32_t paintOrder = StyleSVG()->mPaintOrder;
   if (paintOrder == NS_STYLE_PAINT_ORDER_NORMAL) {
     Render(&aContext, eRenderFill | eRenderStroke, newMatrix);
     PaintMarkers(aContext, aTransform);
new file mode 100644
--- /dev/null
+++ b/layout/svg/SVGImageContext.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+// Main header first:
+#include "SVGImageContext.h"
+
+// Keep others in (case-insensitive) order:
+#include "gfxUtils.h"
+#include "mozilla/Preferences.h"
+#include "nsPresContext.h"
+
+namespace mozilla {
+
+bool
+SVGImageContext::MaybeStoreContextPaint(nsIFrame* aFromFrame)
+{
+  static bool sEnabledForContent = false;
+  static bool sEnabledForContentCached = false;
+
+  if (!sEnabledForContentCached) {
+    Preferences::AddBoolVarCache(&sEnabledForContent,
+                                 "svg.context-properties.content.enabled", false);
+    sEnabledForContentCached = true;
+  }
+
+  if (!sEnabledForContent &&
+      !aFromFrame->PresContext()->IsChrome()) {
+    // Context paint is pref'ed off for content and this is a content doc.
+    return false;
+  }
+
+  // XXX return early if the 'context-properties' property is not set.
+
+  bool haveContextPaint = false;
+
+  RefPtr<SVGEmbeddingContextPaint> contextPaint = new SVGEmbeddingContextPaint();
+
+  const nsStyleSVG* style = aFromFrame->StyleSVG();
+
+  // XXX don't set values for properties not listed in 'context-properties'.
+
+  if (style->mFill.Type() == eStyleSVGPaintType_Color) {
+    haveContextPaint = true;
+    contextPaint->SetFill(style->mFill.GetColor());
+  }
+  if (style->mStroke.Type() == eStyleSVGPaintType_Color) {
+    haveContextPaint = true;
+    contextPaint->SetStroke(style->mStroke.GetColor());
+  }
+
+  if (haveContextPaint) {
+    mContextPaint = contextPaint.forget();
+  }
+
+  return mContextPaint != nullptr;
+}
+
+} // namespace mozilla
--- a/layout/svg/SVGImageContext.h
+++ b/layout/svg/SVGImageContext.h
@@ -2,86 +2,109 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_SVGCONTEXT_H_
 #define MOZILLA_SVGCONTEXT_H_
 
 #include "mozilla/Maybe.h"
+#include "mozilla/SVGContextPaint.h"
 #include "SVGPreserveAspectRatio.h"
 #include "Units.h"
 
+class nsIFrame;
+
 namespace mozilla {
 
 // SVG image-specific rendering context. For imgIContainer::Draw.
 // Used to pass information such as
 //  - viewport information from CSS, and
 //  - overridden attributes from an SVG <image> element
 // to the image's internal SVG document when it's drawn.
 class SVGImageContext
 {
 public:
   SVGImageContext()
     : mGlobalOpacity(1.0)
   { }
 
   // Note: 'aIsPaintingSVGImageElement' should be used to indicate whether
   // the SVG image in question is being painted for an SVG <image> element.
-  SVGImageContext(CSSIntSize aViewportSize,
-                  Maybe<SVGPreserveAspectRatio> aPreserveAspectRatio,
-                  gfxFloat aOpacity = 1.0,
-                  bool aIsPaintingSVGImageElement = false)
+  explicit SVGImageContext(const CSSIntSize& aViewportSize,
+                           Maybe<SVGPreserveAspectRatio> aPreserveAspectRatio = Nothing(),
+                           gfxFloat aOpacity = 1.0,
+                           bool aIsPaintingSVGImageElement = false)
     : mViewportSize(aViewportSize)
     , mPreserveAspectRatio(aPreserveAspectRatio)
     , mGlobalOpacity(aOpacity)
     , mIsPaintingSVGImageElement(aIsPaintingSVGImageElement)
   { }
 
+  bool MaybeStoreContextPaint(nsIFrame* aFromFrame);
+
   const CSSIntSize& GetViewportSize() const {
     return mViewportSize;
   }
 
+  void SetViewportSize(const CSSIntSize& aSize) {
+    mViewportSize = aSize;
+  }
+
   const Maybe<SVGPreserveAspectRatio>& GetPreserveAspectRatio() const {
     return mPreserveAspectRatio;
   }
 
+  void SetPreserveAspectRatio(const Maybe<SVGPreserveAspectRatio>& aPAR) {
+    mPreserveAspectRatio = aPAR;
+  }
+
   gfxFloat GetGlobalOpacity() const {
     return mGlobalOpacity;
   }
 
+  const SVGContextPaint* GetContextPaint() const {
+    return mContextPaint.get();
+  }
+
   bool IsPaintingForSVGImageElement() const {
     return mIsPaintingSVGImageElement;
   }
 
   bool operator==(const SVGImageContext& aOther) const {
     return mViewportSize == aOther.mViewportSize &&
            mPreserveAspectRatio == aOther.mPreserveAspectRatio &&
            mGlobalOpacity == aOther.mGlobalOpacity &&
            mIsPaintingSVGImageElement == aOther.mIsPaintingSVGImageElement;
   }
 
   bool operator!=(const SVGImageContext& aOther) const {
     return !(*this == aOther);
   }
 
   uint32_t Hash() const {
-    return HashGeneric(mViewportSize.width,
+    uint32_t hash = 0;
+    if (mContextPaint) {
+      hash = HashGeneric(hash, mContextPaint->Hash());
+    }
+    return HashGeneric(hash,
+                       mViewportSize.width,
                        mViewportSize.height,
                        mPreserveAspectRatio.map(HashPAR).valueOr(0),
-                       HashBytes(&mGlobalOpacity, sizeof(gfxFloat)),
+                       HashBytes(&mGlobalOpacity, sizeof(mGlobalOpacity)),
                        mIsPaintingSVGImageElement);
   }
 
 private:
   static uint32_t HashPAR(const SVGPreserveAspectRatio& aPAR) {
     return aPAR.Hash();
   }
 
   // NOTE: When adding new member-vars, remember to update Hash() & operator==.
+  RefPtr<SVGContextPaint>       mContextPaint;
   CSSIntSize                    mViewportSize;
   Maybe<SVGPreserveAspectRatio> mPreserveAspectRatio;
   gfxFloat                      mGlobalOpacity;
   bool                          mIsPaintingSVGImageElement;
 };
 
 } // namespace mozilla
 
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -3652,20 +3652,20 @@ SVGTextFrame::PaintSVG(gfxContext& aCont
     // Determine how much of the left and right edges of the text frame we
     // need to ignore.
     SVGCharClipDisplayItem item(run);
 
     // Set up the fill and stroke so that SVG glyphs can get painted correctly
     // when they use context-fill etc.
     aContext.SetMatrix(initialMatrix);
 
-    SVGContextPaintImpl contextPaint;
-    DrawMode drawMode = contextPaint.Init(&aDrawTarget,
-                                          aContext.CurrentMatrix(),
-                                          frame, outerContextPaint);
+    RefPtr<SVGContextPaintImpl> contextPaint = new SVGContextPaintImpl();
+    DrawMode drawMode = contextPaint->Init(&aDrawTarget,
+                                           aContext.CurrentMatrix(),
+                                           frame, outerContextPaint);
 
     if (drawMode & DrawMode::GLYPH_STROKE) {
       // This may change the gfxContext's transform (for non-scaling stroke),
       // in which case this needs to happen before we call SetMatrix() below.
       nsSVGUtils::SetupCairoStrokeGeometry(frame, &aContext, outerContextPaint);
     }
 
     // Set up the transform for painting the text frame for the substring
@@ -3676,17 +3676,17 @@ SVGTextFrame::PaintSVG(gfxContext& aCont
     aContext.SetMatrix(runTransform);
 
     if (drawMode != DrawMode(0)) {
       bool paintSVGGlyphs;
       nsTextFrame::PaintTextParams params(rendCtx.ThebesContext());
       params.framePt = gfxPoint();
       params.dirtyRect = LayoutDevicePixel::
         FromAppUnits(frame->GetVisualOverflowRect(), auPerDevPx);
-      params.contextPaint = &contextPaint;
+      params.contextPaint = contextPaint;
       if (ShouldRenderAsPath(frame, paintSVGGlyphs)) {
         SVGTextDrawPathCallbacks callbacks(&rendCtx, frame,
                                            matrixForPaintServers,
                                            paintSVGGlyphs);
         params.callbacks = &callbacks;
         frame->PaintText(params, item);
       } else {
         frame->PaintText(params, item);
--- a/layout/svg/moz.build
+++ b/layout/svg/moz.build
@@ -55,16 +55,17 @@ UNIFIED_SOURCES += [
     'nsSVGUseFrame.cpp',
     'nsSVGUtils.cpp',
     'SVGContextPaint.cpp',
     'SVGFEContainerFrame.cpp',
     'SVGFEImageFrame.cpp',
     'SVGFELeafFrame.cpp',
     'SVGFEUnstyledLeafFrame.cpp',
     'SVGGeometryFrame.cpp',
+    'SVGImageContext.cpp',
     'SVGTextFrame.cpp',
     'SVGViewFrame.cpp',
 ]
 
 if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']:
     SOURCES += ['nsSVGMaskFrameNEON.cpp']
     SOURCES['nsSVGMaskFrameNEON.cpp'].flags += CONFIG['NEON_FLAGS']
 
--- a/layout/svg/nsSVGImageFrame.cpp
+++ b/layout/svg/nsSVGImageFrame.cpp
@@ -397,19 +397,20 @@ nsSVGImageFrame::PaintSVG(gfxContext& aC
 
     if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
       // Package up the attributes of this image element which can override the
       // attributes of mImageContainer's internal SVG document.  The 'width' &
       // 'height' values we're passing in here are in CSS units (though they
       // come from width/height *attributes* in SVG). They influence the region
       // of the SVG image's internal document that is visible, in combination
       // with preserveAspectRatio and viewBox.
-      SVGImageContext context(CSSIntSize::Truncate(width, height),
-                              Some(imgElem->mPreserveAspectRatio.GetAnimValue()),
-                              1.0, true);
+      const Maybe<const SVGImageContext> context(
+        Some(SVGImageContext(CSSIntSize::Truncate(width, height),
+                             Some(imgElem->mPreserveAspectRatio.GetAnimValue()),
+                             1.0, /* aIsPaintingSVGImageElement */ true)));
 
       // For the actual draw operation to draw crisply (and at the right size),
       // our destination rect needs to be |width|x|height|, *in dev pixels*.
       LayoutDeviceSize devPxSize(width, height);
       nsRect destRect(nsPoint(),
                       LayoutDevicePixel::ToAppUnits(devPxSize,
                                                     appUnitsPerDevPx));
 
@@ -418,17 +419,17 @@ nsSVGImageFrame::PaintSVG(gfxContext& aC
       // and that's not always true for TYPE_VECTOR images.
       result = nsLayoutUtils::DrawSingleImage(
         aContext,
         PresContext(),
         mImageContainer,
         nsLayoutUtils::GetSamplingFilterForFrame(this),
         destRect,
         aDirtyRect ? dirtyRect : destRect,
-        &context,
+        context,
         drawFlags);
     } else { // mImageContainer->GetType() == TYPE_RASTER
       result = nsLayoutUtils::DrawSingleUnscaledImage(
         aContext,
         PresContext(),
         mImageContainer,
         nsLayoutUtils::GetSamplingFilterForFrame(this),
         nsPoint(0, 0),
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -53,16 +53,17 @@
 #include "nsTextFrame.h"
 #include "SVGContentUtils.h"
 #include "SVGTextFrame.h"
 #include "mozilla/Unused.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
+using namespace mozilla::image;
 
 static bool sSVGPathCachingEnabled;
 static bool sSVGDisplayListHitTestingEnabled;
 static bool sSVGDisplayListPaintingEnabled;
 static bool sSVGNewGetBBoxEnabled;
 
 bool
 NS_SVGPathCachingEnabled()
--- a/layout/svg/nsSVGUtils.h
+++ b/layout/svg/nsSVGUtils.h
@@ -5,16 +5,17 @@
 
 #ifndef NS_SVGUTILS_H
 #define NS_SVGUTILS_H
 
 // include math.h to pick up definition of M_ maths defines e.g. M_PI
 #include <math.h>
 
 #include "DrawMode.h"
+#include "DrawResult.h"
 #include "gfx2DGlue.h"
 #include "gfxMatrix.h"
 #include "gfxPoint.h"
 #include "gfxRect.h"
 #include "mozilla/gfx/Rect.h"
 #include "nsAlgorithm.h"
 #include "nsChangeHint.h"
 #include "nsColor.h"
--- a/layout/xul/nsImageBoxFrame.cpp
+++ b/layout/xul/nsImageBoxFrame.cpp
@@ -47,16 +47,17 @@
 #include "ImageContainer.h"
 #include "nsIContent.h"
 
 #include "nsContentUtils.h"
 
 #include "mozilla/BasicEvents.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/Maybe.h"
+#include "SVGImageContext.h"
 
 #define ONLOAD_CALLED_TOO_EARLY 1
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 using namespace mozilla::layers;
 
@@ -404,17 +405,18 @@ nsImageBoxFrame::PaintImage(nsRenderingC
                                                 anchorPoint.ptr());
   }
 
 
   return nsLayoutUtils::DrawSingleImage(
            *aRenderingContext.ThebesContext(),
            PresContext(), imgCon,
            nsLayoutUtils::GetSamplingFilterForFrame(this),
-           dest, dirty, nullptr, aFlags,
+           dest, dirty,
+           /* no SVGImageContext */ Nothing(), aFlags,
            anchorPoint.ptrOr(nullptr),
            hasSubRect ? &mSubRect : nullptr);
 }
 
 void nsDisplayXULImage::Paint(nsDisplayListBuilder* aBuilder,
                               nsRenderingContext* aCtx)
 {
   // Even though we call StartDecoding when we get a new image we pass
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -2747,36 +2747,43 @@ HttpBaseChannel::AddConsoleReport(uint32
 {
   mReportCollector->AddConsoleReport(aErrorFlags, aCategory, aPropertiesFile,
                                      aSourceFileURI, aLineNumber,
                                      aColumnNumber, aMessageName,
                                      aStringParams);
 }
 
 void
+HttpBaseChannel::FlushReportsToConsole(uint64_t aInnerWindowID,
+                                       ReportAction aAction)
+{
+  mReportCollector->FlushReportsToConsole(aInnerWindowID, aAction);
+}
+
+void
 HttpBaseChannel::FlushConsoleReports(nsIDocument* aDocument,
                                      ReportAction aAction)
 {
   mReportCollector->FlushConsoleReports(aDocument, aAction);
 }
 
 void
+HttpBaseChannel::FlushConsoleReports(nsILoadGroup* aLoadGroup,
+                                     ReportAction aAction)
+{
+  mReportCollector->FlushConsoleReports(aLoadGroup, aAction);
+}
+
+void
 HttpBaseChannel::FlushConsoleReports(nsIConsoleReportCollector* aCollector)
 {
   mReportCollector->FlushConsoleReports(aCollector);
 }
 
 void
-HttpBaseChannel::FlushReportsByWindowId(uint64_t aWindowId,
-                                        ReportAction aAction)
-{
-  mReportCollector->FlushReportsByWindowId(aWindowId, aAction);
-}
-
-void
 HttpBaseChannel::ClearConsoleReports()
 {
   mReportCollector->ClearConsoleReports();
 }
 
 nsIPrincipal *
 HttpBaseChannel::GetURIPrincipal()
 {
@@ -2933,21 +2940,25 @@ HttpBaseChannel::DoNotifyListener()
 
   DoNotifyListenerCleanup();
 
   // If this is a navigation, then we must let the docshell flush the reports
   // to the console later.  The LoadDocument() is pointing at the detached
   // document that started the navigation.  We want to show the reports on the
   // new document.  Otherwise the console is wiped and the user never sees
   // the information.
-  if (!IsNavigation() && mLoadInfo) {
-    nsCOMPtr<nsIDOMDocument> dommyDoc;
-    mLoadInfo->GetLoadingDocument(getter_AddRefs(dommyDoc));
-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(dommyDoc);
-    FlushConsoleReports(doc);
+  if (!IsNavigation()) {
+    if (mLoadGroup) {
+      FlushConsoleReports(mLoadGroup);
+    } else if (mLoadInfo) {
+      nsCOMPtr<nsIDOMDocument> dommyDoc;
+      mLoadInfo->GetLoadingDocument(getter_AddRefs(dommyDoc));
+      nsCOMPtr<nsIDocument> doc = do_QueryInterface(dommyDoc);
+      FlushConsoleReports(doc);
+    }
   }
 }
 
 void
 HttpBaseChannel::AddCookiesToRequest()
 {
   if (mLoadFlags & LOAD_ANONYMOUS) {
     return;
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -273,27 +273,31 @@ public:
   AddConsoleReport(uint32_t aErrorFlags, const nsACString& aCategory,
                    nsContentUtils::PropertiesFile aPropertiesFile,
                    const nsACString& aSourceFileURI,
                    uint32_t aLineNumber, uint32_t aColumnNumber,
                    const nsACString& aMessageName,
                    const nsTArray<nsString>& aStringParams) override;
 
   void
+  FlushReportsToConsole(uint64_t aInnerWindowID,
+                        ReportAction aAction = ReportAction::Forget) override;
+
+  void
   FlushConsoleReports(nsIDocument* aDocument,
                       ReportAction aAction = ReportAction::Forget) override;
 
   void
+  FlushConsoleReports(nsILoadGroup* aLoadGroup,
+                      ReportAction aAction = ReportAction::Forget) override;
+
+  void
   FlushConsoleReports(nsIConsoleReportCollector* aCollector) override;
 
   void
-  FlushReportsByWindowId(uint64_t aWindowId,
-                         ReportAction aAction = ReportAction::Forget) override;
-
-  void
   ClearConsoleReports() override;
 
   class nsContentEncodings : public nsIUTF8StringEnumerator
     {
     public:
         NS_DECL_ISUPPORTS
         NS_DECL_NSIUTF8STRINGENUMERATOR
 
--- a/security/nss/Makefile
+++ b/security/nss/Makefile
@@ -91,25 +91,16 @@ endif
 ifdef CCC
 NSPR_CONFIGURE_ENV += CXX=$(CCC)
 endif
 # Remove -arch definitions. NSPR can't handle that.
 NSPR_CONFIGURE_ENV := $(filter-out -arch x86_64,$(NSPR_CONFIGURE_ENV))
 NSPR_CONFIGURE_ENV := $(filter-out -arch i386,$(NSPR_CONFIGURE_ENV))
 NSPR_CONFIGURE_ENV := $(filter-out -arch ppc,$(NSPR_CONFIGURE_ENV))
 
-ifdef SANITIZER_CFLAGS
-ifdef BUILD_OPT
-NSPR_CONFIGURE_OPTS += --enable-debug-symbols
-endif
-NSPR_CONFIGURE_ENV += CFLAGS='$(SANITIZER_CFLAGS)' \
-                      CXXFLAGS='$(SANITIZER_CFLAGS)' \
-                      LDFLAGS='$(SANITIZER_LDFLAGS)'
-endif
-
 #
 # Some pwd commands on Windows (for example, the pwd
 # command in Cygwin) return a pathname that begins
 # with a (forward) slash.  When such a pathname is
 # passed to Windows build tools (for example, cl), it
 # is mistaken as a command-line option.  If that is the case,
 # we use a relative pathname as NSPR's prefix on Windows.
 #
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-0a7ba014dbb3
+0750d7a0402b
--- a/security/nss/automation/taskcluster/graph/src/extend.js
+++ b/security/nss/automation/taskcluster/graph/src/extend.js
@@ -13,18 +13,17 @@ const WINDOWS_CHECKOUT_CMD =
     "(sleep 2; hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss) || " +
     "(sleep 5; hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss)\"";
 
 /*****************************************************************************/
 
 queue.filter(task => {
   if (task.group == "Builds") {
     // Remove extra builds on {A,UB}San and ARM.
-    if (task.collection == "asan" || task.collection == "arm-debug" ||
-        task.collection == "gyp-asan") {
+    if (task.collection == "asan" || task.collection == "arm-debug") {
       return false;
     }
 
     // Remove extra builds w/o libpkix for non-linux64-debug.
     if (task.symbol == "noLibpkix" &&
         (task.platform != "linux64" || task.collection != "debug")) {
       return false;
     }
@@ -43,26 +42,26 @@ queue.filter(task => {
   }
 
   // Temporarily disable SSL tests on ARM.
   if (task.tests == "ssl" && task.collection == "arm-debug") {
     return false;
   }
 
   // GYP builds with -Ddisable_libpkix=1 by default.
-  if ((task.collection == "gyp" || task.collection == "gyp-asan") &&
+  if ((task.collection == "gyp" || task.collection == "asan") &&
       task.tests == "chains") {
     return false;
   }
 
   return true;
 });
 
 queue.map(task => {
-  if (task.collection == "asan" || task.collection == "gyp-asan") {
+  if (task.collection == "asan") {
     // CRMF and FIPS tests still leak, unfortunately.
     if (task.tests == "crmf" || task.tests == "fips") {
       task.env.ASAN_OPTIONS = "detect_leaks=0";
     }
   }
 
   if (task.collection == "arm-debug") {
     // These tests take quite some time on our poor ARM devices.
@@ -113,44 +112,28 @@ export default async function main() {
       "-c",
       "bin/checkout.sh && nss/automation/taskcluster/scripts/build_gyp.sh"
     ],
     platform: "linux64",
     collection: "gyp",
     image: LINUX_IMAGE
   });
 
-  await scheduleLinux("Linux 64 (debug, gyp, asan, ubsan)", {
+  await scheduleLinux("Linux 64 (GYP, ASan, debug)", {
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && nss/automation/taskcluster/scripts/build_gyp.sh -g -v --ubsan --asan"
     ],
     env: {
       UBSAN_OPTIONS: "print_stacktrace=1",
       NSS_DISABLE_ARENA_FREE_LIST: "1",
       NSS_DISABLE_UNLOAD: "1",
       CC: "clang",
-      CCC: "clang++"
-    },
-    platform: "linux64",
-    collection: "gyp-asan",
-    image: LINUX_IMAGE
-  });
-
-  await scheduleLinux("Linux 64 (ASan, debug)", {
-    env: {
-      UBSAN_OPTIONS: "print_stacktrace=1",
-      NSS_DISABLE_ARENA_FREE_LIST: "1",
-      NSS_DISABLE_UNLOAD: "1",
-      CC: "clang",
       CCC: "clang++",
-      USE_UBSAN: "1",
-      USE_ASAN: "1",
-      USE_64: "1"
     },
     platform: "linux64",
     collection: "asan",
     image: LINUX_IMAGE
   });
 
   await scheduleWindows("Windows 2012 64 (opt)", {
     env: {BUILD_OPT: "1"}
--- a/security/nss/automation/taskcluster/graph/src/try_syntax.js
+++ b/security/nss/automation/taskcluster/graph/src/try_syntax.js
@@ -18,17 +18,17 @@ function parseOptions(opts) {
 
   // If the given value is nonsense default to debug and opt builds.
   if (builds.length == 0) {
     builds = ["d", "o"];
   }
 
   // Parse platforms.
   let allPlatforms = ["linux", "linux64", "linux64-asan", "win64", "arm",
-                      "linux64-gyp", "linux64-gyp-asan", "linux64-fuzz"];
+                      "linux64-gyp", "linux64-fuzz"];
   let platforms = intersect(opts.platform.split(/\s*,\s*/), allPlatforms);
 
   // If the given value is nonsense or "none" default to all platforms.
   if (platforms.length == 0 && opts.platform != "none") {
     platforms = allPlatforms;
   }
 
   // Parse unit tests.
@@ -103,33 +103,30 @@ function filter(opts) {
 
     // Filter by platform.
     let found = opts.platforms.some(platform => {
       let aliases = {
         "linux": "linux32",
         "linux64-asan": "linux64",
         "linux64-fuzz": "linux64",
         "linux64-gyp": "linux64",
-        "linux64-gyp-asan": "linux64",
         "win64": "windows2012-64",
         "arm": "linux32"
       };
 
       // Check the platform name.
       let keep = (task.platform == (aliases[platform] || platform));
 
       // Additional checks.
       if (platform == "linux64-asan") {
         keep &= coll("asan");
       } else if (platform == "arm") {
         keep &= coll("arm-opt") || coll("arm-debug");
       } else if (platform == "linux64-gyp") {
         keep &= coll("gyp");
-      } else if (platform == "linux64-gyp-asan") {
-        keep &= coll("gyp-asan");
       } else if (platform == "linux64-fuzz") {
         keep &= coll("fuzz");
       } else {
         keep &= coll("opt") || coll("debug");
       }
 
       return keep;
     });
--- a/security/nss/cmd/selfserv/selfserv.c
+++ b/security/nss/cmd/selfserv/selfserv.c
@@ -154,17 +154,17 @@ static PRLogModuleInfo *lm;
         fflush(stderr); \
     }
 #define VLOG(arg) PR_LOG(lm, PR_LOG_DEBUG, arg)
 
 static void
 PrintUsageHeader(const char *progName)
 {
     fprintf(stderr,
-            "Usage: %s -n rsa_nickname -p port [-BDENRbjlmrsuvx] [-w password]\n"
+            "Usage: %s -n rsa_nickname -p port [-BDENRZbjlmrsuvx] [-w password]\n"
             "         [-t threads] [-i pid_file] [-c ciphers] [-Y] [-d dbdir] [-g numblocks]\n"
             "         [-f password_file] [-L [seconds]] [-M maxProcs] [-P dbprefix]\n"
             "         [-V [min-version]:[max-version]] [-a sni_name]\n"
             "         [ T <good|revoked|unknown|badsig|corrupted|none|ocsp>] [-A ca]\n"
             "         [-C SSLCacheEntries] [-S dsa_nickname] -Q [-I groups]"
 #ifndef NSS_DISABLE_ECC
             " [-e ec_nickname]"
 #endif /* NSS_DISABLE_ECC */
@@ -214,25 +214,26 @@ PrintParameterUsage()
         "   badsig: use a good status but with an invalid signature\n"
         "   corrupted: stapled cert status is an invalid block of data\n"
         "   random: each connection uses a random status from this list:\n"
         "           good, revoked, unknown, failure, badsig, corrupted\n"
         "   ocsp: fetch from external OCSP server using AIA, or none\n"
         "-A <ca> Nickname of a CA used to sign a stapled cert status\n"
         "-U override default ECDHE ephemeral key reuse, 0: refresh, 1: reuse\n"
         "-H override default DHE server support, 0: disable, 1: enable, "
-        " 2: require DH named groups\n"
+        "   2: require DH named groups\n"
         "-W override default DHE server weak parameters support, 0: disable, 1: enable\n"
         "-c Restrict ciphers\n"
         "-Y prints cipher values allowed for parameter -c and exits\n"
         "-G enables the extended master secret extension [RFC7627]\n"
         "-Q enables ALPN for HTTP/1.1 [RFC7301]\n"
         "-I comma separated list of enabled groups for TLS key exchange.\n"
         "   The following values are valid:\n"
-        "   P256, P384, P521, x25519, FF2048, FF3072, FF4096, FF6144, FF8192\n",
+        "   P256, P384, P521, x25519, FF2048, FF3072, FF4096, FF6144, FF8192\n"
+        "-Z enable 0-RTT (for TLS 1.3; also use -u)\n",
         stderr);
 }
 
 static void
 Usage(const char *progName)
 {
     PrintUsageHeader(progName);
     PrintParameterUsage();
--- a/security/nss/coreconf/Darwin.mk
+++ b/security/nss/coreconf/Darwin.mk
@@ -140,11 +140,8 @@ SYS_SQLITE3_VERSION_MINOR := $(shell ech
 
 ifeq (3,$(SYS_SQLITE3_VERSION_MAJOR))
     ifeq (,$(filter-out 0 1 2 3 4,$(SYS_SQLITE3_VERSION_MINOR)))
         # sqlite <= 3.4.x is too old, it doesn't provide sqlite3_file_control
     else
         NSS_USE_SYSTEM_SQLITE = 1
     endif
 endif
-
-include $(CORE_DEPTH)/coreconf/sanitizers.mk
-DARWIN_SDK_SHLIBFLAGS += $(SANITIZER_LDFLAGS)
--- a/security/nss/coreconf/Linux.mk
+++ b/security/nss/coreconf/Linux.mk
@@ -142,19 +142,17 @@ endif
 DSO_CFLAGS		= -fPIC
 DSO_LDOPTS		= -shared $(ARCHFLAG) -Wl,--gc-sections
 # The linker on Red Hat Linux 7.2 and RHEL 2.1 (GNU ld version 2.11.90.0.8)
 # incorrectly reports undefined references in the libraries we link with, so
 # we don't use -z defs there.
 # Also, -z defs conflicts with Address Sanitizer, which emits relocations
 # against the libsanitizer runtime built into the main executable.
 ZDEFS_FLAG		= -Wl,-z,defs
-ifneq ($(USE_ASAN),1)
 DSO_LDOPTS		+= $(if $(findstring 2.11.90.0.8,$(shell ld -v)),,$(ZDEFS_FLAG))
-endif
 LDFLAGS			+= $(ARCHFLAG)
 
 # On Maemo, we need to use the -rpath-link flag for even the standard system
 # library directories.
 ifdef _SBOX_DIR
 LDFLAGS			+= -Wl,-rpath-link,/usr/lib:/lib
 endif
 
@@ -204,10 +202,8 @@ ifeq ($(OS_RELEASE),2.4)
 DEFINES += -DNO_FORK_CHECK
 endif
 
 ifdef USE_GCOV
 OS_CFLAGS += --coverage
 LDFLAGS += --coverage
 DSO_LDOPTS += --coverage
 endif
-
-include $(CORE_DEPTH)/coreconf/sanitizers.mk
--- a/security/nss/coreconf/arch.mk
+++ b/security/nss/coreconf/arch.mk
@@ -7,17 +7,17 @@
 # Master "Core Components" macros for getting the OS architecture     #
 # defines these symbols:
 # OS_ARCH	(from uname -r)
 # OS_TEST	(from uname -m)
 # OS_RELEASE	(from uname -v and/or -r)
 # OS_TARGET	User defined, or set to OS_ARCH
 # CPU_ARCH  	(from unmame -m or -p, ONLY on WINNT)
 # OS_CONFIG	OS_TARGET + OS_RELEASE
-# OBJDIR_TAG    (uses ASAN_TAG, GCOV_TAG, 64BIT_TAG)
+# OBJDIR_TAG    (uses GCOV_TAG, 64BIT_TAG)
 # OBJDIR_NAME
 #######################################################################
 
 #
 # Macros for getting the OS architecture
 #
 
 OS_ARCH := $(subst /,_,$(shell uname -s))
@@ -263,32 +263,27 @@ endif
 
 OS_CONFIG = $(OS_TARGET)$(OS_RELEASE)
 
 #
 # OBJDIR_TAG depends on the predefined variable BUILD_OPT,
 # to distinguish between debug and release builds.
 #
 
-ifeq ($(USE_ASAN), 1)
-    ASAN_TAG = _ASAN
-else
-    ASAN_TAG =
-endif
 ifeq ($(USE_GCOV), 1)
     GCOV_TAG = _GCOV
 else
     GCOV_TAG =
 endif
 ifeq ($(USE_64), 1)
     64BIT_TAG = _64
 else
     64BIT_TAG =
 endif
-OBJDIR_TAG_BASE=$(ASAN_TAG)$(GCOV_TAG)$(64BIT_TAG)
+OBJDIR_TAG_BASE=$(GCOV_TAG)$(64BIT_TAG)
 
 ifdef BUILD_OPT
     OBJDIR_TAG = $(OBJDIR_TAG_BASE)_OPT
 else
     ifdef BUILD_IDG
 	OBJDIR_TAG = $(OBJDIR_TAG_BASE)_IDG
     else
 	OBJDIR_TAG = $(OBJDIR_TAG_BASE)_DBG
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
deleted file mode 100644
--- a/security/nss/coreconf/sanitizers.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# Address Sanitizer support; include this in OS-specific .mk files
-# *after* defining the variables that are appended to here.
-
-ifeq ($(USE_ASAN), 1)
-SANITIZER_FLAGS_COMMON = -fsanitize=address
-
-ifeq ($(USE_UBSAN), 1)
-SANITIZER_FLAGS_COMMON += -fsanitize=undefined -fno-sanitize-recover=undefined
-endif
-
-ifeq ($(FUZZ), 1)
-SANITIZER_FLAGS_COMMON += -fsanitize-coverage=edge
-endif
-
-SANITIZER_FLAGS_COMMON += $(EXTRA_SANITIZER_FLAGS)
-SANITIZER_CFLAGS = $(SANITIZER_FLAGS_COMMON)
-SANITIZER_LDFLAGS = $(SANITIZER_FLAGS_COMMON)
-OS_CFLAGS += $(SANITIZER_CFLAGS)
-LDFLAGS += $(SANITIZER_LDFLAGS)
-
-# ASan needs frame pointers to save stack traces for allocation/free sites.
-# (Warning: some platforms, like ARM Linux in Thumb mode, don't have useful
-# frame pointers even with this option.)
-SANITIZER_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls
-
-ifdef BUILD_OPT
-# You probably want to be able to get debug info for failures, even with an
-# optimized build.
-OPTIMIZER += -g
-else
-# Try maintaining reasonable performance, ASan and UBSan slow things down.
-OPTIMIZER += -O1
-endif
-
-endif
--- a/security/nss/fuzz/clone_libfuzzer.sh
+++ b/security/nss/fuzz/clone_libfuzzer.sh
@@ -1,22 +1,22 @@
 #!/bin/sh
 
 d=$(dirname $0)
-$d/git-copy.sh https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer 33c20f597a2e312611d52677ff0fdd9335b485b7 $d/libFuzzer
+$d/git-copy.sh https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer 0b27dad707a1d67ec854423e25b1a521c9d5ab7a $d/libFuzzer
 
 # [https://llvm.org/bugs/show_bug.cgi?id=31318]
 # This prevents a known buffer overrun that won't be fixed as the affected code
 # will go away in the near future. Until that is we have to patch it as we seem
 # to constantly run into it.
 cat <<EOF | patch -p0 -d $d
 diff --git libFuzzer/FuzzerLoop.cpp libFuzzer/FuzzerLoop.cpp
 --- libFuzzer/FuzzerLoop.cpp
 +++ libFuzzer/FuzzerLoop.cpp
-@@ -472,6 +472,9 @@
+@@ -476,6 +476,9 @@
    uint8_t dummy;
    ExecuteCallback(&dummy, 0);
 
 +  // Number of counters might have changed.
 +  PrepareCounters(&MaxCoverage);
 +
    for (const auto &U : *InitialCorpus) {
      if (size_t NumFeatures = RunOne(U)) {
@@ -25,19 +25,19 @@ EOF
 
 # Latest Libfuzzer uses __sanitizer_dump_coverage(), a symbol to be introduced
 # with LLVM 4.0. To keep our code working with LLVM 3.x to simplify development
 # of fuzzers we'll just provide it ourselves.
 cat <<EOF | patch -p0 -d $d
 diff --git libFuzzer/FuzzerTracePC.cpp libFuzzer/FuzzerTracePC.cpp
 --- libFuzzer/FuzzerTracePC.cpp
 +++ libFuzzer/FuzzerTracePC.cpp
-@@ -24,6 +24,12 @@
- #include <set>
- #include <sstream>
+@@ -31,6 +31,12 @@
+     __sancov_trace_pc_guard_8bit_counters[fuzzer::TracePC::kNumPCs];
+ uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs];
 
 +#if defined(__clang_major__) && (__clang_major__ == 3)
 +void __sanitizer_dump_coverage(const uintptr_t *pcs, uintptr_t len) {
 +  // SanCov in LLVM 4.x will provide this symbol. Make 3.x work.
 +}
 +#endif
 +
  namespace fuzzer {
--- a/security/nss/fuzz/fuzz.gyp
+++ b/security/nss/fuzz/fuzz.gyp
@@ -37,36 +37,17 @@
         '<(DEPTH)/lib/pkcs7/pkcs7.gyp:pkcs7',
         # This is a static build of pk11wrap, softoken, and freebl.
         '<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static',
       ],
       'conditions': [
         ['fuzz_oss==0', {
           'type': 'static_library',
           'sources': [
-            'libFuzzer/FuzzerCrossOver.cpp',
-            'libFuzzer/FuzzerDriver.cpp',
-            'libFuzzer/FuzzerExtFunctionsDlsym.cpp',
-            'libFuzzer/FuzzerExtFunctionsWeak.cpp',
-            'libFuzzer/FuzzerExtFunctionsWeakAlias.cpp',
-            'libFuzzer/FuzzerIO.cpp',
-            'libFuzzer/FuzzerIOPosix.cpp',
-            'libFuzzer/FuzzerIOWindows.cpp',
-            'libFuzzer/FuzzerLoop.cpp',
-            'libFuzzer/FuzzerMain.cpp',
-            'libFuzzer/FuzzerMerge.cpp',
-            'libFuzzer/FuzzerMutate.cpp',
-            'libFuzzer/FuzzerSHA1.cpp',
-            'libFuzzer/FuzzerTracePC.cpp',
-            'libFuzzer/FuzzerTraceState.cpp',
-            'libFuzzer/FuzzerUtil.cpp',
-            'libFuzzer/FuzzerUtilDarwin.cpp',
-            'libFuzzer/FuzzerUtilLinux.cpp',
-            'libFuzzer/FuzzerUtilPosix.cpp',
-            'libFuzzer/FuzzerUtilWindows.cpp',
+            '<!@(ls <(DEPTH)/fuzz/libFuzzer/*.cpp)',
           ],
           'cflags/': [
             ['exclude', '-fsanitize-coverage'],
           ],
           'xcode_settings': {
             'OTHER_CFLAGS/': [
               ['exclude', '-fsanitize-coverage'],
             ],
--- a/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
@@ -118,16 +118,28 @@ TEST_F(TlsConnectTest, TestFallbackFromT
                            SSL_LIBRARY_VERSION_TLS_1_2);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   ConnectExpectFail();
   ASSERT_EQ(SSL_ERROR_RX_MALFORMED_SERVER_HELLO, client_->error_code());
 }
 #endif
 
+TEST_P(TlsConnectGeneric, TestFallbackSCSVVersionMatch) {
+  client_->SetFallbackSCSVEnabled(true);
+  Connect();
+}
+
+TEST_P(TlsConnectGenericPre13, TestFallbackSCSVVersionMismatch) {
+  client_->SetFallbackSCSVEnabled(true);
+  server_->SetVersionRange(version_, version_ + 1);
+  ConnectExpectFail();
+  client_->CheckErrorCode(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT);
+}
+
 // The TLS v1.3 spec section C.4 states that 'Implementations MUST NOT send or
 // accept any records with a version less than { 3, 0 }'. Thus we will not
 // allow version ranges including both SSL v3 and TLS v1.3.
 TEST_F(TlsConnectTest, DisallowSSLv3HelloWithTLSv13Enabled) {
   SECStatus rv;
   SSLVersionRange vrange = {SSL_LIBRARY_VERSION_3_0,
                             SSL_LIBRARY_VERSION_TLS_1_3};
 
--- a/security/nss/gtests/ssl_gtest/tls_agent.cc
+++ b/security/nss/gtests/ssl_gtest/tls_agent.cc
@@ -366,16 +366,24 @@ void TlsAgent::SetSessionCacheEnabled(bo
 void TlsAgent::Set0RttEnabled(bool en) {
   EXPECT_TRUE(EnsureTlsSetup());
 
   SECStatus rv =
       SSL_OptionSet(ssl_fd_, SSL_ENABLE_0RTT_DATA, en ? PR_TRUE : PR_FALSE);
   EXPECT_EQ(SECSuccess, rv);
 }
 
+void TlsAgent::SetFallbackSCSVEnabled(bool en) {
+  EXPECT_TRUE(role_ == CLIENT && EnsureTlsSetup());
+
+  SECStatus rv =
+      SSL_OptionSet(ssl_fd_, SSL_ENABLE_FALLBACK_SCSV, en ? PR_TRUE : PR_FALSE);
+  EXPECT_EQ(SECSuccess, rv);
+}
+
 void TlsAgent::SetShortHeadersEnabled() {
   EXPECT_TRUE(EnsureTlsSetup());
 
   SECStatus rv = SSLInt_EnableShortHeaders(ssl_fd_);
   EXPECT_EQ(SECSuccess, rv);
 }
 
 void TlsAgent::SetVersionRange(uint16_t minver, uint16_t maxver) {
--- a/security/nss/gtests/ssl_gtest/tls_agent.h
+++ b/security/nss/gtests/ssl_gtest/tls_agent.h
@@ -126,16 +126,17 @@ class TlsAgent : public PollTarget {
 
   void SetupClientAuth();
   void RequestClientAuth(bool requireAuth);
 
   void ConfigureSessionCache(SessionResumptionMode mode);
   void SetSessionTicketsEnabled(bool en);
   void SetSessionCacheEnabled(bool en);
   void Set0RttEnabled(bool en);
+  void SetFallbackSCSVEnabled(bool en);
   void SetShortHeadersEnabled();
   void SetVersionRange(uint16_t minver, uint16_t maxver);
   void GetVersionRange(uint16_t* minver, uint16_t* maxver);
   void CheckPreliminaryInfo();
   void ResetPreliminaryInfo();
   void SetExpectedVersion(uint16_t version);
   void SetServerKeyBits(uint16_t bits);
   void ExpectReadWriteError();
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -8549,17 +8549,17 @@ ssl3_HandleClientHello(sslSocket *ss, SS
     /* Now parse the rest of the extensions. */
     rv = ssl3_HandleParsedExtensions(ss, client_hello);
     if (rv != SECSuccess) {
         goto loser; /* malformed */
     }
 
     /* If the ClientHello version is less than our maximum version, check for a
      * TLS_FALLBACK_SCSV and reject the connection if found. */
-    if (ss->vrange.max > ss->clientHelloVersion) {
+    if (ss->vrange.max > ss->version) {
         for (i = 0; i + 1 < suites.len; i += 2) {
             PRUint16 suite_i = (suites.data[i] << 8) | suites.data[i + 1];
             if (suite_i != TLS_FALLBACK_SCSV)
                 continue;
             desc = inappropriate_fallback;
             errCode = SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT;
             goto alert_loser;
         }
--- a/security/nss/nss-tool/common/argparse.h
+++ b/security/nss/nss-tool/common/argparse.h
@@ -8,22 +8,22 @@
 #include <string>
 #include <unordered_map>
 #include <vector>
 
 class ArgParser {
  public:
   ArgParser(const std::vector<std::string>& arguments);
 
-  bool Has(std::string arg) { return programArgs_.count(arg) > 0; }
+  bool Has(std::string arg) const { return programArgs_.count(arg) > 0; }
 
-  std::string Get(std::string arg) { return programArgs_[arg]; }
+  std::string Get(std::string arg) const { return programArgs_.at(arg); }
 
-  size_t GetPositionalArgumentCount() { return positionalArgs_.size(); }
-  std::string GetPositionalArgument(size_t pos) {
+  size_t GetPositionalArgumentCount() const { return positionalArgs_.size(); }
+  std::string GetPositionalArgument(size_t pos) const {
     return positionalArgs_.at(pos);
   }
 
  private:
   std::unordered_map<std::string, std::string> programArgs_;
   std::vector<std::string> positionalArgs_;
 };
 
--- a/security/nss/nss-tool/db/dbtool.cc
+++ b/security/nss/nss-tool/db/dbtool.cc
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "dbtool.h"
 #include "argparse.h"
 #include "scoped_ptrs.h"
 
 #include <dirent.h>
+#include <fstream>
 #include <iomanip>
 #include <iostream>
 #include <memory>
 #include <regex>
 #include <sstream>
 
 #include <cert.h>
 #include <certdb.h>
@@ -46,38 +47,60 @@ static std::string PrintFlags(unsigned i
     ss << "I";
   }
   if (flags & CERTDB_GOVT_APPROVED_CA) {
     ss << "G";
   }
   return ss.str();
 }
 
+static std::vector<char> ReadFromIstream(std::istream &is) {
+  std::vector<char> certData;
+  while (is) {
+    char buf[1024];
+    is.read(buf, sizeof(buf));
+    certData.insert(certData.end(), buf, buf + is.gcount());
+  }
+
+  return certData;
+}
+
 void DBTool::Usage() {
-  std::cerr << "Usage: nss db [--path <directory>] [--create] --list-certs"
+  std::cerr << "Usage: nss db [--path <directory>]" << std::endl;
+  std::cerr << "  --create" << std::endl;
+  std::cerr << "  --list-certs" << std::endl;
+  std::cerr << "  --import-cert [<path>] --name <name> [--trusts <trusts>]"
             << std::endl;
 }
 
 bool DBTool::Run(const std::vector<std::string> &arguments) {
   ArgParser parser(arguments);
 
+  if (!parser.Has("--create") && !parser.Has("--list-certs") &&
+      !parser.Has("--import-cert")) {
+    return false;
+  }
+
+  PRAccessHow how = PR_ACCESS_READ_OK;
+  bool readOnly = true;
+  if (parser.Has("--create") || parser.Has("--import-cert")) {
+    how = PR_ACCESS_WRITE_OK;
+    readOnly = false;
+  }
+
   std::string initDir(".");
   if (parser.Has("--path")) {
     initDir = parser.Get("--path");
-    if (PR_Access(initDir.c_str(), PR_ACCESS_READ_OK) != PR_SUCCESS) {
-      std::cerr << "Directory '" << initDir
-                << "' does not exist or you don't have permissions!"
-                << std::endl;
-      return false;
-    }
+  }
+  if (PR_Access(initDir.c_str(), how) != PR_SUCCESS) {
+    std::cerr << "Directory '" << initDir
+              << "' does not exist or you don't have permissions!" << std::endl;
+    return false;
   }
 
-  if (!parser.Has("--list-certs") && !parser.Has("--create")) {
-    return false;
-  }
   std::cout << "Using database directory: " << initDir << std::endl
             << std::endl;
 
   bool dbFilesExist = PathHasDBFiles(initDir);
   if (parser.Has("--create") && dbFilesExist) {
     std::cerr << "Trying to create database files in a directory where they "
                  "already exists. Delete the db files before creating new ones."
               << std::endl;
@@ -88,38 +111,39 @@ bool DBTool::Run(const std::vector<std::
     std::cerr << "Create them using 'nss db --create [--path /foo/bar]' before "
                  "continuing."
               << std::endl;
     return false;
   }
 
   // init NSS
   const char *certPrefix = "";  // certutil -P option  --- can leave this empty
-  SECStatus rv =
-      NSS_Initialize(initDir.c_str(), certPrefix, certPrefix, "secmod.db", 0);
+  SECStatus rv = NSS_Initialize(initDir.c_str(), certPrefix, certPrefix,
+                                "secmod.db", readOnly ? NSS_INIT_READONLY : 0);
   if (rv != SECSuccess) {
     std::cerr << "NSS init failed!" << std::endl;
     return false;
   }
 
+  bool ret = true;
   if (parser.Has("--list-certs")) {
     ListCertificates();
-  }
-
-  if (parser.Has("--create")) {
+  } else if (parser.Has("--import-cert")) {
+    ret = ImportCertificate(parser);
+  } else if (parser.Has("--create")) {
     std::cout << "DB files created successfully." << std::endl;
   }
 
   // shutdown nss
   if (NSS_Shutdown() != SECSuccess) {
     std::cerr << "NSS Shutdown failed!" << std::endl;
     return false;
   }
 
-  return true;
+  return ret;
 }
 
 bool DBTool::PathHasDBFiles(std::string path) {
   std::regex certDBPattern("cert.*\\.db");
   std::regex keyDBPattern("key.*\\.db");
 
   DIR *dir;
   if (!(dir = opendir(path.c_str()))) {
@@ -180,8 +204,78 @@ void DBTool::ListCertificates() {
       trusts = ss.str();
     } else {
       trusts = ",,";
     }
     std::cout << std::setw(60) << std::left << name << " " << trusts
               << std::endl;
   }
 }
+
+bool DBTool::ImportCertificate(const ArgParser &parser) {
+  if (!parser.Has("--name")) {
+    std::cerr << "A name (--name) is required to import a certificate."
+              << std::endl;
+    return false;
+  }
+
+  std::string derFilePath = parser.Get("--import-cert");
+  std::string certName = parser.Get("--name");
+  std::string trustString("TCu,Cu,Tu");
+  if (parser.Has("--trusts")) {
+    trustString = parser.Get("--trusts");
+  }
+
+  CERTCertTrust trust;
+  SECStatus rv = CERT_DecodeTrustString(&trust, trustString.c_str());
+  if (rv != SECSuccess) {
+    std::cerr << "Cannot decode trust string!" << std::endl;
+    return false;
+  }
+
+  ScopedPK11SlotInfo slot = ScopedPK11SlotInfo(PK11_GetInternalKeySlot());
+  if (slot.get() == nullptr) {
+    std::cerr << "Error: Init PK11SlotInfo failed!\n";
+    return false;
+  }
+
+  std::vector<char> certData;
+  if (derFilePath.empty()) {
+    std::cout << "No Certificate file path given, using stdin." << std::endl;
+    certData = ReadFromIstream(std::cin);
+  } else {
+    std::ifstream is(derFilePath, std::ifstream::binary);
+    if (!is.good()) {
+      std::cerr << "IO Error when opening " << derFilePath << std::endl;
+      std::cerr
+          << "Certificate file does not exist or you don't have permissions."
+          << std::endl;
+      return false;
+    }
+    certData = ReadFromIstream(is);
+  }
+
+  ScopedCERTCertificate cert(
+      CERT_DecodeCertFromPackage(certData.data(), certData.size()));
+  if (cert.get() == nullptr) {
+    std::cerr << "Error: Could not decode certificate!" << std::endl;
+    return false;
+  }
+
+  rv = PK11_ImportCert(slot.get(), cert.get(), CK_INVALID_HANDLE,
+                       certName.c_str(), PR_FALSE);
+  if (rv != SECSuccess) {
+    // TODO handle authentication -> PK11_Authenticate (see certutil.c line
+    // 134)
+    std::cerr << "Error: Could not add certificate to database!" << std::endl;
+    return false;
+  }
+
+  rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert.get(), &trust);
+  if (rv != SECSuccess) {
+    std::cerr << "Cannot change cert's trust" << std::endl;
+    return false;
+  }
+
+  std::cout << "Certificate import was successful!" << std::endl;
+  // TODO show information about imported certificate
+  return true;
+}
--- a/security/nss/nss-tool/db/dbtool.h
+++ b/security/nss/nss-tool/db/dbtool.h
@@ -2,21 +2,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef dbtool_h__
 #define dbtool_h__
 
 #include <string>
 #include <vector>
+#include "argparse.h"
 
 class DBTool {
  public:
   bool Run(const std::vector<std::string>& arguments);
 
   void Usage();
 
  private:
   bool PathHasDBFiles(std::string path);
   void ListCertificates();
+  bool ImportCertificate(const ArgParser& parser);
 };
 
 #endif  // dbtool_h__
--- a/security/nss/nss-tool/nss_tool.cc
+++ b/security/nss/nss-tool/nss_tool.cc
@@ -8,17 +8,17 @@
 
 #include <prinit.h>
 
 #include "argparse.h"
 #include "db/dbtool.h"
 
 static void Usage() {
   std::cerr << "Usage: nss <command> <subcommand> [options]" << std::endl;
-  std::cerr << "       nss db [--path <directory>] --list-certs" << std::endl;
+  std::cerr << "       nss db [--path <directory>] <commands>" << std::endl;
 }
 
 int main(int argc, char **argv) {
   if (argc < 2) {
     Usage();
     return 1;
   }
 
--- a/security/nss/tests/all.sh
+++ b/security/nss/tests/all.sh
@@ -57,17 +57,16 @@
 # -----------------------------------------------------------
 #   HOST         - test machine host name
 #   DOMSUF       - test machine domain name
 #
 # Optional environment variables to specify build to use:
 # -------------------------------------------------------
 #   BUILT_OPT    - use optimized/debug build
 #   USE_64       - use 64bit/32bit build
-#   USE_ASAN     - use Address Sanitizer build
 #
 # Optional environment variables to enable specific NSS features:
 # ---------------------------------------------------------------
 #   NSS_DISABLE_ECC             - disable ECC
 #
 # Optional environment variables to select which cycles/suites to test:
 # ---------------------------------------------------------------------
 #   NSS_CYCLES     - list of cycles to run (separated by space
--- a/taskcluster/taskgraph/target_tasks.py
+++ b/taskcluster/taskgraph/target_tasks.py
@@ -133,16 +133,31 @@ def target_tasks_graphics(full_task_grap
 
     def filter(task):
         if task.attributes['kind'] == 'artifact-build':
             return False
         return True
     return [l for l in filtered_for_project if filter(full_task_graph[l])]
 
 
+@_target_task('mochitest_valgrind')
+def target_tasks_valgrind(full_task_graph, parameters):
+    """Target tasks that only run on the cedar branch."""
+    def filter(task):
+        platform = task.attributes.get('build_platform')
+        # only select platforms
+        if platform not in ['linux64']:
+            return False
+        if task.attributes.get('unittest_suite'):
+            if not (task.attributes['unittest_suite'].startswith('mochitest-valgrind')):
+                return False
+        return True
+    return [l for l, t in full_task_graph.tasks.iteritems() if filter(t)]
+
+
 @_target_task('nightly_fennec')
 def target_tasks_nightly(full_task_graph, parameters):
     """Select the set of tasks required for a nightly build of fennec. The
     nightly build process involves a pipeline of builds, signing,
     and, eventually, uploading the tasks to balrog."""
     def filter(task):
         platform = task.attributes.get('build_platform')
         if platform in ('android-api-15-nightly', 'android-x86-nightly'):
--- a/tools/profiler/lul/LulMain.cpp
+++ b/tools/profiler/lul/LulMain.cpp
@@ -10,16 +10,17 @@
 #include <stdlib.h>
 #include <stdio.h>
 
 #include <algorithm>  // std::sort
 #include <string>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryChecking.h"
 #include "mozilla/Sprintf.h"
 
 #include "LulCommonExt.h"
 #include "LulElfExt.h"
 
 #include "LulMainInt.h"
@@ -29,16 +30,17 @@
 // Set this to 1 for verbose logging
 #define DEBUG_MAIN 0
 
 namespace lul {
 
 using std::string;
 using std::vector;
 using std::pair;
+using mozilla::CheckedInt;
 using mozilla::DebugOnly;
 
 
 // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
 //
 // Some functions in this file are marked RUNS IN NO-MALLOC CONTEXT.
 // Any such function -- and, hence, the transitive closure of those
 // reachable from it -- must not do any dynamic memory allocation.
@@ -1009,23 +1011,40 @@ LUL::CountMappings()
 
 // RUNS IN NO-MALLOC CONTEXT
 static
 TaggedUWord DerefTUW(TaggedUWord aAddr, const StackImage* aStackImg)
 {
   if (!aAddr.Valid()) {
     return TaggedUWord();
   }
+
+  // Lower limit check.  |aAddr.Value()| is the lowest requested address
+  // and |aStackImg->mStartAvma| is the lowest address we actually have,
+  // so the comparison is straightforward.
   if (aAddr.Value() < aStackImg->mStartAvma) {
     return TaggedUWord();
   }
-  if (aAddr.Value() + sizeof(uintptr_t) > aStackImg->mStartAvma
-                                          + aStackImg->mLen) {
+
+  // Upper limit check.  We must compute the highest requested address
+  // and the highest address we actually have, but being careful to
+  // avoid overflow.  In particular if |aAddr| is 0xFFF...FFF or the
+  // 3/7 values below that, then we will get overflow.  See bug #1245477.
+  typedef CheckedInt<uintptr_t> CheckedUWord;
+  CheckedUWord highest_requested_plus_one
+    = CheckedUWord(aAddr.Value()) + CheckedUWord(sizeof(uintptr_t));
+  CheckedUWord highest_available_plus_one
+    = CheckedUWord(aStackImg->mStartAvma) + CheckedUWord(aStackImg->mLen);
+  if (!highest_requested_plus_one.isValid()     // overflow?
+      || !highest_available_plus_one.isValid()  // overflow?
+      || (highest_requested_plus_one.value()
+          > highest_available_plus_one.value())) { // in range?
     return TaggedUWord();
   }
+
   return TaggedUWord(*(uintptr_t*)(aStackImg->mContents + aAddr.Value()
                                    - aStackImg->mStartAvma));
 }
 
 // RUNS IN NO-MALLOC CONTEXT
 static
 TaggedUWord EvaluateReg(int16_t aReg, const UnwindRegs* aOldRegs,
                         TaggedUWord aCFA)