Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 06 Feb 2017 17:45:21 -0800
changeset 479651 af8a2573d0f1e9cc6f2ba0ab67d7a702a197f177
parent 479650 4f8b2d44eaa0c67bc7e7f5bac4e41e9a4b2e27c7 (current diff)
parent 479484 88c906492f0eaaf9cbdf8da5b85611da7a360631 (diff)
child 479652 fd17cb984a9e0986c6d386222629e65e2b0a8fee
child 479654 349ff3ae0ee2d96e6c832aceddd599781452b0db
child 479655 d0596a7e4bb9c9f05db9dd255d77bf547a507135
child 479699 f0453084d86e87070b2894eab61a2b58f1964768
child 479716 d6d1dd7f123f83de13b77e62ad492ccb8cb083bc
child 479732 fe8c2251ae07dd0e74fd7908aec9c0b0cde40051
child 479733 fe9fd318cf2f4e8ef5473a96ed0459992e1badb3
child 479739 194afec5316f77ef455f420f81b228d54bb7496e
child 479764 d917b6ed67c695e2bc8ec37f82b0007b711e0fbc
child 479769 6f2c5f62bab336e5cc2ca330ba9329d4b9b48561
child 479794 4ac6bc4a9ef54addc04adb480d896f8ae3b83198
child 479802 ef77e5850e4d0c07368a0a04fa5074d461299203
child 479811 880499399abb9a3afca6a643e48ac61d109c3059
child 479820 cfc9317005af772987ffdc0e124331a9edbe17c7
child 479825 7727e7ef59fbdb2dff7dbc9373ff8c38fb8c60e5
child 479830 5c90730881cea96b6b843f65f87a41b3e32c61c5
child 479832 e2631857f028c5e8c19c09ee4770324eba987d58
child 479833 3b1e409f9bf72bb32a2a8e00ea0fd108456be85f
child 479835 4f59b36b5363ce5ca261c5a9de83eb3e10695a4e
child 479838 5a33b4de287747f45b13c00526ff970c71959ec7
child 479839 6197cce21f7fc9c1ed4f1c66a6a792b1fac119c2
child 479841 a516dc0f31091954b9bcc88ba8f71a3073813df7
child 479843 f5eb9ed78cffe1edb8756fe0d1eb68af70e871a0
child 479850 1243724a93f1c8b7f80eca4ddbe80cf2af9b5a05
child 479854 92097c501b689b550007a1abc6f82064a48342ec
child 479855 b40c14a0f5bdaf9120c7874ca81d634d708b091c
child 479856 b4dd94fc69cd74f6be26847b34d6c0c5b839d272
child 479884 330ab4695793c499110152a6582afca8e87ae46c
child 479919 2019f3d5f8491b68de6b484f058ed32990959f47
child 479923 39a36b5052e69e395d67cc2243b5e3eff226552a
child 479943 48e3ac9cfa286b0b9353f2aeba3ac0221a7f8a4b
child 479946 952d4eb2c9ced114530e1f63f50631f6ddf61458
child 479949 036beaa822961b25ccc38582a674c2bcdf320d38
child 479952 4fb0dd3a2b337f967d7bfe16cbcb8264f6c706fa
child 479954 24cb0355279d837799e3ce0a8eac5ec8b990df77
child 479960 4d56ff6ba1e0e6301ac7364b3fb094eea8fca187
child 479963 a5e1d7379bd9a27c2d7d9fc4655022d776eb3912
child 480339 968a864242d5646d10af4bb89dc3a0505269154b
child 480412 e14fa1919144a87094d08586a003b60518eb2c5f
child 480413 6a12b172b37e48c2883a17fd7451ff74dab1b99c
child 480414 69c626cf033df4765d5b14f5bf3def1eb204d081
child 480424 c72f3db67bb6dd21c6eea0db5d59be664d7276db
child 480441 d0786aef510d386e817983e93a5d2171e838aca7
child 480449 7cea8331cffdcbfddc83b820be6746e2b8e6ffd8
child 480457 9a8347dc184960d84e76275089850b767bb2953a
child 480462 5af562a0d5a57c879f4f325b4ebd3382a85bb2dc
child 480463 3233fbdb111e5c55e5583bceb06507695cf839d9
child 480510 84460ed61be86f04e972c0efb414c9f509f1ca6c
child 480522 ef0ec47087053eaf88ea7187dab0581eacb2499d
child 480526 fa1501006dd2fc3589861f67ee03a5136d027907
child 480548 7356fc86845aac6ccc8f63b4801c3941b6883757
child 480558 206f2d7c6d9bed4364b839c329fc24ae8bcd69b8
child 480617 9c988c2e4459c13381595c4fcc0875861a362660
child 480728 7e97011a9511053d3b1a89dc29fe0c8e23c7bb66
child 480973 9fa9b819f55894612a9824875139a6d96ea3a95b
child 481106 68ccec64235cebfe7df1dd9b58b3031fab75369c
child 481108 bf2c9f62ca7922f4d39dbd0f4aa35ccc29d9c1fa
child 481171 2e1d7607afb367d483b9cca8b4701f9a792dc890
child 481876 9f07352a6143bb71a0e0ca587bce9fa679f581ba
child 482677 a456baaa0e31a5b578cd8abd3e0cf0f27736de06
child 482678 1d4eff183e0b45016e42464a0497ab750de9043b
child 482718 05b92586493fa3e44f48ba06666d289a9221b384
child 485558 d6f66b1ffdf66b11761e66c48b11d6d503b023f9
child 485562 0b3af3f84a8166379ac79677a1b013714370d244
child 499948 7b159eda0d33d1c09fc0f87367f67b52068653d3
push id44315
push usertnguyen@mozilla.com
push dateTue, 07 Feb 2017 02:03:39 +0000
reviewersmerge
milestone54.0a1
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)