Merge mozilla-central to inbound. a=merge CLOSED TREE
authorshindli <shindli@mozilla.com>
Thu, 14 Feb 2019 06:58:32 +0200
changeset 517001 a0752d7e807345f354f592eda5f09e823c3e1a5a
parent 517000 328de611bcd3d1433140603488b131ae1c859236 (current diff)
parent 516873 f0ea53f47215d2449c7e759fa36aa76863ced3ff (diff)
child 517002 68a001a424062bc60dd8ed56ae263162f1d56d6c
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
browser/base/content/browser.js
dom/script/ScriptLoader.cpp
mobile/android/base/java/org/mozilla/gecko/GeckoFontScaleListener.java
mobile/android/components/BrowserCLH.js
testing/raptor/raptor/playback/python3.manifest
testing/raptor/raptor/playback/python3_x64.manifest
testing/web-platform/meta/css/css-device-adapt/documentElement-clientWidth-on-minimum-scale-size.tentative.html.ini
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,10 +1,8 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
 [[package]]
 name = "Inflector"
 version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -3084,16 +3082,17 @@ dependencies = [
  "app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1809,8 +1809,10 @@ pref("browser.discovery.sites", "addons.
 pref("browser.engagement.recent_visited_origins.expiry", 86400); // 24 * 60 * 60 (24 hours in seconds)
 
 pref("browser.aboutConfig.showWarning", true);
 
 #if defined(XP_WIN) && defined(MOZ_LAUNCHER_PROCESS)
 // Launcher process is disabled by default, will be selectively enabled via SHIELD
 pref("browser.launcherProcess.enabled", false);
 #endif // defined(XP_WIN) && defined(MOZ_LAUNCHER_PROCESS)
+
+pref("browser.toolbars.keyboard_navigation", false);
new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser-toolbarKeyNav.js
@@ -0,0 +1,246 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This file is loaded into the browser window scope.
+/* eslint-env mozilla/browser-window */
+
+/**
+ * Handle keyboard navigation for toolbars.
+ * Having separate tab stops for every toolbar control results in an
+ * unmanageable number of tab stops. Therefore, we group buttons under a single
+ * tab stop and allow movement between them using left/right arrows.
+ * However, text inputs use the arrow keys for their own purposes, so they need
+ * their own tab stop. There are also groups of buttons before and after the
+ * URL bar input which should get their own tab stop. The subsequent buttons on
+ * the toolbar are then another tab stop after that.
+ * Tab stops for groups of buttons are set using the <toolbartabstop/> element.
+ * This element is invisible, but gets included in the tab order. When one of
+ * these gets focus, it redirects focus to the appropriate button. This avoids
+ * the need to continually manage the tabindex of toolbar buttons in response to
+ * toolbarchanges.
+ */
+
+ToolbarKeyboardNavigator = {
+  // Toolbars we want to be keyboard navigable.
+  kToolbars: [CustomizableUI.AREA_NAVBAR, CustomizableUI.AREA_BOOKMARKS],
+
+  _isButton(aElem) {
+    return aElem.tagName == "toolbarbutton" ||
+      aElem.getAttribute("role") == "button";
+  },
+
+  // Get a TreeWalker which includes only controls which should be keyboard
+  // navigable.
+  _getWalker(aRoot) {
+    if (aRoot._toolbarKeyNavWalker) {
+      return aRoot._toolbarKeyNavWalker;
+    }
+
+    let filter = (aNode) => {
+      if (aNode.tagName == "toolbartabstop") {
+        return NodeFilter.FILTER_ACCEPT;
+      }
+
+      // Special case for the "View site information" button, which isn't
+      // actionable in some cases but is still visible.
+      if (aNode.id == "identity-box" &&
+          document.getElementById("urlbar").getAttribute("pageproxystate") ==
+          "invalid") {
+        return NodeFilter.FILTER_REJECT;
+      }
+
+      // Skip invisible or disabled elements.
+      if (aNode.hidden || aNode.disabled) {
+        return NodeFilter.FILTER_REJECT;
+      }
+      // This width check excludes the overflow button when there's no overflow.
+      let bounds = window.windowUtils.getBoundsWithoutFlushing(aNode);
+      if (bounds.width == 0) {
+        return NodeFilter.FILTER_REJECT;
+      }
+
+      if (this._isButton(aNode)) {
+        return NodeFilter.FILTER_ACCEPT;
+      }
+      return NodeFilter.FILTER_SKIP;
+    };
+    aRoot._toolbarKeyNavWalker = document.createTreeWalker(aRoot,
+      NodeFilter.SHOW_ELEMENT, filter);
+    return aRoot._toolbarKeyNavWalker;
+  },
+
+  init() {
+    for (let id of this.kToolbars) {
+      let toolbar = document.getElementById(id);
+      // When enabled, no toolbar buttons should themselves be tabbable.
+      // We manage toolbar focus completely. This attribute ensures that CSS
+      // doesn't set -moz-user-focus: normal.
+      toolbar.setAttribute("keyNav", "true");
+      for (let stop of toolbar.getElementsByTagName("toolbartabstop")) {
+        // These are invisible, but because they need to be in the tab order,
+        // they can't get display: none or similar. They must therefore be
+        // explicitly hidden for accessibility.
+        stop.setAttribute("aria-hidden", "true");
+        stop.addEventListener("focus", this);
+      }
+      toolbar.addEventListener("keydown", this);
+      toolbar.addEventListener("keypress", this);
+    }
+  },
+
+  uninit() {
+    for (let id of this.kToolbars) {
+      let toolbar = document.getElementById(id);
+      for (let stop of toolbar.getElementsByTagName("toolbartabstop")) {
+        stop.removeEventListener("focus", this);
+      }
+      toolbar.removeEventListener("keydown", this);
+      toolbar.removeEventListener("keypress", this);
+      toolbar.removeAttribute("keyNav");
+    }
+  },
+
+  _focusButton(aButton) {
+    // Toolbar buttons aren't focusable because if they were, clicking them
+    // would focus them, which is undesirable. Therefore, we must make a
+    // button focusable only when we want to focus it.
+    aButton.setAttribute("tabindex", "-1");
+    aButton.focus();
+    // We could remove tabindex now, but even though the button keeps DOM
+    // focus, a11y gets confused because the button reports as not being
+    // focusable. This results in weirdness if the user switches windows and
+    // then switches back. Instead, remove tabindex when the button loses
+    // focus.
+    aButton.addEventListener("blur", this);
+  },
+
+  _onButtonBlur(aEvent) {
+    if (document.activeElement == aEvent.target) {
+      // This event was fired because the user switched windows. This button
+      // will get focus again when the user returns.
+      return;
+    }
+    aEvent.target.removeEventListener("blur", this);
+    aEvent.target.removeAttribute("tabindex");
+  },
+
+  _onTabStopFocus(aEvent) {
+    let toolbar = aEvent.target.closest("toolbar");
+    let walker = this._getWalker(toolbar);
+
+    let oldFocus = aEvent.relatedTarget;
+    if (oldFocus) {
+      // Save this because we might rewind focus and the subsequent focus event
+      // won't get a relatedTarget.
+      this._isFocusMovingBackward =
+        oldFocus.compareDocumentPosition(aEvent.target) &
+        Node.DOCUMENT_POSITION_PRECEDING;
+      if (this._isFocusMovingBackward && oldFocus && this._isButton(oldFocus)) {
+        // Shift+tabbing from a button will land on its toolbartabstop. Skip it.
+        document.commandDispatcher.rewindFocus();
+        return;
+      }
+    }
+
+    walker.currentNode = aEvent.target;
+    let button = walker.nextNode();
+    if (!button || !this._isButton(button)) {
+      // No navigable buttons for this tab stop. Skip it.
+      if (this._isFocusMovingBackward) {
+        document.commandDispatcher.rewindFocus();
+      } else {
+        document.commandDispatcher.advanceFocus();
+      }
+      return;
+    }
+
+    this._focusButton(button);
+  },
+
+  navigateButtons(aToolbar, aPrevious) {
+    let oldFocus = document.activeElement;
+    let walker = this._getWalker(aToolbar);
+    // Start from the current control and walk to the next/previous control.
+    walker.currentNode = oldFocus;
+    let newFocus;
+    if (aPrevious) {
+      newFocus = walker.previousNode();
+    } else {
+      newFocus = walker.nextNode();
+    }
+    if (!newFocus || newFocus.tagName == "toolbartabstop") {
+      // There are no more controls or we hit a tab stop placeholder.
+      return;
+    }
+    this._focusButton(newFocus);
+  },
+
+  _onKeyDown(aEvent) {
+    let focus = document.activeElement;
+    if (aEvent.altKey || aEvent.controlKey || aEvent.metaKey ||
+        aEvent.shiftKey || !this._isButton(focus)) {
+      return;
+    }
+
+    switch (aEvent.key) {
+      case "ArrowLeft":
+        this.navigateButtons(aEvent.currentTarget, true);
+        break;
+      case "ArrowRight":
+        this.navigateButtons(aEvent.currentTarget, false);
+        break;
+      default:
+        return;
+    }
+    aEvent.preventDefault();
+  },
+
+  _onKeyPress(aEvent) {
+    let focus = document.activeElement;
+    if ((aEvent.key != "Enter" && aEvent.key != " ") ||
+        !this._isButton(focus)) {
+      return;
+    }
+
+    if (focus.getAttribute("type") == "menu") {
+      focus.open = true;
+    } else {
+      // Several buttons specifically don't use command events; e.g. because
+      // they want to activate for middle click. Therefore, simulate a
+      // click event.
+      // If this button does handle command events, that won't trigger here.
+      // Command events have their own keyboard handling: keypress for enter
+      // and keyup for space. We rely on that behavior, since there's no way
+      // for us to reliably know what events a button handles.
+      focus.dispatchEvent(new MouseEvent("click", {
+        bubbles: true,
+        ctrlKey: aEvent.ctrlKey,
+        altKey: aEvent.altKey,
+        shiftKey: aEvent.shiftKey,
+        metaKey: aEvent.metaKey,
+      }));
+    }
+    // We deliberately don't call aEvent.preventDefault() here so that enter
+    // will trigger a command event handler if appropriate.
+    aEvent.stopPropagation();
+  },
+
+  handleEvent(aEvent) {
+    switch (aEvent.type) {
+      case "focus":
+        this._onTabStopFocus(aEvent);
+        break;
+      case "keydown":
+        this._onKeyDown(aEvent);
+        break;
+      case "keypress":
+        this._onKeyPress(aEvent);
+        break;
+      case "blur":
+        this._onButtonBlur(aEvent);
+        break;
+    }
+  },
+
+};
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -773,17 +773,17 @@ html|input.urlbar-input {
 #urlbar[pageproxystate=invalid] > #page-action-buttons > .urlbar-page-action,
 #identity-box.chromeUI ~ #page-action-buttons > .urlbar-page-action:not(#star-button-box),
 .urlbar-history-dropmarker[usertyping],
 .urlbar-go-button:not([usertyping]),
 .urlbar-go-button:not([parentfocused="true"]) {
   display: none;
 }
 
-#identity-box {
+#nav-bar:not([keyNav=true]) #identity-box {
   -moz-user-focus: normal;
 }
 
 /* We leave 49ch plus whatever space the download button will need when it
  * appears. Normally this should be 16px for the icon, plus 2 * 2px padding
  * plus the toolbarbutton-inner-padding. We're adding 4px to ensure things
  * like rounding on hidpi don't accidentally result in the button going
  * into overflow.
@@ -976,17 +976,17 @@ html|*.pointerlockfswarning-domain-text:
 }
 
 html|*#fullscreen-exit-button {
   pointer-events: auto;
 }
 
 /* notification anchors should only be visible when their associated
    notifications are */
-.notification-anchor-icon {
+#nav-bar:not([keyNav=true]) .notification-anchor-icon {
   -moz-user-focus: normal;
 }
 
 #blocked-permissions-container > .blocked-permission-icon:not([showing]),
 .notification-anchor-icon:not([showing]) {
   display: none;
 }
 
@@ -1422,9 +1422,13 @@ toolbarpaletteitem > toolbaritem {
 }
 
 @media (min-resolution: 1.1dppx) {
   #sidebar-box[sidebarcommand$="-sidebar-action"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
     list-style-image: var(--webextension-menuitem-image-2x, inherit);
   }
 }
 
+toolbar[keyNav=true]:not([collapsed=true]):not([customizing=true]) toolbartabstop {
+  -moz-user-focus: normal;
+}
+
 %include theme-vars.inc.css
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -143,16 +143,18 @@ XPCOMUtils.defineLazyScriptGetter(this, 
 XPCOMUtils.defineLazyScriptGetter(this, "SearchOneOffs",
                                   "chrome://browser/content/search/search-one-offs.js");
 if (AppConstants.NIGHTLY_BUILD) {
   XPCOMUtils.defineLazyScriptGetter(this, "gWebRender",
                                     "chrome://browser/content/browser-webrender.js");
 }
 
 XPCOMUtils.defineLazyScriptGetter(this, "pktUI", "chrome://pocket/content/main.js");
+XPCOMUtils.defineLazyScriptGetter(this, "ToolbarKeyboardNavigator",
+  "chrome://browser/content/browser-toolbarKeyNav.js");
 
 // lazy service getters
 
 XPCOMUtils.defineLazyServiceGetters(this, {
   classifierService: ["@mozilla.org/url-classifier/dbservice;1", "nsIURIClassifier"],
   Favicons: ["@mozilla.org/browser/favicon-service;1", "nsIFaviconService"],
   gAboutNewTabService: ["@mozilla.org/browser/aboutnewtab-service;1", "nsIAboutNewTabService"],
   gDNSService: ["@mozilla.org/network/dns-service;1", "nsIDNSService"],
@@ -278,16 +280,26 @@ XPCOMUtils.defineLazyGetter(this, "Win7F
         }
       },
       handledOpening: false,
     };
   }
   return null;
 });
 
+XPCOMUtils.defineLazyPreferenceGetter(this, "gToolbarKeyNavEnabled",
+  "browser.toolbars.keyboard_navigation", false,
+  (aPref, aOldVal, aNewVal) => {
+    if (aNewVal) {
+      ToolbarKeyboardNavigator.init();
+    } else {
+      ToolbarKeyboardNavigator.uninit();
+    }
+  });
+
 customElements.setElementCreationCallback("translation-notification", () => {
   Services.scriptloader.loadSubScript(
     "chrome://browser/content/translation-notification.js", window);
 });
 
 var gBrowser;
 var gLastValidURLStr = "";
 var gInPrintPreviewMode = false;
@@ -1379,16 +1391,19 @@ var gBrowserInit = {
     gUIDensity.init();
     TabletModeUpdater.init();
     CombinedStopReload.ensureInitialized();
     gPrivateBrowsingUI.init();
     BrowserSearch.init();
     BrowserPageActions.init();
     gAccessibilityServiceIndicator.init();
     AccessibilityRefreshBlocker.init();
+    if (gToolbarKeyNavEnabled) {
+      ToolbarKeyboardNavigator.init();
+    }
 
     gRemoteControl.updateVisualCue(Marionette.running);
 
     // If we are given a tab to swap in, take care of it before first paint to
     // avoid an about:blank flash.
     let tabToAdopt = this.getTabToAdopt();
     if (tabToAdopt) {
       // Stop the about:blank load
@@ -1925,16 +1940,20 @@ var gBrowserInit = {
     SidebarUI.uninit();
 
     DownloadsButton.uninit();
 
     gAccessibilityServiceIndicator.uninit();
 
     AccessibilityRefreshBlocker.uninit();
 
+    if (gToolbarKeyNavEnabled) {
+      ToolbarKeyboardNavigator.uninit();
+    }
+
     LanguagePrompt.uninit();
 
     BrowserSearch.uninit();
 
     // Now either cancel delayedStartup, or clean up the services initialized from
     // it.
     if (this._boundDelayedStartup) {
       this._cancelDelayedStartup();
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -830,16 +830,17 @@ xmlns="http://www.w3.org/1999/xhtml"
              fullscreentoolbar="true" mode="icons" customizable="true"
              customizationtarget="nav-bar-customization-target"
              overflowable="true"
              overflowbutton="nav-bar-overflow-button"
              overflowtarget="widget-overflow-list"
              overflowpanel="widget-overflow"
              context="toolbar-context-menu">
 
+      <toolbartabstop/>
       <hbox id="nav-bar-customization-target" flex="1">
         <toolbarbutton id="back-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                        label="&backCmd.label;"
                        removable="false" overflows="false"
                        keepbroadcastattributeswhencustomizing="true"
                        command="Browser:BackOrBackDuplicate"
                        onclick="checkForMiddleClick(this, event);"
                        tooltip="back-button-tooltip"
@@ -883,16 +884,17 @@ xmlns="http://www.w3.org/1999/xhtml"
                        key="goHome"
                        onclick="BrowserHome(event);"
                        cui-areatype="toolbar"
                        tooltiptext="&homeButton.defaultPage.tooltip;"/>
         <toolbarspring cui-areatype="toolbar" class="chromeclass-toolbar-additional"/>
         <toolbaritem id="urlbar-container" flex="400" persist="width"
                      removable="false"
                      class="chromeclass-location" overflows="false">
+            <toolbartabstop/>
             <textbox id="urlbar" flex="1"
                      placeholder="&urlbar.placeholder2;"
                      defaultPlaceholder="&urlbar.placeholder2;"
                      focused="true"
                      type="autocomplete"
                      autocompletesearch="unifiedcomplete"
                      autocompletesearchparam="enable-actions"
                      autocompletepopup="PopupAutoCompleteRichResult"
@@ -1002,16 +1004,17 @@ xmlns="http://www.w3.org/1999/xhtml"
                   <label id="identity-icon-country-label" class="plain"/>
                 </hbox>
               </box>
               <box id="urlbar-display-box" align="center">
                 <label id="switchtab" class="urlbar-display urlbar-display-switchtab" value="&urlbar.switchToTab.label;"/>
                 <label id="extension" class="urlbar-display urlbar-display-extension" value="&urlbar.extension.label;"/>
               </box>
               <hbox id="page-action-buttons" context="pageActionContextMenu">
+                <toolbartabstop/>
                 <hbox id="contextual-feature-recommendation" role="button" hidden="true">
                   <hbox id="cfr-label-container">
                     <label id="cfr-label"/>
                   </hbox>
                   <image id="cfr-button"
                          class="urlbar-icon urlbar-page-action"
                          role="presentation"/>
                 </hbox>
@@ -1059,16 +1062,17 @@ xmlns="http://www.w3.org/1999/xhtml"
                          role="button"/>
                   <hbox id="star-button-animatable-box">
                     <image id="star-button-animatable-image"
                            role="presentation"/>
                   </hbox>
                 </hbox>
               </hbox>
             </textbox>
+            <toolbartabstop/>
         </toolbaritem>
 
         <toolbarspring cui-areatype="toolbar" class="chromeclass-toolbar-additional"/>
 
         <!-- This is a placeholder for the Downloads Indicator.  It is visible
              during the customization of the toolbar, in the palette, and before
              the Downloads Indicator overlay is loaded. -->
         <toolbarbutton id="downloads-button"
@@ -1154,16 +1158,17 @@ xmlns="http://www.w3.org/1999/xhtml"
 
     <toolbar id="PersonalToolbar"
              mode="icons"
              class="browser-toolbar chromeclass-directories"
              context="toolbar-context-menu"
              toolbarname="&personalbarCmd.label;" accesskey="&personalbarCmd.accesskey;"
              collapsed="true"
              customizable="true">
+      <toolbartabstop skipintoolbarset="true"/>
       <toolbaritem id="personal-bookmarks"
                    title="&bookmarksToolbarItem.label;"
                    cui-areatype="toolbar"
                    removable="true">
         <toolbarbutton id="bookmarks-toolbar-placeholder"
                        class="bookmark-item"
                        label="&bookmarksToolbarItem.label;"/>
         <toolbarbutton id="bookmarks-toolbar-button"
@@ -1323,17 +1328,19 @@ xmlns="http://www.w3.org/1999/xhtml"
       </toolbarbutton>
 
       <toolbaritem id="search-container"
                    class="chromeclass-toolbar-additional"
                    title="&searchItem.title;"
                    align="center"
                    flex="100"
                    persist="width">
+        <toolbartabstop/>
         <searchbar id="searchbar" flex="1"/>
+        <toolbartabstop/>
       </toolbaritem>
     </toolbarpalette>
   </toolbox>
 
   <hbox id="fullscr-toggler" hidden="true"/>
 
   <deck id="content-deck" flex="1">
     <hbox flex="1" id="browser">
--- a/browser/base/content/test/keyboard/browser.ini
+++ b/browser/base/content/test/keyboard/browser.ini
@@ -1,1 +1,3 @@
 [browser_toolbarButtonKeyPress.js]
+[browser_toolbarKeyNav.js]
+support-files = !/browser/base/content/test/permissions/permissions.html
--- a/browser/base/content/test/keyboard/browser_toolbarButtonKeyPress.js
+++ b/browser/base/content/test/keyboard/browser_toolbarButtonKeyPress.js
@@ -25,16 +25,22 @@ function waitForLocationChange() {
         resolve();
       },
     };
     gBrowser.addProgressListener(wpl);
   });
   return promise;
 }
 
+add_task(async function setPref() {
+  await SpecialPowers.pushPrefEnv({
+    set: [["browser.toolbars.keyboard_navigation", true]],
+  });
+});
+
 // Test activation of the app menu button from the keyboard.
 // The app menu should appear and focus should move inside it.
 add_task(async function testAppMenuButtonPress() {
   let button = document.getElementById("PanelUI-menu-button");
   forceFocus(button);
   let focused = BrowserTestUtils.waitForEvent(window.PanelUI.mainView, "focus", true);
   EventUtils.synthesizeKey(" ");
   await focused;
@@ -81,16 +87,17 @@ add_task(async function testDeveloperBut
   EventUtils.synthesizeKey(" ");
   await focused;
   ok(true, "Focus inside Developer menu after toolbar button pressed");
   let hidden = BrowserTestUtils.waitForEvent(document, "popuphidden", true);
   view.closest("panel").hidePopup();
   await hidden;
   CustomizableUI.reset();
 });
+
 // Test that the Developer menu doesn't open when a key other than space or
 // enter is pressed .
 add_task(async function testDeveloperButtonWrongKey() {
   CustomizableUI.addWidgetToArea("developer-button", CustomizableUI.AREA_NAVBAR);
   let button = document.getElementById("developer-button");
   forceFocus(button);
   EventUtils.synthesizeKey("KEY_Tab");
   await TestUtils.waitForTick();
@@ -154,8 +161,85 @@ add_task(async function testSendTabToDev
     ok(view.contains(document.activeElement),
        "Focus inside Page Actions menu after toolbar button pressed");
     let hidden = BrowserTestUtils.waitForEvent(document, "popuphidden", true);
     view.closest("panel").hidePopup();
     await hidden;
     PageActions.actionForID("sendToDevice").pinnedToUrlbar = false;
   });
 });
+
+// Test activation of the Reload button from the keyboard.
+// This is a toolbarbutton with a click handler and no command handler, but
+// the toolbar keyboard navigation code should handle keyboard activation.
+add_task(async function testReloadButtonPress() {
+  await BrowserTestUtils.withNewTab("https://example.com", async function(aBrowser) {
+    let button = document.getElementById("reload-button");
+    await TestUtils.waitForCondition(() => !button.disabled);
+    forceFocus(button);
+    let loaded = BrowserTestUtils.browserLoaded(aBrowser);
+    EventUtils.synthesizeKey(" ");
+    await loaded;
+    ok(true, "Page loaded after Reload button pressed");
+  });
+});
+
+// Test activation of the Sidebars button from the keyboard.
+// This is a toolbarbutton with a command handler.
+add_task(async function testSidebarsButtonPress() {
+  let button = document.getElementById("sidebar-button");
+  ok(!button.checked, "Sidebars button not checked at start of test");
+  let sidebarBox = document.getElementById("sidebar-box");
+  ok(sidebarBox.hidden, "Sidebar hidden at start of test");
+  forceFocus(button);
+  EventUtils.synthesizeKey(" ");
+  await TestUtils.waitForCondition(() => button.checked);
+  ok(true, "Sidebars button checked after press");
+  ok(!sidebarBox.hidden, "Sidebar visible after press");
+  // Make sure the sidebar is fully loaded before we hide it.
+  // Otherwise, the unload event might call JS which isn't loaded yet.
+  // We can't use BrowserTestUtils.browserLoaded because it fails on non-tab
+  // docs. Instead, wait for something in the JS script.
+  let sidebarWin = document.getElementById("sidebar").contentWindow;
+  await TestUtils.waitForCondition(() => sidebarWin.PlacesUIUtils);
+  forceFocus(button);
+  EventUtils.synthesizeKey(" ");
+  await TestUtils.waitForCondition(() => !button.checked);
+  ok(true, "Sidebars button not checked after press");
+  ok(sidebarBox.hidden, "Sidebar hidden after press");
+});
+
+// Test activation of the Bookmark this page button from the keyboard.
+// This is an image with a click handler on its parent and no command handler,
+// but the toolbar keyboard navigation code should handle keyboard activation.
+add_task(async function testBookmarkButtonPress() {
+  await BrowserTestUtils.withNewTab("https://example.com", async function(aBrowser) {
+    let button = document.getElementById("star-button");
+    forceFocus(button);
+    let panel = document.getElementById("editBookmarkPanel");
+    let focused = BrowserTestUtils.waitForEvent(panel, "focus", true);
+    EventUtils.synthesizeKey(" ");
+    await focused;
+    ok(true, "Focus inside edit bookmark panel after Bookmark button pressed");
+    let hidden = BrowserTestUtils.waitForEvent(panel, "popuphidden");
+    EventUtils.synthesizeKey("KEY_Escape");
+    await hidden;
+  });
+});
+
+// Test activation of the Bookmarks Menu button from the keyboard.
+// This is a button with type="menu".
+// The Bookmarks Menu should appear.
+add_task(async function testBookmarksmenuButtonPress() {
+  CustomizableUI.addWidgetToArea("bookmarks-menu-button",
+                                 CustomizableUI.AREA_NAVBAR);
+  let button = document.getElementById("bookmarks-menu-button");
+  forceFocus(button);
+  let menu = document.getElementById("BMB_bookmarksPopup");
+  let shown = BrowserTestUtils.waitForEvent(menu, "popupshown");
+  EventUtils.synthesizeKey(" ");
+  await shown;
+  ok(true, "Bookmarks Menu shown after toolbar button pressed");
+  let hidden = BrowserTestUtils.waitForEvent(menu, "popuphidden");
+  menu.hidePopup();
+  await hidden;
+  CustomizableUI.reset();
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/keyboard/browser_toolbarKeyNav.js
@@ -0,0 +1,219 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Test browser toolbar keyboard navigation.
+ * These tests assume the default browser configuration for toolbars unless
+ * otherwise specified.
+ */
+
+const PERMISSIONS_PAGE = "https://example.com/browser/browser/base/content/test/permissions/permissions.html";
+
+async function expectFocusAfterKey(aKey, aFocus, aAncestorOk = false) {
+  let res = aKey.match(/^(Shift\+)?(?:(.)|(.+))$/);
+  let shift = Boolean(res[1]);
+  let key;
+  if (res[2]) {
+    key = res[2]; // Character.
+  } else {
+    key = "KEY_" + res[3]; // Tab, ArrowRight, etc.
+  }
+  let expected;
+  let friendlyExpected;
+  if (typeof aFocus == "string") {
+    expected = document.getElementById(aFocus);
+    friendlyExpected = aFocus;
+  } else {
+    expected = aFocus;
+    if (aFocus == gURLBar.inputField) {
+      friendlyExpected = "URL bar input";
+    } else if (aFocus == gBrowser.selectedBrowser) {
+      friendlyExpected = "Web document";
+    }
+  }
+  let focused = BrowserTestUtils.waitForEvent(expected, "focus", aAncestorOk);
+  EventUtils.synthesizeKey(key, {shiftKey: shift});
+  await focused;
+  ok(true, friendlyExpected + " focused after " + aKey + " pressed");
+}
+
+function startFromUrlBar() {
+  gURLBar.focus();
+  is(document.activeElement, gURLBar.inputField,
+     "URL bar focused for start of test");
+}
+
+// The Reload button is disabled for a short time even after the page finishes
+// loading. Wait for it to be enabled.
+async function waitUntilReloadEnabled() {
+  let button = document.getElementById("reload-button");
+  await TestUtils.waitForCondition(() => !button.disabled);
+}
+
+add_task(async function setPref() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.toolbars.keyboard_navigation", true],
+      ["accessibility.tabfocus", 7],
+    ],
+  });
+});
+
+// Test tab stops with no page loaded.
+add_task(async function testTabStopsNoPage() {
+  await BrowserTestUtils.withNewTab("about:blank", async function() {
+    startFromUrlBar();
+    await expectFocusAfterKey("Shift+Tab", "home-button");
+    await expectFocusAfterKey("Shift+Tab", "tabbrowser-tabs", true);
+    await expectFocusAfterKey("Tab", "home-button");
+    await expectFocusAfterKey("Tab", gURLBar.inputField);
+    await expectFocusAfterKey("Tab", "library-button");
+    await expectFocusAfterKey("Tab", gBrowser.selectedBrowser);
+  });
+});
+
+// Test tab stops with a page loaded.
+add_task(async function testTabStopsPageLoaded() {
+  await BrowserTestUtils.withNewTab("https://example.com", async function() {
+    await waitUntilReloadEnabled();
+    startFromUrlBar();
+    await expectFocusAfterKey("Shift+Tab", "identity-box");
+    await expectFocusAfterKey("Shift+Tab", "reload-button");
+    await expectFocusAfterKey("Shift+Tab", "tabbrowser-tabs", true);
+    await expectFocusAfterKey("Tab", "reload-button");
+    await expectFocusAfterKey("Tab", "identity-box");
+    await expectFocusAfterKey("Tab", gURLBar.inputField);
+    await expectFocusAfterKey("Tab", "pageActionButton");
+    await expectFocusAfterKey("Tab", "library-button");
+    await expectFocusAfterKey("Tab", gBrowser.selectedBrowser);
+  });
+});
+
+// Test tab stops with a notification anchor visible.
+// The notification anchor should not get its own tab stop.
+add_task(async function testTabStopsWithNotification() {
+  await BrowserTestUtils.withNewTab(PERMISSIONS_PAGE, async function(aBrowser) {
+    let popupShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
+    // Request a permission.
+    BrowserTestUtils.synthesizeMouseAtCenter("#geo", {}, aBrowser);
+    await popupShown;
+    startFromUrlBar();
+    // If the notification anchor were in the tab order, the next shift+tab
+    // would focus it instead of #identity-box.
+    await expectFocusAfterKey("Shift+Tab", "identity-box");
+  });
+});
+
+// Test tab stops with the Bookmarks toolbar visible.
+add_task(async function testTabStopsWithBookmarksToolbar() {
+  await BrowserTestUtils.withNewTab("about:blank", async function() {
+    CustomizableUI.setToolbarVisibility("PersonalToolbar", true);
+    startFromUrlBar();
+    await expectFocusAfterKey("Tab", "library-button");
+    await expectFocusAfterKey("Tab", "PersonalToolbar", true);
+    await expectFocusAfterKey("Tab", gBrowser.selectedBrowser);
+
+    // Make sure the Bookmarks toolbar is no longer tabbable once hidden.
+    CustomizableUI.setToolbarVisibility("PersonalToolbar", false);
+    startFromUrlBar();
+    await expectFocusAfterKey("Tab", "library-button");
+    await expectFocusAfterKey("Tab", gBrowser.selectedBrowser);
+  });
+});
+
+// Test a focusable toolbartabstop which has no navigable buttons.
+add_task(async function testTabStopNoButtons() {
+  await BrowserTestUtils.withNewTab("about:blank", async function() {
+    // The Back, Forward and Reload buttons are all currently disabled.
+    // The Home button is the only other button at that tab stop.
+    CustomizableUI.removeWidgetFromArea("home-button");
+    startFromUrlBar();
+    await expectFocusAfterKey("Shift+Tab", "tabbrowser-tabs", true);
+    await expectFocusAfterKey("Tab", gURLBar.inputField);
+    CustomizableUI.reset();
+    // Make sure the button is reachable now that it has been re-added.
+    await expectFocusAfterKey("Shift+Tab", "home-button", true);
+  });
+});
+
+// Test that right/left arrows move through toolbarbuttons.
+// This also verifies that:
+// 1. Right/left arrows do nothing when at the edges; and
+// 2. The overflow menu button can't be reached by right arrow when it isn't
+// visible.
+add_task(async function testArrowsToolbarbuttons() {
+  await BrowserTestUtils.withNewTab("about:blank", async function() {
+    startFromUrlBar();
+    await expectFocusAfterKey("Tab", "library-button");
+    EventUtils.synthesizeKey("KEY_ArrowLeft");
+    is(document.activeElement.id, "library-button",
+       "ArrowLeft at end of button group does nothing");
+    await expectFocusAfterKey("ArrowRight", "sidebar-button");
+    // This next check also confirms that the overflow menu button is skipped,
+    // since it is currently invisible.
+    await expectFocusAfterKey("ArrowRight", "PanelUI-menu-button");
+    EventUtils.synthesizeKey("KEY_ArrowRight");
+    is(document.activeElement.id, "PanelUI-menu-button",
+       "ArrowRight at end of button group does nothing");
+    await expectFocusAfterKey("ArrowLeft", "sidebar-button");
+    await expectFocusAfterKey("ArrowLeft", "library-button");
+  });
+});
+
+// Test that right/left arrows move through buttons wihch aren't toolbarbuttons
+// but have role="button".
+add_task(async function testArrowsRoleButton() {
+  await BrowserTestUtils.withNewTab("https://example.com", async function() {
+    startFromUrlBar();
+    await expectFocusAfterKey("Tab", "pageActionButton");
+    await expectFocusAfterKey("ArrowRight", "pocket-button");
+    await expectFocusAfterKey("ArrowRight", "star-button");
+    await expectFocusAfterKey("ArrowLeft", "pocket-button");
+    await expectFocusAfterKey("ArrowLeft", "pageActionButton");
+  });
+});
+
+// Test that right/left arrows do not land on disabled buttons.
+add_task(async function testArrowsDisabledButtons() {
+  await BrowserTestUtils.withNewTab("https://example.com", async function(aBrowser) {
+    await waitUntilReloadEnabled();
+    startFromUrlBar();
+    await expectFocusAfterKey("Shift+Tab", "identity-box");
+    // Back and Forward buttons are disabled.
+    await expectFocusAfterKey("Shift+Tab", "reload-button");
+    EventUtils.synthesizeKey("KEY_ArrowLeft");
+    is(document.activeElement.id, "reload-button",
+       "ArrowLeft on Reload button when prior buttons disabled does nothing");
+
+    BrowserTestUtils.loadURI(aBrowser, "https://example.com/2");
+    await BrowserTestUtils.browserLoaded(aBrowser);
+    await waitUntilReloadEnabled();
+    startFromUrlBar();
+    await expectFocusAfterKey("Shift+Tab", "identity-box");
+    await expectFocusAfterKey("Shift+Tab", "back-button");
+    // Forward button is still disabled.
+    await expectFocusAfterKey("ArrowRight", "reload-button");
+  });
+});
+
+// Test that right arrow reaches the overflow menu button when it is visible.
+add_task(async function testArrowsOverflowButton() {
+  await BrowserTestUtils.withNewTab("about:blank", async function() {
+    // Move something to the overflow menu to make the button appear.
+    CustomizableUI.addWidgetToArea("home-button", CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
+    startFromUrlBar();
+    await expectFocusAfterKey("Tab", "library-button");
+    await expectFocusAfterKey("ArrowRight", "sidebar-button");
+    await expectFocusAfterKey("ArrowRight", "nav-bar-overflow-button");
+    await expectFocusAfterKey("ArrowRight", "PanelUI-menu-button");
+    await expectFocusAfterKey("ArrowLeft", "nav-bar-overflow-button");
+    // Make sure the button is not reachable once it is invisible again.
+    await expectFocusAfterKey("ArrowRight", "PanelUI-menu-button");
+    CustomizableUI.reset();
+    // Flush layout so its invisibility can be detected.
+    document.getElementById("nav-bar-overflow-button").clientWidth;
+    await expectFocusAfterKey("ArrowLeft", "sidebar-button");
+  });
+});
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -50,17 +50,18 @@ browser.jar:
         content/browser/browser-media.js              (content/browser-media.js)
         content/browser/browser-pageActions.js        (content/browser-pageActions.js)
         content/browser/browser-places.js             (content/browser-places.js)
         content/browser/browser-plugins.js            (content/browser-plugins.js)
         content/browser/browser-safebrowsing.js       (content/browser-safebrowsing.js)
         content/browser/browser-sidebar.js            (content/browser-sidebar.js)
         content/browser/browser-siteIdentity.js       (content/browser-siteIdentity.js)
         content/browser/browser-sync.js               (content/browser-sync.js)
-        content/browser/browser-tabsintitlebar.js       (content/browser-tabsintitlebar.js)
+        content/browser/browser-tabsintitlebar.js     (content/browser-tabsintitlebar.js)
+        content/browser/browser-toolbarKeyNav.js      (content/browser-toolbarKeyNav.js)
         content/browser/browser-thumbnails.js         (content/browser-thumbnails.js)
         content/browser/browser-webrender.js          (content/browser-webrender.js)
         content/browser/tab-content.js                (content/tab-content.js)
         content/browser/content.js                    (content/content.js)
         content/browser/defaultthemes/1.header.jpg    (content/defaultthemes/1.header.jpg)
         content/browser/defaultthemes/1.icon.jpg      (content/defaultthemes/1.icon.jpg)
         content/browser/defaultthemes/1.preview.jpg   (content/defaultthemes/1.preview.jpg)
         content/browser/defaultthemes/2.header.jpg    (content/defaultthemes/2.header.jpg)
--- a/browser/themes/shared/urlbar-autocomplete.inc.css
+++ b/browser/themes/shared/urlbar-autocomplete.inc.css
@@ -121,31 +121,35 @@
   color: var(--panel-disabled-color);
   margin: 0 .4em;
 }
 
 .urlbarView-title:empty + .urlbarView-secondary::before {
   display: none;
 }
 
+.urlbarView-tags,
 .urlbarView-secondary {
-  color: var(--urlbar-popup-action-color);
   font-size: .85em;
 }
 
-.urlbarView-url {
-  color: var(--urlbar-popup-url-color);
-}
-
 .urlbarView-title > strong,
 .urlbarView-url > strong,
 .urlbarView-tag > strong {
   font-weight: 600;
 }
 
+.urlbarView-secondary {
+  color: var(--urlbar-popup-action-color);
+}
+
+.urlbarView-url {
+  color: var(--urlbar-popup-url-color);
+}
+
 .urlbarView-row[selected] > .urlbarView-row-inner > .urlbarView-secondary::before,
 .urlbarView-row[selected] > .urlbarView-row-inner > .urlbarView-secondary {
   color: inherit;
 }
 
 .urlbarView-row[type=remotetab][selected] > .urlbarView-row-inner > .urlbarView-action,
 .urlbarView-row[type=remotetab]:hover > .urlbarView-row-inner > .urlbarView-action,
 .urlbarView-row[type=remotetab]:not([selected]):not(:hover) > .urlbarView-row-inner > .urlbarView-url,
--- a/devtools/client/debugger/new/src/client/firefox/create.js
+++ b/devtools/client/debugger/new/src/client/firefox/create.js
@@ -15,33 +15,27 @@ import type {
 } from "./types";
 
 import { clientCommands } from "./commands";
 
 export function createFrame(thread: ThreadId, frame: FramePacket): ?Frame {
   if (!frame) {
     return null;
   }
-  let title;
-  if (frame.type == "call") {
-    const c = frame.callee;
-    title = c.name || c.userDisplayName || c.displayName;
-  } else {
-    title = `(${frame.type})`;
-  }
+  
   const location = {
     sourceId: clientCommands.getSourceForActor(frame.where.actor),
     line: frame.where.line,
     column: frame.where.column
   };
 
   return {
     id: frame.actor,
     thread,
-    displayName: title,
+    displayName: frame.displayName,
     location,
     generatedLocation: location,
     this: frame.this,
     source: null,
     scope: frame.environment
   };
 }
 
--- a/devtools/client/debugger/new/src/utils/prefs.js
+++ b/devtools/client/debugger/new/src/utils/prefs.js
@@ -4,17 +4,17 @@
 
 // @flow
 
 import { PrefsHelper } from "devtools-modules";
 import { isDevelopment } from "devtools-environment";
 import Services from "devtools-services";
 import { asyncStoreHelper } from "./asyncStoreHelper";
 
-const prefsSchemaVersion = "1.0.7";
+const prefsSchemaVersion = "1.0.8";
 
 const pref = Services.pref;
 
 if (isDevelopment()) {
   pref("devtools.debugger.logging", false);
   pref("devtools.debugger.alphabetize-outline", false);
   pref("devtools.debugger.auto-pretty-print", false);
   pref("devtools.source-map.client-service.enabled", true);
@@ -132,13 +132,13 @@ export const asyncStore = asyncStoreHelp
   pendingBreakpoints: ["pending-breakpoints", {}],
   tabs: ["tabs", []],
   xhrBreakpoints: ["xhr-breakpoints", []],
   eventListenerBreakpoints: ["event-listener-breakpoints", []]
 });
 
 if (prefs.debuggerPrefsSchemaVersion !== prefsSchemaVersion) {
   // clear pending Breakpoints
-  prefs.pendingBreakpoints = {};
-  prefs.tabs = [];
-  prefs.xhrBreakpoints = [];
+  asyncStore.pendingBreakpoints = {};
+  asyncStore.tabs = [];
+  asyncStore.xhrBreakpoints = [];
   prefs.debuggerPrefsSchemaVersion = prefsSchemaVersion;
 }
--- a/devtools/server/actors/frame.js
+++ b/devtools/server/actors/frame.js
@@ -6,16 +6,25 @@
 
 "use strict";
 
 const { ActorPool } = require("devtools/server/actors/common");
 const { createValueGrip } = require("devtools/server/actors/object/utils");
 const { ActorClassWithSpec } = require("devtools/shared/protocol");
 const { frameSpec } = require("devtools/shared/specs/frame");
 
+function formatDisplayName(frame) {
+  if (frame.type === "call") {
+    const callee = frame.callee;
+    return callee.name || callee.userDisplayName || callee.displayName;
+  }
+
+  return `(${frame.type})`;
+}
+
 /**
  * An actor for a specified stack frame.
  */
 const FrameActor = ActorClassWithSpec(frameSpec, {
   /**
    * Creates the Frame actor.
    *
    * @param frame Debugger.Frame
@@ -71,35 +80,32 @@ const FrameActor = ActorClassWithSpec(fr
 
   /**
    * Returns a frame form for use in a protocol message.
    */
   form: function() {
     const threadActor = this.threadActor;
     const form = { actor: this.actorID,
                    type: this.frame.type };
-    if (this.frame.type === "call") {
-      form.callee = createValueGrip(this.frame.callee, threadActor._pausePool,
-        threadActor.objectGrip);
-    }
 
     // NOTE: ignoreFrameEnvironment lets the client explicitly avoid
     // populating form environments on pause.
     if (
       !this.threadActor._options.ignoreFrameEnvironment &&
       this.frame.environment
     ) {
       form.environment = this.getEnvironment();
     }
 
     if (this.frame.type != "wasmcall") {
       form.this = createValueGrip(this.frame.this, threadActor._pausePool,
         threadActor.objectGrip);
     }
 
+    form.displayName = formatDisplayName(this.frame);
     form.arguments = this._args();
     if (this.frame.script) {
       const generatedLocation = this.threadActor.sources.getFrameLocation(this.frame);
       form.where = {
         actor: generatedLocation.generatedSourceActor.actorID,
         line: generatedLocation.generatedLine,
         column: generatedLocation.generatedColumn,
       };
--- a/devtools/server/tests/unit/test_frameactor-01.js
+++ b/devtools/server/tests/unit/test_frameactor-01.js
@@ -28,17 +28,17 @@ function run_test() {
   });
   do_test_pending();
 }
 
 function test_pause_frame() {
   gThreadClient.addOneTimeListener("paused", function(event, packet) {
     Assert.ok(!!packet.frame);
     Assert.ok(!!packet.frame.actor);
-    Assert.equal(packet.frame.callee.name, "stopMe");
+    Assert.equal(packet.frame.displayName, "stopMe");
     gThreadClient.resume(function() {
       finishClient(gClient);
     });
   });
 
   gDebuggee.eval("(" + function() {
     function stopMe() {
       debugger;
--- a/devtools/server/tests/unit/test_frameactor-04.js
+++ b/devtools/server/tests/unit/test_frameactor-04.js
@@ -24,67 +24,46 @@ function run_test() {
                            function(response, targetFront, threadClient) {
                              gThreadClient = threadClient;
                              test_pause_frame();
                            });
   });
   do_test_pending();
 }
 
-var gFrames = [
+var frameFixtures = [
   // Function calls...
-  { type: "call", callee: { name: "depth3" } },
-  { type: "call", callee: { name: "depth2" } },
-  { type: "call", callee: { name: "depth1" } },
+  { type: "call", displayName: "depth3" },
+  { type: "call", displayName: "depth2" },
+  { type: "call", displayName: "depth1" },
 
   // Anonymous function call in our eval...
-  { type: "call", callee: { name: undefined } },
+  { type: "call", displayName: undefined },
 
   // The eval itself.
-  { type: "eval", callee: { name: undefined } },
-];
-
-var gSliceTests = [
-  { start: 0, count: undefined, resetActors: true },
-  { start: 0, count: 1 },
-  { start: 2, count: 2 },
-  { start: 1, count: 15 },
-  { start: 15, count: undefined },
+  { type: "eval", displayName: "(eval)" },
 ];
 
-function test_frame_slice() {
-  if (gSliceTests.length == 0) {
-    gThreadClient.resume(() => finishClient(gClient));
-    return;
-  }
-
-  const test = gSliceTests.shift();
-  gThreadClient.getFrames(test.start, test.count, function(response) {
-    const testFrames = gFrames.slice(test.start,
-                                   test.count ? test.start + test.count : undefined);
-    Assert.equal(testFrames.length, response.frames.length);
-    for (let i = 0; i < testFrames.length; i++) {
-      const expected = testFrames[i];
+function test_frame_packet() {
+  gThreadClient.getFrames(0, 1000, function(response) {
+    for (let i = 0; i < response.frames.length; i++) {
+      const expected = frameFixtures[i];
       const actual = response.frames[i];
 
-      if (test.resetActors) {
-        expected.actor = actual.actor;
-      }
+      Assert.equal(expected.displayname, actual.displayname, "Frame displayname");
+      Assert.equal(expected.type, actual.type, "Frame displayname");
+    }
 
-      for (const key of ["type", "callee-name"]) {
-        Assert.equal(expected[key] || undefined, actual[key]);
-      }
-    }
-    test_frame_slice();
+    gThreadClient.resume(() => finishClient(gClient));
   });
 }
 
 function test_pause_frame() {
   gThreadClient.addOneTimeListener("paused", function(event, packet) {
-    test_frame_slice();
+    test_frame_packet();
   });
 
   gDebuggee.eval("(" + function() {
     function depth3() {
       debugger;
     }
     function depth2() {
       depth3();
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
@@ -59,18 +59,17 @@ static void Sync(BrowsingContext* aBrows
   if (!XRE_IsContentProcess()) {
     return;
   }
 
   auto cc = ContentChild::GetSingleton();
   MOZ_DIAGNOSTIC_ASSERT(cc);
   RefPtr<BrowsingContext> parent = aBrowsingContext->GetParent();
   BrowsingContext* opener = aBrowsingContext->GetOpener();
-  cc->SendAttachBrowsingContext(BrowsingContextId(parent ? parent->Id() : 0),
-                                BrowsingContextId(opener ? opener->Id() : 0),
+  cc->SendAttachBrowsingContext(parent, opener,
                                 BrowsingContextId(aBrowsingContext->Id()),
                                 aBrowsingContext->Name());
 }
 
 BrowsingContext* BrowsingContext::TopLevelBrowsingContext() {
   BrowsingContext* bc = this;
   while (bc->mParent) {
     bc = bc->mParent;
@@ -98,16 +97,20 @@ BrowsingContext* BrowsingContext::TopLev
     uint64_t aId) {
   if (BrowsingContextMap<WeakPtr>::Ptr abc = sBrowsingContexts->lookup(aId)) {
     return do_AddRef(abc->value().get());
   }
 
   return nullptr;
 }
 
+CanonicalBrowsingContext* BrowsingContext::Canonical() {
+  return CanonicalBrowsingContext::Cast(this);
+}
+
 /* static */ already_AddRefed<BrowsingContext> BrowsingContext::Create(
     BrowsingContext* aParent, BrowsingContext* aOpener, const nsAString& aName,
     Type aType) {
   MOZ_DIAGNOSTIC_ASSERT(!aParent || aParent->mType == aType);
 
   uint64_t id = nsContentUtils::GenerateBrowsingContextId();
 
   MOZ_LOG(GetLog(), LogLevel::Debug,
@@ -233,18 +236,17 @@ void BrowsingContext::Detach() {
   Group()->Unregister(this);
 
   if (!XRE_IsContentProcess()) {
     return;
   }
 
   auto cc = ContentChild::GetSingleton();
   MOZ_DIAGNOSTIC_ASSERT(cc);
-  cc->SendDetachBrowsingContext(BrowsingContextId(Id()),
-                                false /* aMoveToBFCache */);
+  cc->SendDetachBrowsingContext(this, false /* aMoveToBFCache */);
 }
 
 void BrowsingContext::CacheChildren() {
   if (mChildren.IsEmpty()) {
     return;
   }
 
   MOZ_LOG(GetLog(), LogLevel::Debug,
@@ -259,18 +261,17 @@ void BrowsingContext::CacheChildren() {
   mChildren.Clear();
 
   if (!XRE_IsContentProcess()) {
     return;
   }
 
   auto cc = ContentChild::GetSingleton();
   MOZ_DIAGNOSTIC_ASSERT(cc);
-  cc->SendDetachBrowsingContext(BrowsingContextId(Id()),
-                                true /* aMoveToBFCache */);
+  cc->SendDetachBrowsingContext(this, true /* aMoveToBFCache */);
 }
 
 bool BrowsingContext::IsCached() { return sCachedBrowsingContexts->has(Id()); }
 
 void BrowsingContext::GetChildren(
     nsTArray<RefPtr<BrowsingContext>>& aChildren) {
   MOZ_ALWAYS_TRUE(aChildren.AppendElements(mChildren));
 }
@@ -283,18 +284,17 @@ void BrowsingContext::SetOpener(Browsing
   mOpener = aOpener;
 
   if (!XRE_IsContentProcess()) {
     return;
   }
 
   auto cc = ContentChild::GetSingleton();
   MOZ_DIAGNOSTIC_ASSERT(cc);
-  cc->SendSetOpenerBrowsingContext(
-      BrowsingContextId(Id()), BrowsingContextId(aOpener ? aOpener->Id() : 0));
+  cc->SendSetOpenerBrowsingContext(this, aOpener);
 }
 
 BrowsingContext::~BrowsingContext() {
   MOZ_DIAGNOSTIC_ASSERT(!mParent || !mParent->mChildren.Contains(this));
   MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this));
   MOZ_DIAGNOSTIC_ASSERT(!sCachedBrowsingContexts ||
                         !sCachedBrowsingContexts->has(Id()));
 
@@ -321,34 +321,34 @@ void BrowsingContext::NotifyUserGestureA
                       topLevelBC->Id());
   topLevelBC->SetUserGestureActivation();
 
   if (!XRE_IsContentProcess()) {
     return;
   }
   auto cc = ContentChild::GetSingleton();
   MOZ_ASSERT(cc);
-  cc->SendSetUserGestureActivation(BrowsingContextId(topLevelBC->Id()), true);
+  cc->SendSetUserGestureActivation(topLevelBC, true);
 }
 
 void BrowsingContext::NotifyResetUserGestureActivation() {
   // We would reset the user gesture activation flag on the top level browsing
   // context, which would automatically be sync to other top level browsing
   // contexts which are in the different process.
   RefPtr<BrowsingContext> topLevelBC = TopLevelBrowsingContext();
   USER_ACTIVATION_LOG("Get top level browsing context 0x%08" PRIx64,
                       topLevelBC->Id());
   topLevelBC->ResetUserGestureActivation();
 
   if (!XRE_IsContentProcess()) {
     return;
   }
   auto cc = ContentChild::GetSingleton();
   MOZ_ASSERT(cc);
-  cc->SendSetUserGestureActivation(BrowsingContextId(topLevelBC->Id()), false);
+  cc->SendSetUserGestureActivation(topLevelBC, false);
 }
 
 void BrowsingContext::SetUserGestureActivation() {
   MOZ_ASSERT(!mParent, "Set user activation flag on non top-level context!");
   USER_ACTIVATION_LOG(
       "Set user gesture activation for browsing context 0x%08" PRIx64, Id());
   mIsActivatedByUserGesture = true;
 }
@@ -392,28 +392,27 @@ void BrowsingContext::Location(JSContext
                                OOMReporter& aError) {}
 
 void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) {
   // FIXME We need to set mClosed, but only once we're sending the
   //       DOMWindowClose event (which happens in the process where the
   //       document for this browsing context is loaded).
   //       See https://bugzilla.mozilla.org/show_bug.cgi?id=1516343.
   ContentChild* cc = ContentChild::GetSingleton();
-  cc->SendWindowClose(BrowsingContextId(mBrowsingContextId),
-                      aCallerType == CallerType::System);
+  cc->SendWindowClose(this, aCallerType == CallerType::System);
 }
 
 void BrowsingContext::Focus(ErrorResult& aError) {
   ContentChild* cc = ContentChild::GetSingleton();
-  cc->SendWindowFocus(BrowsingContextId(mBrowsingContextId));
+  cc->SendWindowFocus(this);
 }
 
 void BrowsingContext::Blur(ErrorResult& aError) {
   ContentChild* cc = ContentChild::GetSingleton();
-  cc->SendWindowBlur(BrowsingContextId(mBrowsingContextId));
+  cc->SendWindowBlur(this);
 }
 
 Nullable<WindowProxyHolder> BrowsingContext::GetTop(ErrorResult& aError) {
   // We never return null or throw an error, but the implementation in
   // nsGlobalWindow does and we need to use the same signature.
   return WindowProxyHolder(TopLevelBrowsingContext());
 }
 
@@ -455,17 +454,17 @@ void BrowsingContext::PostMessageMoz(JSC
   if (!nsGlobalWindowOuter::GatherPostMessageData(
           aCx, aTargetOrigin, getter_AddRefs(sourceBc), data.origin(),
           getter_AddRefs(data.targetOriginURI()),
           getter_AddRefs(data.callerPrincipal()),
           getter_AddRefs(callerInnerWindow),
           getter_AddRefs(data.callerDocumentURI()), aError)) {
     return;
   }
-  data.source() = BrowsingContextId(sourceBc->Id());
+  data.source() = sourceBc;
   data.isFromPrivateWindow() =
       callerInnerWindow &&
       nsScriptErrorBase::ComputeIsFromPrivateWindow(callerInnerWindow);
 
   JS::Rooted<JS::Value> transferArray(aCx);
   aError = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
                                                              &transferArray);
   if (NS_WARN_IF(aError.Failed())) {
@@ -480,18 +479,17 @@ void BrowsingContext::PostMessageMoz(JSC
 
   ContentChild* cc = ContentChild::GetSingleton();
   ClonedMessageData messageData;
   if (!message.BuildClonedMessageDataForChild(cc, messageData)) {
     aError.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  cc->SendWindowPostMessage(BrowsingContextId(mBrowsingContextId), messageData,
-                            data);
+  cc->SendWindowPostMessage(this, messageData, data);
 }
 
 void BrowsingContext::PostMessageMoz(JSContext* aCx,
                                      JS::Handle<JS::Value> aMessage,
                                      const WindowPostMessageOptions& aOptions,
                                      nsIPrincipal& aSubjectPrincipal,
                                      ErrorResult& aError) {
   PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, aOptions.mTransfer,
@@ -510,9 +508,51 @@ already_AddRefed<BrowsingContext> Browsi
   RefPtr<BrowsingContext> bc;
   if (childDS) {
     childDS->GetBrowsingContext(getter_AddRefs(bc));
   }
   return bc.forget();
 }
 
 }  // namespace dom
+
+namespace ipc {
+
+void IPDLParamTraits<dom::BrowsingContext>::Write(
+    IPC::Message* aMsg, IProtocol* aActor, dom::BrowsingContext* aParam) {
+  uint64_t id = aParam ? aParam->Id() : 0;
+  WriteIPDLParam(aMsg, aActor, id);
+
+  // If his is an in-process send. We want to make sure that our BrowsingContext
+  // object lives long enough to make it to the other side, so we take an extra
+  // reference. This reference is freed in ::Read().
+  if (!aActor->GetIPCChannel()->IsCrossProcess()) {
+    NS_IF_ADDREF(aParam);
+  }
+}
+
+bool IPDLParamTraits<dom::BrowsingContext>::Read(
+    const IPC::Message* aMsg, PickleIterator* aIter, IProtocol* aActor,
+    RefPtr<dom::BrowsingContext>* aResult) {
+  uint64_t id = 0;
+  if (!ReadIPDLParam(aMsg, aIter, aActor, &id)) {
+    return false;
+  }
+
+  if (id == 0) {
+    aResult = nullptr;
+    return true;
+  }
+
+  *aResult = dom::BrowsingContext::Get(id);
+  MOZ_ASSERT(aResult, "Deserialized absent BrowsingContext!");
+
+  // If this is an in-process actor, free the reference taken in ::Write().
+  if (!aActor->GetIPCChannel()->IsCrossProcess()) {
+    dom::BrowsingContext* bc = *aResult;
+    NS_IF_RELEASE(bc);
+  }
+
+  return aResult != nullptr;
+}
+
+}  // namespace ipc
 }  // namespace mozilla
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -15,26 +15,39 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDocShell.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 
 class nsGlobalWindowOuter;
 class nsOuterWindowProxy;
+class PickleIterator;
+
+namespace IPC {
+class Message;
+}  // namespace IPC
 
 namespace mozilla {
 
 class ErrorResult;
 class LogModule;
 class OOMReporter;
 
+namespace ipc {
+class IProtocol;
+
+template <typename T>
+struct IPDLParamTraits;
+}  // namespace ipc
+
 namespace dom {
 
 class BrowsingContextGroup;
+class CanonicalBrowsingContext;
 class ContentParent;
 template <typename>
 struct Nullable;
 template <typename T>
 class Sequence;
 struct WindowPostMessageOptions;
 class WindowProxyHolder;
 
@@ -76,16 +89,19 @@ class BrowsingContext : public nsWrapper
                                                   const nsAString& aName,
                                                   Type aType);
 
   // Create a BrowsingContext object from over IPC.
   static already_AddRefed<BrowsingContext> CreateFromIPC(
       BrowsingContext* aParent, BrowsingContext* aOpener,
       const nsAString& aName, uint64_t aId, ContentParent* aOriginProcess);
 
+  // Cast this object to a canonical browsing context, and return it.
+  CanonicalBrowsingContext* Canonical();
+
   // Get the DocShell for this BrowsingContext if it is in-process, or
   // null if it's not.
   nsIDocShell* GetDocShell() { return mDocShell; }
   void SetDocShell(nsIDocShell* aDocShell);
 
   // Get the outer window object for this BrowsingContext if it is in-process
   // and still has a docshell, or null otherwise.
   nsPIDOMWindowOuter* GetDOMWindow() const {
@@ -249,11 +265,22 @@ class BrowsingContext : public nsWrapper
  * lives in this process, and a same-process WindowProxy should be used (see
  * nsGlobalWindowOuter). This should only be called by bindings code, ToJSValue
  * is the right API to get a WindowProxy for a BrowsingContext.
  */
 extern bool GetRemoteOuterWindowProxy(JSContext* aCx, BrowsingContext* aContext,
                                       JS::MutableHandle<JSObject*> aRetVal);
 
 }  // namespace dom
+
+// Allow sending BrowsingContext objects over IPC.
+namespace ipc {
+template <>
+struct IPDLParamTraits<dom::BrowsingContext> {
+  static void Write(IPC::Message* aMsg, IProtocol* aActor,
+                    dom::BrowsingContext* aParam);
+  static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+                   IProtocol* aActor, RefPtr<dom::BrowsingContext>* aResult);
+};
+}  // namespace ipc
 }  // namespace mozilla
 
 #endif  // !defined(mozilla_dom_BrowsingContext_h)
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -972,17 +972,21 @@ int32_t Element::ScrollWidth() {
   return nsPresContext::AppUnitsToIntCSSPixels(width);
 }
 
 nsRect Element::GetClientAreaRect() {
   nsIFrame* frame;
   nsIScrollableFrame* sf = GetScrollFrame(&frame);
 
   if (sf) {
-    return sf->GetScrollPortRect();
+    nsRect scrollPort = sf->GetScrollPortRect();
+    // The scroll port value might be expanded to the minimum scale size, we
+    // should limit the size to the ICB in such cases.
+    scrollPort.SizeTo(sf->GetLayoutSize());
+    return scrollPort;
   }
 
   if (frame &&
       // The display check is OK even though we're not looking at the style
       // frame, because the style frame only differs from "frame" for tables,
       // and table wrappers have the same display as the table itself.
       (frame->StyleDisplay()->mDisplay != StyleDisplay::Inline ||
        frame->IsFrameOfType(nsIFrame::eReplaced))) {
--- a/dom/file/uri/BlobURL.cpp
+++ b/dom/file/uri/BlobURL.cpp
@@ -161,19 +161,18 @@ BlobURL::Mutate(nsIURIMutator** aMutator
     return rv;
   }
   mutator.forget(aMutator);
   return NS_OK;
 }
 
 // nsIClassInfo methods:
 NS_IMETHODIMP
-BlobURL::GetInterfaces(uint32_t* count, nsIID*** array) {
-  *count = 0;
-  *array = nullptr;
+BlobURL::GetInterfaces(nsTArray<nsIID>& array) {
+  array.Clear();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BlobURL::GetScriptableHelper(nsIXPCScriptable** _retval) {
   *_retval = nullptr;
   return NS_OK;
 }
--- a/dom/interfaces/security/nsIContentSecurityPolicy.idl
+++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl
@@ -297,17 +297,18 @@ interface nsIContentSecurityPolicy : nsI
    */
   short shouldLoad(in nsContentPolicyType aContentType,
                    in nsICSPEventListener aCSPEventListener,
                    in nsIURI          aContentLocation,
                    in nsIURI          aRequestOrigin,
                    in nsISupports     aContext,
                    in ACString        aMimeTypeGuess,
                    in nsIURI          aOriginalURIIfRedirect,
-                   in bool            aSendViolationReports);
+                   in bool            aSendViolationReports,
+                   in AString         aNonce);
 
 %{ C++
 // nsIObserver topic to fire when the policy encounters a violation.
 #define CSP_VIOLATION_TOPIC "csp-on-violate-policy"
 %}
 
   /**
    * Returns the CSP in JSON notation.
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -3585,90 +3585,76 @@ PContentChild::Result ContentChild::OnMe
 
     LSObject::OnSyncMessageHandled();
   }
 
   return result;
 }
 
 mozilla::ipc::IPCResult ContentChild::RecvWindowClose(
-    const BrowsingContextId& aContextId, const bool& aTrustedCaller) {
-  RefPtr<BrowsingContext> bc = BrowsingContext::Get(aContextId);
-  if (!bc) {
+    BrowsingContext* aContext, bool aTrustedCaller) {
+  if (!aContext) {
     MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
-            ("ChildIPC: Trying to send a message to dead or detached context "
-             "0x%08" PRIx64,
-             (uint64_t)aContextId));
+            ("ChildIPC: Trying to send a message to dead or detached context"));
     return IPC_OK();
   }
 
-  nsCOMPtr<nsPIDOMWindowOuter> window = bc->GetDOMWindow();
+  nsCOMPtr<nsPIDOMWindowOuter> window = aContext->GetDOMWindow();
   nsGlobalWindowOuter::Cast(window)->CloseOuter(aTrustedCaller);
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult ContentChild::RecvWindowFocus(
-    const BrowsingContextId& aContextId) {
-  RefPtr<BrowsingContext> bc = BrowsingContext::Get(aContextId);
-  if (!bc) {
+mozilla::ipc::IPCResult ContentChild::RecvWindowFocus(BrowsingContext* aContext) {
+  if (!aContext) {
     MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
-            ("ChildIPC: Trying to send a message to dead or detached context "
-             "0x%08" PRIx64,
-             (uint64_t)aContextId));
+            ("ChildIPC: Trying to send a message to dead or detached context"));
     return IPC_OK();
   }
 
-  nsCOMPtr<nsPIDOMWindowOuter> window = bc->GetDOMWindow();
+  nsCOMPtr<nsPIDOMWindowOuter> window = aContext->GetDOMWindow();
   nsGlobalWindowOuter::Cast(window)->FocusOuter();
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentChild::RecvWindowBlur(
-    const BrowsingContextId& aContextId) {
-  RefPtr<BrowsingContext> bc = BrowsingContext::Get(aContextId);
-  if (!bc) {
+    BrowsingContext* aContext) {
+  if (!aContext) {
     MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
-            ("ChildIPC: Trying to send a message to dead or detached context "
-             "0x%08" PRIx64,
-             (uint64_t)aContextId));
+            ("ChildIPC: Trying to send a message to dead or detached context"));
     return IPC_OK();
   }
 
-  nsCOMPtr<nsPIDOMWindowOuter> window = bc->GetDOMWindow();
+  nsCOMPtr<nsPIDOMWindowOuter> window = aContext->GetDOMWindow();
   nsGlobalWindowOuter::Cast(window)->BlurOuter();
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentChild::RecvWindowPostMessage(
-    const BrowsingContextId& aContextId, const ClonedMessageData& aMessage,
+    BrowsingContext* aContext, const ClonedMessageData& aMessage,
     const PostMessageData& aData) {
-  RefPtr<BrowsingContext> bc = BrowsingContext::Get(aContextId);
-  if (!bc) {
+  if (!aContext) {
     MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
-            ("ChildIPC: Trying to send a message to dead or detached context "
-             "0x%08" PRIx64,
-             (uint64_t)aContextId));
+            ("ChildIPC: Trying to send a message to dead or detached context"));
     return IPC_OK();
   }
 
   RefPtr<nsGlobalWindowOuter> window =
-      nsGlobalWindowOuter::Cast(bc->GetDOMWindow());
+      nsGlobalWindowOuter::Cast(aContext->GetDOMWindow());
   nsCOMPtr<nsIPrincipal> providedPrincipal;
   if (!window->GetPrincipalForPostMessage(
           aData.targetOrigin(), aData.targetOriginURI(),
           aData.callerPrincipal(), *aData.subjectPrincipal(),
           getter_AddRefs(providedPrincipal))) {
     return IPC_OK();
   }
 
-  RefPtr<BrowsingContext> sourceBc = BrowsingContext::Get(aData.source());
+  RefPtr<BrowsingContext> sourceBc = aData.source();
   if (!sourceBc) {
     MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
-            ("ChildIPC: Trying to use a dead or detached context 0x%08" PRIx64,
-             (uint64_t)aData.source()));
+            ("ChildIPC: Trying to use a dead or detached context"));
     return IPC_OK();
   }
 
   // Create and asynchronously dispatch a runnable which will handle actual DOM
   // event creation and dispatch.
   RefPtr<PostMessageEvent> event = new PostMessageEvent(
       sourceBc, aData.origin(), window, providedPrincipal,
       aData.callerDocumentURI(), aData.isFromPrivateWindow());
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -709,22 +709,22 @@ class ContentChild final : public PConte
   virtual already_AddRefed<nsIEventTarget> GetConstructedEventTarget(
       const Message& aMsg) override;
 
   virtual already_AddRefed<nsIEventTarget> GetSpecificMessageEventTarget(
       const Message& aMsg) override;
 
   virtual void OnChannelReceivedMessage(const Message& aMsg) override;
 
-  mozilla::ipc::IPCResult RecvWindowClose(const BrowsingContextId& aContextId,
-                                          const bool& aTrustedCaller);
-  mozilla::ipc::IPCResult RecvWindowFocus(const BrowsingContextId& aContextId);
-  mozilla::ipc::IPCResult RecvWindowBlur(const BrowsingContextId& aContextId);
+  mozilla::ipc::IPCResult RecvWindowClose(BrowsingContext* aContext,
+                                          bool aTrustedCaller);
+  mozilla::ipc::IPCResult RecvWindowFocus(BrowsingContext* aContext);
+  mozilla::ipc::IPCResult RecvWindowBlur(BrowsingContext* aContext);
   mozilla::ipc::IPCResult RecvWindowPostMessage(
-      const BrowsingContextId& aContextId, const ClonedMessageData& aMessage,
+      BrowsingContext* aContext, const ClonedMessageData& aMessage,
       const PostMessageData& aData);
 
 #ifdef NIGHTLY_BUILD
   virtual PContentChild::Result OnMessageReceived(const Message& aMsg) override;
 #else
   using PContentChild::OnMessageReceived;
 #endif
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5604,165 +5604,128 @@ ContentParent::RecvFirstPartyStorageAcce
 
 mozilla::ipc::IPCResult ContentParent::RecvStoreUserInteractionAsPermission(
     const Principal& aPrincipal) {
   AntiTrackingCommon::StoreUserInteractionFor(aPrincipal);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvAttachBrowsingContext(
-    const BrowsingContextId& aParentId, const BrowsingContextId& aOpenerId,
-    const BrowsingContextId& aChildId, const nsString& aName) {
-  RefPtr<CanonicalBrowsingContext> parent =
-      CanonicalBrowsingContext::Get(aParentId);
-  if (aParentId && !parent) {
-    // Unless 'aParentId' is 0 (which it is when the child is a root
-    // BrowsingContext) there should always be a corresponding
-    // 'parent'. The only reason for there not beeing one is if the
-    // parent has already been detached, in which case the
-    // BrowsingContext that tries to attach itself to the context with
-    // 'aParentId' is surely doomed and we can safely do nothing.
-
-    // TODO(farre): When we start syncing/moving BrowsingContexts to
-    // other child processes is it possible to get into races where
-    // constructive operations on already detached BrowsingContexts
-    // are requested? This needs to be answered/handled, but for now
-    // return early. [Bug 1471598]
-    MOZ_LOG(
-        BrowsingContext::GetLog(), LogLevel::Debug,
-        ("ParentIPC: Trying to attach to already detached parent 0x%08" PRIx64,
-         (uint64_t)aParentId));
-    return IPC_OK();
-  }
-
-  if (parent && !parent->IsOwnedByProcess(ChildID())) {
+    BrowsingContext* aParent, BrowsingContext* aOpener,
+    BrowsingContextId aChildId, const nsString& aName) {
+  if (aParent && !aParent->Canonical()->IsOwnedByProcess(ChildID())) {
     // Where trying attach a child BrowsingContext to a parent
     // BrowsingContext in another process. This is illegal since the
     // only thing that could create that child BrowsingContext is a
     // parent docshell in the same process as that BrowsingContext.
 
     // TODO(farre): We're doing nothing now, but is that exactly what
     // we want? Maybe we want to crash the child currently calling
     // SendAttachBrowsingContext and/or the child that originally
     // called SendAttachBrowsingContext or possibly all children that
     // has a BrowsingContext connected to the child that currently
     // called SendAttachBrowsingContext? [Bug 1471598]
     MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning,
             ("ParentIPC: Trying to attach to out of process parent context "
              "0x%08" PRIx64,
-             parent->Id()));
+             aParent->Id()));
     return IPC_OK();
   }
 
   RefPtr<BrowsingContext> child = BrowsingContext::Get(aChildId);
   if (child && !child->IsCached()) {
     // This is highly suspicious. BrowsingContexts should only be
     // attached at most once, but finding one indicates that someone
     // is doing something they shouldn't.
 
     // TODO(farre): To crash or not to crash. Same reasoning as in
     // above TODO. [Bug 1471598]
     MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning,
             ("ParentIPC: Trying to attach already attached 0x%08" PRIx64
              " to 0x%08" PRIx64,
-             child->Id(), (uint64_t)aParentId));
+             child->Id(), aParent ? aParent->Id() : 0));
     return IPC_OK();
   }
 
   if (!child) {
-    RefPtr<BrowsingContext> opener = BrowsingContext::Get(aOpenerId);
-    child = BrowsingContext::CreateFromIPC(parent, opener, aName,
+    child = BrowsingContext::CreateFromIPC(aParent, aOpener, aName,
                                            (uint64_t)aChildId, this);
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvDetachBrowsingContext(
-    const BrowsingContextId& aContextId, const bool& aMoveToBFCache) {
-  RefPtr<CanonicalBrowsingContext> context =
-      CanonicalBrowsingContext::Get(aContextId);
-
-  if (!context) {
+    BrowsingContext* aContext, bool aMoveToBFCache) {
+  if (!aContext) {
     MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
-            ("ParentIPC: Trying to detach already detached 0x%08" PRIx64,
-             (uint64_t)aContextId));
+            ("ParentIPC: Trying to detach already detached"));
     return IPC_OK();
   }
 
-  if (!context->IsOwnedByProcess(ChildID())) {
+  if (!aContext->Canonical()->IsOwnedByProcess(ChildID())) {
     // Where trying to detach a child BrowsingContext in another child
     // process. This is illegal since the owner of the BrowsingContext
     // is the proccess with the in-process docshell, which is tracked
     // by OwnerProcessId.
 
     // TODO(farre): To crash or not to crash. Same reasoning as in
     // above TODO. [Bug 1471598]
     MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning,
             ("ParentIPC: Trying to detach out of process context 0x%08" PRIx64,
-             context->Id()));
+             aContext->Id()));
     return IPC_OK();
   }
 
   if (aMoveToBFCache) {
-    context->CacheChildren();
+    aContext->CacheChildren();
   } else {
-    context->Detach();
+    aContext->Detach();
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvSetOpenerBrowsingContext(
-    const BrowsingContextId& aContextId,
-    const BrowsingContextId& aOpenerContextId) {
-  RefPtr<CanonicalBrowsingContext> context =
-      CanonicalBrowsingContext::Get(aContextId);
-
-  if (!context) {
+    BrowsingContext* aContext, BrowsingContext* aOpener) {
+  if (!aContext) {
     MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
-            ("ParentIPC: Trying to set opener already detached 0x%08" PRIx64,
-             (uint64_t)aContextId));
+            ("ParentIPC: Trying to set opener already detached"));
     return IPC_OK();
   }
 
-  if (!context->IsOwnedByProcess(ChildID())) {
+  if (!aContext->Canonical()->IsOwnedByProcess(ChildID())) {
     // Where trying to set opener on a child BrowsingContext in
     // another child process. This is illegal since the owner of the
     // BrowsingContext is the proccess with the in-process docshell,
     // which is tracked by OwnerProcessId.
 
     // TODO(farre): To crash or not to crash. Same reasoning as in
     // above TODO. [Bug 1471598]
     MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning,
             ("ParentIPC: Trying to set opener on out of process context "
              "0x%08" PRIx64,
-             context->Id()));
+             aContext->Id()));
     return IPC_OK();
   }
 
-  RefPtr<BrowsingContext> opener = BrowsingContext::Get(aOpenerContextId);
-  context->SetOpener(opener);
+  aContext->SetOpener(aOpener);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvSetUserGestureActivation(
-    const BrowsingContextId& aContextId, const bool& aNewValue) {
-  RefPtr<CanonicalBrowsingContext> context =
-      CanonicalBrowsingContext::Get(aContextId);
-
-  if (!context) {
+    BrowsingContext* aContext, bool aNewValue) {
+  if (!aContext) {
     MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
-            ("ParentIPC: Trying to activate wrong context 0x%08" PRIx64,
-             (uint64_t)aContextId));
+            ("ParentIPC: Trying to activate wrong context"));
     return IPC_OK();
   }
 
-  context->NotifySetUserGestureActivationFromIPC(aNewValue);
+  aContext->Canonical()->NotifySetUserGestureActivationFromIPC(aNewValue);
   return IPC_OK();
 }
 
 void ContentParent::RegisterRemoteWorkerActor() { ++mRemoteWorkerActors; }
 
 void ContentParent::UnregisterRemoveWorkerActor() {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -5777,100 +5740,88 @@ void ContentParent::UnregisterRemoveWork
     // allow it to perform shutdown tasks.
     MessageLoop::current()->PostTask(NewRunnableMethod<ShutDownMethod>(
         "dom::ContentParent::ShutDownProcess", this,
         &ContentParent::ShutDownProcess, SEND_SHUTDOWN_MESSAGE));
   }
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvWindowClose(
-    const BrowsingContextId& aContextId, const bool& aTrustedCaller) {
-  RefPtr<CanonicalBrowsingContext> bc =
-      CanonicalBrowsingContext::Get(aContextId);
-  if (!bc) {
-    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
-            ("ParentIPC: Trying to send a message to dead or detached context "
-             "0x%08" PRIx64,
-             (uint64_t)aContextId));
+    BrowsingContext* aContext, bool aTrustedCaller) {
+  if (!aContext) {
+    MOZ_LOG(
+        BrowsingContext::GetLog(), LogLevel::Debug,
+        ("ParentIPC: Trying to send a message to dead or detached context"));
     return IPC_OK();
   }
 
   // FIXME Need to check that the sending process has access to the unit of
   // related
   //       browsing contexts of bc.
 
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
-  ContentParent* cp =
-      cpm->GetContentProcessById(ContentParentId(bc->OwnerProcessId()));
-  Unused << cp->SendWindowClose(aContextId, aTrustedCaller);
+  ContentParent* cp = cpm->GetContentProcessById(
+      ContentParentId(aContext->Canonical()->OwnerProcessId()));
+  Unused << cp->SendWindowClose(aContext, aTrustedCaller);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvWindowFocus(
-    const BrowsingContextId& aContextId) {
-  RefPtr<CanonicalBrowsingContext> bc =
-      CanonicalBrowsingContext::Get(aContextId);
-  if (!bc) {
-    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
-            ("ParentIPC: Trying to send a message to dead or detached context "
-             "0x%08" PRIx64,
-             (uint64_t)aContextId));
+    BrowsingContext* aContext) {
+  if (!aContext) {
+    MOZ_LOG(
+        BrowsingContext::GetLog(), LogLevel::Debug,
+        ("ParentIPC: Trying to send a message to dead or detached context"));
     return IPC_OK();
   }
 
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
-  ContentParent* cp =
-      cpm->GetContentProcessById(ContentParentId(bc->OwnerProcessId()));
-  Unused << cp->SendWindowFocus(aContextId);
+  ContentParent* cp = cpm->GetContentProcessById(
+      ContentParentId(aContext->Canonical()->OwnerProcessId()));
+  Unused << cp->SendWindowFocus(aContext);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvWindowBlur(
-    const BrowsingContextId& aContextId) {
-  RefPtr<CanonicalBrowsingContext> bc =
-      CanonicalBrowsingContext::Get(aContextId);
-  if (!bc) {
-    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
-            ("ParentIPC: Trying to send a message to dead or detached context "
-             "0x%08" PRIx64,
-             (uint64_t)aContextId));
+    BrowsingContext* aContext) {
+  if (!aContext) {
+    MOZ_LOG(
+        BrowsingContext::GetLog(), LogLevel::Debug,
+        ("ParentIPC: Trying to send a message to dead or detached context"));
     return IPC_OK();
   }
 
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
-  ContentParent* cp =
-      cpm->GetContentProcessById(ContentParentId(bc->OwnerProcessId()));
-  Unused << cp->SendWindowBlur(aContextId);
+  ContentParent* cp = cpm->GetContentProcessById(
+      ContentParentId(aContext->Canonical()->OwnerProcessId()));
+  Unused << cp->SendWindowBlur(aContext);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvWindowPostMessage(
-    const BrowsingContextId& aContextId, const ClonedMessageData& aMessage,
+    BrowsingContext* aContext, const ClonedMessageData& aMessage,
     const PostMessageData& aData) {
-  RefPtr<CanonicalBrowsingContext> bc =
-      CanonicalBrowsingContext::Get(aContextId);
-  if (!bc) {
-    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
-            ("ParentIPC: Trying to send a message to dead or detached context "
-             "0x%08" PRIx64,
-             (uint64_t)aContextId));
+  if (!aContext) {
+    MOZ_LOG(
+        BrowsingContext::GetLog(), LogLevel::Debug,
+        ("ParentIPC: Trying to send a message to dead or detached context"));
     return IPC_OK();
   }
 
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
-  ContentParent* cp =
-      cpm->GetContentProcessById(ContentParentId(bc->OwnerProcessId()));
+  ContentParent* cp = cpm->GetContentProcessById(
+      ContentParentId(aContext->Canonical()->OwnerProcessId()));
   StructuredCloneData messageFromChild;
   UnpackClonedMessageDataForParent(aMessage, messageFromChild);
   ClonedMessageData message;
   if (!BuildClonedMessageDataForParent(cp, messageFromChild, message)) {
     // FIXME Logging?
     return IPC_OK();
   }
-  Unused << cp->SendWindowPostMessage(aContextId, message, aData);
+  Unused << cp->SendWindowPostMessage(aContext, message, aData);
   return IPC_OK();
 }
 
 }  // namespace dom
 }  // namespace mozilla
 
 NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -613,37 +613,35 @@ class ContentParent final : public PCont
 
   // Control the priority of the IPC messages for input events.
   void SetInputPriorityEventEnabled(bool aEnabled);
   bool IsInputPriorityEventEnabled() { return mIsInputPriorityEventEnabled; }
 
   static bool IsInputEventQueueSupported();
 
   mozilla::ipc::IPCResult RecvAttachBrowsingContext(
-      const BrowsingContextId& aParentContextId,
-      const BrowsingContextId& aOpenerId, const BrowsingContextId& aContextId,
-      const nsString& aName);
+      BrowsingContext* aParentContext, BrowsingContext* aOpener,
+      BrowsingContextId aContextId, const nsString& aName);
 
-  mozilla::ipc::IPCResult RecvDetachBrowsingContext(
-      const BrowsingContextId& aContextId, const bool& aMoveToBFCache);
+  mozilla::ipc::IPCResult RecvDetachBrowsingContext(BrowsingContext* aContext,
+                                                    bool aMoveToBFCache);
 
   mozilla::ipc::IPCResult RecvSetOpenerBrowsingContext(
-      const BrowsingContextId& aContextId,
-      const BrowsingContextId& aOpenerContextId);
+      BrowsingContext* aContext, BrowsingContext* aOpener);
 
-  mozilla::ipc::IPCResult RecvWindowClose(const BrowsingContextId& aContextId,
-                                          const bool& aTrustedCaller);
-  mozilla::ipc::IPCResult RecvWindowFocus(const BrowsingContextId& aContextId);
-  mozilla::ipc::IPCResult RecvWindowBlur(const BrowsingContextId& aContextId);
+  mozilla::ipc::IPCResult RecvWindowClose(BrowsingContext* aContext,
+                                          bool aTrustedCaller);
+  mozilla::ipc::IPCResult RecvWindowFocus(BrowsingContext* aContext);
+  mozilla::ipc::IPCResult RecvWindowBlur(BrowsingContext* aContext);
   mozilla::ipc::IPCResult RecvWindowPostMessage(
-      const BrowsingContextId& aContextId, const ClonedMessageData& aMessage,
+      BrowsingContext* aContext, const ClonedMessageData& aMessage,
       const PostMessageData& aData);
 
   mozilla::ipc::IPCResult RecvSetUserGestureActivation(
-      const BrowsingContextId& aContextId, const bool& aNewValue);
+      BrowsingContext* aContext, bool aNewValue);
 
  protected:
   void OnChannelConnected(int32_t pid) override;
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   bool ShouldContinueFromReplyTimeout() override;
 
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -22,17 +22,17 @@ using DesktopIntRect from "Units.h";
 using DesktopToLayoutDeviceScale from "Units.h";
 using CSSToLayoutDeviceScale from "Units.h";
 using CSSRect from "Units.h";
 using CSSSize from "Units.h";
 using mozilla::LayoutDeviceIntPoint from "Units.h";
 using hal::ScreenOrientation from "mozilla/HalScreenConfiguration.h";
 using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
 using refcounted class nsIPrincipal from "mozilla/dom/PermissionMessageUtils.h";
-using mozilla::dom::BrowsingContextId from "mozilla/dom/ipc/IdType.h";
+using refcounted class mozilla::dom::BrowsingContext from "mozilla/dom/BrowsingContext.h";
 using refcounted class nsIURI from "mozilla/ipc/URIUtils.h";
 
 namespace mozilla {
 namespace dom {
 
 struct MessagePortIdentifier
 {
   nsID uuid;
@@ -184,17 +184,17 @@ struct PerformanceInfo
   // Counters per category. For workers, a single entry
   CategoryDispatch[] items;
 };
 
 
 struct WindowGlobalInit
 {
   nsIPrincipal principal;
-  BrowsingContextId browsingContextId;
+  BrowsingContext browsingContext;
   uint64_t innerWindowId;
   uint64_t outerWindowId;
 };
 
 struct DocShellLoadStateInit
 {
   nsIURI Referrer;
   nsIURI URI;
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -83,17 +83,17 @@ using mozilla::EventMessage from "mozill
 using nsEventStatus from "mozilla/EventForwards.h";
 using mozilla::Modifiers from "mozilla/EventForwards.h";
 using nsSizeMode from "nsIWidgetListener.h";
 using mozilla::widget::CandidateWindowPosition from "ipc/nsGUIEventIPC.h";
 using class mozilla::NativeEventData from "ipc/nsGUIEventIPC.h";
 using mozilla::FontRange from "ipc/nsGUIEventIPC.h";
 using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/IPCTypes.h";
 using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
-using mozilla::dom::BrowsingContextId from "mozilla/dom/ipc/IdType.h";
+using refcounted class mozilla::dom::BrowsingContext from "mozilla/dom/BrowsingContext.h";
 
 namespace mozilla {
 namespace dom {
 
 struct ShowInfo
 {
   nsString name;
   bool fullscreenAllowed;
@@ -616,17 +616,17 @@ parent:
                                      bool aHideDoorHanger);
 
     sync SetSystemFont(nsCString aFontName);
     sync GetSystemFont() returns (nsCString retval);
 
     sync SetPrefersReducedMotionOverrideForTest(bool aValue);
     sync ResetPrefersReducedMotionOverrideForTest();
 
-    async RootBrowsingContext(BrowsingContextId aId);
+    async RootBrowsingContext(BrowsingContext aContext);
 
 child:
     /**
      * Notify the remote browser that it has been Show()n on this
      * side, with the given |visibleRect|.  This message is expected
      * to trigger creation of the remote browser's "widget".
      *
      * |Show()| and |Move()| take IntSizes rather than Rects because
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -97,16 +97,17 @@ using mozilla::Telemetry::HistogramAccum
 using mozilla::Telemetry::KeyedHistogramAccumulation from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::DynamicScalarDefinition from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
 using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
 using refcounted class nsIInputStream from "mozilla/ipc/IPCStreamUtils.h";
+using refcounted class mozilla::dom::BrowsingContext from "mozilla/dom/BrowsingContext.h";
 using mozilla::dom::BrowsingContextId from "mozilla/dom/ipc/IdType.h";
 
 union ChromeRegistryItem
 {
     ChromePackage;
     OverrideMapping;
     SubstitutionMapping;
 };
@@ -303,17 +304,17 @@ struct NotificationEventData
     nsString tag;
     nsString icon;
     nsString data;
     nsString behavior;
 };
 
 struct PostMessageData
 {
-    BrowsingContextId source;
+    BrowsingContext source;
     nsString origin;
     nsString targetOrigin;
     nsIURI targetOriginURI;
     nsIPrincipal callerPrincipal;
     nsIPrincipal subjectPrincipal;
     nsIURI callerDocumentURI;
     bool isFromPrivateWindow;
 };
@@ -1214,57 +1215,54 @@ parent:
                                                   nsCString aTrackingOrigin,
                                                   nsCString aGrantedOrigin,
                                                   int aAllowMode)
           returns (bool unused);
 
     async StoreUserInteractionAsPermission(Principal aPrincipal);
 
     /**
-     * Sync the BrowsingContext with id 'aContextId' and name 'aName'
-     * to the parent, and attach it to the BrowsingContext with id
-     * 'aParentContextId'. If 'aParentContextId' is '0' the
-     * BrowsingContext is a root in the BrowsingContext
-     * tree. AttachBrowsingContext must only be called at most once
-     * for any child BrowsingContext, and only for BrowsingContexts
-     * where the parent and the child context contains their
-     * nsDocShell.
+     * Sync the BrowsingContext with id 'aContextId' and name 'aName' to the
+     * parent, and attach it to the BrowsingContext 'aParentContext'. If
+     * 'aParentContext' is 'nullptr' the BrowsingContext is a root in the
+     * BrowsingContext tree. AttachBrowsingContext must only be called at most
+     * once for any child BrowsingContext, and only for BrowsingContexts where
+     * the parent and the child context contains their nsDocShell.
      */
-    async AttachBrowsingContext(BrowsingContextId aParentContextId,
-                                BrowsingContextId aOpenerId,
+    async AttachBrowsingContext(BrowsingContext aParentContext,
+                                BrowsingContext aOpener,
                                 BrowsingContextId aContextId,
                                 nsString aName);
 
     /**
-     * Remove the synced BrowsingContext with id 'aContextId' from the
-     * parent. DetachBrowsingContext is only needed to be called once
-     * for any BrowsingContext, since detaching a node in the
-     * BrowsingContext detaches the entire sub-tree rooted at that
-     * node. Calling DetachBrowsingContext with an already detached
-     * BrowsingContext effectively does nothing. Note that it is not
-     * an error to call DetachBrowsingContext on a BrowsingContext
-     * belonging to an already detached subtree. The 'aMoveToBFCache'
-     * paramater controls if detaching a BrowsingContext should move
-     * it to the bfcache allowing it to be re-attached if navigated
+     * Remove the synced BrowsingContext 'aContext' from the parent.
+     * DetachBrowsingContext is only needed to be called once for any
+     * BrowsingContext, since detaching a node in the BrowsingContext detaches
+     * the entire sub-tree rooted at that node. Calling DetachBrowsingContext
+     * with an already detached BrowsingContext effectively does nothing. Note
+     * that it is not an error to call DetachBrowsingContext on a
+     * BrowsingContext belonging to an already detached subtree. The
+     * 'aMoveToBFCache' paramater controls if detaching a BrowsingContext
+     * should move it to the bfcache allowing it to be re-attached if navigated
      * to.
      */
-    async DetachBrowsingContext(BrowsingContextId aContextId,
+    async DetachBrowsingContext(BrowsingContext aContext,
                                 bool aMoveToBFCache);
 
     /**
-     * Set the opener of browsing context with id 'aContextId' to the
-     * browsing context with id 'aOpenerId'.
+     * Set the opener of browsing context 'aContext' to the browsing context
+     * with id 'aOpenerId'.
      */
-    async SetOpenerBrowsingContext(BrowsingContextId aContextId,
-                                   BrowsingContextId aOpenerContextId);
+    async SetOpenerBrowsingContext(BrowsingContext aContext,
+                                   BrowsingContext aOpenerContext);
 
     /**
      * Notify parent to update user gesture activation flag.
      */
-    async SetUserGestureActivation(BrowsingContextId aContextId,
+    async SetUserGestureActivation(BrowsingContext aContext,
                                    bool aNewValue);
 
 both:
      async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
                         Principal aPrincipal, ClonedMessageData aData);
 
     /**
      * Notify `push-subscription-modified` observers in the parent and child.
@@ -1274,18 +1272,17 @@ both:
 
     /**
      * Send a Push error message to all service worker clients in the parent or
      * child.
      */
     async PushError(nsCString scope, Principal principal, nsString message,
                     uint32_t flags);
 
-    async WindowClose(BrowsingContextId aContextId,
-                      bool aTrustedCaller);
-    async WindowFocus(BrowsingContextId aContextId);
-    async WindowBlur(BrowsingContextId aContextId);
-    async WindowPostMessage(BrowsingContextId aContextId, ClonedMessageData aMessage,
+    async WindowClose(BrowsingContext aContext, bool aTrustedCaller);
+    async WindowFocus(BrowsingContext aContext);
+    async WindowBlur(BrowsingContext aContext);
+    async WindowPostMessage(BrowsingContext aContext, ClonedMessageData aMessage,
                             PostMessageData aData);
 };
 
 }
 }
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -549,17 +549,17 @@ nsresult TabChild::Init(mozIDOMWindowPro
   MOZ_ASSERT(loadContext);
   loadContext->SetPrivateBrowsing(OriginAttributesRef().mPrivateBrowsingId > 0);
   loadContext->SetRemoteTabs(mChromeFlags &
                              nsIWebBrowserChrome::CHROME_REMOTE_WINDOW);
 
   // Send our browsing context to the parent process.
   RefPtr<BrowsingContext> browsingContext =
       nsDocShell::Cast(docShell)->GetBrowsingContext();
-  SendRootBrowsingContext(BrowsingContextId(browsingContext->Id()));
+  SendRootBrowsingContext(browsingContext);
 
   // Few lines before, baseWindow->Create() will end up creating a new
   // window root in nsGlobalWindow::SetDocShell.
   // Then this chrome event handler, will be inherited to inner windows.
   // We want to also set it to the docshell so that inner windows
   // and any code that has access to the docshell
   // can all listen to the same chrome event handler.
   // XXX: ideally, we would set a chrome event handler earlier,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -3439,19 +3439,19 @@ mozilla::ipc::IPCResult TabParent::RecvG
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (widget) {
     widget->GetSystemFont(*aFontName);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult TabParent::RecvRootBrowsingContext(
-    const BrowsingContextId& aId) {
+  BrowsingContext* aBrowsingContext) {
   MOZ_ASSERT(!mBrowsingContext, "May only set browsing context once!");
-  mBrowsingContext = CanonicalBrowsingContext::Get(aId);
+  mBrowsingContext = CanonicalBrowsingContext::Cast(aBrowsingContext);
   MOZ_ASSERT(mBrowsingContext, "Invalid ID!");
   return IPC_OK();
 }
 
 NS_IMETHODIMP
 FakeChannel::OnAuthAvailable(nsISupports* aContext,
                              nsIAuthInformation* aAuthInfo) {
   nsAuthInformationHolder* holder =
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -252,21 +252,20 @@ class TabParent final : public PBrowserP
   mozilla::ipc::IPCResult RecvEnableDisableCommands(
       const nsString& aAction, nsTArray<nsCString>&& aEnabledCommands,
       nsTArray<nsCString>&& aDisabledCommands);
 
   mozilla::ipc::IPCResult RecvSetCursor(
       const nsCursor& aValue, const bool& aHasCustomCursor,
       const nsCString& aUri, const uint32_t& aWidth, const uint32_t& aHeight,
       const uint32_t& aStride, const gfx::SurfaceFormat& aFormat,
-      const uint32_t& aHotspotX, const uint32_t& aHotspotY,
-      const bool& aForce);
+      const uint32_t& aHotspotX, const uint32_t& aHotspotY, const bool& aForce);
 
-  mozilla::ipc::IPCResult RecvSetStatus(
-      const uint32_t& aType, const nsString& aStatus);
+  mozilla::ipc::IPCResult RecvSetStatus(const uint32_t& aType,
+                                        const nsString& aStatus);
 
   mozilla::ipc::IPCResult RecvShowTooltip(const uint32_t& aX,
                                           const uint32_t& aY,
                                           const nsString& aTooltip,
                                           const nsString& aDirection);
 
   mozilla::ipc::IPCResult RecvHideTooltip();
 
@@ -590,17 +589,17 @@ class TabParent final : public PBrowserP
                                             const int32_t& aX,
                                             const int32_t& aY,
                                             const int32_t& aCx,
                                             const int32_t& aCy);
 
   mozilla::ipc::IPCResult RecvShowCanvasPermissionPrompt(
       const nsCString& aFirstPartyURI, const bool& aHideDoorHanger);
 
-  mozilla::ipc::IPCResult RecvRootBrowsingContext(const BrowsingContextId& aId);
+  mozilla::ipc::IPCResult RecvRootBrowsingContext(BrowsingContext* aContext);
 
   mozilla::ipc::IPCResult RecvSetSystemFont(const nsCString& aFontName);
   mozilla::ipc::IPCResult RecvGetSystemFont(nsCString* aFontName);
 
   mozilla::ipc::IPCResult RecvVisitURI(const URIParams& aURI,
                                        const OptionalURIParams& aLastVisitedURI,
                                        const uint32_t& aFlags);
 
--- a/dom/ipc/WindowGlobalChild.cpp
+++ b/dom/ipc/WindowGlobalChild.cpp
@@ -34,19 +34,18 @@ already_AddRefed<WindowGlobalChild> Wind
 
   RefPtr<nsDocShell> docshell = nsDocShell::Cast(aWindow->GetDocShell());
   MOZ_ASSERT(docshell);
 
   // Initalize our WindowGlobalChild object.
   RefPtr<dom::BrowsingContext> bc = docshell->GetBrowsingContext();
   RefPtr<WindowGlobalChild> wgc = new WindowGlobalChild(aWindow, bc);
 
-  WindowGlobalInit init(principal,
-                        BrowsingContextId(wgc->BrowsingContext()->Id()),
-                        wgc->mInnerWindowId, wgc->mOuterWindowId);
+  WindowGlobalInit init(principal, bc, wgc->mInnerWindowId,
+                        wgc->mOuterWindowId);
 
   // Send the link constructor over PInProcessChild or PBrowser.
   if (XRE_IsParentProcess()) {
     InProcessChild* ipc = InProcessChild::Singleton();
     if (!ipc) {
       return nullptr;
     }
 
--- a/dom/ipc/WindowGlobalParent.cpp
+++ b/dom/ipc/WindowGlobalParent.cpp
@@ -32,17 +32,17 @@ WindowGlobalParent::WindowGlobalParent(c
       mOuterWindowId(aInit.outerWindowId()),
       mInProcess(aInProcess),
       mIPCClosed(true)  // Closed until WGP::Init
 {
   MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(), "Parent process only");
   MOZ_RELEASE_ASSERT(mDocumentPrincipal, "Must have a valid principal");
 
   // NOTE: mBrowsingContext initialized in Init()
-  MOZ_RELEASE_ASSERT(aInit.browsingContextId() != 0,
+  MOZ_RELEASE_ASSERT(aInit.browsingContext(),
                      "Must be made in BrowsingContext");
 }
 
 void WindowGlobalParent::Init(const WindowGlobalInit& aInit) {
   MOZ_ASSERT(Manager(), "Should have a manager!");
   MOZ_ASSERT(!mFrameLoader, "Cannot Init() a WindowGlobalParent twice!");
 
   MOZ_ASSERT(mIPCClosed, "IPC shouldn't be open yet");
@@ -58,17 +58,17 @@ void WindowGlobalParent::Init(const Wind
   entry.OrInsert([&] { return this; });
 
   // Determine which content process the window global is coming from.
   ContentParentId processId(0);
   if (!mInProcess) {
     processId = static_cast<ContentParent*>(Manager()->Manager())->ChildID();
   }
 
-  mBrowsingContext = CanonicalBrowsingContext::Get(aInit.browsingContextId());
+  mBrowsingContext = CanonicalBrowsingContext::Cast(aInit.browsingContext());
   MOZ_ASSERT(mBrowsingContext);
 
   // XXX(nika): This won't be the case soon, but for now this is a good
   // assertion as we can't switch processes. We should relax this eventually.
   MOZ_ASSERT(mBrowsingContext->IsOwnedByProcess(processId));
 
   // Attach ourself to the browsing context.
   mBrowsingContext->RegisterWindowGlobal(this);
--- a/dom/ipc/WindowGlobalParent.h
+++ b/dom/ipc/WindowGlobalParent.h
@@ -17,16 +17,17 @@ class nsIURI;
 class nsFrameLoader;
 
 namespace mozilla {
 namespace dom {
 
 class CanonicalBrowsingContext;
 class WindowGlobalChild;
 class JSWindowActorParent;
+class TabParent;
 
 /**
  * A handle in the parent process to a specific nsGlobalWindowInner object.
  */
 class WindowGlobalParent final : public nsISupports,
                                  public nsWrapperCache,
                                  public PWindowGlobalParent {
   friend class PWindowGlobalParent;
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -306,16 +306,27 @@ nsresult ScriptLoader::CheckContentPolic
 
   nsCOMPtr<nsINode> requestingNode = do_QueryInterface(aContext);
   nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo(
       aDocument->NodePrincipal(),  // loading principal
       aDocument->NodePrincipal(),  // triggering principal
       requestingNode, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
       contentPolicyType);
 
+  // snapshot the nonce at load start time for performing CSP checks
+  if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT ||
+      contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_MODULE) {
+    nsCOMPtr<Element> element = do_QueryInterface(aContext);
+    if (element && element->IsHTMLElement()) {
+      nsAutoString cspNonce;
+      element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce);
+      secCheckLoadInfo->SetCspNonce(cspNonce);
+    }
+  }
+
   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
   nsresult rv = NS_CheckContentLoadPolicy(
       aRequest->mURI, secCheckLoadInfo, NS_LossyConvertUTF16toASCII(aType),
       &shouldLoad, nsContentUtils::GetContentPolicy());
   if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
     if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
       return NS_ERROR_CONTENT_BLOCKED;
     }
@@ -1252,16 +1263,28 @@ nsresult ScriptLoader::StartLoad(ScriptL
       getter_AddRefs(channel), aRequest->mURI, context,
       aRequest->TriggeringPrincipal(), securityFlags, contentPolicyType,
       nullptr,  // aPerformanceStorage
       loadGroup, prompter,
       nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI);
 
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // snapshot the nonce at load start time for performing CSP checks
+  if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT ||
+      contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_MODULE) {
+    nsCOMPtr<Element> element = do_QueryInterface(context);
+    if (element && element->IsHTMLElement()) {
+      nsAutoString cspNonce;
+      element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce);
+      nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
+      loadInfo->SetCspNonce(cspNonce);
+    }
+  }
+
   // To avoid decoding issues, the build-id is part of the JSBytecodeMimeType
   // constant.
   aRequest->mCacheInfo = nullptr;
   nsCOMPtr<nsICacheInfoChannel> cic(do_QueryInterface(channel));
   if (cic && nsContentUtils::IsBytecodeCacheEnabled() &&
       // Bug 1436400: no bytecode cache support for modules yet.
       !aRequest->IsModuleRequest()) {
     if (!aRequest->IsLoadingSource()) {
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -116,17 +116,18 @@ static void BlockedContentSourceToString
 
 NS_IMETHODIMP
 nsCSPContext::ShouldLoad(nsContentPolicyType aContentType,
                          nsICSPEventListener* aCSPEventListener,
                          nsIURI* aContentLocation, nsIURI* aRequestOrigin,
                          nsISupports* aRequestContext,
                          const nsACString& aMimeTypeGuess,
                          nsIURI* aOriginalURIIfRedirect,
-                         bool aSendViolationReports, int16_t* outDecision) {
+                         bool aSendViolationReports, const nsAString& aNonce,
+                         int16_t* outDecision) {
   if (CSPCONTEXTLOGENABLED()) {
     CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, aContentLocation: %s",
                    aContentLocation->GetSpecOrDefault().get()));
     CSPCONTEXTLOG((">>>>                      aContentType: %d", aContentType));
   }
 
   bool isPreload = nsContentUtils::IsPreloadType(aContentType);
 
@@ -150,39 +151,29 @@ nsCSPContext::ShouldLoad(nsContentPolicy
 
   // If the content type doesn't map to a CSP directive, there's nothing for
   // CSP to do.
   CSPDirective dir = CSP_ContentTypeToDirective(aContentType);
   if (dir == nsIContentSecurityPolicy::NO_DIRECTIVE) {
     return NS_OK;
   }
 
-  nsAutoString nonce;
   bool parserCreated = false;
   if (!isPreload) {
-    if (aContentType == nsIContentPolicy::TYPE_SCRIPT ||
-        aContentType == nsIContentPolicy::TYPE_STYLESHEET) {
-      nsCOMPtr<Element> element = do_QueryInterface(aRequestContext);
-      if (element && element->IsHTMLElement()) {
-        // XXXbz What about SVG elements that can have nonce?
-        element->GetAttribute(NS_LITERAL_STRING("nonce"), nonce);
-      }
-    }
-
     nsCOMPtr<nsIScriptElement> script = do_QueryInterface(aRequestContext);
     if (script && script->GetParserCreated() != mozilla::dom::NOT_FROM_PARSER) {
       parserCreated = true;
     }
   }
 
   bool permitted =
       permitsInternal(dir,
                       nullptr,  // aTriggeringElement
                       aCSPEventListener, aContentLocation,
-                      aOriginalURIIfRedirect, nonce, isPreload,
+                      aOriginalURIIfRedirect, aNonce, isPreload,
                       false,  // allow fallback to default-src
                       aSendViolationReports,
                       true,  // send blocked URI in violation reports
                       parserCreated);
 
   *outDecision =
       permitted ? nsIContentPolicy::ACCEPT : nsIContentPolicy::REJECT_SERVER;
 
--- a/dom/security/nsCSPService.cpp
+++ b/dom/security/nsCSPService.cpp
@@ -167,31 +167,35 @@ CSPService::ShouldLoad(nsIURI *aContentL
   } else {
     principal = node->NodePrincipal();
   }
   if (!principal) {
     // if we can't query a principal, then there is nothing to do.
     return NS_OK;
   }
 
+  nsAutoString cspNonce;
+  rv = aLoadInfo->GetCspNonce(cspNonce);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // 1) Apply speculate CSP for preloads
   bool isPreload = nsContentUtils::IsPreloadType(contentType);
 
   if (isPreload) {
     nsCOMPtr<nsIContentSecurityPolicy> preloadCsp;
     rv = principal->GetPreloadCsp(getter_AddRefs(preloadCsp));
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (preloadCsp) {
       // obtain the enforcement decision
       rv = preloadCsp->ShouldLoad(
           contentType, cspEventListener, aContentLocation, requestOrigin,
           requestContext, aMimeTypeGuess,
           nullptr,  // no redirect, aOriginal URL is null.
-          aLoadInfo->GetSendCSPViolationEvents(), aDecision);
+          aLoadInfo->GetSendCSPViolationEvents(), cspNonce, aDecision);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // if the preload policy already denied the load, then there
       // is no point in checking the real policy
       if (NS_CP_REJECTED(*aDecision)) {
         return NS_OK;
       }
     }
@@ -202,17 +206,18 @@ CSPService::ShouldLoad(nsIURI *aContentL
   rv = principal->GetCsp(getter_AddRefs(csp));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (csp) {
     // obtain the enforcement decision
     rv = csp->ShouldLoad(contentType, cspEventListener, aContentLocation,
                          requestOrigin, requestContext, aMimeTypeGuess,
                          nullptr,  // no redirect, aOriginal URL is null.
-                         aLoadInfo->GetSendCSPViolationEvents(), aDecision);
+                         aLoadInfo->GetSendCSPViolationEvents(), cspNonce,
+                         aDecision);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 CSPService::ShouldProcess(nsIURI *aContentLocation, nsILoadInfo *aLoadInfo,
                           const nsACString &aMimeTypeGuess,
@@ -246,27 +251,16 @@ CSPService::ShouldProcess(nsIURI *aConte
 
 /* nsIChannelEventSink implementation */
 NS_IMETHODIMP
 CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
                                    nsIChannel *newChannel, uint32_t flags,
                                    nsIAsyncVerifyRedirectCallback *callback) {
   net::nsAsyncRedirectAutoCallback autoCallback(callback);
 
-  if (XRE_IsE10sParentProcess()) {
-    nsCOMPtr<nsIParentChannel> parentChannel;
-    NS_QueryNotificationCallbacks(oldChannel, parentChannel);
-    if (parentChannel) {
-      // This is an IPC'd channel. Don't check it here, because we won't have
-      // access to the request context; we'll check them in the content
-      // process instead. Bug 1509738 covers fixing this.
-      return NS_OK;
-    }
-  }
-
   nsCOMPtr<nsIURI> newUri;
   nsresult rv = newChannel->GetURI(getter_AddRefs(newUri));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsILoadInfo> loadInfo = oldChannel->GetLoadInfo();
 
   // if no loadInfo on the channel, nothing for us to do
   if (!loadInfo) {
@@ -298,16 +292,20 @@ CSPService::AsyncOnChannelRedirect(nsICh
   nsCOMPtr<nsIURI> originalUri;
   rv = oldChannel->GetOriginalURI(getter_AddRefs(originalUri));
   if (NS_FAILED(rv)) {
     autoCallback.DontCallback();
     oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
     return rv;
   }
 
+  nsAutoString cspNonce;
+  rv = loadInfo->GetCspNonce(cspNonce);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   bool isPreload = nsContentUtils::IsPreloadType(policyType);
 
   /* On redirect, if the content policy is a preload type, rejecting the preload
    * results in the load silently failing, so we convert preloads to the actual
    * type. See Bug 1219453.
    */
   policyType =
       nsContentUtils::InternalContentPolicyTypeToExternalOrWorker(policyType);
@@ -325,16 +323,17 @@ CSPService::AsyncOnChannelRedirect(nsICh
           policyType,  // load type per nsIContentPolicy (uint32_t)
           cspEventListener,
           newUri,          // nsIURI
           nullptr,         // nsIURI
           requestContext,  // nsISupports
           EmptyCString(),  // ACString - MIME guess
           originalUri,     // Original nsIURI
           true,            // aSendViolationReports
+          cspNonce,        // nonce
           &aDecision);
 
       // if the preload policy already denied the load, then there
       // is no point in checking the real policy
       if (NS_CP_REJECTED(aDecision)) {
         autoCallback.DontCallback();
         oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
         return NS_BINDING_FAILED;
@@ -351,16 +350,17 @@ CSPService::AsyncOnChannelRedirect(nsICh
     csp->ShouldLoad(policyType,  // load type per nsIContentPolicy (uint32_t)
                     cspEventListener,
                     newUri,          // nsIURI
                     nullptr,         // nsIURI
                     requestContext,  // nsISupports
                     EmptyCString(),  // ACString - MIME guess
                     originalUri,     // Original nsIURI
                     true,            // aSendViolationReports
+                    cspNonce,        // nonce
                     &aDecision);
   }
 
   // if ShouldLoad doesn't accept the load, cancel the request
   if (!NS_CP_ACCEPTED(aDecision)) {
     autoCallback.DontCallback();
     oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
     return NS_BINDING_FAILED;
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_nonce_snapshot.sjs
@@ -0,0 +1,48 @@
+"use strict";
+
+const TEST_FRAME =
+  `<!DOCTYPE HTML>
+   <html>
+   <body>
+   <script id='myScript' nonce='123456789' type='application/javascript'></script>
+   <script nonce='123456789'>
+     let myScript = document.getElementById('myScript');
+     // 1) start loading the script using the nonce 123456789
+     myScript.src='file_nonce_snapshot.sjs?redir-script';
+     // 2) dynamically change the nonce, load should use initial nonce
+     myScript.setAttribute('nonce','987654321');
+   </script>
+   </body>
+   </html>`;
+
+const SCRIPT = "window.parent.postMessage('script-loaded', '*');";
+
+function handleRequest(request, response)
+{
+  // avoid confusing cache behaviors
+  response.setHeader("Cache-Control", "no-cache", false);
+ 
+  let queryString = request.queryString;
+
+  if (queryString === "load-frame") {
+    response.setHeader("Content-Security-Policy", "script-src 'nonce-123456789'", false);
+    response.setHeader("Content-Type", "text/html", false);
+    response.write(TEST_FRAME);
+    return;
+  }
+
+  if (queryString === "redir-script") {
+    response.setStatusLine("1.1", 302, "Found");
+    response.setHeader("Location", "file_nonce_snapshot.sjs?load-script", false);
+    return;
+  }
+
+  if (queryString === "load-script") {
+    response.setHeader("Content-Type", "application/javascript", false);
+    response.write(SCRIPT);
+    return;
+  }
+
+  // we should never get here but just in case return something unexpected
+  response.write("do'h");
+}
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -363,8 +363,11 @@ support-files =
   file_frame_src_inner.html
 [test_security_policy_violation_event.html]
 [test_csp_worker_inheritance.html]
 support-files =
   worker.sjs
   worker_helper.js
   main_csp_worker.html
   main_csp_worker.html^headers^
+[test_nonce_snapshot.html]
+support-files =
+  file_nonce_snapshot.sjs
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/test_nonce_snapshot.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Bug 1509738 - Snapshot nonce at load start time</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * a) the test starts loading a script using whitelisted nonce
+ * b) the nonce of the script gets modified
+ * c) the script hits a 302 server side redirect
+ * d) we ensure the script still loads and does not use the modified nonce
+ */
+
+window.addEventListener("message", receiveMessage);
+function receiveMessage(event) {
+  is(event.data, "script-loaded", "script loaded even though nonce was dynamically modified");
+  window.removeEventListener("message", receiveMessage);
+  SimpleTest.finish();
+}
+
+
+SimpleTest.waitForExplicitFinish();
+let src = "file_nonce_snapshot.sjs?load-frame";
+document.getElementById("testframe").src = src;
+
+</script>
+</body>
+</html>
--- a/dom/security/test/unit/test_csp_reports.js
+++ b/dom/security/test/unit/test_csp_reports.js
@@ -148,17 +148,17 @@ function run_test() {
       });
 
   makeTest(2, {"blocked-uri": "http://blocked.test/foo.js"}, false,
       function(csp) {
         // shouldLoad creates and sends out the report here.
         csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT,
                       null, // nsICSPEventListener
                       NetUtil.newURI("http://blocked.test/foo.js"),
-                      null, null, null, null, true);
+                      null, null, null, null, true, null);
       });
 
   // test that inline script violations cause a report in report-only policy
   makeTest(3, {"blocked-uri": "inline"}, true,
       function(csp) {
         let inlineOK = true;
         inlineOK = csp.getAllowsInline(Ci.nsIContentPolicy.TYPE_SCRIPT,
                                        "", // aNonce
@@ -201,42 +201,42 @@ function run_test() {
     function(csp) {
       var base64data =
         "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
         "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
       // shouldLoad creates and sends out the report here.
       csp.shouldLoad(Ci.nsIContentPolicy.TYPE_IMAGE,
                      null, // nsICSPEventListener
                      NetUtil.newURI("data:image/png;base64," + base64data),
-                     null, null, null, null, true);
+                     null, null, null, null, true, null);
       });
 
   // test that only the uri's scheme is reported for globally unique identifiers
   makeTest(6, {"blocked-uri": "intent"}, false,
     function(csp) {
       // shouldLoad creates and sends out the report here.
       csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SUBDOCUMENT,
                      null, // nsICSPEventListener
                      NetUtil.newURI("intent://mymaps.com/maps?um=1&ie=UTF-8&fb=1&sll"),
-                     null, null, null, null, true);
+                     null, null, null, null, true, null);
       });
 
   // test fragment removal
   var selfSpec = REPORT_SERVER_URI + ":" + REPORT_SERVER_PORT + "/foo/self/foo.js";
   makeTest(7, {"blocked-uri": selfSpec}, false,
     function(csp) {
       // shouldLoad creates and sends out the report here.
       csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT,
                      null, // nsICSPEventListener
                      NetUtil.newURI(selfSpec + "#bar"),
-                     null, null, null, null, true);
+                     null, null, null, null, true, null);
       });
 
   // test scheme of ftp:
   makeTest(8, {"blocked-uri": "ftp://blocked.test/profile.png"}, false,
     function(csp) {
       // shouldLoad creates and sends out the report here.
       csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT,
                      null, // nsICSPEventListener
                     NetUtil.newURI("ftp://blocked.test/profile.png"),
-                    null, null, null, null, true);
+                    null, null, null, null, true, null);
     });
 }
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -19,17 +19,17 @@ use std::os::raw::{c_int};
 use gleam::gl;
 
 use webrender::api::*;
 use webrender::{ReadPixelsFormat, Renderer, RendererOptions, RendererStats, ThreadListener};
 use webrender::{ExternalImage, ExternalImageHandler, ExternalImageSource};
 use webrender::DebugFlags;
 use webrender::{ApiRecordingReceiver, BinaryRecorder};
 use webrender::{AsyncPropertySampler, PipelineInfo, SceneBuilderHooks};
-use webrender::{UploadMethod, VertexUsageHint};
+use webrender::{UploadMethod, VertexUsageHint, ProfilerHooks, set_profiler_hooks};
 use webrender::{Device, Shaders, WrShaders, ShaderPrecacheFlags};
 use thread_profiler::register_thread_with_profiler;
 use moz2d_renderer::Moz2dBlobImageHandler;
 use program_cache::{WrProgramCache, remove_disk_cache};
 use app_units::Au;
 use rayon;
 use euclid::SideOffsets2D;
 use nsstring::nsAString;
@@ -774,16 +774,36 @@ pub unsafe extern "C" fn wr_pipeline_inf
     // the underlying vec memory
 }
 
 extern "C" {
     pub fn gecko_profiler_start_marker(name: *const c_char);
     pub fn gecko_profiler_end_marker(name: *const c_char);
 }
 
+/// Simple implementation of the WR ProfilerHooks trait to allow profile
+/// markers to be seen in the Gecko profiler.
+struct GeckoProfilerHooks;
+
+impl ProfilerHooks for GeckoProfilerHooks {
+    fn begin_marker(&self, label: &CStr) {
+        unsafe {
+            gecko_profiler_start_marker(label.as_ptr());
+        }
+    }
+
+    fn end_marker(&self, label: &CStr) {
+        unsafe {
+            gecko_profiler_end_marker(label.as_ptr());
+        }
+    }
+}
+
+const PROFILER_HOOKS: GeckoProfilerHooks = GeckoProfilerHooks {};
+
 #[allow(improper_ctypes)] // this is needed so that rustc doesn't complain about passing the &mut Transaction to an extern function
 extern "C" {
     // These callbacks are invoked from the scene builder thread (aka the APZ
     // updater thread)
     fn apz_register_updater(window_id: WrWindowId);
     fn apz_pre_scene_swap(window_id: WrWindowId);
     // This function takes ownership of the pipeline_info and is responsible for
     // freeing it via wr_pipeline_info_delete.
@@ -1115,16 +1135,19 @@ pub extern "C" fn wr_window_new(window_i
         max_texture_size: Some(8192), // Moz2D doesn't like textures bigger than this
         clear_color: Some(ColorF::new(0.0, 0.0, 0.0, 0.0)),
         precache_flags,
         namespace_alloc_by_client: true,
         enable_picture_caching,
         ..Default::default()
     };
 
+    // Ensure the WR profiler callbacks are hooked up to the Gecko profiler.
+    set_profiler_hooks(Some(&PROFILER_HOOKS));
+
     let notifier = Box::new(CppNotifier {
         window_id: window_id,
     });
     let (renderer, sender) = match Renderer::new(gl, notifier, opts, shaders) {
         Ok((renderer, sender)) => (renderer, sender),
         Err(e) => {
             warn!(" Failed to create a Renderer: {:?}", e);
             let msg = CString::new(format!("wr_window_new: {:?}", e)).unwrap();
--- a/gfx/wr/Cargo.lock
+++ b/gfx/wr/Cargo.lock
@@ -315,16 +315,34 @@ dependencies = [
 ]
 
 [[package]]
 name = "crossbeam-utils"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "cstr"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cstr-macros 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "procedural-masquerade 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cstr-macros"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "procedural-masquerade 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.17 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "deflate"
 version = "0.7.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -1071,16 +1089,21 @@ dependencies = [
 name = "proc-macro2"
 version = "0.4.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "procedural-masquerade"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "quick-error"
 version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "quote"
 version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1602,16 +1625,17 @@ dependencies = [
  "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cstr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1878,16 +1902,18 @@ dependencies = [
 "checksum core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f46450d6f2397261af420b4ccce23807add2e45fa206410a03d66fb7f050ae"
 "checksum crossbeam 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "bd66663db5a988098a89599d4857919b3acf7f61402e61365acfd3919857b9be"
 "checksum crossbeam-channel 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6c0a94250b0278d7fc5a894c3d276b11ea164edc8bf8feb10ca1ea517b44a649"
 "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
 "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
 "checksum crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30fecfcac6abfef8771151f8be4abc9e4edc112c2bcb233314cafde2680536e9"
 "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
 "checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015"
+"checksum cstr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ee681252c9c0a6e84bbb53257faa3d88a49ce6fb32148ae1a9dc24b588302a71"
+"checksum cstr-macros 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5e700cd6ede9b3f81b23ce4cba15f75cc8bf5b5a5dce2db293b1d31f4950ed78"
 "checksum deflate 0.7.18 (registry+https://github.com/rust-lang/crates.io-index)" = "32c8120d981901a9970a3a1c97cf8b630e0fa8c3ca31e75b6fd6fd5f9f427b31"
 "checksum derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f57d78cf3bd45270dad4e70c21ec77a960b36c7a841ff9db76aaa775a8fb871"
 "checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c"
 "checksum dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a"
 "checksum downcast-rs 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "18df8ce4470c189d18aa926022da57544f31e154631eb4cfe796aea97051fe6c"
 "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
 "checksum dwrote 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c31c624339dab99c223a4b26c2e803b7c248adaca91549ce654c76f39a03f5c8"
 "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
@@ -1964,16 +1990,17 @@ dependencies = [
 "checksum pathfinder_gfx_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)" = "<none>"
 "checksum pathfinder_partitioner 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)" = "<none>"
 "checksum pathfinder_path_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)" = "<none>"
 "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
 "checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f"
 "checksum plane-split 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b84b8cf2daa6a829b3e756fb75e0eab8e0d963754de9bfc83a4373a47121323a"
 "checksum png 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9adebf7fb91ccf5eac9da1a8e00e83cb8ae882c3e8d8e4ad59da73cb8c82a2c9"
 "checksum proc-macro2 0.4.25 (registry+https://github.com/rust-lang/crates.io-index)" = "d3797b7142c9aa74954e351fc089bbee7958cebbff6bf2815e7ffff0b19f547d"
+"checksum procedural-masquerade 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9a1574a51c3fd37b26d2c0032b649d08a7d51d4cca9c41bbc5bf7118fa4509d0"
 "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4"
 "checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035"
 "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
 "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
 "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
 "checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2"
 "checksum rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80e811e76f1dbf68abf87a759083d34600017fc4e10b6bd5ad84a700f9dba4b1"
 "checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8"
--- a/gfx/wr/webrender/Cargo.toml
+++ b/gfx/wr/webrender/Cargo.toml
@@ -23,16 +23,17 @@ webrender_build = { version = "0.0.1", p
 
 [dependencies]
 app_units = "0.7"
 base64 = { optional = true, version = "0.10" }
 bincode = "1.0"
 bitflags = "1.0"
 byteorder = "1.0"
 cfg-if = "0.1.2"
+cstr = "0.1.2"
 fxhash = "0.2.1"
 gleam = "0.6.8"
 image = { optional = true, version = "0.21" }
 lazy_static = "1"
 log = "0.4"
 malloc_size_of_derive = "0.1"
 num-traits = "0.2"
 plane-split = "0.13.3"
--- a/gfx/wr/webrender/src/frame_builder.rs
+++ b/gfx/wr/webrender/src/frame_builder.rs
@@ -369,16 +369,18 @@ impl FrameBuilder {
             &mut self.prim_store.pictures,
             &frame_context,
             gpu_cache,
             &self.clip_store,
             &data_stores.clip,
         );
 
         {
+            profile_marker!("UpdateVisibility");
+
             let visibility_context = FrameVisibilityContext {
                 device_pixel_scale,
                 clip_scroll_tree,
                 screen_world_rect,
                 surfaces,
                 debug_flags,
                 scene_properties,
                 config: &self.config,
@@ -434,25 +436,29 @@ impl FrameBuilder {
                 root_spatial_node_index,
                 ROOT_SURFACE_INDEX,
                 true,
                 &mut frame_state,
                 &frame_context,
             )
             .unwrap();
 
-        self.prim_store.prepare_primitives(
-            &mut prim_list,
-            &pic_context,
-            &mut pic_state,
-            &frame_context,
-            &mut frame_state,
-            data_stores,
-            scratch,
-        );
+        {
+            profile_marker!("PreparePrims");
+
+            self.prim_store.prepare_primitives(
+                &mut prim_list,
+                &pic_context,
+                &mut pic_state,
+                &frame_context,
+                &mut frame_state,
+                data_stores,
+                scratch,
+            );
+        }
 
         let pic = &mut self.prim_store.pictures[self.root_pic_index.0];
         pic.restore_context(
             prim_list,
             pic_context,
             pic_state,
             &mut frame_state,
         );
@@ -495,16 +501,17 @@ impl FrameBuilder {
         texture_cache_profile: &mut TextureCacheProfileCounters,
         gpu_cache_profile: &mut GpuCacheProfileCounters,
         scene_properties: &SceneProperties,
         data_stores: &mut DataStores,
         scratch: &mut PrimitiveScratchBuffer,
         debug_flags: DebugFlags,
     ) -> Frame {
         profile_scope!("build");
+        profile_marker!("BuildFrame");
         debug_assert!(
             DeviceIntRect::new(DeviceIntPoint::zero(), self.window_size)
                 .contains_rect(&self.screen_rect)
         );
 
         let mut profile_counters = FrameProfileCounters::new();
         profile_counters
             .total_primitives
@@ -541,89 +548,96 @@ impl FrameBuilder {
             scene_properties,
             &mut transform_palette,
             data_stores,
             &mut surfaces,
             scratch,
             debug_flags,
         );
 
-        resource_cache.block_until_all_resources_added(gpu_cache,
-                                                       &mut render_tasks,
-                                                       texture_cache_profile);
+        {
+            profile_marker!("BlockOnResources");
+
+            resource_cache.block_until_all_resources_added(gpu_cache,
+                                                           &mut render_tasks,
+                                                           texture_cache_profile);
+        }
 
         let mut passes = vec![];
-
-        // Add passes as required for our cached render tasks.
-        if !render_tasks.cacheable_render_tasks.is_empty() {
-            passes.push(RenderPass::new_off_screen(screen_size));
-            for cacheable_render_task in &render_tasks.cacheable_render_tasks {
-                render_tasks.assign_to_passes(
-                    *cacheable_render_task,
-                    0,
-                    screen_size,
-                    &mut passes,
-                );
-            }
-            passes.reverse();
-        }
-
-        if let Some(main_render_task_id) = main_render_task_id {
-            let passes_start = passes.len();
-            passes.push(RenderPass::new_main_framebuffer(screen_size));
-            render_tasks.assign_to_passes(
-                main_render_task_id,
-                passes_start,
-                screen_size,
-                &mut passes,
-            );
-            passes[passes_start..].reverse();
-        }
-
-
         let mut deferred_resolves = vec![];
         let mut has_texture_cache_tasks = false;
         let mut prim_headers = PrimitiveHeaders::new();
-        // Used to generated a unique z-buffer value per primitive.
-        let mut z_generator = ZBufferIdGenerator::new();
-        let use_dual_source_blending = self.config.dual_source_blending_is_enabled &&
-                                       self.config.dual_source_blending_is_supported;
+
+        {
+            profile_marker!("Batching");
 
-        for pass in &mut passes {
-            let mut ctx = RenderTargetContext {
-                device_pixel_scale,
-                prim_store: &self.prim_store,
-                resource_cache,
-                use_dual_source_blending,
-                clip_scroll_tree,
-                data_stores,
-                surfaces: &surfaces,
-                scratch,
-                screen_world_rect,
-                globals: &self.globals,
-            };
+            // Add passes as required for our cached render tasks.
+            if !render_tasks.cacheable_render_tasks.is_empty() {
+                passes.push(RenderPass::new_off_screen(screen_size));
+                for cacheable_render_task in &render_tasks.cacheable_render_tasks {
+                    render_tasks.assign_to_passes(
+                        *cacheable_render_task,
+                        0,
+                        screen_size,
+                        &mut passes,
+                    );
+                }
+                passes.reverse();
+            }
+
+            if let Some(main_render_task_id) = main_render_task_id {
+                let passes_start = passes.len();
+                passes.push(RenderPass::new_main_framebuffer(screen_size));
+                render_tasks.assign_to_passes(
+                    main_render_task_id,
+                    passes_start,
+                    screen_size,
+                    &mut passes,
+                );
+                passes[passes_start..].reverse();
+            }
 
-            pass.build(
-                &mut ctx,
-                gpu_cache,
-                &mut render_tasks,
-                &mut deferred_resolves,
-                &self.clip_store,
-                &mut transform_palette,
-                &mut prim_headers,
-                &mut z_generator,
-            );
+            // Used to generated a unique z-buffer value per primitive.
+            let mut z_generator = ZBufferIdGenerator::new();
+            let use_dual_source_blending = self.config.dual_source_blending_is_enabled &&
+                                           self.config.dual_source_blending_is_supported;
+
+            for pass in &mut passes {
+                let mut ctx = RenderTargetContext {
+                    device_pixel_scale,
+                    prim_store: &self.prim_store,
+                    resource_cache,
+                    use_dual_source_blending,
+                    clip_scroll_tree,
+                    data_stores,
+                    surfaces: &surfaces,
+                    scratch,
+                    screen_world_rect,
+                    globals: &self.globals,
+                };
 
-            match pass.kind {
-                RenderPassKind::MainFramebuffer(ref color) => {
-                    has_texture_cache_tasks |= color.must_be_drawn();
-                }
-                RenderPassKind::OffScreen { ref texture_cache, ref color, .. } => {
-                    has_texture_cache_tasks |= !texture_cache.is_empty();
-                    has_texture_cache_tasks |= color.must_be_drawn();
+                pass.build(
+                    &mut ctx,
+                    gpu_cache,
+                    &mut render_tasks,
+                    &mut deferred_resolves,
+                    &self.clip_store,
+                    &mut transform_palette,
+                    &mut prim_headers,
+                    &mut z_generator,
+                );
+
+                match pass.kind {
+                    RenderPassKind::MainFramebuffer(ref color) => {
+                        has_texture_cache_tasks |= color.must_be_drawn();
+                    }
+                    RenderPassKind::OffScreen { ref texture_cache, ref color, .. } => {
+                        has_texture_cache_tasks |= !texture_cache.is_empty();
+                        has_texture_cache_tasks |= color.must_be_drawn();
+                    }
                 }
             }
         }
 
         let gpu_cache_frame_id = gpu_cache.end_frame(gpu_cache_profile).frame_id();
 
         render_tasks.write_task_data(device_pixel_scale);
 
--- a/gfx/wr/webrender/src/lib.rs
+++ b/gfx/wr/webrender/src/lib.rs
@@ -51,30 +51,35 @@ macro_rules! matches {
     }
 }
 
 #[macro_use]
 extern crate bitflags;
 #[macro_use]
 extern crate cfg_if;
 #[macro_use]
+extern crate cstr;
+#[macro_use]
 extern crate lazy_static;
 #[macro_use]
 extern crate log;
 #[macro_use]
 extern crate malloc_size_of_derive;
 #[cfg(any(feature = "serde"))]
 #[macro_use]
 extern crate serde;
 #[macro_use]
 extern crate thread_profiler;
 
 extern crate wr_malloc_size_of;
 use wr_malloc_size_of as malloc_size_of;
 
+#[macro_use]
+mod profiler;
+
 mod batch;
 mod border;
 mod box_shadow;
 #[cfg(any(feature = "capture", feature = "replay"))]
 mod capture;
 mod clip;
 mod clip_scroll_tree;
 mod debug_colors;
@@ -98,17 +103,16 @@ mod gpu_types;
 mod hit_test;
 mod image;
 mod intern;
 mod intern_types;
 mod internal_types;
 mod picture;
 mod prim_store;
 mod print_tree;
-mod profiler;
 mod record;
 mod render_backend;
 mod render_task;
 mod renderer;
 mod resource_cache;
 mod scene;
 mod scene_builder;
 mod segment;
@@ -202,16 +206,17 @@ pub extern crate webrender_api;
 extern crate webrender_build;
 
 #[doc(hidden)]
 pub use device::{build_shader_strings, ReadPixelsFormat, UploadMethod, VertexUsageHint};
 pub use device::{ProgramBinary, ProgramCache, ProgramCacheObserver};
 pub use device::Device;
 pub use frame_builder::ChasePrimitive;
 pub use picture::FRAMES_BEFORE_PICTURE_CACHING;
+pub use profiler::{ProfilerHooks, set_profiler_hooks};
 pub use renderer::{AsyncPropertySampler, CpuProfile, DebugFlags, OutputImageHandler, RendererKind};
 pub use renderer::{ExternalImage, ExternalImageHandler, ExternalImageSource, GpuProfile};
 pub use renderer::{GraphicsApi, GraphicsApiInfo, PipelineInfo, Renderer, RendererOptions};
 pub use renderer::{RenderResults, RendererStats, SceneBuilderHooks, ThreadListener, ShaderPrecacheFlags};
 pub use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 pub use shade::{Shaders, WrShaders};
 pub use webrender_api as api;
 pub use webrender_api::euclid;
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -1641,16 +1641,18 @@ impl<'a> PictureUpdateState<'a> {
         surfaces: &'a mut Vec<SurfaceInfo>,
         pic_index: PictureIndex,
         picture_primitives: &mut [PicturePrimitive],
         frame_context: &FrameBuildingContext,
         gpu_cache: &mut GpuCache,
         clip_store: &ClipStore,
         clip_data_store: &ClipDataStore,
     ) {
+        profile_marker!("UpdatePictures");
+
         let mut state = PictureUpdateState {
             surfaces,
             surface_stack: vec![SurfaceIndex(0)],
             picture_stack: Vec::new(),
             are_raster_roots_assigned: true,
         };
 
         state.update(
--- a/gfx/wr/webrender/src/profiler.rs
+++ b/gfx/wr/webrender/src/profiler.rs
@@ -2,29 +2,92 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{ColorF, ColorU};
 use debug_render::DebugRenderer;
 use device::query::{GpuSampler, GpuTimer, NamedTag};
 use euclid::{Point2D, Rect, Size2D, vec2};
 use internal_types::FastHashMap;
-use renderer::MAX_VERTEX_TEXTURE_WIDTH;
+use renderer::{MAX_VERTEX_TEXTURE_WIDTH, wr_has_been_initialized};
 use std::collections::vec_deque::VecDeque;
 use std::{f32, mem};
+use std::ffi::CStr;
 use time::precise_time_ns;
 
 const GRAPH_WIDTH: f32 = 1024.0;
 const GRAPH_HEIGHT: f32 = 320.0;
 const GRAPH_PADDING: f32 = 8.0;
 const GRAPH_FRAME_HEIGHT: f32 = 16.0;
 const PROFILE_PADDING: f32 = 10.0;
 
 const ONE_SECOND_NS: u64 = 1000000000;
 
+/// Defines the interface for hooking up an external profiler to WR.
+pub trait ProfilerHooks : Send + Sync {
+    /// Called at the beginning of a profile scope. The label must
+    /// be a C string (null terminated).
+    fn begin_marker(&self, label: &CStr);
+
+    /// Called at the end of a profile scope. The label must
+    /// be a C string (null terminated).
+    fn end_marker(&self, label: &CStr);
+}
+
+/// The current global profiler callbacks, if set by embedder.
+static mut PROFILER_HOOKS: Option<&'static ProfilerHooks> = None;
+
+/// Set the profiler callbacks, or None to disable the profiler.
+/// This function must only ever be called before any WR instances
+/// have been created, or the hooks will not be set.
+pub fn set_profiler_hooks(hooks: Option<&'static ProfilerHooks>) {
+    if !wr_has_been_initialized() {
+        unsafe {
+            PROFILER_HOOKS = hooks;
+        }
+    }
+}
+
+/// A simple RAII style struct to manage a profile scope.
+pub struct ProfileScope {
+    name: &'static CStr,
+}
+
+impl ProfileScope {
+    /// Begin a new profile scope
+    pub fn new(name: &'static CStr) -> Self {
+        unsafe {
+            if let Some(ref hooks) = PROFILER_HOOKS {
+                hooks.begin_marker(name);
+            }
+        }
+
+        ProfileScope {
+            name,
+        }
+    }
+}
+
+impl Drop for ProfileScope {
+    fn drop(&mut self) {
+        unsafe {
+            if let Some(ref hooks) = PROFILER_HOOKS {
+                hooks.end_marker(self.name);
+            }
+        }
+    }
+}
+
+/// A helper macro to define profile scopes.
+macro_rules! profile_marker {
+    ($string:expr) => {
+        let _scope = $crate::profiler::ProfileScope::new(cstr!($string));
+    };
+}
+
 #[derive(Debug, Clone)]
 pub struct GpuProfileTag {
     pub label: &'static str,
     pub color: ColorF,
 }
 
 impl NamedTag for GpuProfileTag {
     fn get_label(&self) -> &str {
--- a/gfx/wr/webrender/src/render_task.rs
+++ b/gfx/wr/webrender/src/render_task.rs
@@ -1247,17 +1247,17 @@ impl RenderTaskCache {
                     render_task.uv_rect_kind(),
                     Eviction::Eager,
                 );
 
                 // Get the allocation details in the texture cache, and store
                 // this in the render task. The renderer will draw this
                 // task into the appropriate layer and rect of the texture
                 // cache on this frame.
-                let (texture_id, texture_layer, uv_rect) =
+                let (texture_id, texture_layer, uv_rect, _) =
                     texture_cache.get_cache_location(&entry.handle);
 
                 render_task.location = RenderTaskLocation::TextureCache {
                     texture: texture_id,
                     layer: texture_layer,
                     rect: uv_rect.to_i32(),
                 };
             }
--- a/gfx/wr/webrender/src/renderer.rs
+++ b/gfx/wr/webrender/src/renderer.rs
@@ -79,16 +79,17 @@ use std::cmp;
 use std::collections::VecDeque;
 use std::collections::hash_map::Entry;
 use std::f32;
 use std::mem;
 use std::os::raw::c_void;
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::mpsc::{channel, Receiver};
 use std::thread;
 use std::cell::RefCell;
 use texture_cache::TextureCache;
 use thread_profiler::{register_thread_with_profiler, write_profile};
 use tiling::{AlphaRenderTarget, ColorRenderTarget};
 use tiling::{BlitJob, BlitJobSource, RenderPass, RenderPassKind, RenderTargetList};
 use tiling::{Frame, RenderTarget, RenderTargetKind, TextureCacheRenderTarget};
@@ -101,16 +102,24 @@ cfg_if! {
         use serde_json;
         use debug_server::{self, DebugServer};
     } else {
         use api::ApiMsg;
         use api::channel::MsgSender;
     }
 }
 
+/// Is only false if no WR instances have ever been created.
+static HAS_BEEN_INITIALIZED: AtomicBool = AtomicBool::new(false);
+
+/// Returns true if a WR instance has ever been initialized in this process.
+pub fn wr_has_been_initialized() -> bool {
+    HAS_BEEN_INITIALIZED.load(Ordering::SeqCst)
+}
+
 pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024;
 /// Enabling this toggle would force the GPU cache scattered texture to
 /// be resized every frame, which enables GPU debuggers to see if this
 /// is performed correctly.
 const GPU_CACHE_RESIZE_TEST: bool = false;
 
 /// Number of GPU blocks per UV rectangle provided for an image.
 pub const BLOCKS_PER_UV_RECT: usize = 2;
@@ -1624,16 +1633,18 @@ impl Renderer {
     /// ```
     /// [rendereroptions]: struct.RendererOptions.html
     pub fn new(
         gl: Rc<gl::Gl>,
         notifier: Box<RenderNotifier>,
         mut options: RendererOptions,
         shaders: Option<&mut WrShaders>
     ) -> Result<(Self, RenderApiSender), RendererError> {
+        HAS_BEEN_INITIALIZED.store(true, Ordering::SeqCst);
+
         let (api_tx, api_rx) = channel::msg_channel()?;
         let (payload_tx, payload_rx) = channel::payload_channel()?;
         let (result_tx, result_rx) = channel();
         let gl_type = gl.get_type();
 
         let debug_server = DebugServer::new(api_tx.clone());
 
         let mut device = Device::new(
--- a/gfx/wr/webrender/src/texture_cache.rs
+++ b/gfx/wr/webrender/src/texture_cache.rs
@@ -30,19 +30,19 @@ const TEXTURE_REGION_PIXELS: usize =
 /// Items in the texture cache can either be standalone textures,
 /// or a sub-rect inside the shared cache.
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 enum EntryDetails {
     Standalone,
     Cache {
-        // Origin within the texture layer where this item exists.
+        /// Origin within the texture layer where this item exists.
         origin: DeviceIntPoint,
-        // The layer index of the texture array.
+        /// The layer index of the texture array.
         layer_index: usize,
     },
 }
 
 impl EntryDetails {
     /// Returns the kind associated with the details.
     fn kind(&self) -> EntryKind {
         match *self {
@@ -116,22 +116,18 @@ impl CacheEntry {
 
     // Update the GPU cache for this texture cache entry.
     // This ensures that the UV rect, and texture layer index
     // are up to date in the GPU cache for vertex shaders
     // to fetch from.
     fn update_gpu_cache(&mut self, gpu_cache: &mut GpuCache) {
         if let Some(mut request) = gpu_cache.request(&mut self.uv_rect_handle) {
             let (origin, layer_index) = match self.details {
-                EntryDetails::Standalone { .. } => (DeviceIntPoint::zero(), 0.0),
-                EntryDetails::Cache {
-                    origin,
-                    layer_index,
-                    ..
-                } => (origin, layer_index as f32),
+                EntryDetails::Standalone => (DeviceIntPoint::zero(), 0.0),
+                EntryDetails::Cache { origin, layer_index } => (origin, layer_index as f32),
             };
             let image_source = ImageSource {
                 p0: origin.to_f32(),
                 p1: (origin + self.size).to_f32(),
                 texture_layer: layer_index,
                 user_data: self.user_data,
                 uv_rect_kind: self.uv_rect_kind,
             };
@@ -290,20 +286,19 @@ struct EntryHandles {
     standalone: Vec<FreeListHandle<CacheEntryMarker>>,
     /// Handles for each shared texture cache entry.
     shared: Vec<FreeListHandle<CacheEntryMarker>>,
 }
 
 impl EntryHandles {
     /// Mutably borrows the requested handle list.
     fn select(&mut self, kind: EntryKind) -> &mut Vec<FreeListHandle<CacheEntryMarker>> {
-        if kind == EntryKind::Standalone {
-            &mut self.standalone
-        } else {
-            &mut self.shared
+        match kind {
+            EntryKind::Standalone => &mut self.standalone,
+            EntryKind::Shared => &mut self.shared,
         }
     }
 }
 
 /// Container struct for the various parameters used in cache allocation.
 struct CacheAllocParams {
     descriptor: ImageDescriptor,
     filter: TextureFilter,
@@ -806,22 +801,18 @@ impl TextureCache {
 
         entry.eviction = eviction;
 
         // Create an update command, which the render thread processes
         // to upload the new image data into the correct location
         // in GPU memory.
         if let Some(data) = data {
             let (layer_index, origin) = match entry.details {
-                EntryDetails::Standalone { .. } => (0, DeviceIntPoint::zero()),
-                EntryDetails::Cache {
-                    layer_index,
-                    origin,
-                    ..
-                } => (layer_index, origin),
+                EntryDetails::Standalone => (0, DeviceIntPoint::zero()),
+                EntryDetails::Cache { layer_index, origin } => (layer_index, origin),
             };
 
             let op = TextureCacheUpdate::new_update(
                 data,
                 &descriptor,
                 origin,
                 entry.size,
                 entry.texture_id,
@@ -839,63 +830,52 @@ impl TextureCache {
     }
 
     // Retrieve the details of an item in the cache. This is used
     // during batch creation to provide the resource rect address
     // to the shaders and texture ID to the batching logic.
     // This function will assert in debug modes if the caller
     // tries to get a handle that was not requested this frame.
     pub fn get(&self, handle: &TextureCacheHandle) -> CacheItem {
-        let entry = self.entries
-            .get_opt(handle)
-            .expect("BUG: was dropped from cache or not updated!");
-        debug_assert_eq!(entry.last_access, self.now);
-        let (layer_index, origin) = match entry.details {
-            EntryDetails::Standalone { .. } => {
-                (0, DeviceIntPoint::zero())
-            }
-            EntryDetails::Cache {
-                layer_index,
-                origin,
-                ..
-            } => (layer_index, origin),
-        };
+        let (texture_id, layer_index, uv_rect, uv_rect_handle) = self.get_cache_location(handle);
         CacheItem {
-            uv_rect_handle: entry.uv_rect_handle,
-            texture_id: TextureSource::TextureCache(entry.texture_id),
-            uv_rect: DeviceIntRect::new(origin, entry.size),
+            uv_rect_handle,
+            texture_id: TextureSource::TextureCache(texture_id),
+            uv_rect,
             texture_layer: layer_index as i32,
         }
     }
 
     /// A more detailed version of get(). This allows access to the actual
     /// device rect of the cache allocation.
     ///
-    /// Returns a tuple identifying the texture, the layer, and the region.
+    /// Returns a tuple identifying the texture, the layer, the region,
+    /// and its GPU handle.
     pub fn get_cache_location(
         &self,
         handle: &TextureCacheHandle,
-    ) -> (CacheTextureId, LayerIndex, DeviceIntRect) {
+    ) -> (CacheTextureId, LayerIndex, DeviceIntRect, GpuCacheHandle) {
         let entry = self.entries
             .get_opt(handle)
             .expect("BUG: was dropped from cache or not updated!");
         debug_assert_eq!(entry.last_access, self.now);
         let (layer_index, origin) = match entry.details {
             EntryDetails::Standalone { .. } => {
                 (0, DeviceIntPoint::zero())
             }
             EntryDetails::Cache {
                 layer_index,
                 origin,
                 ..
             } => (layer_index, origin),
         };
         (entry.texture_id,
          layer_index as usize,
-         DeviceIntRect::new(origin, entry.size))
+         DeviceIntRect::new(origin, entry.size),
+         entry.uv_rect_handle)
     }
 
     pub fn mark_unused(&mut self, handle: &TextureCacheHandle) {
         if let Some(entry) = self.entries.get_opt_mut(handle) {
             // Set last accessed stamp invalid to ensure it gets cleaned up
             // next time we expire entries.
             entry.last_access = FrameStamp::INVALID;
             entry.eviction = Eviction::Auto;
@@ -975,24 +955,21 @@ impl TextureCache {
             self.doc_data.last_shared_cache_expiration = self.now;
         }
         self.doc_data.handles.shared.len() != old_len
     }
 
     // Free a cache entry from the standalone list or shared cache.
     fn free(&mut self, entry: CacheEntry) {
         match entry.details {
-            EntryDetails::Standalone { .. } => {
+            EntryDetails::Standalone => {
                 // This is a standalone texture allocation. Free it directly.
                 self.pending_updates.push_free(entry.texture_id);
             }
-            EntryDetails::Cache {
-                origin,
-                layer_index,
-            } => {
+            EntryDetails::Cache { origin, layer_index } => {
                 // Free the block in the given region.
                 let texture_array = self.shared_textures.select(entry.format, entry.filter);
                 let region = &mut texture_array.regions[layer_index];
 
                 if self.debug_flags.contains(
                     DebugFlags::TEXTURE_CACHE_DBG |
                     DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED) {
                     self.pending_updates.push_debug_clear(
@@ -1197,20 +1174,21 @@ impl TextureCache {
         //
         // This is managed with a database style upsert operation.
         match self.entries.upsert(handle, new_cache_entry) {
             UpsertResult::Updated(old_entry) => {
                 if new_kind != old_entry.details.kind() {
                     // Handle the rare case than an update moves an entry from
                     // shared to standalone or vice versa. This involves a linear
                     // search, but should be rare enough not to matter.
-                    let (from, to) = if new_kind == EntryKind::Standalone {
-                        (&mut self.doc_data.handles.shared, &mut self.doc_data.handles.standalone)
-                    } else {
-                        (&mut self.doc_data.handles.standalone, &mut self.doc_data.handles.shared)
+                    let (from, to) = match new_kind {
+                        EntryKind::Standalone =>
+                            (&mut self.doc_data.handles.shared, &mut self.doc_data.handles.standalone),
+                        EntryKind::Shared =>
+                            (&mut self.doc_data.handles.standalone, &mut self.doc_data.handles.shared),
                     };
                     let idx = from.iter().position(|h| h.weak() == *handle).unwrap();
                     to.push(from.remove(idx));
                 }
                 self.free(old_entry);
             }
             UpsertResult::Inserted(new_handle) => {
                 *handle = new_handle.weak();
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -452,16 +452,19 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadI
   }
 
   OptionalIPCServiceWorkerDescriptor ipcController = mozilla::void_t();
   const Maybe<ServiceWorkerDescriptor>& controller = aLoadInfo->GetController();
   if (controller.isSome()) {
     ipcController = controller.ref().ToIPC();
   }
 
+  nsAutoString cspNonce;
+  Unused << NS_WARN_IF(NS_FAILED(aLoadInfo->GetCspNonce(cspNonce)));
+
   *aOptionalLoadInfoArgs = LoadInfoArgs(
       loadingPrincipalInfo, triggeringPrincipalInfo, principalToInheritInfo,
       sandboxedLoadingPrincipalInfo, topLevelPrincipalInfo,
       topLevelStorageAreaPrincipalInfo, optionalResultPrincipalURI,
       aLoadInfo->GetSecurityFlags(), aLoadInfo->InternalContentPolicyType(),
       static_cast<uint32_t>(aLoadInfo->GetTainting()),
       aLoadInfo->GetUpgradeInsecureRequests(),
       aLoadInfo->GetBrowserUpgradeInsecureRequests(),
@@ -479,17 +482,17 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadI
       aLoadInfo->GetSendCSPViolationEvents(), aLoadInfo->GetOriginAttributes(),
       redirectChainIncludingInternalRedirects, redirectChain,
       ancestorPrincipals, aLoadInfo->AncestorOuterWindowIDs(), ipcClientInfo,
       ipcReservedClientInfo, ipcInitialClientInfo, ipcController,
       aLoadInfo->CorsUnsafeHeaders(), aLoadInfo->GetForcePreflight(),
       aLoadInfo->GetIsPreflight(), aLoadInfo->GetLoadTriggeredFromExternal(),
       aLoadInfo->GetServiceWorkerTaintingSynthesized(),
       aLoadInfo->GetDocumentHasUserInteracted(),
-      aLoadInfo->GetDocumentHasLoaded(),
+      aLoadInfo->GetDocumentHasLoaded(), cspNonce,
       aLoadInfo->GetIsFromProcessingFrameAttributes());
 
   return NS_OK;
 }
 
 nsresult LoadInfoArgsToLoadInfo(
     const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
     nsILoadInfo** outLoadInfo) {
@@ -635,17 +638,17 @@ nsresult LoadInfoArgsToLoadInfo(
       loadInfoArgs.isInThirdPartyContext(), loadInfoArgs.isDocshellReload(),
       loadInfoArgs.sendCSPViolationEvents(), loadInfoArgs.originAttributes(),
       redirectChainIncludingInternalRedirects, redirectChain,
       std::move(ancestorPrincipals), loadInfoArgs.ancestorOuterWindowIDs(),
       loadInfoArgs.corsUnsafeHeaders(), loadInfoArgs.forcePreflight(),
       loadInfoArgs.isPreflight(), loadInfoArgs.loadTriggeredFromExternal(),
       loadInfoArgs.serviceWorkerTaintingSynthesized(),
       loadInfoArgs.documentHasUserInteracted(),
-      loadInfoArgs.documentHasLoaded());
+      loadInfoArgs.documentHasLoaded(), loadInfoArgs.cspNonce());
 
   if (loadInfoArgs.isFromProcessingFrameAttributes()) {
     loadInfo->SetIsFromProcessingFrameAttributes();
   }
 
   loadInfo.forget(outLoadInfo);
   return NS_OK;
 }
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -307,16 +307,21 @@ class MessageChannel : HasResultCodes, M
     return mLink ? mLink->Unsound_NumQueuedMessages() : 0;
   }
 
   static bool IsPumpingMessages() { return sIsPumpingMessages; }
   static void SetIsPumpingMessages(bool aIsPumping) {
     sIsPumpingMessages = aIsPumping;
   }
 
+  /**
+   * Does this MessageChannel cross process boundaries?
+   */
+  bool IsCrossProcess() const { return mIsCrossProcess; }
+
 #ifdef OS_WIN
   struct MOZ_STACK_CLASS SyncStackFrame {
     SyncStackFrame(MessageChannel* channel, bool interrupt);
     ~SyncStackFrame();
 
     bool mInterrupt;
     bool mSpinNestedEvents;
     bool mListenerNotified;
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -641,27 +641,19 @@ def _deallocMethod(ptype, side):
 class _HybridDecl:
     """A hybrid decl stores both an IPDL type and all the C++ type
 info needed by later passes, along with a basic name for the decl."""
 
     def __init__(self, ipdltype, name):
         self.ipdltype = ipdltype
         self.name = name
 
-    def isCopyable(self):
-        return not _cxxTypeNeedsMove(self.ipdltype)
-
     def var(self):
         return ExprVar(self.name)
 
-    def mayMoveExpr(self):
-        if self.isCopyable():
-            return self.var()
-        return ExprMove(self.var())
-
     def bareType(self, side, fq=False):
         """Return this decl's unqualified C++ type."""
         return _cxxBareType(self.ipdltype, side, fq=fq)
 
     def refType(self, side):
         """Return this decl's C++ type as a 'reference' type, which is not
 necessarily a C++ reference."""
         return _cxxRefType(self.ipdltype, side)
@@ -1066,17 +1058,23 @@ class MessageDecl(ipdl.ast.MessageDecl):
         return cxxparams
 
     def makeCxxArgs(self, paramsems='in', retsems='out', retcallsems='out',
                     implicit=True):
         assert not retcallsems or retsems  # retcallsems => returnsems
         cxxargs = []
 
         if paramsems == 'move':
-            cxxargs.extend([p.mayMoveExpr() for p in self.params])
+            # We don't std::move() RefPtr<T> types because current Recv*()
+            # implementors take these parameters as T*, and
+            # std::move(RefPtr<T>) doesn't coerce to T*.
+            cxxargs.extend([
+                p.var() if p.ipdltype.isCxx() and p.ipdltype.isRefcounted() else ExprMove(p.var())
+                for p in self.params
+            ])
         elif paramsems == 'in':
             cxxargs.extend([p.var() for p in self.params])
         else:
             assert False
 
         for ret in self.returns:
             if retsems == 'in':
                 if retcallsems == 'in':
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -113,23 +113,19 @@ class nsXPCComponents_Interfaces final :
  public:
   nsXPCComponents_Interfaces();
 
  private:
   virtual ~nsXPCComponents_Interfaces();
 };
 
 NS_IMETHODIMP
-nsXPCComponents_Interfaces::GetInterfaces(uint32_t* aCount, nsIID*** aArray) {
-  *aCount = 2;
-  nsIID** array = static_cast<nsIID**>(moz_xmalloc(2 * sizeof(nsIID*)));
-  *aArray = array;
-
-  array[0] = NS_GET_IID(nsIXPCComponents_Interfaces).Clone();
-  array[1] = NS_GET_IID(nsIXPCScriptable).Clone();
+nsXPCComponents_Interfaces::GetInterfaces(nsTArray<nsIID>& aArray) {
+  aArray = nsTArray<nsIID>{NS_GET_IID(nsIXPCComponents_Interfaces),
+                           NS_GET_IID(nsIXPCScriptable)};
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Interfaces::GetScriptableHelper(nsIXPCScriptable** retval) {
   *retval = nullptr;
   return NS_OK;
 }
@@ -270,23 +266,19 @@ class nsXPCComponents_Classes final : pu
   nsXPCComponents_Classes();
 
  private:
   virtual ~nsXPCComponents_Classes();
 };
 
 /***************************************************************************/
 NS_IMETHODIMP
-nsXPCComponents_Classes::GetInterfaces(uint32_t* aCount, nsIID*** aArray) {
-  *aCount = 2;
-  nsIID** array = static_cast<nsIID**>(moz_xmalloc(2 * sizeof(nsIID*)));
-  *aArray = array;
-
-  array[0] = NS_GET_IID(nsIXPCComponents_Classes).Clone();
-  array[1] = NS_GET_IID(nsIXPCScriptable).Clone();
+nsXPCComponents_Classes::GetInterfaces(nsTArray<nsIID>& aArray) {
+  aArray = nsTArray<nsIID>{NS_GET_IID(nsIXPCComponents_Classes),
+                           NS_GET_IID(nsIXPCScriptable)};
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Classes::GetScriptableHelper(nsIXPCScriptable** retval) {
   *retval = nullptr;
   return NS_OK;
 }
@@ -427,23 +419,19 @@ class nsXPCComponents_Results final : pu
   nsXPCComponents_Results();
 
  private:
   virtual ~nsXPCComponents_Results();
 };
 
 /***************************************************************************/
 NS_IMETHODIMP
-nsXPCComponents_Results::GetInterfaces(uint32_t* aCount, nsIID*** aArray) {
-  *aCount = 2;
-  nsIID** array = static_cast<nsIID**>(moz_xmalloc(2 * sizeof(nsIID*)));
-  *aArray = array;
-
-  array[0] = NS_GET_IID(nsIXPCComponents_Results).Clone();
-  array[1] = NS_GET_IID(nsIXPCScriptable).Clone();
+nsXPCComponents_Results::GetInterfaces(nsTArray<nsIID>& aArray) {
+  aArray = nsTArray<nsIID>{NS_GET_IID(nsIXPCComponents_Results),
+                           NS_GET_IID(nsIXPCScriptable)};
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Results::GetScriptableHelper(nsIXPCScriptable** retval) {
   *retval = nullptr;
   return NS_OK;
 }
@@ -572,23 +560,19 @@ class nsXPCComponents_ID final : public 
   virtual ~nsXPCComponents_ID();
   static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
                                   JSContext* cx, HandleObject obj,
                                   const CallArgs& args, bool* _retval);
 };
 
 /***************************************************************************/
 NS_IMETHODIMP
-nsXPCComponents_ID::GetInterfaces(uint32_t* aCount, nsIID*** aArray) {
-  *aCount = 2;
-  nsIID** array = static_cast<nsIID**>(moz_xmalloc(2 * sizeof(nsIID*)));
-  *aArray = array;
-
-  array[0] = NS_GET_IID(nsIXPCComponents_ID).Clone();
-  array[1] = NS_GET_IID(nsIXPCScriptable).Clone();
+nsXPCComponents_ID::GetInterfaces(nsTArray<nsIID>& aArray) {
+  aArray = nsTArray<nsIID>{NS_GET_IID(nsIXPCComponents_ID),
+                           NS_GET_IID(nsIXPCScriptable)};
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_ID::GetScriptableHelper(nsIXPCScriptable** retval) {
   *retval = nullptr;
   return NS_OK;
 }
@@ -727,23 +711,19 @@ class nsXPCComponents_Exception final : 
   virtual ~nsXPCComponents_Exception();
   static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
                                   JSContext* cx, HandleObject obj,
                                   const CallArgs& args, bool* _retval);
 };
 
 /***************************************************************************/
 NS_IMETHODIMP
-nsXPCComponents_Exception::GetInterfaces(uint32_t* aCount, nsIID*** aArray) {
-  *aCount = 2;
-  nsIID** array = static_cast<nsIID**>(moz_xmalloc(2 * sizeof(nsIID*)));
-  *aArray = array;
-
-  array[0] = NS_GET_IID(nsIXPCComponents_Exception).Clone();
-  array[1] = NS_GET_IID(nsIXPCScriptable).Clone();
+nsXPCComponents_Exception::GetInterfaces(nsTArray<nsIID>& aArray) {
+  aArray = nsTArray<nsIID>{NS_GET_IID(nsIXPCComponents_Exception),
+                           NS_GET_IID(nsIXPCScriptable)};
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Exception::GetScriptableHelper(nsIXPCScriptable** retval) {
   *retval = nullptr;
   return NS_OK;
 }
@@ -1022,23 +1002,19 @@ class nsXPCComponents_Constructor final 
   static bool InnerConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
   static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
                                   JSContext* cx, HandleObject obj,
                                   const CallArgs& args, bool* _retval);
 };
 
 /***************************************************************************/
 NS_IMETHODIMP
-nsXPCComponents_Constructor::GetInterfaces(uint32_t* aCount, nsIID*** aArray) {
-  *aCount = 2;
-  nsIID** array = static_cast<nsIID**>(moz_xmalloc(2 * sizeof(nsIID*)));
-  *aArray = array;
-
-  array[0] = NS_GET_IID(nsIXPCComponents_Constructor).Clone();
-  array[1] = NS_GET_IID(nsIXPCScriptable).Clone();
+nsXPCComponents_Constructor::GetInterfaces(nsTArray<nsIID>& aArray) {
+  aArray = nsTArray<nsIID>{NS_GET_IID(nsIXPCComponents_Constructor),
+                           NS_GET_IID(nsIXPCScriptable)};
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Constructor::GetScriptableHelper(nsIXPCScriptable** retval) {
   *retval = nullptr;
   return NS_OK;
 }
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -170,26 +170,29 @@ bool XPCConvert::NativeData2JS(MutableHa
       d.set(*static_cast<const Value*>(s));
       return JS_WrapValue(cx, d);
     }
 
     case nsXPTType::T_VOID:
       XPC_LOG_ERROR(("XPCConvert::NativeData2JS : void* params not supported"));
       return false;
 
-    case nsXPTType::T_IID: {
+    case nsXPTType::T_NSIDPTR: {
       nsID* iid2 = *static_cast<nsID* const*>(s);
       if (!iid2) {
         d.setNull();
         return true;
       }
 
       return xpc::ID2JSValue(cx, *iid2, d);
     }
 
+    case nsXPTType::T_NSID:
+      return xpc::ID2JSValue(cx, *static_cast<const nsID*>(s), d);
+
     case nsXPTType::T_ASTRING: {
       const nsAString* p = static_cast<const nsAString*>(s);
       if (!p || p->IsVoid()) {
         d.setNull();
         return true;
       }
 
       nsStringBuffer* buf;
@@ -537,23 +540,30 @@ bool XPCConvert::JSData2Native(JSContext
     case nsXPTType::T_JSVAL:
       *((Value*)d) = s;
       break;
     case nsXPTType::T_VOID:
       XPC_LOG_ERROR(("XPCConvert::JSData2Native : void* params not supported"));
       NS_ERROR("void* params not supported");
       return false;
 
-    case nsXPTType::T_IID:
+    case nsXPTType::T_NSIDPTR:
       if (Maybe<nsID> id = xpc::JSValue2ID(cx, s)) {
         *((const nsID**)d) = id.ref().Clone();
         return true;
       }
       return false;
 
+    case nsXPTType::T_NSID:
+      if (Maybe<nsID> id = xpc::JSValue2ID(cx, s)) {
+        *((nsID*)d) = id.ref();
+        return true;
+      }
+      return false;
+
     case nsXPTType::T_ASTRING: {
       nsAString* ws = (nsAString*)d;
       if (s.isUndefined() || s.isNull()) {
         ws->SetIsVoid(true);
         return true;
       }
       size_t length = 0;
       JSString* str = ToString(cx, s);
@@ -1610,17 +1620,17 @@ void xpc::InnerCleanupValue(const nsXPTT
       ((nsAString*)aValue)->Truncate();
       break;
     case nsXPTType::T_UTF8STRING:
     case nsXPTType::T_CSTRING:
       ((nsACString*)aValue)->Truncate();
       break;
 
     // Pointer Types
-    case nsXPTType::T_IID:
+    case nsXPTType::T_NSIDPTR:
     case nsXPTType::T_CHAR_STR:
     case nsXPTType::T_WCHAR_STR:
     case nsXPTType::T_PSTRING_SIZE_IS:
     case nsXPTType::T_PWSTRING_SIZE_IS:
       free(*(void**)aValue);
       break;
 
     // Legacy Array Type
@@ -1642,16 +1652,21 @@ void xpc::InnerCleanupValue(const nsXPTT
 
       for (uint32_t i = 0; i < array->Length(); ++i) {
         DestructValue(elty, elty.ElementPtr(array->Elements(), i));
       }
       array->Clear();
       break;
     }
 
+    // Clear nsID& parameters to `0`
+    case nsXPTType::T_NSID:
+      ((nsID*)aValue)->Clear();
+      break;
+
     // Clear the JS::Value to `undefined`
     case nsXPTType::T_JSVAL:
       ((JS::Value*)aValue)->setUndefined();
       break;
 
     // Non-arithmetic types requiring no cleanup
     case nsXPTType::T_VOID:
       break;
--- a/js/xpconnect/src/XPCRuntimeService.cpp
+++ b/js/xpconnect/src/XPCRuntimeService.cpp
@@ -66,23 +66,19 @@ BackstagePass::NewEnumerate(nsIXPConnect
   JS::RootedObject obj(cx, objArg);
   *_retval = WebIDLGlobalNameHash::NewEnumerateSystemGlobal(cx, obj, properties,
                                                             enumerableOnly);
   return *_retval ? NS_OK : NS_ERROR_FAILURE;
 }
 
 /***************************************************************************/
 NS_IMETHODIMP
-BackstagePass::GetInterfaces(uint32_t* aCount, nsIID*** aArray) {
-  *aCount = 2;
-  nsIID** array = static_cast<nsIID**>(moz_xmalloc(2 * sizeof(nsIID*)));
-  *aArray = array;
-
-  array[0] = NS_GET_IID(nsIXPCScriptable).Clone();
-  array[1] = NS_GET_IID(nsIScriptObjectPrincipal).Clone();
+BackstagePass::GetInterfaces(nsTArray<nsIID>& aArray) {
+  aArray = nsTArray<nsIID>{NS_GET_IID(nsIXPCScriptable),
+                           NS_GET_IID(nsIScriptObjectPrincipal)};
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BackstagePass::GetScriptableHelper(nsIXPCScriptable** retval) {
   nsCOMPtr<nsIXPCScriptable> scriptable = this;
   scriptable.forget(retval);
   return NS_OK;
--- a/js/xpconnect/src/XPCVariant.cpp
+++ b/js/xpconnect/src/XPCVariant.cpp
@@ -226,17 +226,17 @@ bool XPCArrayHomogenizer::GetTypeForArra
       break;
     case tBool:
       *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::BOOL);
       break;
     case tStr:
       *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::PWSTRING);
       break;
     case tID:
-      *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::PNSIID);
+      *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::NSIDPTR);
       break;
     case tISup:
       *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::INTERFACE_IS_TYPE);
       *resultID = NS_GET_IID(nsISupports);
       break;
     case tNull:
       // FALL THROUGH
     case tVar:
@@ -461,17 +461,17 @@ bool XPCVariant::VariantDataToJS(nsIVari
       return XPCConvert::NativeData2JS(pJSVal, (const void*)&wc, {TD_WCHAR},
                                        &iid, 0, pErr);
     }
     case nsIDataType::VTYPE_ID: {
       if (NS_FAILED(variant->GetAsID(&iid))) {
         return false;
       }
       nsID* v = &iid;
-      return XPCConvert::NativeData2JS(pJSVal, (const void*)&v, {TD_PNSIID},
+      return XPCConvert::NativeData2JS(pJSVal, (const void*)&v, {TD_NSIDPTR},
                                        &iid, 0, pErr);
     }
     case nsIDataType::VTYPE_ASTRING: {
       nsAutoString astring;
       if (NS_FAILED(variant->GetAsAString(astring))) {
         return false;
       }
       return XPCConvert::NativeData2JS(pJSVal, &astring, {TD_ASTRING}, &iid, 0,
@@ -607,17 +607,17 @@ bool XPCVariant::VariantDataToJS(nsIVari
           break;
         case nsIDataType::VTYPE_CHAR:
           xptIndex = nsXPTType::Idx::CHAR;
           break;
         case nsIDataType::VTYPE_WCHAR:
           xptIndex = nsXPTType::Idx::WCHAR;
           break;
         case nsIDataType::VTYPE_ID:
-          xptIndex = nsXPTType::Idx::PNSIID;
+          xptIndex = nsXPTType::Idx::NSIDPTR;
           break;
         case nsIDataType::VTYPE_CHAR_STR:
           xptIndex = nsXPTType::Idx::PSTRING;
           break;
         case nsIDataType::VTYPE_WCHAR_STR:
           xptIndex = nsXPTType::Idx::PWSTRING;
           break;
         case nsIDataType::VTYPE_INTERFACE:
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -554,25 +554,27 @@ bool nsXPCWrappedJSClass::GetInterfaceTy
     if (!inner.GetInterface()) {
       return false;
     }
 
     *result = inner.GetInterface()->IID();
   } else if (inner.Tag() == nsXPTType::T_INTERFACE_IS) {
     // Get IID from a passed parameter.
     const nsXPTParamInfo& param = method->Param(inner.ArgNum());
-    if (param.Type().Tag() != nsXPTType::T_IID) {
+    if (param.Type().Tag() != nsXPTType::T_NSID &&
+        param.Type().Tag() != nsXPTType::T_NSIDPTR) {
       return false;
     }
 
     void* ptr = nativeParams[inner.ArgNum()].val.p;
 
-    // If the IID is passed indirectly (as an outparam), dereference by an
-    // extra level.
-    if (ptr && param.IsIndirect()) {
+    // If our IID is passed as a pointer outparameter, an extra level of
+    // dereferencing is required.
+    if (ptr && param.Type().Tag() == nsXPTType::T_NSIDPTR &&
+        param.IsIndirect()) {
       ptr = *(nsID**)ptr;
     }
 
     if (!ptr) {
       return false;
     }
 
     *result = *(nsID*)ptr;
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -1257,23 +1257,33 @@ bool CallMethodHelper::GetInterfaceTypeF
   const nsXPTType& inner = type.InnermostType();
   if (inner.Tag() == nsXPTType::T_INTERFACE) {
     if (!inner.GetInterface()) {
       return Throw(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO, mCallContext);
     }
 
     *result = inner.GetInterface()->IID();
   } else if (inner.Tag() == nsXPTType::T_INTERFACE_IS) {
-    nsID* id = (nsID*)GetDispatchParam(inner.ArgNum())->val.p;
-    if (!id) {
+    const nsXPTCVariant* param = GetDispatchParam(inner.ArgNum());
+    if (param->type.Tag() != nsXPTType::T_NSID &&
+        param->type.Tag() != nsXPTType::T_NSIDPTR) {
+      return Throw(NS_ERROR_UNEXPECTED, mCallContext);
+    }
+
+    const void* ptr = &param->val;
+    if (param->type.Tag() == nsXPTType::T_NSIDPTR) {
+      ptr = *static_cast<nsID* const*>(ptr);
+    }
+
+    if (!ptr) {
       return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO,
                            inner.ArgNum(), mCallContext);
     }
 
-    *result = *id;
+    *result = *static_cast<const nsID*>(ptr);
   }
   return true;
 }
 
 bool CallMethodHelper::GetOutParamSource(uint8_t paramIndex,
                                          MutableHandleValue srcp) const {
   const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
   bool isRetval = &paramInfo == mMethodInfo->GetRetval();
@@ -1494,19 +1504,19 @@ bool CallMethodHelper::ConvertIndependen
   if (!paramInfo.IsIn()) {
     return true;
   }
 
   // Some types usually don't support default values, but we want to handle
   // the default value if IsOptional is true.
   if (i >= mArgc) {
     MOZ_ASSERT(paramInfo.IsOptional(), "missing non-optional argument!");
-    if (type.Tag() == nsXPTType::T_IID) {
-      // NOTE: 'const nsIID&' is supported, so it must be allocated.
-      dp->val.p = new nsIID();
+    if (type.Tag() == nsXPTType::T_NSID) {
+      // Use a default value of the null ID for optional NSID objects.
+      dp->ext.nsid.Clear();
       return true;
     }
   }
 
   // We're definitely some variety of 'in' now, so there's something to
   // convert. The source value for conversion depends on whether we're
   // dealing with an 'in' or an 'inout' parameter. 'inout' was handled above,
   // so all that's left is 'in'.
--- a/js/xpconnect/src/XPCWrappedNativeInfo.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeInfo.cpp
@@ -508,96 +508,70 @@ already_AddRefed<XPCNativeSet> XPCNative
   }
 
   RefPtr<XPCNativeSet> set = map->Find(classInfo);
 
   if (set) {
     return set.forget();
   }
 
-  nsIID** iidArray = nullptr;
-  uint32_t iidCount = 0;
-
-  if (NS_FAILED(classInfo->GetInterfaces(&iidCount, &iidArray))) {
+  AutoTArray<nsIID, 4> iids;
+  if (NS_FAILED(classInfo->GetInterfaces(iids))) {
     // Note: I'm making it OK for this call to fail so that one can add
     // nsIClassInfo to classes implemented in script without requiring this
     // method to be implemented.
 
     // Make sure these are set correctly...
-    iidArray = nullptr;
-    iidCount = 0;
+    iids.Clear();
   }
 
-  MOZ_ASSERT((iidCount && iidArray) || !(iidCount || iidArray),
-             "GetInterfaces returned bad array");
-
-  // !!! from here on we only exit through the 'out' label !!!
-
-  if (iidCount) {
-    nsTArray<RefPtr<XPCNativeInterface>> interfaceArray(iidCount);
-    nsIID** currentIID = iidArray;
+  // Try to look up each IID's XPCNativeInterface object.
+  nsTArray<RefPtr<XPCNativeInterface>> interfaces(iids.Length());
+  for (auto& iid : iids) {
+    RefPtr<XPCNativeInterface> iface = XPCNativeInterface::GetNewOrUsed(&iid);
+    if (iface) {
+      interfaces.AppendElement(iface.forget());
+    }
+  }
 
-    for (uint32_t i = 0; i < iidCount; i++) {
-      nsIID* iid = *(currentIID++);
-      if (!iid) {
-        NS_ERROR("Null found in classinfo interface list");
-        continue;
-      }
-
-      RefPtr<XPCNativeInterface> iface = XPCNativeInterface::GetNewOrUsed(iid);
-
-      if (!iface) {
-        // XXX warn here
-        continue;
+  // Build a set from the interfaces specified here.
+  if (interfaces.Length() > 0) {
+    set = NewInstance(std::move(interfaces));
+    if (set) {
+      NativeSetMap* map2 = xpcrt->GetNativeSetMap();
+      if (!map2) {
+        return set.forget();
       }
 
-      interfaceArray.AppendElement(iface.forget());
-    }
-
-    if (interfaceArray.Length() > 0) {
-      set = NewInstance(std::move(interfaceArray));
-      if (set) {
-        NativeSetMap* map2 = xpcrt->GetNativeSetMap();
-        if (!map2) {
-          goto out;
-        }
-
-        XPCNativeSetKey key(set);
+      XPCNativeSetKey key(set);
+      XPCNativeSet* set2 = map2->Add(&key, set);
+      if (!set2) {
+        NS_ERROR("failed to add our set");
+        return nullptr;
+      }
 
-        XPCNativeSet* set2 = map2->Add(&key, set);
-        if (!set2) {
-          NS_ERROR("failed to add our set!");
-          set = nullptr;
-          goto out;
-        }
-        // It is okay to find an existing entry here because
-        // we did not look for one before we called Add().
-        if (set2 != set) {
-          set = set2;
-        }
+      // It is okay to find an existing entry here because
+      // we did not look for one before we called Add().
+      if (set2 != set) {
+        set = set2;
       }
-    } else
-      set = GetNewOrUsed(&NS_GET_IID(nsISupports));
-  } else
+    }
+  } else {
     set = GetNewOrUsed(&NS_GET_IID(nsISupports));
+  }
 
   if (set) {
 #ifdef DEBUG
     XPCNativeSet* set2 =
 #endif
         map->Add(classInfo, set);
     MOZ_ASSERT(set2, "failed to add our set!");
     MOZ_ASSERT(set2 == set, "hashtables inconsistent!");
   }
 
-out:
-  if (iidArray) {
-    NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(iidCount, iidArray);
-  }
-
   return set.forget();
 }
 
 // static
 void XPCNativeSet::ClearCacheEntryForClassInfo(nsIClassInfo* classInfo) {
   XPCJSRuntime* xpcrt = nsXPConnect::GetRuntimeInstance();
   ClassInfo2NativeSetMap* map = xpcrt->GetClassInfo2NativeSetMap();
   if (map) {
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2891,17 +2891,17 @@ nsresult HasInstance(JSContext* cx, JS::
 // Returns the principal associated with |obj|'s realm. The object must not be a
 // cross-compartment wrapper.
 nsIPrincipal* GetObjectPrincipal(JSObject* obj);
 
 // Attempt to clean up the passed in value pointer. The pointer `value` must be
 // a pointer to a value described by the type `nsXPTType`.
 //
 // This method expects a value of the following types:
-//   TD_PNSIID
+//   TD_NSIDPTR
 //     value : nsID* (free)
 //   TD_ASTRING, TD_CSTRING, TD_UTF8STRING
 //     value : ns[C]String* (truncate)
 //   TD_PSTRING, TD_PWSTRING, TD_PSTRING_SIZE_IS, TD_PWSTRING_SIZE_IS
 //     value : char[16_t]** (free)
 //   TD_INTERFACE_TYPE, TD_INTERFACE_IS_TYPE
 //     value : nsISupports** (release)
 //   TD_LEGACY_ARRAY (NOTE: aArrayLen should be passed)
--- a/js/xpconnect/tests/unit/component-blob.js
+++ b/js/xpconnect/tests/unit/component-blob.js
@@ -54,21 +54,17 @@ BlobComponent.prototype =
   // nsIClassInfo + information for XPCOM registration code in XPCOMUtils.jsm
   classDescription: "Blob in components scope code",
   classID: Components.ID("{06215993-a3c2-41e3-bdfd-0a3a2cc0b65c}"),
   contractID: "@mozilla.org/tests/component-blob;1",
 
   // nsIClassInfo
   flags: 0,
 
-  getInterfaces: function getInterfaces(aCount) {
-    var interfaces = [Ci.nsIClassInfo];
-    aCount.value = interfaces.length;
-    return interfaces;
-  },
+  interfaces: [Ci.nsIClassInfo],
 
   getScriptableHelper: function getScriptableHelper() {
     return null;
   },
 
   // nsISupports
   QueryInterface: ChromeUtils.generateQI([Ci.nsIClassInfo])
 };
--- a/js/xpconnect/tests/unit/component-file.js
+++ b/js/xpconnect/tests/unit/component-file.js
@@ -85,21 +85,17 @@ FileComponent.prototype =
   // nsIClassInfo + information for XPCOM registration code in XPCOMUtils.jsm
   classDescription: "File in components scope code",
   classID: Components.ID("{da332370-91d4-464f-a730-018e14769cab}"),
   contractID: "@mozilla.org/tests/component-file;1",
 
   // nsIClassInfo
   flags: 0,
 
-  getInterfaces: function getInterfaces(aCount) {
-    var interfaces = [Ci.nsIClassInfo];
-    aCount.value = interfaces.length;
-    return interfaces;
-  },
+  interfaces: [Ci.nsIClassInfo],
 
   getScriptableHelper: function getScriptableHelper() {
     return null;
   },
 
   // nsISupports
   QueryInterface: ChromeUtils.generateQI([Ci.nsIClassInfo])
 };
--- a/js/xpconnect/tests/unit/component_import.js
+++ b/js/xpconnect/tests/unit/component_import.js
@@ -12,27 +12,26 @@ FooComponent.prototype =
   // nsIClassInfo + information for XPCOM registration code in XPCOMUtils.jsm
   classDescription:  "Foo Component",
   classID:           Components.ID("{6b933fe6-6eba-4622-ac86-e4f654f1b474}"),
   contractID:       "@mozilla.org/tests/module-importer;1",
 
   // nsIClassInfo
   flags: 0,
 
-  getInterfaces: function getInterfaces(aCount) {
+  get interfaces() {
     var interfaces = [Ci.nsIClassInfo];
-    aCount.value = interfaces.length;
 
     // Guerilla test for line numbers hiding in this method
     var threw = true;
     try {
       thereIsNoSuchIdentifier;
       threw = false;
     } catch (ex) {
-      Assert.ok(ex.lineNumber == 27);
+      Assert.ok(ex.lineNumber == 26);
     }
     Assert.ok(threw);
 
     return interfaces;
   },
 
   getScriptableHelper: function getScriptableHelper() {
     return null;
@@ -55,21 +54,17 @@ BarComponent.prototype =
   // nsIClassInfo + information for XPCOM registration code in XPCOMUtils.jsm
   classDescription: "Module importer test 2",
   classID: Components.ID("{708a896a-b48d-4bff-906e-fc2fd134b296}"),
   contractID: "@mozilla.org/tests/module-importer;2",
 
   // nsIClassInfo
   flags: 0,
 
-  getInterfaces: function getInterfaces(aCount) {
-    var interfaces = [Ci.nsIClassInfo];
-    aCount.value = interfaces.length;
-    return interfaces;
-  },
+  interfaces: [Ci.nsIClassInfo],
 
   getScriptableHelper: function getScriptableHelper() {
     return null;
   },
 
   // nsISupports
   QueryInterface: ChromeUtils.generateQI([Ci.nsIClassInfo])
 };
--- a/js/xpconnect/tests/unit/test_import.js
+++ b/js/xpconnect/tests/unit/test_import.js
@@ -70,20 +70,20 @@ function run_test() {
   Assert.ok(foo.contractID == contractID + "1");
   // XXX the following check succeeds only if the test component wasn't
   //     already registered. Need to figure out a way to force registration
   //     (to manually force it, delete compreg.dat before running the test)
   // do_check_true(foo.wrappedJSObject.postRegisterCalled);
 
   // Call getInterfaces to test line numbers in JS components.  But as long as
   // we're doing that, why not test what it returns too?
-  // Kind of odd that this is not returning an array containing the
-  // number... Or for that matter not returning an array containing an object?
-  var interfaces = foo.getInterfaces({});
-  Assert.equal(interfaces, Ci.nsIClassInfo.number);
+  var interfaces = foo.interfaces;
+  Assert.ok(Array.isArray(interfaces));
+  Assert.equal(interfaces.length, 1);
+  Assert.ok(interfaces[0].equals(Ci.nsIClassInfo))
 
   // try to create another component which doesn't directly implement QI
   Assert.ok((contractID + "2") in Cc);
   var bar = Cc[contractID + "2"]
               .createInstance(Ci.nsIClassInfo);
   Assert.ok(Boolean(bar));
   Assert.ok(bar.contractID == contractID + "2");
 }
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -375,19 +375,24 @@ bool nsHTMLScrollFrame::TryLayout(Scroll
       std::max(aKidMetrics->Height(), vScrollbarMinHeight);
   aState->mInsideBorderSize =
       ComputeInsideBorderSize(aState, desiredInsideBorderSize);
 
   nsSize layoutSize = mHelper.mIsUsingMinimumScaleSize
                           ? mHelper.mMinimumScaleSize
                           : aState->mInsideBorderSize;
 
-  nsSize scrollPortSize =
+  const nsSize scrollPortSize =
       nsSize(std::max(0, layoutSize.width - vScrollbarDesiredWidth),
              std::max(0, layoutSize.height - hScrollbarDesiredHeight));
+  if (mHelper.mIsUsingMinimumScaleSize) {
+    mHelper.mICBSize =
+        nsSize(std::max(0, aState->mInsideBorderSize.width - vScrollbarDesiredWidth),
+               std::max(0, aState->mInsideBorderSize.height - hScrollbarDesiredHeight));
+  }
 
   nsSize visualViewportSize = scrollPortSize;
   nsIPresShell* presShell = PresShell();
   if (mHelper.mIsRoot && presShell->IsVisualViewportSizeSet()) {
     nsSize compositionSize =
         nsLayoutUtils::CalculateCompositionSizeForFrame(this, false);
     float resolution = presShell->GetResolution();
     compositionSize.width /= resolution;
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -190,16 +190,22 @@ class ScrollFrameHelper : public nsIRefl
                             nscoord* aRangeLength);
 
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    * Update scrollbar curpos attributes to reflect current scroll position
    */
   void UpdateScrollbarPosition();
 
+  nsSize GetLayoutSize() const {
+    if (mIsUsingMinimumScaleSize) {
+      return mICBSize;
+    }
+    return mScrollPort.Size();
+  }
   nsRect GetScrollPortRect() const { return mScrollPort; }
   nsPoint GetScrollPosition() const {
     return mScrollPort.TopLeft() - mScrolledFrame->GetPosition();
   }
   /**
    * For LTR frames, the logical scroll position is the offset of the top left
    * corner of the frame from the top left corner of the scroll port (same as
    * GetScrollPosition).
@@ -558,16 +564,20 @@ class ScrollFrameHelper : public nsIRefl
   nsAtom* mLastSmoothScrollOrigin;
   Maybe<nsPoint> mApzSmoothScrollDestination;
   uint32_t mScrollGeneration;
   // NOTE: On mobile this value might be factoring into overflow:hidden region
   // in the case of the top level document.
   nsRect mScrollPort;
   nsSize mMinimumScaleSize;
 
+  // Stores the ICB size for the root document if this frame is using the
+  // minimum scale size for |mScrollPort|.
+  nsSize mICBSize;
+
   // Where we're currently scrolling to, if we're scrolling asynchronously.
   // If we're not in the middle of an asynchronous scroll then this is
   // just the current scroll position. ScrollBy will choose its
   // destination based on this value.
   nsPoint mDestination;
 
   // A goal position to try to scroll to as content loads. As long as mLastPos
   // matches the current logical scroll position, we try to scroll to
@@ -883,16 +893,19 @@ class nsHTMLScrollFrame : public nsConta
     return GetDesiredScrollbarSizes(&bls);
   }
   virtual nscoord GetNondisappearingScrollbarWidth(
       nsPresContext* aPresContext, gfxContext* aRC,
       mozilla::WritingMode aWM) override {
     nsBoxLayoutState bls(aPresContext, aRC, 0);
     return mHelper.GetNondisappearingScrollbarWidth(&bls, aWM);
   }
+  virtual nsSize GetLayoutSize() const override {
+    return mHelper.GetLayoutSize();
+  }
   virtual nsRect GetScrolledRect() const override {
     return mHelper.GetScrolledRect();
   }
   virtual nsRect GetScrollPortRect() const override {
     return mHelper.GetScrollPortRect();
   }
   virtual nsPoint GetScrollPosition() const override {
     return mHelper.GetScrollPosition();
@@ -1357,16 +1370,19 @@ class nsXULScrollFrame final : public ns
     return GetDesiredScrollbarSizes(&bls);
   }
   virtual nscoord GetNondisappearingScrollbarWidth(
       nsPresContext* aPresContext, gfxContext* aRC,
       mozilla::WritingMode aWM) override {
     nsBoxLayoutState bls(aPresContext, aRC, 0);
     return mHelper.GetNondisappearingScrollbarWidth(&bls, aWM);
   }
+  virtual nsSize GetLayoutSize() const override {
+    return mHelper.GetLayoutSize();
+  }
   virtual nsRect GetScrolledRect() const override {
     return mHelper.GetScrolledRect();
   }
   virtual nsRect GetScrollPortRect() const override {
     return mHelper.GetScrollPortRect();
   }
   virtual nsPoint GetScrollPosition() const override {
     return mHelper.GetScrollPosition();
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -106,16 +106,25 @@ class nsIScrollableFrame : public nsIScr
                                             gfxContext* aRC) = 0;
   /**
    * Return the width for non-disappearing scrollbars.
    */
   virtual nscoord GetNondisappearingScrollbarWidth(
       nsPresContext* aPresContext, gfxContext* aRC,
       mozilla::WritingMode aWM) = 0;
   /**
+   * Get the layout size of this frame.
+   * Note that this is a value which is not expanded by the minimum scale size.
+   * For scroll frames other than the root content document's scroll frame, this
+   * value will be the same as GetScrollPortRect().Size().
+   *
+   * This value is used for Element.clientWidth and clientHeight.
+   */
+  virtual nsSize GetLayoutSize() const = 0;
+  /**
    * GetScrolledRect is designed to encapsulate deciding which
    * directions of overflow should be reachable by scrolling and which
    * should not.  Callers should NOT depend on it having any particular
    * behavior (although nsXULScrollFrame currently does).
    *
    * This should only be called when the scrolled frame has been
    * reflowed with the scroll port size given in mScrollPort.
    *
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -853,16 +853,26 @@ nsresult Loader::CheckContentPolicy(nsIP
   nsContentPolicyType contentPolicyType =
       aIsPreload ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD
                  : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET;
 
   nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo(
       aLoadingPrincipal, aTriggeringPrincipal, aRequestingNode,
       nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, contentPolicyType);
 
+  // snapshot the nonce at load start time for performing CSP checks
+  if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET) {
+    nsCOMPtr<Element> element = do_QueryInterface(aRequestingNode);
+    if (element && element->IsHTMLElement()) {
+      nsAutoString cspNonce;
+      element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce);
+      secCheckLoadInfo->SetCspNonce(cspNonce);
+    }
+  }
+
   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
   nsresult rv = NS_CheckContentLoadPolicy(
       aTargetURI, secCheckLoadInfo, NS_LITERAL_CSTRING("text/css"), &shouldLoad,
       nsContentUtils::GetContentPolicy());
   if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
     return NS_ERROR_CONTENT_BLOCKED;
   }
   return NS_OK;
@@ -1305,16 +1315,28 @@ nsresult Loader::LoadSheet(SheetLoadData
       }
     }
     if (NS_FAILED(rv)) {
       LOG_ERROR(("  Failed to create channel"));
       SheetComplete(aLoadData, rv);
       return rv;
     }
 
+    // snapshot the nonce at load start time for performing CSP checks
+    if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET) {
+      nsCOMPtr<Element> element =
+          do_QueryInterface(aLoadData->mRequestingNode);
+      if (element && element->IsHTMLElement()) {
+        nsAutoString cspNonce;
+        element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce);
+        nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
+        loadInfo->SetCspNonce(cspNonce);
+      }
+    }
+
     nsCOMPtr<nsIInputStream> stream;
     rv = channel->Open(getter_AddRefs(stream));
 
     if (NS_FAILED(rv)) {
       LOG_ERROR(("  Failed to open URI synchronously"));
       SheetComplete(aLoadData, rv);
       return rv;
     }
@@ -1433,16 +1455,27 @@ nsresult Loader::LoadSheet(SheetLoadData
 #ifdef DEBUG
     mSyncCallback = false;
 #endif
     LOG_ERROR(("  Failed to create channel"));
     SheetComplete(aLoadData, rv);
     return rv;
   }
 
+  // snapshot the nonce at load start time for performing CSP checks
+  if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET) {
+    nsCOMPtr<Element> element = do_QueryInterface(aLoadData->mRequestingNode);
+    if (element && element->IsHTMLElement()) {
+      nsAutoString cspNonce;
+      element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce);
+      nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
+      loadInfo->SetCspNonce(cspNonce);
+    }
+  }
+
   if (!aLoadData->ShouldDefer()) {
     nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
     if (cos) {
       cos->AddClassFlags(nsIClassOfService::Leader);
     }
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
--- a/modules/libjar/nsJARURI.cpp
+++ b/modules/libjar/nsJARURI.cpp
@@ -131,19 +131,18 @@ nsJARURI::Write(nsIObjectOutputStream *a
   rv = aOutputStream->WriteStringZ(mCharsetHint.get());
   return rv;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIClassInfo methods:
 
 NS_IMETHODIMP
-nsJARURI::GetInterfaces(uint32_t *count, nsIID ***array) {
-  *count = 0;
-  *array = nullptr;
+nsJARURI::GetInterfaces(nsTArray<nsIID> &array) {
+  array.Clear();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARURI::GetScriptableHelper(nsIXPCScriptable **_retval) {
   *_retval = nullptr;
   return NS_OK;
 }
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -473,16 +473,17 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
       mForcePreflight(rhs.mForcePreflight),
       mIsPreflight(rhs.mIsPreflight),
       mLoadTriggeredFromExternal(rhs.mLoadTriggeredFromExternal),
       // mServiceWorkerTaintingSynthesized must be handled specially during
       // redirect
       mServiceWorkerTaintingSynthesized(false),
       mDocumentHasUserInteracted(rhs.mDocumentHasUserInteracted),
       mDocumentHasLoaded(rhs.mDocumentHasLoaded),
+      mCspNonce(rhs.mCspNonce),
       mIsFromProcessingFrameAttributes(rhs.mIsFromProcessingFrameAttributes) {}
 
 LoadInfo::LoadInfo(
     nsIPrincipal* aLoadingPrincipal, nsIPrincipal* aTriggeringPrincipal,
     nsIPrincipal* aPrincipalToInherit, nsIPrincipal* aSandboxedLoadingPrincipal,
     nsIPrincipal* aTopLevelPrincipal,
     nsIPrincipal* aTopLevelStorageAreaPrincipal, nsIURI* aResultPrincipalURI,
     const Maybe<ClientInfo>& aClientInfo,
@@ -505,17 +506,17 @@ LoadInfo::LoadInfo(
     const OriginAttributes& aOriginAttributes,
     RedirectHistoryArray& aRedirectChainIncludingInternalRedirects,
     RedirectHistoryArray& aRedirectChain,
     nsTArray<nsCOMPtr<nsIPrincipal>>&& aAncestorPrincipals,
     const nsTArray<uint64_t>& aAncestorOuterWindowIDs,
     const nsTArray<nsCString>& aCorsUnsafeHeaders, bool aForcePreflight,
     bool aIsPreflight, bool aLoadTriggeredFromExternal,
     bool aServiceWorkerTaintingSynthesized, bool aDocumentHasUserInteracted,
-    bool aDocumentHasLoaded)
+    bool aDocumentHasLoaded, const nsAString& aCspNonce)
     : mLoadingPrincipal(aLoadingPrincipal),
       mTriggeringPrincipal(aTriggeringPrincipal),
       mPrincipalToInherit(aPrincipalToInherit),
       mTopLevelPrincipal(aTopLevelPrincipal),
       mTopLevelStorageAreaPrincipal(aTopLevelStorageAreaPrincipal),
       mResultPrincipalURI(aResultPrincipalURI),
       mClientInfo(aClientInfo),
       mReservedClientInfo(aReservedClientInfo),
@@ -551,16 +552,17 @@ LoadInfo::LoadInfo(
       mAncestorOuterWindowIDs(aAncestorOuterWindowIDs),
       mCorsUnsafeHeaders(aCorsUnsafeHeaders),
       mForcePreflight(aForcePreflight),
       mIsPreflight(aIsPreflight),
       mLoadTriggeredFromExternal(aLoadTriggeredFromExternal),
       mServiceWorkerTaintingSynthesized(aServiceWorkerTaintingSynthesized),
       mDocumentHasUserInteracted(aDocumentHasUserInteracted),
       mDocumentHasLoaded(aDocumentHasLoaded),
+      mCspNonce(aCspNonce),
       mIsFromProcessingFrameAttributes(false) {
   // Only top level TYPE_DOCUMENT loads can have a null loadingPrincipal
   MOZ_ASSERT(mLoadingPrincipal ||
              aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT);
   MOZ_ASSERT(mTriggeringPrincipal);
 
   mRedirectChainIncludingInternalRedirects.SwapElements(
       aRedirectChainIncludingInternalRedirects);
@@ -1250,16 +1252,30 @@ LoadInfo::GetDocumentHasLoaded(bool* aDo
 
 NS_IMETHODIMP
 LoadInfo::SetDocumentHasLoaded(bool aDocumentHasLoaded) {
   mDocumentHasLoaded = aDocumentHasLoaded;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+LoadInfo::GetCspNonce(nsAString& aCspNonce) {
+  aCspNonce = mCspNonce;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadInfo::SetCspNonce(const nsAString& aCspNonce) {
+  MOZ_ASSERT(!mInitialSecurityCheckDone,
+             "setting the nonce is only allowed before any sec checks");
+  mCspNonce = aCspNonce;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 LoadInfo::GetIsTopLevelLoad(bool* aResult) {
   *aResult = mFrameOuterWindowID ? mFrameOuterWindowID == mOuterWindowID
                                  : mParentOuterWindowID == mOuterWindowID;
   return NS_OK;
 }
 
 void LoadInfo::SetIsFromProcessingFrameAttributes() {
   mIsFromProcessingFrameAttributes = true;
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -7,16 +7,17 @@
 #ifndef mozilla_LoadInfo_h
 #define mozilla_LoadInfo_h
 
 #include "nsIContentPolicy.h"
 #include "nsILoadInfo.h"
 #include "nsIPrincipal.h"
 #include "nsIWeakReferenceUtils.h"  // for nsWeakPtr
 #include "nsIURI.h"
+#include "nsString.h"
 #include "nsTArray.h"
 
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/dom/ClientInfo.h"
 #include "mozilla/dom/ServiceWorkerDescriptor.h"
 
 class nsINode;
 class nsPIDOMWindowOuter;
@@ -116,17 +117,18 @@ class LoadInfo final : public nsILoadInf
            const OriginAttributes& aOriginAttributes,
            RedirectHistoryArray& aRedirectChainIncludingInternalRedirects,
            RedirectHistoryArray& aRedirectChain,
            nsTArray<nsCOMPtr<nsIPrincipal>>&& aAncestorPrincipals,
            const nsTArray<uint64_t>& aAncestorOuterWindowIDs,
            const nsTArray<nsCString>& aUnsafeHeaders, bool aForcePreflight,
            bool aIsPreflight, bool aLoadTriggeredFromExternal,
            bool aServiceWorkerTaintingSynthesized,
-           bool aDocumentHasUserInteracted, bool aDocumentHasLoaded);
+           bool aDocumentHasUserInteracted, bool aDocumentHasLoaded,
+           const nsAString& aCspNonce);
   LoadInfo(const LoadInfo& rhs);
 
   NS_IMETHOD GetRedirects(JSContext* aCx,
                           JS::MutableHandle<JS::Value> aRedirects,
                           const RedirectHistoryArray& aArra);
 
   friend nsresult mozilla::ipc::LoadInfoArgsToLoadInfo(
       const mozilla::net::OptionalLoadInfoArgs& aLoadInfoArgs,
@@ -193,16 +195,17 @@ class LoadInfo final : public nsILoadInf
   nsTArray<uint64_t> mAncestorOuterWindowIDs;
   nsTArray<nsCString> mCorsUnsafeHeaders;
   bool mForcePreflight;
   bool mIsPreflight;
   bool mLoadTriggeredFromExternal;
   bool mServiceWorkerTaintingSynthesized;
   bool mDocumentHasUserInteracted;
   bool mDocumentHasLoaded;
+  nsString mCspNonce;
 
   // Is true if this load was triggered by processing the attributes of the
   // browsing context container.
   // See nsILoadInfo.isFromProcessingFrameAttributes
   bool mIsFromProcessingFrameAttributes;
 };
 
 }  // namespace net
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -1043,16 +1043,24 @@ interface nsILoadInfo : nsISupports
 
   /**
     * This attribute represents whether the document to which this
     * load belongs had finished loading when the load was initiated.
     */
   [infallible] attribute boolean documentHasLoaded;
 
   /**
+   * A snapshot of the nonce at load start time which is used for CSP
+   * checks and only set for:
+   *  * TYPE_SCRIPT and
+   *  * TYPE_STYLESHEET
+   */
+  attribute AString cspNonce;
+
+  /**
     * The object in charged to receive CSP violation events. It can be null.
     * This attribute will be merged into the CSP object eventually.
     * See bug 1500908.
     */
   attribute nsICSPEventListener cspEventListener;
 
   /**
    * This attribute will be true if this is a load triggered by
--- a/netwerk/base/nsSimpleURI.cpp
+++ b/netwerk/base/nsSimpleURI.cpp
@@ -602,19 +602,18 @@ nsSimpleURI::GetAsciiHost(nsACString &re
   return NS_OK;
 }
 
 //----------------------------------------------------------------------------
 // nsSimpleURI::nsIClassInfo
 //----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-nsSimpleURI::GetInterfaces(uint32_t *count, nsIID ***array) {
-  *count = 0;
-  *array = nullptr;
+nsSimpleURI::GetInterfaces(nsTArray<nsIID> &array) {
+  array.Clear();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSimpleURI::GetScriptableHelper(nsIXPCScriptable **_retval) {
   *_retval = nullptr;
   return NS_OK;
 }
--- a/netwerk/base/nsSocketTransport2.cpp
+++ b/netwerk/base/nsSocketTransport2.cpp
@@ -2939,18 +2939,18 @@ nsSocketTransport::GetInterface(const ns
   if (iid.Equals(NS_GET_IID(nsIDNSRecord))) {
     return mDNSRecord ? mDNSRecord->QueryInterface(iid, result)
                       : NS_ERROR_NO_INTERFACE;
   }
   return this->QueryInterface(iid, result);
 }
 
 NS_IMETHODIMP
-nsSocketTransport::GetInterfaces(uint32_t *count, nsIID ***array) {
-  return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport)(count, array);
+nsSocketTransport::GetInterfaces(nsTArray<nsIID>& array) {
+  return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport)(array);
 }
 
 NS_IMETHODIMP
 nsSocketTransport::GetScriptableHelper(nsIXPCScriptable **_retval) {
   *_retval = nullptr;
   return NS_OK;
 }
 
--- a/netwerk/base/nsStandardURL.cpp
+++ b/netwerk/base/nsStandardURL.cpp
@@ -3442,19 +3442,18 @@ bool nsStandardURL::Deserialize(const UR
   return true;
 }
 
 //----------------------------------------------------------------------------
 // nsStandardURL::nsIClassInfo
 //----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-nsStandardURL::GetInterfaces(uint32_t *count, nsIID ***array) {
-  *count = 0;
-  *array = nullptr;
+nsStandardURL::GetInterfaces(nsTArray<nsIID> &array) {
+  array.Clear();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStandardURL::GetScriptableHelper(nsIXPCScriptable **_retval) {
   *_retval = nullptr;
   return NS_OK;
 }
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -104,16 +104,17 @@ struct LoadInfoArgs
 
   nsCString[]                 corsUnsafeHeaders;
   bool                        forcePreflight;
   bool                        isPreflight;
   bool                        loadTriggeredFromExternal;
   bool                        serviceWorkerTaintingSynthesized;
   bool                        documentHasUserInteracted;
   bool                        documentHasLoaded;
+  nsString                    cspNonce;
   bool                        isFromProcessingFrameAttributes;
 };
 
 /**
  * Not every channel necessarily has a loadInfo attached.
  */
 union OptionalLoadInfoArgs
 {
--- a/security/manager/ssl/TransportSecurityInfo.cpp
+++ b/security/manager/ssl/TransportSecurityInfo.cpp
@@ -511,19 +511,18 @@ TransportSecurityInfo::Read(nsIObjectInp
     return rv;
   }
   mFailedCertChain = do_QueryInterface(failedCertChainSupports);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TransportSecurityInfo::GetInterfaces(uint32_t* count, nsIID*** array) {
-  *count = 0;
-  *array = nullptr;
+TransportSecurityInfo::GetInterfaces(nsTArray<nsIID>& array) {
+  array.Clear();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TransportSecurityInfo::GetScriptableHelper(nsIXPCScriptable** _retval) {
   *_retval = nullptr;
   return NS_OK;
 }
--- a/security/manager/ssl/nsNSSCertificate.cpp
+++ b/security/manager/ssl/nsNSSCertificate.cpp
@@ -1222,19 +1222,18 @@ nsNSSCertificate::Read(nsIObjectInputStr
   if (!InitFromDER(const_cast<char*>(str.get()), len)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsNSSCertificate::GetInterfaces(uint32_t* count, nsIID*** array) {
-  *count = 0;
-  *array = nullptr;
+nsNSSCertificate::GetInterfaces(nsTArray<nsIID>& array) {
+  array.Clear();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNSSCertificate::GetScriptableHelper(nsIXPCScriptable** _retval) {
   *_retval = nullptr;
   return NS_OK;
 }
--- a/storage/mozStorageAsyncStatement.cpp
+++ b/storage/mozStorageAsyncStatement.cpp
@@ -40,18 +40,18 @@ NS_IMPL_CI_INTERFACE_GETTER(AsyncStateme
 
 class AsyncStatementClassInfo : public nsIClassInfo {
  public:
   constexpr AsyncStatementClassInfo() {}
 
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_IMETHOD
-  GetInterfaces(uint32_t *_count, nsIID ***_array) override {
-    return NS_CI_INTERFACE_GETTER_NAME(AsyncStatement)(_count, _array);
+  GetInterfaces(nsTArray<nsIID> &_array) override {
+    return NS_CI_INTERFACE_GETTER_NAME(AsyncStatement)(_array);
   }
 
   NS_IMETHOD
   GetScriptableHelper(nsIXPCScriptable **_helper) override {
     static AsyncStatementJSHelper sJSHelper;
     *_helper = &sJSHelper;
     return NS_OK;
   }
--- a/storage/mozStorageStatement.cpp
+++ b/storage/mozStorageStatement.cpp
@@ -42,18 +42,18 @@ NS_IMPL_CI_INTERFACE_GETTER(Statement, m
 
 class StatementClassInfo : public nsIClassInfo {
  public:
   constexpr StatementClassInfo() {}
 
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_IMETHOD
-  GetInterfaces(uint32_t *_count, nsIID ***_array) override {
-    return NS_CI_INTERFACE_GETTER_NAME(Statement)(_count, _array);
+  GetInterfaces(nsTArray<nsIID> &_array) override {
+    return NS_CI_INTERFACE_GETTER_NAME(Statement)(_array);
   }
 
   NS_IMETHOD
   GetScriptableHelper(nsIXPCScriptable **_helper) override {
     static StatementJSHelper sJSHelper;
     *_helper = &sJSHelper;
     return NS_OK;
   }
--- a/taskcluster/ci/test/mochitest.yml
+++ b/taskcluster/ci/test/mochitest.yml
@@ -99,16 +99,17 @@ mochitest-browser-chrome:
             linux.*/debug: 16
             linux64-asan/opt: 16
             macosx64/debug: 12
             default: 7
     max-run-time:
         by-test-platform:
             linux64-.*cov/opt: 7200
             windows10-64-ccov/debug: 7200
+            windows10-aarch64/*: 7200
             macosx64-ccov/debug: 10800
             linux.*/debug: 5400
             default: 3600
     mozharness:
         mochitest-flavor: browser
         chunked: true
     # Bug 1281241: migrating to m3.large instances
     instance-size: default
--- a/taskcluster/ci/test/talos.yml
+++ b/taskcluster/ci/test/talos.yml
@@ -286,20 +286,22 @@ talos-xperf:
     description: "Talos xperf"
     try-name: xperf
     treeherder-symbol: T(x)
     virtualization: virtual
     os-groups: ['Administrators']
     run-as-administrator:
         by-test-platform:
             windows7-32.*: false
+            windows10-aarch64.*: false
             windows10-64.*: true
     run-on-projects:
         by-test-platform:
             windows7-32(-pgo)?/.*: ['mozilla-beta', 'trunk', 'try']
+            windows10-aarch64/opt: []
             windows10-64.*/opt: ['mozilla-beta', 'trunk', 'try']
             default: []
     tier:
         by-test-platform:
             windows7-32.*: default
             windows10-64(-pgo)?/.*: default
             windows10-64-ccov/debug: 3
             default: 3  # this should be disabled but might run via try syntax anyway, so explicitly downgrade to tier-3
--- a/taskcluster/ci/test/test-platforms.yml
+++ b/taskcluster/ci/test/test-platforms.yml
@@ -200,16 +200,28 @@ windows10-64/opt:
         - desktop-screenshot-capture
         - windows-talos
         - marionette-gpu-tests
         - windows-tests
         - web-platform-tests
         - mochitest-headless
         - raptor-firefox
 
+# windows10-aarch64/opt:
+#     build-platform: win64-aarch64/opt
+#     test-sets:
+#         - awsy
+#         - desktop-screenshot-capture
+#         - windows-talos
+#         - marionette-gpu-tests
+#         - windows-tests
+#         - web-platform-tests
+#         - mochitest-headless
+#         - raptor-firefox
+
 windows10-64-ux/opt:
     build-platform: win64-nightly/opt
     test-sets:
         - raptor-firefox
         - talos-ux
 
 windows10-64-pgo/opt:
     build-platform: win64-pgo/opt
--- a/taskcluster/ci/test/web-platform.yml
+++ b/taskcluster/ci/test/web-platform.yml
@@ -30,30 +30,32 @@ web-platform-tests:
     treeherder-symbol: W(wpt)
     chunks:
         by-test-platform:
             android.*: 36
             linux.*/debug: 18
             macosx64.*/opt: 8
             macosx64/debug: 16
             windows10.*/debug: 18
+            windows10-aarch64/opt: 12
             macosx64-ccov/debug: 24
             default: 12
     max-run-time:
         by-test-platform:
             linux64-ccov/debug: 10800
             windows10-64-ccov/debug: 10800
             default: 7200
     e10s:
         by-test-platform:
             linux32/debug: both
             default: true
     run-on-projects:
         by-test-platform:
             android.*: ['mozilla-central', 'try']
+            windows10-aarch64/opt: ['try', 'mozilla-central']
             .*-qr/.*: ['release', 'try']  # skip on integration branches due to high load
             default: built-projects
     tier:
         by-test-platform:
             android.*: 2
             linux64-asan/opt: 2
             .*-qr/.*: 2  # can't be tier-1 if it's not running on integration branches
             default: default
@@ -99,16 +101,17 @@ web-platform-tests-reftests:
             default: 6
     e10s:
         by-test-platform:
             linux32/debug: both
             default: true
     run-on-projects:
         by-test-platform:
             android.*: ['mozilla-central', 'try']
+            windows10-aarch64/opt: ['try', 'mozilla-central']
             linux64-qr/.*: ['release', 'try']  # skip on integration branches due to high load
             default: built-projects
     tier:
         by-test-platform:
             android.*: 2
             linux64-asan/opt: 2
             linux64-qr/.*: 2  # can't be tier-1 if it's not running on integration branches
             default: default
@@ -146,16 +149,17 @@ web-platform-tests-wdspec:
             linux64-ccov/debug: 4
             windows10-64-ccov/debug: 4
             default: 2
     mozharness:
         extra-options:
             - --test-type=wdspec
     run-on-projects:
         by-test-platform:
+            windows10-aarch64/opt: ['try', 'mozilla-central']
             android.*: ['mozilla-central', 'try']
             .*-qr/.*: ['release', 'try']  # skip on integration branches due to high load
             default: built-projects
     tier:
         by-test-platform:
             android.*: 3
             linux64-asan/opt: 2
             .*-qr/.*: 2  # can't be tier-1 if it's not running on integration branches
--- a/taskcluster/ci/test/xpcshell.yml
+++ b/taskcluster/ci/test/xpcshell.yml
@@ -18,34 +18,37 @@ job-defaults:
                     - remove_executables.py
                 macosx.*:
                     - unittests/mac_unittest.py
                 windows.*:
                     - unittests/win_taskcluster_unittest.py
         requires-signed-builds:
             by-test-platform:
                 windows10-64-asan/opt: false    # No XPCShell on ASAN yet
+                windows10-aarch64/*: false    # No signing on arm64
                 windows.*: true
                 default: false
 
 xpcshell:
     description: "xpcshell test run"
     suite: xpcshell
     treeherder-symbol: X(X)
     run-on-projects:
         by-test-platform:
             windows10-64-asan/opt: []  # No XPCShell on ASAN yet
+            windows10-aarch64/opt: ['try', 'mozilla-central']
             default: built-projects
     chunks:
         by-test-platform:
             linux32/debug: 12
             linux64/debug: 10
             android-em-4.3-arm7-api-16/debug: 12
             macosx.*: 1
             windows.*: 1
+            windows10-aarch64/opt: 8
             windows10-64-ccov/debug: 8
             macosx64-ccov/debug: 8
             default: 8
     instance-size:
         by-test-platform:
             android-em.*: xlarge
             default: default
     max-run-time:
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -72,16 +72,21 @@ WINDOWS_WORKER_TYPES = {
       'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win7-32-gpu',
       'hardware': 'releng-hardware/gecko-t-win10-64-hw',
     },
     'windows10-64': {
       'virtual': 'aws-provisioner-v1/gecko-t-win10-64',
       'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win10-64-gpu',
       'hardware': 'releng-hardware/gecko-t-win10-64-hw',
     },
+    'windows10-aarch64': {
+      'virtual': 'test-provisioner/bitbar',
+      'virtual-with-gpu': 'test-provisioner/bitbar',
+      'hardware': 'test-provisioner/bitbar',
+    },
     'windows10-64-ccov': {
       'virtual': 'aws-provisioner-v1/gecko-t-win10-64',
       'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win10-64-gpu',
       'hardware': 'releng-hardware/gecko-t-win10-64-hw',
     },
     'windows10-64-pgo': {
       'virtual': 'aws-provisioner-v1/gecko-t-win10-64',
       'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win10-64-gpu',
@@ -578,16 +583,17 @@ def set_treeherder_machine_platform(conf
     translation = {
         # Linux64 build platforms for asan and pgo are specified differently to
         # treeherder.
         'linux64-asan/opt': 'linux64/asan',
         'linux64-pgo/opt': 'linux64/pgo',
         'macosx64/debug': 'osx-10-10/debug',
         'macosx64/opt': 'osx-10-10/opt',
         'win64-asan/opt': 'windows10-64/asan',
+        'win64-aarch64/opt': 'windows10-aarch64/opt',
         'win32-pgo/opt': 'windows7-32/pgo',
         'win64-pgo/opt': 'windows10-64/pgo',
         # The build names for Android platforms have partially evolved over the
         # years and need to be translated.
         'android-api-16/debug': 'android-em-4-3-armv7-api16/debug',
         'android-api-16-ccov/debug': 'android-em-4-3-armv7-api16-ccov/debug',
         'android-api-16/opt': 'android-em-4-3-armv7-api16/opt',
         'android-x86/opt': 'android-em-4-2-x86/opt',
@@ -639,16 +645,17 @@ def set_tier(config, tests):
                                          'linux64-asan/opt',
                                          'linux64-qr/opt',
                                          'linux64-qr/debug',
                                          'windows7-32/debug',
                                          'windows7-32/opt',
                                          'windows7-32-pgo/opt',
                                          'windows7-32-devedition/opt',
                                          'windows7-32-nightly/opt',
+                                         'windows10-aarch64/opt',
                                          'windows10-64/debug',
                                          'windows10-64/opt',
                                          'windows10-64-pgo/opt',
                                          'windows10-64-devedition/opt',
                                          'windows10-64-nightly/opt',
                                          'windows10-64-asan/opt',
                                          'windows10-64-qr/opt',
                                          'windows10-64-qr/debug',
--- a/taskcluster/taskgraph/util/workertypes.py
+++ b/taskcluster/taskgraph/util/workertypes.py
@@ -29,16 +29,17 @@ WORKER_TYPES = {
     'aws-provisioner-v1/gecko-t-linux-xlarge': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-t-win10-64': ('generic-worker', 'windows'),
     'aws-provisioner-v1/gecko-t-win10-64-gpu': ('generic-worker', 'windows'),
     'releng-hardware/gecko-t-win10-64-hw': ('generic-worker', 'windows'),
     'releng-hardware/gecko-t-win10-64-ux': ('generic-worker', 'windows'),
     'aws-provisioner-v1/gecko-t-win7-32': ('generic-worker', 'windows'),
     'aws-provisioner-v1/gecko-t-win7-32-gpu': ('generic-worker', 'windows'),
     'releng-hardware/gecko-t-win7-32-hw': ('generic-worker', 'windows'),
+    'test-provisioner/bitbar': ('generic-worker', 'windows'),
     'aws-provisioner-v1/taskcluster-generic': ('docker-worker', 'linux'),
     'invalid/invalid': ('invalid', None),
     'invalid/always-optimized': ('always-optimized', None),
     'releng-hardware/gecko-t-linux-talos': ('generic-worker', 'linux'),
     'scriptworker-prov-v1/balrog-dev': ('balrog', None),
     'scriptworker-prov-v1/balrogworker-v1': ('balrog', None),
     'scriptworker-prov-v1/beetmoverworker-v1': ('beetmover', None),
     'scriptworker-prov-v1/pushapk-v1': ('push-apk', None),
--- a/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
+++ b/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
@@ -719,17 +719,17 @@ class AndroidEmulator(object):
 
     def _get_avd_type(self, requested):
         if requested in AVD_DICT.keys():
             return requested
         if self.substs:
             if not self.substs['TARGET_CPU'].startswith('arm'):
                 return 'x86-7.0'
             else:
-                return '7.0'
+                return '4.3'
         return 'x86-7.0'
 
 
 def _find_sdk_exe(substs, exe, tools):
     if tools:
         subdirs = ['emulator', 'tools']
     else:
         subdirs = ['platform-tools']
--- a/testing/raptor/mach_commands.py
+++ b/testing/raptor/mach_commands.py
@@ -127,20 +127,16 @@ class RaptorRunner(MozbuildObject):
             'base_work_dir': self.mozharness_dir,
             'exes': {
                 'python': self.python_interp,
                 'virtualenv': [self.python_interp, self.virtualenv_script],
             },
             'title': socket.gethostname(),
             'default_actions': default_actions,
             'raptor_cmd_line_args': self.raptor_args,
-            'python3_manifest': {
-                'win32': 'python3.manifest',
-                'win64': 'python3_x64.manifest',
-            },
             'host': self.host,
             'power_test': self.power_test,
             'is_release_build': self.is_release_build,
         }
 
     def make_args(self):
         self.args = {
             'config': {},
--- a/testing/raptor/raptor/manifest.py
+++ b/testing/raptor/raptor/manifest.py
@@ -14,17 +14,17 @@ here = os.path.abspath(os.path.dirname(_
 raptor_ini = os.path.join(here, 'raptor.ini')
 tests_dir = os.path.join(here, 'tests')
 LOG = get_proxy_logger(component="raptor-manifest")
 
 required_settings = ['apps', 'type', 'page_cycles', 'test_url', 'measure',
                      'unit', 'lower_is_better', 'alert_threshold']
 
 playback_settings = ['playback_binary_manifest', 'playback_pageset_manifest',
-                     'playback_recordings', 'python3_win_manifest']
+                     'playback_recordings']
 
 
 def filter_app(tests, values):
     for test in tests:
         if values["app"] in test['apps']:
             yield test
 
 
new file mode 100644
--- /dev/null
+++ b/testing/raptor/raptor/playback/mitmproxy-rel-bin-win.manifest
@@ -0,0 +1,10 @@
+[
+    {
+    "size": 13576786,
+    "visibility": "public",
+    "digest": "5d471e470369381f130de28a3c54db27c3724e1e9269c510a593d61c4cf41713c9424da62e46ae98fd1decce00ecca876209ed059487f5a02882ba16a50daed1",
+    "algorithm": "sha512",
+    "filename": "mitmdump-win-2.0.2.zip",
+    "unpack": true
+    }
+]
\ No newline at end of file
--- a/testing/raptor/raptor/playback/mitmproxy.py
+++ b/testing/raptor/raptor/playback/mitmproxy.py
@@ -7,36 +7,32 @@ from __future__ import absolute_import
 import os
 import subprocess
 import sys
 import time
 
 import mozinfo
 
 from mozlog import get_proxy_logger
+from mozprocess import ProcessHandler
 
 from .base import Playback
 
 here = os.path.dirname(os.path.realpath(__file__))
 LOG = get_proxy_logger(component='raptor-mitmproxy')
 
 # needed so unit tests can find their imports
 if os.environ.get('SCRIPTSPATH', None) is not None:
     # in production it is env SCRIPTS_PATH
     mozharness_dir = os.environ['SCRIPTSPATH']
 else:
     # locally it's in source tree
     mozharness_dir = os.path.join(here, '../../../mozharness')
 sys.path.insert(0, mozharness_dir)
 
-# required for using a python3 virtualenv on win for mitmproxy
-from mozharness.base.python import Python3Virtualenv
-from mozharness.mozilla.testing.testbase import TestingMixin
-from mozharness.base.vcs.vcsbase import MercurialScript
-
 raptor_dir = os.path.join(here, '..')
 sys.path.insert(0, raptor_dir)
 
 from utils import transform_platform, tooltool_download, download_file_from_url
 
 # path for mitmproxy certificate, generated auto after mitmdump is started
 # on local machine it is 'HOME', however it is different on production machines
 try:
@@ -74,17 +70,17 @@ POLICIES_CONTENT_OFF = '''{
     "Proxy": {
       "Mode": "none",
       "Locked": false
     }
   }
 }'''
 
 
-class Mitmproxy(Playback, Python3Virtualenv, TestingMixin, MercurialScript):
+class Mitmproxy(Playback):
 
     def __init__(self, config):
         self.config = config
         self.mitmproxy_proc = None
         self.mitmdump_path = None
         self.recordings = config.get('playback_recordings', None)
         self.browser_path = config.get('binary', None)
 
@@ -101,85 +97,41 @@ class Mitmproxy(Playback, Python3Virtual
         # add raptor to raptor_dir
         self.raptor_dir = os.path.join(self.raptor_dir, "testing", "raptor")
         self.recordings_path = self.raptor_dir
         LOG.info("raptor_dir used for mitmproxy downloads and exe files: %s" % self.raptor_dir)
 
         # go ahead and download and setup mitmproxy
         self.download()
 
-        # on windows we must use a python3 virtualen for mitmproxy
-        if 'win' in self.config['platform']:
-            self.setup_py3_virtualenv()
-
         # mitmproxy must be started before setup, so that the CA cert is available
         self.start()
         self.setup()
 
     def download(self):
         """Download and unpack mitmproxy binary and pageset using tooltool"""
         if not os.path.exists(self.raptor_dir):
             os.makedirs(self.raptor_dir)
 
-        if 'win' in self.config['platform']:
-            # on windows we need a python3 environment and use our own package from tooltool
-            self.py3_path = self.fetch_python3()
-            LOG.info("python3 path is: %s" % self.py3_path)
-        else:
-            # on osx and linux we use pre-built binaries
-            LOG.info("downloading mitmproxy binary")
-            _manifest = os.path.join(here, self.config['playback_binary_manifest'])
-            transformed_manifest = transform_platform(_manifest, self.config['platform'])
-            tooltool_download(transformed_manifest, self.config['run_local'], self.raptor_dir)
+        LOG.info("downloading mitmproxy binary")
+        _manifest = os.path.join(here, self.config['playback_binary_manifest'])
+        transformed_manifest = transform_platform(_manifest, self.config['platform'])
+        tooltool_download(transformed_manifest, self.config['run_local'], self.raptor_dir)
 
         # we use one pageset for all platforms
         LOG.info("downloading mitmproxy pageset")
         _manifest = os.path.join(here, self.config['playback_pageset_manifest'])
         transformed_manifest = transform_platform(_manifest, self.config['platform'])
         tooltool_download(transformed_manifest, self.config['run_local'], self.raptor_dir)
         return
 
-    def fetch_python3(self):
-        """Mitmproxy on windows needs Python 3.x"""
-        python3_path = os.path.join(self.raptor_dir, 'python3.6', 'python')
-        if not os.path.exists(os.path.dirname(python3_path)):
-            _manifest = os.path.join(here, self.config['python3_win_manifest'])
-            transformed_manifest = transform_platform(_manifest, self.config['platform'],
-                                                      self.config['processor'])
-            LOG.info("downloading py3 package for mitmproxy windows: %s" % transformed_manifest)
-            tooltool_download(transformed_manifest, self.config['run_local'], self.raptor_dir)
-        cmd = [python3_path, '--version']
-        # just want python3 ver printed in production log
-        subprocess.Popen(cmd, env=os.environ.copy())
-        return python3_path
+    def start(self):
+        """Start playing back the mitmproxy recording."""
 
-    def setup_py3_virtualenv(self):
-        """Mitmproxy on windows needs Python 3.x; set up a separate py 3.x env here"""
-        LOG.info("Setting up python 3.x virtualenv, required for mitmproxy on windows")
-        # these next two are required for py3_venv_configuration
-        self.abs_dirs = {'base_work_dir': mozharness_dir}
-        self.log_obj = None
-        # now create the py3 venv
-        venv_path = os.path.join(self.raptor_dir, 'py3venv')
-        self.py3_venv_configuration(python_path=self.py3_path, venv_path=venv_path)
-        self.py3_create_venv()
-        self.py3_install_modules(["cffi==1.10.0"])
-        requirements = [os.path.join(here, "mitmproxy_requirements.txt")]
-        self.py3_install_requirement_files(requirements)
-        # add py3 executables path to system path
-        sys.path.insert(1, self.py3_path_to_executables())
-        # install mitmproxy itself
-        self.py3_install_modules(modules=['mitmproxy'])
-        self.mitmdump_path = os.path.join(self.py3_path_to_executables(), 'mitmdump')
-
-    def start(self):
-        """Start playing back the mitmproxy recording. If on windows, the mitmdump_path was
-        already set when creating py3 env"""
-        if self.mitmdump_path is None:
-            self.mitmdump_path = os.path.join(self.raptor_dir, 'mitmdump')
+        self.mitmdump_path = os.path.join(self.raptor_dir, 'mitmdump')
 
         recordings_list = self.recordings.split()
         self.mitmproxy_proc = self.start_mitmproxy_playback(self.mitmdump_path,
                                                             self.recordings_path,
                                                             recordings_list,
                                                             self.browser_path)
         return
 
@@ -223,34 +175,34 @@ class Mitmproxy(Playback, Python3Virtual
         env["PATH"] = os.path.dirname(browser_path) + ";" + env["PATH"]
 
         command = [mitmdump_path, '-k', '-q', '-s', param2]
 
         LOG.info("Starting mitmproxy playback using env path: %s" % env["PATH"])
         LOG.info("Starting mitmproxy playback using command: %s" % ' '.join(command))
         # to turn off mitmproxy log output, use these params for Popen:
         # Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
-        mitmproxy_proc = subprocess.Popen(command, env=env)
+        mitmproxy_proc = ProcessHandler(command, env=env)
+        mitmproxy_proc.run()
+
         time.sleep(MITMDUMP_SLEEP)
         data = mitmproxy_proc.poll()
         if data is None:  # None value indicates process hasn't terminated
             LOG.info("Mitmproxy playback successfully started as pid %d" % mitmproxy_proc.pid)
             return mitmproxy_proc
         # cannot continue as we won't be able to playback the pages
         LOG.error('Aborting: mitmproxy playback process failed to start, poll returned: %s' % data)
         sys.exit()
 
     def stop_mitmproxy_playback(self):
         """Stop the mitproxy server playback"""
         mitmproxy_proc = self.mitmproxy_proc
         LOG.info("Stopping mitmproxy playback, klling process %d" % mitmproxy_proc.pid)
-        if mozinfo.os == 'win':
-            mitmproxy_proc.kill()
-        else:
-            mitmproxy_proc.terminate()
+        mitmproxy_proc.kill()
+
         time.sleep(MITMDUMP_SLEEP)
         status = mitmproxy_proc.poll()
         if status is None:  # None value indicates process hasn't terminated
             # I *think* we can still continue, as process will be automatically
             # killed anyway when mozharness is done (?) if not, we won't be able
             # to startup mitmxproy next time if it is already running
             LOG.error("Failed to kill the mitmproxy playback process")
             LOG.info(str(status))
@@ -329,18 +281,18 @@ class MitmproxyDesktop(Mitmproxy):
     def is_mitmproxy_cert_installed(self):
         """Verify mitmxproy CA cert was added to Firefox"""
         try:
             # read autoconfig file, confirm mitmproxy cert is in there
             contents = self.read_policies_json(self.policies_dir)
             LOG.info("Firefox policies file contents:")
             LOG.info(contents)
             if (POLICIES_CONTENT_ON % {
-                    'cert': self.cert_path,
-                    'host': self.config['host']}) in contents:
+                'cert': self.cert_path,
+                'host': self.config['host']}) in contents:
                 LOG.info("Verified mitmproxy CA certificate is installed in Firefox")
             else:
 
                 return False
         except Exception as e:
             LOG.info("failed to read Firefox policies file, exeption: %s" % e)
             return False
         return True
deleted file mode 100644
--- a/testing/raptor/raptor/playback/python3.manifest
+++ /dev/null
@@ -1,10 +0,0 @@
-[
-  {
-    "size": 15380470,
-    "visibility": "public",
-    "digest": "cd78b88d95b69bef99d7192b71dd34118700f44db0a0069a13bfd4943b131e8d7fdac83859f8ac15d873d4b329eef69d8d75d0a6746d06fdcfc5d06da0c9784c",
-    "algorithm": "sha512",
-    "unpack": true,
-    "filename": "python3.6.zip"
-  }
-]
deleted file mode 100644
--- a/testing/raptor/raptor/playback/python3_x64.manifest
+++ /dev/null
@@ -1,10 +0,0 @@
-[
-  {
-    "size": 16026760,
-    "visibility": "public",
-    "digest": "379428e3955671213a245ccd9ccf6f9d17d368db68c02da8baed7be629f2691127cd3e3f86807b25e2098d9840083fdc07946ab1bed0c14db4a5b628a47ed9ef",
-    "algorithm": "sha512",
-    "unpack": true,
-    "filename": "python3.6.amd64.zip"
-  }
-]
--- a/testing/raptor/raptor/raptor.py
+++ b/testing/raptor/raptor/raptor.py
@@ -166,17 +166,16 @@ class Raptor(object):
         self.log.info("test uses playback tool: %s " % self.config['playback_tool'])
         self.config['playback_binary_manifest'] = test.get('playback_binary_manifest', None)
         _key = 'playback_binary_zip_%s' % self.config['platform']
         self.config['playback_binary_zip'] = test.get(_key, None)
         self.config['playback_pageset_manifest'] = test.get('playback_pageset_manifest', None)
         _key = 'playback_pageset_zip_%s' % self.config['platform']
         self.config['playback_pageset_zip'] = test.get(_key, None)
         self.config['playback_recordings'] = test.get('playback_recordings', None)
-        self.config['python3_win_manifest'] = test.get('python3_win_manifest', None)
 
     def run_test(self, test, timeout=None):
         self.log.info("starting raptor test: %s" % test['name'])
         self.log.info("test settings: %s" % str(test))
         self.log.info("raptor config: %s" % str(self.config))
 
         # benchmark-type tests require the benchmark test to be served out
         if test.get('type') == "benchmark":
--- a/testing/raptor/raptor/tests/raptor-tp6-1.ini
+++ b/testing/raptor/raptor/tests/raptor-tp6-1.ini
@@ -3,17 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # raptor tp6-1
 
 [DEFAULT]
 type =  pageload
 playback = mitmproxy
 playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
-python3_win_manifest = python3{x64}.manifest
 playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-1.manifest
 page_cycles = 25
 unit = ms
 lower_is_better = true
 alert_threshold = 2.0
 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds
 # beyond typical pageload time
 page_timeout = 30000
--- a/testing/raptor/raptor/tests/raptor-tp6-10.ini
+++ b/testing/raptor/raptor/tests/raptor-tp6-10.ini
@@ -3,17 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # raptor tp6-10
 
 [DEFAULT]
 type =  pageload
 playback = mitmproxy
 playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
-python3_win_manifest = python3{x64}.manifest
 page_cycles = 25
 unit = ms
 lower_is_better = true
 alert_threshold = 2.0
 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds
 # beyond typical pageload time
 page_timeout = 60000
 gecko_profile_interval = 1
--- a/testing/raptor/raptor/tests/raptor-tp6-2.ini
+++ b/testing/raptor/raptor/tests/raptor-tp6-2.ini
@@ -3,17 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # raptor tp6-2
 
 [DEFAULT]
 type =  pageload
 playback = mitmproxy
 playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
-python3_win_manifest = python3{x64}.manifest
 playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-2.manifest
 page_cycles = 25
 unit = ms
 lower_is_better = true
 alert_threshold = 2.0
 page_timeout = 60000
 gecko_profile_interval = 1
 gecko_profile_entries = 14000000
--- a/testing/raptor/raptor/tests/raptor-tp6-3.ini
+++ b/testing/raptor/raptor/tests/raptor-tp6-3.ini
@@ -3,17 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # raptor tp6-3
 
 [DEFAULT]
 type =  pageload
 playback = mitmproxy
 playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
-python3_win_manifest = python3{x64}.manifest
 playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-3.manifest
 page_cycles = 25
 unit = ms
 lower_is_better = true
 alert_threshold = 2.0
 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds
 # beyond typical pageload time
 page_timeout = 30000
--- a/testing/raptor/raptor/tests/raptor-tp6-4.ini
+++ b/testing/raptor/raptor/tests/raptor-tp6-4.ini
@@ -3,17 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # raptor tp6-4
 
 [DEFAULT]
 type =  pageload
 playback = mitmproxy
 playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
-python3_win_manifest = python3{x64}.manifest
 playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-4.manifest
 page_cycles = 25
 unit = ms
 lower_is_better = true
 alert_threshold = 2.0
 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds
 # beyond typical pageload time
 page_timeout = 30000
--- a/testing/raptor/raptor/tests/raptor-tp6-5.ini
+++ b/testing/raptor/raptor/tests/raptor-tp6-5.ini
@@ -3,17 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # raptor tp6-5
 
 [DEFAULT]
 type =  pageload
 playback = mitmproxy
 playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
-python3_win_manifest = python3{x64}.manifest
 playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-5.manifest
 page_cycles = 25
 unit = ms
 lower_is_better = true
 alert_threshold = 2.0
 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds
 # beyond typical pageload time
 page_timeout = 30000
--- a/testing/raptor/raptor/tests/raptor-tp6-6.ini
+++ b/testing/raptor/raptor/tests/raptor-tp6-6.ini
@@ -3,17 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # raptor tp6-6
 
 [DEFAULT]
 type =  pageload
 playback = mitmproxy
 playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
-python3_win_manifest = python3{x64}.manifest
 playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-6.manifest
 page_cycles = 25
 unit = ms
 lower_is_better = true
 alert_threshold = 2.0
 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds
 # beyond typical pageload time
 page_timeout = 30000
--- a/testing/raptor/raptor/tests/raptor-tp6-7.ini
+++ b/testing/raptor/raptor/tests/raptor-tp6-7.ini
@@ -3,17 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # raptor tp6-7
 
 [DEFAULT]
 type =  pageload
 playback = mitmproxy
 playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
-python3_win_manifest = python3{x64}.manifest
 playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-7.manifest
 page_cycles = 25
 unit = ms
 lower_is_better = true
 alert_threshold = 2.0
 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds
 # beyond typical pageload time
 page_timeout = 60000
--- a/testing/raptor/raptor/tests/raptor-tp6-8.ini
+++ b/testing/raptor/raptor/tests/raptor-tp6-8.ini
@@ -3,17 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # raptor tp6-8
 
 [DEFAULT]
 type =  pageload
 playback = mitmproxy
 playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
-python3_win_manifest = python3{x64}.manifest
 page_cycles = 25
 unit = ms
 lower_is_better = true
 alert_threshold = 2.0
 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds
 # beyond typical pageload time
 page_timeout = 30000
 gecko_profile_interval = 1
--- a/testing/raptor/raptor/tests/raptor-tp6-9.ini
+++ b/testing/raptor/raptor/tests/raptor-tp6-9.ini
@@ -3,17 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # raptor tp6-9
 
 [DEFAULT]
 type =  pageload
 playback = mitmproxy
 playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
-python3_win_manifest = python3{x64}.manifest
 page_cycles = 25
 unit = ms
 lower_is_better = true
 alert_threshold = 2.0
 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds
 # beyond typical pageload time
 page_timeout = 30000
 gecko_profile_interval = 1
--- a/testing/raptor/raptor/tests/raptor-tp6m-1.ini
+++ b/testing/raptor/raptor/tests/raptor-tp6m-1.ini
@@ -3,17 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # raptor tp6m-1
 
 [DEFAULT]
 type =  pageload
 playback = mitmproxy-android
 playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
-python3_win_manifest = python3{x64}.manifest
 page_cycles = 15
 unit = ms
 lower_is_better = true
 alert_threshold = 2.0
 page_timeout = 60000
 alert_on = fcp, loadtime
 
 [raptor-tp6m-amazon-geckoview]
--- a/testing/raptor/test/test_manifest.py
+++ b/testing/raptor/test/test_manifest.py
@@ -31,17 +31,16 @@ VALID_MANIFESTS = [{'apps': 'firefox',
                     'alert_on': 'fcp',
                     'unit': 'ms',
                     'lower_is_better': True,
                     'alert_threshold': 2.0,
                     'playback': 'mitmproxy',
                     'playback_binary_manifest': 'binary.manifest',
                     'playback_pageset_manifest': 'pageset.manifest',
                     'playback_recordings': 'recorded_site.mp',
-                    'python3_win_manifest': 'py3.manifest',
                     'manifest': 'valid_details_1'},
                    {'apps': 'chrome',
                     'type': 'benchmark',
                     'page_cycles': 5,
                     'test_url': 'http://www.test-url/goes/here',
                     'measure': 'fcp',
                     'unit': 'score',
                     'lower_is_better': False,
@@ -54,17 +53,16 @@ INVALID_MANIFESTS = [{'apps': 'firefox',
                       'test_url': 'http://www.test-url/goes/here',
                       'unit': 'ms',
                       'lower_is_better': True,
                       'alert_threshold': 2.0,
                       'playback': 'mitmproxy',
                       'playback_binary_manifest': 'binary.manifest',
                       'playback_pageset_manifest': 'pageset.manifest',
                       'playback_recordings': 'recorded_site.mp',
-                      'python3_win_manifest': 'py3.manifest',
                       'manifest': 'invalid_details_1'},
                      {'apps': 'chrome',
                       'type': 'pageload',
                       'page_cycles': 25,
                       'test_url': 'http://www.test-url/goes/here',
                       'measure': 'fnbpaint, fcp',
                       'unit': 'ms',
                       'lower_is_better': True,
@@ -79,17 +77,16 @@ INVALID_MANIFESTS = [{'apps': 'firefox',
                       'alert_on': 'nope',
                       'unit': 'ms',
                       'lower_is_better': True,
                       'alert_threshold': 2.0,
                       'playback': 'mitmproxy',
                       'playback_binary_manifest': 'binary.manifest',
                       'playback_pageset_manifest': 'pageset.manifest',
                       'playback_recordings': 'recorded_site.mp',
-                      'python3_win_manifest': 'py3.manifest',
                       'manifest': 'invalid_details_3'}]
 
 
 @pytest.mark.parametrize('app', ['firefox', 'chrome', 'geckoview'])
 def test_get_browser_test_list(app):
     test_list = get_browser_test_list(app)
     assert len(test_list) > 0
 
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-device-adapt/documentElement-clientWidth-on-minimum-scale-size.tentative.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[documentElement-clientWidth-on-minimum-scale-size.tentative.html]
-  [documentElement clientWidth should be equal to device-width even if overflow:hidden region is visible]
-    expected:
-      if (os == "android"): FAIL
-
--- a/toolkit/components/extensions/ExtensionParent.jsm
+++ b/toolkit/components/extensions/ExtensionParent.jsm
@@ -1776,18 +1776,19 @@ class CacheStore {
     let [store] = await this.getStore();
 
     return new Map(store);
   }
 
   async delete(path) {
     let [store, key] = await this.getStore(path);
 
-    store.delete(key);
-    StartupCache.save();
+    if (store.delete(key)) {
+      StartupCache.save();
+    }
   }
 }
 
 for (let name of StartupCache.STORE_NAMES) {
   StartupCache[name] = new CacheStore(name);
 }
 
 var ExtensionParent = {
--- a/toolkit/components/extensions/ExtensionPermissions.jsm
+++ b/toolkit/components/extensions/ExtensionPermissions.jsm
@@ -58,27 +58,36 @@ var ExtensionPermissions = {
   },
 
   async _get(extensionId) {
     await lazyInit();
 
     let perms = prefs.data[extensionId];
     if (!perms) {
       perms = emptyPermissions();
-      prefs.data[extensionId] = perms;
     }
 
     return perms;
   },
 
   async _getCached(extensionId) {
     return StartupCache.permissions.get(extensionId,
                                         () => this._get(extensionId));
   },
 
+  /**
+   * Retrieves the optional permissions for the given extension.
+   * The information may be retrieved from the StartupCache, and otherwise fall
+   * back to data from the disk (and cache the result in the StartupCache).
+   *
+   * @param {string} extensionId The extensionId
+   * @returns {object} An object with "permissions" and "origins" array.
+   *   The object may be a direct reference to the storage or cache, so its
+   *   value should immediately be used and not be modified by callers.
+   */
   get(extensionId) {
     return this._getCached(extensionId);
   },
 
   /**
    * Add new permissions for the given extension.  `permissions` is
    * in the format that is passed to browser.permissions.request().
    *
@@ -149,20 +158,20 @@ var ExtensionPermissions = {
       this._saveSoon(extensionId);
       if (emitter) {
         emitter.emit("remove-permissions", removed);
       }
     }
   },
 
   async removeAll(extensionId) {
-    let perms = await this._getCached(extensionId);
-
-    if (perms.permissions.length || perms.origins.length) {
-      Object.assign(perms, emptyPermissions());
+    await lazyInit();
+    StartupCache.permissions.delete(extensionId);
+    if (prefs.data[extensionId]) {
+      delete prefs.data[extensionId];
       prefs.saveSoon();
     }
   },
 
   // This is meant for tests only
   async _uninit() {
     if (!_initPromise) {
       return;
--- a/toolkit/components/extensions/test/xpcshell/test_ext_permissions_uninstall.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_permissions_uninstall.js
@@ -12,16 +12,50 @@ const observer = {
   observe(subject, topic, data) {
     if (topic == "webextension-optional-permission-prompt") {
       let {resolve} = subject.wrappedJSObject;
       resolve(true);
     }
   },
 };
 
+// Look up the cached permissions, if any.
+async function getCachedPermissions(extensionId) {
+  const NotFound = Symbol("extension ID not found in permissions cache");
+  try {
+    return await ExtensionParent.StartupCache.permissions.get(extensionId, () => {
+      // Throw error to prevent the key from being created.
+      throw NotFound;
+    });
+  } catch (e) {
+    if (e === NotFound) {
+      return null;
+    }
+    throw e;
+  }
+}
+
+// Look up the permissions from the file. Internal methods are used to avoid
+// inadvertently changing the permissions in the cache or JSON file.
+async function getStoredPermissions(extensionId) {
+  // The two _get calls that follow are expected to return the same object if
+  // the entry exists in the JSON file, otherwise we expect two different
+  // objects (with the same default properties).
+  let perms1 = await ExtensionPermissions._get(extensionId);
+  let perms2 = await ExtensionPermissions._get(extensionId);
+  if (perms1 === perms2) {
+    // There is an entry in the file.
+    return perms1;
+  }
+  // Sanity check: The returned object should be empty.
+  Assert.deepEqual(perms1, perms2, "Expected same permission values");
+  Assert.deepEqual(perms1, {origins: [], permissions: []}, "Expected empty permissions");
+  return null;
+}
+
 add_task(async function setup() {
   Services.prefs.setBoolPref("extensions.webextOptionalPermissionPrompts", true);
   Services.obs.addObserver(observer, "webextension-optional-permission-prompt");
   await AddonTestUtils.promiseStartupManager();
   registerCleanupFunction(async () => {
     await AddonTestUtils.promiseShutdownManager();
     Services.obs.removeObserver(observer, "webextension-optional-permission-prompt");
     Services.prefs.clearUserPref("extensions.webextOptionalPermissionPrompts");
@@ -57,14 +91,36 @@ add_task(async function test_permissions
     let result = await extension.awaitMessage("request.result");
     equal(result, true, "request() for optional permissions succeeded");
   });
 
   let id = extension.id;
   let perms = await ExtensionPermissions.get(id);
   equal(perms.permissions.length, 1, "optional permission added");
 
+  Assert.deepEqual(await getCachedPermissions(id), {
+    permissions: ["idle"],
+    origins: [],
+  }, "Optional permission added to cache");
+  Assert.deepEqual(await getStoredPermissions(id), {
+    permissions: ["idle"],
+    origins: [],
+  }, "Optional permission added to persistent file");
+
   await extension.unload();
 
+  // Directly read from the internals instead of using ExtensionPermissions.get,
+  // because the latter will lazily cache the extension ID.
+  Assert.deepEqual(await getCachedPermissions(id), null, "Cached permissions removed");
+  Assert.deepEqual(await getStoredPermissions(id), null, "Stored permissions removed");
+
   perms = await ExtensionPermissions.get(id);
   equal(perms.permissions.length, 0, "no permissions after uninstall");
   equal(perms.origins.length, 0, "no origin permissions after uninstall");
+
+  // The public ExtensionPermissions.get method should not store (empty)
+  // permissions in the persistent JSON file. Polluting the cache is not ideal,
+  // but acceptable since the cache will eventually be cleared, and non-test
+  // code is not likely to call ExtensionPermissions.get() for non-installed
+  // extensions anyway.
+  Assert.deepEqual(await getCachedPermissions(id), perms, "Permissions cached");
+  Assert.deepEqual(await getStoredPermissions(id), null, "Permissions not saved");
 });
--- a/toolkit/components/telemetry/tests/utils/TelemetryTestUtils.jsm
+++ b/toolkit/components/telemetry/tests/utils/TelemetryTestUtils.jsm
@@ -127,25 +127,28 @@ var TelemetryTestUtils = {
    * other indexes are all zero.
    *
    * @param {Object} histogram The histogram to check.
    * @param {Number} index The index to check against the expected value.
    * @param {Number} expected The expected value of the index.
    */
   assertHistogram(histogram, index, expected) {
     const snapshot = histogram.snapshot();
+    let found = false;
     for (let [i, val] of Object.entries(snapshot.values)) {
       if (i == index) {
+        found = true;
         Assert.equal(val, expected,
           `expected counts should match for the histogram index ${i}`);
       } else {
         Assert.equal(val, 0,
           `unexpected counts should be zero for the histogram index ${i}`);
       }
     }
+    Assert.ok(found, `Should have found an entry for histogram index ${index}`);
   },
 
   /**
    * Assert that a key within a keyed histogram contains the required sum.
    *
    * @param {Object} histogram The keyed histogram to check.
    * @param {String} key The key to check.
    * @param {Number} [expected] The expected sum for the key.
@@ -167,16 +170,20 @@ var TelemetryTestUtils = {
    *
    * @param {Object} histogram The keyed histogram to check.
    * @param {String} key The key to check.
    * @param {Number} index The index to check against the expected value.
    * @param {Number} [expected] The expected values for the key.
    */
   assertKeyedHistogramValue(histogram, key, index, expected) {
     const snapshot = histogram.snapshot();
+    if (!(key in snapshot)) {
+      Assert.ok(false, `The histogram must contain ${key}`);
+      return;
+    }
     for (let [i, val] of Object.entries(snapshot[key].values)) {
       if (i == index) {
         Assert.equal(val, expected,
           `expected counts should match for the histogram index ${i}`);
       } else {
         Assert.equal(val, 0,
           `unexpected counts should be zero for the histogram index ${i}`);
       }
--- a/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
+++ b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
@@ -3557,21 +3557,17 @@ function UpdatePrompt(aCallback) {
                      Array.prototype.slice.call(arguments));
     };
   });
 }
 
 UpdatePrompt.prototype = {
   flags: Ci.nsIClassInfo.SINGLETON,
   getScriptableHelper: () => null,
-  getInterfaces(aCount) {
-    let interfaces = [Ci.nsISupports, Ci.nsIUpdatePrompt];
-    aCount.value = interfaces.length;
-    return interfaces;
-  },
+  interfaces: [Ci.nsISupports, Ci.nsIUpdatePrompt],
   QueryInterface: ChromeUtils.generateQI([Ci.nsIClassInfo, Ci.nsIUpdatePrompt]),
 };
 
 /* Update check listener */
 const updateCheckListener = {
   onProgress: function UCL_onProgress(aRequest, aPosition, aTotalSize) {
   },
 
--- a/xpcom/base/nsClassInfoImpl.cpp
+++ b/xpcom/base/nsClassInfoImpl.cpp
@@ -11,18 +11,18 @@ NS_IMETHODIMP_(MozExternalRefCountType)
 GenericClassInfo::AddRef() { return 2; }
 
 NS_IMETHODIMP_(MozExternalRefCountType)
 GenericClassInfo::Release() { return 1; }
 
 NS_IMPL_QUERY_INTERFACE(GenericClassInfo, nsIClassInfo)
 
 NS_IMETHODIMP
-GenericClassInfo::GetInterfaces(uint32_t* aCount, nsIID*** aArray) {
-  return mData->getinterfaces(aCount, aArray);
+GenericClassInfo::GetInterfaces(nsTArray<nsIID>& aArray) {
+  return mData->getinterfaces(aArray);
 }
 
 NS_IMETHODIMP
 GenericClassInfo::GetScriptableHelper(nsIXPCScriptable** aHelper) {
   if (mData->getscriptablehelper) {
     return mData->getscriptablehelper(aHelper);
   }
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/xpcom/base/nsIClassInfoImpl.h
+++ b/xpcom/base/nsIClassInfoImpl.h
@@ -84,18 +84,17 @@
  * as a singleton.
  */
 
 class GenericClassInfo : public nsIClassInfo {
  public:
   struct ClassInfoData {
     // This function pointer uses NS_CALLBACK_ because it's always set to an
     // NS_IMETHOD function, which uses __stdcall on Win32.
-    typedef NS_CALLBACK_(nsresult, GetInterfacesProc)(uint32_t* aCountP,
-                                                      nsIID*** aArray);
+    typedef NS_CALLBACK_(nsresult, GetInterfacesProc)(nsTArray<nsIID>& aArray);
     GetInterfacesProc getinterfaces;
 
     // This function pointer doesn't use NS_CALLBACK_ because it's always set to
     // a vanilla function.
     typedef nsresult (*GetScriptableHelperProc)(nsIXPCScriptable** aHelper);
     GetScriptableHelperProc getscriptablehelper;
 
     uint32_t flags;
@@ -108,18 +107,19 @@ class GenericClassInfo : public nsIClass
   explicit GenericClassInfo(const ClassInfoData* aData) : mData(aData) {}
 
  private:
   const ClassInfoData* mData;
 };
 
 #define NS_CLASSINFO_NAME(_class) g##_class##_classInfoGlobal
 #define NS_CI_INTERFACE_GETTER_NAME(_class) _class##_GetInterfacesHelper
-#define NS_DECL_CI_INTERFACE_GETTER(_class) \
-  extern NS_IMETHODIMP NS_CI_INTERFACE_GETTER_NAME(_class)(uint32_t*, nsIID***);
+#define NS_DECL_CI_INTERFACE_GETTER(_class)                                  \
+  extern NS_IMETHODIMP NS_CI_INTERFACE_GETTER_NAME(_class)(nsTArray<nsIID> & \
+                                                           array);
 
 #define NS_IMPL_CLASSINFO(_class, _getscriptablehelper, _flags, _cid)       \
   NS_DECL_CI_INTERFACE_GETTER(_class)                                       \
   static const GenericClassInfo::ClassInfoData k##_class##ClassInfoData = { \
       NS_CI_INTERFACE_GETTER_NAME(_class),                                  \
       _getscriptablehelper,                                                 \
       _flags | nsIClassInfo::SINGLETON_CLASSINFO,                           \
       _cid,                                                                 \
@@ -130,29 +130,27 @@ class GenericClassInfo : public nsIClass
 #define NS_IMPL_QUERY_CLASSINFO(_class)                                      \
   if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {                               \
     if (!NS_CLASSINFO_NAME(_class))                                          \
       NS_CLASSINFO_NAME(_class) = new (k##_class##ClassInfoDataPlace.addr()) \
           GenericClassInfo(&k##_class##ClassInfoData);                       \
     foundInterface = NS_CLASSINFO_NAME(_class);                              \
   } else
 
-#define NS_CLASSINFO_HELPER_BEGIN(_class, _c)                              \
-  NS_IMETHODIMP                                                            \
-  NS_CI_INTERFACE_GETTER_NAME(_class)(uint32_t * count, nsIID * **array) { \
-    *count = _c;                                                           \
-    *array = (nsIID**)moz_xmalloc(sizeof(nsIID*) * _c);                    \
-    uint32_t i = 0;
+#define NS_CLASSINFO_HELPER_BEGIN(_class, _c)                    \
+  NS_IMETHODIMP                                                  \
+  NS_CI_INTERFACE_GETTER_NAME(_class)(nsTArray<nsIID> & array) { \
+    array.Clear();                                               \
+    array.SetCapacity(_c);
 
 #define NS_CLASSINFO_HELPER_ENTRY(_interface) \
-  (*array)[i++] = NS_GET_IID(_interface).Clone();
+  array.AppendElement(NS_GET_IID(_interface));
 
-#define NS_CLASSINFO_HELPER_END                           \
-  MOZ_ASSERT(i == *count, "Incorrent number of entries"); \
-  return NS_OK;                                           \
+#define NS_CLASSINFO_HELPER_END \
+  return NS_OK;                 \
   }
 
 #define NS_IMPL_CI_INTERFACE_GETTER(aClass, ...)                       \
   static_assert(MOZ_ARG_COUNT(__VA_ARGS__) > 0,                        \
                 "Need more arguments to NS_IMPL_CI_INTERFACE_GETTER"); \
   NS_CLASSINFO_HELPER_BEGIN(aClass, MOZ_ARG_COUNT(__VA_ARGS__))        \
   MOZ_FOR_EACH(NS_CLASSINFO_HELPER_ENTRY, (), (__VA_ARGS__))           \
   NS_CLASSINFO_HELPER_END
--- a/xpcom/base/nsISupportsImpl.h
+++ b/xpcom/base/nsISupportsImpl.h
@@ -1242,50 +1242,50 @@ class Runnable;
   nsISupports* foundInterface;
 
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
  * Macro to generate nsIClassInfo methods for classes which do not have
  * corresponding nsIFactory implementations.
  */
-#define NS_IMPL_THREADSAFE_CI(_class)                           \
-  NS_IMETHODIMP                                                 \
-  _class::GetInterfaces(uint32_t* _count, nsIID*** _array) {    \
-    return NS_CI_INTERFACE_GETTER_NAME(_class)(_count, _array); \
-  }                                                             \
-                                                                \
-  NS_IMETHODIMP                                                 \
-  _class::GetScriptableHelper(nsIXPCScriptable** _retval) {     \
-    *_retval = nullptr;                                         \
-    return NS_OK;                                               \
-  }                                                             \
-                                                                \
-  NS_IMETHODIMP                                                 \
-  _class::GetContractID(nsACString& _contractID) {              \
-    _contractID.SetIsVoid(true);                                \
-    return NS_OK;                                               \
-  }                                                             \
-                                                                \
-  NS_IMETHODIMP                                                 \
-  _class::GetClassDescription(nsACString& _classDescription) {  \
-    _classDescription.SetIsVoid(true);                          \
-    return NS_OK;                                               \
-  }                                                             \
-                                                                \
-  NS_IMETHODIMP                                                 \
-  _class::GetClassID(nsCID** _classID) {                        \
-    *_classID = nullptr;                                        \
-    return NS_OK;                                               \
-  }                                                             \
-                                                                \
-  NS_IMETHODIMP                                                 \
-  _class::GetFlags(uint32_t* _flags) {                          \
-    *_flags = nsIClassInfo::THREADSAFE;                         \
-    return NS_OK;                                               \
-  }                                                             \
-                                                                \
-  NS_IMETHODIMP                                                 \
-  _class::GetClassIDNoAlloc(nsCID* _classIDNoAlloc) {           \
-    return NS_ERROR_NOT_AVAILABLE;                              \
+#define NS_IMPL_THREADSAFE_CI(_class)                          \
+  NS_IMETHODIMP                                                \
+  _class::GetInterfaces(nsTArray<nsIID>& _array) {             \
+    return NS_CI_INTERFACE_GETTER_NAME(_class)(_array);        \
+  }                                                            \
+                                                               \
+  NS_IMETHODIMP                                                \
+  _class::GetScriptableHelper(nsIXPCScriptable** _retval) {    \
+    *_retval = nullptr;                                        \
+    return NS_OK;                                              \
+  }                                                            \
+                                                               \
+  NS_IMETHODIMP                                                \
+  _class::GetContractID(nsACString& _contractID) {             \
+    _contractID.SetIsVoid(true);                               \
+    return NS_OK;                                              \
+  }                                                            \
+                                                               \
+  NS_IMETHODIMP                                                \
+  _class::GetClassDescription(nsACString& _classDescription) { \
+    _classDescription.SetIsVoid(true);                         \
+    return NS_OK;                                              \
+  }                                                            \
+                                                               \
+  NS_IMETHODIMP                                                \
+  _class::GetClassID(nsCID** _classID) {                       \
+    *_classID = nullptr;                                       \
+    return NS_OK;                                              \
+  }                                                            \
+                                                               \
+  NS_IMETHODIMP                                                \
+  _class::GetFlags(uint32_t* _flags) {                         \
+    *_flags = nsIClassInfo::THREADSAFE;                        \
+    return NS_OK;                                              \
+  }                                                            \
+                                                               \
+  NS_IMETHODIMP                                                \
+  _class::GetClassIDNoAlloc(nsCID* _classIDNoAlloc) {          \
+    return NS_ERROR_NOT_AVAILABLE;                             \
   }
 
 #endif
--- a/xpcom/components/nsIClassInfo.idl
+++ b/xpcom/components/nsIClassInfo.idl
@@ -13,25 +13,21 @@ interface nsIXPCScriptable;
  * your class to implement nsIClassInfo, see nsIClassInfoImpl.h for
  * instructions--you most likely do not want to inherit from nsIClassInfo.
  */
 
 [scriptable, uuid(a60569d7-d401-4677-ba63-2aa5971af25d)]
 interface nsIClassInfo : nsISupports
 {
     /**
-     * Get an ordered list of the interface ids that instances of the class 
-     * promise to implement. Note that nsISupports is an implicit member 
-     * of any such list and need not be included. 
-     *
-     * Should set *count = 0 and *array = null and return NS_OK if getting the 
-     * list is not supported.
+     * Returns a list of the interfaces which instances of this class promise
+     * to implement. Note that nsISupports is an implicit member of any such
+     * list, and need not be included.
      */
-    void getInterfaces(out uint32_t count, 
-                       [array, size_is(count), retval] out nsIIDPtr array);
+    readonly attribute Array<nsIIDRef> interfaces;
 
     /**
      * Return an object to assist XPConnect in supplying JavaScript-specific
      * behavior to callers of the instance object, or null if not needed.
      */
     nsIXPCScriptable getScriptableHelper();
 
     /**
--- a/xpcom/ds/nsIVariant.idl
+++ b/xpcom/ds/nsIVariant.idl
@@ -28,17 +28,17 @@ struct nsIDataType
         VTYPE_UINT32            = TD_UINT32           ,
         VTYPE_UINT64            = TD_UINT64           ,
         VTYPE_FLOAT             = TD_FLOAT            ,
         VTYPE_DOUBLE            = TD_DOUBLE           ,
         VTYPE_BOOL              = TD_BOOL             ,
         VTYPE_CHAR              = TD_CHAR             ,
         VTYPE_WCHAR             = TD_WCHAR            ,
         VTYPE_VOID              = TD_VOID             ,
-        VTYPE_ID                = TD_PNSIID           ,
+        VTYPE_ID                = TD_NSIDPTR          ,
         VTYPE_CHAR_STR          = TD_PSTRING          ,
         VTYPE_WCHAR_STR         = TD_PWSTRING         ,
         VTYPE_INTERFACE         = TD_INTERFACE_TYPE   ,
         VTYPE_INTERFACE_IS      = TD_INTERFACE_IS_TYPE,
         VTYPE_ARRAY             = TD_LEGACY_ARRAY     ,
         VTYPE_STRING_SIZE_IS    = TD_PSTRING_SIZE_IS  ,
         VTYPE_WSTRING_SIZE_IS   = TD_PWSTRING_SIZE_IS ,
         VTYPE_UTF8STRING        = TD_UTF8STRING       ,
--- a/xpcom/idl-parser/xpidl/jsonxpt.py
+++ b/xpcom/idl-parser/xpidl/jsonxpt.py
@@ -32,17 +32,17 @@ TypeMap = {
     'unsigned long long': 'TD_UINT64',
     'float':              'TD_FLOAT',
     'double':             'TD_DOUBLE',
     'char':               'TD_CHAR',
     'string':             'TD_PSTRING',
     'wchar':              'TD_WCHAR',
     'wstring':            'TD_PWSTRING',
     # special types
-    'nsid':               'TD_PNSIID',
+    'nsid':               'TD_NSID',
     'astring':            'TD_ASTRING',
     'utf8string':         'TD_UTF8STRING',
     'cstring':            'TD_CSTRING',
     'jsval':              'TD_JSVAL',
     'promise':            'TD_PROMISE',
 }
 
 
@@ -88,17 +88,19 @@ def get_type(type, calltype, iid_is=None
         return {
             'tag': 'TD_DOMOBJECT',
             'name': type.name,
             'native': type.native,
             'headerFile': type.headerFile,
         }
 
     if isinstance(type, xpidl.Native):
-        if type.specialtype:
+        if type.specialtype == 'nsid' and type.isPtr(calltype):
+            return {'tag': 'TD_NSIDPTR'}
+        elif type.specialtype:
             return {
                 'tag': TypeMap[type.specialtype]
             }
         elif iid_is is not None:
             return {
                 'tag': 'TD_INTERFACE_IS_TYPE',
                 'iid_is': iid_is,
             }
--- a/xpcom/idl-parser/xpidl/xpidl.py
+++ b/xpcom/idl-parser/xpidl/xpidl.py
@@ -555,30 +555,31 @@ class Native(object):
             else:
                 return self.nativename[2] + ' '
 
         # 'in' nsid parameters should be made 'const'
         if self.specialtype == 'nsid' and calltype == 'in':
             const = True
 
         if calltype == 'element':
+            if self.specialtype == 'nsid':
+                if self.isPtr(calltype):
+                    raise IDLError("Array<nsIDPtr> not yet supported. "
+                                   "File an XPConnect bug if you need it.", self.location)
+
+                # ns[CI]?IDs should be held directly in Array<T>s
+                return self.nativename
+
             if self.isRef(calltype):
                 raise IDLError("[ref] qualified type unsupported in Array<T>", self.location)
 
             # Promises should be held in RefPtr<T> in Array<T>s
             if self.specialtype == 'promise':
                 return 'RefPtr<mozilla::dom::Promise>'
 
-            # We don't support nsIDPtr, in Array<T> currently, although
-            # this or support for Array<nsID> will be needed to replace
-            # [array] completely.
-            if self.specialtype == 'nsid':
-                raise IDLError("Array<nsIDPtr> not yet supported. "
-                               "File an XPConnect bug if you need it.", self.location)
-
         if self.isRef(calltype):
             m = '& '  # [ref] is always passed with a single indirection
         else:
             m = '* ' if 'out' in calltype else ''
             if self.isPtr(calltype):
                 m += '* '
         return "%s%s %s" % (const and 'const ' or '', self.nativename, m)
 
--- a/xpcom/reflect/xptcall/xptcall.h
+++ b/xpcom/reflect/xptcall/xptcall.h
@@ -46,16 +46,17 @@ struct nsXPTCVariant {
     // ExtendedVal is an extension on nsXPTCMiniVariant. It contains types
     // unknown to the assembly implementations which must be passed by indirect
     // semantics.
     //
     // nsXPTCVariant contains enough space to store ExtendedVal inline, which
     // can be used to store these types when IsIndirect() is true.
     nsXPTCMiniVariant mini;
 
+    nsID nsid;
     nsCString nscstr;
     nsString nsstr;
     JS::Value jsval;
     xpt::detail::UntypedTArray array;
 
     // This type contains non-standard-layout types, so needs an explicit
     // Ctor/Dtor - we'll just delete them.
     ExtendedVal() = delete;
--- a/xpcom/reflect/xptinfo/xptcodegen.py
+++ b/xpcom/reflect/xptinfo/xptcodegen.py
@@ -166,17 +166,17 @@ utility_types = [
     {'tag': 'TD_UINT32'},
     {'tag': 'TD_INT64'},
     {'tag': 'TD_UINT64'},
     {'tag': 'TD_FLOAT'},
     {'tag': 'TD_DOUBLE'},
     {'tag': 'TD_BOOL'},
     {'tag': 'TD_CHAR'},
     {'tag': 'TD_WCHAR'},
-    {'tag': 'TD_PNSIID'},
+    {'tag': 'TD_NSIDPTR'},
     {'tag': 'TD_PSTRING'},
     {'tag': 'TD_PWSTRING'},
     {'tag': 'TD_INTERFACE_IS_TYPE', 'iid_is': 0},
 ]
 
 
 # Core of the code generator. Takes a list of raw JSON XPT interfaces, and
 # writes out a file containing the necessary static declarations into fd.
--- a/xpcom/reflect/xptinfo/xptinfo.h
+++ b/xpcom/reflect/xptinfo/xptinfo.h
@@ -160,17 +160,17 @@ enum nsXPTTypeTag : uint8_t {
   _TD_LAST_ARITHMETIC = TD_WCHAR,
 
   // Pointer Types
   //  - Require cleanup unless NULL,
   //  - All-zeros (NULL) bit pattern is valid,
   //  - Outparams may be uninitialized by caller,
   //  - Supported in xptcall as raw pointer.
   TD_VOID = 13,
-  TD_PNSIID = 14,
+  TD_NSIDPTR = 14,
   TD_PSTRING = 15,
   TD_PWSTRING = 16,
   TD_INTERFACE_TYPE = 17,
   TD_INTERFACE_IS_TYPE = 18,
   TD_LEGACY_ARRAY = 19,
   TD_PSTRING_SIZE_IS = 20,
   TD_PWSTRING_SIZE_IS = 21,
   TD_DOMOBJECT = 22,
@@ -180,18 +180,19 @@ enum nsXPTTypeTag : uint8_t {
   // Complex Types
   //  - Require cleanup,
   //  - Always passed indirectly,
   //  - Outparams must be initialized by caller,
   //  - Supported in xptcall due to indirection.
   TD_UTF8STRING = 24,
   TD_CSTRING = 25,
   TD_ASTRING = 26,
-  TD_JSVAL = 27,
-  TD_ARRAY = 28,
+  TD_NSID = 27,
+  TD_JSVAL = 28,
+  TD_ARRAY = 29,
   _TD_LAST_COMPLEX = TD_ARRAY
 };
 
 static_assert(_TD_LAST_COMPLEX < 32, "nsXPTTypeTag must fit in 5 bits");
 
 /*
  * A nsXPTType is a union used to identify the type of a method argument or
  * return value. The internal data is stored as an 5-bit tag, and two 8-bit
@@ -289,17 +290,17 @@ struct nsXPTType {
     UINT32,
     INT64,
     UINT64,
     FLOAT,
     DOUBLE,
     BOOL,
     CHAR,
     WCHAR,
-    PNSIID,
+    NSIDPTR,
     PSTRING,
     PWSTRING,
     INTERFACE_IS_TYPE
   };
 
   // Helper methods for fabricating nsXPTType values used by xpconnect.
   static nsXPTType MkArrayType(Idx aInner) {
     MOZ_ASSERT(aInner <= Idx::INTERFACE_IS_TYPE);
@@ -330,27 +331,28 @@ struct nsXPTType {
   TD_ALIAS_(T_U32, TD_UINT32);
   TD_ALIAS_(T_U64, TD_UINT64);
   TD_ALIAS_(T_FLOAT, TD_FLOAT);
   TD_ALIAS_(T_DOUBLE, TD_DOUBLE);
   TD_ALIAS_(T_BOOL, TD_BOOL);
   TD_ALIAS_(T_CHAR, TD_CHAR);
   TD_ALIAS_(T_WCHAR, TD_WCHAR);
   TD_ALIAS_(T_VOID, TD_VOID);
-  TD_ALIAS_(T_IID, TD_PNSIID);
+  TD_ALIAS_(T_NSIDPTR, TD_NSIDPTR);
   TD_ALIAS_(T_CHAR_STR, TD_PSTRING);
   TD_ALIAS_(T_WCHAR_STR, TD_PWSTRING);
   TD_ALIAS_(T_INTERFACE, TD_INTERFACE_TYPE);
   TD_ALIAS_(T_INTERFACE_IS, TD_INTERFACE_IS_TYPE);
   TD_ALIAS_(T_LEGACY_ARRAY, TD_LEGACY_ARRAY);
   TD_ALIAS_(T_PSTRING_SIZE_IS, TD_PSTRING_SIZE_IS);
   TD_ALIAS_(T_PWSTRING_SIZE_IS, TD_PWSTRING_SIZE_IS);
   TD_ALIAS_(T_UTF8STRING, TD_UTF8STRING);
   TD_ALIAS_(T_CSTRING, TD_CSTRING);
   TD_ALIAS_(T_ASTRING, TD_ASTRING);
+  TD_ALIAS_(T_NSID, TD_NSID);
   TD_ALIAS_(T_JSVAL, TD_JSVAL);
   TD_ALIAS_(T_DOMOBJECT, TD_DOMOBJECT);
   TD_ALIAS_(T_PROMISE, TD_PROMISE);
   TD_ALIAS_(T_ARRAY, TD_ARRAY);
 #undef TD_ALIAS_
 
   ////////////////////////////////////////////////////////////////
   // Ensure these fields are in the same order as xptcodegen.py //
@@ -630,31 +632,32 @@ inline const char* GetString(uint32_t aI
   MACRO(TD_FLOAT, float)                    \
   MACRO(TD_DOUBLE, double)                  \
   MACRO(TD_BOOL, bool)                      \
   MACRO(TD_CHAR, char)                      \
   MACRO(TD_WCHAR, char16_t)
 
 #define XPT_FOR_EACH_POINTER_TYPE(MACRO)    \
   MACRO(TD_VOID, void*)                     \
-  MACRO(TD_PNSIID, nsID*)                   \
+  MACRO(TD_NSIDPTR, nsID*)                  \
   MACRO(TD_PSTRING, char*)                  \
   MACRO(TD_PWSTRING, wchar_t*)              \
   MACRO(TD_INTERFACE_TYPE, nsISupports*)    \
   MACRO(TD_INTERFACE_IS_TYPE, nsISupports*) \
   MACRO(TD_LEGACY_ARRAY, void*)             \
   MACRO(TD_PSTRING_SIZE_IS, char*)          \
   MACRO(TD_PWSTRING_SIZE_IS, wchar_t*)      \
   MACRO(TD_DOMOBJECT, void*)                \
   MACRO(TD_PROMISE, mozilla::dom::Promise*)
 
 #define XPT_FOR_EACH_COMPLEX_TYPE(MACRO) \
   MACRO(TD_UTF8STRING, nsCString)        \
   MACRO(TD_CSTRING, nsCString)           \
   MACRO(TD_ASTRING, nsString)            \
+  MACRO(TD_NSID, nsID)                   \
   MACRO(TD_JSVAL, JS::Value)             \
   MACRO(TD_ARRAY, xpt::detail::UntypedTArray)
 
 #define XPT_FOR_EACH_TYPE(MACRO)      \
   XPT_FOR_EACH_ARITHMETIC_TYPE(MACRO) \
   XPT_FOR_EACH_POINTER_TYPE(MACRO)    \
   XPT_FOR_EACH_COMPLEX_TYPE(MACRO)
 
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -135,18 +135,18 @@ class nsThreadClassInfo : public nsIClas
 
 NS_IMETHODIMP_(MozExternalRefCountType)
 nsThreadClassInfo::AddRef() { return 2; }
 NS_IMETHODIMP_(MozExternalRefCountType)
 nsThreadClassInfo::Release() { return 1; }
 NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo, nsIClassInfo)
 
 NS_IMETHODIMP
-nsThreadClassInfo::GetInterfaces(uint32_t* aCount, nsIID*** aArray) {
-  return NS_CI_INTERFACE_GETTER_NAME(nsThread)(aCount, aArray);
+nsThreadClassInfo::GetInterfaces(nsTArray<nsIID>& aArray) {
+  return NS_CI_INTERFACE_GETTER_NAME(nsThread)(aArray);
 }
 
 NS_IMETHODIMP
 nsThreadClassInfo::GetScriptableHelper(nsIXPCScriptable** aResult) {
   *aResult = nullptr;
   return NS_OK;
 }