Merge inbound to mozilla-central. a=merge
authorGurzau Raul <rgurzau@mozilla.com>
Tue, 30 Oct 2018 00:00:56 +0200
changeset 499844 4c7772c170a1
parent 499800 b851d42e2620 (current diff)
parent 499843 c44da5dca91c (diff)
child 499855 1c7d0042fc4a
child 499881 99ff4d9918ce
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone65.0a1
first release with
nightly linux32
4c7772c170a1 / 65.0a1 / 20181029230149 / files
nightly linux64
4c7772c170a1 / 65.0a1 / 20181029230149 / files
nightly mac
4c7772c170a1 / 65.0a1 / 20181029230149 / files
nightly win32
4c7772c170a1 / 65.0a1 / 20181029230149 / files
nightly win64
4c7772c170a1 / 65.0a1 / 20181029230149 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
browser/app/profile/firefox.js
docshell/base/nsDocShellLoadState.cpp
docshell/base/nsDocShellLoadState.h
dom/base/nsGlobalWindowOuter.cpp
dom/base/nsPIDOMWindow.h
dom/tests/mochitest/whatwg/browserFu.js
modules/libpref/init/all.js
testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
testing/web-platform/meta/html/browsers/offline/appcache/appcache-iframe.https.html.ini
testing/web-platform/meta/service-workers/service-worker/postmessage.https.html.ini
testing/web-platform/meta/webmessaging/MessageEvent-trusted.html.ini
testing/web-platform/meta/webmessaging/message-channels/dictionary-transferrable.html.ini
testing/web-platform/meta/webmessaging/with-options/host-specific-origin.tentative.html.ini
testing/web-platform/meta/webmessaging/with-options/message-channel-transferable.tentative.html.ini
testing/web-platform/meta/webmessaging/with-options/no-target-origin.tentative.html.ini
testing/web-platform/meta/webmessaging/with-options/null-transfer.tentative.html.ini
testing/web-platform/meta/webmessaging/with-options/one-arg.tentative.html.ini
testing/web-platform/meta/webmessaging/with-options/slash-origin.tentative.html.ini
testing/web-platform/meta/webmessaging/with-options/undefined-transferable.tentative.html.ini
testing/web-platform/meta/webmessaging/with-options/unknown-parameter.tentative.html.ini
testing/web-platform/meta/webmessaging/with-ports/027.html.ini
testing/web-platform/meta/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/second-argument-dictionary.html.ini
testing/web-platform/meta/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/second-argument-null.html.ini
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -463,16 +463,21 @@ pref("browser.tabs.extraDragSpace", fals
 // true   return to the tab that opened this tab (its owner)
 // false  return to the adjacent tab (old default)
 pref("browser.tabs.selectOwnerOnClose", true);
 
 pref("browser.tabs.showAudioPlayingIcon", true);
 // This should match Chromium's audio indicator delay.
 pref("browser.tabs.delayHidingAudioPlayingIconMS", 3000);
 
+// Pref to control whether we use separate privileged content processes.
+#ifdef NIGHTLY_BUILD
+pref("browser.tabs.remote.separatePrivilegedContentProcess", true);
+#endif
+
 pref("browser.ctrlTab.recentlyUsedOrder", true);
 
 // By default, do not export HTML at shutdown.
 // If true, at shutdown the bookmarks in your menu and toolbar will
 // be exported as HTML to the bookmarks.html file.
 pref("browser.bookmarks.autoExportHTML",          false);
 
 // The maximum number of daily bookmark backups to
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -705,17 +705,17 @@ window._gBrowser = {
     }
 
     return rv;
   },
 
   /**
    * Determine if a URI is an about: page pointing to a local resource.
    */
-  _isLocalAboutURI(aURI, aResolvedURI) {
+  isLocalAboutURI(aURI, aResolvedURI) {
     if (!aURI.schemeIs("about")) {
       return false;
     }
 
     // Specially handle about:blank as local
     if (aURI.pathQueryRef === "blank") {
       return true;
     }
@@ -732,16 +732,25 @@ window._gBrowser = {
       ).URI;
       return resolvedURI.schemeIs("jar") || resolvedURI.schemeIs("file");
     } catch (ex) {
       // aURI might be invalid.
       return false;
     }
   },
 
+  /**
+   * Sets an icon for the tab if the URI is defined in FAVICON_DEFAULTS.
+   */
+  setDefaultIcon(aTab, aURI) {
+    if (aURI && aURI.spec in FAVICON_DEFAULTS) {
+      this.setIcon(aTab, FAVICON_DEFAULTS[aURI.spec]);
+    }
+  },
+
   setIcon(aTab, aIconURL = "", aOriginalURL = aIconURL, aLoadingPrincipal = null) {
     let makeString = (url) => url instanceof Ci.nsIURI ? url.spec : url;
 
     aIconURL = makeString(aIconURL);
     aOriginalURL = makeString(aOriginalURL);
 
     let LOCAL_PROTOCOLS = [
       "chrome:",
@@ -2500,19 +2509,17 @@ window._gBrowser = {
         let notificationbox = this.getNotificationBox(t.linkedBrowser);
         notificationbox.remove();
       }
       throw e;
     }
 
     // Hack to ensure that the about:newtab, and about:welcome favicon is loaded
     // instantaneously, to avoid flickering and improve perceived performance.
-    if (aURI in FAVICON_DEFAULTS) {
-      this.setIcon(t, FAVICON_DEFAULTS[aURI]);
-    }
+    this.setDefaultIcon(t, aURIObject);
 
     // Dispatch a new tab notification.  We do this once we're
     // entirely done, so that things are in a consistent state
     // even if the event listener opens or closes tabs.
     let evt = new CustomEvent("TabOpen", { bubbles: true, detail: eventDetail || {} });
     t.dispatchEvent(evt);
 
     if (!usingPreloadedContent && originPrincipal && aURI) {
@@ -4811,17 +4818,17 @@ class TabProgressListener {
 
   _shouldShowProgress(aRequest) {
     if (this.mBlank)
       return false;
 
     // Don't show progress indicators in tabs for about: URIs
     // pointing to local resources.
     if ((aRequest instanceof Ci.nsIChannel) &&
-        gBrowser._isLocalAboutURI(aRequest.originalURI, aRequest.URI)) {
+        gBrowser.isLocalAboutURI(aRequest.originalURI, aRequest.URI)) {
       return false;
     }
 
     return true;
   }
 
   _isForInitialAboutBlank(aWebProgress, aStateFlags, aLocation) {
     if (!this.mBlank || !aWebProgress.isTopLevel) {
--- a/browser/base/content/test/general/browser_e10s_about_process.js
+++ b/browser/base/content/test/general/browser_e10s_about_process.js
@@ -1,31 +1,39 @@
-const CHROME_PROCESS = Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
-const CONTENT_PROCESS = Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
+const CHROME_PROCESS = E10SUtils.NOT_REMOTE;
+const WEB_CONTENT_PROCESS = E10SUtils.WEB_REMOTE_TYPE;
+const PRIVILEGED_CONTENT_PROCESS = E10SUtils.PRIVILEGED_REMOTE_TYPE;
 
 const CHROME = {
   id: "cb34538a-d9da-40f3-b61a-069f0b2cb9fb",
   path: "test-chrome",
   flags: 0,
 };
 const CANREMOTE = {
   id: "2480d3e1-9ce4-4b84-8ae3-910b9a95cbb3",
   path: "test-allowremote",
   flags: Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD,
 };
 const MUSTREMOTE = {
   id: "f849cee5-e13e-44d2-981d-0fb3884aaead",
   path: "test-mustremote",
   flags: Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD,
 };
+const CANPRIVILEGEDREMOTE = {
+  id: "a04ffafe-6c63-4266-acae-0f4b093165aa",
+  path: "test-canprivilegedremote",
+  flags: Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD |
+         Ci.nsIAboutModule.URI_CAN_LOAD_IN_PRIVILEGED_CHILD,
+};
 
 const TEST_MODULES = [
   CHROME,
   CANREMOTE,
   MUSTREMOTE,
+  CANPRIVILEGEDREMOTE,
 ];
 
 function AboutModule() {
 }
 
 AboutModule.prototype = {
   newChannel(aURI, aLoadInfo) {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
@@ -74,41 +82,75 @@ add_task(async function init() {
 
 registerCleanupFunction(() => {
   let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
   for (let module of TEST_MODULES) {
     registrar.unregisterFactory(Components.ID(module.id), AboutModuleFactory);
   }
 });
 
-function test_url(url, chromeResult, contentResult) {
-  is(E10SUtils.canLoadURIInProcess(url, CHROME_PROCESS),
+function test_url(url, chromeResult, webContentResult, privilegedContentResult) {
+  is(E10SUtils.canLoadURIInRemoteType(url, CHROME_PROCESS),
      chromeResult, "Check URL in chrome process.");
-  is(E10SUtils.canLoadURIInProcess(url, CONTENT_PROCESS),
-     contentResult, "Check URL in content process.");
+  is(E10SUtils.canLoadURIInRemoteType(url, WEB_CONTENT_PROCESS),
+     webContentResult, "Check URL in web content process.");
+  is(E10SUtils.canLoadURIInRemoteType(url, PRIVILEGED_CONTENT_PROCESS),
+     privilegedContentResult, "Check URL in privileged content process.");
 
-  is(E10SUtils.canLoadURIInProcess(url + "#foo", CHROME_PROCESS),
+  is(E10SUtils.canLoadURIInRemoteType(url + "#foo", CHROME_PROCESS),
      chromeResult, "Check URL with ref in chrome process.");
-  is(E10SUtils.canLoadURIInProcess(url + "#foo", CONTENT_PROCESS),
-     contentResult, "Check URL with ref in content process.");
+  is(E10SUtils.canLoadURIInRemoteType(url + "#foo", WEB_CONTENT_PROCESS),
+     webContentResult, "Check URL with ref in web content process.");
+  is(E10SUtils.canLoadURIInRemoteType(url + "#foo", PRIVILEGED_CONTENT_PROCESS),
+     privilegedContentResult, "Check URL with ref in privileged content process.");
 
-  is(E10SUtils.canLoadURIInProcess(url + "?foo", CHROME_PROCESS),
+  is(E10SUtils.canLoadURIInRemoteType(url + "?foo", CHROME_PROCESS),
      chromeResult, "Check URL with query in chrome process.");
-  is(E10SUtils.canLoadURIInProcess(url + "?foo", CONTENT_PROCESS),
-     contentResult, "Check URL with query in content process.");
+  is(E10SUtils.canLoadURIInRemoteType(url + "?foo", WEB_CONTENT_PROCESS),
+     webContentResult, "Check URL with query in web content process.");
+  is(E10SUtils.canLoadURIInRemoteType(url + "?foo", PRIVILEGED_CONTENT_PROCESS),
+     privilegedContentResult, "Check URL with query in privileged content process.");
 
-  is(E10SUtils.canLoadURIInProcess(url + "?foo#bar", CHROME_PROCESS),
+  is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", CHROME_PROCESS),
      chromeResult, "Check URL with query and ref in chrome process.");
-  is(E10SUtils.canLoadURIInProcess(url + "?foo#bar", CONTENT_PROCESS),
-     contentResult, "Check URL with query and ref in content process.");
+  is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", WEB_CONTENT_PROCESS),
+     webContentResult, "Check URL with query and ref in web content process.");
+  is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", PRIVILEGED_CONTENT_PROCESS),
+     privilegedContentResult, "Check URL with query and ref in privileged content process.");
 }
 
 add_task(async function test_chrome() {
-  test_url("about:" + CHROME.path, true, false);
+  test_url("about:" + CHROME.path, true, false, false);
 });
 
 add_task(async function test_any() {
-  test_url("about:" + CANREMOTE.path, true, true);
+  test_url("about:" + CANREMOTE.path, true, true, false);
 });
 
 add_task(async function test_remote() {
-  test_url("about:" + MUSTREMOTE.path, false, true);
+  test_url("about:" + MUSTREMOTE.path, false, true, false);
 });
+
+add_task(async function test_privileged_remote_true() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.tabs.remote.separatePrivilegedContentProcess", true],
+    ],
+  });
+
+  // This shouldn't be taken literally. We will always use the privileged
+  // content type if the URI_CAN_LOAD_IN_PRIVILEGED_CHILD flag is enabled and
+  // the pref is turned on.
+  test_url("about:" + CANPRIVILEGEDREMOTE.path, false, false, true);
+});
+
+add_task(async function test_privileged_remote_false() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.tabs.remote.separatePrivilegedContentProcess", false],
+    ],
+  });
+
+  // This shouldn't be taken literally. We will always use the privileged
+  // content type if the URI_CAN_LOAD_IN_PRIVILEGED_CHILD flag is enabled and
+  // the pref is turned on.
+  test_url("about:" + CANPRIVILEGEDREMOTE.path, false, true, false);
+});
--- a/browser/base/content/test/general/browser_e10s_chrome_process.js
+++ b/browser/base/content/test/general/browser_e10s_chrome_process.js
@@ -27,52 +27,52 @@ function makeTest(name, startURL, startP
     }
     await docLoadedPromise;
 
     is(browser.currentURI.spec, endURL, "Should have made it to the final URL");
     is(browser.isRemoteBrowser, endProcessIsRemote, "Should be displayed in the right process");
   };
 }
 
-const CHROME_PROCESS = Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
-const CONTENT_PROCESS = Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
+const CHROME_PROCESS = E10SUtils.NOT_REMOTE;
+const WEB_CONTENT_PROCESS = E10SUtils.WEB_REMOTE_TYPE;
 const PATH = (getRootDirectory(gTestPath) + "test_process_flags_chrome.html").replace("chrome://mochitests", "");
 
 const CHROME = "chrome://mochitests" + PATH;
 const CANREMOTE = "chrome://mochitests-any" + PATH;
 const MUSTREMOTE = "chrome://mochitests-content" + PATH;
 
 add_task(async function init() {
   gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {forceNotRemote: true});
 });
 
 registerCleanupFunction(() => {
   gBrowser.removeCurrentTab();
 });
 
 function test_url(url, chromeResult, contentResult) {
-  is(E10SUtils.canLoadURIInProcess(url, CHROME_PROCESS),
+  is(E10SUtils.canLoadURIInRemoteType(url, CHROME_PROCESS),
      chromeResult, "Check URL in chrome process.");
-  is(E10SUtils.canLoadURIInProcess(url, CONTENT_PROCESS),
-     contentResult, "Check URL in content process.");
+  is(E10SUtils.canLoadURIInRemoteType(url, WEB_CONTENT_PROCESS),
+     contentResult, "Check URL in web content process.");
 
-  is(E10SUtils.canLoadURIInProcess(url + "#foo", CHROME_PROCESS),
+  is(E10SUtils.canLoadURIInRemoteType(url + "#foo", CHROME_PROCESS),
      chromeResult, "Check URL with ref in chrome process.");
-  is(E10SUtils.canLoadURIInProcess(url + "#foo", CONTENT_PROCESS),
-     contentResult, "Check URL with ref in content process.");
+  is(E10SUtils.canLoadURIInRemoteType(url + "#foo", WEB_CONTENT_PROCESS),
+     contentResult, "Check URL with ref in web content process.");
 
-  is(E10SUtils.canLoadURIInProcess(url + "?foo", CHROME_PROCESS),
+  is(E10SUtils.canLoadURIInRemoteType(url + "?foo", CHROME_PROCESS),
      chromeResult, "Check URL with query in chrome process.");
-  is(E10SUtils.canLoadURIInProcess(url + "?foo", CONTENT_PROCESS),
-     contentResult, "Check URL with query in content process.");
+  is(E10SUtils.canLoadURIInRemoteType(url + "?foo", WEB_CONTENT_PROCESS),
+     contentResult, "Check URL with query in web content process.");
 
-  is(E10SUtils.canLoadURIInProcess(url + "?foo#bar", CHROME_PROCESS),
+  is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", CHROME_PROCESS),
      chromeResult, "Check URL with query and ref in chrome process.");
-  is(E10SUtils.canLoadURIInProcess(url + "?foo#bar", CONTENT_PROCESS),
-     contentResult, "Check URL with query and ref in content process.");
+  is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", WEB_CONTENT_PROCESS),
+     contentResult, "Check URL with query and ref in web content process.");
 }
 
 add_task(async function test_chrome() {
   test_url(CHROME, true, false);
 });
 
 add_task(async function test_any() {
   test_url(CANREMOTE, true, true);
--- a/browser/base/content/test/general/browser_e10s_javascript.js
+++ b/browser/base/content/test/general/browser_e10s_javascript.js
@@ -1,11 +1,11 @@
-const CHROME_PROCESS = Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
-const CONTENT_PROCESS = Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
+const CHROME_PROCESS = E10SUtils.NOT_REMOTE;
+const WEB_CONTENT_PROCESS = E10SUtils.WEB_REMOTE_TYPE;
 
 add_task(async function() {
   let url = "javascript:dosomething()";
 
-  ok(E10SUtils.canLoadURIInProcess(url, CHROME_PROCESS),
+  ok(E10SUtils.canLoadURIInRemoteType(url, CHROME_PROCESS),
      "Check URL in chrome process.");
-  ok(E10SUtils.canLoadURIInProcess(url, CONTENT_PROCESS),
-     "Check URL in content process.");
+  ok(E10SUtils.canLoadURIInRemoteType(url, WEB_CONTENT_PROCESS),
+     "Check URL in web content process.");
 });
--- a/browser/base/content/test/tabs/browser_isLocalAboutURI.js
+++ b/browser/base/content/test/tabs/browser_isLocalAboutURI.js
@@ -1,41 +1,41 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /**
- * Unit tests for tabbrowser._isLocalAboutURI to make sure it returns the
+ * Unit tests for tabbrowser.isLocalAboutURI to make sure it returns the
  * appropriate values for various URIs as well as optional resolved URI.
  */
 
 add_task(function test_URI() {
   const check = (spec, expect, description) => {
     const URI = Services.io.newURI(spec);
     try {
-      is(gBrowser._isLocalAboutURI(URI), expect, description);
+      is(gBrowser.isLocalAboutURI(URI), expect, description);
     } catch (ex) {
-      ok(false, "_isLocalAboutURI should not throw");
+      ok(false, "isLocalAboutURI should not throw");
     }
   };
   check("https://www.mozilla.org/", false, "https is not about");
   check("http://www.mozilla.org/", false, "http is not about");
   check("about:blank", true, "about:blank is local");
   check("about:about", true, "about:about is local");
   check("about:newtab", true, "about:newtab is local");
   check("about:random-invalid-uri", false,
         "about:random-invalid-uri is invalid but should not throw");
 });
 
 add_task(function test_URI_with_resolved() {
   const check = (spec, resolvedSpec, expect, description) => {
     const URI = Services.io.newURI(spec);
     const resolvedURI = Services.io.newURI(resolvedSpec);
-    is(gBrowser._isLocalAboutURI(URI, resolvedURI), expect, description);
+    is(gBrowser.isLocalAboutURI(URI, resolvedURI), expect, description);
   };
   check("about:newtab",
     "jar:file:///Applications/Firefox.app/Contents/Resources/browser/omni.ja!/chrome/browser/res/activity-stream/prerendered/en-US/activity-stream.html",
     true,
     "about:newtab with jar is local");
   check("about:newtab",
     "file:///mozilla-central/browser/base/content/newtab/newTab.xhtml",
     true,
--- a/browser/base/content/test/tabs/browser_new_tab_in_privileged_process_pref.js
+++ b/browser/base/content/test/tabs/browser_new_tab_in_privileged_process_pref.js
@@ -25,17 +25,17 @@ const TEST_HTTP = "http://example.org/";
  * @param {string} expectedRemoteType
  *        The expected remoteType value for the browser in both the parent
  *        and child processes.
  * @param {optional string} message
  *        If provided, shows this string as the message when remoteType values
  *        do not match. If not present, it uses the default message defined
  *        in the function parameters.
  */
-async function checkBrowserRemoteType(
+function checkBrowserRemoteType(
   browser,
   expectedRemoteType,
   message = `Ensures that tab runs in the ${expectedRemoteType} content process.`
 ) {
   // Check both parent and child to ensure that they have the correct remoteType.
   is(browser.remoteType, expectedRemoteType, message);
   is(browser.messageManager.remoteType, expectedRemoteType,
     "Parent and child process should agree on the remote type.");
@@ -58,17 +58,17 @@ add_task(async function setup() {
  * the privileged content process. With the reference, we can then open Activity
  * Stream links in a new tab and ensure that the new tab opens in the same
  * privileged content process as our reference.
  */
 add_task(async function activity_stream_in_privileged_content_process() {
   Services.ppmm.releaseCachedProcesses();
 
   await BrowserTestUtils.withNewTab(ABOUT_NEWTAB, async function(browser1) {
-    await checkBrowserRemoteType(browser1, E10SUtils.PRIVILEGED_REMOTE_TYPE);
+    checkBrowserRemoteType(browser1, E10SUtils.PRIVILEGED_REMOTE_TYPE);
 
     // Note the processID for about:newtab for comparison later.
     let privilegedPid = browser1.frameLoader.tabParent.osPid;
 
     for (let url of [
       ABOUT_NEWTAB,
       ABOUT_WELCOME,
       ABOUT_HOME,
@@ -92,17 +92,17 @@ add_task(async function activity_stream_
 /*
  * Test to ensure that a process switch occurs when navigating between normal
  * web pages and Activity Stream pages in the same tab.
  */
 add_task(async function process_switching_through_loading_in_the_same_tab() {
   Services.ppmm.releaseCachedProcesses();
 
   await BrowserTestUtils.withNewTab(TEST_HTTP, async function(browser) {
-    await checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
+    checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
 
     for (let [url, remoteType] of [
       [ABOUT_NEWTAB, E10SUtils.PRIVILEGED_REMOTE_TYPE],
       [ABOUT_BLANK, E10SUtils.PRIVILEGED_REMOTE_TYPE],
       [TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
       [ABOUT_HOME, E10SUtils.PRIVILEGED_REMOTE_TYPE],
       [TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
       [ABOUT_WELCOME, E10SUtils.PRIVILEGED_REMOTE_TYPE],
@@ -118,33 +118,33 @@ add_task(async function process_switchin
       [TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
       [`${ABOUT_WELCOME}?q=bar`, E10SUtils.PRIVILEGED_REMOTE_TYPE],
       [TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
       [`${ABOUT_HOME}?q=baz`, E10SUtils.PRIVILEGED_REMOTE_TYPE],
       [TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
     ]) {
       BrowserTestUtils.loadURI(browser, url);
       await BrowserTestUtils.browserLoaded(browser, false, url);
-      await checkBrowserRemoteType(browser, remoteType);
+      checkBrowserRemoteType(browser, remoteType);
     }
   });
 
   Services.ppmm.releaseCachedProcesses();
 });
 
 /*
  * Test to ensure that a process switch occurs when navigating between normal
  * web pages and Activity Stream pages using the browser's navigation features
  * such as history and location change.
  */
 add_task(async function process_switching_through_navigation_features() {
   Services.ppmm.releaseCachedProcesses();
 
   await BrowserTestUtils.withNewTab(ABOUT_NEWTAB, async function(browser) {
-    await checkBrowserRemoteType(browser, E10SUtils.PRIVILEGED_REMOTE_TYPE);
+    checkBrowserRemoteType(browser, E10SUtils.PRIVILEGED_REMOTE_TYPE);
 
     // Note the processID for about:newtab for comparison later.
     let privilegedPid = browser.frameLoader.tabParent.osPid;
 
     // Check that about:newtab opened from JS in about:newtab page is in the same process.
     let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, ABOUT_NEWTAB, true);
     await ContentTask.spawn(browser, ABOUT_NEWTAB, uri => {
       content.open(uri, "_blank");
@@ -161,17 +161,17 @@ add_task(async function process_switchin
     BrowserReload();
     await BrowserTestUtils.browserLoaded(browser, false, ABOUT_NEWTAB);
     is(browser.frameLoader.tabParent.osPid, privilegedPid,
       "Check that about:newtab is still in privileged content process after reload.");
 
     // Load http webpage
     BrowserTestUtils.loadURI(browser, TEST_HTTP);
     await BrowserTestUtils.browserLoaded(browser, false, TEST_HTTP);
-    await checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
+    checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
 
     // Check that using the history back feature switches back to privileged content process.
     let promiseLocation = BrowserTestUtils.waitForLocationChange(gBrowser, ABOUT_NEWTAB);
     browser.goBack();
     await promiseLocation;
     // We will need to ensure that the process flip has fully completed so that
     // the navigation history data will be available when we do browser.goForward();
     await BrowserTestUtils.waitForEvent(newTab, "SSTabRestored");
@@ -180,29 +180,29 @@ add_task(async function process_switchin
 
     // Check that using the history forward feature switches back to the web content process.
     promiseLocation = BrowserTestUtils.waitForLocationChange(gBrowser, TEST_HTTP);
     browser.goForward();
     await promiseLocation;
     // We will need to ensure that the process flip has fully completed so that
     // the navigation history data will be available when we do browser.gotoIndex(0);
     await BrowserTestUtils.waitForEvent(newTab, "SSTabRestored");
-    await checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE,
+    checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE,
       "Check that tab runs in the web content process after using history goForward.");
 
     // Check that goto history index does not break the affinity.
     promiseLocation = BrowserTestUtils.waitForLocationChange(gBrowser, ABOUT_NEWTAB);
     browser.gotoIndex(0);
     await promiseLocation;
     is(browser.frameLoader.tabParent.osPid, privilegedPid,
       "Check that about:newtab is in privileged content process after history gotoIndex.");
 
     BrowserTestUtils.loadURI(browser, TEST_HTTP);
     await BrowserTestUtils.browserLoaded(browser, false, TEST_HTTP);
-    await checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
+    checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
 
     // Check that location change causes a change in process type as well.
     await ContentTask.spawn(browser, ABOUT_NEWTAB, uri => {
       content.location = uri;
     });
     await BrowserTestUtils.browserLoaded(browser, false, ABOUT_NEWTAB);
     is(browser.frameLoader.tabParent.osPid, privilegedPid,
       "Check that about:newtab is in privileged content process after location change.");
--- a/browser/components/newtab/aboutNewTabService.js
+++ b/browser/components/newtab/aboutNewTabService.js
@@ -103,16 +103,17 @@ AboutNewTabService.prototype = {
     Ci.nsIObserver,
   ]),
 
   observe(subject, topic, data) {
     switch (topic) {
       case "nsPref:changed":
         if (data === PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS) {
           this._privilegedContentProcess = Services.prefs.getBoolPref(PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS);
+          this.notifyChange();
         } else if (data === PREF_ACTIVITY_STREAM_PRERENDER_ENABLED) {
           this._activityStreamPrerender = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED);
           this.notifyChange();
         } else if (!IS_RELEASE_OR_BETA && data === PREF_ACTIVITY_STREAM_DEBUG) {
           this._activityStreamDebug = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_DEBUG, false);
           this.updatePrerenderedPath();
           this.notifyChange();
         }
--- a/browser/components/newtab/test/browser/browser_packaged_as_locales.js
+++ b/browser/components/newtab/test/browser/browser_packaged_as_locales.js
@@ -1,15 +1,17 @@
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
 // Tests are by default run with non-debug en-US configuration
-const DEFAULT_URL = "resource://activity-stream/prerendered/en-US/activity-stream-prerendered.html";
+const DEFAULT_URL = SpecialPowers.getBoolPref("browser.tabs.remote.separatePrivilegedContentProcess")
+  ? "resource://activity-stream/prerendered/en-US/activity-stream-prerendered-noscripts.html"
+  : "resource://activity-stream/prerendered/en-US/activity-stream-prerendered.html";
 
 /**
  * Temporarily change the app locale to get the localized activity stream url
  */
 async function getUrlForLocale(locale) {
   const origAvailable = Services.locale.availableLocales;
   const origRequested = Services.locale.requestedLocales;
   try {
--- a/browser/components/newtab/test/xpcshell/test_AboutNewTabService.js
+++ b/browser/components/newtab/test/xpcshell/test_AboutNewTabService.js
@@ -9,33 +9,65 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
 const IS_RELEASE_OR_BETA = AppConstants.RELEASE_OR_BETA;
 
-const ACTIVITY_STREAM_PRERENDER_URL = "resource://activity-stream/prerendered/en-US/activity-stream-prerendered.html";
-const ACTIVITY_STREAM_PRERENDER_DEBUG_URL = "resource://activity-stream/prerendered/static/activity-stream-prerendered-debug.html";
-const ACTIVITY_STREAM_URL = "resource://activity-stream/prerendered/en-US/activity-stream.html";
-const ACTIVITY_STREAM_DEBUG_URL = "resource://activity-stream/prerendered/static/activity-stream-debug.html";
-
 const DOWNLOADS_URL = "chrome://browser/content/downloads/contentAreaDownloadsView.xul";
+const SEPARATE_PRIVILEGED_CONTENT_PROCESS_PREF = "browser.tabs.remote.separatePrivilegedContentProcess";
 const ACTIVITY_STREAM_PRERENDER_PREF = "browser.newtabpage.activity-stream.prerender";
 const ACTIVITY_STREAM_DEBUG_PREF = "browser.newtabpage.activity-stream.debug";
 
 function cleanup() {
+  Services.prefs.clearUserPref(SEPARATE_PRIVILEGED_CONTENT_PROCESS_PREF);
   Services.prefs.clearUserPref(ACTIVITY_STREAM_PRERENDER_PREF);
   Services.prefs.clearUserPref(ACTIVITY_STREAM_DEBUG_PREF);
   aboutNewTabService.resetNewTabURL();
 }
 
 registerCleanupFunction(cleanup);
 
+let ACTIVITY_STREAM_PRERENDER_URL;
+let ACTIVITY_STREAM_PRERENDER_DEBUG_URL;
+let ACTIVITY_STREAM_URL;
+let ACTIVITY_STREAM_DEBUG_URL;
+
+function setExpectedUrlsWithScripts() {
+  ACTIVITY_STREAM_PRERENDER_URL = "resource://activity-stream/prerendered/en-US/activity-stream-prerendered.html";
+  ACTIVITY_STREAM_PRERENDER_DEBUG_URL = "resource://activity-stream/prerendered/static/activity-stream-prerendered-debug.html";
+  ACTIVITY_STREAM_URL = "resource://activity-stream/prerendered/en-US/activity-stream.html";
+  ACTIVITY_STREAM_DEBUG_URL = "resource://activity-stream/prerendered/static/activity-stream-debug.html";
+}
+
+function setExpectedUrlsWithoutScripts() {
+  ACTIVITY_STREAM_PRERENDER_URL = "resource://activity-stream/prerendered/en-US/activity-stream-prerendered-noscripts.html";
+  ACTIVITY_STREAM_PRERENDER_DEBUG_URL = "resource://activity-stream/prerendered/static/activity-stream-prerendered-debug-noscripts.html";
+  ACTIVITY_STREAM_URL = "resource://activity-stream/prerendered/en-US/activity-stream-noscripts.html";
+  ACTIVITY_STREAM_DEBUG_URL = "resource://activity-stream/prerendered/static/activity-stream-debug-noscripts.html";
+}
+
+// Default expected URLs to files with scripts in them.
+setExpectedUrlsWithScripts();
+
+function addTestsWithPrivilegedContentProcessPref(test) {
+  add_task(async() => {
+    await setPrivilegedContentProcessPref(true);
+    setExpectedUrlsWithoutScripts();
+    await test();
+  });
+  add_task(async() => {
+    await setPrivilegedContentProcessPref(false);
+    setExpectedUrlsWithScripts();
+    await test();
+  });
+}
+
 function nextChangeNotificationPromise(aNewURL, testMessage) {
   return new Promise(resolve => {
     Services.obs.addObserver(function observer(aSubject, aTopic, aData) { // jshint unused:false
       Services.obs.removeObserver(observer, aTopic);
       Assert.equal(aData, aNewURL, testMessage);
       resolve();
     }, "newtab-url-changed");
   });
@@ -58,16 +90,26 @@ function setupASPrerendered() {
     return Promise.resolve();
   }
 
   let notificationPromise = nextChangeNotificationPromise("about:newtab");
   Services.prefs.setBoolPref(ACTIVITY_STREAM_PRERENDER_PREF, true);
   return notificationPromise;
 }
 
+function setPrivilegedContentProcessPref(usePrivilegedContentProcess) {
+  if (usePrivilegedContentProcess === Services.prefs.getBoolPref(SEPARATE_PRIVILEGED_CONTENT_PROCESS_PREF)) {
+    return Promise.resolve();
+  }
+
+  let notificationPromise = nextChangeNotificationPromise("about:newtab");
+  Services.prefs.setBoolPref(SEPARATE_PRIVILEGED_CONTENT_PROCESS_PREF, usePrivilegedContentProcess);
+  return notificationPromise;
+}
+
 add_task(async function test_as_and_prerender_initialized() {
   Assert.ok(aboutNewTabService.activityStreamEnabled,
     ".activityStreamEnabled should be set to the correct initial value");
   Assert.equal(aboutNewTabService.activityStreamPrerender, Services.prefs.getBoolPref(ACTIVITY_STREAM_PRERENDER_PREF),
     ".activityStreamPrerender should be set to the correct initial value");
   // This pref isn't defined on release or beta, so we fall back to false
   Assert.equal(aboutNewTabService.activityStreamDebug, Services.prefs.getBoolPref(ACTIVITY_STREAM_DEBUG_PREF, false),
     ".activityStreamDebug should be set to the correct initial value");
@@ -100,20 +142,21 @@ add_task(async function test_override_ac
   aboutNewTabService.newTabURL = DOWNLOADS_URL;
   await notificationPromise;
   Assert.ok(aboutNewTabService.overridden, "Newtab URL should be overridden");
   Assert.equal(aboutNewTabService.newTabURL, DOWNLOADS_URL, "Newtab URL should be the custom URL");
 
   cleanup();
 });
 
-add_task(async function test_override_activity_stream_enabled() {
+addTestsWithPrivilegedContentProcessPref(async function test_override_activity_stream_enabled() {
   let notificationPromise = await setupASPrerendered();
 
-  Assert.equal(aboutNewTabService.defaultURL, ACTIVITY_STREAM_PRERENDER_URL, "Newtab URL should be the default activity stream prerendered URL");
+  Assert.equal(aboutNewTabService.defaultURL, ACTIVITY_STREAM_PRERENDER_URL,
+    "Newtab URL should be the default activity stream prerendered URL");
   Assert.ok(!aboutNewTabService.overridden, "Newtab URL should not be overridden");
   Assert.ok(aboutNewTabService.activityStreamEnabled, "Activity Stream should be enabled");
   Assert.ok(aboutNewTabService.activityStreamPrerender, "Activity Stream should be prerendered");
 
   // change to a chrome URL while activity stream is enabled
   notificationPromise = nextChangeNotificationPromise(DOWNLOADS_URL);
   aboutNewTabService.newTabURL = DOWNLOADS_URL;
   await notificationPromise;
@@ -122,18 +165,19 @@ add_task(async function test_override_ac
   Assert.equal(aboutNewTabService.defaultURL, ACTIVITY_STREAM_PRERENDER_URL,
                "Newtab URL defaultURL still set to the default activity stream prerendered URL");
   Assert.ok(aboutNewTabService.overridden, "Newtab URL should be overridden");
   Assert.ok(!aboutNewTabService.activityStreamEnabled, "Activity Stream should not be enabled");
 
   cleanup();
 });
 
-add_task(async function test_default_url() {
+addTestsWithPrivilegedContentProcessPref(async function test_default_url() {
   await setupASPrerendered();
+
   Assert.equal(aboutNewTabService.defaultURL, ACTIVITY_STREAM_PRERENDER_URL,
     "Newtab defaultURL initially set to prerendered AS url");
 
   // Only debug variants aren't available on release/beta
   if (!IS_RELEASE_OR_BETA) {
     await setBoolPrefAndWaitForChange(ACTIVITY_STREAM_DEBUG_PREF, true,
       "A notification occurs after changing the debug pref to true");
     Assert.equal(aboutNewTabService.activityStreamDebug, true,
@@ -156,17 +200,17 @@ add_task(async function test_default_url
   }
 
   Assert.equal(aboutNewTabService.defaultURL, ACTIVITY_STREAM_URL,
     "Newtab defaultURL set to un-prerendered AS if prerender is false and debug is false");
 
   cleanup();
 });
 
-add_task(async function test_welcome_url() {
+addTestsWithPrivilegedContentProcessPref(async function test_welcome_url() {
   await setupASPrerendered();
 
   Assert.equal(aboutNewTabService.activityStreamPrerender, true,
     "Prerendering is enabled by default.");
   Assert.equal(aboutNewTabService.welcomeURL, ACTIVITY_STREAM_URL,
     "Newtab welcomeURL set to un-prerendered AS when prerendering enabled and debug disabled.");
   await setBoolPrefAndWaitForChange(ACTIVITY_STREAM_PRERENDER_PREF, false,
     "A notification occurs after changing the prerender pref to false.");
@@ -189,21 +233,21 @@ add_task(async function test_welcome_url
 });
 
 add_task(function test_locale() {
   Assert.equal(aboutNewTabService.activityStreamLocale, "en-US",
     "The locale for testing should be en-US");
 });
 
 /**
- * Tests reponse to updates to prefs
+ * Tests response to updates to prefs
  */
-add_task(async function test_updates() {
-   // Simulates a "cold-boot" situation, with some pref already set before testing a series
-   // of changes.
+addTestsWithPrivilegedContentProcessPref(async function test_updates() {
+  // Simulates a "cold-boot" situation, with some pref already set before testing a series
+  // of changes.
   await setupASPrerendered();
 
   aboutNewTabService.resetNewTabURL(); // need to set manually because pref notifs are off
   let notificationPromise;
 
   // test update fires on override and reset
   let testURL = "https://example.com/";
   notificationPromise = nextChangeNotificationPromise(
--- a/browser/components/payments/test/unit/test_response_creation.js
+++ b/browser/components/payments/test/unit/test_response_creation.js
@@ -5,47 +5,39 @@
  * destructured arguments properly to the `init` methods. Full testing of the init
  * methods is left to the DOM code.
  */
 
 const DIALOG_WRAPPER_URI = "chrome://payments/content/paymentDialogWrapper.js";
 let dialogGlobal = {};
 Services.scriptloader.loadSubScript(DIALOG_WRAPPER_URI, dialogGlobal);
 
-/**
- * @param {Object} responseData with properties in the order matching `nsIBasicCardResponseData`
- *                              init method args.
- * @returns {string} serialized card data
- */
-function serializeBasicCardResponseData(responseData) {
-  return [...Object.entries(responseData)].map(array => array.join(":")).join(";") + ";";
-}
-
-
 add_task(async function test_createBasicCardResponseData_basic() {
   let expected = {
     cardholderName: "John Smith",
     cardNumber: "1234567890",
     expiryMonth: "01",
     expiryYear: "2017",
     cardSecurityCode: "0123",
   };
   let actual = dialogGlobal.paymentDialogWrapper.createBasicCardResponseData(expected);
-  let expectedSerialized = serializeBasicCardResponseData(expected);
-  Assert.equal(actual.data, expectedSerialized, "Check data");
+  Assert.equal(actual.cardholderName, expected.cardholderName, "Check cardholderName");
+  Assert.equal(actual.cardNumber, expected.cardNumber, "Check cardNumber");
+  Assert.equal(actual.expiryMonth, expected.expiryMonth, "Check expiryMonth");
+  Assert.equal(actual.expiryYear, expected.expiryYear, "Check expiryYear");
+  Assert.equal(actual.cardSecurityCode, expected.cardSecurityCode, "Check cardSecurityCode");
 });
 
 add_task(async function test_createBasicCardResponseData_minimal() {
   let expected = {
     cardNumber: "1234567890",
   };
   let actual = dialogGlobal.paymentDialogWrapper.createBasicCardResponseData(expected);
-  let expectedSerialized = serializeBasicCardResponseData(expected);
-  info(actual.data);
-  Assert.equal(actual.data, expectedSerialized, "Check data");
+  info(actual.cardNumber);
+  Assert.equal(actual.cardNumber, expected.cardNumber, "Check cardNumber");
 });
 
 add_task(async function test_createBasicCardResponseData_withoutNumber() {
   let data = {
     cardholderName: "John Smith",
     expiryMonth: "01",
     expiryYear: "2017",
     cardSecurityCode: "0123",
@@ -125,16 +117,11 @@ add_task(async function test_createShowR
   for (let [propName, propVal] of Object.entries(actual)) {
     if (typeof(propVal) != "string") {
       continue;
     }
     if (propName == "requestId") {
       Assert.equal(propVal, requestId, `Check ${propName}`);
       continue;
     }
-    if (propName == "data") {
-      Assert.equal(propVal, serializeBasicCardResponseData(cardData), `Check ${propName}`);
-      continue;
-    }
-
     Assert.equal(propVal, responseData[propName], `Check ${propName}`);
   }
 });
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -2431,18 +2431,27 @@ var SessionStoreInternal = {
 
     let tabOptions = {
       userContextId,
       ...(aTab == aWindow.gBrowser.selectedTab ? {relatedToCurrent: true, ownerTab: aTab} : {}),
     };
     let newTab = aWindow.gBrowser.addTrustedTab(null, tabOptions);
 
     // Start the throbber to pretend we're doing something while actually
-    // waiting for data from the frame script.
-    newTab.setAttribute("busy", "true");
+    // waiting for data from the frame script. This throbber is disabled
+    // if the URI is a local about: URI.
+    let uriObj = aTab.linkedBrowser.currentURI;
+    if (!uriObj || (uriObj && !aWindow.gBrowser.isLocalAboutURI(uriObj))) {
+      newTab.setAttribute("busy", "true");
+    }
+
+    // Hack to ensure that the about:home, about:newtab, and about:welcome
+    // favicon is loaded instantaneously, to avoid flickering and improve
+    // perceived performance.
+    aWindow.gBrowser.setDefaultIcon(newTab, uriObj);
 
     // Collect state before flushing.
     let tabState = TabState.collect(aTab, TAB_CUSTOM_VALUES.get(aTab));
 
     // Flush to get the latest tab state to duplicate.
     let browser = aTab.linkedBrowser;
     TabStateFlusher.flush(browser).then(() => {
       // The new tab might have been closed in the meantime.
@@ -2769,17 +2778,24 @@ var SessionStoreInternal = {
         win.gBrowser.setInitialTabTitle(tab, activePageData.title, { isContentTitle: true });
       } else {
         win.gBrowser.setInitialTabTitle(tab, activePageData.url);
       }
     }
 
     // Restore the tab icon.
     if ("image" in tabData) {
-      win.gBrowser.setIcon(tab, tabData.image, undefined, tabData.iconLoadingPrincipal);
+      // We know that about:blank is safe to load in any remote type. Since
+      // SessionStore is triggered with about:blank, there must be a process
+      // flip. We will ignore the first about:blank load to prevent resetting the
+      // favicon that we have set earlier to avoid flickering and improve
+      // perceived performance.
+      if (!activePageData || (activePageData && activePageData.url != "about:blank")) {
+        win.gBrowser.setIcon(tab, tabData.image, undefined, tabData.iconLoadingPrincipal);
+      }
       TabStateCache.update(browser, { image: null, iconLoadingPrincipal: null });
     }
   },
 
   // This method deletes all the closedTabs matching userContextId.
   _forgetTabsWithUserContextId(userContextId) {
     for (let window of Services.wm.getEnumerator("navigator:browser")) {
       let windowState = this._windows[window.__SSi];
@@ -3009,19 +3025,32 @@ var SessionStoreInternal = {
     this._remotenessChangingBrowsers.set(browser.permanentKey, loadArguments);
 
     if (alreadyRestoring) {
       // This tab was already being restored to run in the
       // correct process. We're done here.
       return;
     }
 
+    let uriObj;
+    try {
+      uriObj = Services.io.newURI(loadArguments.uri);
+    } catch (e) {}
+
     // Start the throbber to pretend we're doing something while actually
-    // waiting for data from the frame script.
-    tab.setAttribute("busy", "true");
+    // waiting for data from the frame script. This throbber is disabled
+    // if the URI is a local about: URI.
+    if (!uriObj || (uriObj && !window.gBrowser.isLocalAboutURI(uriObj))) {
+      tab.setAttribute("busy", "true");
+    }
+
+    // Hack to ensure that the about:home, about:newtab, and about:welcome
+    // favicon is loaded instantaneously, to avoid flickering and improve
+    // perceived performance.
+    window.gBrowser.setDefaultIcon(tab, uriObj);
 
     // Flush to get the latest tab state.
     TabStateFlusher.flush(browser).then(() => {
       // loadArguments might have been overwritten by multiple calls
       // to navigateAndRestore while we waited for the tab to flush,
       // so we use the most recently stored one.
       let recentLoadArguments =
         this._remotenessChangingBrowsers.get(browser.permanentKey);
@@ -4037,17 +4066,17 @@ var SessionStoreInternal = {
 
     browser.messageManager.sendAsyncMessage("SessionStore:restoreTabContent",
       {loadArguments, isRemotenessUpdate,
        reason: aOptions.restoreContentReason ||
                RESTORE_TAB_CONTENT_REASON.SET_STATE,
        requestTime: Services.telemetry.msSystemNow()});
 
     // Focus the tab's content area.
-    if (aTab.selected) {
+    if (aTab.selected && !window.isBlankPageURL(uri)) {
       browser.focus();
     }
   },
 
   /**
    * Marks a given pending tab as restoring.
    *
    * @param aTab
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -1360,22 +1360,22 @@ var UITour = {
     } else if (aMenuName == "urlbar") {
       aWindow.gURLBar.closePopup();
     } else if (aMenuName == "pageActionPanel") {
       aWindow.BrowserPageActions.panelNode.hidePopup();
     }
   },
 
   showNewTab(aWindow, aBrowser) {
+    aWindow.gURLBar.focus();
     let url = "about:newtab";
     aWindow.openLinkIn(url, "current", {
       targetBrowser: aBrowser,
       triggeringPrincipal: Services.scriptSecurityManager.createCodebasePrincipal(Services.io.newURI(url), {}),
     });
-    aWindow.gURLBar.focus();
   },
 
   _hideAnnotationsForPanel(aEvent, aShouldClosePanel, aTargetPositionCallback) {
     let win = aEvent.target.ownerGlobal;
     let hideHighlightMethod = null;
     let hideInfoMethod = null;
     if (aShouldClosePanel) {
       hideHighlightMethod = aWin => this.hideHighlight(aWin);
--- a/browser/config/mozconfigs/linux64/hazards
+++ b/browser/config/mozconfigs/linux64/hazards
@@ -31,15 +31,18 @@ mk_add_options MOZ_OBJDIR=obj-analyzed
 ac_add_options --enable-debug
 ac_add_options --enable-tests
 ac_add_options --enable-optimize
 ac_add_options --with-compiler-wrapper=$TOOLTOOL_DIR/sixgill/usr/libexec/sixgill/scripts/wrap_gcc/basecc
 ac_add_options --without-ccache
 
 ac_add_options --disable-replace-malloc
 
-CFLAGS="$CFLAGS -Wno-attributes"
-CPPFLAGS="$CPPFLAGS -Wno-attributes"
-CXXFLAGS="$CXXFLAGS -Wno-attributes"
+# -Wattributes is very verbose due to attributes being ignored on template
+# instantiations. -Wignored-attributes is very verbose due to attributes being
+# ignored on template parameters.
+CFLAGS="$CFLAGS -Wno-attributes -Wno-ignored-attributes"
+CPPFLAGS="$CPPFLAGS -Wno-attributes -Wno-ignored-attributes"
+CXXFLAGS="$CXXFLAGS -Wno-attributes -Wno-ignored-attributes"
 
 NODEJS="$TOOLTOOL_DIR/node/bin/node"
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,5 +1,5 @@
 This is the PDF.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 2.0.943
+Current extension version is: 2.1.15
 
-Taken from upstream commit: dc98bf76
+Taken from upstream commit: 991a574c
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -118,18 +118,18 @@ return /******/ (function(modules) { // 
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.0.943';
-var pdfjsBuild = 'dc98bf76';
+var pdfjsVersion = '2.1.15';
+var pdfjsBuild = '991a574c';
 var pdfjsSharedUtil = __w_pdfjs_require__(1);
 var pdfjsDisplayAPI = __w_pdfjs_require__(7);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(19);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(20);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(8);
 var pdfjsDisplaySVG = __w_pdfjs_require__(21);
 let pdfjsDisplayWorkerOptions = __w_pdfjs_require__(13);
 let pdfjsDisplayAPICompatibility = __w_pdfjs_require__(10);
@@ -4221,17 +4221,17 @@ function _fetchDocument(worker, source, 
     return Promise.reject(new Error('Worker was destroyed'));
   }
   if (pdfDataRangeTransport) {
     source.length = pdfDataRangeTransport.length;
     source.initialData = pdfDataRangeTransport.initialData;
   }
   return worker.messageHandler.sendWithPromise('GetDocRequest', {
     docId,
-    apiVersion: '2.0.943',
+    apiVersion: '2.1.15',
     source: {
       data: source.data,
       url: source.url,
       password: source.password,
       disableAutoFetch: source.disableAutoFetch,
       rangeChunkSize: source.rangeChunkSize,
       length: source.length
     },
@@ -5550,18 +5550,18 @@ var InternalRenderTask = function Intern
         }
       });
     }
   };
   return InternalRenderTask;
 }();
 var version, build;
 {
-  exports.version = version = '2.0.943';
-  exports.build = build = 'dc98bf76';
+  exports.version = version = '2.1.15';
+  exports.build = build = '991a574c';
 }
 exports.getDocument = getDocument;
 exports.LoopbackPort = LoopbackPort;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports.setPDFNetworkStreamFactory = setPDFNetworkStreamFactory;
@@ -7457,17 +7457,17 @@ var CanvasGraphics = function CanvasGrap
     },
     paintFormXObjectBegin: function CanvasGraphics_paintFormXObjectBegin(matrix, bbox) {
       this.save();
       this.baseTransformStack.push(this.baseTransform);
       if (Array.isArray(matrix) && matrix.length === 6) {
         this.transform.apply(this, matrix);
       }
       this.baseTransform = this.ctx.mozCurrentTransform;
-      if (Array.isArray(bbox) && bbox.length === 4) {
+      if (bbox) {
         var width = bbox[2] - bbox[0];
         var height = bbox[3] - bbox[1];
         this.ctx.rect(bbox[0], bbox[1], width, height);
         this.clip();
         this.endPath();
       }
     },
     paintFormXObjectEnd: function CanvasGraphics_paintFormXObjectEnd() {
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -118,18 +118,18 @@ return /******/ (function(modules) { // 
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.0.943';
-var pdfjsBuild = 'dc98bf76';
+var pdfjsVersion = '2.1.15';
+var pdfjsBuild = '991a574c';
 var pdfjsCoreWorker = __w_pdfjs_require__(1);
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 1 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
@@ -322,17 +322,17 @@ var WorkerMessageHandler = {
     });
   },
   createDocumentHandler(docParams, port) {
     var pdfManager;
     var terminated = false;
     var cancelXHRs = null;
     var WorkerTasks = [];
     let apiVersion = docParams.apiVersion;
-    let workerVersion = '2.0.943';
+    let workerVersion = '2.1.15';
     if (apiVersion !== workerVersion) {
       throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
     }
     var docId = docParams.docId;
     var docBaseUrl = docParams.docBaseUrl;
     var workerHandlerName = docParams.docId + '_worker';
     var handler = new _message_handler.MessageHandler(workerHandlerName, docId, port);
     handler.postMessageTransfers = docParams.postMessageTransfers;
@@ -19137,16 +19137,21 @@ var PartialEvaluator = function PartialE
         }
       }
       return false;
     },
     buildFormXObject: function PartialEvaluator_buildFormXObject(resources, xobj, smask, operatorList, task, initialState) {
       var dict = xobj.dict;
       var matrix = dict.getArray('Matrix');
       var bbox = dict.getArray('BBox');
+      if (Array.isArray(bbox) && bbox.length === 4) {
+        bbox = _util.Util.normalizeRect(bbox);
+      } else {
+        bbox = null;
+      }
       var group = dict.get('Group');
       if (group) {
         var groupOptions = {
           matrix,
           bbox,
           smask,
           isolated: false,
           knockout: false
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -4187,24 +4187,32 @@ const CHARACTERS_TO_NORMALIZE = {
   '\u201C': '"',
   '\u201D': '"',
   '\u201E': '"',
   '\u201F': '"',
   '\u00BC': '1/4',
   '\u00BD': '1/2',
   '\u00BE': '3/4'
 };
+let normalizationRegex = null;
+function normalize(text) {
+  if (!normalizationRegex) {
+    const replace = Object.keys(CHARACTERS_TO_NORMALIZE).join('');
+    normalizationRegex = new RegExp(`[${replace}]`, 'g');
+  }
+  return text.replace(normalizationRegex, function (ch) {
+    return CHARACTERS_TO_NORMALIZE[ch];
+  });
+}
 class PDFFindController {
   constructor({ linkService, eventBus = (0, _dom_events.getGlobalEventBus)() }) {
     this._linkService = linkService;
     this._eventBus = eventBus;
     this._reset();
     eventBus.on('findbarclose', this._onFindBarClose.bind(this));
-    const replace = Object.keys(CHARACTERS_TO_NORMALIZE).join('');
-    this._normalizationRegex = new RegExp(`[${replace}]`, 'g');
   }
   get highlightMatches() {
     return this._highlightMatches;
   }
   get pageMatches() {
     return this._pageMatches;
   }
   get pageMatchesLength() {
@@ -4251,41 +4259,44 @@ class PDFFindController {
         this._nextMatch();
       }
     });
   }
   _reset() {
     this._highlightMatches = false;
     this._pdfDocument = null;
     this._pageMatches = [];
-    this._pageMatchesLength = null;
+    this._pageMatchesLength = [];
     this._state = null;
     this._selected = {
       pageIdx: -1,
       matchIdx: -1
     };
     this._offset = {
       pageIdx: null,
-      matchIdx: null
+      matchIdx: null,
+      wrapped: false
     };
     this._extractTextPromises = [];
     this._pageContents = [];
     this._matchesCountTotal = 0;
     this._pagesToSearch = null;
     this._pendingFindMatches = Object.create(null);
     this._resumePageIdx = null;
     this._dirtyMatch = false;
     clearTimeout(this._findTimeout);
     this._findTimeout = null;
     this._firstPageCapability = (0, _pdfjsLib.createPromiseCapability)();
   }
-  _normalize(text) {
-    return text.replace(this._normalizationRegex, function (ch) {
-      return CHARACTERS_TO_NORMALIZE[ch];
-    });
+  get _query() {
+    if (this._state.query !== this._rawQuery) {
+      this._rawQuery = this._state.query;
+      this._normalizedQuery = normalize(this._state.query);
+    }
+    return this._normalizedQuery;
   }
   _prepareMatches(matchesWithLength, matches, matchesLength) {
     function isSubTerm(matchesWithLength, currentIndex) {
       const currentElem = matchesWithLength[currentIndex];
       const nextElem = matchesWithLength[currentIndex + 1];
       if (currentIndex < matchesWithLength.length - 1 && currentElem.match === nextElem.match) {
         currentElem.skipped = true;
         return true;
@@ -4367,26 +4378,23 @@ class PDFFindController {
         }
         matchesWithLength.push({
           match: matchIdx,
           matchLength: subqueryLen,
           skipped: false
         });
       }
     }
-    if (!this._pageMatchesLength) {
-      this._pageMatchesLength = [];
-    }
     this._pageMatchesLength[pageIndex] = [];
     this._pageMatches[pageIndex] = [];
     this._prepareMatches(matchesWithLength, this._pageMatches[pageIndex], this._pageMatchesLength[pageIndex]);
   }
   _calculateMatch(pageIndex) {
-    let pageContent = this._normalize(this._pageContents[pageIndex]);
-    let query = this._normalize(this._state.query);
+    let pageContent = this._pageContents[pageIndex];
+    let query = this._query;
     const { caseSensitive, entireWord, phraseSearch } = this._state;
     if (query.length === 0) {
       return;
     }
     if (!caseSensitive) {
       pageContent = pageContent.toLowerCase();
       query = query.toLowerCase();
     }
@@ -4418,17 +4426,17 @@ class PDFFindController {
         return this._pdfDocument.getPage(i + 1).then(pdfPage => {
           return pdfPage.getTextContent({ normalizeWhitespace: true });
         }).then(textContent => {
           const textItems = textContent.items;
           const strBuf = [];
           for (let j = 0, jj = textItems.length; j < jj; j++) {
             strBuf.push(textItems[j].str);
           }
-          this._pageContents[i] = strBuf.join('');
+          this._pageContents[i] = normalize(strBuf.join(''));
           extractTextCapability.resolve(i);
         }, reason => {
           console.error(`Unable to get text content for page ${i + 1}`, reason);
           this._pageContents[i] = '';
           extractTextCapability.resolve(i);
         });
       });
     }
@@ -4437,42 +4445,50 @@ class PDFFindController {
     if (this._selected.pageIdx === index) {
       this._linkService.page = index + 1;
     }
     this._eventBus.dispatch('updatetextlayermatches', {
       source: this,
       pageIndex: index
     });
   }
+  _updateAllPages() {
+    this._eventBus.dispatch('updatetextlayermatches', {
+      source: this,
+      pageIndex: -1
+    });
+  }
   _nextMatch() {
     const previous = this._state.findPrevious;
     const currentPageIndex = this._linkService.page - 1;
     const numPages = this._linkService.pagesCount;
     this._highlightMatches = true;
     if (this._dirtyMatch) {
       this._dirtyMatch = false;
       this._selected.pageIdx = this._selected.matchIdx = -1;
       this._offset.pageIdx = currentPageIndex;
       this._offset.matchIdx = null;
+      this._offset.wrapped = false;
       this._resumePageIdx = null;
       this._pageMatches.length = 0;
-      this._pageMatchesLength = null;
+      this._pageMatchesLength.length = 0;
       this._matchesCountTotal = 0;
+      this._updateAllPages();
       for (let i = 0; i < numPages; i++) {
-        this._updatePage(i);
-        if (!(i in this._pendingFindMatches)) {
-          this._pendingFindMatches[i] = true;
-          this._extractTextPromises[i].then(pageIdx => {
-            delete this._pendingFindMatches[pageIdx];
-            this._calculateMatch(pageIdx);
-          });
+        if (this._pendingFindMatches[i] === true) {
+          continue;
         }
-      }
-    }
-    if (this._state.query === '') {
+        this._pendingFindMatches[i] = true;
+        this._extractTextPromises[i].then(pageIdx => {
+          delete this._pendingFindMatches[pageIdx];
+          this._calculateMatch(pageIdx);
+        });
+      }
+    }
+    if (this._query === '') {
       this._updateUIState(FindState.FOUND);
       return;
     }
     if (this._resumePageIdx) {
       return;
     }
     const offset = this._offset;
     this._pagesToSearch = numPages;
@@ -4553,23 +4569,24 @@ class PDFFindController {
     const pdfDocument = this._pdfDocument;
     this._firstPageCapability.promise.then(() => {
       if (!this._pdfDocument || pdfDocument && this._pdfDocument !== pdfDocument) {
         return;
       }
       if (this._findTimeout) {
         clearTimeout(this._findTimeout);
         this._findTimeout = null;
-        this._updateUIState(FindState.FOUND);
-      }
+      }
+      if (this._resumePageIdx) {
+        this._resumePageIdx = null;
+        this._dirtyMatch = true;
+      }
+      this._updateUIState(FindState.FOUND);
       this._highlightMatches = false;
-      this._eventBus.dispatch('updatetextlayermatches', {
-        source: this,
-        pageIndex: -1
-      });
+      this._updateAllPages();
     });
   }
   _requestMatchesCount() {
     const { pageIdx, matchIdx } = this._selected;
     let current = 0,
         total = this._matchesCountTotal;
     if (matchIdx !== -1) {
       for (let i = 0; i < pageIdx; i++) {
@@ -7972,44 +7989,44 @@ class TextLayerBuilder {
       textDivs: this.textDivs,
       textContentItemsStr: this.textContentItemsStr,
       timeout,
       enhanceTextSelection: this.enhanceTextSelection
     });
     this.textLayerRenderTask.promise.then(() => {
       this.textLayerDiv.appendChild(textLayerFrag);
       this._finishRendering();
-      this.updateMatches();
+      this._updateMatches();
     }, function (reason) {});
   }
   cancel() {
     if (this.textLayerRenderTask) {
       this.textLayerRenderTask.cancel();
       this.textLayerRenderTask = null;
     }
   }
   setTextContentStream(readableStream) {
     this.cancel();
     this.textContentStream = readableStream;
   }
   setTextContent(textContent) {
     this.cancel();
     this.textContent = textContent;
   }
-  convertMatches(matches, matchesLength) {
-    let i = 0;
-    let iIndex = 0;
-    let textContentItemsStr = this.textContentItemsStr;
-    let end = textContentItemsStr.length - 1;
-    let queryLen = this.findController === null ? 0 : this.findController.state.query.length;
-    let ret = [];
+  _convertMatches(matches, matchesLength) {
     if (!matches) {
-      return ret;
-    }
-    for (let m = 0, len = matches.length; m < len; m++) {
+      return [];
+    }
+    const { findController, textContentItemsStr } = this;
+    let i = 0,
+        iIndex = 0;
+    const end = textContentItemsStr.length - 1;
+    const queryLen = findController.state.query.length;
+    const result = [];
+    for (let m = 0, mm = matches.length; m < mm; m++) {
       let matchIdx = matches[m];
       while (i !== end && matchIdx >= iIndex + textContentItemsStr[i].length) {
         iIndex += textContentItemsStr[i].length;
         i++;
       }
       if (i === textContentItemsStr.length) {
         console.error('Could not find a matching mapping');
       }
@@ -8027,31 +8044,29 @@ class TextLayerBuilder {
       while (i !== end && matchIdx > iIndex + textContentItemsStr[i].length) {
         iIndex += textContentItemsStr[i].length;
         i++;
       }
       match.end = {
         divIdx: i,
         offset: matchIdx - iIndex
       };
-      ret.push(match);
-    }
-    return ret;
-  }
-  renderMatches(matches) {
+      result.push(match);
+    }
+    return result;
+  }
+  _renderMatches(matches) {
     if (matches.length === 0) {
       return;
     }
-    let textContentItemsStr = this.textContentItemsStr;
-    let textDivs = this.textDivs;
+    const { findController, pageIdx, textContentItemsStr, textDivs } = this;
+    const isSelectedPage = pageIdx === findController.selected.pageIdx;
+    const selectedMatchIdx = findController.selected.matchIdx;
+    const highlightAll = findController.state.highlightAll;
     let prevEnd = null;
-    let pageIdx = this.pageIdx;
-    let isSelectedPage = this.findController === null ? false : pageIdx === this.findController.selected.pageIdx;
-    let selectedMatchIdx = this.findController === null ? -1 : this.findController.selected.matchIdx;
-    let highlightAll = this.findController === null ? false : this.findController.state.highlightAll;
     let infinity = {
       divIdx: -1,
       offset: undefined
     };
     function beginText(begin, className) {
       let divIdx = begin.divIdx;
       textDivs[divIdx].textContent = '';
       appendTextToDiv(divIdx, 0, begin.offset, className);
@@ -8078,24 +8093,22 @@ class TextLayerBuilder {
       return;
     }
     for (let i = i0; i < i1; i++) {
       let match = matches[i];
       let begin = match.begin;
       let end = match.end;
       let isSelected = isSelectedPage && i === selectedMatchIdx;
       let highlightSuffix = isSelected ? ' selected' : '';
-      if (this.findController) {
-        if (this.findController.selected.matchIdx === i && this.findController.selected.pageIdx === pageIdx) {
-          const spot = {
-            top: MATCH_SCROLL_OFFSET_TOP,
-            left: MATCH_SCROLL_OFFSET_LEFT
-          };
-          (0, _ui_utils.scrollIntoView)(textDivs[begin.divIdx], spot, true);
-        }
+      if (findController.selected.matchIdx === i && findController.selected.pageIdx === pageIdx) {
+        const spot = {
+          top: MATCH_SCROLL_OFFSET_TOP,
+          left: MATCH_SCROLL_OFFSET_LEFT
+        };
+        (0, _ui_utils.scrollIntoView)(textDivs[begin.divIdx], spot, true);
       }
       if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
         if (prevEnd !== null) {
           appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
         }
         beginText(begin);
       } else {
         appendTextToDiv(prevEnd.divIdx, prevEnd.offset, begin.offset);
@@ -8110,44 +8123,39 @@ class TextLayerBuilder {
         beginText(end, 'highlight end' + highlightSuffix);
       }
       prevEnd = end;
     }
     if (prevEnd) {
       appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
     }
   }
-  updateMatches() {
+  _updateMatches() {
     if (!this.renderingDone) {
       return;
     }
-    let matches = this.matches;
-    let textDivs = this.textDivs;
-    let textContentItemsStr = this.textContentItemsStr;
+    const { findController, matches, pageIdx, textContentItemsStr, textDivs } = this;
     let clearedUntilDivIdx = -1;
-    for (let i = 0, len = matches.length; i < len; i++) {
+    for (let i = 0, ii = matches.length; i < ii; i++) {
       let match = matches[i];
       let begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
       for (let n = begin, end = match.end.divIdx; n <= end; n++) {
         let div = textDivs[n];
         div.textContent = textContentItemsStr[n];
         div.className = '';
       }
       clearedUntilDivIdx = match.end.divIdx + 1;
     }
-    if (!this.findController || !this.findController.highlightMatches) {
-      return;
-    }
-    let pageMatches, pageMatchesLength;
-    if (this.findController !== null) {
-      pageMatches = this.findController.pageMatches[this.pageIdx] || null;
-      pageMatchesLength = this.findController.pageMatchesLength ? this.findController.pageMatchesLength[this.pageIdx] || null : null;
-    }
-    this.matches = this.convertMatches(pageMatches, pageMatchesLength);
-    this.renderMatches(this.matches);
+    if (!findController || !findController.highlightMatches) {
+      return;
+    }
+    const pageMatches = findController.pageMatches[pageIdx] || null;
+    const pageMatchesLength = findController.pageMatchesLength[pageIdx] || null;
+    this.matches = this._convertMatches(pageMatches, pageMatchesLength);
+    this._renderMatches(this.matches);
   }
   _bindEvents() {
     const { eventBus, _boundEvents } = this;
     _boundEvents.pageCancelled = evt => {
       if (evt.pageNumber !== this.pageNumber) {
         return;
       }
       if (this.textLayerRenderTask) {
@@ -8158,17 +8166,17 @@ class TextLayerBuilder {
         eventBus.off(name.toLowerCase(), _boundEvents[name]);
         delete _boundEvents[name];
       }
     };
     _boundEvents.updateTextLayerMatches = evt => {
       if (evt.pageIndex !== this.pageIdx && evt.pageIndex !== -1) {
         return;
       }
-      this.updateMatches();
+      this._updateMatches();
     };
     eventBus.on('pagecancelled', _boundEvents.pageCancelled);
     eventBus.on('updatetextlayermatches', _boundEvents.updateTextLayerMatches);
   }
   _bindMouse() {
     let div = this.textLayerDiv;
     let expandDivsTimer = null;
     div.addEventListener('mousedown', evt => {
--- a/browser/extensions/pdfjs/moz.yaml
+++ b/browser/extensions/pdfjs/moz.yaml
@@ -15,15 +15,15 @@ origin:
   description: Portable Document Format (PDF) viewer that is built with HTML5
 
   # Full URL for the package's homepage/etc
   # Usually different from repository url
   url: https://github.com/mozilla/pdf.js
 
   # Human-readable identifier for this version/release
   # Generally "version NNN", "tag SSS", "bookmark SSS"
-  release: version 2.0.943
+  release: version 2.1.15
 
   # The package's license, where possible using the mnemonic from
   # https://spdx.org/licenses/
   # Multiple licenses can be specified (as a YAML list)
   # A "LICENSE" file must exist containing the full license text
   license: Apache-2.0
--- a/browser/modules/AsyncTabSwitcher.jsm
+++ b/browser/modules/AsyncTabSwitcher.jsm
@@ -348,17 +348,17 @@ class AsyncTabSwitcher {
       //    tab crashed page yet (in this case, the TabParent is null)
       // 2. The tab has never presented, and has not finished loading
       //    a non-local-about: page.
       //
       // For (2), "finished loading a non-local-about: page" is
       // determined by the busy state on the tab element and checking
       // if the loaded URI is local.
       let hasSufficientlyLoaded = !this.requestedTab.hasAttribute("busy") &&
-        !this.tabbrowser._isLocalAboutURI(requestedBrowser.currentURI);
+        !this.tabbrowser.isLocalAboutURI(requestedBrowser.currentURI);
 
       let fl = requestedBrowser.frameLoader;
       shouldBeBlank = !this.minimizedOrFullyOccluded &&
         (!fl.tabParent ||
           (!hasSufficientlyLoaded && !fl.tabParent.hasPresented));
     }
 
     this.log("Tab should be blank: " + shouldBeBlank);
--- a/caps/NullPrincipal.cpp
+++ b/caps/NullPrincipal.cpp
@@ -33,34 +33,29 @@ NS_IMPL_QUERY_INTERFACE_CI(NullPrincipal
                            nsISerializable)
 NS_IMPL_CI_INTERFACE_GETTER(NullPrincipal,
                             nsIPrincipal,
                             nsISerializable)
 
 /* static */ already_AddRefed<NullPrincipal>
 NullPrincipal::CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom)
 {
-  MOZ_ASSERT(aInheritFrom);
-  return CreateWithInheritedAttributes(Cast(aInheritFrom)->OriginAttributesRef(), false);
+  RefPtr<NullPrincipal> nullPrin = new NullPrincipal();
+  nsresult rv = nullPrin->Init(Cast(aInheritFrom)->OriginAttributesRef());
+  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+  return nullPrin.forget();
 }
 
 /* static */ already_AddRefed<NullPrincipal>
 NullPrincipal::CreateWithInheritedAttributes(nsIDocShell* aDocShell, bool aIsFirstParty)
 {
-  MOZ_ASSERT(aDocShell);
-
   OriginAttributes attrs = nsDocShell::Cast(aDocShell)->GetOriginAttributes();
-  return CreateWithInheritedAttributes(attrs, aIsFirstParty);
-}
 
-/* static */ already_AddRefed<NullPrincipal>
-NullPrincipal::CreateWithInheritedAttributes(const OriginAttributes& aOriginAttributes, bool aIsFirstParty)
-{
   RefPtr<NullPrincipal> nullPrin = new NullPrincipal();
-  nsresult rv = nullPrin->Init(aOriginAttributes, aIsFirstParty);
+  nsresult rv = nullPrin->Init(attrs, aIsFirstParty);
   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
   return nullPrin.forget();
 }
 
 /* static */ already_AddRefed<NullPrincipal>
 NullPrincipal::Create(const OriginAttributes& aOriginAttributes, nsIURI* aURI)
 {
   RefPtr<NullPrincipal> nullPrin = new NullPrincipal();
--- a/caps/NullPrincipal.h
+++ b/caps/NullPrincipal.h
@@ -51,29 +51,24 @@ public:
   NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
   NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
   NS_IMETHOD GetURI(nsIURI** aURI) override;
   NS_IMETHOD GetDomain(nsIURI** aDomain) override;
   NS_IMETHOD SetDomain(nsIURI* aDomain) override;
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
   NS_IMETHOD GetAddonId(nsAString& aAddonId) override;
 
-  static already_AddRefed<NullPrincipal>
-  CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom);
+  static already_AddRefed<NullPrincipal> CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom);
 
   // Create NullPrincipal with origin attributes from docshell.
   // If aIsFirstParty is true, and the pref 'privacy.firstparty.isolate' is also
   // enabled, the mFirstPartyDomain value of the origin attributes will be set
   // to an unique value.
   static already_AddRefed<NullPrincipal>
-  CreateWithInheritedAttributes(nsIDocShell* aDocShell,
-                                bool aIsFirstParty = false);
-  static already_AddRefed<NullPrincipal>
-  CreateWithInheritedAttributes(const OriginAttributes& aOriginAttributes,
-                                bool aIsFirstParty = false);
+  CreateWithInheritedAttributes(nsIDocShell* aDocShell, bool aIsFirstParty = false);
 
   static already_AddRefed<NullPrincipal>
   Create(const OriginAttributes& aOriginAttributes,
          nsIURI* aURI = nullptr);
 
   static already_AddRefed<NullPrincipal>
   CreateWithoutOriginAttributes();
 
@@ -85,18 +80,17 @@ public:
   nsresult GetSiteIdentifier(SiteIdentifier& aSite) override {
     aSite.Init(this);
     return NS_OK;
   }
 
  protected:
   virtual ~NullPrincipal() = default;
 
-  bool SubsumesInternal(nsIPrincipal* aOther,
-                        DocumentDomainConsideration aConsideration) override
+  bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override
   {
     return aOther == this;
   }
 
   bool MayLoadInternal(nsIURI* aURI) override;
 
   nsCOMPtr<nsIURI> mURI;
 
--- a/devtools/client/locales/en-US/netmonitor.properties
+++ b/devtools/client/locales/en-US/netmonitor.properties
@@ -313,16 +313,20 @@ charts.totalTransferredSize=Transferred 
 # LOCALIZATION NOTE (charts.cacheEnabled): This is the label displayed
 # in the performance analysis view for "cache enabled" charts.
 charts.cacheEnabled=Primed cache
 
 # LOCALIZATION NOTE (charts.cacheDisabled): This is the label displayed
 # in the performance analysis view for "cache disabled" charts.
 charts.cacheDisabled=Empty cache
 
+# LOCALIZATION NOTE (charts.learnMore): This is the label displayed
+# in the performance analysis view, with a link to external documentation.
+charts.learnMore=Learn more about performance analysis
+
 # LOCALIZATION NOTE (charts.totalSize): This is the label displayed
 # in the performance analysis view for total requests size, in kilobytes.
 charts.totalSize=Size: %S KB
 
 # LOCALIZATION NOTE (charts.totalSeconds): Semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # This is the label displayed in the performance analysis view for the
 # total requests time, in seconds.
@@ -730,16 +734,20 @@ netmonitor.summary.address=Remote addres
 # LOCALIZATION NOTE (netmonitor.summary.status): This is the label displayed
 # in the network details headers tab identifying the status code.
 netmonitor.summary.status=Status code:
 
 # LOCALIZATION NOTE (netmonitor.summary.version): This is the label displayed
 # in the network details headers tab identifying the http version.
 netmonitor.summary.version=Version:
 
+# LOCALIZATION NOTE (netmonitor.summary.learnMore): This is the label displayed
+# in the network details headers tab, with a link to external documentation.
+netmonitor.summary.learnMore=Learn more about status code
+
 # LOCALIZATION NOTE (netmonitor.summary.editAndResend): This is the label displayed
 # on the button in the headers tab that opens a form to edit and resend the currently
 # displayed request
 netmonitor.summary.editAndResend=Edit and Resend
 
 # LOCALIZATION NOTE (netmonitor.summary.rawHeaders): This is the label displayed
 # on the button in the headers tab that toggle view for raw request/response headers
 # from the currently displayed request
@@ -799,16 +807,20 @@ netmonitor.timings.send=Sending:
 # in a "wait" state.
 netmonitor.timings.wait=Waiting:
 
 # LOCALIZATION NOTE (netmonitor.timings.receive): This is the label displayed
 # in the network details timings tab identifying the amount of time spent
 # in a "receive" state.
 netmonitor.timings.receive=Receiving:
 
+# LOCALIZATION NOTE (netmonitor.timings.learnMore): This is the label displayed
+# in the network details timings tab, with a link to external documentation
+netmonitor.timings.learnMore=Learn more about timings
+
 # LOCALIZATION NOTE (netmonitor.security.warning.cipher): A tooltip
 # for warning icon that indicates a connection uses insecure cipher suite.
 netmonitor.security.warning.cipher=The cipher used for encryption is deprecated and insecure.
 
 # LOCALIZATION NOTE (netmonitor.security.error): This is the label displayed
 # in the security tab if a security error prevented the connection.
 netmonitor.security.error=An error occurred:
 
--- a/devtools/client/netmonitor/src/components/HeadersPanel.js
+++ b/devtools/client/netmonitor/src/components/HeadersPanel.js
@@ -50,16 +50,17 @@ const HEADERS_FILTER_TEXT = L10N.getStr(
 const REQUEST_HEADERS = L10N.getStr("requestHeaders");
 const REQUEST_HEADERS_FROM_UPLOAD = L10N.getStr("requestHeadersFromUpload");
 const RESPONSE_HEADERS = L10N.getStr("responseHeaders");
 const SUMMARY_ADDRESS = L10N.getStr("netmonitor.summary.address");
 const SUMMARY_METHOD = L10N.getStr("netmonitor.summary.method");
 const SUMMARY_URL = L10N.getStr("netmonitor.summary.url");
 const SUMMARY_STATUS = L10N.getStr("netmonitor.summary.status");
 const SUMMARY_VERSION = L10N.getStr("netmonitor.summary.version");
+const SUMMARY_STATUS_LEARN_MORE = L10N.getStr("netmonitor.summary.learnMore");
 
 /**
  * Headers panel component
  * Lists basic information about the request
  */
 class HeadersPanel extends Component {
   static get propTypes() {
     return {
@@ -227,16 +228,17 @@ class HeadersPanel extends Component {
             className: "tabpanel-summary-value textbox-input devtools-monospace"
               + " status-text",
             readOnly: true,
             value: `${statusText}`,
             size: `${inputWidth}`,
           }),
           statusCodeDocURL ? MDNLink({
             url: statusCodeDocURL,
+            title: SUMMARY_STATUS_LEARN_MORE,
           }) : span({
             className: "headers-summary learn-more-link",
           }),
           button({
             className: "devtools-button edit-and-resend-button",
             onClick: cloneSelectedRequest,
           }, EDIT_AND_RESEND),
           button({
--- a/devtools/client/netmonitor/src/components/RequestListEmptyNotice.js
+++ b/devtools/client/netmonitor/src/components/RequestListEmptyNotice.js
@@ -20,16 +20,17 @@ const RequestListHeader = createFactory(
 const { button, div, span } = dom;
 
 const RELOAD_NOTICE_1 = L10N.getStr("netmonitor.reloadNotice1");
 const RELOAD_NOTICE_2 = L10N.getStr("netmonitor.reloadNotice2");
 const RELOAD_NOTICE_3 = L10N.getStr("netmonitor.reloadNotice3");
 const PERFORMANCE_NOTICE_1 = L10N.getStr("netmonitor.perfNotice1");
 const PERFORMANCE_NOTICE_2 = L10N.getStr("netmonitor.perfNotice2");
 const PERFORMANCE_NOTICE_3 = L10N.getStr("netmonitor.perfNotice3");
+const PERFORMANCE_LEARN_MORE = L10N.getStr("charts.learnMore");
 
 /**
  * UI displayed when the request list is empty. Contains instructions on reloading
  * the page and on triggering performance analysis of the page.
  */
 class RequestListEmptyNotice extends Component {
   static get propTypes() {
     return {
@@ -61,17 +62,20 @@ class RequestListEmptyNotice extends Com
         span(null, PERFORMANCE_NOTICE_1),
         button({
           title: PERFORMANCE_NOTICE_3,
           className: "devtools-button requests-list-perf-notice-button",
           "data-standalone": true,
           onClick: this.props.onPerfClick,
         }),
         span(null, PERFORMANCE_NOTICE_2),
-        MDNLink({ url: getPerformanceAnalysisURL() })
+        MDNLink({
+          url: getPerformanceAnalysisURL(),
+          title: PERFORMANCE_LEARN_MORE,
+        })
       )
     );
   }
 }
 
 module.exports = connect(
   undefined,
   (dispatch, props) => ({
--- a/devtools/client/netmonitor/src/components/StatisticsPanel.js
+++ b/devtools/client/netmonitor/src/components/StatisticsPanel.js
@@ -24,16 +24,17 @@ const MDNLink = createFactory(require("d
 
 const { button, div } = dom;
 const MediaQueryList = window.matchMedia("(min-width: 700px)");
 
 const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200;
 const BACK_BUTTON = L10N.getStr("netmonitor.backButton");
 const CHARTS_CACHE_ENABLED = L10N.getStr("charts.cacheEnabled");
 const CHARTS_CACHE_DISABLED = L10N.getStr("charts.cacheDisabled");
+const CHARTS_LEARN_MORE = L10N.getStr("charts.learnMore");
 
 /*
  * Statistics panel component
  * Performance analysis tool which shows you how long the browser takes to
  * download the different parts of your site.
  */
 class StatisticsPanel extends Component {
   static get propTypes() {
@@ -120,17 +121,24 @@ class StatisticsPanel extends Component 
     }
 
     // MDNLink is a React component but Chart isn't.  To get the link
     // into the chart we mount a new ReactDOM at the appropriate
     // location after the chart has been created.
     const title = this.refs[chartId].querySelector(".table-chart-title");
     const containerNode = document.createElement("span");
     title.appendChild(containerNode);
-    ReactDOM.render(MDNLink({ url }), containerNode);
+
+    ReactDOM.render(
+      MDNLink({
+        url: url,
+        title: CHARTS_LEARN_MORE,
+      }),
+      containerNode
+    );
     this.mdnLinkContainerNodes.set(chartId, containerNode);
   }
 
   unmountMDNLinkContainers() {
     for (const [, node] of this.mdnLinkContainerNodes) {
       ReactDOM.unmountComponentAtNode(node);
     }
   }
--- a/devtools/client/netmonitor/src/components/TimingsPanel.js
+++ b/devtools/client/netmonitor/src/components/TimingsPanel.js
@@ -91,15 +91,16 @@ class TimingsPanel extends Component {
       );
     });
 
     return (
       div({ className: "panel-container" },
         timelines,
         MDNLink({
           url: getNetMonitorTimingsURL(),
+          title: L10N.getStr("netmonitor.timings.learnMore"),
         }),
       )
     );
   }
 }
 
 module.exports = TimingsPanel;
--- a/devtools/client/netmonitor/src/utils/mdn-utils.js
+++ b/devtools/client/netmonitor/src/utils/mdn-utils.js
@@ -187,35 +187,36 @@ function getHTTPStatusCodeURL(statusCode
 }
 
 /**
  * Get the MDN URL of the Timings tag for Network Monitor.
  *
  * @return {string} the MDN URL of the Timings tag for Network Monitor.
  */
 function getNetMonitorTimingsURL() {
-  return `${MDN_URL}Tools/Network_Monitor${getGAParams()}#Timings`;
+  return `${MDN_URL}Tools/Network_Monitor/request_details${getGAParams()}#Timings`;
 }
 
 /**
  * Get the MDN URL for Performance Analysis
  *
  * @return {string} The MDN URL for the documentation of Performance Analysis.
  */
 function getPerformanceAnalysisURL() {
-  return `${MDN_URL}Tools/Network_Monitor${getGAParams()}#Performance_analysis`;
+  return `${MDN_URL}Tools/Network_Monitor/Performance_analysis${getGAParams()}`;
 }
 
 /**
  * Get the MDN URL for Filter box
  *
  * @return {string} The MDN URL for the documentation of Filter box.
  */
 function getFilterBoxURL() {
-  return `${MDN_URL}Tools/Network_Monitor${getGAParams()}#Filtering_by_properties`;
+  return `${MDN_URL}Tools/Network_Monitor/request_list${getGAParams()}` +
+    `#Filtering_by_properties`;
 }
 
 module.exports = {
   getHeadersURL,
   getHTTPStatusCodeURL,
   getNetMonitorTimingsURL,
   getPerformanceAnalysisURL,
   getFilterBoxURL,
--- a/devtools/client/netmonitor/test/unit/test_mdn-utils.js
+++ b/devtools/client/netmonitor/test/unit/test_mdn-utils.js
@@ -13,28 +13,33 @@ function run_test() {
   const GTM_PARAMS_WC = "?utm_source=mozilla" +
     "&utm_medium=devtools-webconsole&utm_campaign=default";
 
   const {
     getHeadersURL,
     getHTTPStatusCodeURL,
     getNetMonitorTimingsURL,
     getPerformanceAnalysisURL,
+    getFilterBoxURL,
   } = require("devtools/client/netmonitor/src/utils/mdn-utils");
 
   info("Checking for supported headers");
   equal(getHeadersURL("Accept"), `${MDN_URL}Web/HTTP/Headers/Accept${GTM_PARAMS_NM}`);
   info("Checking for unsupported headers");
   equal(getHeadersURL("Width"), null);
 
   info("Checking for supported status code");
   equal(getHTTPStatusCodeURL("200", "webconsole"),
     `${MDN_URL}Web/HTTP/Status/200${GTM_PARAMS_WC}`);
   info("Checking for unsupported status code");
   equal(getHTTPStatusCodeURL("999", "webconsole"),
     `${MDN_URL}Web/HTTP/Status${GTM_PARAMS_WC}`);
 
   equal(getNetMonitorTimingsURL(),
-    `${MDN_URL}Tools/Network_Monitor${GTM_PARAMS_NM}#Timings`);
+    `${MDN_URL}Tools/Network_Monitor/request_details${GTM_PARAMS_NM}#Timings`);
 
   equal(getPerformanceAnalysisURL(),
-    `${MDN_URL}Tools/Network_Monitor${GTM_PARAMS_NM}#Performance_analysis`);
+    `${MDN_URL}Tools/Network_Monitor/Performance_analysis${GTM_PARAMS_NM}`);
+
+  equal(getFilterBoxURL(),
+    `${MDN_URL}Tools/Network_Monitor/request_list` +
+    `${GTM_PARAMS_NM}#Filtering_by_properties`);
 }
--- a/devtools/client/responsive.html/browser/swap.js
+++ b/devtools/client/responsive.html/browser/swap.js
@@ -101,30 +101,42 @@ function swapToInnerBrowser({ tab, conta
       browser.loadURI(uri, options);
     });
   }
 
   return {
 
     async start() {
       // In some cases, such as a preloaded browser used for about:newtab, browser code
-      // will force a new frameloader on next navigation to ensure balanced process
-      // assignment.  If this case will happen here, navigate to about:blank first to get
-      // this out of way so that we stay within one process while RDM is open.
-      const { newFrameloader } = E10SUtils.shouldLoadURIInBrowser(
-        tab.linkedBrowser,
-        "about:blank"
+      // will force a new frameloader on next navigation to remote content to ensure
+      // balanced process assignment.  If this case will happen here, navigate to
+      // about:blank first to get this out of way so that we stay within one process while
+      // RDM is open. Some process selection rules are specific to remote content, so we
+      // use `http://example.com` as a test for what a remote navigation would cause.
+      const {
+        requiredRemoteType,
+        mustChangeProcess,
+        newFrameloader,
+      } = E10SUtils.shouldLoadURIInBrowser(
+         tab.linkedBrowser,
+        "http://example.com"
       );
       if (newFrameloader) {
         debug(`Tab will force a new frameloader on navigation, load about:blank first`);
         await loadURIWithNewFrameLoader(tab.linkedBrowser, "about:blank", {
           flags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY,
           triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
         });
       }
+      if (mustChangeProcess) {
+        debug(`Tab will force a process flip on navigation, load about:blank first`);
+        gBrowser.updateBrowserRemoteness(tab.linkedBrowser, true, {
+          remoteType: requiredRemoteType,
+        });
+      }
 
       tab.isResponsiveDesignMode = true;
 
       // Hide the browser content temporarily while things move around to avoid displaying
       // strange intermediate states.
       tab.linkedBrowser.style.visibility = "hidden";
 
       // Freeze navigation temporarily to avoid "blinking" in the location bar.
--- a/docshell/base/moz.build
+++ b/docshell/base/moz.build
@@ -56,17 +56,17 @@ XPIDL_SOURCES += [
     'nsIWebPageDescriptor.idl',
 ]
 
 XPIDL_MODULE = 'docshell'
 
 EXPORTS += [
     'nsCTooltipTextProvider.h',
     'nsDocShell.h',
-    'nsDocShellLoadState.h',
+    'nsDocShellLoadInfo.h',
     'nsDocShellLoadTypes.h',
     'nsDocShellTreeOwner.h',
     'nsILinkHandler.h',
     'nsIScrollObserver.h',
     'SerializedLoadContext.h',
 ]
 
 EXPORTS.mozilla += [
@@ -83,17 +83,17 @@ UNIFIED_SOURCES += [
     'BrowsingContext.cpp',
     'ChromeBrowsingContext.cpp',
     'LoadContext.cpp',
     'nsAboutRedirector.cpp',
     'nsDefaultURIFixup.cpp',
     'nsDocShell.cpp',
     'nsDocShellEditorData.cpp',
     'nsDocShellEnumerator.cpp',
-    'nsDocShellLoadState.cpp',
+    'nsDocShellLoadInfo.cpp',
     'nsDocShellTreeOwner.cpp',
     'nsDSURIContentListener.cpp',
     'nsPingListener.cpp',
     'nsRefreshTimer.cpp',
     'nsWebNavigationInfo.cpp',
     'SerializedLoadContext.cpp',
 ]
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -164,17 +164,17 @@
 #include "nsContentDLF.h"
 #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...)
 #include "nsContentSecurityManager.h"
 #include "nsContentUtils.h"
 #include "nsCURILoader.h"
 #include "nsDocShellCID.h"
 #include "nsDocShellEditorData.h"
 #include "nsDocShellEnumerator.h"
-#include "nsDocShellLoadState.h"
+#include "nsDocShellLoadInfo.h"
 #include "nsDocShellLoadTypes.h"
 #include "nsDOMCID.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsDSURIContentListener.h"
 #include "nsError.h"
 #include "nsEscape.h"
 #include "nsFocusManager.h"
 #include "nsGlobalWindow.h"
@@ -638,247 +638,385 @@ nsDocShell::GetInterface(const nsIID& aI
     return nsDocLoader::GetInterface(aIID, aSink);
   }
 
   NS_IF_ADDREF(((nsISupports*)*aSink));
   return *aSink ? NS_OK : NS_NOINTERFACE;
 }
 
 NS_IMETHODIMP
-nsDocShell::LoadURI(nsDocShellLoadState* aLoadState)
-{
-  MOZ_ASSERT(aLoadState, "Must have a valid load state!");
-  MOZ_ASSERT((aLoadState->LoadFlags() & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0,
-             "Should not have these flags set");
-  MOZ_ASSERT(aLoadState->URI(), "Should have a valid URI to load");
+nsDocShell::LoadURI(nsIURI* aURI,
+                    nsDocShellLoadInfo* aLoadInfo,
+                    uint32_t aLoadFlags,
+                    bool aFirstParty)
+{
+  MOZ_ASSERT(aLoadInfo || (aLoadFlags & EXTRA_LOAD_FLAGS) == 0,
+             "Unexpected flags");
+  MOZ_ASSERT((aLoadFlags & 0xf) == 0, "Should not have these flags set");
 
   // Note: we allow loads to get through here even if mFiredUnloadEvent is
   // true; that case will get handled in LoadInternal or LoadHistoryEntry,
   // so we pass false as the second parameter to IsNavigationAllowed.
   // However, we don't allow the page to change location *in the middle of*
   // firing beforeunload, so we do need to check if *beforeunload* is currently
   // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
   if (!IsNavigationAllowed(true, false)) {
     return NS_OK; // JS may not handle returning of an error code
   }
 
+  nsCOMPtr<nsIURI> referrer;
+  nsCOMPtr<nsIURI> originalURI;
+  Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
+  bool keepResultPrincipalURIIfSet = false;
+  bool loadReplace = false;
+  nsCOMPtr<nsIInputStream> postStream;
+  nsCOMPtr<nsIInputStream> headersStream;
+  nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+  bool inheritPrincipal = false;
+  bool principalIsExplicit = false;
+  bool sendReferrer = true;
+  uint32_t referrerPolicy = RP_Unset;
+  bool isSrcdoc = false;
+  nsCOMPtr<nsISHEntry> shEntry;
+  nsString target;
+  nsAutoString srcdoc;
+  bool forceAllowDataURI = false;
+  bool originalFrameSrc = false;
+  nsCOMPtr<nsIDocShell> sourceDocShell;
+  nsCOMPtr<nsIURI> baseURI;
+
+  uint32_t loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
+
+  NS_ENSURE_ARG(aURI);
+
   if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
-      mItemType == typeContent && !NS_IsAboutBlank(aLoadState->URI())) {
+      mItemType == typeContent && !NS_IsAboutBlank(aURI)) {
     StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
   }
 
+  // Extract the info from the DocShellLoadInfo struct...
+  if (aLoadInfo) {
+    referrer = aLoadInfo->Referrer();
+    originalURI = aLoadInfo->OriginalURI();
+    aLoadInfo->GetMaybeResultPrincipalURI(resultPrincipalURI);
+    keepResultPrincipalURIIfSet = aLoadInfo->KeepResultPrincipalURIIfSet();
+    loadReplace = aLoadInfo->LoadReplace();
+    // Get the appropriate loadType from nsIDocShellLoadInfo type
+    loadType = aLoadInfo->LoadType();
+
+    triggeringPrincipal = aLoadInfo->TriggeringPrincipal();
+    inheritPrincipal = aLoadInfo->InheritPrincipal();
+    principalIsExplicit = aLoadInfo->PrincipalIsExplicit();
+    shEntry = aLoadInfo->SHEntry();
+    aLoadInfo->GetTarget(target);
+    postStream = aLoadInfo->PostDataStream();
+    headersStream = aLoadInfo->HeadersStream();
+    sendReferrer = aLoadInfo->SendReferrer();
+    referrerPolicy = aLoadInfo->ReferrerPolicy();
+    isSrcdoc = aLoadInfo->IsSrcdocLoad();
+    aLoadInfo->GetSrcdocData(srcdoc);
+    sourceDocShell = aLoadInfo->SourceDocShell();
+    baseURI = aLoadInfo->BaseURI();
+    forceAllowDataURI = aLoadInfo->ForceAllowDataURI();
+    originalFrameSrc = aLoadInfo->OriginalFrameSrc();
+  }
+
   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
           ("nsDocShell[%p]: loading %s with flags 0x%08x",
-           this, aLoadState->URI()->GetSpecOrDefault().get(),
-           aLoadState->LoadFlags()));
-
-  if (!aLoadState->SHEntry() &&
-      !LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
-                           LOAD_FLAGS_REPLACE_HISTORY)) {
-    // This is possibly a subframe, so handle it accordingly.
-    //
-    // If history exists, it will be loaded into the aLoadState object, and the
-    // LoadType will be changed.
-    MaybeHandleSubframeHistory(aLoadState);
-  }
-
-  if (aLoadState->SHEntry()) {
-#ifdef DEBUG
-    MOZ_LOG(gDocShellLog, LogLevel::Debug,
-           ("nsDocShell[%p]: loading from session history", this));
-#endif
-
-    return LoadHistoryEntry(aLoadState->SHEntry(), aLoadState->LoadType());
-  }
-
-  // On history navigation via Back/Forward buttons, don't execute
-  // automatic JavaScript redirection such as |location.href = ...| or
-  // |window.open()|
-  //
-  // LOAD_NORMAL:        window.open(...) etc.
-  // LOAD_STOP_CONTENT:  location.href = ..., location.assign(...)
-  if ((aLoadState->LoadType() == LOAD_NORMAL ||
-       aLoadState->LoadType() == LOAD_STOP_CONTENT) &&
-      ShouldBlockLoadingForBackButton()) {
-    return NS_OK;
-  }
-
-  // Set up the inheriting principal in LoadState.
-  nsresult rv = aLoadState->SetupInheritingPrincipal(mItemType, mOriginAttributes);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = aLoadState->SetupTriggeringPrincipal(mOriginAttributes);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  aLoadState->CalculateDocShellInternalLoadFlags();
-
-  mozilla::Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
-  aLoadState->GetMaybeResultPrincipalURI(resultPrincipalURI);
-
-  MOZ_ASSERT(aLoadState->TypeHint().IsVoid(),
-             "Typehint should be null when calling InternalLoad from LoadURI");
-  MOZ_ASSERT(aLoadState->FileName().IsVoid(),
-             "FileName should be null when calling InternalLoad from LoadURI");
-  MOZ_ASSERT(aLoadState->SHEntry() == nullptr,
-             "SHEntry should be null when calling InternalLoad from LoadURI");
-
-  return InternalLoad(aLoadState->URI(),
-                      aLoadState->OriginalURI(),
-                      resultPrincipalURI,
-                      aLoadState->KeepResultPrincipalURIIfSet(),
-                      aLoadState->LoadReplace(),
-                      aLoadState->Referrer(),
-                      aLoadState->ReferrerPolicy(),
-                      aLoadState->TriggeringPrincipal(),
-                      aLoadState->PrincipalToInherit(),
-                      aLoadState->DocShellInternalLoadFlags(),
-                      aLoadState->Target(),
-                      aLoadState->TypeHint(),
-                      aLoadState->FileName(),
-                      aLoadState->PostDataStream(),
-                      aLoadState->HeadersStream(),
-                      aLoadState->LoadType(),
-                      aLoadState->SHEntry(),
-                      aLoadState->FirstParty(),
-                      aLoadState->SrcdocData(),
-                      aLoadState->SourceDocShell(),
-                      aLoadState->BaseURI(),
-                      nullptr, // no nsIDocShell
-                      nullptr); // no nsIRequest
-}
-
-void
-nsDocShell::MaybeHandleSubframeHistory(nsDocShellLoadState* aLoadState)
-{
-  // First, verify if this is a subframe.
+           this, aURI->GetSpecOrDefault().get(), aLoadFlags));
+
+  if (!shEntry &&
+      !LOAD_TYPE_HAS_FLAGS(loadType, LOAD_FLAGS_REPLACE_HISTORY)) {
+    // First verify if this is a subframe.
     nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
     GetSameTypeParent(getter_AddRefs(parentAsItem));
     nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
-
-  if (!parentDS || parentDS == static_cast<nsIDocShell*>(this)) {
-    // This is the root docshell. If we got here while
-    // executing an onLoad Handler,this load will not go
-    // into session history.
-    bool inOnLoadHandler = false;
-    GetIsExecutingOnLoadHandler(&inOnLoadHandler);
-    if (inOnLoadHandler) {
-      aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
-    }
-    return;
-  }
-
-  /* OK. It is a subframe. Checkout the parent's loadtype. If the parent was
-   * loaded through a history mechanism, then get the SH entry for the child from
-   * the parent. This is done to restore frameset navigation while going
-   * back/forward. If the parent was loaded through any other loadType, set the
-   * child's loadType too accordingly, so that session history does not get
-   * confused.
+    uint32_t parentLoadType;
+
+    if (parentDS && parentDS != static_cast<nsIDocShell*>(this)) {
+      /* OK. It is a subframe. Checkout the
+       * parent's loadtype. If the parent was loaded thro' a history
+       * mechanism, then get the SH entry for the child from the parent.
+       * This is done to restore frameset navigation while going back/forward.
+       * If the parent was loaded through any other loadType, set the
+       * child's loadType too accordingly, so that session history does not
+       * get confused.
        */
 
       // Get the parent's load type
-  uint32_t parentLoadType;
       parentDS->GetLoadType(&parentLoadType);
 
       // Get the ShEntry for the child from the parent
       nsCOMPtr<nsISHEntry> currentSH;
       bool oshe = false;
       parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
       bool dynamicallyAddedChild = mDynamicallyCreated;
-
       if (!dynamicallyAddedChild && !oshe && currentSH) {
         currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
       }
-
       if (!dynamicallyAddedChild) {
         // Only use the old SHEntry, if we're sure enough that
         // it wasn't originally for some other frame.
-    nsCOMPtr<nsISHEntry> shEntry;
         parentDS->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
-    aLoadState->SetSHEntry(shEntry);
       }
 
       // Make some decisions on the child frame's loadType based on the
       // parent's loadType, if the subframe hasn't loaded anything into it.
       //
       // In some cases privileged scripts may try to get the DOMWindow
       // reference of this docshell before the loading starts, causing the
       // initial about:blank content viewer being created and mCurrentURI being
       // set. To handle this case we check if mCurrentURI is about:blank and
       // currentSHEntry is null.
       nsCOMPtr<nsISHEntry> currentChildEntry;
       GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe);
-
-  if (mCurrentURI && (!NS_IsAboutBlank(mCurrentURI) || currentChildEntry)) {
+      if (!mCurrentURI || (NS_IsAboutBlank(mCurrentURI) && !currentChildEntry)) {
+        // This is a newly created frame. Check for exception cases first.
+        // By default the subframe will inherit the parent's loadType.
+        if (shEntry && (parentLoadType == LOAD_NORMAL ||
+                        parentLoadType == LOAD_LINK   ||
+                        parentLoadType == LOAD_NORMAL_EXTERNAL)) {
+          // The parent was loaded normally. In this case, this *brand new*
+          // child really shouldn't have a SHEntry. If it does, it could be
+          // because the parent is replacing an existing frame with a new frame,
+          // in the onLoadHandler. We don't want this url to get into session
+          // history. Clear off shEntry, and set load type to
+          // LOAD_BYPASS_HISTORY.
+          bool inOnLoadHandler = false;
+          parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
+          if (inOnLoadHandler) {
+            loadType = LOAD_NORMAL_REPLACE;
+            shEntry = nullptr;
+          }
+        } else if (parentLoadType == LOAD_REFRESH) {
+          // Clear shEntry. For refresh loads, we have to load
+          // what comes thro' the pipe, not what's in history.
+          shEntry = nullptr;
+        } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
+                   (shEntry &&
+                    ((parentLoadType & LOAD_CMD_HISTORY) ||
+                     (parentLoadType == LOAD_RELOAD_NORMAL) ||
+                     (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) ||
+                     (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) ||
+                     (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) {
+          // If the parent url, bypassed history or was loaded from
+          // history, pass on the parent's loadType to the new child
+          // frame too, so that the child frame will also
+          // avoid getting into history.
+          loadType = parentLoadType;
+        } else if (parentLoadType == LOAD_ERROR_PAGE) {
+          // If the parent document is an error page, we don't
+          // want to update global/session history. However,
+          // this child frame is not an error page.
+          loadType = LOAD_BYPASS_HISTORY;
+        } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
+                   (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
+                   (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
+          // the new frame should inherit the parent's load type so that it also
+          // bypasses the cache and/or proxy
+          loadType = parentLoadType;
+        }
+      } else {
         // This is a pre-existing subframe. If
         // 1. The load of this frame was not originally initiated by session
         //    history directly (i.e. (!shEntry) condition succeeded, but it can
         //    still be a history load on parent which causes this frame being
-    //    loaded), which we checked with the above assert, and
+        //    loaded), and
         // 2. mCurrentURI is not null, nor the initial about:blank,
         // it is possible that a parent's onLoadHandler or even self's
         // onLoadHandler is loading a new page in this child. Check parent's and
         // self's busy flag and if it is set, we don't want this onLoadHandler
         // load to get in to session history.
         uint32_t parentBusy = BUSY_FLAGS_NONE;
         uint32_t selfBusy = BUSY_FLAGS_NONE;
         parentDS->GetBusyFlags(&parentBusy);
         GetBusyFlags(&selfBusy);
         if (parentBusy & BUSY_FLAGS_BUSY ||
             selfBusy & BUSY_FLAGS_BUSY) {
-      aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
-      aLoadState->SetSHEntry(nullptr);
-    }
-    return;
-  }
-
-  // This is a newly created frame. Check for exception cases first.
-  // By default the subframe will inherit the parent's loadType.
-  if (aLoadState->SHEntry() && (parentLoadType == LOAD_NORMAL ||
-                                parentLoadType == LOAD_LINK   ||
-                                parentLoadType == LOAD_NORMAL_EXTERNAL)) {
-    // The parent was loaded normally. In this case, this *brand new*
-    // child really shouldn't have a SHEntry. If it does, it could be
-    // because the parent is replacing an existing frame with a new frame,
-    // in the onLoadHandler. We don't want this url to get into session
-    // history. Clear off shEntry, and set load type to
-    // LOAD_BYPASS_HISTORY.
+          loadType = LOAD_NORMAL_REPLACE;
+          shEntry = nullptr;
+        }
+      }
+    } // parentDS
+    else {
+      // This is the root docshell. If we got here while
+      // executing an onLoad Handler,this load will not go
+      // into session history.
       bool inOnLoadHandler = false;
-    parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
+      GetIsExecutingOnLoadHandler(&inOnLoadHandler);
       if (inOnLoadHandler) {
-      aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
-      aLoadState->SetSHEntry(nullptr);
-    }
-  } else if (parentLoadType == LOAD_REFRESH) {
-    // Clear shEntry. For refresh loads, we have to load
-    // what comes through the pipe, not what's in history.
-    aLoadState->SetSHEntry(nullptr);
-  } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
-             (aLoadState->SHEntry() &&
-              ((parentLoadType & LOAD_CMD_HISTORY) ||
-               (parentLoadType == LOAD_RELOAD_NORMAL) ||
-               (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) ||
-               (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) ||
-               (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) {
-    // If the parent url, bypassed history or was loaded from
-    // history, pass on the parent's loadType to the new child
-    // frame too, so that the child frame will also
-    // avoid getting into history.
-    aLoadState->SetLoadType(parentLoadType);
-  } else if (parentLoadType == LOAD_ERROR_PAGE) {
-    // If the parent document is an error page, we don't
-    // want to update global/session history. However,
-    // this child frame is not an error page.
-    aLoadState->SetLoadType(LOAD_BYPASS_HISTORY);
-  } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
-             (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
-             (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
-    // the new frame should inherit the parent's load type so that it also
-    // bypasses the cache and/or proxy
-    aLoadState->SetLoadType(parentLoadType);
-  }
+        loadType = LOAD_NORMAL_REPLACE;
+      }
+    }
+  } // !shEntry
+
+  if (shEntry) {
+#ifdef DEBUG
+    MOZ_LOG(gDocShellLog, LogLevel::Debug,
+           ("nsDocShell[%p]: loading from session history", this));
+#endif
+
+    return LoadHistoryEntry(shEntry, loadType);
+  }
+
+  // On history navigation via Back/Forward buttons, don't execute
+  // automatic JavaScript redirection such as |location.href = ...| or
+  // |window.open()|
+  //
+  // LOAD_NORMAL:        window.open(...) etc.
+  // LOAD_STOP_CONTENT:  location.href = ..., location.assign(...)
+  if ((loadType == LOAD_NORMAL || loadType == LOAD_STOP_CONTENT) &&
+      ShouldBlockLoadingForBackButton()) {
+    return NS_OK;
+  }
+
+  // Perform the load...
+
+  // We need a principalToInherit.
+  //
+  // If principalIsExplicit is not set there are 4 possibilities:
+  // (1) If the system principal or an expanded principal was passed
+  //     in and we're a typeContent docshell, inherit the principal
+  //     from the current document instead.
+  // (2) In all other cases when the principal passed in is not null,
+  //     use that principal.
+  // (3) If the caller has allowed inheriting from the current document,
+  //     or if we're being called from system code (eg chrome JS or pure
+  //     C++) then inheritPrincipal should be true and InternalLoad will get
+  //     a principal from the current document. If none of these things are
+  //     true, then
+  // (4) we don't pass a principal into the channel, and a principal will be
+  //     created later from the channel's internal data.
+  //
+  // If principalIsExplicit *is* set, there are 4 possibilities
+  // (1) If the system principal or an expanded principal was passed in
+  //     and we're a typeContent docshell, return an error.
+  // (2) In all other cases when the principal passed in is not null,
+  //     use that principal.
+  // (3) If the caller has allowed inheriting from the current document,
+  //     then inheritPrincipal should be true and InternalLoad will get
+  //     a principal from the current document. If none of these things are
+  //     true, then
+  // (4) we dont' pass a principal into the channel, and a principal will be
+  //     created later from the channel's internal data.
+  nsCOMPtr<nsIPrincipal> principalToInherit = triggeringPrincipal;
+  if (principalToInherit && mItemType != typeChrome) {
+    if (nsContentUtils::IsSystemPrincipal(principalToInherit)) {
+      if (principalIsExplicit) {
+        return NS_ERROR_DOM_SECURITY_ERR;
+      }
+      principalToInherit = nullptr;
+      inheritPrincipal = true;
+    } else if (nsContentUtils::IsExpandedPrincipal(principalToInherit)) {
+      if (principalIsExplicit) {
+        return NS_ERROR_DOM_SECURITY_ERR;
+      }
+      // Don't inherit from the current page.  Just do the safe thing
+      // and pretend that we were loaded by a nullprincipal.
+      //
+      // We didn't inherit OriginAttributes here as ExpandedPrincipal doesn't
+      // have origin attributes.
+      principalToInherit = NullPrincipal::CreateWithInheritedAttributes(this);
+      inheritPrincipal = false;
+    }
+  }
+  if (!principalToInherit && !inheritPrincipal && !principalIsExplicit) {
+    // See if there's system or chrome JS code running
+    inheritPrincipal = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
+  }
+
+  if (aLoadFlags & LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL) {
+    inheritPrincipal = false;
+    // If aFirstParty is true and the pref 'privacy.firstparty.isolate' is
+    // enabled, we will set firstPartyDomain on the origin attributes.
+    principalToInherit = NullPrincipal::CreateWithInheritedAttributes(this, aFirstParty);
+  }
+
+  // If the triggeringPrincipal is not passed explicitly, we first try to create
+  // a principal from the referrer, since the referrer URI reflects the web origin
+  // that triggered the load. If there is no referrer URI, we fall back to using
+  // the SystemPrincipal. It's safe to assume that no provided triggeringPrincipal
+  // and no referrer simulate a load that was triggered by the system.
+  // It's important to note that this block of code needs to appear *after* the block
+  // where we munge the principalToInherit, because otherwise we would never enter
+  // code blocks checking if the principalToInherit is null and we will end up with
+  // a wrong inheritPrincipal flag.
+  if (!triggeringPrincipal) {
+    if (referrer) {
+      nsresult rv = CreatePrincipalFromReferrer(referrer,
+                                                getter_AddRefs(triggeringPrincipal));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+    else {
+      triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
+    }
+  }
+
+  uint32_t flags = 0;
+
+  if (inheritPrincipal) {
+    MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(principalToInherit), "Should not inherit SystemPrincipal");
+    flags |= INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
+  }
+
+  if (!sendReferrer) {
+    flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
+  }
+
+  if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
+    flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
+  }
+
+  if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD) {
+    flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
+  }
+
+  if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER) {
+    flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
+  }
+
+  if (aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_COOKIES) {
+    flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
+  }
+
+  if (isSrcdoc) {
+    flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
+  }
+
+  if (forceAllowDataURI) {
+    flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
+  }
+
+  if (originalFrameSrc) {
+    flags |= INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC;
+  }
+
+  return InternalLoad(aURI,
+                      originalURI,
+                      resultPrincipalURI,
+                      keepResultPrincipalURIIfSet,
+                      loadReplace,
+                      referrer,
+                      referrerPolicy,
+                      triggeringPrincipal,
+                      principalToInherit,
+                      flags,
+                      target,
+                      nullptr,      // No type hint
+                      VoidString(), // No forced download
+                      postStream,
+                      headersStream,
+                      loadType,
+                      nullptr, // No SHEntry
+                      aFirstParty,
+                      srcdoc,
+                      sourceDocShell,
+                      baseURI,
+                      nullptr,  // No nsIDocShell
+                      nullptr); // No nsIRequest
 }
 
 /*
  * Reset state to a new content model within the current document and the
  * document viewer. Called by the document before initiating an out of band
  * document.write().
  */
 NS_IMETHODIMP
@@ -4083,17 +4221,17 @@ nsDocShell::LoadURIWithOptions(const nsA
                                uint32_t aLoadFlags,
                                nsIURI* aReferringURI,
                                uint32_t aReferrerPolicy,
                                nsIInputStream* aPostStream,
                                nsIInputStream* aHeaderStream,
                                nsIURI* aBaseURI,
                                nsIPrincipal* aTriggeringPrincipal)
 {
-  NS_ASSERTION((aLoadFlags & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0, "Unexpected flags");
+  NS_ASSERTION((aLoadFlags & 0xf) == 0, "Unexpected flags");
 
   if (!IsNavigationAllowed()) {
     return NS_OK; // JS may not handle returning of an error code
   }
   nsCOMPtr<nsIURI> uri;
   nsCOMPtr<nsIInputStream> postStream(aPostStream);
   nsresult rv = NS_OK;
 
@@ -4178,47 +4316,46 @@ nsDocShell::LoadURIWithOptions(const nsA
     aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
 
   // Don't pass certain flags that aren't needed and end up confusing
   // ConvertLoadTypeToDocShellInfoLoadType.  We do need to ensure that they are
   // passed to LoadURI though, since it uses them.
   uint32_t extraFlags = (aLoadFlags & EXTRA_LOAD_FLAGS);
   aLoadFlags &= ~EXTRA_LOAD_FLAGS;
 
-  RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState();
+  RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo();
 
   /*
    * If the user "Disables Protection on This Page", we have to make sure to
    * remember the users decision when opening links in child tabs [Bug 906190]
    */
+  uint32_t loadType;
   if (aLoadFlags & LOAD_FLAGS_ALLOW_MIXED_CONTENT) {
-    loadState->SetLoadType(MAKE_LOAD_TYPE(LOAD_NORMAL_ALLOW_MIXED_CONTENT, aLoadFlags));
+    loadType = MAKE_LOAD_TYPE(LOAD_NORMAL_ALLOW_MIXED_CONTENT, aLoadFlags);
   } else {
-    loadState->SetLoadType(MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags));
-  }
-
-  loadState->SetURI(uri);
-  loadState->SetLoadFlags(extraFlags);
-  loadState->SetFirstParty(true);
-  loadState->SetPostDataStream(postStream);
-  loadState->SetReferrer(aReferringURI);
-  loadState->SetReferrerPolicy((mozilla::net::ReferrerPolicy)aReferrerPolicy);
-  loadState->SetHeadersStream(aHeaderStream);
-  loadState->SetBaseURI(aBaseURI);
-  loadState->SetTriggeringPrincipal(aTriggeringPrincipal);
-  loadState->SetForceAllowDataURI(forceAllowDataURI);
+    loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
+  }
+
+  loadInfo->SetLoadType(loadType);
+  loadInfo->SetPostDataStream(postStream);
+  loadInfo->SetReferrer(aReferringURI);
+  loadInfo->SetReferrerPolicy((mozilla::net::ReferrerPolicy)aReferrerPolicy);
+  loadInfo->SetHeadersStream(aHeaderStream);
+  loadInfo->SetBaseURI(aBaseURI);
+  loadInfo->SetTriggeringPrincipal(aTriggeringPrincipal);
+  loadInfo->SetForceAllowDataURI(forceAllowDataURI);
 
   if (fixupInfo) {
     nsAutoString searchProvider, keyword;
     fixupInfo->GetKeywordProviderName(searchProvider);
     fixupInfo->GetKeywordAsSent(keyword);
     MaybeNotifyKeywordSearchLoading(searchProvider, keyword);
   }
 
-  rv = LoadURI(loadState);
+  rv = LoadURI(uri, loadInfo, extraFlags, true);
 
   // Save URI string in case it's needed later when
   // sending to search engine service in EndPageLoad()
   mOriginalUriString = uriString;
 
   return rv;
 }
 
@@ -4780,29 +4917,29 @@ nsDocShell::LoadErrorPage(nsIURI* aError
     // we go back or forward to another SHEntry with the same doc
     // identifier, the error page won't persist.
     mLSHE->AbandonBFCacheEntry();
   }
 
   return InternalLoad(aErrorURI, nullptr, Nothing(), false, false, nullptr, RP_Unset,
                       nsContentUtils::GetSystemPrincipal(), nullptr,
                       INTERNAL_LOAD_FLAGS_NONE, EmptyString(),
-                      VoidCString(), VoidString(), nullptr, nullptr,
+                      nullptr, VoidString(), nullptr, nullptr,
                       LOAD_ERROR_PAGE, nullptr, true, VoidString(), this,
                       nullptr, nullptr, nullptr);
 }
 
 NS_IMETHODIMP
 nsDocShell::Reload(uint32_t aReloadFlags)
 {
   if (!IsNavigationAllowed()) {
     return NS_OK; // JS may not handle returning of an error code
   }
   nsresult rv;
-  NS_ASSERTION(((aReloadFlags & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0),
+  NS_ASSERTION(((aReloadFlags & 0xf) == 0),
                "Reload command not updated to use load flags!");
   NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
                "Don't pass these flags to Reload");
 
   uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
   NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
 
   // Send notifications to the HistoryListener if any, about the impending
@@ -4880,17 +5017,17 @@ nsDocShell::Reload(uint32_t aReloadFlags
                       false,
                       loadReplace,
                       referrerURI,
                       referrerPolicy,
                       triggeringPrincipal,
                       triggeringPrincipal,
                       flags,
                       EmptyString(),   // No window target
-                      NS_LossyConvertUTF16toASCII(contentTypeHint),
+                      NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
                       VoidString(),    // No forced download
                       nullptr,         // No post data
                       nullptr,         // No headers data
                       loadType,        // Load type
                       nullptr,         // No SHEntry
                       true,
                       srcdoc,          // srcdoc argument for iframe
                       this,            // For reloads we are the source
@@ -6132,80 +6269,76 @@ nsDocShell::ForceRefreshURIFromTimer(nsI
   return ForceRefreshURI(aURI, aPrincipal, aDelay, aMetaRefresh);
 }
 
 NS_IMETHODIMP
 nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal, int32_t aDelay, bool aMetaRefresh)
 {
   NS_ENSURE_ARG(aURI);
 
-  RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState();
+  RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo();
 
   /* We do need to pass in a referrer, but we don't want it to
    * be sent to the server.
    */
-  loadState->SetSendReferrer(false);
+  loadInfo->SetSendReferrer(false);
 
   /* for most refreshes the current URI is an appropriate
    * internal referrer
    */
-  loadState->SetReferrer(mCurrentURI);
-
-  loadState->SetOriginalURI(mCurrentURI);
-  loadState->SetResultPrincipalURI(aURI);
-  loadState->SetResultPrincipalURIIsSome(true);
-  loadState->SetKeepResultPrincipalURIIfSet(true);
+  loadInfo->SetReferrer(mCurrentURI);
+
+  loadInfo->SetOriginalURI(mCurrentURI);
+  loadInfo->SetResultPrincipalURI(aURI);
+  loadInfo->SetResultPrincipalURIIsSome(true);
+  loadInfo->SetKeepResultPrincipalURIIfSet(true);
 
   // Set the triggering pricipal to aPrincipal if available, or current
   // document's principal otherwise.
   nsCOMPtr<nsIPrincipal> principal = aPrincipal;
   if (!principal) {
     nsCOMPtr<nsIDocument> doc = GetDocument();
     if (!doc) {
       return NS_ERROR_FAILURE;
     }
     principal = doc->NodePrincipal();
   }
-  loadState->SetTriggeringPrincipal(principal);
-  loadState->SetPrincipalIsExplicit(true);
+  loadInfo->SetTriggeringPrincipal(principal);
+  loadInfo->SetPrincipalIsExplicit(true);
 
   /* Check if this META refresh causes a redirection
    * to another site.
    */
   bool equalUri = false;
   nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
   if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh &&
       aDelay <= REFRESH_REDIRECT_TIMER) {
     /* It is a META refresh based redirection within the threshold time
      * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
      * Pass a REPLACE flag to LoadURI().
      */
-    loadState->SetLoadType(LOAD_NORMAL_REPLACE);
+    loadInfo->SetLoadType(LOAD_NORMAL_REPLACE);
 
     /* for redirects we mimic HTTP, which passes the
      *  original referrer
      */
     nsCOMPtr<nsIURI> internalReferrer;
     GetReferringURI(getter_AddRefs(internalReferrer));
     if (internalReferrer) {
-      loadState->SetReferrer(internalReferrer);
+      loadInfo->SetReferrer(internalReferrer);
     }
   } else {
-    loadState->SetLoadType(LOAD_REFRESH);
-  }
-
-  loadState->SetURI(aURI);
-  loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL);
-  loadState->SetFirstParty(true);
+    loadInfo->SetLoadType(LOAD_REFRESH);
+  }
 
   /*
    * LoadURI(...) will cancel all refresh timers... This causes the
    * Timer and its refreshData instance to be released...
    */
-  LoadURI(loadState);
+  LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL, true);
 
   return NS_OK;
 }
 
 nsresult
 nsDocShell::SetupRefreshURIFromHeader(nsIURI* aBaseURI,
                                       nsIPrincipal* aPrincipal,
                                       const nsACString& aHeader)
@@ -8962,27 +9095,26 @@ public:
                     nsIURI* aOriginalURI,
                     Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
                     bool aKeepResultPrincipalURIIfSet,
                     bool aLoadReplace,
                     nsIURI* aReferrer, uint32_t aReferrerPolicy,
                     nsIPrincipal* aTriggeringPrincipal,
                     nsIPrincipal* aPrincipalToInherit,
                     uint32_t aFlags,
-                    const nsACString& aTypeHint,
+                    const char* aTypeHint,
                     nsIInputStream* aPostData,
                     nsIInputStream* aHeadersData,
                     uint32_t aLoadType,
                     nsISHEntry* aSHEntry,
                     bool aFirstParty,
                     const nsAString& aSrcdoc,
                     nsIDocShell* aSourceDocShell,
                     nsIURI* aBaseURI)
     : mozilla::Runnable("InternalLoadEvent")
-    , mTypeHint(aTypeHint)
     , mSrcdoc(aSrcdoc)
     , mDocShell(aDocShell)
     , mURI(aURI)
     , mOriginalURI(aOriginalURI)
     , mResultPrincipalURI(aResultPrincipalURI)
     , mKeepResultPrincipalURIIfSet(aKeepResultPrincipalURIIfSet)
     , mLoadReplace(aLoadReplace)
     , mReferrer(aReferrer)
@@ -8993,29 +9125,36 @@ public:
     , mHeadersData(aHeadersData)
     , mSHEntry(aSHEntry)
     , mFlags(aFlags)
     , mLoadType(aLoadType)
     , mFirstParty(aFirstParty)
     , mSourceDocShell(aSourceDocShell)
     , mBaseURI(aBaseURI)
   {
+    // Make sure to keep null things null as needed
+    if (aTypeHint) {
+      mTypeHint = aTypeHint;
+    } else {
+      mTypeHint.SetIsVoid(true);
+    }
   }
 
   NS_IMETHOD
   Run() override
   {
     return mDocShell->InternalLoad(mURI, mOriginalURI, mResultPrincipalURI,
                                    mKeepResultPrincipalURIIfSet,
                                    mLoadReplace,
                                    mReferrer,
                                    mReferrerPolicy,
                                    mTriggeringPrincipal, mPrincipalToInherit,
                                    mFlags, EmptyString(),
-                                   mTypeHint,
+                                   mTypeHint.IsVoid() ? nullptr
+                                                      : mTypeHint.get(),
                                    VoidString(), mPostData,
                                    mHeadersData, mLoadType, mSHEntry,
                                    mFirstParty, mSrcdoc, mSourceDocShell,
                                    mBaseURI, nullptr,
                                    nullptr);
   }
 
 private:
@@ -9076,17 +9215,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
                          bool aKeepResultPrincipalURIIfSet,
                          bool aLoadReplace,
                          nsIURI* aReferrer,
                          uint32_t aReferrerPolicy,
                          nsIPrincipal* aTriggeringPrincipal,
                          nsIPrincipal* aPrincipalToInherit,
                          uint32_t aFlags,
                          const nsAString& aWindowTarget,
-                         const nsACString& aTypeHint,
+                         const char* aTypeHint,
                          const nsAString& aFileName,
                          nsIInputStream* aPostData,
                          nsIInputStream* aHeadersData,
                          uint32_t aLoadType,
                          nsISHEntry* aSHEntry,
                          bool aFirstParty,
                          const nsAString& aSrcdoc,
                          nsIDocShell* aSourceDocShell,
@@ -9357,41 +9496,41 @@ nsDocShell::InternalLoad(nsIURI* aURI,
         // If OnLinkClickSync was invoked inside the onload handler, the load
         // type would be set to LOAD_NORMAL_REPLACE; otherwise it should be
         // LOAD_LINK.
         MOZ_ASSERT(aLoadType == LOAD_LINK ||
                    aLoadType == LOAD_NORMAL_REPLACE);
         MOZ_ASSERT(!aSHEntry);
         MOZ_ASSERT(aFirstParty); // Windowwatcher will assume this.
 
-        RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState();
+        RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo();
 
         // Set up our loadinfo so it will do the load as much like we would have
         // as possible.
-        loadState->SetReferrer(aReferrer);
-        loadState->SetReferrerPolicy((mozilla::net::ReferrerPolicy)aReferrerPolicy);
-        loadState->SetSendReferrer(!(aFlags &
+        loadInfo->SetReferrer(aReferrer);
+        loadInfo->SetReferrerPolicy((mozilla::net::ReferrerPolicy)aReferrerPolicy);
+        loadInfo->SetSendReferrer(!(aFlags &
                                     INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
-        loadState->SetOriginalURI(aOriginalURI);
-        loadState->SetMaybeResultPrincipalURI(aResultPrincipalURI);
-        loadState->SetKeepResultPrincipalURIIfSet(aKeepResultPrincipalURIIfSet);
-        loadState->SetLoadReplace(aLoadReplace);
-        loadState->SetTriggeringPrincipal(aTriggeringPrincipal);
-        loadState->SetInheritPrincipal(
+        loadInfo->SetOriginalURI(aOriginalURI);
+        loadInfo->SetMaybeResultPrincipalURI(aResultPrincipalURI);
+        loadInfo->SetKeepResultPrincipalURIIfSet(aKeepResultPrincipalURIIfSet);
+        loadInfo->SetLoadReplace(aLoadReplace);
+        loadInfo->SetTriggeringPrincipal(aTriggeringPrincipal);
+        loadInfo->SetInheritPrincipal(
           aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
         // Explicit principal because we do not want any guesses as to what the
         // principal to inherit is: it should be aTriggeringPrincipal.
-        loadState->SetPrincipalIsExplicit(true);
-        loadState->SetLoadType(LOAD_LINK);
-        loadState->SetForceAllowDataURI(aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI);
+        loadInfo->SetPrincipalIsExplicit(true);
+        loadInfo->SetLoadType(LOAD_LINK);
+        loadInfo->SetForceAllowDataURI(aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI);
 
         rv = win->Open(NS_ConvertUTF8toUTF16(spec),
                        aWindowTarget, // window name
                        EmptyString(), // Features
-                       loadState,
+                       loadInfo,
                        true, // aForceNoOpener
                        getter_AddRefs(newWin));
         MOZ_ASSERT(!newWin);
         return rv;
       }
 
       rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
                                aWindowTarget,  // window name
@@ -10192,17 +10331,17 @@ nsDocShell::DoURILoad(nsIURI* aURI,
                       bool aLoadFromExternal,
                       bool aForceAllowDataURI,
                       bool aOriginalFrameSrc,
                       nsIURI* aReferrerURI,
                       bool aSendReferrer,
                       uint32_t aReferrerPolicy,
                       nsIPrincipal* aTriggeringPrincipal,
                       nsIPrincipal* aPrincipalToInherit,
-                      const nsACString& aTypeHint,
+                      const char* aTypeHint,
                       const nsAString& aFileName,
                       nsIInputStream* aPostData,
                       nsIInputStream* aHeadersData,
                       bool aFirstParty,
                       nsIDocShell** aDocShell,
                       nsIRequest** aRequest,
                       bool aIsNewWindowTarget,
                       bool aBypassClassifier,
@@ -10541,18 +10680,18 @@ nsDocShell::DoURILoad(nsIURI* aURI,
   loadInfo->GetResultPrincipalURI(getter_AddRefs(rpURI));
   if (aResultPrincipalURI &&
       (!aKeepResultPrincipalURIIfSet || !rpURI)) {
     // Unconditionally override, we want the replay to be equal to what has
     // been captured.
     loadInfo->SetResultPrincipalURI(aResultPrincipalURI.ref());
   }
 
-  if (!aTypeHint.IsVoid()) {
-    channel->SetContentType(aTypeHint);
+  if (aTypeHint && *aTypeHint) {
+    channel->SetContentType(nsDependentCString(aTypeHint));
     mContentTypeHint = aTypeHint;
   } else {
     mContentTypeHint.Truncate();
   }
 
   if (!aFileName.IsVoid()) {
     rv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -12096,17 +12235,17 @@ nsDocShell::LoadHistoryEntry(nsISHEntry*
                     false,
                     loadReplace,
                     referrerURI,
                     referrerPolicy,
                     triggeringPrincipal,
                     principalToInherit,
                     flags,
                     EmptyString(),      // No window target
-                    contentType,        // Type hint
+                    contentType.get(),  // Type hint
                     VoidString(),       // No forced file download
                     postData,           // Post data stream
                     nullptr,            // No headers stream
                     aLoadType,          // Load type
                     aEntry,             // SHEntry
                     true,
                     srcdoc,
                     nullptr,            // Source docshell, see comment above
@@ -13284,17 +13423,17 @@ nsDocShell::OnLinkClickSync(nsIContent* 
                              false,
                              false,                     // LoadReplace
                              referer,                   // Referer URI
                              refererPolicy,             // Referer policy
                              triggeringPrincipal,
                              aContent->NodePrincipal(),
                              flags,
                              target,                    // Window target
-                             NS_LossyConvertUTF16toASCII(typeHint),
+                             NS_LossyConvertUTF16toASCII(typeHint).get(),
                              aFileName,                 // Download as file
                              aPostDataStream,           // Post data stream
                              aHeadersDataStream,        // Headers stream
                              loadType,                  // Load type
                              nullptr,                   // No SHEntry
                              true,                      // first party site
                              VoidString(),              // No srcdoc
                              this,                      // We are the source
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -529,17 +529,17 @@ private: // member functions
                      bool aLoadFromExternal,
                      bool aForceAllowDataURI,
                      bool aOriginalFrameSrc,
                      nsIURI* aReferrer,
                      bool aSendReferrer,
                      uint32_t aReferrerPolicy,
                      nsIPrincipal* aTriggeringPrincipal,
                      nsIPrincipal* aPrincipalToInherit,
-                     const nsACString& aTypeHint,
+                     const char* aTypeHint,
                      const nsAString& aFileName,
                      nsIInputStream* aPostData,
                      nsIInputStream* aHeadersData,
                      bool aFirstParty,
                      nsIDocShell** aDocShell,
                      nsIRequest** aRequest,
                      bool aIsNewWindowTarget,
                      bool aBypassClassifier,
@@ -900,24 +900,16 @@ private: // member functions
     return (mObserveErrorPages ? sUseErrorPages : mUseErrorPages);
   }
 
   bool CSSErrorReportingEnabled() const
   {
     return mCSSErrorReportingEnabled;
   }
 
-  // Handles retrieval of subframe session history for nsDocShell::LoadURI. If a
-  // load is requested in a subframe of the current DocShell, the subframe
-  // loadType may need to reflect the loadType of the parent document, or in
-  // some cases (like reloads), the history load may need to be cancelled. See
-  // function comments for in-depth logic descriptions.
-  void
-  MaybeHandleSubframeHistory(nsDocShellLoadState* aLoadState);
-
 private: // data members
   static nsIURIFixup* sURIFixup;
 
   // Cached value of the "browser.xul.error_pages.enabled" preference.
   static bool sUseErrorPages;
 
 #ifdef DEBUG
   // We're counting the number of |nsDocShells| to help find leaks
new file mode 100644
--- /dev/null
+++ b/docshell/base/nsDocShellLoadInfo.cpp
@@ -0,0 +1,318 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDocShellLoadInfo.h"
+#include "nsISHEntry.h"
+#include "nsIInputStream.h"
+#include "nsIURI.h"
+#include "nsIDocShell.h"
+#include "mozilla/net/ReferrerPolicy.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Maybe.h"
+
+namespace mozilla {
+
+
+} // mozilla
+
+nsDocShellLoadInfo::nsDocShellLoadInfo()
+  : mResultPrincipalURIIsSome(false)
+  , mKeepResultPrincipalURIIfSet(false)
+  , mLoadReplace(false)
+  , mInheritPrincipal(false)
+  , mPrincipalIsExplicit(false)
+  , mForceAllowDataURI(false)
+  , mOriginalFrameSrc(false)
+  , mSendReferrer(true)
+  , mReferrerPolicy(mozilla::net::RP_Unset)
+  , mLoadType(LOAD_NORMAL)
+  , mIsSrcdocLoad(false)
+{
+}
+
+nsDocShellLoadInfo::~nsDocShellLoadInfo()
+{
+}
+
+nsIURI*
+nsDocShellLoadInfo::Referrer() const
+{
+  return mReferrer;
+}
+
+void
+nsDocShellLoadInfo::SetReferrer(nsIURI* aReferrer)
+{
+  mReferrer = aReferrer;
+}
+
+nsIURI*
+nsDocShellLoadInfo::OriginalURI() const
+{
+  return mOriginalURI;
+}
+
+void
+nsDocShellLoadInfo::SetOriginalURI(nsIURI* aOriginalURI)
+{
+  mOriginalURI = aOriginalURI;
+}
+
+nsIURI*
+nsDocShellLoadInfo::ResultPrincipalURI() const
+{
+  return mResultPrincipalURI;
+}
+
+void
+nsDocShellLoadInfo::SetResultPrincipalURI(nsIURI* aResultPrincipalURI)
+{
+  mResultPrincipalURI = aResultPrincipalURI;
+}
+
+bool
+nsDocShellLoadInfo::ResultPrincipalURIIsSome() const
+{
+  return mResultPrincipalURIIsSome;
+}
+
+void
+nsDocShellLoadInfo::SetResultPrincipalURIIsSome(bool aIsSome)
+{
+  mResultPrincipalURIIsSome = aIsSome;
+}
+
+bool
+nsDocShellLoadInfo::KeepResultPrincipalURIIfSet() const
+{
+  return mKeepResultPrincipalURIIfSet;
+}
+
+void
+nsDocShellLoadInfo::SetKeepResultPrincipalURIIfSet(bool aKeep)
+{
+  mKeepResultPrincipalURIIfSet = aKeep;
+}
+
+bool
+nsDocShellLoadInfo::LoadReplace() const
+{
+  return mLoadReplace;
+}
+
+void
+nsDocShellLoadInfo::SetLoadReplace(bool aLoadReplace)
+{
+  mLoadReplace = aLoadReplace;
+}
+
+nsIPrincipal*
+nsDocShellLoadInfo::TriggeringPrincipal() const
+{
+  return mTriggeringPrincipal;
+}
+
+void
+nsDocShellLoadInfo::SetTriggeringPrincipal(nsIPrincipal* aTriggeringPrincipal)
+{
+  mTriggeringPrincipal = aTriggeringPrincipal;
+}
+
+bool
+nsDocShellLoadInfo::InheritPrincipal() const
+{
+  return mInheritPrincipal;
+}
+
+void
+nsDocShellLoadInfo::SetInheritPrincipal(bool aInheritPrincipal)
+{
+  mInheritPrincipal = aInheritPrincipal;
+}
+
+bool
+nsDocShellLoadInfo::PrincipalIsExplicit() const
+{
+  return mPrincipalIsExplicit;
+}
+
+void
+nsDocShellLoadInfo::SetPrincipalIsExplicit(bool aPrincipalIsExplicit)
+{
+  mPrincipalIsExplicit = aPrincipalIsExplicit;
+}
+
+bool
+nsDocShellLoadInfo::ForceAllowDataURI() const
+{
+  return mForceAllowDataURI;
+}
+
+void
+nsDocShellLoadInfo::SetForceAllowDataURI(bool aForceAllowDataURI)
+{
+  mForceAllowDataURI = aForceAllowDataURI;
+}
+
+bool
+nsDocShellLoadInfo::OriginalFrameSrc() const
+{
+  return mOriginalFrameSrc;
+}
+
+void
+nsDocShellLoadInfo::SetOriginalFrameSrc(bool aOriginalFrameSrc)
+{
+  mOriginalFrameSrc = aOriginalFrameSrc;
+}
+
+uint32_t
+nsDocShellLoadInfo::LoadType() const
+{
+  return mLoadType;
+}
+
+void
+nsDocShellLoadInfo::SetLoadType(uint32_t aLoadType)
+{
+  mLoadType = aLoadType;
+}
+
+nsISHEntry*
+nsDocShellLoadInfo::SHEntry() const
+{
+  return mSHEntry;
+}
+
+void
+nsDocShellLoadInfo::SetSHEntry(nsISHEntry* aSHEntry)
+{
+  mSHEntry = aSHEntry;
+}
+
+void
+nsDocShellLoadInfo::GetTarget(nsAString& aTarget) const
+{
+  aTarget = mTarget;
+}
+
+void
+nsDocShellLoadInfo::SetTarget(const nsAString& aTarget)
+{
+  mTarget = aTarget;
+}
+
+nsIInputStream*
+nsDocShellLoadInfo::PostDataStream() const
+{
+  return mPostDataStream;
+}
+
+void
+nsDocShellLoadInfo::SetPostDataStream(nsIInputStream* aStream)
+{
+  mPostDataStream = aStream;
+}
+
+nsIInputStream*
+nsDocShellLoadInfo::HeadersStream() const
+{
+  return mHeadersStream;
+}
+
+void
+nsDocShellLoadInfo::SetHeadersStream(nsIInputStream* aHeadersStream)
+{
+  mHeadersStream = aHeadersStream;
+}
+
+bool
+nsDocShellLoadInfo::SendReferrer() const
+{
+  return mSendReferrer;
+}
+
+void
+nsDocShellLoadInfo::SetSendReferrer(bool aSendReferrer)
+{
+  mSendReferrer = aSendReferrer;
+}
+
+uint32_t
+nsDocShellLoadInfo::ReferrerPolicy() const
+{
+  return mReferrerPolicy;
+}
+
+void
+nsDocShellLoadInfo::SetReferrerPolicy(mozilla::net::ReferrerPolicy aReferrerPolicy)
+{
+  mReferrerPolicy = aReferrerPolicy;
+}
+
+bool
+nsDocShellLoadInfo::IsSrcdocLoad() const
+{
+  return mIsSrcdocLoad;
+}
+
+void
+nsDocShellLoadInfo::GetSrcdocData(nsAString& aSrcdocData) const
+{
+  aSrcdocData = mSrcdocData;
+}
+
+void
+nsDocShellLoadInfo::SetSrcdocData(const nsAString& aSrcdocData)
+{
+  mSrcdocData = aSrcdocData;
+  mIsSrcdocLoad = true;
+}
+
+nsIDocShell*
+nsDocShellLoadInfo::SourceDocShell() const
+{
+  return mSourceDocShell;
+}
+
+void
+nsDocShellLoadInfo::SetSourceDocShell(nsIDocShell* aSourceDocShell)
+{
+  mSourceDocShell = aSourceDocShell;
+}
+
+nsIURI*
+nsDocShellLoadInfo::BaseURI() const
+{
+  return mBaseURI;
+}
+
+void
+nsDocShellLoadInfo::SetBaseURI(nsIURI* aBaseURI)
+{
+  mBaseURI = aBaseURI;
+}
+
+void
+nsDocShellLoadInfo::GetMaybeResultPrincipalURI(mozilla::Maybe<nsCOMPtr<nsIURI>>& aRPURI) const
+{
+  bool isSome = ResultPrincipalURIIsSome();
+  aRPURI.reset();
+
+  if (!isSome) {
+    return;
+  }
+
+  nsCOMPtr<nsIURI> uri = ResultPrincipalURI();
+  aRPURI.emplace(std::move(uri));
+}
+
+void
+nsDocShellLoadInfo::SetMaybeResultPrincipalURI(mozilla::Maybe<nsCOMPtr<nsIURI>> const& aRPURI)
+{
+  SetResultPrincipalURI(aRPURI.refOr(nullptr));
+  SetResultPrincipalURIIsSome(aRPURI.isSome());
+}
new file mode 100644
--- /dev/null
+++ b/docshell/base/nsDocShellLoadInfo.h
@@ -0,0 +1,214 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDocShellLoadInfo_h__
+#define nsDocShellLoadInfo_h__
+
+// Helper Classes
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsDocShellLoadTypes.h"
+#include "mozilla/net/ReferrerPolicy.h"
+
+class nsIInputStream;
+class nsISHEntry;
+class nsIURI;
+class nsIDocShell;
+
+/**
+ * nsDocShellLoadInfo contains setup information used in a nsIDocShell::loadURI
+ * call.
+ */
+class nsDocShellLoadInfo
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(nsDocShellLoadInfo);
+
+  nsDocShellLoadInfo();
+
+  nsIURI* Referrer() const;
+
+  void SetReferrer(nsIURI* aReferrer);
+
+  nsIURI* OriginalURI() const;
+
+  void SetOriginalURI(nsIURI* aOriginalURI);
+
+  nsIURI* ResultPrincipalURI() const;
+
+  void SetResultPrincipalURI(nsIURI* aResultPrincipalURI);
+
+  bool ResultPrincipalURIIsSome() const;
+
+  void SetResultPrincipalURIIsSome(bool aIsSome);
+
+  bool KeepResultPrincipalURIIfSet() const;
+
+  void SetKeepResultPrincipalURIIfSet(bool aKeep);
+
+  bool LoadReplace() const;
+
+  void SetLoadReplace(bool aLoadReplace);
+
+  nsIPrincipal* TriggeringPrincipal() const;
+
+  void SetTriggeringPrincipal(nsIPrincipal* aTriggeringPrincipal);
+
+  bool InheritPrincipal() const;
+
+  void SetInheritPrincipal(bool aInheritPrincipal);
+
+  bool PrincipalIsExplicit() const;
+
+  void SetPrincipalIsExplicit(bool aPrincipalIsExplicit);
+
+  bool ForceAllowDataURI() const;
+
+  void SetForceAllowDataURI(bool aForceAllowDataURI);
+
+  bool OriginalFrameSrc() const;
+
+  void SetOriginalFrameSrc(bool aOriginalFrameSrc);
+
+  uint32_t LoadType() const;
+
+  void SetLoadType(uint32_t aLoadType);
+
+  nsISHEntry* SHEntry() const;
+
+  void SetSHEntry(nsISHEntry* aSHEntry);
+
+  void GetTarget(nsAString& aTarget) const;
+
+  void SetTarget(const nsAString& aTarget);
+
+  nsIInputStream* PostDataStream() const;
+
+  void SetPostDataStream(nsIInputStream* aStream);
+
+  nsIInputStream* HeadersStream() const;
+
+  void SetHeadersStream(nsIInputStream* aHeadersStream);
+
+  bool SendReferrer() const;
+
+  void SetSendReferrer(bool aSendReferrer);
+
+  uint32_t ReferrerPolicy() const;
+
+  void SetReferrerPolicy(mozilla::net::ReferrerPolicy aReferrerPolicy);
+
+  bool IsSrcdocLoad() const;
+
+  void GetSrcdocData(nsAString& aSrcdocData) const;
+
+  void SetSrcdocData(const nsAString& aSrcdocData);
+
+  nsIDocShell* SourceDocShell() const;
+
+  void SetSourceDocShell(nsIDocShell* aSourceDocShell);
+
+  nsIURI* BaseURI() const;
+
+  void SetBaseURI(nsIURI* aBaseURI);
+
+  // Helper function allowing convenient work with mozilla::Maybe in C++, hiding
+  // resultPrincipalURI and resultPrincipalURIIsSome attributes from the consumer.
+  void
+  GetMaybeResultPrincipalURI(mozilla::Maybe<nsCOMPtr<nsIURI>>& aRPURI) const;
+
+  void
+  SetMaybeResultPrincipalURI(mozilla::Maybe<nsCOMPtr<nsIURI>> const& aRPURI);
+
+protected:
+  virtual ~nsDocShellLoadInfo();
+
+protected:
+  // This is the referrer for the load.
+  nsCOMPtr<nsIURI> mReferrer;
+
+  // The originalURI to be passed to nsIDocShell.internalLoad. May be null.
+  nsCOMPtr<nsIURI> mOriginalURI;
+
+  // Result principal URL from nsILoadInfo, may be null. Valid only if
+  // mResultPrincipalURIIsSome is true (has the same meaning as isSome() on
+  // mozilla::Maybe.)
+  nsCOMPtr<nsIURI> mResultPrincipalURI;
+  bool mResultPrincipalURIIsSome;
+
+  // The principal of the load, that is, the entity responsible for causing the
+  // load to occur. In most cases the referrer and the triggeringPrincipal's URI
+  // will be identical.
+  nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
+
+  // if http-equiv="refresh" cause reload we do not want to replace
+  // ResultPrinicpalURI if it was already set.
+  bool mKeepResultPrincipalURIIfSet;
+
+  // loadReplace flag to be passed to nsIDocShell.internalLoad.
+  bool mLoadReplace;
+
+  // If this attribute is true and no triggeringPrincipal is specified,
+  // copy the principal from the referring document.
+  bool mInheritPrincipal;
+
+  // If this attribute is true only ever use the principal specified
+  // by the triggeringPrincipal and inheritPrincipal attributes.
+  // If there are security reasons for why this is unsafe, such
+  // as trying to use a systemprincipal as the triggeringPrincipal
+  // for a content docshell the load fails.
+  bool mPrincipalIsExplicit;
+
+  // If this attribute is true, then a top-level navigation
+  // to a data URI will be allowed.
+  bool mForceAllowDataURI;
+
+  // If this attribute is true, this load corresponds to a frame
+  // element loading its original src (or srcdoc) attribute.
+  bool mOriginalFrameSrc;
+
+  // True if the referrer should be sent, false if it shouldn't be sent, even if
+  // it's available. This attribute defaults to true.
+  bool mSendReferrer;
+
+  // Referrer policy for the load. This attribute holds one of the values
+  // (REFERRER_POLICY_*) defined in nsIHttpChannel.
+  mozilla::net::ReferrerPolicy mReferrerPolicy;
+
+  // Contains a load type as specified by the nsDocShellLoadTypes::load*
+  // constants
+  uint32_t mLoadType;
+
+  // SHEntry for this page
+  nsCOMPtr<nsISHEntry> mSHEntry;
+
+  // Target for load, like _content, _blank etc.
+  nsString mTarget;
+
+  // Post data
+  nsCOMPtr<nsIInputStream> mPostDataStream;
+
+  // Additional Headers
+  nsCOMPtr<nsIInputStream> mHeadersStream;
+
+  // True if the docshell has been created to load an iframe where the srcdoc
+  // attribute has been set. Set when srcdocData is specified.
+  bool mIsSrcdocLoad;
+
+  // When set, the load will be interpreted as a srcdoc load, where contents of
+  // this string will be loaded instead of the URI. Setting srcdocData sets
+  // isSrcdocLoad to true
+  nsString mSrcdocData;
+
+  // When set, this is the Source Browsing Context for the navigation.
+  nsCOMPtr<nsIDocShell> mSourceDocShell;
+
+  // Used for srcdoc loads to give view-source knowledge of the load's base URI
+  // as this information isn't embedded in the load's URI.
+  nsCOMPtr<nsIURI> mBaseURI;
+};
+
+#endif /* nsDocShellLoadInfo_h__ */
deleted file mode 100644
--- a/docshell/base/nsDocShellLoadState.cpp
+++ /dev/null
@@ -1,554 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsDocShellLoadState.h"
-#include "nsIDocShell.h"
-#include "nsIDocShellTreeItem.h"
-#include "nsIScriptSecurityManager.h"
-#include "nsIWebNavigation.h"
-
-#include "mozilla/OriginAttributes.h"
-#include "mozilla/NullPrincipal.h"
-
-nsDocShellLoadState::nsDocShellLoadState()
-  : mResultPrincipalURIIsSome(false)
-  , mKeepResultPrincipalURIIfSet(false)
-  , mLoadReplace(false)
-  , mInheritPrincipal(false)
-  , mPrincipalIsExplicit(false)
-  , mForceAllowDataURI(false)
-  , mOriginalFrameSrc(false)
-  , mSendReferrer(true)
-  , mReferrerPolicy(mozilla::net::RP_Unset)
-  , mLoadType(LOAD_NORMAL)
-  , mIsSrcdocLoad(false)
-  , mLoadFlags(0)
-  , mFirstParty(false)
-  , mTypeHint(VoidCString())
-  , mFileName(VoidString())
-  , mDocShellInternalLoadFlags(0)
-{
-}
-
-nsDocShellLoadState::~nsDocShellLoadState()
-{
-}
-
-nsIURI*
-nsDocShellLoadState::Referrer() const
-{
-  return mReferrer;
-}
-
-void
-nsDocShellLoadState::SetReferrer(nsIURI* aReferrer)
-{
-  mReferrer = aReferrer;
-}
-
-nsIURI*
-nsDocShellLoadState::URI() const
-{
-  return mURI;
-}
-
-void
-nsDocShellLoadState::SetURI(nsIURI* aURI)
-{
-  mURI = aURI;
-}
-
-nsIURI*
-nsDocShellLoadState::OriginalURI() const
-{
-  return mOriginalURI;
-}
-
-void
-nsDocShellLoadState::SetOriginalURI(nsIURI* aOriginalURI)
-{
-  mOriginalURI = aOriginalURI;
-}
-
-nsIURI*
-nsDocShellLoadState::ResultPrincipalURI() const
-{
-  return mResultPrincipalURI;
-}
-
-void
-nsDocShellLoadState::SetResultPrincipalURI(nsIURI* aResultPrincipalURI)
-{
-  mResultPrincipalURI = aResultPrincipalURI;
-}
-
-bool
-nsDocShellLoadState::ResultPrincipalURIIsSome() const
-{
-  return mResultPrincipalURIIsSome;
-}
-
-void
-nsDocShellLoadState::SetResultPrincipalURIIsSome(bool aIsSome)
-{
-  mResultPrincipalURIIsSome = aIsSome;
-}
-
-bool
-nsDocShellLoadState::KeepResultPrincipalURIIfSet() const
-{
-  return mKeepResultPrincipalURIIfSet;
-}
-
-void
-nsDocShellLoadState::SetKeepResultPrincipalURIIfSet(bool aKeep)
-{
-  mKeepResultPrincipalURIIfSet = aKeep;
-}
-
-bool
-nsDocShellLoadState::LoadReplace() const
-{
-  return mLoadReplace;
-}
-
-void
-nsDocShellLoadState::SetLoadReplace(bool aLoadReplace)
-{
-  mLoadReplace = aLoadReplace;
-}
-
-nsIPrincipal*
-nsDocShellLoadState::TriggeringPrincipal() const
-{
-  return mTriggeringPrincipal;
-}
-
-void
-nsDocShellLoadState::SetTriggeringPrincipal(nsIPrincipal* aTriggeringPrincipal)
-{
-  mTriggeringPrincipal = aTriggeringPrincipal;
-}
-
-nsIPrincipal*
-nsDocShellLoadState::PrincipalToInherit() const
-{
-  return mPrincipalToInherit;
-}
-
-void
-nsDocShellLoadState::SetPrincipalToInherit(nsIPrincipal* aPrincipalToInherit)
-{
-  mPrincipalToInherit = aPrincipalToInherit;
-}
-
-bool
-nsDocShellLoadState::InheritPrincipal() const
-{
-  return mInheritPrincipal;
-}
-
-void
-nsDocShellLoadState::SetInheritPrincipal(bool aInheritPrincipal)
-{
-  mInheritPrincipal = aInheritPrincipal;
-}
-
-bool
-nsDocShellLoadState::PrincipalIsExplicit() const
-{
-  return mPrincipalIsExplicit;
-}
-
-void
-nsDocShellLoadState::SetPrincipalIsExplicit(bool aPrincipalIsExplicit)
-{
-  mPrincipalIsExplicit = aPrincipalIsExplicit;
-}
-
-bool
-nsDocShellLoadState::ForceAllowDataURI() const
-{
-  return mForceAllowDataURI;
-}
-
-void
-nsDocShellLoadState::SetForceAllowDataURI(bool aForceAllowDataURI)
-{
-  mForceAllowDataURI = aForceAllowDataURI;
-}
-
-bool
-nsDocShellLoadState::OriginalFrameSrc() const
-{
-  return mOriginalFrameSrc;
-}
-
-void
-nsDocShellLoadState::SetOriginalFrameSrc(bool aOriginalFrameSrc)
-{
-  mOriginalFrameSrc = aOriginalFrameSrc;
-}
-
-uint32_t
-nsDocShellLoadState::LoadType() const
-{
-  return mLoadType;
-}
-
-void
-nsDocShellLoadState::SetLoadType(uint32_t aLoadType)
-{
-  mLoadType = aLoadType;
-}
-
-nsISHEntry*
-nsDocShellLoadState::SHEntry() const
-{
-  return mSHEntry;
-}
-
-void
-nsDocShellLoadState::SetSHEntry(nsISHEntry* aSHEntry)
-{
-  mSHEntry = aSHEntry;
-}
-
-const nsString&
-nsDocShellLoadState::Target() const
-{
-  return mTarget;
-}
-
-void
-nsDocShellLoadState::SetTarget(const nsAString& aTarget)
-{
-  mTarget = aTarget;
-}
-
-nsIInputStream*
-nsDocShellLoadState::PostDataStream() const
-{
-  return mPostDataStream;
-}
-
-void
-nsDocShellLoadState::SetPostDataStream(nsIInputStream* aStream)
-{
-  mPostDataStream = aStream;
-}
-
-nsIInputStream*
-nsDocShellLoadState::HeadersStream() const
-{
-  return mHeadersStream;
-}
-
-void
-nsDocShellLoadState::SetHeadersStream(nsIInputStream* aHeadersStream)
-{
-  mHeadersStream = aHeadersStream;
-}
-
-bool
-nsDocShellLoadState::SendReferrer() const
-{
-  return mSendReferrer;
-}
-
-void
-nsDocShellLoadState::SetSendReferrer(bool aSendReferrer)
-{
-  mSendReferrer = aSendReferrer;
-}
-
-uint32_t
-nsDocShellLoadState::ReferrerPolicy() const
-{
-  return mReferrerPolicy;
-}
-
-void
-nsDocShellLoadState::SetReferrerPolicy(mozilla::net::ReferrerPolicy aReferrerPolicy)
-{
-  mReferrerPolicy = aReferrerPolicy;
-}
-
-bool
-nsDocShellLoadState::IsSrcdocLoad() const
-{
-  return mIsSrcdocLoad;
-}
-
-const nsString&
-nsDocShellLoadState::SrcdocData() const
-{
-  return mSrcdocData;
-}
-
-void
-nsDocShellLoadState::SetSrcdocData(const nsAString& aSrcdocData)
-{
-  mSrcdocData = aSrcdocData;
-  mIsSrcdocLoad = true;
-}
-
-nsIDocShell*
-nsDocShellLoadState::SourceDocShell() const
-{
-  return mSourceDocShell;
-}
-
-void
-nsDocShellLoadState::SetSourceDocShell(nsIDocShell* aSourceDocShell)
-{
-  mSourceDocShell = aSourceDocShell;
-}
-
-nsIURI*
-nsDocShellLoadState::BaseURI() const
-{
-  return mBaseURI;
-}
-
-void
-nsDocShellLoadState::SetBaseURI(nsIURI* aBaseURI)
-{
-  mBaseURI = aBaseURI;
-}
-
-void
-nsDocShellLoadState::GetMaybeResultPrincipalURI(mozilla::Maybe<nsCOMPtr<nsIURI>>& aRPURI) const
-{
-  bool isSome = ResultPrincipalURIIsSome();
-  aRPURI.reset();
-
-  if (!isSome) {
-    return;
-  }
-
-  nsCOMPtr<nsIURI> uri = ResultPrincipalURI();
-  aRPURI.emplace(std::move(uri));
-}
-
-void
-nsDocShellLoadState::SetMaybeResultPrincipalURI(mozilla::Maybe<nsCOMPtr<nsIURI>> const& aRPURI)
-{
-  SetResultPrincipalURI(aRPURI.refOr(nullptr));
-  SetResultPrincipalURIIsSome(aRPURI.isSome());
-}
-
-uint32_t
-nsDocShellLoadState::LoadFlags() const
-{
-  return mLoadFlags;
-}
-
-void
-nsDocShellLoadState::SetLoadFlags(uint32_t aLoadFlags)
-{
-  mLoadFlags = aLoadFlags;
-}
-
-bool
-nsDocShellLoadState::FirstParty() const
-{
-  return mFirstParty;
-}
-
-void
-nsDocShellLoadState::SetFirstParty(bool aFirstParty)
-{
-  mFirstParty = aFirstParty;
-}
-
-const nsCString&
-nsDocShellLoadState::TypeHint() const
-{
-  return mTypeHint;
-}
-
-void
-nsDocShellLoadState::SetTypeHint(const nsCString& aTypeHint)
-{
-  mTypeHint = aTypeHint;
-}
-
-const nsString&
-nsDocShellLoadState::FileName() const
-{
-  return mFileName;
-}
-
-void
-nsDocShellLoadState::SetFileName(const nsAString& aFileName)
-{
-  mFileName = aFileName;
-}
-
-uint32_t
-nsDocShellLoadState::DocShellInternalLoadFlags() const
-{
-  return mDocShellInternalLoadFlags;
-}
-
-void
-nsDocShellLoadState::SetDocShellInternalLoadFlags(uint32_t aFlags)
-{
-  mDocShellInternalLoadFlags = aFlags;
-}
-
-
-nsresult
-nsDocShellLoadState::SetupInheritingPrincipal(uint32_t aItemType,
-                                              const mozilla::OriginAttributes& aOriginAttributes)
-{
-  // We need a principalToInherit.
-  //
-  // If principalIsExplicit is not set there are 4 possibilities:
-  // (1) If the system principal or an expanded principal was passed
-  //     in and we're a typeContent docshell, inherit the principal
-  //     from the current document instead.
-  // (2) In all other cases when the principal passed in is not null,
-  //     use that principal.
-  // (3) If the caller has allowed inheriting from the current document,
-  //     or if we're being called from system code (eg chrome JS or pure
-  //     C++) then inheritPrincipal should be true and InternalLoad will get
-  //     a principal from the current document. If none of these things are
-  //     true, then
-  // (4) we don't pass a principal into the channel, and a principal will be
-  //     created later from the channel's internal data.
-  //
-  // If principalIsExplicit *is* set, there are 4 possibilities
-  // (1) If the system principal or an expanded principal was passed in
-  //     and we're a typeContent docshell, return an error.
-  // (2) In all other cases when the principal passed in is not null,
-  //     use that principal.
-  // (3) If the caller has allowed inheriting from the current document,
-  //     then inheritPrincipal should be true and InternalLoad will get
-  //     a principal from the current document. If none of these things are
-  //     true, then
-  // (4) we dont' pass a principal into the channel, and a principal will be
-  //     created later from the channel's internal data.
-  mPrincipalToInherit = mTriggeringPrincipal;
-  if (mPrincipalToInherit && aItemType != nsIDocShellTreeItem::typeChrome) {
-    if (nsContentUtils::IsSystemPrincipal(mPrincipalToInherit)) {
-      if (mPrincipalIsExplicit) {
-        return NS_ERROR_DOM_SECURITY_ERR;
-      }
-      mPrincipalToInherit = nullptr;
-      mInheritPrincipal = true;
-    } else if (nsContentUtils::IsExpandedPrincipal(mPrincipalToInherit)) {
-      if (mPrincipalIsExplicit) {
-        return NS_ERROR_DOM_SECURITY_ERR;
-      }
-      // Don't inherit from the current page.  Just do the safe thing
-      // and pretend that we were loaded by a nullprincipal.
-      //
-      // We didn't inherit OriginAttributes here as ExpandedPrincipal doesn't
-      // have origin attributes.
-      mPrincipalToInherit =
-        NullPrincipal::CreateWithInheritedAttributes(aOriginAttributes,
-                                                     false);
-      mInheritPrincipal = false;
-    }
-  }
-
-  if (!mPrincipalToInherit && !mInheritPrincipal && !mPrincipalIsExplicit) {
-    // See if there's system or chrome JS code running
-    mInheritPrincipal = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
-  }
-
-  if (mLoadFlags & nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL) {
-    mInheritPrincipal = false;
-    // If mFirstParty is true and the pref 'privacy.firstparty.isolate' is
-    // enabled, we will set firstPartyDomain on the origin attributes.
-    mPrincipalToInherit =
-      NullPrincipal::CreateWithInheritedAttributes(aOriginAttributes,
-                                                   mFirstParty);
-  }
-
-  return NS_OK;
-}
-
-nsresult
-nsDocShellLoadState::SetupTriggeringPrincipal(const mozilla::OriginAttributes& aOriginAttributes)
-{
-  // If the triggeringPrincipal is not set, we first try to create a principal
-  // from the referrer, since the referrer URI reflects the web origin that
-  // triggered the load. If there is no referrer URI, we fall back to using the
-  // SystemPrincipal. It's safe to assume that no provided triggeringPrincipal
-  // and no referrer simulate a load that was triggered by the system. It's
-  // important to note that this block of code needs to appear *after* the block
-  // where we munge the principalToInherit, because otherwise we would never
-  // enter code blocks checking if the principalToInherit is null and we will
-  // end up with a wrong inheritPrincipal flag.
-  if (!mTriggeringPrincipal) {
-    if (mReferrer) {
-      mTriggeringPrincipal =
-        BasePrincipal::CreateCodebasePrincipal(mReferrer, aOriginAttributes);
-
-      if (!mTriggeringPrincipal) {
-        return NS_ERROR_FAILURE;
-      }
-    } else {
-      mTriggeringPrincipal = nsContentUtils::GetSystemPrincipal();
-    }
-  }
-  return NS_OK;
-}
-
-void
-nsDocShellLoadState::CalculateDocShellInternalLoadFlags()
-{
-  MOZ_ASSERT(mDocShellInternalLoadFlags == 0,
-             "Shouldn't have any load flags set at this point.");
-
-  if (mInheritPrincipal) {
-    MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(mPrincipalToInherit),
-               "Should not inherit SystemPrincipal");
-    mDocShellInternalLoadFlags |=
-      nsIDocShell::INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
-  }
-
-  if (!mSendReferrer) {
-    mDocShellInternalLoadFlags |=
-      nsIDocShell::INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
-  }
-
-  if (mLoadFlags & nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
-    mDocShellInternalLoadFlags |=
-      nsIDocShell::INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
-  }
-
-  if (mLoadFlags & nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD) {
-    mDocShellInternalLoadFlags |= nsIDocShell::INTERNAL_LOAD_FLAGS_FIRST_LOAD;
-  }
-
-  if (mLoadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_CLASSIFIER) {
-    mDocShellInternalLoadFlags |=
-      nsIDocShell::INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
-  }
-
-  if (mLoadFlags & nsIWebNavigation::LOAD_FLAGS_FORCE_ALLOW_COOKIES) {
-    mDocShellInternalLoadFlags |=
-      nsIDocShell::INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
-  }
-
-  if (mIsSrcdocLoad) {
-    mDocShellInternalLoadFlags |= nsIDocShell::INTERNAL_LOAD_FLAGS_IS_SRCDOC;
-  }
-
-  if (mForceAllowDataURI) {
-    mDocShellInternalLoadFlags |=
-      nsIDocShell::INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
-  }
-
-  if (mOriginalFrameSrc) {
-    mDocShellInternalLoadFlags |=
-      nsIDocShell::INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC;
-  }
-}
deleted file mode 100644
--- a/docshell/base/nsDocShellLoadState.h
+++ /dev/null
@@ -1,293 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsDocShellLoadState_h__
-#define nsDocShellLoadState_h__
-
-// Helper Classes
-#include "nsCOMPtr.h"
-#include "nsString.h"
-#include "nsDocShellLoadTypes.h"
-#include "mozilla/net/ReferrerPolicy.h"
-
-class nsIInputStream;
-class nsISHEntry;
-class nsIURI;
-class nsIDocShell;
-class OriginAttibutes;
-
-/**
- * nsDocShellLoadState contains setup information used in a nsIDocShell::loadURI
- * call.
- */
-class nsDocShellLoadState final
-{
-public:
-  NS_INLINE_DECL_REFCOUNTING(nsDocShellLoadState);
-
-  nsDocShellLoadState();
-
-  // Getters and Setters
-
-  nsIURI* Referrer() const;
-
-  void SetReferrer(nsIURI* aReferrer);
-
-  nsIURI* URI() const;
-
-  void SetURI(nsIURI* aURI);
-
-  nsIURI* OriginalURI() const;
-
-  void SetOriginalURI(nsIURI* aOriginalURI);
-
-  nsIURI* ResultPrincipalURI() const;
-
-  void SetResultPrincipalURI(nsIURI* aResultPrincipalURI);
-
-  bool ResultPrincipalURIIsSome() const;
-
-  void SetResultPrincipalURIIsSome(bool aIsSome);
-
-  bool KeepResultPrincipalURIIfSet() const;
-
-  void SetKeepResultPrincipalURIIfSet(bool aKeep);
-
-  nsIPrincipal* PrincipalToInherit() const;
-
-  void SetPrincipalToInherit(nsIPrincipal* aPrincipalToInherit);
-
-  bool LoadReplace() const;
-
-  void SetLoadReplace(bool aLoadReplace);
-
-  nsIPrincipal* TriggeringPrincipal() const;
-
-  void SetTriggeringPrincipal(nsIPrincipal* aTriggeringPrincipal);
-
-  bool InheritPrincipal() const;
-
-  void SetInheritPrincipal(bool aInheritPrincipal);
-
-  bool PrincipalIsExplicit() const;
-
-  void SetPrincipalIsExplicit(bool aPrincipalIsExplicit);
-
-  bool ForceAllowDataURI() const;
-
-  void SetForceAllowDataURI(bool aForceAllowDataURI);
-
-  bool OriginalFrameSrc() const;
-
-  void SetOriginalFrameSrc(bool aOriginalFrameSrc);
-
-  uint32_t LoadType() const;
-
-  void SetLoadType(uint32_t aLoadType);
-
-  nsISHEntry* SHEntry() const;
-
-  void SetSHEntry(nsISHEntry* aSHEntry);
-
-  const nsString& Target() const;
-
-  void SetTarget(const nsAString& aTarget);
-
-  nsIInputStream* PostDataStream() const;
-
-  void SetPostDataStream(nsIInputStream* aStream);
-
-  nsIInputStream* HeadersStream() const;
-
-  void SetHeadersStream(nsIInputStream* aHeadersStream);
-
-  bool SendReferrer() const;
-
-  void SetSendReferrer(bool aSendReferrer);
-
-  uint32_t ReferrerPolicy() const;
-
-  void SetReferrerPolicy(mozilla::net::ReferrerPolicy aReferrerPolicy);
-
-  bool IsSrcdocLoad() const;
-
-  const nsString& SrcdocData() const;
-
-  void SetSrcdocData(const nsAString& aSrcdocData);
-
-  nsIDocShell* SourceDocShell() const;
-
-  void SetSourceDocShell(nsIDocShell* aSourceDocShell);
-
-  nsIURI* BaseURI() const;
-
-  void SetBaseURI(nsIURI* aBaseURI);
-
-  // Helper function allowing convenient work with mozilla::Maybe in C++, hiding
-  // resultPrincipalURI and resultPrincipalURIIsSome attributes from the consumer.
-  void
-  GetMaybeResultPrincipalURI(mozilla::Maybe<nsCOMPtr<nsIURI>>& aRPURI) const;
-
-  void
-  SetMaybeResultPrincipalURI(mozilla::Maybe<nsCOMPtr<nsIURI>> const& aRPURI);
-
-  uint32_t LoadFlags() const;
-
-  void SetLoadFlags(uint32_t aFlags);
-
-  bool FirstParty() const;
-
-  void SetFirstParty(bool aFirstParty);
-
-  const nsCString& TypeHint() const;
-
-  void SetTypeHint(const nsCString& aTypeHint);
-
-  const nsString& FileName() const;
-
-  void SetFileName(const nsAString& aFileName);
-
-  uint32_t DocShellInternalLoadFlags() const;
-
-  void SetDocShellInternalLoadFlags(uint32_t aFlags);
-
-  // Give the type of DocShell we're loading into (chrome/content/etc) and
-  // origin attributes for the URI we're loading, figure out if we should
-  // inherit our principal from the document the load was requested from, or
-  // else if the principal should be set up later in the process (after loads).
-  // See comments in function for more info on principal selection algorithm
-  nsresult SetupInheritingPrincipal(uint32_t aItemType, const mozilla::OriginAttributes& aOriginAttributes);
-
-  // If no triggering principal exists at the moment, create one using referrer
-  // information and origin attributes.
-  nsresult SetupTriggeringPrincipal(const mozilla::OriginAttributes& aOriginAttributes);
-
-  // When loading a document through nsDocShell::LoadURI(), a special set of
-  // flags needs to be set based on other values in nsDocShellLoadState. This
-  // function calculates those flags, before the LoadState is passed to
-  // nsDocShell::InternalLoad.
-  void CalculateDocShellInternalLoadFlags();
-protected:
-  // Destructor can't be defaulted or inlined, as header doesn't have all type
-  // includes it needs to do so.
-  ~nsDocShellLoadState();
-
-protected:
-  // This is the referrer for the load.
-  nsCOMPtr<nsIURI> mReferrer;
-
-  // The URI we are navigating to. Will not be null once set.
-  nsCOMPtr<nsIURI> mURI;
-
-  // The originalURI to be passed to nsIDocShell.internalLoad. May be null.
-  nsCOMPtr<nsIURI> mOriginalURI;
-
-  // Result principal URL from nsILoadInfo, may be null. Valid only if
-  // mResultPrincipalURIIsSome is true (has the same meaning as isSome() on
-  // mozilla::Maybe.)
-  nsCOMPtr<nsIURI> mResultPrincipalURI;
-  bool mResultPrincipalURIIsSome;
-
-  // The principal of the load, that is, the entity responsible for causing the
-  // load to occur. In most cases the referrer and the triggeringPrincipal's URI
-  // will be identical.
-  nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
-
-  // if http-equiv="refresh" cause reload we do not want to replace
-  // ResultPrinicpalURI if it was already set.
-  bool mKeepResultPrincipalURIIfSet;
-
-  // loadReplace flag to be passed to nsIDocShell.internalLoad.
-  bool mLoadReplace;
-
-  // If this attribute is true and no triggeringPrincipal is specified,
-  // copy the principal from the referring document.
-  bool mInheritPrincipal;
-
-  // If this attribute is true only ever use the principal specified
-  // by the triggeringPrincipal and inheritPrincipal attributes.
-  // If there are security reasons for why this is unsafe, such
-  // as trying to use a systemprincipal as the triggeringPrincipal
-  // for a content docshell the load fails.
-  bool mPrincipalIsExplicit;
-
-  // Principal we're inheriting. If null, this means the principal should be
-  // inherited from the current document. If set to NullPrincipal, the channel
-  // will fill in principal information later in the load. See internal function
-  // comments for more info.
-  nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
-
-  // If this attribute is true, then a top-level navigation
-  // to a data URI will be allowed.
-  bool mForceAllowDataURI;
-
-  // If this attribute is true, this load corresponds to a frame
-  // element loading its original src (or srcdoc) attribute.
-  bool mOriginalFrameSrc;
-
-  // True if the referrer should be sent, false if it shouldn't be sent, even if
-  // it's available. This attribute defaults to true.
-  bool mSendReferrer;
-
-  // Referrer policy for the load. This attribute holds one of the values
-  // (REFERRER_POLICY_*) defined in nsIHttpChannel.
-  mozilla::net::ReferrerPolicy mReferrerPolicy;
-
-  // Contains a load type as specified by the nsDocShellLoadTypes::load*
-  // constants
-  uint32_t mLoadType;
-
-  // SHEntry for this page
-  nsCOMPtr<nsISHEntry> mSHEntry;
-
-  // Target for load, like _content, _blank etc.
-  nsString mTarget;
-
-  // Post data
-  nsCOMPtr<nsIInputStream> mPostDataStream;
-
-  // Additional Headers
-  nsCOMPtr<nsIInputStream> mHeadersStream;
-
-  // True if the docshell has been created to load an iframe where the srcdoc
-  // attribute has been set. Set when srcdocData is specified.
-  bool mIsSrcdocLoad;
-
-  // When set, the load will be interpreted as a srcdoc load, where contents of
-  // this string will be loaded instead of the URI. Setting srcdocData sets
-  // isSrcdocLoad to true
-  nsString mSrcdocData;
-
-  // When set, this is the Source Browsing Context for the navigation.
-  nsCOMPtr<nsIDocShell> mSourceDocShell;
-
-  // Used for srcdoc loads to give view-source knowledge of the load's base URI
-  // as this information isn't embedded in the load's URI.
-  nsCOMPtr<nsIURI> mBaseURI;
-
-  // Set of Load Flags, taken from nsDocShellLoadTypes.h
-  uint32_t mLoadFlags;
-
-  // Is this a First Party Load?
-  bool mFirstParty;
-
-  // A hint as to the content-type of the resulting data. If no hint, IsVoid()
-  // should return true.
-  nsCString mTypeHint;
-
-  // Non-void when the link should be downloaded as the given filename.
-  // mFileName being non-void but empty means that no filename hint was
-  // specified, but link should still trigger a download. If not a download,
-  // mFileName.IsVoid() should return true.
-  nsString mFileName;
-
-  // LoadFlags calculated in nsDocShell::LoadURI and passed to
-  // nsDocShell::InternalLoad, taken from the INTERNAL_LOAD consts in
-  // nsIDocShell.idl
-  uint32_t mDocShellInternalLoadFlags;
-};
-
-#endif /* nsDocShellLoadState_h__ */
--- a/docshell/base/nsDocShellLoadTypes.h
+++ b/docshell/base/nsDocShellLoadTypes.h
@@ -6,16 +6,17 @@
 
 #ifndef nsDocShellLoadTypes_h_
 #define nsDocShellLoadTypes_h_
 
 #ifdef MOZILLA_INTERNAL_API
 
 #include "nsDOMNavigationTiming.h"
 #include "nsIDocShell.h"
+#include "nsDocShellLoadInfo.h"
 #include "nsIWebNavigation.h"
 
 /**
  * Load flag for error pages. This uses one of the reserved flag
  * values from nsIWebNavigation.
  */
 #define LOAD_FLAGS_ERROR_PAGE 0x0001U
 
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -12,17 +12,17 @@
 #include "js/TypeDecls.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsIURI.h"
 class nsPresContext;
 class nsIPresShell;
-class nsDocShellLoadState;
+class nsDocShellLoadInfo;
 namespace mozilla {
 class Encoding;
 class HTMLEditor;
 namespace dom {
 class BrowsingContext;
 class ClientSource;
 } // namespace dom
 }
@@ -58,50 +58,58 @@ interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
 interface nsITabParent;
 interface nsITabChild;
 interface nsICommandManager;
 interface nsICommandParams;
 interface nsILoadURIDelegate;
 native TabChildRef(already_AddRefed<nsITabChild>);
-native nsDocShellLoadStatePtr(nsDocShellLoadState*);
+native nsDocShellLoadInfoPtr(nsDocShellLoadInfo*);
 
 webidl BrowsingContext;
 webidl ContentFrameMessageManager;
 webidl EventTarget;
 
 [scriptable, builtinclass, uuid(049234fe-da10-478b-bc5d-bc6f9a1ba63d)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
-   * @param loadState   - This is the extended load info for this load.
+   * @param uri        - The URI to load.
+   * @param loadInfo   - This is the extended load info for this load.  This
+   *                     most often will be null, but if you need to do
+   *                     additional setup for this load you can get a loadInfo
+   *                     object by calling createLoadInfo.  Once you have this
+   *                     object you can set the needed properties on it and
+   *                     then pass it to loadURI.
+   * @param aLoadFlags - Flags to modify load behaviour. Flags are defined in
+   *                     nsIWebNavigation.  Note that using flags outside
+   *                     LOAD_FLAGS_MASK is only allowed if passing in a
+   *                     non-null loadInfo.  And even some of those might not
+   *                     be allowed.  Use at your own risk.
    */
-  [noscript]void loadURI(in nsDocShellLoadStatePtr loadState);
+  [noscript]void loadURI(in nsIURI uri,
+                         in nsDocShellLoadInfoPtr loadInfo,
+                         in unsigned long aLoadFlags,
+                         in boolean firstParty);
 
   const long INTERNAL_LOAD_FLAGS_NONE                    = 0x0;
   const long INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL       = 0x1;
   const long INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER      = 0x2;
   const long INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP = 0x4;
 
   // This flag marks the first load in this object
   // @see nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD
   const long INTERNAL_LOAD_FLAGS_FIRST_LOAD              = 0x8;
 
-
-  // The set of flags that should not be set before calling into
-  // nsDocShell::LoadURI and other nsDocShell loading functions.
-  const long INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS     = 0xf;
-
-
   const long INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER       = 0x10;
   const long INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES     = 0x20;
 
   // Whether the load should be treated as srcdoc load, rather than a URI one.
   const long INTERNAL_LOAD_FLAGS_IS_SRCDOC               = 0x40;
 
   // Whether this is the load of a frame's original src attribute
   const long INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC      = 0x80;
@@ -185,17 +193,17 @@ interface nsIDocShell : nsIDocShellTreeI
                               in bool aKeepResultPrincipalURIIfSet,
                               in boolean aLoadReplace,
                               in nsIURI aReferrer,
                               in unsigned long aReferrerPolicy,
                               in nsIPrincipal aTriggeringPrincipal,
                               in nsIPrincipal aPrincipalToInherit,
                               in uint32_t aFlags,
                               in AString aWindowTarget,
-                              in ACString aTypeHint,
+                              in string aTypeHint,
                               in AString aFileName,
                               in nsIInputStream aPostDataStream,
                               in nsIInputStream aHeadersStream,
                               in unsigned long aLoadFlags,
                               in nsISHEntry aSHEntry,
                               in boolean aFirstParty,
                               in AString aSrcdoc,
                               in nsIDocShell aSourceDocShell,
--- a/docshell/shistory/nsSHEntry.cpp
+++ b/docshell/shistory/nsSHEntry.cpp
@@ -4,18 +4,18 @@
  * 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 "nsSHEntry.h"
 
 #include <algorithm>
 
 #include "nsDocShellEditorData.h"
-#include "nsDocShellLoadTypes.h"
 #include "nsIContentViewer.h"
+#include "nsDocShellLoadInfo.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIInputStream.h"
 #include "nsILayoutHistoryState.h"
 #include "nsIStructuredCloneContainer.h"
 #include "nsIURI.h"
 #include "nsSHEntryShared.h"
 #include "nsSHistory.h"
 
--- a/docshell/shistory/nsSHistory.cpp
+++ b/docshell/shistory/nsSHistory.cpp
@@ -8,17 +8,17 @@
 
 #include <algorithm>
 
 #include "nsCOMArray.h"
 #include "nsComponentManagerUtils.h"
 #include "nsDocShell.h"
 #include "nsIContentViewer.h"
 #include "nsIDocShell.h"
-#include "nsDocShellLoadState.h"
+#include "nsDocShellLoadInfo.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsILayoutHistoryState.h"
 #include "nsIObserverService.h"
 #include "nsISHEntry.h"
 #include "nsISHistoryListener.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "nsTArray.h"
@@ -1570,39 +1570,37 @@ nsSHistory::LoadDifferingEntries(nsISHEn
 }
 
 nsresult
 nsSHistory::InitiateLoad(nsISHEntry* aFrameEntry, nsIDocShell* aFrameDS,
                          long aLoadType)
 {
   NS_ENSURE_STATE(aFrameDS && aFrameEntry);
 
-  RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState();
+  RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo();
 
   /* Set the loadType in the SHEntry too to  what was passed on.
    * This will be passed on to child subframes later in nsDocShell,
    * so that proper loadType is maintained through out a frameset
    */
   aFrameEntry->SetLoadType(aLoadType);
 
-  loadState->SetLoadType(aLoadType);
-  loadState->SetSHEntry(aFrameEntry);
+  loadInfo->SetLoadType(aLoadType);
+  loadInfo->SetSHEntry(aFrameEntry);
 
   nsCOMPtr<nsIURI> originalURI = aFrameEntry->GetOriginalURI();
-  loadState->SetOriginalURI(originalURI);
+  loadInfo->SetOriginalURI(originalURI);
 
-  loadState->SetLoadReplace(aFrameEntry->GetLoadReplace());
+  loadInfo->SetLoadReplace(aFrameEntry->GetLoadReplace());
 
-  nsCOMPtr<nsIURI> newURI = aFrameEntry->GetURI();
-  loadState->SetURI(newURI);
-  loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);
-  loadState->SetFirstParty(false);
+  nsCOMPtr<nsIURI> nextURI = aFrameEntry->GetURI();
+  // Time to initiate a document load
+  return aFrameDS->LoadURI(nextURI, loadInfo,
+                           nsIWebNavigation::LOAD_FLAGS_NONE, false);
 
-  // Time to initiate a document load
-  return aFrameDS->LoadURI(loadState);
 }
 
 NS_IMETHODIMP_(void)
 nsSHistory::SetRootDocShell(nsIDocShell* aDocShell)
 {
   mRootDocShell = aDocShell;
 
   // Init mHistoryTracker on setting mRootDocShell so we can bind its event
--- a/dom/base/Location.cpp
+++ b/dom/base/Location.cpp
@@ -4,17 +4,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 "Location.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIDocShell.h"
-#include "nsDocShellLoadState.h"
+#include "nsDocShellLoadInfo.h"
 #include "nsIWebNavigation.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIURIFixup.h"
 #include "nsIURL.h"
 #include "nsIURIMutator.h"
 #include "nsIJARURI.h"
 #include "nsNetUtil.h"
 #include "nsCOMPtr.h"
@@ -56,17 +56,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Location, mInnerWindow)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Location)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Location)
 
-already_AddRefed<nsDocShellLoadState>
+already_AddRefed<nsDocShellLoadInfo>
 Location::CheckURL(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aRv)
 {
   nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
   if (NS_WARN_IF(!docShell)) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
@@ -147,26 +147,26 @@ Location::CheckURL(nsIURI* aURI, nsIPrin
       }
     }
   } else {
     // No document; just use our subject principal as the triggering principal.
     triggeringPrincipal = &aSubjectPrincipal;
   }
 
   // Create load info
-  RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState();
+  RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo();
 
-  loadState->SetTriggeringPrincipal(triggeringPrincipal);
+  loadInfo->SetTriggeringPrincipal(triggeringPrincipal);
 
   if (sourceURI) {
-    loadState->SetReferrer(sourceURI);
-    loadState->SetReferrerPolicy(referrerPolicy);
+    loadInfo->SetReferrer(sourceURI);
+    loadInfo->SetReferrerPolicy(referrerPolicy);
   }
 
-  return loadState.forget();
+  return loadInfo.forget();
 }
 
 nsresult
 Location::GetURI(nsIURI** aURI, bool aGetInnermostURI)
 {
   *aURI = nullptr;
 
   nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
@@ -207,41 +207,37 @@ Location::GetURI(nsIURI** aURI, bool aGe
 }
 
 void
 Location::SetURI(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal,
                  ErrorResult& aRv, bool aReplace)
 {
   nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
   if (docShell) {
-
-    RefPtr<nsDocShellLoadState> loadState =
+    RefPtr<nsDocShellLoadInfo> loadInfo =
       CheckURL(aURI, aSubjectPrincipal, aRv);
     if (aRv.Failed()) {
       return;
     }
 
     if (aReplace) {
-      loadState->SetLoadType(LOAD_STOP_CONTENT_AND_REPLACE);
+      loadInfo->SetLoadType(LOAD_STOP_CONTENT_AND_REPLACE);
     } else {
-      loadState->SetLoadType(LOAD_STOP_CONTENT);
+      loadInfo->SetLoadType(LOAD_STOP_CONTENT);
     }
 
     // Get the incumbent script's browsing context to set as source.
     nsCOMPtr<nsPIDOMWindowInner> sourceWindow =
       do_QueryInterface(mozilla::dom::GetIncumbentGlobal());
     if (sourceWindow) {
-      loadState->SetSourceDocShell(sourceWindow->GetDocShell());
+      loadInfo->SetSourceDocShell(sourceWindow->GetDocShell());
     }
 
-    loadState->SetURI(aURI);
-    loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);
-    loadState->SetFirstParty(true);
-
-    nsresult rv = docShell->LoadURI(loadState);
+    nsresult rv = docShell->LoadURI(aURI, loadInfo,
+                                    nsIWebNavigation::LOAD_FLAGS_NONE, true);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       aRv.Throw(rv);
     }
   }
 }
 
 void
 Location::GetHash(nsAString& aHash,
--- a/dom/base/Location.h
+++ b/dom/base/Location.h
@@ -12,17 +12,16 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
 
 class nsIDocShell;
 class nsIURI;
-class nsDocShellLoadState;
 
 namespace mozilla {
 namespace dom {
 
 //*****************************************************************************
 // Location: Script "location" object
 //*****************************************************************************
 
@@ -163,17 +162,16 @@ protected:
   virtual ~Location();
 
   // In the case of jar: uris, we sometimes want the place the jar was
   // fetched from as the URI instead of the jar: uri itself.  Pass in
   // true for aGetInnermostURI when that's the case.
   // Note, this method can return NS_OK with a null value for aURL. This happens
   // if the docShell is null.
   nsresult GetURI(nsIURI** aURL, bool aGetInnermostURI = false);
-
   void SetURI(nsIURI* aURL, nsIPrincipal& aSubjectPrincipal,
               ErrorResult& aRv, bool aReplace = false);
   void SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
                        nsIPrincipal& aSubjectPrincipal,
                        bool aReplace, ErrorResult& aRv);
 
   // Helper for Assign/SetHref/Replace
   void DoSetHref(const nsAString& aHref, nsIPrincipal& aSubjectPrincipal,
@@ -181,17 +179,17 @@ protected:
 
   // Get the base URL we should be using for our relative URL
   // resolution for SetHref/Assign/Replace.
   already_AddRefed<nsIURI> GetSourceBaseURL();
 
   // Check whether it's OK to load the given url with the given subject
   // principal, and if so construct the right nsDocShellLoadInfo for the load
   // and return it.
-  already_AddRefed<nsDocShellLoadState> CheckURL(nsIURI *url,
+  already_AddRefed<nsDocShellLoadInfo> CheckURL(nsIURI *url,
                                                 nsIPrincipal& aSubjectPrincipal,
                                                 ErrorResult& aRv);
 
   bool CallerSubsumes(nsIPrincipal* aSubjectPrincipal);
 
   nsString mCachedHash;
   nsCOMPtr<nsPIDOMWindowInner> mInnerWindow;
   nsWeakPtr mDocShell;
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -28,28 +28,26 @@
 
 namespace mozilla {
 namespace dom {
 
 PostMessageEvent::PostMessageEvent(nsGlobalWindowOuter* aSource,
                                    const nsAString& aCallerOrigin,
                                    nsGlobalWindowOuter* aTargetWindow,
                                    nsIPrincipal* aProvidedPrincipal,
-                                   nsIDocument* aSourceDocument,
-                                   bool aTrustedCaller)
+                                   nsIDocument* aSourceDocument)
   : Runnable("dom::PostMessageEvent")
   , StructuredCloneHolder(CloningSupported,
                           TransferringSupported,
                           StructuredCloneScope::SameProcessSameThread)
   , mSource(aSource)
   , mCallerOrigin(aCallerOrigin)
   , mTargetWindow(aTargetWindow)
   , mProvidedPrincipal(aProvidedPrincipal)
   , mSourceDocument(aSourceDocument)
-  , mTrustedCaller(aTrustedCaller)
 {
 }
 
 PostMessageEvent::~PostMessageEvent()
 {
 }
 
 NS_IMETHODIMP
@@ -106,23 +104,16 @@ PostMessageEvent::Run()
 
       MOZ_DIAGNOSTIC_ASSERT(sourceAttrs.mAppId == targetAttrs.mAppId,
         "Target and source should have the same mAppId attribute.");
       MOZ_DIAGNOSTIC_ASSERT(sourceAttrs.mUserContextId == targetAttrs.mUserContextId,
         "Target and source should have the same userContextId attribute.");
       MOZ_DIAGNOSTIC_ASSERT(sourceAttrs.mInIsolatedMozBrowser == targetAttrs.mInIsolatedMozBrowser,
         "Target and source should have the same inIsolatedMozBrowser attribute.");
 
-      if (!nsContentUtils::IsSystemOrExpandedPrincipal(targetPrin) &&
-          !nsContentUtils::IsSystemOrExpandedPrincipal(mProvidedPrincipal) &&
-          !mTrustedCaller) {
-        MOZ_DIAGNOSTIC_ASSERT(sourceAttrs.mPrivateBrowsingId == targetAttrs.mPrivateBrowsingId,
-          "Target and source should have the same mPrivateBrowsingId attribute.");
-      }
-
       nsAutoString providedOrigin, targetOrigin;
       nsresult rv = nsContentUtils::GetUTFOrigin(targetPrin, targetOrigin);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = nsContentUtils::GetUTFOrigin(mProvidedPrincipal, providedOrigin);
       NS_ENSURE_SUCCESS(rv, rv);
 
       const char16_t* params[] = { providedOrigin.get(), targetOrigin.get() };
 
@@ -193,17 +184,17 @@ PostMessageEvent::Dispatch(nsGlobalWindo
   // We can't simply call dispatchEvent on the window because doing so ends
   // up flipping the trusted bit on the event, and we don't want that to
   // happen because then untrusted content can call postMessage on a chrome
   // window if it can get a reference to it.
 
   RefPtr<nsPresContext> presContext =
     aTargetWindow->GetExtantDoc()->GetPresContext();
 
-  aEvent->SetTrusted(mTrustedCaller);
+  aEvent->SetTrusted(true);
   WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
 
   nsEventStatus status = nsEventStatus_eIgnore;
   EventDispatcher::Dispatch(aTargetWindow->AsInner(),
                             presContext,
                             internalEvent,
                             aEvent,
                             &status);
--- a/dom/base/PostMessageEvent.h
+++ b/dom/base/PostMessageEvent.h
@@ -31,33 +31,31 @@ class PostMessageEvent final : public Ru
 {
 public:
   NS_DECL_NSIRUNNABLE
 
   PostMessageEvent(nsGlobalWindowOuter* aSource,
                    const nsAString& aCallerOrigin,
                    nsGlobalWindowOuter* aTargetWindow,
                    nsIPrincipal* aProvidedPrincipal,
-                   nsIDocument* aSourceDocument,
-                   bool aTrustedCaller);
+                   nsIDocument* aSourceDocument);
 
 private:
   ~PostMessageEvent();
 
   void
   Dispatch(nsGlobalWindowInner* aTargetWindow, Event* aEvent);
 
   void
   DispatchError(JSContext* aCx, nsGlobalWindowInner* aTargetWindow,
                 mozilla::dom::EventTarget* aEventTarget);
 
   RefPtr<nsGlobalWindowOuter> mSource;
   nsString mCallerOrigin;
   RefPtr<nsGlobalWindowOuter> mTargetWindow;
   nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
   nsCOMPtr<nsIDocument> mSourceDocument;
-  bool mTrustedCaller;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PostMessageEvent_h
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -20,17 +20,17 @@
 #include "nsIContentInlines.h"
 #include "nsIContentViewer.h"
 #include "nsIDocument.h"
 #include "nsPIDOMWindow.h"
 #include "nsIWebNavigation.h"
 #include "nsIWebProgress.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeOwner.h"
-#include "nsDocShellLoadState.h"
+#include "nsDocShellLoadInfo.h"
 #include "nsIBaseWindow.h"
 #include "nsIBrowser.h"
 #include "nsContentUtils.h"
 #include "nsIXPConnect.h"
 #include "nsUnicharUtils.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScrollable.h"
@@ -385,97 +385,95 @@ nsFrameLoader::ReallyStartLoadingInterna
   }
   NS_ASSERTION(mDocShell,
                "MaybeCreateDocShell succeeded with a null mDocShell");
 
   // Just to be safe, recheck uri.
   rv = CheckURILoad(mURIToLoad, mTriggeringPrincipal);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState();
-
-  loadState->SetOriginalFrameSrc(mLoadingOriginalSrc);
+  RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo();
+
+  loadInfo->SetOriginalFrameSrc(mLoadingOriginalSrc);
   mLoadingOriginalSrc = false;
 
   // If this frame is sandboxed with respect to origin we will set it up with
   // a null principal later in nsDocShell::DoURILoad.
   // We do it there to correctly sandbox content that was loaded into
   // the frame via other methods than the src attribute.
   // We'll use our principal, not that of the document loaded inside us.  This
   // is very important; needed to prevent XSS attacks on documents loaded in
   // subframes!
   if (mTriggeringPrincipal) {
-    loadState->SetTriggeringPrincipal(mTriggeringPrincipal);
+    loadInfo->SetTriggeringPrincipal(mTriggeringPrincipal);
   } else {
-    loadState->SetTriggeringPrincipal(mOwnerContent->NodePrincipal());
+    loadInfo->SetTriggeringPrincipal(mOwnerContent->NodePrincipal());
   }
 
   nsCOMPtr<nsIURI> referrer;
 
   nsAutoString srcdoc;
   bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
                   mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::srcdoc,
                                          srcdoc);
 
   if (isSrcdoc) {
     nsAutoString referrerStr;
     mOwnerContent->OwnerDoc()->GetReferrer(referrerStr);
     rv = NS_NewURI(getter_AddRefs(referrer), referrerStr);
 
-    loadState->SetSrcdocData(srcdoc);
+    loadInfo->SetSrcdocData(srcdoc);
     nsCOMPtr<nsIURI> baseURI = mOwnerContent->GetBaseURI();
-    loadState->SetBaseURI(baseURI);
+    loadInfo->SetBaseURI(baseURI);
   }
   else {
     rv = mOwnerContent->NodePrincipal()->GetURI(getter_AddRefs(referrer));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Use referrer as long as it is not an NullPrincipalURI.
   // We could add a method such as GetReferrerURI to principals to make this
   // cleaner, but given that we need to start using Source Browsing Context for
   // referrer (see Bug 960639) this may be wasted effort at this stage.
   if (referrer) {
     bool isNullPrincipalScheme;
     rv = referrer->SchemeIs(NS_NULLPRINCIPAL_SCHEME, &isNullPrincipalScheme);
     if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
-      loadState->SetReferrer(referrer);
+      loadInfo->SetReferrer(referrer);
     }
   }
 
   // get referrer policy for this iframe:
   // first load document wide policy, then
   // load iframe referrer attribute if enabled in preferences
   // per element referrer overrules document wide referrer if enabled
   net::ReferrerPolicy referrerPolicy = mOwnerContent->OwnerDoc()->GetReferrerPolicy();
   HTMLIFrameElement* iframe = HTMLIFrameElement::FromNode(mOwnerContent);
   if (iframe) {
     net::ReferrerPolicy iframeReferrerPolicy = iframe->GetReferrerPolicyAsEnum();
     if (iframeReferrerPolicy != net::RP_Unset) {
       referrerPolicy = iframeReferrerPolicy;
     }
   }
-  loadState->SetReferrerPolicy(referrerPolicy);
+  loadInfo->SetReferrerPolicy(referrerPolicy);
 
   // Default flags:
   int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE;
 
   // Flags for browser frame:
   if (OwnerIsMozBrowserFrame()) {
     flags = nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
             nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
   }
 
   // Kick off the load...
   bool tmpState = mNeedsAsyncDestroy;
   mNeedsAsyncDestroy = true;
-  loadState->SetURI(mURIToLoad);
-  loadState->SetLoadFlags(flags);
-  loadState->SetFirstParty(false);
-  rv = mDocShell->LoadURI(loadState);
+  nsCOMPtr<nsIURI> uriToLoad = mURIToLoad;
+  rv = mDocShell->LoadURI(uriToLoad, loadInfo, flags, false);
   mNeedsAsyncDestroy = tmpState;
   mURIToLoad = nullptr;
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -4120,16 +4120,35 @@ nsGlobalWindowInner::PostMessageMoz(JSCo
     return;
   }
 
   PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray,
                  aSubjectPrincipal, aRv);
 }
 
 void
+nsGlobalWindowInner::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                                    const WindowPostMessageOptions& aOptions,
+                                    nsIPrincipal& aSubjectPrincipal,
+                                    ErrorResult& aRv)
+{
+  JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
+
+  aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx,
+                                                          aOptions.mTransfer,
+                                                          &transferArray);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
+  PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, transferArray,
+                 aSubjectPrincipal, aRv);
+}
+
+void
 nsGlobalWindowInner::Close(ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(CloseOuter, (nsContentUtils::IsCallerChrome()), aError, );
 }
 
 nsresult
 nsGlobalWindowInner::Close()
 {
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -128,16 +128,17 @@ class U2F;
 class VisualViewport;
 class VRDisplay;
 enum class VRDisplayEventReason : uint8_t;
 class VREventObserver;
 class WakeLock;
 #if defined(MOZ_WIDGET_ANDROID)
 class WindowOrientationObserver;
 #endif
+struct WindowPostMessageOptions;
 class Worklet;
 namespace cache {
 class CacheStorage;
 } // namespace cache
 class IDBFactory;
 } // namespace dom
 } // namespace mozilla
 
@@ -731,16 +732,20 @@ public:
                                                 mozilla::dom::CallerType aCallerType,
                                                 mozilla::ErrorResult& aRv);
   void Print(mozilla::ErrorResult& aError);
   void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const nsAString& aTargetOrigin,
                       const mozilla::dom::Sequence<JSObject*>& aTransfer,
                       nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
+  void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                      const mozilla::dom::WindowPostMessageOptions& aOptions,
+                      nsIPrincipal& aSubjectPrincipal,
+                      mozilla::ErrorResult& aError);
   int32_t SetTimeout(JSContext* aCx, mozilla::dom::Function& aFunction,
                      int32_t aTimeout,
                      const mozilla::dom::Sequence<JS::Value>& aArguments,
                      mozilla::ErrorResult& aError);
   int32_t SetTimeout(JSContext* aCx, const nsAString& aHandler,
                      int32_t aTimeout,
                      const mozilla::dom::Sequence<JS::Value>& /* unused */,
                      mozilla::ErrorResult& aError);
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -61,17 +61,17 @@
 #include "nsWindowSizes.h"
 #include "WindowNamedPropertiesHandler.h"
 #include "nsFrameSelection.h"
 #include "nsNetUtil.h"
 #include "nsVariant.h"
 #include "nsPrintfCString.h"
 #include "mozilla/intl/LocaleService.h"
 #include "WindowDestroyedEvent.h"
-#include "nsDocShellLoadState.h"
+#include "nsDocShellLoadInfo.h"
 
 // Helper Classes
 #include "nsJSUtils.h"
 #include "jsapi.h"
 #include "js/Wrapper.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsReadableUtils.h"
 #include "nsJSEnvironment.h"
@@ -5521,43 +5521,43 @@ nsGlobalWindowOuter::OpenOuter(const nsA
 {
   nsCOMPtr<nsPIDOMWindowOuter> window;
   aError = OpenJS(aUrl, aName, aOptions, getter_AddRefs(window));
   return window.forget();
 }
 
 nsresult
 nsGlobalWindowOuter::Open(const nsAString& aUrl, const nsAString& aName,
-                          const nsAString& aOptions, nsDocShellLoadState* aLoadState,
+                          const nsAString& aOptions, nsDocShellLoadInfo* aLoadInfo,
                           bool aForceNoOpener, nsPIDOMWindowOuter **_retval)
 {
   return OpenInternal(aUrl, aName, aOptions,
                       false,          // aDialog
                       false,          // aContentModal
                       true,           // aCalledNoScript
                       false,          // aDoJSFixups
                       true,           // aNavigate
                       nullptr, nullptr,  // No args
-                      aLoadState,
+                      aLoadInfo,
                       aForceNoOpener,
                       _retval);
 }
 
 nsresult
 nsGlobalWindowOuter::OpenJS(const nsAString& aUrl, const nsAString& aName,
                             const nsAString& aOptions, nsPIDOMWindowOuter **_retval)
 {
   return OpenInternal(aUrl, aName, aOptions,
                       false,          // aDialog
                       false,          // aContentModal
                       false,          // aCalledNoScript
                       true,           // aDoJSFixups
                       true,           // aNavigate
                       nullptr, nullptr,  // No args
-                      nullptr,        // aLoadState
+                      nullptr,        // aLoadInfo
                       false,          // aForceNoOpener
                       _retval);
 }
 
 // like Open, but attaches to the new window any extra parameters past
 // [features] as a JS property named "arguments"
 nsresult
 nsGlobalWindowOuter::OpenDialog(const nsAString& aUrl, const nsAString& aName,
@@ -5567,17 +5567,17 @@ nsGlobalWindowOuter::OpenDialog(const ns
 {
   return OpenInternal(aUrl, aName, aOptions,
                       true,                    // aDialog
                       false,                   // aContentModal
                       true,                    // aCalledNoScript
                       false,                   // aDoJSFixups
                       true,                    // aNavigate
                       nullptr, aExtraArgument, // Arguments
-                      nullptr,                 // aLoadState
+                      nullptr,                 // aLoadInfo
                       false,                   // aForceNoOpener
                       _retval);
 }
 
 // Like Open, but passes aNavigate=false.
 /* virtual */ nsresult
 nsGlobalWindowOuter::OpenNoNavigate(const nsAString& aUrl,
                                     const nsAString& aName,
@@ -5586,17 +5586,17 @@ nsGlobalWindowOuter::OpenNoNavigate(cons
 {
   return OpenInternal(aUrl, aName, aOptions,
                       false,          // aDialog
                       false,          // aContentModal
                       true,           // aCalledNoScript
                       false,          // aDoJSFixups
                       false,          // aNavigate
                       nullptr, nullptr,  // No args
-                      nullptr,        // aLoadState
+                      nullptr,        // aLoadInfo
                       false,          // aForceNoOpener
                       _retval);
 
 }
 
 already_AddRefed<nsPIDOMWindowOuter>
 nsGlobalWindowOuter::OpenDialogOuter(JSContext* aCx, const nsAString& aUrl,
                                      const nsAString& aName, const nsAString& aOptions,
@@ -5614,17 +5614,17 @@ nsGlobalWindowOuter::OpenDialogOuter(JSC
   nsCOMPtr<nsPIDOMWindowOuter> dialog;
   aError = OpenInternal(aUrl, aName, aOptions,
                         true,             // aDialog
                         false,            // aContentModal
                         false,            // aCalledNoScript
                         false,            // aDoJSFixups
                         true,                // aNavigate
                         argvArray, nullptr,  // Arguments
-                        nullptr,          // aLoadState
+                        nullptr,          // aLoadInfo
                         false,            // aForceNoOpener
                         getter_AddRefs(dialog));
   return dialog.forget();
 }
 
 already_AddRefed<nsPIDOMWindowOuter>
 nsGlobalWindowOuter::GetFramesOuter()
 {
@@ -5823,18 +5823,17 @@ nsGlobalWindowOuter::PostMessageMozOuter
     new PostMessageEvent(nsContentUtils::IsCallerChrome() || !callerInnerWin
                          ? nullptr
                          : callerInnerWin->GetOuterWindowInternal(),
                          origin,
                          this,
                          providedPrincipal,
                          callerInnerWin
                          ? callerInnerWin->GetDoc()
-                         : nullptr,
-                         nsContentUtils::IsCallerChrome());
+                         : nullptr);
 
   JS::Rooted<JS::Value> message(aCx, aMessage);
   JS::Rooted<JS::Value> transfer(aCx, aTransfer);
 
   event->Write(aCx, message, transfer, JS::CloneDataPolicy(), aError);
   if (NS_WARN_IF(aError.Failed())) {
     return;
   }
@@ -6969,17 +6968,17 @@ public:
 
 nsresult
 nsGlobalWindowOuter::OpenInternal(const nsAString& aUrl, const nsAString& aName,
                                   const nsAString& aOptions, bool aDialog,
                                   bool aContentModal, bool aCalledNoScript,
                                   bool aDoJSFixups, bool aNavigate,
                                   nsIArray *argv,
                                   nsISupports *aExtraArgument,
-                                  nsDocShellLoadState* aLoadState,
+                                  nsDocShellLoadInfo* aLoadInfo,
                                   bool aForceNoOpener,
                                   nsPIDOMWindowOuter **aReturn)
 {
 #ifdef DEBUG
   uint32_t argc = 0;
   if (argv)
       argv->GetLength(&argc);
 #endif
@@ -7118,17 +7117,17 @@ nsGlobalWindowOuter::OpenInternal(const 
       // We asserted at the top of this function that aNavigate is true for
       // !aCalledNoScript.
       rv = pwwatch->OpenWindow2(this, url.IsVoid() ? nullptr : url.get(),
                                 name_ptr,
                                 options_ptr, /* aCalledFromScript = */ true,
                                 aDialog, aNavigate, argv,
                                 isPopupSpamWindow,
                                 forceNoOpener,
-                                aLoadState,
+                                aLoadInfo,
                                 getter_AddRefs(domReturn));
     } else {
       // Force a system caller here so that the window watcher won't screw us
       // up.  We do NOT want this case looking at the JS context on the stack
       // when searching.  Compare comments on
       // nsIDOMWindow::OpenWindow and nsIWindowWatcher::OpenWindow.
 
       // Note: Because nsWindowWatcher is so broken, it's actually important
@@ -7141,17 +7140,17 @@ nsGlobalWindowOuter::OpenInternal(const 
       }
 
       rv = pwwatch->OpenWindow2(this, url.IsVoid() ? nullptr : url.get(),
                                 name_ptr,
                                 options_ptr, /* aCalledFromScript = */ false,
                                 aDialog, aNavigate, aExtraArgument,
                                 isPopupSpamWindow,
                                 forceNoOpener,
-                                aLoadState,
+                                aLoadInfo,
                                 getter_AddRefs(domReturn));
 
     }
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // success!
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -67,17 +67,17 @@ class nsIControllers;
 class nsIJSID;
 class nsIScriptContext;
 class nsIScriptTimeoutHandler;
 class nsITabChild;
 class nsITimeoutHandler;
 class nsIWebBrowserChrome;
 class mozIDOMWindowProxy;
 
-class nsDocShellLoadState;
+class nsDocShellLoadInfo;
 class nsDOMWindowList;
 class nsScreen;
 class nsHistory;
 class nsGlobalWindowObserver;
 class nsGlobalWindowInner;
 class nsDOMWindowUtils;
 class nsIIdleService;
 struct nsRect;
@@ -601,17 +601,17 @@ public:
   mozilla::dom::Element* GetFrameElement() override;
   already_AddRefed<nsPIDOMWindowOuter>
   OpenOuter(const nsAString& aUrl,
             const nsAString& aName,
             const nsAString& aOptions,
             mozilla::ErrorResult& aError);
   nsresult Open(const nsAString& aUrl, const nsAString& aName,
                 const nsAString& aOptions,
-                nsDocShellLoadState* aLoadState,
+                nsDocShellLoadInfo* aLoadInfo,
                 bool aForceNoOpener,
                 nsPIDOMWindowOuter **_retval) override;
   mozilla::dom::Navigator* GetNavigator() override;
 
 #if defined(MOZ_WIDGET_ANDROID)
   int16_t Orientation(mozilla::dom::CallerType aCallerType) const;
 #endif
 
@@ -873,17 +873,17 @@ private:
    *
    * @param argv The arguments to pass to the new window.  The first
    *        three args, if present, will be aUrl, aName, and aOptions.  So this
    *        param only matters if there are more than 3 arguments.
    *
    * @param aExtraArgument Another way to pass arguments in.  This is mutually
    *        exclusive with the argv approach.
    *
-   * @param aLoadState to be passed on along to the windowwatcher.
+   * @param aLoadInfo to be passed on along to the windowwatcher.
    *
    * @param aForceNoOpener if true, will act as if "noopener" were passed in
    *                       aOptions, but without affecting any other window
    *                       features.
    *
    * @param aReturn [out] The window that was opened, if any.  Will be null if
    *                      aForceNoOpener is true of if aOptions contains
    *                      "noopener".
@@ -895,17 +895,17 @@ private:
                         const nsAString& aOptions,
                         bool aDialog,
                         bool aContentModal,
                         bool aCalledNoScript,
                         bool aDoJSFixups,
                         bool aNavigate,
                         nsIArray *argv,
                         nsISupports *aExtraArgument,
-                        nsDocShellLoadState* aLoadState,
+                        nsDocShellLoadInfo* aLoadInfo,
                         bool aForceNoOpener,
                         nsPIDOMWindowOuter **aReturn);
 
   // Checks that the channel was loaded by the URI currently loaded in aDoc
   static bool SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel);
 
 public:
   // Helper Functions
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -26,17 +26,17 @@ class nsDOMOfflineResourceList;
 class nsDOMWindowList;
 class nsGlobalWindowInner;
 class nsGlobalWindowOuter;
 class nsIArray;
 class nsIChannel;
 class nsIContent;
 class nsICSSDeclaration;
 class nsIDocShell;
-class nsDocShellLoadState;
+class nsDocShellLoadInfo;
 class nsIDocument;
 class nsIPrincipal;
 class nsIScriptTimeoutHandler;
 class nsISerialEventTarget;
 class nsIURI;
 class nsPIDOMWindowInner;
 class nsPIDOMWindowOuter;
 class nsPIWindowRoot;
@@ -1115,22 +1115,22 @@ public:
 
   virtual nsresult GetPrompter(nsIPrompt** aPrompt) = 0;
   virtual nsresult GetControllers(nsIControllers** aControllers) = 0;
   virtual already_AddRefed<mozilla::dom::Selection> GetSelection() = 0;
   virtual already_AddRefed<nsPIDOMWindowOuter> GetOpener() = 0;
 
   virtual nsDOMWindowList* GetFrames() = 0;
 
-  // aLoadState will be passed on through to the windowwatcher.
+  // aLoadInfo will be passed on through to the windowwatcher.
   // aForceNoOpener will act just like a "noopener" feature in aOptions except
   //                will not affect any other window features.
   virtual nsresult Open(const nsAString& aUrl, const nsAString& aName,
                         const nsAString& aOptions,
-                        nsDocShellLoadState* aLoadState,
+                        nsDocShellLoadInfo* aLoadInfo,
                         bool aForceNoOpener,
                         nsPIDOMWindowOuter **_retval) = 0;
   virtual nsresult OpenDialog(const nsAString& aUrl, const nsAString& aName,
                               const nsAString& aOptions,
                               nsISupports* aExtraArgument,
                               nsPIDOMWindowOuter** _retval) = 0;
 
   virtual nsresult GetInnerWidth(int32_t* aWidth) = 0;
--- a/dom/cache/QuotaClient.cpp
+++ b/dom/cache/QuotaClient.cpp
@@ -85,16 +85,24 @@ LockedGetPaddingSizeFromDB(nsIFile* aDir
   if (rv == NS_ERROR_FILE_NOT_FOUND ||
       rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
     // Return NS_OK with size = 0 if both the db and padding file don't exist.
     // There is no other way to get the overall padding size of an origin.
     return NS_OK;
   }
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  // Make sure that the database has the latest schema before we try to read
+  // from it. We have to do this because LockedGetPaddingSizeFromDB is called
+  // by QuotaClient::GetUsageForOrigin which may run at any time (there's no
+  // guarantee that SetupAction::RunSyncWithDBOnTarget already checked the
+  // schema for the given origin).
+  rv = mozilla::dom::cache::db::CreateOrMigrateSchema(conn);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
   int64_t paddingSize = 0;
   rv = mozilla::dom::cache::
        LockedDirectoryPaddingRestore(aDir, conn, /* aMustRestore */ false,
                                      &paddingSize);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   *aPaddingSizeOut = paddingSize;
 
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fc57bc1043d7a786246e3dd37c233d5a489cd3d9
GIT binary patch
literal 2834
zc$^FHW@h1H0D+|Da9;)_z`-EEP+XE<l$f5X9~!~Kz;Aj!db|Gl=&y_-3;}RmI&G-B
z1dw#4q^2d7=9Iv7e=$Mm76IE}RU7VG`}e+p0Z1zo0|OsIw_b5!PG(6eNN=Cf`Dl4n
zh+b~KFDG?8Lp@IGpYqiSG)PNGN=Qj52na|3f|jH^4V?`(9{cB=aE|M^6==Z0h~(1t
z;z$&iYB0!PxHO}rq@Y+^Tf4lxTrW4jDl;c1Q7^wJT|YF0lY#l}wu{jqTw1}+z{v8I
zk%0m13*0v7kzhk|Vsb_*L3<#f2D0az{;EH*EFfDL8LUaR2TT^jf*6mbZ||JVd+fq+
z;A7$K-pO^|4U1NFn{YTv-`doD!Aqgh<(5>)MBiy9+PVBEK3U}@PdpZT+cCP(OJAmv
zO>a&GOI$1W9;P`D4*XEgowG!<`$yK7|0n%&&R5tN&--|z=+@!$T6r%{B%in6>hyD6
zgs8FcBW2B2fz4TQV&TuO+DzATzin!|e(C+|Gez^)|J$}izw79o+Kr#ydgcDy{gnOl
z)<fTa#(#SM+Wv+9^LNkd=4`NMcu>FS`?aRHnE2~@+nio!-sW^VzuoUwymxi!;yVT@
zi}yqurtAvLD_aw+Zm~JXT`olWn7UhRST=8DZOqD?)B1CYcSf&#yKMEg)a^2tbNh}=
zDSe#0I4|UT&ta9I4KK~SZ+{B>`yg>=or=9_-?~0w_2@}U=N^sOxMv;b_RA&n*w5d}
zT4KC5@5RyAX|wD<&n;1uy8Nk7^8SqL-QVA3?)|)@XP?)V*DQTEzur!}&3kAfZ}-fi
zewNd|TUDfwO+MyuaIW8r`S(LEX-t|e5_)Y_(ft(?uMDc69^U(DU*NA*3&g*dZMo-N
zSgFT<yJ*d_$Lwh#vd?ZTXf@f-ezo>-=bR16B^Mq(cv3U1>iyHNkIwa#eXQCx=~Beu
z*PkD+)|mXPK(sb<>uzP~|7}&TZ7+O%bZuJM$EaCf(_^nTtuLAvsmqgUBm6`EdGxa@
zoblJIJemEJXaBWXDQ&Skm5=SI@$*WjKQ9t8H{5yi{ovcvm)v-FKAxH7D!MS0b#2at
ztKTkN|2Fr>rw?aW7hYYKxh%eFnU;RP`8q$gU0<(m^qafkscE}*S&Z6$pE(wXUv5p$
zIU*gr%Tn^bWm(PrkL$gc-hb@3Ynpu&H?!*ZS&#MR{Y|)CK5La#>}pw`uBW9=qOZ?n
z@BUU<QD<g&bOo;uNBt|)$H(SO(dPB~{bj4?@`sKYt#?04o;_mV{Jhd4s@Pe4_3PDo
zy%w6Q8<Wdx%|jl}P1e6`*>07lmSprPMy=@CZrR(mn{O39C}@i>aFKrdt$FRrm%sXC
z{VeqVvZbq~S33EGo3>3};W0Vt$3`h}|Ipbdk7-sGSA9IpyLP!cbM`W!#qSdO^3JE~
zxn6%OG}TU6&HV9culHvo_wLxZFjhyUT(&vTNV|Mfxx&pQd3!GOygucty~S<!_l<8$
ztrx2=`jVk0-mBy8X*SuqPhr)CKHEK3)6Zs^8OViiyBhVf*jInu+RVM-k5neNO{zJU
zCgid0ZiGi<-JKP(Ay?P&_hvayd({4Y{|`oR;gZ*`YMQORpW777ZO}AXe0ItC|NFV1
z{LG*WY1g?<%JaCK|DW`A|2YA$rXT9A|F85vuLpCMNx%OORy?C_&wq9hBjMM_fAV1M
zFOPq=2P?YVfB!!-h+*)z;y*u#ao|h$y#HIL&Tp9oR&Zc#Y545Z^#L1N^<@$}18Qd-
z3iIBUvGVDQ@BeR|I`0H25FdQoW&QG}JW|<sr>-7P$!SV*iIxjwmJ(Zl3YA3qI(*y3
zb`Z#u07~-%txGOSO)N=`2b-psRSY)h$@YuUAdF^EXy3uC!v;KU@9VfO22XOn6B}M8
z@h0XQ+rh0<BQAIx3=7+Iv3&mHz*Fnw?|=8+_L`^U!#U}S$ZamcAq#jInJbq|IIx;1
z)vTMYUHN0eO~LQg!lBc{vumA}e`$?-7HRQ)lG^SN%aH4J$wI#`$>(;iG1SX{lNfVT
zo6|X}GT_XuEzgcSR5opHDz{6r+b#K!m+vbRpQxPQvIxg4W1SO?6<N0~%g@=cK}9*X
zFr9VR?8V39^pEDxyZv_m+KDrYj%;Wz|I8iW&B!FmjH}w`fiWH4I)W%fBY*?m2tYOg
zvk8D~f*Fzthz0=%yg>jl30HFfVbYSu5`;<c#sLSsaR4$$ju}@~F9CPk0mlbeT!g#g
zXN9CEjI@N?Ha!&EoEV9>4b}+2XD6;EfCSwBhQ?JS+lkg7Al=iZD4woiBh>-${7cyH
mn7J2XH6z&XuYp!$<YiV!UdAw#l?{^QIT+Z1h1^$gAqW6AWdx`I
new file mode 100644
--- /dev/null
+++ b/dom/cache/test/xpcshell/test_bug1425146.js
@@ -0,0 +1,55 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This test is mainly to verify that we are able to recover from a situation
+// in which the padding file and the padding column are missing during origin
+// initialization. This was originally reported in bug 1425146 comment 39.
+// The situation can be described as follows:
+// 1. A profile is used in FF57. The storage version is 2.1. There's no cache
+//    storage for http://www.mozilla.org
+// 2. The same profile is used in FF56. The storage version is still 2.1 which
+//    doesn't prevent storage system from working since minor upgrades are
+//    backwards-compatible. The cache storage for http://www.mozilla.org is
+//    created with schema version 25 (without any padding stuff).
+// 3. The profile is used in FF57 again. Zero padding files for existing cache
+//    storages are not created because storage is already at version 2.1.
+//    Storage is being initialized and a missing padding file triggers padding
+//    size computation from the cache database with schema version 25. Since
+//    the computation happens before any real DOM cache operation, the database
+//    is not upgraded to schema version 26, so the padding column is missing.
+
+async function run_test() {
+  do_test_pending();
+
+  // The profile contains one cache storage, a script for cache creation and
+  // the storage database:
+  // - storage/default/http+++www.mozilla.org/cache
+  // - create_cache.js
+  // - storage.sqlite
+  // The file create_cache.js in the package was run locally, specifically it
+  // was temporarily added to xpcshell.ini and then executed:
+  //   mach xpcshell-test --interactive dom/cache/test/xpcshell/create_cache.js
+  // Note: it must be executed in FF56 and it only creates the directory
+  // "storage/default/chrome/cache" and the file "storage.sqlite". To make it
+  // become the profile in the test, additional manual steps are needed.
+  // 1. Create "http+++www.mozilla.org" folder under the ""storage/default".
+  // 2. Copy the "cache" folder under the "storage/default/chrome" to
+  //    "storage/default/http+++www.mozilla.org".
+  // 3. Remove the folder "storage/default/chrome"
+  // 4. Remove the folder "storage/temporary".
+  // 5. Add "create_cache.js".
+  // 6. Replace the "storage.sqlite" created by FF56 (storage v2.0) with the
+  //    "storage.sqlite" created by FF57 (storage v2.1)
+  create_test_profile('bug1425146_profile.zip');
+
+  try {
+    await caches.open("test");
+    ok(true, "Should not have thrown");
+  } catch(ex) {
+    ok(false, "Should not have thrown");
+  }
+
+  do_test_finished();
+}
--- a/dom/cache/test/xpcshell/xpcshell.ini
+++ b/dom/cache/test/xpcshell/xpcshell.ini
@@ -1,17 +1,19 @@
 # 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/.
 
 [DEFAULT]
 head = head.js
 support-files =
+  bug1425146_profile.zip
   schema_15_profile.zip
   schema_25_profile.zip
 
 # dummy test entry to generate profile zip files
 [make_profile.js]
   skip-if = true
 
+[test_bug1425146.js]
 [test_migration.js]
 [test_padding_error_handle.js]
 [test_schema_26_upgrade.js]
--- a/dom/clients/api/Client.cpp
+++ b/dom/clients/api/Client.cpp
@@ -7,16 +7,17 @@
 #include "Client.h"
 
 #include "ClientDOMUtil.h"
 #include "mozilla/dom/ClientHandle.h"
 #include "mozilla/dom/ClientIPCTypes.h"
 #include "mozilla/dom/ClientManager.h"
 #include "mozilla/dom/ClientState.h"
 #include "mozilla/dom/DOMMozPromiseRequestHolder.h"
+#include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "nsIGlobalObject.h"
 
 namespace mozilla {
 namespace dom {
 
@@ -137,16 +138,24 @@ Client::PostMessage(JSContext* aCx, JS::
   if (aRv.Failed()) {
     return;
   }
 
   EnsureHandle();
   mHandle->PostMessage(data, workerPrivate->GetServiceWorkerDescriptor());
 }
 
+void
+Client::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                    const PostMessageOptions& aOptions,
+                    ErrorResult& aRv)
+{
+  PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
+}
+
 VisibilityState
 Client::GetVisibilityState() const
 {
   return mData->state().get_IPCClientWindowState().visibilityState();
 }
 
 bool
 Client::Focused() const
--- a/dom/clients/api/Client.h
+++ b/dom/clients/api/Client.h
@@ -17,16 +17,17 @@ class nsIGlobalObject;
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 
 class ClientHandle;
 class ClientInfoAndState;
+struct PostMessageOptions;
 class Promise;
 
 template <typename t> class Sequence;
 
 class Client final : public nsISupports
                    , public nsWrapperCache
 {
   nsCOMPtr<nsIGlobalObject> mGlobal;
@@ -84,16 +85,21 @@ public:
   already_AddRefed<Promise>
   Navigate(const nsAString& aURL, ErrorResult& aRv);
 
   void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
               const Sequence<JSObject*>& aTransferrable,
               ErrorResult& aRv);
 
+  void
+  PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+              const PostMessageOptions& aOptions,
+              ErrorResult& aRv);
+
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(mozilla::dom::Client)
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // _mozilla_dom_Client_h
--- a/dom/clients/manager/ClientNavigateOpChild.cpp
+++ b/dom/clients/manager/ClientNavigateOpChild.cpp
@@ -4,17 +4,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 "ClientNavigateOpChild.h"
 
 #include "ClientState.h"
 #include "mozilla/Unused.h"
 #include "nsIDocShell.h"
-#include "nsDocShellLoadState.h"
+#include "nsDocShellLoadInfo.h"
 #include "nsIWebNavigation.h"
 #include "nsIWebProgress.h"
 #include "nsIWebProgressListener.h"
 #include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
 #include "nsURLHelper.h"
 
 namespace mozilla {
@@ -231,26 +231,23 @@ ClientNavigateOpChild::DoNavigate(const 
 
   nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
   nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
   if (!docShell || !webProgress) {
     ref = ClientOpPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
     return ref.forget();
   }
 
-  RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState();
+  RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo();
 
-  loadState->SetTriggeringPrincipal(principal);
-  loadState->SetReferrerPolicy(doc->GetReferrerPolicy());
-  loadState->SetLoadType(LOAD_STOP_CONTENT);
-  loadState->SetSourceDocShell(docShell);
-  loadState->SetURI(url);
-  loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);
-  loadState->SetFirstParty(true);
-  rv = docShell->LoadURI(loadState);
+  loadInfo->SetTriggeringPrincipal(principal);
+  loadInfo->SetReferrerPolicy(doc->GetReferrerPolicy());
+  loadInfo->SetLoadType(LOAD_STOP_CONTENT);
+  loadInfo->SetSourceDocShell(docShell);
+  rv = docShell->LoadURI(url, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, true);
   if (NS_FAILED(rv)) {
     ref = ClientOpPromise::CreateAndReject(rv, __func__);
     return ref.forget();
   }
 
   RefPtr<ClientOpPromise::Private> promise =
     new ClientOpPromise::Private(__func__);
 
--- a/dom/interfaces/payments/nsIPaymentActionResponse.idl
+++ b/dom/interfaces/payments/nsIPaymentActionResponse.idl
@@ -64,19 +64,44 @@ interface nsIGeneralResponseData : nsIPa
  *  given JSObject directly, because PaymentAddress creation in JS code is hard.
  *  To let UI code can create BasicCardResponse easier, nsIBasicCardResponse is
  *  provided for UI by passing the raw data of BasicCardResponse,
  */
 [builtinclass, scriptable, uuid(0d55a5e6-d185-44f0-b992-a8e1321e4bce)]
 interface nsIBasicCardResponseData : nsIPaymentResponseData
 {
   /**
-   *  The stringified response data.
+   *  The cardholder name.
+   */
+  readonly attribute AString cardholderName;
+
+  /**
+   *  The card number.
+   */
+  readonly attribute AString cardNumber;
+
+  /**
+   *  The expiry month.
    */
-  readonly attribute AString data;
+  readonly attribute AString expiryMonth;
+
+  /**
+   *  The expiry year.
+   */
+  readonly attribute AString expiryYear;
+
+  /**
+   *  The card security number.
+   */
+  readonly attribute AString cardSecurityCode;
+
+  /**
+   *  The billing address.
+   */
+  readonly attribute nsIPaymentAddress billingAddress;
 
   /**
    *  The initial method for nsIBasicCardResponseData.
    *  @param aCardholderName   - the cardholder name.
    *  @param aCardNumber       - the card number.
    *  @param aExpiryMonth      - the expiry month.
    *  @param aExpiryYear       - the expiry year.
    *  @param aCardSecurityCode - the card security code.
@@ -178,17 +203,17 @@ interface nsIPaymentShowActionResponse :
   /**
    *  The decided payment method name. i.e. "basic-card".
    */
   readonly attribute AString methodName;
 
   /**
    *  The data needed by the payment method. (it must be serializable)
    */
-  readonly attribute AString data;
+  readonly attribute nsIPaymentResponseData data;
 
   /**
    *  The payer name information.
    */
   readonly attribute AString payerName;
 
   /**
    *  The payer email information.
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -114,17 +114,17 @@
 #include <unistd.h>
 #endif
 #endif
 
 #include "mozilla/Unused.h"
 
 #include "mozInlineSpellChecker.h"
 #include "nsDocShell.h"
-#include "nsDocShellLoadState.h"
+#include "nsDocShellLoadInfo.h"
 #include "nsIConsoleListener.h"
 #include "nsIContentViewer.h"
 #include "nsICycleCollectorListener.h"
 #include "nsIIdlePeriod.h"
 #include "nsIDragService.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIMemoryReporter.h"
 #include "nsIMemoryInfoDumper.h"
@@ -770,29 +770,29 @@ ContentChild::ProvideWindow(mozIDOMWindo
                             uint32_t aChromeFlags,
                             bool aCalledFromJS,
                             bool aPositionSpecified,
                             bool aSizeSpecified,
                             nsIURI* aURI,
                             const nsAString& aName,
                             const nsACString& aFeatures,
                             bool aForceNoOpener,
-                            nsDocShellLoadState* aLoadState,
+                            nsDocShellLoadInfo* aLoadInfo,
                             bool* aWindowIsNew,
                             mozIDOMWindowProxy** aReturn)
 {
   return ProvideWindowCommon(nullptr, aParent, false, aChromeFlags,
                              aCalledFromJS, aPositionSpecified,
                              aSizeSpecified, aURI, aName, aFeatures,
-                             aForceNoOpener, aLoadState, aWindowIsNew, aReturn);
+                             aForceNoOpener, aLoadInfo, aWindowIsNew, aReturn);
 }
 
 static nsresult
 GetCreateWindowParams(mozIDOMWindowProxy* aParent,
-                      nsDocShellLoadState* aLoadState,
+                      nsDocShellLoadInfo* aLoadInfo,
                       nsACString& aBaseURIString, float* aFullZoom,
                       uint32_t* aReferrerPolicy,
                       nsIPrincipal** aTriggeringPrincipal)
 {
   *aFullZoom = 1.0f;
   if (!aTriggeringPrincipal) {
     NS_ERROR("aTriggeringPrincipal is null");
     return NS_ERROR_FAILURE;
@@ -810,21 +810,21 @@ GetCreateWindowParams(mozIDOMWindowProxy
   nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
   if (!baseURI) {
     NS_ERROR("nsIDocument didn't return a base URI");
     return NS_ERROR_FAILURE;
   }
 
   baseURI->GetSpec(aBaseURIString);
 
-  if (aLoadState) {
-    if (!aLoadState->SendReferrer()) {
+  if (aLoadInfo) {
+    if (!aLoadInfo->SendReferrer()) {
       *aReferrerPolicy = mozilla::net::RP_No_Referrer;
     } else {
-      *aReferrerPolicy = aLoadState->ReferrerPolicy();
+      *aReferrerPolicy = aLoadInfo->ReferrerPolicy();
     }
   }
 
   RefPtr<nsDocShell> openerDocShell =
     static_cast<nsDocShell*>(opener->GetDocShell());
   if (!openerDocShell) {
     return NS_OK;
   }
@@ -845,17 +845,17 @@ ContentChild::ProvideWindowCommon(TabChi
                                   uint32_t aChromeFlags,
                                   bool aCalledFromJS,
                                   bool aPositionSpecified,
                                   bool aSizeSpecified,
                                   nsIURI* aURI,
                                   const nsAString& aName,
                                   const nsACString& aFeatures,
                                   bool aForceNoOpener,
-                                  nsDocShellLoadState* aLoadState,
+                                  nsDocShellLoadInfo* aLoadInfo,
                                   bool* aWindowIsNew,
                                   mozIDOMWindowProxy** aReturn)
 {
   *aReturn = nullptr;
 
   nsAutoPtr<IPCTabContext> ipcContext;
   TabId openerTabId = TabId(0);
   nsAutoCString features(aFeatures);
@@ -892,17 +892,17 @@ ContentChild::ProvideWindowCommon(TabChi
 
   // If we're in a content process and we have noopener set, there's no reason
   // to load in our process, so let's load it elsewhere!
   if (loadInDifferentProcess) {
     nsAutoCString baseURIString;
     float fullZoom;
     nsCOMPtr<nsIPrincipal> triggeringPrincipal;
     uint32_t referrerPolicy = mozilla::net::RP_Unset;
-    rv = GetCreateWindowParams(aParent, aLoadState, baseURIString, &fullZoom,
+    rv = GetCreateWindowParams(aParent, aLoadInfo, baseURIString, &fullZoom,
                                &referrerPolicy,
                                getter_AddRefs(triggeringPrincipal));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     OptionalURIParams uriToLoad;
     SerializeURI(aURI, uriToLoad);
@@ -1106,17 +1106,17 @@ ContentChild::ProvideWindowCommon(TabChi
                                          NS_ConvertUTF8toUTF16(url),
                                          name, NS_ConvertUTF8toUTF16(features),
                                          std::move(resolve), std::move(reject));
   } else {
     nsAutoCString baseURIString;
     float fullZoom;
     nsCOMPtr<nsIPrincipal> triggeringPrincipal;
     uint32_t referrerPolicy = mozilla::net::RP_Unset;
-    rv = GetCreateWindowParams(aParent, aLoadState, baseURIString, &fullZoom,
+    rv = GetCreateWindowParams(aParent, aLoadInfo, baseURIString, &fullZoom,
                                &referrerPolicy,
                                getter_AddRefs(triggeringPrincipal));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     OptionalURIParams uriToLoad;
     if (aURI) {
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -29,17 +29,17 @@
 
 struct ChromePackage;
 class nsIObserver;
 struct SubstitutionMapping;
 struct OverrideMapping;
 class nsIDomainPolicy;
 class nsIURIClassifierCallback;
 struct LookAndFeelInt;
-class nsDocShellLoadState;
+class nsDocShellLoadInfo;
 
 namespace mozilla {
 class RemoteSpellcheckEngineChild;
 class ChildProfilerController;
 
 using mozilla::loader::PScriptCacheChild;
 
 #if !defined(XP_WIN)
@@ -114,17 +114,17 @@ public:
                       uint32_t aChromeFlags,
                       bool aCalledFromJS,
                       bool aPositionSpecified,
                       bool aSizeSpecified,
                       nsIURI* aURI,
                       const nsAString& aName,
                       const nsACString& aFeatures,
                       bool aForceNoOpener,
-                      nsDocShellLoadState* aLoadState,
+                      nsDocShellLoadInfo* aLoadInfo,
                       bool* aWindowIsNew,
                       mozIDOMWindowProxy** aReturn);
 
   bool Init(MessageLoop* aIOLoop,
             base::ProcessId aParentPid,
             const char* aParentBuildID,
             IPC::Channel* aChannel,
             uint64_t aChildID,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -119,17 +119,17 @@
 #include "nsCommandParams.h"
 #include "nsISHistory.h"
 #include "nsQueryObject.h"
 #include "nsIHttpChannel.h"
 #include "mozilla/dom/DocGroup.h"
 #include "nsString.h"
 #include "nsISupportsPrimitives.h"
 #include "mozilla/Telemetry.h"
-#include "nsDocShellLoadState.h"
+#include "nsDocShellLoadInfo.h"
 #include "nsWebBrowser.h"
 
 #ifdef XP_WIN
 #include "mozilla/plugins/PluginWidgetChild.h"
 #endif
 
 #ifdef NS_PRINTING
 #include "nsIPrintSession.h"
@@ -953,17 +953,17 @@ TabChild::GetInterface(const nsIID & aII
 
 NS_IMETHODIMP
 TabChild::ProvideWindow(mozIDOMWindowProxy* aParent,
                         uint32_t aChromeFlags,
                         bool aCalledFromJS,
                         bool aPositionSpecified, bool aSizeSpecified,
                         nsIURI* aURI, const nsAString& aName,
                         const nsACString& aFeatures, bool aForceNoOpener,
-                        nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
+                        nsDocShellLoadInfo* aLoadInfo, bool* aWindowIsNew,
                         mozIDOMWindowProxy** aReturn)
 {
     *aReturn = nullptr;
 
     // If aParent is inside an <iframe mozbrowser> and this isn't a request to
     // open a modal-type window, we're going to create a new <iframe mozbrowser>
     // and return its window here.
     nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent);
@@ -997,17 +997,17 @@ TabChild::ProvideWindow(mozIDOMWindowPro
                                    aChromeFlags,
                                    aCalledFromJS,
                                    aPositionSpecified,
                                    aSizeSpecified,
                                    aURI,
                                    aName,
                                    aFeatures,
                                    aForceNoOpener,
-                                   aLoadState,
+                                   aLoadInfo,
                                    aWindowIsNew,
                                    aReturn);
 }
 
 void
 TabChild::DestroyWindow()
 {
     if (mCoalescedMouseEventFlusher) {
--- a/dom/messagechannel/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -407,16 +407,24 @@ MessagePort::PostMessage(JSContext* aCx,
   // note: `messages` will borrow the underlying buffer, but this is okay
   // because reverse destruction order means `messages` will be destroyed prior
   // to `array`/`data`.
   SharedMessagePortMessage::FromSharedToMessagesChild(mActor, array, messages);
   mActor->SendPostMessages(messages);
 }
 
 void
+MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                         const PostMessageOptions& aOptions,
+                         ErrorResult& aRv)
+{
+  PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
+}
+
+void
 MessagePort::Start()
 {
   if (mMessageQueueEnabled) {
     return;
   }
 
   mMessageQueueEnabled = true;
   Dispatch();
--- a/dom/messagechannel/MessagePort.h
+++ b/dom/messagechannel/MessagePort.h
@@ -19,16 +19,17 @@
 class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
 class ClonedMessageData;
 class MessagePortChild;
 class MessagePortIdentifier;
+struct PostMessageOptions;
 class PostMessageRunnable;
 class SharedMessagePortMessage;
 class StrongWorkerRef;
 
 class MessagePort final : public DOMEventTargetHelper
 {
   friend class PostMessageRunnable;
 
@@ -53,16 +54,21 @@ public:
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
               const Sequence<JSObject*>& aTransferable,
               ErrorResult& aRv);
 
+  void
+  PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+              const PostMessageOptions& aOptions,
+              ErrorResult& aRv);
+
   void Start();
 
   void Close();
 
   EventHandlerNonNull* GetOnmessage();
 
   void SetOnmessage(EventHandlerNonNull* aCallback);
 
--- a/dom/payments/BasicCardPayment.cpp
+++ b/dom/payments/BasicCardPayment.cpp
@@ -9,127 +9,29 @@
 #include "mozilla/ClearOnShutdown.h"
 #include "nsArrayUtils.h"
 #include "nsISupportsPrimitives.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsDataHashtable.h"
 
 namespace mozilla {
 namespace dom {
-#ifndef PaymentBasicCardMacros
-#define PaymentBasicCardMacros
-
-#define AMEX NS_LITERAL_STRING("amex")
-#define CARTEBANCAIRE NS_LITERAL_STRING("cartebancaire")
-#define DINERS NS_LITERAL_STRING("diners")
-#define DISCOVER NS_LITERAL_STRING("discover")
-#define JCB NS_LITERAL_STRING("jcb")
-#define MASTERCARD NS_LITERAL_STRING("mastercard")
-#define MIR NS_LITERAL_STRING("mir")
-#define UNIONPAY NS_LITERAL_STRING("unionpay")
-#define VISA NS_LITERAL_STRING("visa")
-
-#define CardholderName NS_LITERAL_STRING("cardholderName")
-#define CardNumber NS_LITERAL_STRING("cardNumber")
-#define ExpiryMonth NS_LITERAL_STRING("expiryMonth")
-#define ExpiryYear NS_LITERAL_STRING("expiryYear")
-#define CardSecurityCode NS_LITERAL_STRING("cardSecurityCode")
-
-#define Country NS_LITERAL_STRING("country")
-#define AddressLine NS_LITERAL_STRING("addressLine")
-#define Region NS_LITERAL_STRING("region")
-#define RegionCode NS_LITERAL_STRING("regionCode")
-#define City NS_LITERAL_STRING("city")
-#define DependentLocality NS_LITERAL_STRING("dependentLocality")
-#define PostalCode NS_LITERAL_STRING("postalCode")
-#define SortingCode NS_LITERAL_STRING("sortingCode")
-#define Organization NS_LITERAL_STRING("organization")
-#define Recipient NS_LITERAL_STRING("recipient")
-#define Phone NS_LITERAL_STRING("phone")
-
-#define PropertySpliter NS_LITERAL_STRING(";")
-#define KeyValueSpliter NS_LITERAL_STRING(":")
-#define AddressLineSpliter NS_LITERAL_STRING("%")
-
-#define EncodeBasicCardProperty(aPropertyName, aPropertyValue, aResult)        \
-  do {                                                                         \
-    if (!(aPropertyValue).IsEmpty()) {                                         \
-      (aResult) += (aPropertyName)                                             \
-                 + KeyValueSpliter                                             \
-                 + (aPropertyValue)                                            \
-                 + PropertySpliter;                                            \
-    }                                                                          \
-  } while(0)
-
-#define EncodeAddressProperty(aAddress, aPropertyName, aResult)                \
-  do {                                                                         \
-    nsAutoString propertyValue;                                                \
-    NS_ENSURE_SUCCESS((aAddress)->Get##aPropertyName(propertyValue),           \
-                                                     NS_ERROR_FAILURE);        \
-    EncodeBasicCardProperty((aPropertyName) ,propertyValue , (aResult));       \
-  } while(0)
-
-#define DecodeBasicCardProperty(aPropertyName, aPropertyValue,                 \
-                                aMatchPropertyName, aResponse)                 \
-  do {                                                                         \
-    if ((aPropertyName).Equals((aMatchPropertyName))) {                        \
-      (aResponse).m##aMatchPropertyName.Construct();                           \
-      (aResponse).m##aMatchPropertyName.Value() = (aPropertyValue);            \
-    }                                                                          \
-  } while(0)
-
-#define DecodeAddressProperty(aPropertyName, aPropertyValue,                   \
-                              aMatchPropertyName, aMatchPropertyValue)         \
-  do {                                                                         \
-    if ((aPropertyName).Equals((aMatchPropertyName))) {                        \
-      (aMatchPropertyValue) = (aPropertyValue);                                \
-    }                                                                          \
-  } while(0)
-
-#endif
-
 namespace {
-
 bool IsValidNetwork(const nsAString& aNetwork)
 {
-  return AMEX.Equals(aNetwork) ||
-         CARTEBANCAIRE.Equals(aNetwork) ||
-         DINERS.Equals(aNetwork) ||
-         DISCOVER.Equals(aNetwork) ||
-         JCB.Equals(aNetwork) ||
-         MASTERCARD.Equals(aNetwork) ||
-         MIR.Equals(aNetwork) ||
-         UNIONPAY.Equals(aNetwork) ||
-         VISA.Equals(aNetwork);
+  return aNetwork.Equals(NS_LITERAL_STRING("amex")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("cartebancaire")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("diners")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("discover")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("jcb")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("mastercard")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("mir")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("unionpay")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("visa"));
 }
-
-bool IsBasicCardKey(const nsAString& aKey)
-{
-  return CardholderName.Equals(aKey) ||
-         CardNumber.Equals(aKey) ||
-         ExpiryMonth.Equals(aKey) ||
-         ExpiryYear.Equals(aKey) ||
-         CardSecurityCode.Equals(aKey);
-}
-
-bool IsAddressKey(const nsAString& aKey)
-{
-  return Country.Equals(aKey) ||
-         AddressLine.Equals(aKey) ||
-         Region.Equals(aKey) ||
-         RegionCode.Equals(aKey) ||
-         City.Equals(aKey) ||
-         DependentLocality.Equals(aKey) ||
-         PostalCode.Equals(aKey) ||
-         SortingCode.Equals(aKey) ||
-         Organization.Equals(aKey) ||
-         Recipient.Equals(aKey) ||
-         Phone.Equals(aKey);
-}
-
 } // end of namespace
 
 
 StaticRefPtr<BasicCardService> gBasicCardService;
 
 already_AddRefed<BasicCardService>
 BasicCardService::GetService()
 {
@@ -218,185 +120,22 @@ BasicCardService::IsValidExpiryYear(cons
           aExpiryYear.CharAt(index) > '9') {
         return false;
       }
     }
   }
   return true;
 }
 
-nsresult
-BasicCardService::EncodeBasicCardData(const nsAString& aCardholderName,
-                                      const nsAString& aCardNumber,
-                                      const nsAString& aExpiryMonth,
-                                      const nsAString& aExpiryYear,
-                                      const nsAString& aCardSecurityCode,
-                                      nsIPaymentAddress* aBillingAddress,
-                                      nsAString& aResult)
-{
-  // aBillingAddress can be nullptr
-  if (aCardNumber.IsEmpty()) {
-    return NS_ERROR_FAILURE;
-  }
-  EncodeBasicCardProperty(CardholderName, aCardholderName, aResult);
-  EncodeBasicCardProperty(CardNumber, aCardNumber, aResult);
-  EncodeBasicCardProperty(ExpiryMonth, aExpiryMonth, aResult);
-  EncodeBasicCardProperty(ExpiryYear, aExpiryYear, aResult);
-  EncodeBasicCardProperty(CardSecurityCode, aCardSecurityCode, aResult);
-  if (!aBillingAddress) {
-    return NS_OK;
-  }
-  EncodeAddressProperty(aBillingAddress, Country, aResult);
-  nsCOMPtr<nsIArray> addressLine;
-  NS_ENSURE_SUCCESS(aBillingAddress->GetAddressLine(getter_AddRefs(addressLine)),
-                                                    NS_ERROR_FAILURE);
-  uint32_t length;
-  nsAutoString addressLineString;
-  NS_ENSURE_SUCCESS(addressLine->GetLength(&length), NS_ERROR_FAILURE);
-  for (uint32_t index = 0; index < length; ++index) {
-    nsCOMPtr<nsISupportsString> address = do_QueryElementAt(addressLine, index);
-    MOZ_ASSERT(address);
-    nsAutoString addressString;
-    NS_ENSURE_SUCCESS(address->GetData(addressString), NS_ERROR_FAILURE);
-    addressLineString += addressString + AddressLineSpliter;
-  }
-  EncodeBasicCardProperty(AddressLine ,addressLineString , aResult);
-  EncodeAddressProperty(aBillingAddress, Region, aResult);
-  EncodeAddressProperty(aBillingAddress, RegionCode, aResult);
-  EncodeAddressProperty(aBillingAddress, City, aResult);
-  EncodeAddressProperty(aBillingAddress, DependentLocality, aResult);
-  EncodeAddressProperty(aBillingAddress, PostalCode, aResult);
-  EncodeAddressProperty(aBillingAddress, SortingCode, aResult);
-  EncodeAddressProperty(aBillingAddress, Organization, aResult);
-  EncodeAddressProperty(aBillingAddress, Recipient, aResult);
-  EncodeAddressProperty(aBillingAddress, Phone, aResult);
-  return NS_OK;
-}
-
-nsresult
-BasicCardService::DecodeBasicCardData(const nsAString& aData,
-                                      nsPIDOMWindowInner* aWindow,
-                                      BasicCardResponse& aResponse)
-{
-  // aWindow can be nullptr
-  bool isBillingAddressPassed = false;
-  nsTArray<nsString> addressLine;
-  nsAutoString country;
-  nsAutoString region;
-  nsAutoString regionCode;
-  nsAutoString city;
-  nsAutoString dependentLocality;
-  nsAutoString postalCode;
-  nsAutoString sortingCode;
-  nsAutoString organization;
-  nsAutoString recipient;
-  nsAutoString phone;
-
-  nsCharSeparatedTokenizer propertyTokenizer(aData, PropertySpliter.CharAt(0));
-  while (propertyTokenizer.hasMoreTokens()) {
-    nsDependentSubstring property = propertyTokenizer.nextToken();
-    nsCharSeparatedTokenizer keyValueTokenizer(property, KeyValueSpliter.CharAt(0));
-    MOZ_ASSERT(keyValueTokenizer.hasMoreTokens());
-    nsDependentSubstring key = keyValueTokenizer.nextToken();
-    nsDependentSubstring value = keyValueTokenizer.nextToken();
-    if (IsAddressKey(key) && !isBillingAddressPassed) {
-      isBillingAddressPassed = true;
-    }
-    if (!IsAddressKey(key) && !IsBasicCardKey(key)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    if (key.Equals(CardNumber)) {
-      aResponse.mCardNumber = (value);
-    }
-
-    DecodeBasicCardProperty(key, value, CardholderName, aResponse);
-    DecodeBasicCardProperty(key, value, ExpiryMonth, aResponse);
-    DecodeBasicCardProperty(key, value, ExpiryYear, aResponse);
-    DecodeBasicCardProperty(key, value, CardSecurityCode, aResponse);
-
-    DecodeAddressProperty(key, value, Country, country);
-    DecodeAddressProperty(key, value, Region, region);
-    DecodeAddressProperty(key, value, RegionCode, regionCode);
-    DecodeAddressProperty(key, value, City, city);
-    DecodeAddressProperty(key, value, DependentLocality, dependentLocality);
-    DecodeAddressProperty(key, value, PostalCode, postalCode);
-    DecodeAddressProperty(key, value, SortingCode, sortingCode);
-    DecodeAddressProperty(key, value, Organization, organization);
-    DecodeAddressProperty(key, value, Recipient, recipient);
-    DecodeAddressProperty(key, value, Phone, phone);
-
-    if ((key).Equals(AddressLine)) {
-      nsCharSeparatedTokenizer addressTokenizer(value, AddressLineSpliter.CharAt(0));
-      while (addressTokenizer.hasMoreTokens()) {
-        addressLine.AppendElement(addressTokenizer.nextToken());
-      }
-    }
-  }
-  if (isBillingAddressPassed) {
-    aResponse.mBillingAddress.Construct();
-    aResponse.mBillingAddress.Value() = new PaymentAddress(aWindow,
-                                                           country,
-                                                           addressLine,
-                                                           region,
-                                                           regionCode,
-                                                           city,
-                                                           dependentLocality,
-                                                           postalCode,
-                                                           sortingCode,
-                                                           organization,
-                                                           recipient,
-                                                           phone);
-  }
-  return NS_OK;
-}
-
 bool
 BasicCardService::IsValidBasicCardErrors(JSContext* aCx,
                                          JSObject* aData)
 {
   if (!aData) {
     return true;
   }
   JS::RootedValue data(aCx, JS::ObjectValue(*aData));
 
   BasicCardErrors bcError;
   return !bcError.Init(aCx, data);
 }
-
-#ifdef PaymentBasicCardMacros
-#undef PaymentBasicCardMacros
-#undef EncodeBasicCardProperty
-#undef EncodeAddressProperty
-#undef DecodeBasicCardProperty
-#undef DecodeAddressProperty
-#undef AMEX
-#undef CARTEBANCAIRE
-#undef DINERS
-#undef DISCOVER
-#undef JCB
-#undef MASTERCARD
-#undef MIR
-#undef UNIONPAY
-#undef VISA
-#undef CardholderName
-#undef CardNumber
-#undef ExpiryMonth
-#undef ExpiryYear
-#undef CardSecurityCode
-#undef Country
-#undef AddressLine
-#undef Region
-#undef RegionCode
-#undef City
-#undef DependentLocality
-#undef PostalCode
-#undef SortingCode
-#undef Organization
-#undef Recipient
-#undef Phone
-#undef PropertySpliter
-#undef KeyValueSpliter
-#undef AddressLineSpliter
-#endif
-
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/BasicCardPayment.h
+++ b/dom/payments/BasicCardPayment.h
@@ -22,32 +22,16 @@ public:
 
   static already_AddRefed<BasicCardService> GetService();
 
   bool IsBasicCardPayment(const nsAString& aSupportedMethods);
   bool IsValidBasicCardRequest(JSContext* aCx, JSObject* aData, nsAString& aErrorMsg);
   bool IsValidBasicCardErrors(JSContext* aCx, JSObject* aData);
   bool IsValidExpiryMonth(const nsAString& aExpiryMonth);
   bool IsValidExpiryYear(const nsAString& aExpiryYear);
-/*
-  To let BasicCardResponse using the same data type with non-BasicCard response
-  in IPC transferring, following two methods is used to Encode/Decode the raw
-  data of BasicCardResponse.
-*/
-  nsresult EncodeBasicCardData(const nsAString& aCardholderName,
-                               const nsAString& aCardNumber,
-                               const nsAString& aExpiryMonth,
-                               const nsAString& aExpiryYear,
-                               const nsAString& aCardSecurityCode,
-                               nsIPaymentAddress* aBillingAddress,
-                               nsAString& aResult);
-
-  nsresult DecodeBasicCardData(const nsAString& aData,
-                               nsPIDOMWindowInner* aWindow,
-                               BasicCardResponse& aResponse);
 private:
   BasicCardService() = default;
   ~BasicCardService() = default;
 };
 
 } // end of namespace dom
 } // end of namespace mozilla
 
--- a/dom/payments/PaymentActionResponse.cpp
+++ b/dom/payments/PaymentActionResponse.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "PaymentActionResponse.h"
 #include "PaymentRequestUtils.h"
-#include "BasicCardPayment.h"
 
 namespace mozilla {
 namespace dom {
 
 /* PaymentResponseData */
 
 NS_IMPL_ISUPPORTS(PaymentResponseData, nsIPaymentResponseData)
 
@@ -73,19 +72,57 @@ NS_IMPL_ISUPPORTS_INHERITED(BasicCardRes
                             nsIBasicCardResponseData)
 
 BasicCardResponseData::BasicCardResponseData()
 {
   Init(nsIPaymentResponseData::BASICCARD_RESPONSE);
 }
 
 NS_IMETHODIMP
-BasicCardResponseData::GetData(nsAString& aData)
+BasicCardResponseData::GetCardholderName(nsAString& aCardholderName)
+{
+  aCardholderName = mCardholderName;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BasicCardResponseData::GetCardNumber(nsAString& aCardNumber)
+{
+  aCardNumber = mCardNumber;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BasicCardResponseData::GetExpiryMonth(nsAString& aExpiryMonth)
 {
-  aData = mData;
+  aExpiryMonth = mExpiryMonth;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BasicCardResponseData::GetExpiryYear(nsAString& aExpiryYear)
+{
+  aExpiryYear = mExpiryYear;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BasicCardResponseData::GetCardSecurityCode(nsAString& aCardSecurityCode)
+{
+  aCardSecurityCode = mCardSecurityCode;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BasicCardResponseData::GetBillingAddress(nsIPaymentAddress** aBillingAddress)
+{
+  NS_ENSURE_ARG_POINTER(aBillingAddress);
+  nsCOMPtr<nsIPaymentAddress> address;
+  address = mBillingAddress;
+  address.forget(aBillingAddress);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BasicCardResponseData::InitData(const nsAString& aCardholderName,
                                 const nsAString& aCardNumber,
                                 const nsAString& aExpiryMonth,
                                 const nsAString& aExpiryYear,
@@ -102,26 +139,24 @@ BasicCardResponseData::InitData(const ns
 
   if (!service->IsValidExpiryMonth(aExpiryMonth)) {
     return NS_ERROR_FAILURE;
   }
 
   if (!service->IsValidExpiryYear(aExpiryYear)) {
     return NS_ERROR_FAILURE;
   }
-  nsresult rv = service->EncodeBasicCardData(aCardholderName,
-                                             aCardNumber,
-                                             aExpiryMonth,
-                                             aExpiryYear,
-                                             aCardSecurityCode,
-                                             aBillingAddress,
-                                             mData);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
+
+  mCardholderName = aCardholderName;
+  mCardNumber = aCardNumber;
+  mExpiryMonth = aExpiryMonth;
+  mExpiryYear = aExpiryYear;
+  mCardSecurityCode = aCardSecurityCode;
+  mBillingAddress = aBillingAddress;
+
   return NS_OK;
 }
 
 /* PaymentActionResponse */
 
 NS_IMPL_ISUPPORTS(PaymentActionResponse,
                   nsIPaymentActionResponse)
 
@@ -197,19 +232,21 @@ PaymentShowActionResponse::GetAcceptStat
 NS_IMETHODIMP
 PaymentShowActionResponse::GetMethodName(nsAString& aMethodName)
 {
   aMethodName = mMethodName;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PaymentShowActionResponse::GetData(nsAString& aData)
+PaymentShowActionResponse::GetData(nsIPaymentResponseData** aData)
 {
-  aData = mData;
+  NS_ENSURE_ARG_POINTER(aData);
+  nsCOMPtr<nsIPaymentResponseData> data = mData;
+  data.forget(aData);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PaymentShowActionResponse::GetPayerName(nsAString& aPayerName)
 {
   aPayerName = mPayerName;
   return NS_OK;
@@ -253,38 +290,30 @@ PaymentShowActionResponse::Init(const ns
   if (aAcceptStatus == nsIPaymentActionResponse::PAYMENT_ACCEPTED) {
     uint32_t responseType;
     NS_ENSURE_SUCCESS(aData->GetType(&responseType), NS_ERROR_FAILURE);
     switch (responseType) {
       case nsIPaymentResponseData::GENERAL_RESPONSE: {
         if (isBasicCardPayment) {
           return NS_ERROR_FAILURE;
         }
-        nsCOMPtr<nsIGeneralResponseData> data = do_QueryInterface(aData);
-        MOZ_ASSERT(data);
-        NS_ENSURE_SUCCESS(data->GetData(mData), NS_ERROR_FAILURE);
         break;
       }
       case nsIPaymentResponseData::BASICCARD_RESPONSE: {
         if (!isBasicCardPayment) {
           return NS_ERROR_FAILURE;
         }
-        nsCOMPtr<nsIBasicCardResponseData> data = do_QueryInterface(aData);
-        MOZ_ASSERT(data);
-        NS_ENSURE_SUCCESS(data->GetData(mData), NS_ERROR_FAILURE);
         break;
       }
       default: {
         return NS_ERROR_FAILURE;
       }
     }
-    if (mData.IsEmpty()) {
-      return NS_ERROR_FAILURE;
-    }
   }
+  mData = aData;
   mPayerName = aPayerName;
   mPayerEmail = aPayerEmail;
   mPayerPhone = aPayerPhone;
   return NS_OK;
 }
 
 /* PaymentAbortActionResponse */
 
--- a/dom/payments/PaymentActionResponse.h
+++ b/dom/payments/PaymentActionResponse.h
@@ -52,17 +52,21 @@ public:
   NS_FORWARD_NSIPAYMENTRESPONSEDATA(PaymentResponseData::)
   NS_DECL_NSIBASICCARDRESPONSEDATA
 
   BasicCardResponseData();
 
 private:
   ~BasicCardResponseData() = default;
 
-  nsString mData;
+  nsString mCardholderName;
+  nsString mCardNumber;
+  nsString mExpiryMonth;
+  nsString mExpiryYear;
+  nsString mCardSecurityCode;
   nsCOMPtr<nsIPaymentAddress> mBillingAddress;
 };
 
 class PaymentActionResponse : public nsIPaymentActionResponse
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPAYMENTACTIONRESPONSE
@@ -102,17 +106,17 @@ public:
 
   PaymentShowActionResponse();
 
 private:
   ~PaymentShowActionResponse() = default;
 
   uint32_t mAcceptStatus;
   nsString mMethodName;
-  nsString mData;
+  nsCOMPtr<nsIPaymentResponseData> mData;
   nsString mPayerName;
   nsString mPayerEmail;
   nsString mPayerPhone;
 };
 
 class PaymentAbortActionResponse final : public nsIPaymentAbortActionResponse
                                        , public PaymentActionResponse
 {
--- a/dom/payments/PaymentRequest.cpp
+++ b/dom/payments/PaymentRequest.cpp
@@ -4,27 +4,27 @@
  * 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 "BasicCardPayment.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/FeaturePolicyUtils.h"
 #include "mozilla/dom/PaymentRequest.h"
 #include "mozilla/dom/PaymentRequestChild.h"
-#include "mozilla/dom/PaymentResponse.h"
+#include "mozilla/dom/PaymentRequestManager.h"
 #include "mozilla/intl/LocaleService.h"
 #include "mozilla/intl/MozLocale.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/StaticPrefs.h"
 #include "nsContentUtils.h"
 #include "nsIScriptError.h"
 #include "nsIURLParser.h"
 #include "nsNetCID.h"
-#include "PaymentRequestManager.h"
 #include "mozilla/dom/MerchantValidationEvent.h"
+#include "PaymentResponse.h"
 
 using mozilla::intl::LocaleService;
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(PaymentRequest)
 
@@ -792,17 +792,17 @@ PaymentRequest::RejectShowPayment(nsresu
     mAcceptPromise->MaybeReject(aRejectReason);
   }
   mState = eClosed;
   mAcceptPromise = nullptr;
 }
 
 void
 PaymentRequest::RespondShowPayment(const nsAString& aMethodName,
-                                   const nsAString& aDetails,
+                                   const ResponseData& aDetails,
                                    const nsAString& aPayerName,
                                    const nsAString& aPayerEmail,
                                    const nsAString& aPayerPhone,
                                    nsresult aRv)
 {
   MOZ_ASSERT(mAcceptPromise || mResponse);
   MOZ_ASSERT(mState == eInteractive);
 
@@ -883,17 +883,17 @@ PaymentRequest::RespondAbortPayment(bool
   //   => Reject |mAcceptPromise| and reset |mUpdateError| to complete
   //      the action, regardless of |aSuccess|.
   //
   // - Otherwise, we are handling |Abort| method call from merchant.
   //   => Resolve/Reject |mAbortPromise| based on |aSuccess|.
   if (NS_FAILED(mUpdateError)) {
     // Respond show with mUpdateError, set mUpdating to false.
     mUpdating = false;
-    RespondShowPayment(EmptyString(), EmptyString(), EmptyString(),
+    RespondShowPayment(EmptyString(), ResponseData(), EmptyString(),
                        EmptyString(), EmptyString(), mUpdateError);
     mUpdateError = NS_OK;
     return;
   }
 
   MOZ_ASSERT(mAbortPromise);
   MOZ_ASSERT(mState == eInteractive);
 
--- a/dom/payments/PaymentRequest.h
+++ b/dom/payments/PaymentRequest.h
@@ -18,16 +18,17 @@
 
 namespace mozilla {
 namespace dom {
 
 class EventHandlerNonNull;
 class PaymentAddress;
 class PaymentRequestChild;
 class PaymentResponse;
+class ResponseData;
 
 class PaymentRequest final
   : public DOMEventTargetHelper
   , public PromiseNativeHandler
   , public nsIDocumentActivity
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
@@ -91,17 +92,17 @@ public:
 
   already_AddRefed<Promise> CanMakePayment(ErrorResult& aRv);
   void RespondCanMakePayment(bool aResult);
 
   already_AddRefed<Promise> Show(
     const Optional<OwningNonNull<Promise>>& detailsPromise,
     ErrorResult& aRv);
   void RespondShowPayment(const nsAString& aMethodName,
-                          const nsAString& aDetails,
+                          const ResponseData& aDetails,
                           const nsAString& aPayerName,
                           const nsAString& aPayerEmail,
                           const nsAString& aPayerPhone,
                           nsresult aRv);
   void RejectShowPayment(nsresult aRejectReason);
   void RespondComplete();
 
   already_AddRefed<Promise> Abort(ErrorResult& aRv);
--- a/dom/payments/PaymentRequestManager.cpp
+++ b/dom/payments/PaymentRequestManager.cpp
@@ -1,23 +1,24 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "PaymentRequestManager.h"
-#include "PaymentRequestUtils.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/PaymentRequestChild.h"
 #include "mozilla/dom/TabChild.h"
-#include "mozilla/dom/PaymentRequestChild.h"
 #include "nsContentUtils.h"
 #include "nsString.h"
 #include "nsIPrincipal.h"
+#include "PaymentRequestManager.h"
+#include "PaymentRequestUtils.h"
+#include "PaymentResponse.h"
 
 namespace mozilla {
 namespace dom {
 namespace {
 
 /*
  *  Following Convert* functions are used for convert PaymentRequest structs
  *  to transferable structs for IPC.
@@ -244,16 +245,57 @@ ConvertOptions(const PaymentOptions& aOp
       PaymentShippingTypeValues::strings[shippingTypeIndex].value);
   }
   aIPCOption = IPCPaymentOptions(aOptions.mRequestPayerName,
                                  aOptions.mRequestPayerEmail,
                                  aOptions.mRequestPayerPhone,
                                  aOptions.mRequestShipping,
                                  shippingType);
 }
+
+void
+ConvertResponseData(const IPCPaymentResponseData& aIPCData,
+                    ResponseData& aData)
+{
+  switch (aIPCData.type()) {
+    case IPCPaymentResponseData::TIPCGeneralResponse : {
+      const IPCGeneralResponse& data = aIPCData;
+      GeneralData gData;
+      gData.data = data.data();
+      aData = gData;
+      break;
+    }
+    case IPCPaymentResponseData::TIPCBasicCardResponse: {
+      const IPCBasicCardResponse& data = aIPCData;
+      BasicCardData bData;
+      bData.cardholderName = data.cardholderName();
+      bData.cardNumber = data.cardNumber();
+      bData.expiryMonth = data.expiryMonth();
+      bData.expiryYear = data.expiryYear();
+      bData.cardSecurityCode = data.cardSecurityCode();
+      bData.billingAddress.country = data.billingAddress().country();
+      bData.billingAddress.addressLine = data.billingAddress().addressLine();
+      bData.billingAddress.region = data.billingAddress().region();
+      bData.billingAddress.regionCode = data.billingAddress().regionCode();
+      bData.billingAddress.city = data.billingAddress().city();
+      bData.billingAddress.dependentLocality =
+        data.billingAddress().dependentLocality();
+      bData.billingAddress.postalCode = data.billingAddress().postalCode();
+      bData.billingAddress.sortingCode = data.billingAddress().sortingCode();
+      bData.billingAddress.organization = data.billingAddress().organization();
+      bData.billingAddress.recipient = data.billingAddress().recipient();
+      bData.billingAddress.phone = data.billingAddress().phone();
+      aData = bData;
+      break;
+    }
+    default: {
+      break;
+    }
+  }
+}
 } // end of namespace
 
 /* PaymentRequestManager */
 
 StaticRefPtr<PaymentRequestManager> gPaymentManager;
 
 PaymentRequestChild*
 PaymentRequestManager::GetPaymentChild(PaymentRequest* aRequest)
@@ -584,16 +626,18 @@ PaymentRequestManager::RespondPayment(Pa
       const IPCPaymentCanMakeActionResponse& response = aResponse;
       aRequest->RespondCanMakePayment(response.result());
       NotifyRequestDone(aRequest);
       break;
     }
     case IPCPaymentActionResponse::TIPCPaymentShowActionResponse: {
       const IPCPaymentShowActionResponse& response = aResponse;
       nsresult rejectedReason = NS_ERROR_DOM_ABORT_ERR;
+      ResponseData responseData;
+      ConvertResponseData(response.data(), responseData);
       switch (response.status()) {
         case nsIPaymentActionResponse::PAYMENT_ACCEPTED: {
           rejectedReason = NS_OK;
           break;
         }
         case nsIPaymentActionResponse::PAYMENT_REJECTED: {
           rejectedReason = NS_ERROR_DOM_ABORT_ERR;
           break;
@@ -603,17 +647,17 @@ PaymentRequestManager::RespondPayment(Pa
           break;
         }
         default: {
           rejectedReason = NS_ERROR_UNEXPECTED;
           break;
         }
       }
       aRequest->RespondShowPayment(response.methodName(),
-                                   response.data(),
+                                   responseData,
                                    response.payerName(),
                                    response.payerEmail(),
                                    response.payerPhone(),
                                    rejectedReason);
       if (NS_FAILED(rejectedReason)) {
         NotifyRequestDone(aRequest);
       }
       break;
--- a/dom/payments/PaymentResponse.cpp
+++ b/dom/payments/PaymentResponse.cpp
@@ -46,17 +46,17 @@ NS_IMPL_ADDREF_INHERITED(PaymentResponse
 NS_IMPL_RELEASE_INHERITED(PaymentResponse, DOMEventTargetHelper)
 
 PaymentResponse::PaymentResponse(nsPIDOMWindowInner* aWindow,
                                  PaymentRequest* aRequest,
                                  const nsAString& aRequestId,
                                  const nsAString& aMethodName,
                                  const nsAString& aShippingOption,
                                  PaymentAddress* aShippingAddress,
-                                 const nsAString& aDetails,
+                                 const ResponseData& aDetails,
                                  const nsAString& aPayerName,
                                  const nsAString& aPayerEmail,
                                  const nsAString& aPayerPhone)
   : DOMEventTargetHelper(aWindow)
   , mCompleteCalled(false)
   , mRequest(aRequest)
   , mRequestId(aRequestId)
   , mMethodName(aMethodName)
@@ -96,33 +96,80 @@ PaymentResponse::GetMethodName(nsString&
 {
   aRetVal = mMethodName;
 }
 
 void
 PaymentResponse::GetDetails(JSContext* aCx,
                             JS::MutableHandle<JSObject*> aRetVal) const
 {
-  RefPtr<BasicCardService> service = BasicCardService::GetService();
-  MOZ_ASSERT(service);
-  if (!service->IsBasicCardPayment(mMethodName)) {
-    DeserializeToJSObject(mDetails, aCx, aRetVal);
-  } else {
-    BasicCardResponse response;
-    nsresult rv = service->DecodeBasicCardData(mDetails, GetOwner(), response);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
+  switch(mDetails.type()) {
+    case ResponseData::GeneralResponse: {
+      const GeneralData& rawData = mDetails.generalData();
+      DeserializeToJSObject(rawData.data, aCx, aRetVal);
+      break;
     }
-
-    MOZ_ASSERT(aCx);
-    JS::RootedValue value(aCx);
-    if (NS_WARN_IF(!response.ToObjectInternal(aCx, &value))) {
-      return;
+    case ResponseData::BasicCardResponse: {
+      const BasicCardData& rawData = mDetails.basicCardData();
+      BasicCardResponse basicCardResponse;
+      if (!rawData.cardholderName.IsEmpty()) {
+        basicCardResponse.mCardholderName.Construct();
+        basicCardResponse.mCardholderName.Value() = rawData.cardholderName;
+      }
+      basicCardResponse.mCardNumber = rawData.cardNumber;
+      if (!rawData.expiryMonth.IsEmpty()) {
+        basicCardResponse.mExpiryMonth.Construct();
+        basicCardResponse.mExpiryMonth.Value() = rawData.expiryMonth;
+      }
+      if (!rawData.expiryYear.IsEmpty()) {
+        basicCardResponse.mExpiryYear.Construct();
+        basicCardResponse.mExpiryYear.Value() = rawData.expiryYear;
+      }
+      if (!rawData.cardSecurityCode.IsEmpty()) {
+        basicCardResponse.mCardSecurityCode.Construct();
+        basicCardResponse.mCardSecurityCode.Value() = rawData.cardSecurityCode;
+      }
+      if (!rawData.billingAddress.country.IsEmpty() ||
+          !rawData.billingAddress.addressLine.IsEmpty() ||
+          !rawData.billingAddress.region.IsEmpty() ||
+          !rawData.billingAddress.regionCode.IsEmpty() ||
+          !rawData.billingAddress.city.IsEmpty() ||
+          !rawData.billingAddress.dependentLocality.IsEmpty() ||
+          !rawData.billingAddress.postalCode.IsEmpty() ||
+          !rawData.billingAddress.sortingCode.IsEmpty() ||
+          !rawData.billingAddress.organization.IsEmpty() ||
+          !rawData.billingAddress.recipient.IsEmpty() ||
+          !rawData.billingAddress.phone.IsEmpty()) {
+        basicCardResponse.mBillingAddress.Construct();
+        basicCardResponse.mBillingAddress.Value() =
+          new PaymentAddress(GetOwner(),
+                             rawData.billingAddress.country,
+                             rawData.billingAddress.addressLine,
+                             rawData.billingAddress.region,
+                             rawData.billingAddress.regionCode,
+                             rawData.billingAddress.city,
+                             rawData.billingAddress.dependentLocality,
+                             rawData.billingAddress.postalCode,
+                             rawData.billingAddress.sortingCode,
+                             rawData.billingAddress.organization,
+                             rawData.billingAddress.recipient,
+                             rawData.billingAddress.phone);
+      }
+      MOZ_ASSERT(aCx);
+      JS::RootedValue value(aCx);
+      if (NS_WARN_IF(!basicCardResponse.ToObjectInternal(aCx, &value))) {
+        return;
+      }
+      aRetVal.set(&value.toObject());
+      break;
     }
-    aRetVal.set(&value.toObject());
+    default: {
+      MOZ_ASSERT(false);
+      break;
+    }
   }
 }
 
 void
 PaymentResponse::GetShippingOption(nsString& aRetVal) const
 {
   aRetVal = mShippingOption;
 }
@@ -270,17 +317,17 @@ PaymentResponse::Retry(JSContext* aCx,
   mRetryPromise = promise;
   return promise.forget();
 }
 
 void
 PaymentResponse::RespondRetry(const nsAString& aMethodName,
                               const nsAString& aShippingOption,
                               PaymentAddress* aShippingAddress,
-                              const nsAString& aDetails,
+                              const ResponseData& aDetails,
                               const nsAString& aPayerName,
                               const nsAString& aPayerEmail,
                               const nsAString& aPayerPhone)
 {
   mMethodName = aMethodName;
   mShippingOption = aShippingOption;
   mShippingAddress = aShippingAddress;
   mDetails = aDetails;
--- a/dom/payments/PaymentResponse.h
+++ b/dom/payments/PaymentResponse.h
@@ -14,16 +14,93 @@
 
 namespace mozilla {
 namespace dom {
 
 class PaymentAddress;
 class PaymentRequest;
 class Promise;
 
+class GeneralData final
+{
+public:
+  GeneralData() = default;
+  ~GeneralData() = default;
+  nsString data;
+};
+
+class BasicCardData final
+{
+public:
+  struct Address {
+    nsString country;
+    nsTArray<nsString> addressLine;
+    nsString region;
+    nsString regionCode;
+    nsString city;
+    nsString dependentLocality;
+    nsString postalCode;
+    nsString sortingCode;
+    nsString organization;
+    nsString recipient;
+    nsString phone;
+  };
+  BasicCardData() = default;
+  ~BasicCardData() = default;
+
+  nsString cardholderName;
+  nsString cardNumber;
+  nsString expiryMonth;
+  nsString expiryYear;
+  nsString cardSecurityCode;
+  Address billingAddress;
+};
+
+class ResponseData final
+{
+public:
+  enum Type {
+    Unknown = 0,
+    GeneralResponse = 1,
+    BasicCardResponse
+  };
+  ResponseData()
+    : mType(ResponseData::Unknown)
+  {}
+  explicit ResponseData(const GeneralData& aGeneralData)
+    : mType(GeneralResponse)
+    , mGeneralData(aGeneralData)
+  {}
+  explicit ResponseData(const BasicCardData& aBasicCardData)
+    : mType(BasicCardResponse)
+    , mBasicCardData(aBasicCardData)
+  {}
+  ResponseData& operator = (const GeneralData& aGeneralData) {
+    mType = GeneralResponse;
+    mGeneralData = aGeneralData;
+    mBasicCardData = BasicCardData();
+    return *this;
+  }
+  ResponseData& operator = (const BasicCardData& aBasicCardData) {
+    mType = BasicCardResponse;
+    mGeneralData = GeneralData();
+    mBasicCardData = aBasicCardData;
+    return *this;
+  }
+  virtual ~ResponseData() = default;
+
+  const Type& type() const { return mType; }
+  const GeneralData& generalData() const { return mGeneralData; }
+  const BasicCardData& basicCardData() const { return mBasicCardData;}
+private:
+  Type mType;
+  GeneralData mGeneralData;
+  BasicCardData mBasicCardData;
+};
+
 class PaymentResponse final
   : public DOMEventTargetHelper
   , public nsITimerCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PaymentResponse,
@@ -32,17 +109,17 @@ public:
   NS_IMETHOD Notify(nsITimer* aTimer) override;
 
   PaymentResponse(nsPIDOMWindowInner* aWindow,
                   PaymentRequest* aRequest,
                   const nsAString& aRequestId,
                   const nsAString& aMethodName,
                   const nsAString& aShippingOption,
                   PaymentAddress* aShippingAddress,
-                  const nsAString& aDetails,
+                  const ResponseData& aDetails,
                   const nsAString& aPayerName,
                   const nsAString& aPayerEmail,
                   const nsAString& aPayerPhone);
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   void GetRequestId(nsString& aRetVal) const;
@@ -75,17 +152,17 @@ public:
 
   already_AddRefed<Promise> Retry(JSContext* aCx,
                                   const PaymentValidationErrors& errorField,
                                   ErrorResult& aRv);
 
   void RespondRetry(const nsAString& aMethodName,
                     const nsAString& aShippingOption,
                     PaymentAddress* aShippingAddress,
-                    const nsAString& aDetails,
+                    const ResponseData& aDetails,
                     const nsAString& aPayerName,
                     const nsAString& aPayerEmail,
                     const nsAString& aPayerPhone);
   void RejectRetry(nsresult aRejectReason);
 
 protected:
   ~PaymentResponse();
 
@@ -98,17 +175,17 @@ protected:
 
   nsresult DispatchUpdateEvent(const nsAString& aType);
 
 private:
   bool mCompleteCalled;
   PaymentRequest* mRequest;
   nsString mRequestId;
   nsString mMethodName;
-  nsString mDetails;
+  ResponseData mDetails;
   nsString mShippingOption;
   nsString mPayerName;
   nsString mPayerEmail;
   nsString mPayerPhone;
   RefPtr<PaymentAddress> mShippingAddress;
   // Promise for "PaymentResponse::Complete"
   RefPtr<Promise> mPromise;
   // Timer for timing out if the page doesn't call
--- a/dom/payments/ipc/PPaymentRequest.ipdl
+++ b/dom/payments/ipc/PPaymentRequest.ipdl
@@ -133,22 +133,58 @@ union IPCPaymentActionRequest
 };
 
 struct IPCPaymentCanMakeActionResponse
 {
   nsString requestId;
   bool result;
 };
 
+struct IPCPaymentAddress
+{
+  nsString country;
+  nsString[] addressLine;
+  nsString region;
+  nsString regionCode;
+  nsString city;
+  nsString dependentLocality;
+  nsString postalCode;
+  nsString sortingCode;
+  nsString organization;
+  nsString recipient;
+  nsString phone;
+};
+
+struct IPCGeneralResponse
+{
+  nsString data;
+};
+
+struct IPCBasicCardResponse
+{
+  nsString cardholderName;
+  nsString cardNumber;
+  nsString expiryMonth;
+  nsString expiryYear;
+  nsString cardSecurityCode;
+  IPCPaymentAddress billingAddress;
+};
+
+union IPCPaymentResponseData
+{
+  IPCGeneralResponse;
+  IPCBasicCardResponse;
+};
+
 struct IPCPaymentShowActionResponse
 {
   nsString requestId;
   uint32_t status;
   nsString methodName;
-  nsString data;
+  IPCPaymentResponseData data;
   nsString payerName;
   nsString payerEmail;
   nsString payerPhone;
 };
 
 struct IPCPaymentAbortActionResponse
 {
   nsString requestId;
@@ -164,31 +200,16 @@ struct IPCPaymentCompleteActionResponse
 union IPCPaymentActionResponse
 {
   IPCPaymentCanMakeActionResponse;
   IPCPaymentShowActionResponse;
   IPCPaymentAbortActionResponse;
   IPCPaymentCompleteActionResponse;
 };
 
-struct IPCPaymentAddress
-{
-  nsString country;
-  nsString[] addressLine;
-  nsString region;
-  nsString regionCode;
-  nsString city;
-  nsString dependentLocality;
-  nsString postalCode;
-  nsString sortingCode;
-  nsString organization;
-  nsString recipient;
-  nsString phone;
-};
-
 sync protocol PPaymentRequest
 {
   manager PBrowser;
 
 parent:
   async __delete__();
 
   async RequestPayment(IPCPaymentActionRequest aAction);
--- a/dom/payments/ipc/PaymentRequestParent.cpp
+++ b/dom/payments/ipc/PaymentRequestParent.cpp
@@ -133,28 +133,37 @@ PaymentRequestParent::RespondPayment(nsI
     case nsIPaymentActionResponse::SHOW_ACTION: {
       nsCOMPtr<nsIPaymentShowActionResponse> response =
         do_QueryInterface(aResponse);
       MOZ_ASSERT(response);
       uint32_t acceptStatus;
       NS_ENSURE_SUCCESS(response->GetAcceptStatus(&acceptStatus), NS_ERROR_FAILURE);
       nsAutoString methodName;
       NS_ENSURE_SUCCESS(response->GetMethodName(methodName), NS_ERROR_FAILURE);
-      nsAutoString data;
-      NS_ENSURE_SUCCESS(response->GetData(data), NS_ERROR_FAILURE);
+      IPCPaymentResponseData ipcData;
+      if (acceptStatus == nsIPaymentActionResponse::PAYMENT_ACCEPTED) {
+        nsCOMPtr<nsIPaymentResponseData> data;
+        NS_ENSURE_SUCCESS(response->GetData(getter_AddRefs(data)),
+                          NS_ERROR_FAILURE);
+        MOZ_ASSERT(data);
+        NS_ENSURE_SUCCESS(SerializeResponseData(ipcData, data), NS_ERROR_FAILURE);
+      } else {
+        ipcData = IPCGeneralResponse();
+      }
+
       nsAutoString payerName;
       NS_ENSURE_SUCCESS(response->GetPayerName(payerName), NS_ERROR_FAILURE);
       nsAutoString payerEmail;
       NS_ENSURE_SUCCESS(response->GetPayerEmail(payerEmail), NS_ERROR_FAILURE);
       nsAutoString payerPhone;
       NS_ENSURE_SUCCESS(response->GetPayerPhone(payerPhone), NS_ERROR_FAILURE);
       IPCPaymentShowActionResponse actionResponse(requestId,
                                                   acceptStatus,
                                                   methodName,
-                                                  data,
+                                                  ipcData,
                                                   payerName,
                                                   payerEmail,
                                                   payerPhone);
       if (!SendRespondPayment(actionResponse)) {
         return NS_ERROR_FAILURE;
       }
       break;
     }
@@ -204,77 +213,21 @@ PaymentRequestParent::ChangeShippingAddr
     {
       self->ChangeShippingAddress(requestId, address);
     });
     return NS_DispatchToMainThread(r);
   }
   if (!mActorAlive) {
     return NS_ERROR_FAILURE;
   }
-  nsAutoString country;
-  nsresult rv = aAddress->GetCountry(country);
-  NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIArray> iaddressLine;
-  rv = aAddress->GetAddressLine(getter_AddRefs(iaddressLine));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString region;
-  rv = aAddress->GetRegion(region);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString regionCode;
-  rv = aAddress->GetRegionCode(regionCode);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString city;
-  rv = aAddress->GetCity(city);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString dependentLocality;
-  rv = aAddress->GetDependentLocality(dependentLocality);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString postalCode;
-  rv = aAddress->GetPostalCode(postalCode);
+  IPCPaymentAddress ipcAddress;
+  nsresult rv = SerializeAddress(ipcAddress, aAddress);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsAutoString sortingCode;
-  rv = aAddress->GetSortingCode(sortingCode);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString organization;
-  rv = aAddress->GetOrganization(organization);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString recipient;
-  rv = aAddress->GetRecipient(recipient);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString phone;
-  rv = aAddress->GetPhone(phone);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsTArray<nsString> addressLine;
-  uint32_t length;
-  rv = iaddressLine->GetLength(&length);
-  NS_ENSURE_SUCCESS(rv, rv);
-  for (uint32_t index = 0; index < length; ++index) {
-    nsCOMPtr<nsISupportsString> iaddress = do_QueryElementAt(iaddressLine, index);
-    MOZ_ASSERT(iaddress);
-    nsAutoString address;
-    rv = iaddress->GetData(address);
-    NS_ENSURE_SUCCESS(rv, rv);
-    addressLine.AppendElement(address);
-  }
-
-  IPCPaymentAddress ipcAddress(country, addressLine, region, regionCode, city,
-                               dependentLocality, postalCode, sortingCode,
-                               organization, recipient, phone);
-
   nsAutoString requestId(aRequestId);
   if (!SendChangeShippingAddress(requestId, ipcAddress)) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
@@ -355,10 +308,127 @@ PaymentRequestParent::ActorDestroy(Actor
       return;
     }
     payments::PaymentRequest* rowRequest =
       static_cast<payments::PaymentRequest*>(request.get());
     MOZ_ASSERT(rowRequest);
     rowRequest->SetIPC(nullptr);
   }
 }
+
+nsresult
+PaymentRequestParent::SerializeAddress(IPCPaymentAddress& aIPCAddress,
+                                       nsIPaymentAddress* aAddress)
+{
+  // address can be nullptr
+  if (!aAddress) {
+    return NS_OK;
+  }
+  nsAutoString country;
+  nsresult rv = aAddress->GetCountry(country);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIArray> iaddressLine;
+  rv = aAddress->GetAddressLine(getter_AddRefs(iaddressLine));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString region;
+  rv = aAddress->GetRegion(region);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString regionCode;
+  rv = aAddress->GetRegionCode(regionCode);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString city;
+  rv = aAddress->GetCity(city);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString dependentLocality;
+  rv = aAddress->GetDependentLocality(dependentLocality);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString postalCode;
+  rv = aAddress->GetPostalCode(postalCode);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString sortingCode;
+  rv = aAddress->GetSortingCode(sortingCode);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString organization;
+  rv = aAddress->GetOrganization(organization);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString recipient;
+  rv = aAddress->GetRecipient(recipient);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString phone;
+  rv = aAddress->GetPhone(phone);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsTArray<nsString> addressLine;
+  uint32_t length;
+  rv = iaddressLine->GetLength(&length);
+  NS_ENSURE_SUCCESS(rv, rv);
+  for (uint32_t index = 0; index < length; ++index) {
+    nsCOMPtr<nsISupportsString> iaddress = do_QueryElementAt(iaddressLine, index);
+    MOZ_ASSERT(iaddress);
+    nsAutoString address;
+    rv = iaddress->GetData(address);
+    NS_ENSURE_SUCCESS(rv, rv);
+    addressLine.AppendElement(address);
+  }
+
+  aIPCAddress = IPCPaymentAddress(country, addressLine, region, regionCode, city,
+                                  dependentLocality, postalCode, sortingCode,
+                                  organization, recipient, phone);
+  return NS_OK;
+}
+
+nsresult
+PaymentRequestParent::SerializeResponseData(IPCPaymentResponseData& aIPCData,
+                                            nsIPaymentResponseData* aData)
+{
+  NS_ENSURE_ARG_POINTER(aData);
+  uint32_t dataType;
+  NS_ENSURE_SUCCESS(aData->GetType(&dataType), NS_ERROR_FAILURE);
+  switch(dataType) {
+    case nsIPaymentResponseData::GENERAL_RESPONSE: {
+      nsCOMPtr<nsIGeneralResponseData> response = do_QueryInterface(aData);
+      MOZ_ASSERT(response);
+      IPCGeneralResponse data;
+      NS_ENSURE_SUCCESS(response->GetData(data.data()), NS_ERROR_FAILURE);
+      aIPCData = data;
+      break;
+    }
+    case nsIPaymentResponseData::BASICCARD_RESPONSE: {
+      nsCOMPtr<nsIBasicCardResponseData> response = do_QueryInterface(aData);
+      MOZ_ASSERT(response);
+      IPCBasicCardResponse data;
+      NS_ENSURE_SUCCESS(response->GetCardholderName(data.cardholderName()),
+                        NS_ERROR_FAILURE);
+      NS_ENSURE_SUCCESS(response->GetCardNumber(data.cardNumber()),
+                        NS_ERROR_FAILURE);
+      NS_ENSURE_SUCCESS(response->GetExpiryMonth(data.expiryMonth()),
+                        NS_ERROR_FAILURE);
+      NS_ENSURE_SUCCESS(response->GetExpiryYear(data.expiryYear()),
+                        NS_ERROR_FAILURE);
+      NS_ENSURE_SUCCESS(response->GetCardSecurityCode(data.cardSecurityCode()),
+                        NS_ERROR_FAILURE);
+      nsCOMPtr<nsIPaymentAddress> address;
+      NS_ENSURE_SUCCESS(response->GetBillingAddress(getter_AddRefs(address)),
+                        NS_ERROR_FAILURE);
+      IPCPaymentAddress ipcAddress;
+      NS_ENSURE_SUCCESS(SerializeAddress(ipcAddress, address), NS_ERROR_FAILURE);
+      data.billingAddress() = ipcAddress;
+      aIPCData = data;
+      break;
+    }
+    default: {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  return NS_OK;
+}
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/ipc/PaymentRequestParent.h
+++ b/dom/payments/ipc/PaymentRequestParent.h
@@ -36,16 +36,21 @@ protected:
   RecvRequestPayment(const IPCPaymentActionRequest& aRequest) override;
 
   mozilla::ipc::IPCResult Recv__delete__() override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 private:
   ~PaymentRequestParent() = default;
 
+  nsresult SerializeAddress(IPCPaymentAddress& ipcAddress,
+                            nsIPaymentAddress* aAddress);
+  nsresult SerializeResponseData(IPCPaymentResponseData& ipcData,
+                                 nsIPaymentResponseData* aData);
+
   bool mActorAlive;
   uint64_t mTabId;
   nsString mRequestId;
 };
 
 } // end of namespace dom
 } // end of namespace mozilla
 
--- a/dom/payments/test/BasiccardChromeScript.js
+++ b/dom/payments/test/BasiccardChromeScript.js
@@ -24,16 +24,34 @@ billingAddress.init("USA",              
                      "San Bruno",        // city
                      "",                 // dependent locality
                      "94066",            // postal code
                      "123456",           // sorting code
                      "",                 // organization
                      "Bill A. Pacheco",  // recipient
                      "+14344413879"); // phone
 
+const specialAddress = Cc["@mozilla.org/dom/payments/payment-address;1"].
+                           createInstance(Ci.nsIPaymentAddress);
+const specialAddressLine = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+const specialData = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+specialData.data = ":$%@&*";
+specialAddressLine.appendElement(specialData);
+specialAddress.init("USA",               // country
+                     specialAddressLine, // address line
+                     "CA",               // region
+                     "CA",               // region code
+                     "San Bruno",        // city
+                     "",                 // dependent locality
+                     "94066",            // postal code
+                     "123456",           // sorting code
+                     "",                 // organization
+                     "Bill A. Pacheco",  // recipient
+                     "+14344413879"); // phone
+
 const basiccardResponseData = Cc["@mozilla.org/dom/payments/basiccard-response-data;1"].
                                  createInstance(Ci.nsIBasicCardResponseData);
 
 const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
                         createInstance(Ci.nsIPaymentShowActionResponse);
 
 function abortPaymentResponse(requestId) {
   let abortResponse = Cc["@mozilla.org/dom/payments/payment-abort-action-response;1"].
@@ -104,24 +122,58 @@ const simpleResponseUI = {
   completePayment: completePaymentResponse,
   updatePayment: function(requestId) {
   },
   closePayment: function(requestId) {
   },
   QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
 };
 
+const specialAddressUI = {
+  showPayment: function(requestId) {
+    try {
+      basiccardResponseData.initData("Bill A. Pacheco",  // cardholderName
+                                     "4916855166538720", // cardNumber
+                                     "01",               // expiryMonth
+                                     "2024",             // expiryYear
+                                     "180",              // cardSecurityCode
+                                     specialAddress);    // billingAddress
+    } catch (e) {
+      emitTestFail("Fail to initialize basic card response data.");
+    }
+    showResponse.init(requestId,
+                      Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+                      "basic-card",         // payment method
+                      basiccardResponseData,// payment method data
+                      "Bill A. Pacheco",    // payer name
+                      "",                   // payer email
+                      "");                  // payer phone
+    paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+  },
+  abortPayment: abortPaymentResponse,
+  completePayment: completePaymentResponse,
+  updatePayment: function(requestId) {
+  },
+  closePayment: function (requestId) {
+  },
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
+};
+
 addMessageListener("set-detailed-ui-service", function() {
   paymentSrv.setTestingUIService(detailedResponseUI.QueryInterface(Ci.nsIPaymentUIService));
 });
 
 addMessageListener("set-simple-ui-service", function() {
   paymentSrv.setTestingUIService(simpleResponseUI.QueryInterface(Ci.nsIPaymentUIService));
 });
 
+addMessageListener("set-special-address-ui-service", function() {
+  paymentSrv.setTestingUIService(specialAddressUI.QueryInterface(Ci.nsIPaymentUIService));
+});
+
 addMessageListener("error-response-test", function() {
   // test empty cardNumber
   try {
     basiccardResponseData.initData("", "", "", "", "", null);
     emitTestFail("BasicCardResponse should not be initialized with empty cardNumber.");
   } catch (e) {
     if (e.name != "NS_ERROR_FAILURE") {
       emitTestFail("Empty cardNumber expected 'NS_ERROR_FAILURE', but got " + e.name + ".");
--- a/dom/payments/test/test_basiccard.html
+++ b/dom/payments/test/test_basiccard.html
@@ -236,16 +236,34 @@ https://bugzilla.mozilla.org/show_bug.cg
         });
       }).catch( e => {
         ok(false, "Unexpected error: " + e.name);
         resolve();
       }).finally(handler.destruct);
     });
   }
 
+  function testSpecialAddressResponse() {
+    const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+    gScript.sendAsyncMessage("set-special-address-ui-service");
+    return new Promise((resolve, reject) => {
+      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      payRequest.show().then(response => {
+        ok(response.details, "BasiccardResponse should exist.");
+        ok(response.details.billingAddress,
+           "BasiccardResponse.billingAddress should exist.");
+        is(response.details.billingAddress.addressLine[0], ":$%@&*",
+           "AddressLine should be ':$%@&*'");
+        response.complete("success").then(()=>{
+          resolve();
+        });
+      }).finally(handler.destruct);
+    });
+  }
+
   function testBasicCardErrorResponse() {
     return new Promise((resolve, reject) => {
       gScript.addMessageListener("error-response-complete",
                                  function errorResponseCompleteHandler() {
         gScript.removeMessageListener("error-response-complete",
                                       errorResponseCompleteHandler);
         resolve();
       });
@@ -267,16 +285,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     testBasicCardRequestWithErrorTypes()
     .then(testBasicCardRequestWithErrorNetworks)
     .then(testBasicCardRequestWithUnconvertableData)
     .then(testBasicCardRequestWithNullData)
     .then(testBasicCardRequestWithEmptyData)
     .then(testCanMakePaymentWithBasicCardRequest)
     .then(testBasicCardSimpleResponse)
     .then(testBasicCardDetailedResponse)
+    .then(testSpecialAddressResponse)
     .then(testBasicCardErrorResponse)
     .then(teardown)
     .catch( e => {
       ok(false, "Unexpected error: " + e.name);
       SimpleTest.finish();
     });
   }
 
--- a/dom/serviceworkers/ServiceWorker.cpp
+++ b/dom/serviceworkers/ServiceWorker.cpp
@@ -13,16 +13,17 @@
 #include "ServiceWorkerImpl.h"
 #include "ServiceWorkerManager.h"
 #include "ServiceWorkerPrivate.h"
 #include "ServiceWorkerRegistration.h"
 #include "ServiceWorkerUtils.h"
 
 #include "mozilla/dom/ClientIPCTypes.h"
 #include "mozilla/dom/ClientState.h"
+#include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/StaticPrefs.h"
 
 #ifdef XP_WIN
 #undef PostMessage
 #endif
@@ -225,16 +226,23 @@ ServiceWorker::PostMessage(JSContext* aC
   data->Write(aCx, aMessage, transferable, aRv);
   if (aRv.Failed()) {
     return;
   }
 
   mInner->PostMessage(std::move(data), clientInfo.ref(), clientState.ref());
 }
 
+void
+ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                           const PostMessageOptions& aOptions,
+                           ErrorResult& aRv)
+{
+  PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
+}
 
 const ServiceWorkerDescriptor&
 ServiceWorker::Descriptor() const
 {
   return mDescriptor;
 }
 
 void
--- a/dom/serviceworkers/ServiceWorker.h
+++ b/dom/serviceworkers/ServiceWorker.h
@@ -16,16 +16,17 @@
 #undef PostMessage
 #endif
 
 class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
+struct PostMessageOptions;
 class ServiceWorkerCloneData;
 
 #define NS_DOM_SERVICEWORKER_IID \
   {0xd42e0611, 0x3647, 0x4319, {0xae, 0x05, 0x19, 0x89, 0x59, 0xba, 0x99, 0x5e}}
 
 bool
 ServiceWorkerVisible(JSContext* aCx, JSObject* aObj);
 
@@ -94,16 +95,20 @@ public:
 
   void
   GetScriptURL(nsString& aURL) const;
 
   void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
               const Sequence<JSObject*>& aTransferable, ErrorResult& aRv);
 
+  void
+  PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+              const PostMessageOptions& aOptions, ErrorResult& aRv);
+
   const ServiceWorkerDescriptor&
   Descriptor() const;
 
   void
   DisconnectFromOwner() override;
 
 private:
   ServiceWorker(nsIGlobalObject* aWindow,
deleted file mode 100644
--- a/dom/tests/mochitest/whatwg/browserFu.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
- * File which includes stuff for Mozilla-specific checks which shouldn't happen
- * in other browsers but which we wish to test.
- */
-
-var isMozilla = navigator.product === "Gecko" && "buildID" in navigator;
--- a/dom/tests/mochitest/whatwg/mochitest.ini
+++ b/dom/tests/mochitest/whatwg/mochitest.ini
@@ -1,11 +1,10 @@
 [DEFAULT]
 support-files =
-  browserFu.js
   postMessage_chrome_helper.html
   postMessage_closed_helper.html
   postMessage_hash.html
   postMessage_helper.html
   postMessage_idn_helper.html
   postMessage_joined_helper2.html
   postMessage_joined_helper.html
   postMessage_onOther.html
--- a/dom/tests/mochitest/whatwg/postMessage_helper.html
+++ b/dom/tests/mochitest/whatwg/postMessage_helper.html
@@ -1,13 +1,12 @@
 <!DOCTYPE html>
 <html>
 <head>
   <title>postMessage message receiver</title>
-  <script type="application/javascript" src="browserFu.js"></script>
   <script type="application/javascript">
     function $(id) { return document.getElementById(id); }
 
     function setup()
     {
       var target = $("domain");
       target.textContent = location.hostname + ":" + (location.port || 80);
     }
@@ -21,22 +20,16 @@
 
       if (evt.source !== window.parent)
       {
         response += " unexpected-source(" + evt.source + ")";
         response += " window-parent-is(" + window.parent + ")";
         response += " location(" + window.location.href + ")";
       }
 
-      if (isMozilla)
-      {
-        if (evt.isTrusted !== false)
-          response += " unexpected-trusted";
-      }
-
       if (evt.type != "message")
         response += " wrong-type(" + evt.type + ")";
 
       var data = evt.data;
       if (data == "post-to-other-same-domain")
       {
         receiveSame(evt, response);
       }
--- a/dom/tests/mochitest/whatwg/postMessage_joined_helper.html
+++ b/dom/tests/mochitest/whatwg/postMessage_joined_helper.html
@@ -1,16 +1,15 @@
 <!DOCTYPE html>
 <html>
 <!--
 http://sub1.test1.example.org/tests/dom/tests/mochitest/whatwg/postMessage_joined_helper.html
 -->
 <head>
   <title>postMessage joined domains, inner frame</title>
-  <script type="application/javascript" src="browserFu.js"></script>
   <script type="application/javascript">
     function receiveMessage(evt)
     {
       var response, target, providedOrigin;
       var data = evt.data;
       if (data === "subframe-test-finished")
       {
         target = window.parent;
@@ -37,21 +36,16 @@ http://sub1.test1.example.org/tests/dom/
         response += " wrong-type(" + evt.type + ")";
 
       if (evt.target !== window)
       {
         response += " wrong-target(" + evt.target + ")";
         response += " location(" + window.location.href + ")";
       }
 
-      if (isMozilla && evt.isTrusted === true)
-      {
-        response += " unexpected-trusted-event";
-      }
-
       var origin;
       if (data == "subframe-test-finished")
         origin = "http://example.org";
       else if (data === "start-test")
         origin = "http://mochi.test:8888";
       else
         origin = "unreached";
 
--- a/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html
+++ b/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html
@@ -1,16 +1,15 @@
 <!DOCTYPE html>
 <html>
 <!--
 http://example.org/tests/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html
 -->
 <head>
   <title>postMessage joined domains, innermost frame</title>
-  <script type="application/javascript" src="browserFu.js"></script>
   <script type="application/javascript">
     function receiveMessage(evt)
     {
       var response = "subframe-test-finished";
 
       if (evt.origin !== "http://sub1.test1.example.org")
       {
         response += " wrong-origin(" + evt.origin + ")";
@@ -22,22 +21,16 @@ http://example.org/tests/dom/tests/mochi
       if (evt.type !== "message")
         response += " wrong-type(" + evt.type + ")";
       if (evt.target !== window)
       {
         response += " wrong-target(" + evt.target + ")";
         response += " location(" + window.location.href + ")";
       }
 
-      if (isMozilla)
-      {
-        if (evt.isTrusted !== false)
-          response += " unexpected-trusted-event";
-      }
-
       if (evt.source !== window.parent)
       {
         response += " unexpected-source(" + evt.source + ")";
         response += " window-parent-is(" + window.parent + ")";
         response += " location(" + window.location.href + ")";
       }
 
       // verify that document.domain was actually joined with this domain
--- a/dom/tests/mochitest/whatwg/test_MessageEvent.html
+++ b/dom/tests/mochitest/whatwg/test_MessageEvent.html
@@ -1,17 +1,16 @@
 <!DOCTYPE html>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
 -->
 <head>
   <title>MessageEvent tests</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
-  <script type="text/javascript" src="browserFu.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage">Mozilla Bug 387706</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
--- a/dom/tests/mochitest/whatwg/test_postMessage.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage.html
@@ -1,17 +1,16 @@
 <!DOCTYPE html>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
 -->
 <head>
   <title>Basic postMessage tests</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
-  <script type="text/javascript" src="browserFu.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage">Mozilla Bug 387706</a>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 
 <iframe src="http://mochi.test:8888/tests/dom/tests/mochitest/whatwg/postMessage_helper.html" 
@@ -46,21 +45,17 @@ function finish()
 function messageReceiver(evt)
 {
   try
   {
     ok(evt instanceof MessageEvent, "umm, how did we get this?");
     is(evt.lastEventId, "",
        "postMessage creates events with empty lastEventId");
     is(evt.type, "message", "expected events of type 'message'");
-  
-    if (isMozilla)
-    {
-      ok(evt.isTrusted === false, "shouldn't have been a trusted event");
-    }
+    ok(evt.isTrusted === true, "should have been a trusted event");
   
     var data = evt.data;
   
     // Check for the message we send to ourselves; it can't be
     // counted as a test, and it's conceptually distinct from
     // the other cases, so just return after handling it.
     if (data === "post-to-self")
     {
--- a/dom/tests/mochitest/whatwg/test_postMessage_basehref.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_basehref.html
@@ -1,17 +1,16 @@
 <!DOCTYPE html>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=414815
 -->
 <head>
   <title>postMessage's interaction with a &lt;base&gt; tag</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
-  <script type="text/javascript" src="browserFu.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <base href="http://example.com/" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=414815">Mozilla Bug 414815</a>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 
--- a/dom/tests/mochitest/whatwg/test_postMessage_closed.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_closed.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html>
 <head>
   <title>postMessage's interaction with closed windows</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
-  <script type="text/javascript" src="browserFu.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <p><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=417075">Bug 417075</a></p>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 
 <div id="holder"></div>
--- a/dom/tests/mochitest/whatwg/test_postMessage_hash.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_hash.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html>
 <head>
   <title>postMessage's interaction with hash URIs</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
-  <script type="text/javascript" src="browserFu.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <base href="http://example.com/" />
 </head>
 <body>
 <p>(no bug; this is a preemptive test)</p>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 
--- a/dom/tests/mochitest/whatwg/test_postMessage_idn.xhtml
+++ b/dom/tests/mochitest/whatwg/test_postMessage_idn.xhtml
@@ -2,17 +2,16 @@
 <!DOCTYPE html>
 <html xmlns="http://www.w3.org/1999/xhtml">
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
 -->
 <head>
   <title>postMessage uri/domain values and IDN encoding</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
-  <script type="text/javascript" src="browserFu.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage">Mozilla Bug 387706</a>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 
 <iframe src="http://sub1.ält.example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_idn_helper.html"></iframe>
@@ -25,21 +24,17 @@ SimpleTest.waitForExplicitFinish();
 
 var responseReceived = false;
 var idnWindow = null;
 
 function receiveMessage(evt)
 {
   ok(evt instanceof MessageEvent, "umm, how did we get this?");
   is(evt.type, "message", "expected events of type 'message'");
-
-  if (isMozilla)
-  {
-    ok(evt.isTrusted === false, "shouldn't have been a trusted event");
-  }
+  ok(evt.isTrusted === true, "should have been a trusted event");
 
   is(evt.origin, "http://sub1.xn--lt-uia.example.org:8000",
      "wrong origin -- IDN issue, perhaps?");
 
   is(evt.data, "idn-response", "unexpected test result");
   is(evt.lastEventId, "", "postMessage creates events with empty lastEventId");
   ok(evt.source === idnWindow, "wrong source");
 
--- a/dom/tests/mochitest/whatwg/test_postMessage_joined.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_joined.html
@@ -1,17 +1,16 @@
 <!DOCTYPE html>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
 -->
 <head>
   <title>postMessage with document.domain setting to join origins</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
-  <script type="text/javascript" src="browserFu.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage">Mozilla Bug 387706</a>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 
 <iframe src="http://sub1.test1.example.org/tests/dom/tests/mochitest/whatwg/postMessage_joined_helper.html"
@@ -23,21 +22,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 /** Test for Bug 387706 **/
 
 SimpleTest.waitForExplicitFinish();
 
 function receiveTestResult(evt)
 {
   ok(evt instanceof MessageEvent, "umm, how did we get this?");
   is(evt.type, "message", "expected events of type 'message'");
-
-  if (isMozilla)
-  {
-    ok(evt.isTrusted === false, "shouldn't have been a trusted event");
-  }
+  ok(evt.isTrusted === true, "should have been a trusted event");
 
   is(evt.lastEventId, "", "postMessage creates events with empty lastEventId");
 
   // Either we passed the test or we failed it.  The message's
   // contents should help to diagnose the failure.  Either way,
   // consider this the end of the test.
   is(evt.data, "test-passed", "unexpected test result");
   SimpleTest.finish();
--- a/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml
+++ b/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml
@@ -1,17 +1,16 @@
 <!DOCTYPE html>
 <html xmlns="http://www.w3.org/1999/xhtml">
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=417075
 -->
 <head>
   <title>postMessage from about:blank, data URLs</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
-  <script type="text/javascript" src="browserFu.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=417075">Mozilla Bug 417075</a>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 
 <iframe src="http://mochi.test:8888/tests/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml"
@@ -358,23 +357,16 @@ var tests =
      source: "idnKidNoWhitelist",
      returnOrigin: "http://sub1.xn--exaple-kqf.test",
    },
    {
      args: ["PASS", "http://sub1.xn--exaple-kqf.test/foobar"],
      source: "idnKidNoWhitelist",
      returnOrigin: "http://sub1.xn--exaple-kqf.test",
    },
-   // 55
-   {
-     args: ["NOT-RECEIVED", undefined],
-     source: "sameDomain",
-     name: "SyntaxError",
-     code: DOMException.SYNTAX_ERR
-   },
   ];
 
 function allTests(callback)
 {
   var test, target, called;
 
   function eventCheck(evt)
   {
--- a/dom/tests/mochitest/whatwg/test_postMessage_special.xhtml
+++ b/dom/tests/mochitest/whatwg/test_postMessage_special.xhtml
@@ -1,17 +1,16 @@
 <!DOCTYPE html>
 <html xmlns="http://www.w3.org/1999/xhtml">
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
 -->
 <head>
   <title>postMessage from about:blank, data URLs</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
-  <script type="text/javascript" src="browserFu.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage">Mozilla Bug 387706</a>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 
 <pre id="test">
@@ -104,21 +103,17 @@ function messageReceiver(evt)
     return;
   }
 
 
   try
   {
     ok(evt instanceof MessageEvent, "umm, how did we get this?");
     is(evt.type, "message", "expected events of type 'message'");
-  
-    if (isMozilla)
-    {
-      ok(evt.isTrusted === false, "shouldn't have been a trusted event");
-    }
+    ok(evt.isTrusted === true, "should have been a trusted event");
   
     if (evt.data === "about:blank-response")
     {
       // This isn't clarified in HTML5 yet, but the origin for a document which
       // has been open()ed is the origin of the calling code, somewhat loosely
       // speaking.  For the specific case of about:blank it's also possible
       // that the origin is determined by the code that opens the window.  It's
       // not codified yet which of these two causes the identifier tokens on
--- a/dom/tests/mochitest/whatwg/test_postMessage_transfer.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_transfer.html
@@ -1,17 +1,16 @@
 <!DOCTYPE html>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=822094
 -->
 <head><meta charset=utf-8>
   <title>postMessage transferable tests</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
-  <script type="text/javascript" src="browserFu.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822094">Mozilla Bug 822094</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 <iframe src="postMessage_transfer_helper.html"
--- a/dom/webidl/Client.webidl
+++ b/dom/webidl/Client.webidl
@@ -18,17 +18,19 @@ interface Client {
 
   readonly attribute ClientType type;
   readonly attribute DOMString id;
 
   // Implement reserved in bug 1264177
   // readonly attribute boolean reserved;
 
   [Throws]
-  void postMessage(any message, optional sequence<object> transfer = []);
+  void postMessage(any message, sequence<object> transfer);
+  [Throws]
+  void postMessage(any message, optional PostMessageOptions aOptions);
 };
 
 [Exposed=ServiceWorker]
 interface WindowClient : Client {
   [BinaryName="GetVisibilityState"]
   readonly attribute VisibilityState visibilityState;
   readonly attribute boolean focused;
 
--- a/dom/webidl/DedicatedWorkerGlobalScope.webidl
+++ b/dom/webidl/DedicatedWorkerGlobalScope.webidl
@@ -14,15 +14,17 @@
 
 [Global=(Worker,DedicatedWorker),
  Exposed=DedicatedWorker]
 interface DedicatedWorkerGlobalScope : WorkerGlobalScope {
   [Replaceable]
   readonly attribute DOMString name;
 
   [Throws]
-  void postMessage(any message, optional sequence<object> transfer = []);
+  void postMessage(any message, sequence<object> transfer);
+  [Throws]
+  void postMessage(any message, optional PostMessageOptions options);
 
   void close();
 
   attribute EventHandler onmessage;
   attribute EventHandler onmessageerror;
 };
--- a/dom/webidl/MessagePort.webidl
+++ b/dom/webidl/MessagePort.webidl
@@ -5,18 +5,24 @@
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#channel-messaging
  */
 
 [Exposed=(Window,Worker,AudioWorklet)]
 interface MessagePort : EventTarget {
   [Throws]
-  void postMessage(any message, optional sequence<object> transferable = []);
+  void postMessage(any message, sequence<object> transferable);
+  [Throws]
+  void postMessage(any message, optional PostMessageOptions options);
 
   void start();
   void close();
 
   // event handlers
   attribute EventHandler onmessage;
   attribute EventHandler onmessageerror;
 };
 // MessagePort implements Transferable;
+
+dictionary PostMessageOptions {
+  sequence<object> transfer = [];
+};
--- a/dom/webidl/ServiceWorker.webidl
+++ b/dom/webidl/ServiceWorker.webidl
@@ -15,17 +15,19 @@
  Exposed=(Window,Worker)]
 interface ServiceWorker : EventTarget {
   readonly attribute USVString scriptURL;
   readonly attribute ServiceWorkerState state;
 
   attribute EventHandler onstatechange;
 
   [Throws]
-  void postMessage(any message, optional sequence<object> transferable = []);
+  void postMessage(any message, sequence<object> transferable);
+  [Throws]
+  void postMessage(any message, optional PostMessageOptions options);
 };
 
 ServiceWorker implements AbstractWorker;
 
 enum ServiceWorkerState {
   // https://github.com/w3c/ServiceWorker/issues/1162
   "parsed",
 
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -80,16 +80,18 @@ typedef OfflineResourceList ApplicationC
   [Throws, NeedsSubjectPrincipal] void alert(DOMString message);
   [Throws, NeedsSubjectPrincipal] boolean confirm(optional DOMString message = "");
   [Throws, NeedsSubjectPrincipal] DOMString? prompt(optional DOMString message = "", optional DOMString default = "");
   [Throws, Func="nsGlobalWindowInner::IsWindowPrintEnabled"]
   void print();
 
   [Throws, CrossOriginCallable, NeedsSubjectPrincipal]
   void postMessage(any message, DOMString targetOrigin, optional sequence<object> transfer = []);
+  [Throws, CrossOriginCallable, NeedsSubjectPrincipal]
+  void postMessage(any message, optional WindowPostMessageOptions options);
 
   // also has obsolete members
 };
 Window implements GlobalEventHandlers;
 Window implements WindowEventHandlers;
 
 // https://www.w3.org/TR/appmanifest/#onappinstalled-attribute
 partial interface Window {
@@ -571,8 +573,12 @@ partial interface Window {
 
 Window implements WebGPUProvider;
 
 partial interface Window {
   [SameObject, Pref="dom.visualviewport.enabled", Replaceable]
   readonly attribute VisualViewport visualViewport;
 
 };
+
+dictionary WindowPostMessageOptions : PostMessageOptions {
+  USVString targetOrigin = "/";
+};
--- a/dom/webidl/Worker.webidl
+++ b/dom/webidl/Worker.webidl
@@ -13,17 +13,19 @@
  */
 
 [Constructor(USVString scriptURL, optional WorkerOptions options),
  Exposed=(Window,DedicatedWorker,SharedWorker)]
 interface Worker : EventTarget {
   void terminate();
 
   [Throws]
-  void postMessage(any message, optional sequence<object> transfer = []);
+  void postMessage(any message, sequence<object> transfer);
+  [Throws]
+  void postMessage(any message, optional PostMessageOptions aOptions);
 
   attribute EventHandler onmessage;
   attribute EventHandler onmessageerror;
 };
 
 Worker implements AbstractWorker;
 
 dictionary WorkerOptions {
--- a/dom/workers/Worker.cpp
+++ b/dom/workers/Worker.cpp
@@ -120,16 +120,24 @@ Worker::PostMessage(JSContext* aCx, JS::
   }
 
   if (!runnable->Dispatch()) {
     aRv.Throw(NS_ERROR_FAILURE);
   }
 }
 
 void
+Worker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                    const PostMessageOptions& aOptions,
+                    ErrorResult& aRv)
+{
+  PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
+}
+
+void
 Worker::Terminate()
 {
   NS_ASSERT_OWNINGTHREAD(Worker);
 
   if (mWorkerPrivate) {
     mWorkerPrivate->Cancel();
     mWorkerPrivate = nullptr;
   }
--- a/dom/workers/Worker.h
+++ b/dom/workers/Worker.h
@@ -14,16 +14,17 @@
 
 #ifdef XP_WIN
 #undef PostMessage
 #endif
 
 namespace mozilla {
 namespace dom {
 
+struct PostMessageOptions;
 struct WorkerOptions;
 class WorkerPrivate;
 
 class Worker : public DOMEventTargetHelper
              , public SupportsWeakPtr<Worker>
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
@@ -39,16 +40,21 @@ public:
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
               const Sequence<JSObject*>& aTransferable,
               ErrorResult& aRv);
 
   void
+  PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+              const PostMessageOptions& aOptions,
+              ErrorResult& aRv);
+
+  void
   Terminate();
 
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(message)
   IMPL_EVENT_HANDLER(messageerror)
 
 protected:
   Worker(nsIGlobalObject* aGlobalObject,
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -610,16 +610,26 @@ DedicatedWorkerGlobalScope::PostMessage(
                                         const Sequence<JSObject*>& aTransferable,
                                         ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
   mWorkerPrivate->PostMessageToParent(aCx, aMessage, aTransferable, aRv);
 }
 
 void
+DedicatedWorkerGlobalScope::PostMessage(JSContext* aCx,
+                                        JS::Handle<JS::Value> aMessage,
+                                        const PostMessageOptions& aOptions,
+                                        ErrorResult& aRv)
+{
+  mWorkerPrivate->AssertIsOnWorkerThread();
+  mWorkerPrivate->PostMessageToParent(aCx, aMessage, aOptions.mTransfer, aRv);
+}
+
+void
 DedicatedWorkerGlobalScope::Close()
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
   mWorkerPrivate->CloseInternal();
 }
 
 SharedWorkerGlobalScope::SharedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate,
                                                  const nsString& aName)
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -28,16 +28,17 @@ class ClientInfo;
 class Clients;
 class ClientState;
 class Console;
 class Crypto;
 class Function;
 class IDBFactory;
 enum class ImageBitmapFormat : uint8_t;
 class Performance;
+struct PostMessageOptions;
 class Promise;
 class RequestOrUSVString;
 class WorkerLocation;
 class WorkerNavigator;
 class WorkerPrivate;
 enum class CallerType : uint32_t;
 
 namespace cache {
@@ -256,16 +257,20 @@ public:
   {
     aName.AsAString() = mName;
   }
 
   void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
               const Sequence<JSObject*>& aTransferable,
               ErrorResult& aRv);
+  void
+  PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+              const PostMessageOptions& aOptions,
+              ErrorResult& aRv);
 
   void
   Close();
 
   IMPL_EVENT_HANDLER(message)
   IMPL_EVENT_HANDLER(messageerror)
 };
 
--- a/dom/xhr/tests/browser_xhr_onchange_leak.js
+++ b/dom/xhr/tests/browser_xhr_onchange_leak.js
@@ -6,18 +6,18 @@
 // Bug 1336811 - An XHR that has a .onreadystatechange waiting should
 // not leak forever once the tab is closed. CC optimizations need to be
 // turned off once it is closed.
 
 add_task(async function test() {
   const url = "http://mochi.test:8888/browser/dom/xhr/tests/browser_xhr_onchange_leak.html";
   let newTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
   let browser = gBrowser.selectedBrowser;
-  let done = await ContentTask.spawn(browser,{}, async function(browser){
+  let done = await ContentTask.spawn(browser, {}, async function(browser) {
     let doc = content.document;
     let promise = ContentTaskUtils.waitForEvent(this, "DOMContentLoaded", true);
-    content.location = "about:home";
+    content.location = "http://example.org/";
     await promise;
     return true;
   });
   is(done, true, "need to check something");
   BrowserTestUtils.removeTab(newTab);
 });
--- a/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
+++ b/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
@@ -346,16 +346,18 @@ function moveMouseAndScrollWheelOver(ele
 // Synthesizes events to drag |element|'s vertical scrollbar by the distance
 // specified, synthesizing a mousemove for each increment as specified.
 // Returns false if the element doesn't have a vertical scrollbar. Otherwise,
 // returns a generator that should be invoked after the mousemoves have been
 // processed by the widget code, to end the scrollbar drag. Mousemoves being
 // processed by the widget code can be detected by listening for the mousemove
 // events in the caller, or for some other event that is triggered by the
 // mousemove, such as the scroll event resulting from the scrollbar drag.
+// Note: helper_scrollbar_snap_bug1501062.html contains a copy of this code
+// with modifications. Fixes here should be copied there if appropriate.
 function* dragVerticalScrollbar(element, testDriver, distance = 20, increment = 5) {
   var boundingClientRect = element.getBoundingClientRect();
   var verticalScrollbarWidth = boundingClientRect.width - element.clientWidth;
   if (verticalScrollbarWidth == 0) {
     return false;
   }
 
   var upArrowHeight = verticalScrollbarWidth; // assume square scrollbar buttons
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_scrollbar_snap_bug1501062.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width; initial-scale=1.0">
+  <title>Exercising the slider.snapMultiplier code</title>
+  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+  <script type="application/javascript" src="apz_test_utils.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+</head>
+<body>
+  <div id="scrollable" style="width: 300px; height: 300px; overflow: auto">
+    <div id="filler" style="height: 2000px; background-image: linear-gradient(red,blue)"></div>
+  </div>
+</body>
+<script type="text/javascript">
+function* test(testDriver) {
+  // Note that this pref is a read-once-on-startup pref so we can't change it
+  // and have the change take effect. Instead we just use the value to determine
+  // what the expected behaviour is.
+  var snapMultiplier = SpecialPowers.getIntPref("slider.snapMultiplier");
+
+  // Much of the code below is "inlined" from dragVerticalScrollbar. Reusing
+  // that code was nontrivial given the modifications we needed to make, and
+  // would have increased the complexity of that helper function more than I'd
+  // like. However if any bugfixes are made to that function this code might
+  // need to be updated as well.
+
+  var scrollableDiv = document.getElementById('scrollable');
+  var boundingClientRect = scrollableDiv.getBoundingClientRect();
+  var verticalScrollbarWidth = boundingClientRect.width - scrollableDiv.clientWidth;
+  if (verticalScrollbarWidth == 0) {
+    ok(true, "No scrollbar, can't do this test");
+    return;
+  }
+
+  // register a scroll listener for the initial drag
+  scrollableDiv.addEventListener('scroll', () => setTimeout(testDriver, 0), {once: true});
+
+  var upArrowHeight = verticalScrollbarWidth; // assume square scrollbar buttons
+  var mouseX = scrollableDiv.clientWidth + (verticalScrollbarWidth / 2);
+  var mouseY = upArrowHeight + 5; // start dragging somewhere in the thumb
+
+  dump("Starting drag at " + mouseX + ", " + mouseY + " from top-left of #" + scrollableDiv.id + "\n");
+
+  // Move the mouse to the scrollbar thumb and drag it down
+  yield synthesizeNativeMouseEvent(scrollableDiv, mouseX, mouseY, nativeMouseMoveEventMsg(), testDriver);
+  yield synthesizeNativeMouseEvent(scrollableDiv, mouseX, mouseY, nativeMouseDownEventMsg(), testDriver);
+  // drag down by 100 pixels
+  mouseY += 100;
+  yield synthesizeNativeMouseEvent(scrollableDiv, mouseX, mouseY, nativeMouseMoveEventMsg(), testDriver);
+
+  // wait here until the scroll event listener is triggered.
+  yield;
+  var savedScrollPos = scrollableDiv.scrollTop;
+  ok(savedScrollPos > 0, "Scrolled to " + savedScrollPos);
+
+  // register a new scroll event listener. The next mousemove below will either
+  // trigger the snapback behaviour (if snapMultiplier > 0) or trigger a vertical
+  // scroll (if snapMultiplier == 0) because of the x- and y-coordinates we move
+  // the mouse to. This allows us to wait for a scroll event in either case.
+  // If we only triggered the snapback case then waiting for the scroll to
+  // "not happen" in the other case would be more error-prone.
+  scrollableDiv.addEventListener('scroll', () => setTimeout(testDriver, 0), {once: true});
+  // Add 2 to snapMultipler just to make sure we get far enough away from the scrollbar
+  var snapBackDistance = (snapMultiplier + 2) * verticalScrollbarWidth;
+  yield synthesizeNativeMouseEvent(scrollableDiv, mouseX + snapBackDistance, mouseY + 10, nativeMouseMoveEventMsg(), testDriver);
+
+  // wait here until the scroll happens
+  yield;
+  if (snapMultiplier > 0) {
+    ok(scrollableDiv.scrollTop == 0, "Scroll position snapped back to " + scrollableDiv.scrollTop);
+  } else {
+    ok(scrollableDiv.scrollTop > savedScrollPos, "Scroll position increased to " + scrollableDiv.scrollTop);
+  }
+
+  // Now we move the mouse back to the old position to ensure the scroll position
+  // gets restored properly
+  scrollableDiv.addEventListener('scroll', () => setTimeout(testDriver, 0), {once: true});
+  yield synthesizeNativeMouseEvent(scrollableDiv, mouseX, mouseY, nativeMouseMoveEventMsg(), testDriver);
+
+  // wait here until the scroll happens
+  yield;
+  ok(scrollableDiv.scrollTop == savedScrollPos, "Scroll position was restored to " + scrollableDiv.scrollTop);
+
+  // Release mouse and ensure the scroll position stuck
+  yield synthesizeNativeMouseEvent(scrollableDiv, mouseX, mouseY, nativeMouseUpEventMsg(), testDriver);
+  // Flush everything just to be safe
+  yield flushApzRepaints(testDriver);
+
+  ok(scrollableDiv.scrollTop == savedScrollPos, "Final scroll position was " + scrollableDiv.scrollTop);
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+</script>
+</html>
--- a/gfx/layers/apz/test/mochitest/test_group_mouseevents.html
+++ b/gfx/layers/apz/test/mochitest/test_group_mouseevents.html
@@ -20,17 +20,20 @@ var subtests = [
   {'file': 'helper_drag_scroll.html'},
   // Test for dragging the scrollbar with a fixed-pos element overlaying it
   {'file': 'helper_bug1346632.html'},
   // Test for scrollbar-dragging on a scrollframe that's inactive
   {'file': 'helper_bug1326290.html'},
   // Test for scrollbar-dragging on a scrollframe inside an SVGEffects
   {'file': 'helper_bug1331693.html'},
   // Test for scrollbar-dragging on a transformed scrollframe inside a fixed-pos item
-  {'file': 'helper_bug1462961.html'}
+  {'file': 'helper_bug1462961.html'},
+  // Scrollbar dragging where we exercise the snapback behaviour by moving the
+  // mouse away from the scrollbar during drag
+  {'file': 'helper_scrollbar_snap_bug1501062.html'}
 ];
 
 if (isApzEnabled()) {
   SimpleTest.waitForExplicitFinish();
   window.onload = function() {
     runSubtestsSeriallyInFreshWindows(subtests)
     .then(SimpleTest.finish, SimpleTest.finish);
   };
--- a/gfx/layers/wr/WebRenderScrollData.h
+++ b/gfx/layers/wr/WebRenderScrollData.h
@@ -69,16 +69,17 @@ public:
   void SetTransformIsPerspective(bool aTransformIsPerspective) { mTransformIsPerspective = aTransformIsPerspective; }
   bool GetTransformIsPerspective() const { return mTransformIsPerspective; }
 
   void AddEventRegions(const EventRegions& aRegions) { mEventRegions.OrWith(aRegions); }
   EventRegions GetEventRegions() const { return mEventRegions; }
   void SetEventRegionsOverride(const EventRegionsOverride& aOverride) { mEventRegionsOverride = aOverride; }
   EventRegionsOverride GetEventRegionsOverride() const { return mEventRegionsOverride; }
 
+  void SetVisibleRegion(const LayerIntRegion& aRegion) { mVisibleRegion = aRegion; }
   const LayerIntRegion& GetVisibleRegion() const { return mVisibleRegion; }
   void SetReferentId(LayersId aReferentId) { mReferentId = Some(aReferentId); }
   Maybe<LayersId> GetReferentId() const { return mReferentId; }
 
   void SetScrollbarData(const ScrollbarData& aData) { mScrollbarData = aData; }
   const ScrollbarData& GetScrollbarData() const { return mScrollbarData; }
   void SetScrollbarAnimationId(const uint64_t& aId) { mScrollbarAnimationId = aId; }
   const uint64_t& GetScrollbarAnimationId() const { return mScrollbarAnimationId; }
--- a/gfx/src/nsDeviceContext.cpp
+++ b/gfx/src/nsDeviceContext.cpp
@@ -44,17 +44,17 @@ using namespace mozilla::gfx;
 using mozilla::services::GetObserverService;
 using mozilla::widget::ScreenManager;
 
 class nsFontCache final : public nsIObserver
 {
 public:
     nsFontCache(): mContext(nullptr) {}
 
-    NS_DECL_ISUPPORTS
+    NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIOBSERVER
 
     void Init(nsDeviceContext* aContext);
     void Destroy();
 
     already_AddRefed<nsFontMetrics> GetMetricsFor(
         const nsFont& aFont, const nsFontMetrics::Params& aParams);
 
--- a/js/public/GCAnnotations.h
+++ b/js/public/GCAnnotations.h
@@ -10,25 +10,28 @@
 // Set of annotations for the rooting hazard analysis, used to categorize types
 // and functions.
 #ifdef XGILL_PLUGIN
 
 // Mark a type as being a GC thing (eg js::gc::Cell has this annotation).
 # define JS_HAZ_GC_THING __attribute__((annotate("GC Thing")))
 
 // Mark a type as holding a pointer to a GC thing (eg JS::Value has this
-// annotation.)
+// annotation.) "Inherited" by templatized types with
+// MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS.
 # define JS_HAZ_GC_POINTER __attribute__((annotate("GC Pointer")))
 
 // Mark a type as a rooted pointer, suitable for use on the stack (eg all
-// Rooted<T> instantiations should have this.)
+// Rooted<T> instantiations should have this.) "Inherited" by templatized types with
+// MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS.
 # define JS_HAZ_ROOTED __attribute__((annotate("Rooted Pointer")))
 
 // Mark a type as something that should not be held live across a GC, but which
-// is not itself a GC pointer.
+// is not itself a GC pointer. Note that this property is *not* inherited by
+// templatized types with MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS.
 # define JS_HAZ_GC_INVALIDATED __attribute__((annotate("Invalidated by GC")))
 
 // Mark a class as a base class of rooted types, eg CustomAutoRooter. All
 // descendants of this class will be considered rooted, though classes that
 // merely contain these as a field member will not be. "Inherited" by
 // templatized types with MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS
 # define JS_HAZ_ROOTED_BASE __attribute__((annotate("Rooted Base")))
 
@@ -47,23 +50,29 @@
 
 // Mark an RAII class as suppressing GC within its scope.
 # define JS_HAZ_GC_SUPPRESSED __attribute__((annotate("Suppress GC")))
 
 // Mark a function as one that can run script if called.  This obviously
 // subsumes JS_HAZ_GC_CALL, since anything that can run script can GC.`
 # define JS_HAZ_CAN_RUN_SCRIPT __attribute__((annotate("Can run script")))
 
+// Mark a function as able to call JSNatives. Otherwise, JSNatives don't show
+// up in the callgraph. This doesn't matter for the can-GC analysis, but it is
+// very nice for other uses of the callgraph.
+# define JS_HAZ_JSNATIVE_CALLER __attribute__((annotate("Calls JSNatives")))
+
 #else
 
 # define JS_HAZ_GC_THING
 # define JS_HAZ_GC_POINTER
 # define JS_HAZ_ROOTED
 # define JS_HAZ_GC_INVALIDATED
 # define JS_HAZ_ROOTED_BASE
 # define JS_HAZ_NON_GC_POINTER
 # define JS_HAZ_GC_CALL
 # define JS_HAZ_GC_SUPPRESSED
 # define JS_HAZ_CAN_RUN_SCRIPT
+# define JS_HAZ_JSNATIVE_CALLER
 
 #endif
 
 #endif /* js_GCAnnotations_h */
--- a/js/public/StableStringChars.h
+++ b/js/public/StableStringChars.h
@@ -51,17 +51,17 @@ class MOZ_STACK_CLASS JS_FRIEND_API(Auto
      * When copying string char, use this many bytes of inline storage.  This is
      * chosen to allow the inline string types to be copied without allocating.
      * This is asserted in AutoStableStringChars::allocOwnChars.
      */
     static const size_t InlineCapacity = 24;
 
     /* Ensure the string is kept alive while we're using its chars. */
     Rooted<JSString*> s_;
-    MOZ_INIT_OUTSIDE_CTOR union {
+    union MOZ_INIT_OUTSIDE_CTOR {
         const char16_t* twoByteChars_;
         const Latin1Char* latin1Chars_;
     };
     mozilla::Maybe<js::Vector<uint8_t, InlineCapacity>> ownChars_;
     enum State { Uninitialized, Latin1, TwoByte };
     State state_;
 
   public:
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -475,17 +475,17 @@ static inline void js_free(void* p)
  * JS_DECLARE_NEW_METHODS (see js::MallocProvider for an example).
  *
  * Note: Do not add a ; at the end of a use of JS_DECLARE_NEW_METHODS,
  * or the build will break.
  */
 #define JS_DECLARE_NEW_METHODS(NEWNAME, ALLOCATOR, QUALIFIERS) \
     template <class T, typename... Args> \
     QUALIFIERS T * \
-    NEWNAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \
+    MOZ_HEAP_ALLOCATOR NEWNAME(Args&&... args) { \
         void* memory = ALLOCATOR(sizeof(T)); \
         return MOZ_LIKELY(memory) \
             ? new(memory) T(std::forward<Args>(args)...) \
             : nullptr; \
     }
 
 /*
  * Given a class which should provide 'make' methods, add
@@ -495,17 +495,17 @@ static inline void js_free(void* p)
  * ownership of the created object.
  *
  * Note: Do not add a ; at the end of a use of JS_DECLARE_MAKE_METHODS,
  * or the build will break.
  */
 #define JS_DECLARE_MAKE_METHODS(MAKENAME, NEWNAME, QUALIFIERS)\
     template <class T, typename... Args> \
     QUALIFIERS mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \
+    MOZ_HEAP_ALLOCATOR MAKENAME(Args&&... args) { \
         T* ptr = NEWNAME<T>(std::forward<Args>(args)...); \
         return mozilla::UniquePtr<T, JS::DeletePolicy<T>>(ptr); \
     }
 
 JS_DECLARE_NEW_METHODS(js_new, js_malloc, static MOZ_ALWAYS_INLINE)
 
 namespace js {
 
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -311,17 +311,17 @@ CanonicalizeNaN(double d)
  *   that toString, toObject, toSymbol will return an invalid pointer (because
  *   some high bits will be set) when called on a Value with a different type
  *   tag.
  *
  * - On 32-bit platforms,when unboxing an object/string/symbol Value, we use a
  *   conditional move (not speculated) to zero the payload register if the type
  *   doesn't match.
  */
-union MOZ_NON_PARAM alignas(8) Value
+union alignas(8) Value
 {
   private:
     uint64_t asBits_;
     double asDouble_;
 
 #if defined(JS_PUNBOX64) && !defined(_WIN64)
     // MSVC doesn't pack these correctly :-(
     struct {
@@ -924,17 +924,17 @@ union MOZ_NON_PARAM alignas(8) Value
         MOZ_ASSERT((((uintptr_t)cell) >> JSVAL_TAG_SHIFT) == 0);
 #endif
         asBits_ = bitsFromTagAndPayload(JSVAL_TAG_PRIVATE_GCTHING, PayloadType(cell));
     }
 
     bool isPrivateGCThing() const {
         return toTag() == JSVAL_TAG_PRIVATE_GCTHING;
     }
-} JS_HAZ_GC_POINTER;
+} JS_HAZ_GC_POINTER MOZ_NON_PARAM;
 
 static_assert(sizeof(Value) == 8,
               "Value size must leave three tag bits, be a binary power, and "
               "is ubiquitously depended upon everywhere");
 
 inline bool
 IsOptimizedPlaceholderMagicValue(const Value& v)
 {
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -5637,16 +5637,104 @@ BaselineCompile(JSContext* cx, unsigned 
 
     if (returnedStr) {
         return ReturnStringCopy(cx, args, returnedStr);
     }
 
     return true;
 }
 
+static bool
+PCCountProfiling_Start(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    js::StartPCCountProfiling(cx);
+
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
+PCCountProfiling_Stop(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    js::StopPCCountProfiling(cx);
+
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
+PCCountProfiling_Purge(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    js::PurgePCCounts(cx);
+
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
+PCCountProfiling_ScriptCount(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    size_t length = js::GetPCCountScriptCount(cx);
+
+    args.rval().setNumber(double(length));
+    return true;
+}
+
+static bool
+PCCountProfiling_ScriptSummary(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (!args.requireAtLeast(cx, "summary", 1)) {
+        return false;
+    }
+
+    uint32_t index;
+    if (!JS::ToUint32(cx, args[0], &index)) {
+        return false;
+    }
+
+    JSString* str = js::GetPCCountScriptSummary(cx, index);
+    if (!str) {
+        return false;
+    }
+
+    args.rval().setString(str);
+    return true;
+}
+
+static bool
+PCCountProfiling_ScriptContents(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (!args.requireAtLeast(cx, "contents", 1)) {
+        return false;
+    }
+
+    uint32_t index;
+    if (!JS::ToUint32(cx, args[0], &index)) {
+        return false;
+    }
+
+    JSString* str = js::GetPCCountScriptContents(cx, index);
+    if (!str) {
+        return false;
+    }
+
+    args.rval().setString(str);
+    return true;
+}
+
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'zone' [, 'shrinking'])",
 "  Run the garbage collector. When obj is given, GC only its zone.\n"
 "  If 'zone' is given, GC any zones that were scheduled for\n"
 "  GC via schedulegc.\n"
 "  If 'shrinking' is passed as the optional second argument, perform a\n"
 "  shrinking GC rather than a normal GC."),
@@ -6355,27 +6443,70 @@ JS_FN_HELP("setDefaultLocale", SetDefaul
 "setDefaultLocale(locale)",
 "  Set the runtime default locale to the given value.\n"
 "  An empty string or undefined resets the runtime locale to its default value.\n"
 "  NOTE: The input string is not fully validated, it must be a valid BCP-47 language tag."),
 
     JS_FS_HELP_END
 };
 
+static const JSFunctionSpecWithHelp PCCountProfilingTestingFunctions[] = {
+    JS_FN_HELP("start", PCCountProfiling_Start, 0, 0,
+    "start()",
+    "  Start PC count profiling."),
+
+    JS_FN_HELP("stop", PCCountProfiling_Stop, 0, 0,
+    "stop()",
+    "  Stop PC count profiling."),
+
+    JS_FN_HELP("purge", PCCountProfiling_Purge, 0, 0,
+    "purge()",
+    "  Purge the collected PC count profiling data."),
+
+    JS_FN_HELP("count", PCCountProfiling_ScriptCount, 0, 0,
+    "count()",
+    "  Return the number of profiled scripts."),
+
+    JS_FN_HELP("summary", PCCountProfiling_ScriptSummary, 1, 0,
+    "summary(index)",
+    "  Return the PC count profiling summary for the given script index.\n"
+    "  The script index must be in the range [0, pc.count())."),
+
+    JS_FN_HELP("contents", PCCountProfiling_ScriptContents, 1, 0,
+    "contents(index)",
+    "  Return the complete profiling contents for the given script index.\n"
+    "  The script index must be in the range [0, pc.count())."),
+
+    JS_FS_HELP_END
+};
+
 bool
 js::DefineTestingFunctions(JSContext* cx, HandleObject obj, bool fuzzingSafe_,
                            bool disableOOMFunctions_)
 {
     fuzzingSafe = fuzzingSafe_;
     if (EnvVarIsDefined("MOZ_FUZZING_SAFE")) {
         fuzzingSafe = true;
     }
 
     disableOOMFunctions = disableOOMFunctions_;
 
     if (!fuzzingSafe) {
         if (!JS_DefineFunctionsWithHelp(cx, obj, FuzzingUnsafeTestingFunctions)) {
             return false;
         }
+
+        RootedObject pccount(cx, JS_NewPlainObject(cx));
+        if (!pccount) {
+            return false;
+        }
+
+        if (!JS_DefineProperty(cx, obj, "pccount", pccount, 0)) {
+            return false;
+        }
+
+        if (!JS_DefineFunctionsWithHelp(cx, pccount, PCCountProfilingTestingFunctions)) {
+            return false;
+        }
     }
 
     return JS_DefineFunctionsWithHelp(cx, obj, TestingFunctions);
 }
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -976,17 +976,17 @@ function TypedArraySet(overloaded, offse
 
     // Steps 9-10.
     var targetBuffer = GetAttachedArrayBuffer(target);
 
     // Step 11.
     var targetLength = TypedArrayLength(target);
 
     // Steps 12 et seq.
-    if (IsPossiblyWrappedTypedArray(overloaded))
+    if (IsObject(overloaded) && IsPossiblyWrappedTypedArray(overloaded))
         return SetFromTypedArray(target, overloaded, targetOffset, targetLength);
 
     return SetFromNonTypedArray(target, overloaded, targetOffset, targetLength, targetBuffer);
 }
 
 // ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e
 // 22.2.3.24 %TypedArray%.prototype.slice ( start, end )
 function TypedArraySlice(start, end) {
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -422,8 +422,20 @@ function isOverridableField(initialCSU, 
 }
 
 function listNonGCPointers() {
     return [
         // Safe only because jsids are currently only made from pinned strings.
         'NPIdentifier',
     ];
 }
+
+function isJSNative(mangled)
+{
+    // _Z...E = function
+    // 9JSContext = JSContext*
+    // j = uint32
+    // PN2JS5Value = JS::Value*
+    //   P = pointer
+    //   N2JS = JS::
+    //   5Value = Value
+    return mangled.endsWith("P9JSContextjPN2JS5ValueE") && mangled.startsWith("_Z");
+}
--- a/js/src/devtools/rootAnalysis/computeCallgraph.js
+++ b/js/src/devtools/rootAnalysis/computeCallgraph.js
@@ -13,16 +13,19 @@ if (scriptArgs[0] == '--function') {
 var typeInfo_filename = scriptArgs[0] || "typeInfo.txt";
 var callgraphOut_filename = scriptArgs[1] || "callgraph.txt";
 
 var origOut = os.file.redirect(callgraphOut_filename);
 
 var memoized = new Map();
 var memoizedCount = 0;
 
+var JSNativeCaller = Object.create(null);
+var JSNatives = [];
+
 var unmangled2id = new Set();
 
 function getId(name)
 {
     let id = memoized.get(name);
     if (id !== undefined)
         return id;
 
@@ -97,18 +100,21 @@ function getAnnotations(functionName, bo
 // Scan through a function body, pulling out all annotations and calls and
 // recording them in callgraph.txt.
 function processBody(functionName, body)
 {
     if (!('PEdge' in body))
         return;
 
 
-    for (var tag of getAnnotations(functionName, body).values())
+    for (var tag of getAnnotations(functionName, body).values()) {
         print("T " + functionId(functionName) + " " + tag);
+        if (tag == "Calls JSNatives")
+            JSNativeCaller[functionName] = true;
+    }
 
     // Set of all callees that have been output so far, in order to suppress
     // repeated callgraph edges from being recorded. This uses a Map from
     // callees to limit sets, because we don't want a limited edge to prevent
     // an unlimited edge from being recorded later. (So an edge will be skipped
     // if it exists and is at least as limited as the previously seen edge.)
     //
     // Limit sets are implemented as integers interpreted as bitfields.
@@ -217,16 +223,18 @@ function process(functionName, functionB
     // This is slightly conservative in the case where they are *not*
     // identical, but that should be rare enough that we don't care.
     var markerPos = functionName.indexOf(internalMarker);
     if (markerPos > 0) {
         var inChargeXTor = functionName.replace(internalMarker, "");
         printOnce("D " + functionId(inChargeXTor) + " " + functionId(functionName));
     }
 
+    const [ mangled, unmangled ] = splitFunction(functionName);
+
     // Further note: from https://itanium-cxx-abi.github.io/cxx-abi/abi.html the
     // different kinds of constructors/destructors are:
     // C1	# complete object constructor
     // C2	# base object constructor
     // C3	# complete object allocating constructor
     // D0	# deleting destructor
     // D1	# complete object destructor
     // D2	# base object destructor
@@ -251,17 +259,16 @@ function process(functionName, functionB
     //                ::= D2 # base object (not-in-charge) destructor
     // <special-name> ::= C1   # complete object constructor
     //                ::= C2   # base object constructor
     //                ::= C3   # complete object allocating constructor
     //
     // Currently, allocating constructors are never used.
     //
     if (functionName.indexOf("C4") != -1) {
-        var [ mangled, unmangled ] = splitFunction(functionName);
         // E terminates the method name (and precedes the method parameters).
         // If eg "C4E" shows up in the mangled name for another reason, this
         // will create bogus edges in the callgraph. But it will affect little
         // and is somewhat difficult to avoid, so we will live with it.
         //
         // Another possibility! A templatized constructor will contain C4I...E
         // for template arguments.
         //
@@ -300,19 +307,32 @@ function process(functionName, functionB
         const not_in_charge_dtor = functionName.replace("(int32)", "()");
         const D0 = not_in_charge_dtor.replace("D4Ev", "D0Ev") + " [[deleting_dtor]]";
         const D1 = not_in_charge_dtor.replace("D4Ev", "D1Ev") + " [[complete_dtor]]";
         const D2 = not_in_charge_dtor.replace("D4Ev", "D2Ev") + " [[base_dtor]]";
         printOnce("D " + functionId(D0) + " " + functionId(D1));
         printOnce("D " + functionId(D1) + " " + functionId(D2));
         printOnce("D " + functionId(D2) + " " + functionId(functionName));
     }
+
+    if (isJSNative(mangled))
+        JSNatives.push(functionName);
+}
+
+function postprocess_callgraph() {
+    for (const caller of Object.keys(JSNativeCaller)) {
+        const caller_id = functionId(caller);
+        for (const callee of JSNatives)
+            printOnce(`D ${caller_id} ${functionId(callee)}`);
+    }
 }
 
 for (var nameIndex = minStream; nameIndex <= maxStream; nameIndex++) {
     var name = xdb.read_key(nameIndex);
     var data = xdb.read_entry(name);
     process(name.readString(), JSON.parse(data.readString()));
     xdb.free_string(name);
     xdb.free_string(data);
 }
 
+postprocess_callgraph();
+
 os.file.close(os.file.redirect(origOut));
--- a/js/src/devtools/rootAnalysis/computeGCTypes.js
+++ b/js/src/devtools/rootAnalysis/computeGCTypes.js
@@ -6,21 +6,25 @@ loadRelativeToScript('utility.js');
 loadRelativeToScript('annotations.js');
 
 var gcTypes_filename = scriptArgs[0] || "gcTypes.txt";
 var typeInfo_filename = scriptArgs[1] || "typeInfo.txt";
 
 var typeInfo = {
     'GCPointers': [],
     'GCThings': [],
+    'GCInvalidated': [],
     'NonGCTypes': {}, // unused
     'NonGCPointers': {},
     'RootedGCThings': {},
     'RootedPointers': {},
     'RootedBases': {'JS::AutoGCRooter': true},
+    'InheritFromTemplateArgs': {},
+    'OtherCSUTags': {},
+    'OtherFieldTags': {},
 
     // RAII types within which we should assume GC is suppressed, eg
     // AutoSuppressGC.
     'GCSuppressors': {},
 };
 
 var gDescriptors = new Map; // Map from descriptor string => Set of typeName
 
@@ -39,47 +43,69 @@ function processCSU(csu, body)
 {
     for (let { 'Name': [ annType, tag ] } of (body.Annotation || [])) {
         if (annType != 'annotate')
             continue;
 
         if (tag == 'GC Pointer')
             typeInfo.GCPointers.push(csu);
         else if (tag == 'Invalidated by GC')
-            typeInfo.GCPointers.push(csu);
+            typeInfo.GCInvalidated.push(csu);
         else if (tag == 'GC Thing')
             typeInfo.GCThings.push(csu);
         else if (tag == 'Suppressed GC Pointer')
             typeInfo.NonGCPointers[csu] = true;
         else if (tag == 'Rooted Pointer')
             typeInfo.RootedPointers[csu] = true;
         else if (tag == 'Rooted Base')
             typeInfo.RootedBases[csu] = true;
         else if (tag == 'Suppress GC')
             typeInfo.GCSuppressors[csu] = true;
+        else if (tag == 'moz_inherit_type_annotations_from_template_args')
+            typeInfo.InheritFromTemplateArgs[csu] = true;
+        else
+            addToKeyedList(typeInfo.OtherCSUTags, csu, tag);
     }
 
     for (let { 'Base': base } of (body.CSUBaseClass || []))
         addBaseClass(csu, base);
 
-    for (let field of (body.DataField || [])) {
+    for (const field of (body.DataField || [])) {
         var type = field.Field.Type;
         var fieldName = field.Field.Name[0];
         if (type.Kind == "Pointer") {
             var target = type.Type;
             if (target.Kind == "CSU")
                 addNestedPointer(csu, target.Name, fieldName);
         }
         if (type.Kind == "Array") {
             var target = type.Type;
             if (target.Kind == "CSU")
                 addNestedStructure(csu, target.Name, fieldName);
         }
         if (type.Kind == "CSU")
             addNestedStructure(csu, type.Name, fieldName);
+
+        for (const { 'Name': [ annType, tag ] } of (field.Annotation || [])) {
+            if (!(csu in typeInfo.OtherFieldTags))
+                typeInfo.OtherFieldTags[csu] = [];
+            addToKeyedList(typeInfo.OtherFieldTags[csu], fieldName, tag);
+        }
+    }
+
+    for (const funcfield of (body.FunctionField || [])) {
+        const fields = funcfield.Field;
+        // Pure virtual functions will not have field.Variable; others will.
+        for (const field of funcfield.Field) {
+            for (const {'Name': [annType, tag]} of (field.Annotation || [])) {
+                if (!(csu in typeInfo.OtherFieldTags))
+                    typeInfo.OtherFieldTags[csu] = {};
+                addToKeyedList(typeInfo.OtherFieldTags[csu], field.Name[0], tag);
+            }
+        }
     }
 }
 
 // csu.field is of type inner
 function addNestedStructure(csu, inner, field)
 {
     if (!(inner in structureParents))
         structureParents[inner] = [];
@@ -134,16 +160,57 @@ for (const typename of extraRootedPointe
     typeInfo.RootedPointers[typename] = true;
 
 // Now that we have the whole hierarchy set up, add all the types and propagate
 // info.
 for (const csu of typeInfo.GCThings)
     addGCType(csu);
 for (const csu of typeInfo.GCPointers)
     addGCPointer(csu);
+for (const csu of typeInfo.GCInvalidated)
+    addGCPointer(csu);
+
+// GC Thing and GC Pointer annotations can be inherited from template args if
+// this annotation is used. Think of Maybe<T> for example: Maybe<JSObject*> has
+// the same GC rules as JSObject*. But this needs to be done in a conservative
+// direction: Maybe<AutoSuppressGC> should not be regarding as suppressing GC
+// (because it might still be None).
+//
+// Note that there is an order-dependence here that is being mostly ignored (eg
+// Maybe<Maybe<Cell*>> -- if that is processed before Maybe<Cell*> is
+// processed, we won't get the right answer). We'll at least sort by string
+// length to make it hard to hit that case.
+var inheritors = Object.keys(typeInfo.InheritFromTemplateArgs).sort((a, b) => a.length - b.length);
+for (const csu of inheritors) {
+    // Unfortunately, we just have a string type name, not the full structure
+    // of a templatized type, so we will have to resort to loose (buggy)
+    // pattern matching.
+    //
+    // Currently, the simplest ways I know of to break this are:
+    //
+    //   foo<T>::bar<U>
+    //   foo<bar<T,U>>
+    //
+    const [_, params_str] = csu.match(/<(.*)>/);
+    for (let param of params_str.split(",")) {
+        param = param.replace(/^\s+/, '')
+        param = param.replace(/\s+$/, '')
+        const pieces = param.split("*");
+        const core_type = pieces[0];
+        const ptrdness = pieces.length - 1;
+        if (ptrdness > 1)
+            continue;
+        const paramDesc = 'template-param-' + param;
+        const why = '(inherited annotations from ' + param + ')';
+        if (typeInfo.GCThings.indexOf(core_type) != -1)
+            markGCType(csu, paramDesc, why, ptrdness, 0, "");
+        if (typeInfo.GCPointers.indexOf(core_type) != -1)
+            markGCType(csu, paramDesc, why, ptrdness + 1, 0, "");
+    }
+}
 
 // Everything that inherits from a "Rooted Base" is considered to be rooted.
 // This is for things like CustomAutoRooter and its subclasses.
 var basework = Object.keys(typeInfo.RootedBases);
 while (basework.length) {
     const base = basework.pop();
     typeInfo.RootedPointers[base] = true;
     if (base in subClasses)
new file mode 100644
--- /dev/null
+++ b/js/src/devtools/rootAnalysis/mozconfig.haz
@@ -0,0 +1,46 @@
+# This mozconfig is used when compiling the browser for the rooting hazard
+# analysis build (labeled H on treeherder). See
+# https://wiki.mozilla.org/Javascript:SpiderMonkey:ExactStackRooting
+
+# Do NOT include build/unix/mozconfig.linux because it points directly at the
+# tooltool-installed gcc, and the analysis works by wrapping the gcc invocation
+# with a script that invokes the real gcc with -fplugin and its configuration
+# directives. Instead, duplicate the contents of that mozconfig here:
+
+MOZ_HAZARD=1
+
+# Skip as many non-compile steps as we can.
+MOZ_AUTOMATION_BUILD_SYMBOLS=0
+MOZ_AUTOMATION_L10N_CHECK=0
+MOZ_AUTOMATION_PACKAGE=0
+MOZ_AUTOMATION_PACKAGE_TESTS=0
+MOZ_AUTOMATION_UPLOAD=0
+
+ac_add_options --enable-js-shell
+
+# The objdir must be at a known location so its path can be stripped from the
+# filenames stored by the analysis
+mk_add_options MOZ_OBJDIR=obj-analyzed
+
+export LLVM_CONFIG=$TOOLTOOL_DIR/clang/bin/llvm-config
+export CBINDGEN="${TOOLTOOL_DIR}/cbindgen/cbindgen"
+
+# The configuration options are chosen to compile the most code
+# (--enable-debug, --enable-tests) in the trickiest way possible
+# (--enable-optimize) to maximize the chance of seeing tricky static orderings.
+ac_add_options --enable-debug
+ac_add_options --enable-tests
+ac_add_options --enable-optimize
+ac_add_options --with-compiler-wrapper=$TOOLTOOL_DIR/sixgill/usr/libexec/sixgill/scripts/wrap_gcc/basecc
+ac_add_options --without-ccache
+
+ac_add_options --disable-replace-malloc
+
+# -Wattributes is very verbose due to attributes being ignored on template
+# instantiations. -Wignored-attributes is very verbose due to attributes being
+# ignored on template parameters.
+CFLAGS="$CFLAGS -Wno-attributes -Wno-ignored-attributes"
+CPPFLAGS="$CPPFLAGS -Wno-attributes -Wno-ignored-attributes"
+CXXFLAGS="$CXXFLAGS -Wno-attributes -Wno-ignored-attributes"
+
+NODEJS="$TOOLTOOL_DIR/node/bin/node"
--- a/js/src/devtools/rootAnalysis/t/hazards/source.cpp
+++ b/js/src/devtools/rootAnalysis/t/hazards/source.cpp
@@ -1,12 +1,17 @@
 #define ANNOTATE(property) __attribute__((annotate(property)))
 
 struct Cell { int f; } ANNOTATE("GC Thing");
 
+template<typename T, typename U>
+struct UntypedContainer {
+    char data[sizeof(T) + sizeof(U)];
+} ANNOTATE("moz_inherit_type_annotations_from_template_args");
+
 struct RootedCell { RootedCell(Cell*) {} } ANNOTATE("Rooted Pointer");
 
 class AutoSuppressGC_Base {
   public:
     AutoSuppressGC_Base() {}
     ~AutoSuppressGC_Base() {}
 } ANNOTATE("Suppress GC");
 
@@ -52,16 +57,22 @@ struct GCInDestructor {
     ~GCInDestructor() {
         invisible();
         asm("");
         *xp = 4;
         GC();
     }
 };
 
+template <typename T>
+void
+usecontainer(T* value) {
+    if (value) asm("");
+}
+
 Cell*
 f()
 {
     GCInDestructor kaboom;
 
     Cell cell;
     Cell* cell1 = &cell;
     Cell* cell2 = &cell;
@@ -80,16 +91,33 @@ f()
         // Old bug: it would look from the first AutoSuppressGC constructor it
         // found to the last destructor. This statement *should* have no effect.
         AutoSuppressGC nogc;
     }
     usecell(cell3);
     Cell* cell5 = &cell;
     usecell(cell5);
 
+    {
+        // Templatized container that inherits attributes from Cell*, should
+        // report a hazard.
+        UntypedContainer<int, Cell*> container1;
+        usecontainer(&container1);
+        GC();
+        usecontainer(&container1);
+    }
+
+    {
+        // As above, but with a non-GC type.
+        UntypedContainer<int, double> container2;
+        usecontainer(&container2);
+        GC();
+        usecontainer(&container2);
+    }
+
     // Hazard in return value due to ~GCInDestructor
     Cell* cell6 = &cell;
     return cell6;
 }
 
 Cell* copy_and_gc(Cell* src)
 {
     GC();
--- a/js/src/devtools/rootAnalysis/t/hazards/test.py
+++ b/js/src/devtools/rootAnalysis/t/hazards/test.py
@@ -30,16 +30,19 @@ assert(len(set(haz.function for haz in h
 # Check that the correct GC call is reported for each hazard. (cell3 has a
 # hazard from two different GC calls; it doesn't really matter which is
 # reported.)
 assert(hazmap['cell2'].GCFunction == 'void halfSuppressedFunction()')
 assert(hazmap['cell3'].GCFunction in (
     'void halfSuppressedFunction()', 'void unsuppressedFunction()'))
 assert(hazmap['<returnvalue>'].GCFunction == 'void GCInDestructor::~GCInDestructor()')
 
+assert('container1' in hazmap);
+assert('container2' not in hazmap);
+
 # Type names are handy to have in the report.
 assert(hazmap['cell2'].type == 'Cell*')
 assert(hazmap['<returnvalue>'].type == 'Cell*')
 
 # loopy hazards. See comments in source.
 assert('haz1' not in hazmap)
 assert('haz2' not in hazmap)
 assert('haz3' in hazmap)
--- a/js/src/devtools/rootAnalysis/t/suppression/test.py
+++ b/js/src/devtools/rootAnalysis/t/suppression/test.py
@@ -1,24 +1,20 @@
 # flake8: noqa: F821
 test.compile("source.cpp")
 test.run_analysis_script('gcTypes', upto='gcFunctions')
 
-# The suppressions file uses only mangled names since it's for internal use,
-# though I may change that soon given (1) the unfortunate non-uniqueness of
-# mangled constructor names, and (2) the usefulness of this file for
-# mrgiggles's reporting.
+# The suppressions file uses mangled names.
 suppressed = test.load_suppressed_functions()
 
 # Only one of these is fully suppressed (ie, *always* called within the scope
 # of an AutoSuppressGC).
-assert(len(list(filter(lambda f: 'suppressedFunction' in f, suppressed))) == 1)
-assert(len(list(filter(lambda f: 'halfSuppressedFunction' in f, suppressed))) == 0)
-assert(len(list(filter(lambda f: 'unsuppressedFunction' in f, suppressed))) == 0)
+assert len(list(filter(lambda f: 'suppressedFunction' in f, suppressed))) == 1
+assert len(list(filter(lambda f: 'halfSuppressedFunction' in f, suppressed))) == 0
+assert len(list(filter(lambda f: 'unsuppressedFunction' in f, suppressed))) == 0
 
 # gcFunctions should be the inverse, but we get to rely on unmangled names here.
 gcFunctions = test.load_gcFunctions()
-print(gcFunctions)
-assert('void GC()' in gcFunctions)
-assert('void suppressedFunction()' not in gcFunctions)
-assert('void halfSuppressedFunction()' in gcFunctions)
-assert('void unsuppressedFunction()' in gcFunctions)
-assert('void f()' in gcFunctions)
+assert 'void GC()' in gcFunctions
+assert 'void suppressedFunction()' not in gcFunctions
+assert 'void halfSuppressedFunction()' in gcFunctions
+assert 'void unsuppressedFunction()' in gcFunctions
+assert 'void f()' in gcFunctions
--- a/js/src/devtools/rootAnalysis/t/testlib.py
+++ b/js/src/devtools/rootAnalysis/t/testlib.py
@@ -114,16 +114,20 @@ sixgill_bin = '{bindir}'
                 return (m.group(1) + 's', m.group(2))
             return None
 
         gctypes = defaultdict(list)
         for collection, typename in self.load_text_file('gcTypes.txt', extract=grab_type):
             gctypes[collection].append(typename)
         return gctypes
 
+    def load_typeInfo(self, filename="typeInfo.txt"):
+        with open(os.path.join(self.outdir, filename)) as fh:
+            return json.load(fh)
+
     def load_gcFunctions(self):
         return self.load_text_file('gcFunctions.lst', extract=extract_unmangled)
 
     def load_callgraph(self):
         data = Callgraph(
             functionNames=['dummy'],
             nameToId={},
             mangledToUnmangled={},
--- a/js/src/devtools/rootAnalysis/t/virtual/source.cpp
+++ b/js/src/devtools/rootAnalysis/t/virtual/source.cpp
@@ -12,17 +12,17 @@ struct Cell { int f; } ANNOTATE("GC Thin
 
 extern void foo();
 
 typedef void (*func_t)();
 
 class Base {
   public:
     int ANNOTATE("field annotation") dummy;
-    virtual void someGC() = 0;
+    virtual void someGC() ANNOTATE("Base pure virtual method") = 0;
     func_t functionField;
 
     // For now, this is just to verify that the plugin doesn't crash. The
     // analysis code does not yet look at this annotation or output it anywhere
     // (though it *is* being recorded.)
     static float testAnnotations() ANNOTATE("static func");
 
     // Similar, though sixgill currently completely ignores parameter annotations.
@@ -47,19 +47,24 @@ class Super : public Base {
 
 void bar() {
     GC();
 }
 
 class Sub1 : public Super {
   public:
     void noneGC() override { foo(); }
-    void someGC() override { foo(); }
+    void someGC() override
+      ANNOTATE("Sub1 override")
+      ANNOTATE("second attr")
+    {
+        foo();
+    }
     void allGC() override { foo(); bar(); }
-};
+} ANNOTATE("CSU1") ANNOTATE("CSU2");
 
 class Sub2 : public Super {
   public:
     void noneGC() override { foo(); }
     void someGC() override { foo(); bar(); }
     void allGC() override { foo(); bar(); }
 };
 
--- a/js/src/devtools/rootAnalysis/t/virtual/test.py
+++ b/js/src/devtools/rootAnalysis/t/virtual/test.py
@@ -1,38 +1,43 @@
 # 'test' is provided by the calling script.
 # flake8: noqa: F821
 
 test.compile("source.cpp")
 test.run_analysis_script('gcTypes')
 
-# The suppressions file uses only mangled names since it's for internal use,
-# though I may change that soon given (1) the unfortunate non-uniqueness of
-# mangled constructor names, and (2) the usefulness of this file for
-# mrgiggles's reporting.
-suppressed = test.load_suppressed_functions()
+info = test.load_typeInfo()
 
-# gcFunctions should be the inverse, but we get to rely on unmangled names here.
+assert 'Sub1' in info['OtherCSUTags']
+assert ['CSU1', 'CSU2'] == sorted(info['OtherCSUTags']['Sub1'])
+assert 'Base' in info['OtherFieldTags']
+assert 'someGC' in info['OtherFieldTags']['Base']
+assert 'Sub1' in info['OtherFieldTags']
+assert 'someGC' in info['OtherFieldTags']['Sub1']
+assert ['Sub1 override', 'second attr'] == sorted(info['OtherFieldTags']['Sub1']['someGC'])
+
 gcFunctions = test.load_gcFunctions()
 
 assert 'void Sub1::noneGC()' not in gcFunctions
 assert 'void Sub1::someGC()' not in gcFunctions
 assert 'void Sub1::allGC()' in gcFunctions
 assert 'void Sub2::noneGC()' not in gcFunctions
 assert 'void Sub2::someGC()' in gcFunctions
 assert 'void Sub2::allGC()' in gcFunctions
 
 callgraph = test.load_callgraph()
+
 assert callgraph.calleeGraph['void f()']['Super.noneGC']
 assert callgraph.calleeGraph['Super.noneGC']['void Sub1::noneGC()']
 assert callgraph.calleeGraph['Super.noneGC']['void Sub2::noneGC()']
 assert 'void Sibling::noneGC()' not in callgraph.calleeGraph['Super.noneGC']
 
 hazards = test.load_hazards()
 hazmap = {haz.variable: haz for haz in hazards}
+
 assert 'c1' not in hazmap
 assert 'c2' in hazmap
 assert 'c3' in hazmap
 assert 'c4' not in hazmap
 assert 'c5' in hazmap
 assert 'c6' in hazmap
 assert 'c7' not in hazmap
 assert 'c8' in hazmap
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/profiler/pc-count-profiler.js
@@ -0,0 +1,53 @@
+// Smoke test for the PC count profiler. 
+
+function f(x) {
+    if (x < 10)
+        return 0;
+    if (x < 50)
+        return 1;
+    return 2;
+}
+
+L: try {
+    // Profile the test function "f".
+    pccount.start();
+    for (var i = 0; i < 100; ++i) f(i);
+    pccount.stop();
+
+    // Count the profiled scripts, stop if no scripts were profiled.
+    var n = pccount.count();
+    if (n === 0)
+        break L;
+
+    // Find the script index for "f".
+    var scriptIndex = -1;
+    for (var i = 0; i < n; ++i) {
+        var summary = JSON.parse(pccount.summary(i));
+        if (summary.name === "f")
+            scriptIndex = i;
+    }
+
+    // Stop if the script index for "f" wasn't found.
+    if (scriptIndex < 0)
+        break L;
+
+    // Retrieve the profiler data for "f".
+    var contents = pccount.contents(scriptIndex);
+    assertEq(typeof contents, "string");
+
+    // The profiler data should be parsable as JSON text.
+    var contents = JSON.parse(contents, (name, value) => {
+        // Split the Ion code segments into multiple entries.
+        if (name === "code")
+            return value.split("\n");
+
+        return value;
+    });
+
+    // Pretty print the profiler data.
+    var pretty = JSON.stringify(contents, null, 1);
+    print(pretty);
+} finally {
+    // Clean-up.
+    pccount.purge();
+}
--- a/js/src/jit-test/tests/self-hosting/is-possibly-wrapped-typed-array.js
+++ b/js/src/jit-test/tests/self-hosting/is-possibly-wrapped-typed-array.js
@@ -13,23 +13,16 @@ var declareSamples = `
         { value: new Uint8ClampedArray(1), expected: true }
     ];
 
     var allObjectSamples = [
         { value: new Array(1), expected: false },
         { value: {}, expected: false },
         { value: { length: 1 }, expected: false }
     ];
-
-    var allNonObjectSamples = [
-        { value: "a", expected: false },
-        { value: 1.2, expected: false },
-        { value: true, expected: false },
-        { value: Symbol("a"), expected: false }
-    ];
 `;
 
 // Create a new global to wrap with cross compartment wrappers.
 var g = newGlobal();
 evaluate(declareSamples)
 g.evaluate(declareSamples);
 
 var assertCode = `function (value, expected) {
@@ -57,95 +50,42 @@ function checkSamples(samples) {
             var e = samples[i];
             if (!e) continue;
             spinInJit = spinInJit && assert(e.value, e.expected);
         }
     } while(!spinInJit);
 }
 
 // Check a mix of samples from each type.
-function test(a, b, c, d, e, f) {
+function test(a, b, c, d) {
     var samples = [
         a == -1 ? null : allTypedArraySamples[a],
         b == -1 ? null : allObjectSamples[b],
-        c == -1 ? null : allNonObjectSamples[c],
-        d == -1 ? null : g.allTypedArraySamples[d],
-        e == -1 ? null : g.allObjectSamples[e],
-        f == -1 ? null : g.allNonObjectSamples[f]
+        c == -1 ? null : g.allTypedArraySamples[c],
+        d == -1 ? null : g.allObjectSamples[d],
     ];
 
     checkSamples(samples);
 }
 
 // Check all samples.
 checkSamples(allTypedArraySamples);
 checkSamples(allObjectSamples);
-checkSamples(allNonObjectSamples);
 checkSamples(g.allTypedArraySamples);
 checkSamples(g.allObjectSamples);
-checkSamples(g.allNonObjectSamples);
 
 // Check combinations mixing 2 elements from different types.
-test(-1, -1, -1, -1,  0,  0);
-test(-1, -1, -1,  0, -1,  0);
-test(-1, -1, -1,  0,  0, -1);
-test(-1, -1,  0, -1, -1,  0);
-test(-1, -1,  0, -1,  0, -1);
-test(-1, -1,  0,  0, -1, -1);
-test(-1,  0, -1, -1, -1,  0);
-test(-1,  0, -1, -1,  0, -1);
-test(-1,  0, -1,  0, -1, -1);
-test(-1,  0,  0, -1, -1, -1);
-test( 0, -1, -1, -1, -1,  0);
-test( 0, -1, -1, -1,  0, -1);
-test( 0, -1, -1,  0, -1, -1);
-test( 0, -1,  0, -1, -1, -1);
-test( 0,  0, -1, -1, -1, -1);
+test(-1, -1,  0,  0);
+test(-1,  0, -1,  0);
+test(-1,  0,  0, -1);
+test( 0, -1, -1,  0);
+test( 0, -1,  0, -1);
+test( 0,  0, -1, -1);
+test( 0,  0, -1,  0);
 
 // Check combinations mixing 3 elements from different types.
-test(-1, -1, -1,  0,  0,  0);
-test(-1, -1,  0, -1,  0,  0);
-test(-1, -1,  0,  0, -1,  0);
-test(-1, -1,  0,  0,  0, -1);
-test(-1,  0, -1, -1,  0,  0);
-test(-1,  0, -1,  0, -1,  0);
-test(-1,  0, -1,  0,  0, -1);
-test(-1,  0,  0, -1, -1,  0);
-test(-1,  0,  0, -1,  0, -1);
-test(-1,  0,  0,  0, -1, -1);
-test( 0, -1, -1, -1,  0,  0);
-test( 0, -1, -1,  0, -1,  0);
-test( 0, -1, -1,  0,  0, -1);
-test( 0, -1,  0, -1, -1,  0);
-test( 0, -1,  0, -1,  0, -1);
-test( 0, -1,  0,  0, -1, -1);
-test( 0,  0, -1, -1, -1,  0);
-test( 0,  0, -1, -1,  0, -1);
-test( 0,  0, -1,  0, -1, -1);
-test( 0,  0,  0, -1, -1, -1);
+test(-1,  0,  0,  0);
+test( 0, -1,  0,  0);
+test( 0,  0, -1,  0);
+test( 0,  0,  0, -1);
 
 // Check combinations mixing 4 elements from different types.
-test(-1, -1,  0,  0,  0,  0);
-test(-1,  0, -1,  0,  0,  0);
-test(-1,  0,  0, -1,  0,  0);
-test(-1,  0,  0,  0, -1,  0);
-test(-1,  0,  0,  0,  0, -1);
-test( 0, -1, -1,  0,  0,  0);
-test( 0, -1,  0, -1,  0,  0);
-test( 0, -1,  0,  0, -1,  0);
-test( 0, -1,  0,  0,  0, -1);
-test( 0,  0, -1, -1,  0,  0);
-test( 0,  0, -1,  0, -1,  0);
-test( 0,  0, -1,  0,  0, -1);
-test( 0,  0,  0, -1, -1,  0);
-test( 0,  0,  0, -1,  0, -1);
-test( 0,  0,  0,  0, -1, -1);
-
-// Check combinations mixing 5 elements from different types.
-test(-1,  0,  0,  0,  0,  0);
-test( 0, -1,  0,  0,  0,  0);
-test( 0,  0, -1,  0,  0,  0);
-test( 0,  0,  0, -1,  0,  0);
-test( 0,  0,  0,  0, -1,  0);
-test( 0,  0,  0,  0,  0, -1);
-
-// Check combinations mixing 6 elements from different types.
-test( 0,  0,  0,  0,  0,  0);
+test( 0,  0,  0,  0);
--- a/js/src/jit/Jit.cpp
+++ b/js/src/jit/Jit.cpp
@@ -12,17 +12,17 @@
 #include "jit/JitRealm.h"
 #include "vm/Interpreter.h"
 
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
-static EnterJitStatus
+static EnterJitStatus JS_HAZ_JSNATIVE_CALLER
 EnterJit(JSContext* cx, RunState& state, uint8_t* code)
 {
     MOZ_ASSERT(state.script()->hasBaselineScript());
     MOZ_ASSERT(code);
     MOZ_ASSERT(IsBaselineEnabled(cx));
 
     if (!CheckRecursionLimit(cx)) {
         return EnterJitStatus::Error;
--- a/js/src/vm/BytecodeUtil.cpp
+++ b/js/src/vm/BytecodeUtil.cpp
@@ -2652,414 +2652,262 @@ js::GetPCCountScriptCount(JSContext* cx)
 
     if (!rt->scriptAndCountsVector) {
         return 0;
     }
 
     return rt->scriptAndCountsVector->length();
 }
 
-enum MaybeComma {NO_COMMA, COMMA};
-
 static MOZ_MUST_USE bool
-AppendJSONProperty(StringBuffer& buf, const char* name, MaybeComma comma = COMMA)
-{
-    if (comma && !buf.append(',')) {
+JSONStringProperty(Sprinter& sp, JSONPrinter& json, const char* name, JSString* str) {
+    json.beginStringProperty(name);
+    if (!JSONQuoteString(&sp, str)) {
         return false;
     }
-
-    return buf.append('\"') &&
-           buf.append(name, strlen(name)) &&
-           buf.append("\":", 2);
+    json.endStringProperty();
+    return true;
 }
 
 JS_FRIEND_API(JSString*)
 js::GetPCCountScriptSummary(JSContext* cx, size_t index)
 {
     JSRuntime* rt = cx->runtime();
 
     if (!rt->scriptAndCountsVector || index >= rt->scriptAndCountsVector->length()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BUFFER_TOO_SMALL);
         return nullptr;
     }
 
     const ScriptAndCounts& sac = (*rt->scriptAndCountsVector)[index];
     RootedScript script(cx, sac.script);
 
-    /*
-     * OOM on buffer appends here will not be caught immediately, but since
-     * StringBuffer uses a TempAllocPolicy will trigger an exception on the
-     * context if they occur, which we'll catch before returning.
-     */
-    StringBuffer buf(cx);
-
-    if (!buf.append('{')) {
-        return nullptr;
-    }
-
-    if (!AppendJSONProperty(buf, "file", NO_COMMA)) {
-        return nullptr;
-    }
-    JSString* str = JS_NewStringCopyZ(cx, script->filename());
-    if (!str || !(str = StringToSource(cx, str))) {
+    Sprinter sp(cx);
+    if (!sp.init()) {
         return nullptr;
     }
-    if (!buf.append(str)) {
-        return nullptr;
-    }
-
-    if (!AppendJSONProperty(buf, "line")) {
-        return nullptr;
-    }
-    if (!NumberValueToStringBuffer(cx, Int32Value(script->lineno()), buf)) {
+
+    JSONPrinter json(sp, false);
+
+    json.beginObject();
+
+    RootedString filename(cx, NewStringCopyZ<CanGC>(cx, script->filename()));
+    if (!filename) {
         return nullptr;
     }
-
-    if (script->functionNonDelazifying()) {
-        JSAtom* atom = script->functionNonDelazifying()->displayAtom();
-        if (atom) {
-            if (!AppendJSONProperty(buf, "name")) {
-                return nullptr;
-            }
-            if (!(str = StringToSource(cx, atom))) {
-                return nullptr;
-            }
-            if (!buf.append(str)) {
+    if (!JSONStringProperty(sp, json, "file", filename)) {
+        return nullptr;
+    }
+    json.property("line", script->lineno());
+
+    if (JSFunction* fun = script->functionNonDelazifying()) {
+        if (JSAtom* atom = fun->displayAtom()) {
+            if (!JSONStringProperty(sp, json, "name", atom)) {
                 return nullptr;
             }
         }
     }
 
     uint64_t total = 0;
 
     jsbytecode* codeEnd = script->codeEnd();
     for (jsbytecode* pc = script->code(); pc < codeEnd; pc = GetNextPc(pc)) {
-        const PCCounts* counts = sac.maybeGetPCCounts(pc);
-        if (!counts) {
-            continue;
+        if (const PCCounts* counts = sac.maybeGetPCCounts(pc)) {
+            total += counts->numExec();
         }
-        total += counts->numExec();
-    }
-
-    if (!AppendJSONProperty(buf, "totals")) {
-        return nullptr;
     }
-    if (!buf.append('{')) {
-        return nullptr;
-    }
-
-    if (!AppendJSONProperty(buf, PCCounts::numExecName, NO_COMMA)) {
-        return nullptr;
-    }
-    if (!NumberValueToStringBuffer(cx, DoubleValue(total), buf)) {
-        return nullptr;
-    }
+
+    json.beginObjectProperty("totals");
+
+    json.property(PCCounts::numExecName, total);
 
     uint64_t ionActivity = 0;
     jit::IonScriptCounts* ionCounts = sac.getIonCounts();
     while (ionCounts) {
         for (size_t i = 0; i < ionCounts->numBlocks(); i++) {
             ionActivity += ionCounts->block(i).hitCount();
         }
         ionCounts = ionCounts->previous();
     }
     if (ionActivity) {
-        if (!AppendJSONProperty(buf, "ion", COMMA)) {
-            return nullptr;
-        }
-        if (!NumberValueToStringBuffer(cx, DoubleValue(ionActivity), buf)) {
-            return nullptr;
-        }
+        json.property("ion", ionActivity);
     }
 
-    if (!buf.append('}')) {
+    json.endObject();
+
+    json.endObject();
+
+    if (sp.hadOutOfMemory()) {
         return nullptr;
     }
-    if (!buf.append('}')) {
-        return nullptr;
-    }
-
-    MOZ_ASSERT(!cx->isExceptionPending());
-
-    return buf.finishString();
+
+    return NewStringCopyZ<CanGC>(cx, sp.string());
 }
 
 static bool
-GetPCCountJSON(JSContext* cx, const ScriptAndCounts& sac, StringBuffer& buf)
+GetPCCountJSON(JSContext* cx, const ScriptAndCounts& sac, Sprinter& sp)
 {
+    JSONPrinter json(sp, false);
+
     RootedScript script(cx, sac.script);
 
-    if (!buf.append('{')) {
-        return false;
-    }
-    if (!AppendJSONProperty(buf, "text", NO_COMMA)) {
-        return false;
-    }
-
-    JSString* str = JS_DecompileScript(cx, script);
-    if (!str || !(str = StringToSource(cx, str))) {
-        return false;
-    }
-
-    if (!buf.append(str)) {
+    BytecodeParser parser(cx, script);
+    if (!parser.parse()) {
         return false;
     }
 
-    if (!AppendJSONProperty(buf, "line")) {
-        return false;
-    }
-    if (!NumberValueToStringBuffer(cx, Int32Value(script->lineno()), buf)) {
+    json.beginObject();
+
+    JSString* str = JS_DecompileScript(cx, script);
+    if (!str) {
         return false;
     }
 
-    if (!AppendJSONProperty(buf, "opcodes")) {
+    if (!JSONStringProperty(sp, json, "text", str)) {
         return false;
     }
-    if (!buf.append('[')) {
-        return false;
-    }
-    bool comma = false;
+
+    json.property("line", script->lineno());
+
+    json.beginListProperty("opcodes");
 
     uint64_t hits = 0;
-
     for (BytecodeRangeWithPosition range(cx, script); !range.empty(); range.popFront()) {
         jsbytecode *pc = range.frontPC();
         size_t offset = script->pcToOffset(pc);
         JSOp op = JSOp(*pc);
 
         // If the current instruction is a jump target,
         // then update the number of hits.
-        const PCCounts* counts = sac.maybeGetPCCounts(pc);
-        if (counts) {
+        if (const PCCounts* counts = sac.maybeGetPCCounts(pc)) {
             hits = counts->numExec();
         }
 
-        if (comma && !buf.append(',')) {
-            return false;
-        }
-        comma = true;
-
-        if (!buf.append('{')) {
-            return false;
-        }
-
-        if (!AppendJSONProperty(buf, "id", NO_COMMA)) {
-            return false;
-        }
-        if (!NumberValueToStringBuffer(cx, Int32Value(offset), buf)) {
-            return false;
-        }
-
-        if (!AppendJSONProperty(buf, "line")) {
-            return false;
-        }
-        if (!NumberValueToStringBuffer(cx, Int32Value(range.frontLineNumber()), buf)) {
-            return false;
-        }
+        json.beginObject();
+
+        json.property("id", offset);
+        json.property("line", range.frontLineNumber());
+        json.property("name", CodeName[op]);
 
         {
-            const char* name = CodeName[op];
-            if (!AppendJSONProperty(buf, "name")) {
-                return false;
-            }
-            if (!buf.append('\"')) {
-                return false;
-            }
-            if (!buf.append(name, strlen(name))) {
-                return false;
-            }
-            if (!buf.append('\"')) {
-                return false;
-            }
-        }
-
-        {
-            BytecodeParser parser(cx, script);
-            if (!parser.parse()) {
-                return false;
-            }
             ExpressionDecompiler ed(cx, script, parser);
             if (!ed.init()) {
                 return false;
             }
             // defIndex passed here is not used.
             if (!ed.decompilePC(pc, /* defIndex = */ 0)) {
                 return false;
             }
             UniqueChars text = ed.getOutput();
             if (!text) {
                 return false;
             }
+
             JS::ConstUTF8CharsZ utf8chars(text.get(), strlen(text.get()));
             JSString* str = NewStringCopyUTF8Z<CanGC>(cx, utf8chars);
-            if (!AppendJSONProperty(buf, "text")) {
+            if (!str) {
                 return false;
             }
-            if (!str || !(str = StringToSource(cx, str))) {
-                return false;
-            }
-            if (!buf.append(str)) {
+
+            if (!JSONStringProperty(sp, json, "text", str)) {
                 return false;
             }
         }
 
-        if (!AppendJSONProperty(buf, "counts")) {
-            return false;
-        }
-        if (!buf.append('{')) {
-            return false;
-        }
-
+        json.beginObjectProperty("counts");
         if (hits > 0) {
-            if (!AppendJSONProperty(buf, PCCounts::numExecName, NO_COMMA)) {
-                return false;
-            }
-            if (!NumberValueToStringBuffer(cx, DoubleValue(hits), buf)) {
-                return false;
-            }
+            json.property(PCCounts::numExecName, hits);
         }
-
-        if (!buf.append('}')) {
-            return false;
-        }
-        if (!buf.append('}')) {
-            return false;
-        }
+        json.endObject();
+
+        json.endObject();
 
         // If the current instruction has thrown,
         // then decrement the hit counts with the number of throws.
-        counts = sac.maybeGetThrowCounts(pc);
-        if (counts) {
+        if (const PCCounts* counts = sac.maybeGetThrowCounts(pc)) {
             hits -= counts->numExec();
         }
     }
 
-    if (!buf.append(']')) {
-        return false;
-    }
-
-    jit::IonScriptCounts* ionCounts = sac.getIonCounts();
-    if (ionCounts) {
-        if (!AppendJSONProperty(buf, "ion")) {
-            return false;
-        }
-        if (!buf.append('[')) {
-            return false;
-        }
-        bool comma = false;
+    json.endList();
+
+    if (jit::IonScriptCounts* ionCounts = sac.getIonCounts()) {
+        json.beginListProperty("ion");
+
         while (ionCounts) {
-            if (comma && !buf.append(',')) {
-                return false;
-            }
-            comma = true;
-
-            if (!buf.append('[')) {
-                return false;
-            }
+            json.beginList();
             for (size_t i = 0; i < ionCounts->numBlocks(); i++) {
-                if (i && !buf.append(',')) {
-                    return false;
+                const jit::IonBlockCounts& block = ionCounts->block(i);
+
+                json.beginObject();
+                json.property("id", block.id());
+                json.property("offset", block.offset());
+
+                json.beginListProperty("successors");
+                for (size_t j = 0; j < block.numSuccessors(); j++) {
+                    json.value(block.successor(j));
                 }
-                const jit::IonBlockCounts& block = ionCounts->block(i);
-