Merge mozilla-central to mozilla-inbound.
authorCosmin Sabou <csabou@mozilla.com>
Fri, 15 Feb 2019 20:54:03 +0200
changeset 459553 db3c4f90508207462b85037b1a69c563b1a6b88e
parent 459491 3122e3cad0c677d416dffe483f3226996d6798df (current diff)
parent 459552 8961019ee4c6fe27e6319d9cf9ea6867b4f3c7bb (diff)
child 459554 6fa064cb22ba0be5576eb8c101f38e3566aa511a
push id111964
push usercsabou@mozilla.com
push dateFri, 15 Feb 2019 18:54:44 +0000
treeherdermozilla-inbound@db3c4f905082 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
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 mozilla-inbound.
browser/components/urlbar/tests/legacy/browser_urlbarOneOffs.js
browser/components/urlbar/tests/legacy/browser_urlbarOneOffs_searchSuggestions.js
devtools/client/webconsole/webconsole-frame.js
docshell/test/browser/browser_browsingContext.js
toolkit/components/remotebrowserutils/tests/browser/browser_httpToFileHistory.js
toolkit/components/remotebrowserutils/tests/browser/head.js
--- a/.eslintignore
+++ b/.eslintignore
@@ -25,18 +25,16 @@ layout/**
 modules/**
 netwerk/cookie/test/browser/**
 netwerk/test/browser/**
 netwerk/test/mochitests/**
 netwerk/test/unit*/**
 tools/update-packaging/**
 uriloader/exthandler/**
 uriloader/exthandler/tests/mochitest/**
-widget/headless/tests/**
-widget/tests/**
 xpfe/**
 
 # We currently have no js files in these directories, so we ignore them by
 # default to aid ESLint's performance.
 build/**
 config/**
 db/**
 embedding/**
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3074,17 +3074,17 @@ version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "lalrpop 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lalrpop-util 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender"
-version = "0.59.0"
+version = "0.60.0"
 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)",
@@ -3102,24 +3102,24 @@ dependencies = [
  "plane-split 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
  "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "webrender_api 0.58.0",
+ "webrender_api 0.60.0",
  "webrender_build 0.0.1",
  "wr_malloc_size_of 0.0.1",
 ]
 
 [[package]]
 name = "webrender_api"
-version = "0.58.0"
+version = "0.60.0"
 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)",
  "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)",
  "derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3145,17 +3145,17 @@ dependencies = [
  "foreign-types 0.3.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)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "nsstring 0.1.0",
  "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "webrender 0.59.0",
+ "webrender 0.60.0",
 ]
 
 [[package]]
 name = "webrender_build"
 version = "0.0.1"
 dependencies = [
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
  "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -478,19 +478,16 @@ pref("browser.tabs.delayHidingAudioPlayi
 // New, experimental, tab open/close animations.
 pref("browser.tabs.newanimations", false);
 
 // Pref to control whether we use separate privileged content processes.
 #if defined(NIGHTLY_BUILD) && !defined(MOZ_ASAN)
 pref("browser.tabs.remote.separatePrivilegedContentProcess", true);
 #endif
 
-// Turn on HTTP response process selection.
-pref("browser.tabs.remote.useHTTPResponseProcessSelection", true);
-
 pref("browser.ctrlTab.recentlyUsedOrder", true);
 
 // By default, do not export HTML at shutdown.
 // If true, at shutdown the bookmarks in your menu and toolbar will
 // be exported as HTML to the bookmarks.html file.
 pref("browser.bookmarks.autoExportHTML",          false);
 
 // The maximum number of daily bookmark backups to
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2169,17 +2169,17 @@ function BrowserReloadSkipCache() {
   BrowserReloadWithFlags(reloadFlags);
 }
 
 function BrowserHome(aEvent) {
   if (aEvent && "button" in aEvent &&
       aEvent.button == 2) // right-click: do nothing
     return;
 
-  var homePage = HomePage.get(window);
+  var homePage = HomePage.get();
   var where = whereToOpenLink(aEvent, false, true);
   var urls;
   var notifyObservers;
 
   // Home page should open in a new tab when current tab is an app tab
   if (where == "current" &&
       gBrowser &&
       gBrowser.selectedTab.pinned)
--- a/browser/base/content/test/tabs/browser_new_web_tab_in_file_process_pref.js
+++ b/browser/base/content/test/tabs/browser_new_web_tab_in_file_process_pref.js
@@ -19,17 +19,16 @@ function CheckBrowserNotInPid(browser, u
 
 // Test for bug 1343184.
 add_task(async function() {
   // Set prefs to ensure file content process, to allow linked web content in
   // file URI process and allow more that one file content process.
   await SpecialPowers.pushPrefEnv(
     {set: [["browser.tabs.remote.separateFileUriProcess", true],
            ["browser.tabs.remote.allowLinkedWebInFileUriProcess", true],
-           ["browser.tabs.remote.useHTTPResponseProcessSelection", false],
            ["dom.ipc.processCount.file", 2]]});
 
   // Open file:// page.
   let dir = getChromeDir(getResolvedURI(gTestPath));
   dir.append(TEST_FILE);
   const uriString = Services.io.newFileURI(dir).spec;
   await BrowserTestUtils.withNewTab(uriString, async function(fileBrowser) {
     // Get the file:// URI pid for comparison later.
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -19,29 +19,20 @@ XPCOMUtils.defineLazyModuleGetters(this,
 
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
 Object.defineProperty(this, "BROWSER_NEW_TAB_URL", {
   enumerable: true,
   get() {
-    if (PrivateBrowsingUtils.isWindowPrivate(window)) {
-      if (!PrivateBrowsingUtils.permanentPrivateBrowsing &&
-          !aboutNewTabService.overridden) {
-        return "about:privatebrowsing";
-      }
-      // If the extension does not have private browsing permission,
-      // use about:privatebrowsing.
-      if (aboutNewTabService.newTabURL.startsWith("moz-extension")) {
-        let url = new URL(aboutNewTabService.newTabURL);
-        if (!WebExtensionPolicy.getByHostname(url.hostname).privateBrowsingAllowed) {
-          return "about:privatebrowsing";
-        }
-      }
+    if (PrivateBrowsingUtils.isWindowPrivate(window) &&
+        !PrivateBrowsingUtils.permanentPrivateBrowsing &&
+        !aboutNewTabService.overridden) {
+      return "about:privatebrowsing";
     }
     return aboutNewTabService.newTabURL;
   },
 });
 
 var TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
 
 var gBidiUI = false;
--- a/browser/components/BrowserContentHandler.jsm
+++ b/browser/components/BrowserContentHandler.jsm
@@ -204,17 +204,17 @@ function getPostUpdateOverridePage(defau
  */
 function openBrowserWindow(cmdLine, triggeringPrincipal, urlOrUrlList, postData = null,
                            forcePrivate = false) {
   let chromeURL = AppConstants.BROWSER_CHROME_URL;
 
   let args;
   if (!urlOrUrlList) {
     // Just pass in the defaultArgs directly. We'll use system principal on the other end.
-    args = [gBrowserContentHandler.getDefaultArgs(forcePrivate)];
+    args = [gBrowserContentHandler.defaultArgs];
   } else {
     let pService = Cc["@mozilla.org/toolkit/profile-service;1"].
                   getService(Ci.nsIToolkitProfileService);
     if (cmdLine && cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH &&
         pService.createdAlternateProfile) {
       let url = getNewInstallPage();
       if (Array.isArray(urlOrUrlList)) {
         urlOrUrlList.unshift(url);
@@ -513,17 +513,17 @@ nsBrowserContentHandler.prototype = {
     info += "  --window-size width[,height] Width and optionally height of screenshot.\n";
     info += "  --search <term>    Search <term> with your default search engine.\n";
     info += "  --setDefaultBrowser Set this app as the default browser.\n";
     return info;
   },
 
   /* nsIBrowserHandler */
 
-  getDefaultArgs(forcePrivate = false) {
+  get defaultArgs() {
     var prefb = Services.prefs;
 
     if (!gFirstWindow) {
       gFirstWindow = true;
       if (PrivateBrowsingUtils.isInTemporaryAutoStartMode) {
         return "about:privatebrowsing";
       }
     }
@@ -598,17 +598,17 @@ nsBrowserContentHandler.prototype = {
         overridePage = additionalPage;
       }
     }
 
     var startPage = "";
     try {
       var choice = prefb.getIntPref("browser.startup.page");
       if (choice == 1 || choice == 3)
-        startPage = forcePrivate ? HomePage.getPrivate() : HomePage.get();
+        startPage = HomePage.get();
     } catch (e) {
       Cu.reportError(e);
     }
 
     if (startPage == "about:blank")
       startPage = "";
 
     let skipStartPage = ((override == OVERRIDE_NEW_PROFILE) || (override == OVERRIDE_ALTERNATE_PROFILE)) &&
@@ -616,20 +616,16 @@ nsBrowserContentHandler.prototype = {
     // Only show the startPage if we're not restoring an update session and are
     // not set to skip the start page on this profile
     if (overridePage && startPage && !willRestoreSession && !skipStartPage)
       return overridePage + "|" + startPage;
 
     return overridePage || startPage || "about:blank";
   },
 
-  get defaultArgs() {
-    return this.getDefaultArgs(PrivateBrowsingUtils.permanentPrivateBrowsing);
-  },
-
   mFeatures: null,
 
   getFeatures: function bch_features(cmdLine) {
     if (this.mFeatures === null) {
       this.mFeatures = "";
 
       if (cmdLine) {
         try {
--- a/browser/components/extensions/ExtensionControlledPopup.jsm
+++ b/browser/components/extensions/ExtensionControlledPopup.jsm
@@ -26,18 +26,16 @@ const {XPCOMUtils} = ChromeUtils.import(
 ChromeUtils.defineModuleGetter(this, "AddonManager",
                                "resource://gre/modules/AddonManager.jsm");
 ChromeUtils.defineModuleGetter(this, "BrowserUtils",
                                "resource://gre/modules/BrowserUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "CustomizableUI",
                                "resource:///modules/CustomizableUI.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                                "resource://gre/modules/ExtensionSettingsStore.jsm");
-ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
-                               "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 let {
   makeWidgetId,
 } = ExtensionCommon;
 
 XPCOMUtils.defineLazyGetter(this, "strBundle", function() {
   return Services.strings.createBundle("chrome://global/locale/extensions.properties");
 });
@@ -192,30 +190,25 @@ class ExtensionControlledPopup {
     this.removeObserver();
 
     if (!extensionId) {
       let item = ExtensionSettingsStore.getSetting(
         this.settingType, this.settingKey);
       extensionId = item && item.id;
     }
 
-    let win = targetWindow || this.topWindow;
-    let isPrivate = PrivateBrowsingUtils.isWindowPrivate(win);
-    if (isPrivate && extensionId && !WebExtensionPolicy.getByID(extensionId).privateBrowsingAllowed) {
-      return;
-    }
-
     // The item should have an extension and the user shouldn't have confirmed
     // the change here, but just to be sure check that it is still controlled
     // and the user hasn't already confirmed the change.
     // If there is no id, then the extension is no longer in control.
     if (!extensionId || this.userHasConfirmed(extensionId)) {
       return;
     }
 
+    let win = targetWindow || this.topWindow;
     // If the window closes while waiting for focus, this might reject/throw,
     // and we should stop trying to show the popup.
     try {
       await this._ensureWindowReady(win);
     } catch (ex) {
       return;
     }
 
--- a/browser/components/extensions/parent/ext-windows.js
+++ b/browser/components/extensions/parent/ext-windows.js
@@ -186,23 +186,18 @@ this.windows = class extends ExtensionAP
               for (let url of createData.url) {
                 array.appendElement(mkstr(url));
               }
               args.appendElement(array);
             } else {
               args.appendElement(mkstr(createData.url));
             }
           } else {
-            let url;
-            if (createData.incognito) {
-              url = PrivateBrowsingUtils.permanentPrivateBrowsing ?
-                HomePage.getPrivate().split("|", 1)[0] : "about:privatebrowsing";
-            } else {
-              url = HomePage.get().split("|", 1)[0];
-            }
+            let url = createData.incognito && !PrivateBrowsingUtils.permanentPrivateBrowsing ?
+              "about:privatebrowsing" : HomePage.get().split("|", 1)[0];
             args.appendElement(mkstr(url));
 
             if (url.startsWith("about:") &&
                 !context.checkLoadURL(url, {dontReportErrors: true})) {
               // The extension principal cannot directly load about:-URLs,
               // except for about:blank. So use the system principal instead.
               principal = Services.scriptSecurityManager.getSystemPrincipal();
             }
--- a/browser/components/extensions/test/browser/browser_ext_chrome_settings_overrides_home.js
+++ b/browser/components/extensions/test/browser/browser_ext_chrome_settings_overrides_home.js
@@ -417,124 +417,8 @@ add_task(async function test_doorhanger_
   ok(getHomePageURL().endsWith("ext1.html"), "The homepage is still the set");
 
   await BrowserTestUtils.closeWindow(win);
   await ext1.unload();
   await ext2.unload();
 
   ok(!isConfirmed(ext1Id), "The confirmation is cleaned up on uninstall");
 });
-
-add_task(async function test_overriding_home_page_incognito_not_allowed() {
-  await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});
-
-  let extension = ExtensionTestUtils.loadExtension({
-    manifest: {
-      chrome_settings_overrides: {"homepage": "home.html"},
-      name: "extension",
-    },
-    background() {
-      browser.test.sendMessage("url", browser.runtime.getURL("home.html"));
-    },
-    files: {"home.html": "<h1>1</h1>"},
-    useAddonManager: "temporary",
-  });
-
-  await extension.startup();
-  let url = await extension.awaitMessage("url");
-
-  let windowOpenedPromise = BrowserTestUtils.waitForNewWindow({url});
-  let win = OpenBrowserWindow();
-  await windowOpenedPromise;
-  let doc = win.document;
-  let description = doc.getElementById("extension-homepage-notification-description");
-  let panel = doc.getElementById("extension-notification-panel");
-  await promisePopupShown(panel);
-
-  let popupnotification = description.closest("popupnotification");
-  is(description.textContent,
-     "An extension,  extension, changed what you see when you open your homepage and new windows.Learn more",
-     "The extension name is in the popup");
-  is(popupnotification.hidden, false, "The expected popup notification is visible");
-
-  ok(win.gURLBar.value.endsWith("home.html"), "extension is in control");
-  await BrowserTestUtils.closeWindow(win);
-
-  // Verify a private window does not open the extension page.
-  windowOpenedPromise = BrowserTestUtils.waitForNewWindow();
-  win = OpenBrowserWindow({private: true});
-  await windowOpenedPromise;
-  win.BrowserHome();
-  await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
-
-  is(win.gURLBar.value, "", "home page not used in private window");
-
-  await extension.unload();
-  await BrowserTestUtils.closeWindow(win);
-});
-
-add_task(async function test_overriding_home_page_incognito_not_allowed_bypass() {
-  await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});
-
-  let extension = ExtensionTestUtils.loadExtension({
-    manifest: {
-      name: "extension",
-    },
-    background() {
-      browser.test.sendMessage("url", browser.runtime.getURL("home.html"));
-    },
-    files: {"home.html": "<h1>1</h1>"},
-    useAddonManager: "temporary",
-  });
-
-  await extension.startup();
-  let url = await extension.awaitMessage("url");
-
-  // Verify manually setting the pref to the extension page does not work.
-  let changed = promisePrefChangeObserved(HOMEPAGE_URL_PREF);
-  Services.prefs.setStringPref(HOMEPAGE_URL_PREF, url);
-  await changed;
-  let windowOpenedPromise = BrowserTestUtils.waitForNewWindow();
-  let win = OpenBrowserWindow({private: true});
-  await windowOpenedPromise;
-  win.BrowserHome();
-  await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
-
-  is(win.gURLBar.value, "", "home page not used in private window");
-  changed = promisePrefChangeObserved(HOMEPAGE_URL_PREF);
-  Services.prefs.clearUserPref(HOMEPAGE_URL_PREF);
-  await changed;
-
-  await extension.unload();
-  await BrowserTestUtils.closeWindow(win);
-});
-
-add_task(async function test_overriding_home_page_incognito_spanning() {
-  await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});
-
-  let extension = ExtensionTestUtils.loadExtension({
-    manifest: {
-      chrome_settings_overrides: {"homepage": "home.html"},
-      name: "private extension",
-      applications: {
-        gecko: {id: "@spanning-home"},
-      },
-    },
-    files: {"home.html": "<h1>1</h1>"},
-    useAddonManager: "permanent",
-    incognitoOverride: "spanning",
-  });
-
-  await extension.startup();
-
-  let windowOpenedPromise = BrowserTestUtils.waitForNewWindow();
-  let win = OpenBrowserWindow({private: true});
-  await windowOpenedPromise;
-  let doc = win.document;
-  let panel = doc.getElementById("extension-notification-panel");
-  win.BrowserHome();
-  await promisePopupShown(panel);
-
-  ok(win.gURLBar.value.endsWith("home.html"), "extension is in control in private window");
-
-  await extension.unload();
-  await BrowserTestUtils.closeWindow(win);
-});
--- a/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_eval_bindings.js
+++ b/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_eval_bindings.js
@@ -105,24 +105,23 @@ add_task(async function test_devtools_in
   await inspectorPanelSelectedPromise;
   info("Toolbox has been switched to the inspector as expected");
 
   info("Test inspectedWindow.eval inspect() binding called for a JS object");
 
   const splitPanelOpenedPromise = (async () => {
     await toolbox.once("split-console");
     const {hud} = toolbox.getPanel("webconsole");
-    let {jsterm} = hud;
 
     // Wait for the message to appear on the console.
     const messageNode = await new Promise(resolve => {
-      jsterm.hud.on("new-messages", function onThisMessage(messages) {
+      hud.ui.on("new-messages", function onThisMessage(messages) {
         for (let m of messages) {
           resolve(m.node);
-          jsterm.hud.off("new-messages", onThisMessage);
+          hud.ui.off("new-messages", onThisMessage);
           return;
         }
       });
     });
     let objectInspectors = [...messageNode.querySelectorAll(".tree")];
     is(objectInspectors.length, 1, "There is the expected number of object inspectors");
 
     // We need to wait for the object to be expanded so we don't call the server on a closed connection.
--- a/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
+++ b/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
@@ -1,18 +1,15 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 
 "use strict";
 
 ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                                "resource://gre/modules/ExtensionSettingsStore.jsm");
-XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
-                                   "@mozilla.org/browser/aboutnewtab-service;1",
-                                   "nsIAboutNewTabService");
 
 const NEWTAB_URI_1 = "webext-newtab-1.html";
 
 function getNotificationSetting(extensionId) {
   return ExtensionSettingsStore.getSetting("newTabNotification", extensionId);
 }
 
 function getNewTabDoorhanger() {
@@ -520,194 +517,8 @@ add_task(async function dontTemporarilyS
   is(gURLBar.value, "", "URL bar value should be empty.");
   ContentTask.spawn(tab.linkedBrowser, null, function() {
     is(content.document.body.textContent, "New tab!", "New tab page is loaded.");
   });
 
   BrowserTestUtils.removeTab(tab);
   await extension.unload();
 });
-
-add_task(async function test_overriding_newtab_incognito_not_allowed() {
-  await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});
-
-  let panel = getNewTabDoorhanger().closest("panel");
-
-  let extension = ExtensionTestUtils.loadExtension({
-    manifest: {
-      chrome_url_overrides: {"newtab": "newtab.html"},
-      name: "extension",
-      applications: {
-        gecko: {id: "@not-allowed-newtab"},
-      },
-    },
-    files: {
-      "newtab.html": `
-        <!DOCTYPE html>
-        <head>
-          <meta charset="utf-8"/></head>
-        <html>
-          <body>
-            <script src="newtab.js"></script>
-          </body>
-        </html>
-      `,
-
-      "newtab.js": function() {
-        window.onload = () => {
-          browser.test.sendMessage("from-newtab-page", window.location.href);
-        };
-      },
-    },
-    useAddonManager: "permanent",
-  });
-
-  await extension.startup();
-
-  let popupShown = promisePopupShown(panel);
-  BrowserOpenTab();
-  await popupShown;
-
-  let url = await extension.awaitMessage("from-newtab-page");
-  ok(url.endsWith("newtab.html"),
-     "Newtab url is overridden by the extension.");
-
-  // This will show a confirmation doorhanger, make sure we don't leave it open.
-  let popupHidden = promisePopupHidden(panel);
-  panel.hidePopup();
-  await popupHidden;
-
-  BrowserTestUtils.removeTab(gBrowser.selectedTab);
-
-  // Verify a private window does not open the extension page.  We would
-  // get an extra notification that we don't listen for if it gets loaded.
-  let windowOpenedPromise = BrowserTestUtils.waitForNewWindow();
-  let win = OpenBrowserWindow({private: true});
-  await windowOpenedPromise;
-
-  let newTabOpened = waitForNewTab();
-  win.BrowserOpenTab();
-  await newTabOpened;
-
-  is(win.gURLBar.value, "", "newtab not used in private window");
-
-  // Verify setting the pref directly doesn't bypass permissions.
-  let origUrl = aboutNewTabService.newTabURL;
-  aboutNewTabService.newTabURL = url;
-  newTabOpened = waitForNewTab();
-  win.BrowserOpenTab();
-  await newTabOpened;
-
-  is(win.gURLBar.value, "", "directly set newtab not used in private window");
-
-  aboutNewTabService.newTabURL = origUrl;
-
-  await extension.unload();
-  await BrowserTestUtils.closeWindow(win);
-});
-
-add_task(async function test_overriding_newtab_incognito_not_allowed_bypass() {
-  await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});
-
-  let extension = ExtensionTestUtils.loadExtension({
-    manifest: {
-      name: "extension",
-      applications: {
-        gecko: {id: "@not-allowed-newtab"},
-      },
-    },
-    background() {
-      browser.test.sendMessage("url", browser.runtime.getURL("newtab.html"));
-    },
-    files: {
-      "newtab.html": `
-        <!DOCTYPE html>
-        <head>
-          <meta charset="utf-8"/></head>
-        <html>
-          <body>
-          </body>
-        </html>
-      `,
-    },
-    useAddonManager: "permanent",
-  });
-
-  await extension.startup();
-  let url = await extension.awaitMessage("url");
-
-  // Verify setting the pref directly doesn't bypass permissions.
-  let origUrl = aboutNewTabService.newTabURL;
-  aboutNewTabService.newTabURL = url;
-
-  // Verify a private window does not open the extension page.  We would
-  // get an extra notification that we don't listen for if it gets loaded.
-  let windowOpenedPromise = BrowserTestUtils.waitForNewWindow();
-  let win = OpenBrowserWindow({private: true});
-  await windowOpenedPromise;
-
-  let newTabOpened = waitForNewTab();
-  win.BrowserOpenTab();
-  await newTabOpened;
-
-  is(win.gURLBar.value, "", "directly set newtab not used in private window");
-
-  aboutNewTabService.newTabURL = origUrl;
-
-  await extension.unload();
-  await BrowserTestUtils.closeWindow(win);
-});
-
-add_task(async function test_overriding_newtab_incognito_spanning() {
-  await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});
-
-  let extension = ExtensionTestUtils.loadExtension({
-    manifest: {
-      chrome_url_overrides: {"newtab": "newtab.html"},
-      name: "extension",
-      applications: {
-        gecko: {id: "@spanning-newtab"},
-      },
-    },
-    files: {
-      "newtab.html": `
-        <!DOCTYPE html>
-        <head>
-          <meta charset="utf-8"/></head>
-        <html>
-          <body>
-            <script src="newtab.js"></script>
-          </body>
-        </html>
-      `,
-
-      "newtab.js": function() {
-        window.onload = () => {
-          browser.test.sendMessage("from-newtab-page", window.location.href);
-        };
-      },
-    },
-    useAddonManager: "permanent",
-    incognitoOverride: "spanning",
-  });
-
-  await extension.startup();
-
-  let windowOpenedPromise = BrowserTestUtils.waitForNewWindow();
-  let win = OpenBrowserWindow({private: true});
-  await windowOpenedPromise;
-  let panel = win.document.getElementById("extension-new-tab-notification").closest("panel");
-  let popupShown = promisePopupShown(panel);
-  win.BrowserOpenTab();
-  await popupShown;
-
-  let url = await extension.awaitMessage("from-newtab-page");
-  ok(url.endsWith("newtab.html"),
-     "Newtab url is overridden by the extension.");
-
-  // This will show a confirmation doorhanger, make sure we don't leave it open.
-  let popupHidden = promisePopupHidden(panel);
-  panel.hidePopup();
-  await popupHidden;
-
-  await extension.unload();
-  await BrowserTestUtils.closeWindow(win);
-});
--- a/browser/components/resistfingerprinting/test/browser/head.js
+++ b/browser/components/resistfingerprinting/test/browser/head.js
@@ -200,16 +200,26 @@ async function testWindowSizeSetting(aBr
 
         if (input.testOuter) {
           win.outerWidth = input.settingWidth;
         } else {
           win.innerWidth = input.settingWidth;
         }
       });
 
+      win.close();
+      // Open a new window and wait until it loads.
+      await new Promise(resolve => {
+        // Given a initial window size which should be different from target
+        // size. We need this to trigger 'onresize' event.
+        let initWinFeatures = "width=" + input.initWidth + ",height=" + input.initHeight;
+        win = content.open("http://example.net/", "", initWinFeatures);
+        win.onload = () => resolve();
+      });
+
       // Test inner/outerHeight.
       await new Promise(resolve => {
         win.addEventListener("resize", () => {
           is(win.screen.height, input.targetHeight,
             "The screen.height has a correct rounded value");
           is(win.innerHeight, input.targetHeight,
             "The window.innerHeight has a correct rounded value");
 
--- a/browser/components/sessionstore/ContentRestore.jsm
+++ b/browser/components/sessionstore/ContentRestore.jsm
@@ -181,18 +181,17 @@ ContentRestoreInternal.prototype = {
       webNavigation.setCurrentURI(Services.io.newURI("about:blank"));
     }
 
     try {
       if (loadArguments) {
         // If the load was started in another process, and the in-flight channel
         // was redirected into this process, resume that load within our process.
         if (loadArguments.redirectLoadSwitchId) {
-          webNavigation.resumeRedirectedLoad(loadArguments.redirectLoadSwitchId,
-                                             loadArguments.redirectHistoryIndex);
+          webNavigation.resumeRedirectedLoad(loadArguments.redirectLoadSwitchId);
           return true;
         }
 
         // A load has been redirected to a new process so get history into the
         // same state it was before the load started then trigger the load.
         // Referrer information is now stored as a referrerInfo property. We
         // should also cope with the old format of passing `referrer` and
         // `referrerPolicy` separately.
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -43,17 +43,19 @@ const SCREEN_EDGE_SLOP = 8;
 
 // global notifications observed
 const OBSERVING = [
   "browser-window-before-show", "domwindowclosed",
   "quit-application-granted", "browser-lastwindow-close-granted",
   "quit-application", "browser:purge-session-history",
   "browser:purge-session-history-for-domain",
   "idle-daily", "clear-origin-attributes-data",
-  "http-on-may-change-process",
+  "http-on-examine-response",
+  "http-on-examine-merged-response",
+  "http-on-examine-cached-response",
 ];
 
 // XUL Window properties to (re)store
 // Restored in restoreDimensions()
 const WINDOW_ATTRIBUTES = ["width", "height", "screenX", "screenY", "sizemode"];
 
 // Hideable window features to (re)store
 // Restored in restoreWindowFeatures()
@@ -807,18 +809,20 @@ var SessionStoreInternal = {
         let userContextId = 0;
         try {
           userContextId = JSON.parse(aData).userContextId;
         } catch (e) {}
         if (userContextId) {
           this._forgetTabsWithUserContextId(userContextId);
         }
         break;
-      case "http-on-may-change-process":
-        this.onMayChangeProcess(aSubject);
+      case "http-on-examine-response":
+      case "http-on-examine-cached-response":
+      case "http-on-examine-merged-response":
+        this.onExamineResponse(aSubject);
         break;
     }
   },
 
   /**
    * This method handles incoming messages sent by the session store content
    * script via the Frame Message Manager or Parent Process Message Manager,
    * and thus enables communication with OOP tabs.
@@ -2277,18 +2281,16 @@ var SessionStoreInternal = {
   },
 
   /**
    * Perform a destructive process switch into a distinct process.
    * This method is asynchronous, as it requires multiple calls into content
    * processes.
    */
   async _doProcessSwitch(aBrowser, aRemoteType, aChannel, aSwitchId) {
-    debug(`[process-switch]: performing switch from ${aBrowser.remoteType} to ${aRemoteType}`);
-
     // Don't try to switch tabs before delayed startup is completed.
     await aBrowser.ownerGlobal.delayedStartupPromise;
 
     // Perform a navigateAndRestore to trigger the process switch.
     let tab = aBrowser.ownerGlobal.gBrowser.getTabForBrowser(aBrowser);
     let loadArguments = {
       newFrameloader: true,  // Switch even if remoteType hasn't changed.
       remoteType: aRemoteType,  // Don't derive remoteType to switch to.
@@ -2302,109 +2304,73 @@ var SessionStoreInternal = {
     // If the process switch seems to have failed, send an error over to our
     // caller, to give it a chance to kill our channel.
     if (aBrowser.remoteType != aRemoteType ||
         !aBrowser.frameLoader || !aBrowser.frameLoader.tabParent) {
       throw Cr.NS_ERROR_FAILURE;
     }
 
     // Tell our caller to redirect the load into this newly created process.
-    let tabParent = aBrowser.frameLoader.tabParent;
-    debug(`[process-switch]: new tabID: ${tabParent.tabId}`);
-    return tabParent;
+    return aBrowser.frameLoader.tabParent;
   },
 
   // Examine the channel response to see if we should change the process
   // performing the given load.
-  onMayChangeProcess(aChannel) {
+  onExamineResponse(aChannel) {
     if (!E10SUtils.useHttpResponseProcessSelection()) {
       return;
     }
 
     if (!aChannel.isDocument || !aChannel.loadInfo) {
       return; // Not a document load.
     }
 
-    // Check that this is a toplevel document load.
-    let cpType = aChannel.loadInfo.externalContentPolicyType;
-    let toplevel = cpType == Ci.nsIContentPolicy.TYPE_DOCUMENT;
-    if (!toplevel) {
-      debug(`[process-switch]: non-toplevel - ignoring`);
-      return;
-    }
-
-    // Check that the document has a corresponding BrowsingContext.
-    let browsingContext = toplevel
-        ? aChannel.loadInfo.browsingContext
-        : aChannel.loadInfo.frameBrowsingContext;
+    let browsingContext = aChannel.loadInfo.browsingContext;
     if (!browsingContext) {
-      debug(`[process-switch]: no BrowsingContext - ignoring`);
-      return;
+      return; // Not loading in a browsing context.
+    }
+
+    if (browsingContext.parent) {
+      return; // Not a toplevel load, can't flip procs.
     }
 
     // Get principal for a document already loaded in the BrowsingContext.
     let currentPrincipal = null;
     if (browsingContext.currentWindowGlobal) {
       currentPrincipal = browsingContext.currentWindowGlobal.documentPrincipal;
     }
 
-    // Ensure we have an nsIParentChannel listener for a remote load.
-    let parentChannel;
-    try {
-      parentChannel = aChannel.notificationCallbacks
-                              .getInterface(Ci.nsIParentChannel);
-    } catch (e) {
-      debug(`[process-switch]: No nsIParentChannel callback - ignoring`);
-      return;
-    }
-
-    // Ensure we have a nsITabParent for our remote load.
-    let tabParent;
-    try {
-      tabParent = parentChannel.QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsITabParent);
-    } catch (e) {
-      debug(`[process-switch]: No nsITabParent for channel - ignoring`);
-      return;
-    }
-
-    // Ensure we're loaded in a regular tabbrowser environment, and can swap processes.
+    let parentChannel = aChannel.notificationCallbacks
+                                .getInterface(Ci.nsIParentChannel);
+    if (!parentChannel) {
+      return; // Not an actor channel
+    }
+
+    let tabParent = parentChannel.QueryInterface(Ci.nsIInterfaceRequestor)
+                                 .getInterface(Ci.nsITabParent);
+    if (!tabParent || !tabParent.ownerElement) {
+      console.warn("warning: Missing tabParent");
+      return; // Not an embedded browsing context
+    }
+
     let browser = tabParent.ownerElement;
-    if (!browser) {
-      debug(`[process-switch]: TabParent has no ownerElement - ignoring`);
-    }
-
-    let tabbrowser = browser.ownerGlobal.gBrowser;
-    if (!tabbrowser) {
-      debug(`[process-switch]: cannot find tabbrowser for loading tab - ignoring`);
-      return;
-    }
-
-    let tab = tabbrowser.getTabForBrowser(browser);
-    if (!tab) {
-      debug(`[process-switch]: not a normal tab, so cannot swap processes - ignoring`);
-      return;
-    }
-
-    // Determine the process type the load should be performed in.
+    if (browser.tagName !== "browser") {
+      console.warn("warning: Not a xul:browser element:", browser.tagName);
+      return; // Not a vanilla xul:browser element performing embedding.
+    }
+
     let resultPrincipal =
       Services.scriptSecurityManager.getChannelResultPrincipal(aChannel);
+    let useRemoteTabs = browser.ownerGlobal.gMultiProcessBrowser;
     let remoteType = E10SUtils.getRemoteTypeForPrincipal(resultPrincipal,
-                                                         true,
+                                                         useRemoteTabs,
                                                          browser.remoteType,
                                                          currentPrincipal);
     if (browser.remoteType == remoteType) {
-      debug(`[process-switch]: type (${remoteType}) is compatible - ignoring`);
-      return;
-    }
-
-    if (remoteType == E10SUtils.NOT_REMOTE ||
-        browser.remoteType == E10SUtils.NOT_REMOTE) {
-      debug(`[process-switch]: non-remote source/target - ignoring`);
-      return;
+      return; // Already in compatible process.
     }
 
     // ------------------------------------------------------------------------
     // DANGER ZONE: Perform a process switch into the new process. This is
     // destructive.
     // ------------------------------------------------------------------------
     let identifier = ++this._switchIdMonotonic;
     let tabPromise = this._doProcessSwitch(browser, remoteType,
@@ -3260,24 +3226,16 @@ var SessionStoreInternal = {
       restoreContentReason: RESTORE_TAB_CONTENT_REASON.NAVIGATE_AND_RESTORE,
     };
 
     if (historyIndex >= 0) {
       tabState.index = historyIndex + 1;
       tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length));
     } else {
       options.loadArguments = loadArguments;
-
-      // If we're resuming a load which has been redirected from another
-      // process, record the history index which is currently being requested.
-      // It has to be offset by 1 to get back to native history indices from
-      // SessionStore history indicies.
-      if (loadArguments.redirectLoadSwitchId) {
-        loadArguments.redirectHistoryIndex = tabState.requestedIndex - 1;
-      }
     }
 
     // Need to reset restoring tabs.
     if (TAB_STATE_FOR_BROWSER.has(tab.linkedBrowser)) {
       this._resetLocalTabRestoringState(tab);
     }
 
     // Restore the state into the tab.
@@ -3336,17 +3294,17 @@ var SessionStoreInternal = {
     // home pages then we'll end up overwriting all of them. Otherwise we'll
     // just close the tabs that match home pages. Tabs with the about:blank
     // URI will always be overwritten.
     let homePages = ["about:blank"];
     let removableTabs = [];
     let tabbrowser = aWindow.gBrowser;
     let startupPref = this._prefBranch.getIntPref("startup.page");
     if (startupPref == 1)
-      homePages = homePages.concat(HomePage.get(aWindow).split("|"));
+      homePages = homePages.concat(HomePage.get().split("|"));
 
     for (let i = tabbrowser._numPinnedTabs; i < tabbrowser.tabs.length; i++) {
       let tab = tabbrowser.tabs[i];
       if (homePages.includes(tab.linkedBrowser.currentURI.spec)) {
         removableTabs.push(tab);
       }
     }
 
--- a/browser/components/sessionstore/TabState.jsm
+++ b/browser/components/sessionstore/TabState.jsm
@@ -179,18 +179,14 @@ var TabStateInternal = {
 
         if (value.hasOwnProperty("userContextId")) {
           tabData.userContextId = value.userContextId;
         }
 
         if (value.hasOwnProperty("index")) {
           tabData.index = value.index;
         }
-
-        if (value.hasOwnProperty("requestedIndex")) {
-          tabData.requestedIndex = value.requestedIndex;
-        }
       } else {
         tabData[key] = value;
       }
     }
   },
 };
--- a/browser/components/urlbar/UrlbarController.jsm
+++ b/browser/components/urlbar/UrlbarController.jsm
@@ -12,16 +12,17 @@ const {XPCOMUtils} = ChromeUtils.import(
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 XPCOMUtils.defineLazyModuleGetters(this, {
   AppConstants: "resource://gre/modules/AppConstants.jsm",
   // BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
   PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
   UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
   UrlbarProvidersManager: "resource:///modules/UrlbarProvidersManager.jsm",
   UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
+  URLBAR_SELECTED_RESULT_TYPES: "resource:///modules/BrowserUsageTelemetry.jsm",
 });
 
 const TELEMETRY_1ST_RESULT = "PLACES_AUTOCOMPLETE_1ST_RESULT_TIME_MS";
 const TELEMETRY_6_FIRST_RESULTS = "PLACES_AUTOCOMPLETE_6_FIRST_RESULTS_TIME_MS";
 
 /**
  * The address bar controller handles queries from the address bar, obtains
  * results and returns them to the UI for display.
@@ -310,16 +311,74 @@ class UrlbarController {
       }
       default: {
         throw new Error("Invalid speculative connection reason");
       }
     }
   }
 
   /**
+   * Records details of the selected result in telemetry. We only record the
+   * type and index here,
+   * @param {Event} event The event which triggered the result to be selected.
+   * @param {UrlbarResult} result The result that was selected.
+   * @param {number} index The index of the result.
+   */
+  recordSelectedResult(event, result, index) {
+    let telemetryType;
+    switch (result.type) {
+      case UrlbarUtils.RESULT_TYPE.TAB_SWITCH:
+        telemetryType = "switchtab";
+        break;
+      case UrlbarUtils.RESULT_TYPE.SEARCH:
+        telemetryType = result.payload.suggestion ? "searchsuggestion" : "searchengine";
+        break;
+      case UrlbarUtils.RESULT_TYPE.URL:
+        if (result.autofill) {
+          telemetryType = "autofill";
+        } else if (result.source == UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL &&
+                   result.heuristic) {
+          telemetryType = "visiturl";
+        } else {
+          telemetryType = result.source == UrlbarUtils.RESULT_SOURCE.BOOKMARKS ? "bookmark" : "history";
+        }
+        break;
+      case UrlbarUtils.RESULT_TYPE.KEYWORD:
+        telemetryType = "keyword";
+        break;
+      case UrlbarUtils.RESULT_TYPE.OMNIBOX:
+        telemetryType = "extension";
+        break;
+      case UrlbarUtils.RESULT_TYPE.REMOTE_TAB:
+        telemetryType = "remotetab";
+        break;
+      default:
+        Cu.reportError(`Unknown Result Type ${result.type}`);
+        return;
+    }
+
+    Services.telemetry
+            .getHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX")
+            .add(index);
+    // You can add values but don't change any of the existing values.
+    // Otherwise you'll break our data.
+    if (telemetryType in URLBAR_SELECTED_RESULT_TYPES) {
+      Services.telemetry
+              .getHistogramById("FX_URLBAR_SELECTED_RESULT_TYPE")
+              .add(URLBAR_SELECTED_RESULT_TYPES[telemetryType]);
+      Services.telemetry
+              .getKeyedHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX_BY_TYPE")
+              .add(telemetryType, index);
+    } else {
+      Cu.reportError("Unknown FX_URLBAR_SELECTED_RESULT_TYPE type: " +
+                     telemetryType);
+    }
+  }
+
+  /**
    * Internal function handling deletion of entries. We only support removing
    * of history entries - other result sources will be ignored.
    *
    * @returns {boolean} Returns true if the deletion was acted upon.
    */
   _handleDeleteEntry() {
     if (!this._lastQueryContext) {
       Cu.reportError("Cannot delete - the latest query is not present");
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -55,19 +55,20 @@ class UrlbarInput {
       browserWindow: this.window,
     });
     this.controller.setInput(this);
     this.view = new UrlbarView(this);
     this.valueIsTyped = false;
     this.userInitiatedFocus = false;
     this.isPrivate = PrivateBrowsingUtils.isWindowPrivate(this.window);
     this.lastQueryContextPromise = Promise.resolve();
-    this._untrimmedValue = "";
+    this._actionOverrideKeyCount = 0;
+    this._resultForCurrentValue = null;
     this._suppressStartQuery = false;
-    this._actionOverrideKeyCount = 0;
+    this._untrimmedValue = "";
 
     // Forward textbox methods and properties.
     const METHODS = ["addEventListener", "removeEventListener",
       "setAttribute", "hasAttribute", "removeAttribute", "getAttribute",
       "select"];
     const READ_ONLY_PROPERTIES = ["inputField", "editor"];
     const READ_WRITE_PROPERTIES = ["placeholder", "readOnly",
       "selectionStart", "selectionEnd"];
@@ -234,19 +235,19 @@ class UrlbarInput {
       if (selectedOneOff && !selectedOneOff.engine) {
         selectedOneOff.doCommand();
         return;
       }
     }
 
     // Use the selected result if we have one; this is usually the case
     // when the view is open.
-    let result = !selectedOneOff && this.view.selectedResult;
-    if (result) {
-      this.pickResult(event, result);
+    let index = this.view.selectedIndex;
+    if (!selectedOneOff && index != -1) {
+      this.pickResult(event, index);
       return;
     }
 
     let url;
     if (selectedOneOff) {
       // If there's a selected one-off button then load a search using
       // the button's engine.
       [url, openParams.postData] = UrlbarUtils.getSearchQueryUrl(
@@ -301,27 +302,29 @@ class UrlbarInput {
       this.select();
     }
   }
 
   /**
    * Called by the view when a result is picked.
    *
    * @param {Event} event The event that picked the result.
-   * @param {UrlbarResult} result The result that was picked.
+   * @param {resultIndex} resultIndex The index of the result that was picked.
    */
-  pickResult(event, result) {
+  pickResult(event, resultIndex) {
+    let result = this.view.getResult(resultIndex);
     this.setValueFromResult(result);
 
     this.view.close();
 
-    // TODO: Work out how we get the user selection behavior, probably via passing
+    // TODO Bug 1500476: Work out how we get the user selection behavior, probably via passing
     // it in, since we don't have the old autocomplete controller to work with.
     // BrowserUsageTelemetry.recordUrlbarSelectedResultMethod(
     //   event, this.userSelectionBehavior);
+    this.controller.recordSelectedResult(event, result, resultIndex);
 
     let where = this._whereToOpen(event);
     let {url, postData} = UrlbarUtils.getUrlFromResult(result);
     let openParams = {
       postData,
       allowInheritPrincipal: false,
     };
 
@@ -390,16 +393,17 @@ class UrlbarInput {
    * @param {UrlbarResult} result The result that was selected.
    */
   setValueFromResult(result) {
     if (result.autofill) {
       this._setValueFromResultAutofill(result);
     } else {
       this.value = this._valueFromResultPayload(result);
     }
+    this._resultForCurrentValue = result;
 
     // Also update userTypedValue. See bug 287996.
     this.window.gBrowser.userTypedValue = this.value;
 
     // The value setter clobbers the actiontype attribute, so update this after that.
     switch (result.type) {
       case UrlbarUtils.RESULT_TYPE.TAB_SWITCH:
         this.setAttribute("actiontype", "switchtab");
@@ -515,16 +519,17 @@ class UrlbarInput {
     let originalUrl = ReaderMode.getOriginalUrlObjectForDisplay(val);
     if (originalUrl) {
       val = originalUrl.displaySpec;
     }
 
     val = this.trimValue(val);
 
     this.valueIsTyped = false;
+    this._resultForCurrentValue = null;
     this.inputField.value = val;
     this.formatValue();
     this.removeAttribute("actiontype");
 
     // Dispatch ValueChange event for accessibility.
     let event = this.document.createEvent("Events");
     event.initEvent("ValueChange", true, true);
     this.inputField.dispatchEvent(event);
@@ -588,19 +593,16 @@ class UrlbarInput {
     if (this.focused || !this._overflowing) {
       this.inputField.removeAttribute("title");
     } else {
       this.inputField.setAttribute("title", this.value);
     }
   }
 
   _getSelectedValueForClipboard() {
-    // Grab the actual input field's value, not our value, which could
-    // include "moz-action:".
-    let inputVal = this.inputField.value;
     let selection = this.editor.selection;
     const flags = Ci.nsIDocumentEncoder.OutputPreformatted |
                   Ci.nsIDocumentEncoder.OutputRaw;
     let selectedVal = selection.toStringWithFormat("text/plain", flags, 0);
 
     // Handle multiple-range selection as a string for simplicity.
     if (selection.rangeCount > 1) {
       return selectedVal;
@@ -611,42 +613,52 @@ class UrlbarInput {
     // nothing else to do here.
     if (this.selectionStart > 0 || this.valueIsTyped || selectedVal == "") {
       return selectedVal;
     }
 
     // The selection doesn't span the full domain if it doesn't contain a slash and is
     // followed by some character other than a slash.
     if (!selectedVal.includes("/")) {
-      let remainder = inputVal.replace(selectedVal, "");
+      let remainder = this.textValue.replace(selectedVal, "");
       if (remainder != "" && remainder[0] != "/") {
         return selectedVal;
       }
     }
 
     let uri;
     if (this.getAttribute("pageproxystate") == "valid") {
       uri = this.window.gBrowser.currentURI;
     } else {
-      // We're dealing with an autocompleted value, create a new URI from that.
+      // We're dealing with an autocompleted value.
+      if (!this._resultForCurrentValue) {
+        throw new Error("UrlbarInput: Should have a UrlbarResult since " +
+                        "pageproxystate != 'valid' and valueIsTyped == false");
+      }
+      let resultURL = this._resultForCurrentValue.payload.url;
+      if (!resultURL) {
+        return selectedVal;
+      }
+
       try {
-        uri = Services.uriFixup.createFixupURI(inputVal, Services.uriFixup.FIXUP_FLAG_NONE);
+        uri = Services.uriFixup.createFixupURI(resultURL, Services.uriFixup.FIXUP_FLAG_NONE);
       } catch (e) {}
       if (!uri) {
         return selectedVal;
       }
     }
 
     uri = this.makeURIReadable(uri);
 
     // If the entire URL is selected, just use the actual loaded URI,
     // unless we want a decoded URI, or it's a data: or javascript: URI,
     // since those are hard to read when encoded.
-    if (inputVal == selectedVal &&
-        !uri.schemeIs("javascript") && !uri.schemeIs("data") &&
+    if (this.textValue == selectedVal &&
+        !uri.schemeIs("javascript") &&
+        !uri.schemeIs("data") &&
         !UrlbarPrefs.get("decodeURLsOnCopy")) {
       return uri.displaySpec;
     }
 
     // Just the beginning of the URL is selected, or we want a decoded
     // url. First check for a trimmed value.
     let spec = uri.displaySpec;
     let trimmedSpec = this.trimValue(spec);
--- a/browser/components/urlbar/UrlbarView.jsm
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -104,16 +104,29 @@ class UrlbarView {
       return null;
     }
 
     let resultIndex = this._selected.getAttribute("resultIndex");
     return this._queryContext.results[resultIndex];
   }
 
   /**
+   * Gets the result for the index.
+   * @param {number} index
+   *   The index to look up.
+   * @returns {UrlbarResult}
+   */
+  getResult(index) {
+    if (index < 0 || index > this._queryContext.results.length) {
+      throw new Error(`UrlbarView: Index ${index} is out of bounds`);
+    }
+    return this._queryContext.results[index];
+  }
+
+  /**
    * Selects the next or previous view item. An item could be an autocomplete
    * result or a one-off search button.
    *
    * @param {boolean} options.reverse
    *   Set to true to select the previous item. By default the next item
    *   will be selected.
    */
   selectNextItem({reverse = false} = {}) {
@@ -508,21 +521,17 @@ class UrlbarView {
       // Ignore right clicks.
       return;
     }
 
     let row = event.target;
     while (!row.classList.contains("urlbarView-row")) {
       row = row.parentNode;
     }
-    let resultIndex = row.getAttribute("resultIndex");
-    let result = this._queryContext.results[resultIndex];
-    if (result) {
-      this.input.pickResult(event, result);
-    }
+    this.input.pickResult(event, parseInt(row.getAttribute("resultIndex")));
   }
 
   _on_overflow(event) {
     if (event.target.classList.contains("urlbarView-row-inner")) {
       event.target.toggleAttribute("overflow", true);
     }
   }
 
--- a/browser/components/urlbar/tests/UrlbarTestUtils.jsm
+++ b/browser/components/urlbar/tests/UrlbarTestUtils.jsm
@@ -77,16 +77,26 @@ var UrlbarTestUtils = {
    * @returns {object} The oneOffSearchButtons
    */
   getOneOffSearchButtons(win) {
     let urlbar = getUrlbarAbstraction(win);
     return urlbar.oneOffSearchButtons;
   },
 
   /**
+   * Returns true if the oneOffSearchButtons are visible.
+   * @param {object} win The window containing the urlbar
+   * @returns {boolena} True if the buttons are visible.
+   */
+  getOneOffSearchButtonsVisible(win) {
+    let urlbar = getUrlbarAbstraction(win);
+    return urlbar.oneOffSearchButtonsVisible;
+  },
+
+  /**
    * Gets an abstracted rapresentation of the result at an index.
    * @param {object} win The window containing the urlbar
    * @param {number} index The index to look for
    */
   async getDetailsOfResultAt(win, index) {
     let urlbar = getUrlbarAbstraction(win);
     return urlbar.getDetailsOfResultAt(index);
   },
@@ -107,16 +117,26 @@ var UrlbarTestUtils = {
    * @returns {number} The selected index.
    */
   getSelectedIndex(win) {
     let urlbar = getUrlbarAbstraction(win);
     return urlbar.getSelectedIndex();
   },
 
   /**
+   * Selects the item at the index specified.
+   * @param {object} win The window containing the urlbar.
+   * @param {index} index The index to select.
+   */
+  setSelectedIndex(win, index) {
+    let urlbar = getUrlbarAbstraction(win);
+    urlbar.setSelectedIndex(index);
+  },
+
+  /**
    * Gets the number of results.
    * You must wait for the query to be complete before using this.
    * @param {object} win The window containing the urlbar
    * @returns {number} the number of results.
    */
   getResultCount(win) {
     let urlbar = getUrlbarAbstraction(win);
     return urlbar.getResultCount();
@@ -247,16 +267,24 @@ class UrlbarAbstraction {
     return this.quantumbar ? this.urlbar.panel : this.urlbar.popup;
   }
 
   get oneOffSearchButtons() {
     return this.quantumbar ? this.urlbar.view.oneOffSearchButtons :
            this.urlbar.popup.oneOffSearchButtons;
   }
 
+  get oneOffSearchButtonsVisible() {
+    if (!this.quantumbar) {
+      return this.window.getComputedStyle(this.oneOffSearchButtons.container).display != "none";
+    }
+
+    return this.oneOffSearchButtons.style.display != "none";
+  }
+
   startSearch(text) {
     if (this.quantumbar) {
       this.urlbar.value = text;
       this.urlbar.startQuery();
     } else {
       this.urlbar.controller.startSearch(text);
     }
   }
@@ -303,16 +331,23 @@ class UrlbarAbstraction {
       this.panel.richlistbox.itemChildren[this.panel.selectedIndex] : null;
   }
 
   getSelectedIndex() {
     return this.quantumbar ? this.urlbar.view.selectedIndex
                            : this.panel.selectedIndex;
   }
 
+  setSelectedIndex(index) {
+    if (!this.quantumbar) {
+      return this.panel.selectedIndex = index;
+    }
+    return this.urlbar.view.selectedIndex;
+  }
+
   getResultCount() {
     return this.quantumbar ? this.urlbar.view._rows.children.length
                            : this.urlbar.controller.matchCount;
   }
 
   async getDetailsOfResultAt(index) {
     let element = await this.promiseResultAt(index);
     function getType(style, action) {
--- a/browser/components/urlbar/tests/browser/browser.ini
+++ b/browser/components/urlbar/tests/browser/browser.ini
@@ -91,17 +91,26 @@ skip-if = os == "linux" # Bug 1073339 - 
 [browser_UrlbarInput_overflow.js]
 [browser_UrlbarInput_search.js]
 [browser_UrlbarInput_tooltip.js]
 [browser_UrlbarInput_trimURLs.js]
 subsuite = clipboard
 [browser_UrlbarInput_unit.js]
 support-files = empty.xul
 [browser_UrlbarLoadRace.js]
+[browser_urlbarOneOffs_searchSuggestions.js]
+skip-if = true # Bug 1528024 - Need to correctly handle suggestions and one-offs.
+support-files =
+  ../browser/searchSuggestionEngine.xml
+  ../browser/searchSuggestionEngine.sjs
 [browser_urlbarOneOffs_settings.js]
+[browser_urlbarOneOffs.js]
+support-files =
+  ../browser/searchSuggestionEngine.xml
+  ../browser/searchSuggestionEngine.sjs
 [browser_urlbarPlaceholder.js]
 support-files =
   searchSuggestionEngine.xml
   searchSuggestionEngine.sjs
 [browser_urlbarRevert.js]
 [browser_urlbarSearchFunction.js]
 [browser_urlbarSearchSingleWordNotification.js]
 [browser_urlbarSearchSuggestions.js]
rename from browser/components/urlbar/tests/legacy/browser_urlbarOneOffs.js
rename to browser/components/urlbar/tests/browser/browser_urlbarOneOffs.js
--- a/browser/components/urlbar/tests/legacy/browser_urlbarOneOffs.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarOneOffs.js
@@ -1,12 +1,16 @@
 const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
 
 let gMaxResults;
 
+XPCOMUtils.defineLazyGetter(this, "oneOffSearchButtons", () => {
+  return UrlbarTestUtils.getOneOffSearchButtons(window);
+});
+
 add_task(async function init() {
   Services.prefs.setBoolPref("browser.urlbar.oneOffSearches", true);
   gMaxResults = Services.prefs.getIntPref("browser.urlbar.maxRichResults");
 
   // Add a search suggestion engine and move it to the front so that it appears
   // as the first one-off.
   let engine = await SearchTestUtils.promiseNewSearchEngine(
     getRootDirectory(gTestPath) + TEST_ENGINE_BASENAME);
@@ -29,35 +33,37 @@ add_task(async function init() {
     });
   }
   await PlacesTestUtils.addVisits(visits);
 });
 
 // Keys up and down through the history panel, i.e., the panel that's shown when
 // there's no text in the textbox.
 add_task(async function history() {
-  gURLBar.popup.toggleOneOffSearches(true);
+  if (!UrlbarPrefs.get("quantumbar")) {
+    gURLBar.popup.toggleOneOffSearches(true);
+  }
 
   gURLBar.focus();
-  EventUtils.synthesizeKey("KEY_ArrowDown");
-  await promisePopupShown(gURLBar.popup);
+  await UrlbarTestUtils.promisePopupOpen(window, () => {
+    EventUtils.synthesizeKey("KEY_ArrowDown");
+  });
   await waitForAutocompleteResultAt(gMaxResults - 1);
 
   assertState(-1, -1, "");
 
   // Key down through each result.
   for (let i = 0; i < gMaxResults; i++) {
     EventUtils.synthesizeKey("KEY_ArrowDown");
     assertState(i, -1,
       "example.com/browser_urlbarOneOffs.js/?" + (gMaxResults - i - 1));
   }
 
   // Key down through each one-off.
-  let numButtons =
-    gURLBar.popup.oneOffSearchButtons.getSelectableButtons(true).length;
+  let numButtons = oneOffSearchButtons.getSelectableButtons(true).length;
   for (let i = 0; i < numButtons; i++) {
     EventUtils.synthesizeKey("KEY_ArrowDown");
     assertState(-1, i, "");
   }
 
   // Key down once more.  Nothing should be selected.
   EventUtils.synthesizeKey("KEY_ArrowDown");
   assertState(-1, -1, "");
@@ -120,18 +126,17 @@ add_task(async function() {
     // i starts at zero so that the textValue passed to assertState is correct.
     // But that means that i + 1 is the expected selected index, since initially
     // (when this loop starts) the first result is selected.
     assertState(i + 1, -1,
       "example.com/browser_urlbarOneOffs.js/?" + (gMaxResults - i - 1));
   }
 
   // Key down through each one-off.
-  let numButtons =
-    gURLBar.popup.oneOffSearchButtons.getSelectableButtons(true).length;
+  let numButtons = oneOffSearchButtons.getSelectableButtons(true).length;
   for (let i = 0; i < numButtons; i++) {
     EventUtils.synthesizeKey("KEY_ArrowDown");
     assertState(-1, i, typedValue);
   }
 
   // Key down once more.  The selection should wrap around to the first result.
   EventUtils.synthesizeKey("KEY_ArrowDown");
   assertState(0, -1, typedValue);
@@ -155,36 +160,40 @@ add_task(async function() {
   assertState(0, -1, typedValue);
 
   await hidePopup();
 });
 
 // Checks that "Search with Current Search Engine" items are updated to "Search
 // with One-Off Engine" when a one-off is selected.
 add_task(async function searchWith() {
+  // TODO Bug 1527947: Implement "search with" change with button change.
+  if (UrlbarPrefs.get("quantumbar")) {
+    return;
+  }
   let typedValue = "foo";
   await promiseAutocompleteResultPopup(typedValue);
-  await waitForAutocompleteResultAt(0);
+  let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
   assertState(0, -1, typedValue);
 
-  let item = gURLBar.popup.richlistbox.firstElementChild;
-  Assert.equal(item._actionText.textContent,
+  Assert.equal(result.displayed.action,
                "Search with " + (await Services.search.getDefault()).name,
                "Sanity check: first result's action text");
 
   // Alt+Down to the first one-off.  Now the first result and the first one-off
   // should both be selected.
   EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true });
   assertState(0, 0, typedValue);
 
-  let engineName = gURLBar.popup.oneOffSearchButtons.selectedButton.engine.name;
+  let engineName = oneOffSearchButtons.selectedButton.engine.name;
   Assert.notEqual(engineName, (await Services.search.getDefault()).name,
                   "Sanity check: First one-off engine should not be " +
                   "the current engine");
-  Assert.equal(item._actionText.textContent,
+  result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+  Assert.equal(result.displayed.action,
                "Search with " + engineName,
                "First result's action text should be updated");
 
   await hidePopup();
 });
 
 // Clicks a one-off.
 add_task(async function oneOffClick() {
@@ -192,17 +201,17 @@ add_task(async function oneOffClick() {
 
   // We are explicitly using something that looks like a url, to make the test
   // stricter. Even if it looks like a url, we should search.
   let typedValue = "foo.bar";
   await promiseAutocompleteResultPopup(typedValue);
   await waitForAutocompleteResultAt(1);
   assertState(0, -1, typedValue);
 
-  let oneOffs = gURLBar.popup.oneOffSearchButtons.getSelectableButtons(true);
+  let oneOffs = oneOffSearchButtons.getSelectableButtons(true);
   let resultsPromise =
     BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false,
                                    "http://mochi.test:8888/?terms=foo.bar");
   EventUtils.synthesizeMouseAtCenter(oneOffs[0], {});
   await resultsPromise;
 
   gBrowser.removeTab(gBrowser.selectedTab);
 });
@@ -239,55 +248,58 @@ add_task(async function collapsedOneOffs
   await SpecialPowers.pushPrefEnv({"set": [
     [ "browser.search.hiddenOneOffs", engines.map(e => e.name).join(",") ],
   ]});
 
   let typedValue = "foo";
   await promiseAutocompleteResultPopup(typedValue, window, true);
   await waitForAutocompleteResultAt(0);
   assertState(0, -1);
-  Assert.ok(gURLBar.popup.oneOffSearchButtons.buttons.collapsed,
+  Assert.ok(oneOffSearchButtons.buttons.collapsed,
     "The one-off buttons should be collapsed");
   EventUtils.synthesizeKey("KEY_ArrowUp");
   assertState(1, -1);
   await hidePopup();
 });
 
 
 // The one-offs should be hidden when searching with an "@engine" search engine
 // alias.
 add_task(async function hiddenWhenUsingSearchAlias() {
+  // TODO Bug 1527934: Implement this for QuantumBar.
+  if (UrlbarPrefs.get("quantumbar")) {
+    return;
+  }
   let typedValue = "@example";
   await promiseAutocompleteResultPopup(typedValue, window, true);
   await waitForAutocompleteResultAt(0);
-  Assert.equal(gURLBar.popup.oneOffSearchesEnabled, false);
-  Assert.equal(
-    window.getComputedStyle(gURLBar.popup.oneOffSearchButtons.container).display,
-    "none"
-  );
+  Assert.equal(UrlbarTestUtils.getOneOffSearchButtonsVisible(window), false,
+    "Should not be showing the one-off buttons");
   await hidePopup();
 
   typedValue = "not an engine alias";
   await promiseAutocompleteResultPopup(typedValue, window, true);
   await waitForAutocompleteResultAt(0);
-  Assert.equal(gURLBar.popup.oneOffSearchesEnabled, true);
-  Assert.equal(
-    window.getComputedStyle(gURLBar.popup.oneOffSearchButtons.container).display,
-    "-moz-box"
-  );
+  Assert.equal(UrlbarTestUtils.getOneOffSearchButtonsVisible(window), true,
+    "Should be showing the one-off buttons");
   await hidePopup();
 });
 
 
 function assertState(result, oneOff, textValue = undefined) {
-  Assert.equal(gURLBar.popup.selectedIndex, result,
-               "Expected result should be selected");
-  Assert.equal(gURLBar.popup.oneOffSearchButtons.selectedButtonIndex, oneOff,
-               "Expected one-off should be selected");
+  Assert.equal(UrlbarTestUtils.getSelectedIndex(window), result,
+    "Expected result should be selected");
+  Assert.equal(oneOffSearchButtons.selectedButtonIndex,
+    oneOff, "Expected one-off should be selected");
+  // TODO Bug 1527946: Fix textValue differences for QuantumBar
+  if (UrlbarPrefs.get("quantumbar")) {
+    return;
+  }
   if (textValue !== undefined) {
     Assert.equal(gURLBar.textValue, textValue, "Expected textValue");
   }
 }
 
-async function hidePopup() {
-  EventUtils.synthesizeKey("KEY_Escape");
-  await promisePopupHidden(gURLBar.popup);
+function hidePopup() {
+  return UrlbarTestUtils.promisePopupClose(window, () => {
+    EventUtils.synthesizeKey("KEY_Escape");
+  });
 }
rename from browser/components/urlbar/tests/legacy/browser_urlbarOneOffs_searchSuggestions.js
rename to browser/components/urlbar/tests/browser/browser_urlbarOneOffs_searchSuggestions.js
--- a/browser/components/urlbar/tests/legacy/browser_urlbarOneOffs_searchSuggestions.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarOneOffs_searchSuggestions.js
@@ -11,18 +11,17 @@ add_task(async function init() {
   let oldDefaultEngine = await Services.search.getDefault();
   await Services.search.moveEngine(engine, 0);
   await Services.search.setDefault(engine);
   registerCleanupFunction(async function() {
     await Services.search.setDefault(oldDefaultEngine);
 
     await PlacesUtils.history.clear();
     // Make sure the popup is closed for the next test.
-    gURLBar.blur();
-    Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
+    await UrlbarTestUtils.promisePopupClose(window);
   });
 });
 
 // Presses the Return key when a one-off is selected after selecting a search
 // suggestion.
 add_task(async function oneOffReturnAfterSuggestion() {
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
 
@@ -65,17 +64,17 @@ add_task(async function oneOffClickAfter
   // Down to select the first search suggestion.
   EventUtils.synthesizeKey("KEY_ArrowDown");
   assertState(1, -1, "foofoo");
 
   // Down to select the next search suggestion.
   EventUtils.synthesizeKey("KEY_ArrowDown");
   assertState(2, -1, "foobar");
 
-  let oneOffs = gURLBar.popup.oneOffSearchButtons.getSelectableButtons(true);
+  let oneOffs = UrlbarTestUtils.getOneOffSearchButtons(window).getSelectableButtons(true);
   let resultsPromise =
     BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false,
                                    `http://mochi.test:8888/?terms=foobar`);
   EventUtils.synthesizeMouseAtCenter(oneOffs[0], {});
   await resultsPromise;
 
   await PlacesUtils.history.clear();
   BrowserTestUtils.removeTab(tab);
@@ -90,33 +89,33 @@ add_task(async function overridden_engin
     // Down to select the first search suggestion.
     EventUtils.synthesizeKey("KEY_ArrowDown");
     assertState(1, -1, "foofoo");
     // ALT+Down to select the second search engine.
     EventUtils.synthesizeKey("KEY_ArrowDown", {altKey: true});
     EventUtils.synthesizeKey("KEY_ArrowDown", {altKey: true});
     assertState(1, 1, "foofoo");
 
-    let label = gURLBar.popup.richlistbox.itemChildren[gURLBar.popup.richlistbox.selectedIndex].label;
+    let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
+    let label = result.displayed.action;
     // Run again the query, check the label has been replaced.
     await promiseAutocompleteResultPopup(typedValue, window, true);
     await promiseSuggestionsPresent();
     assertState(0, -1, "foo");
-    let newLabel = gURLBar.popup.richlistbox.itemChildren[1].label;
-    Assert.notEqual(newLabel, label, "The label should have been updated");
+    result = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
+    Assert.notEqual(result.displayed.action, label, "The label should have been updated");
 
     BrowserTestUtils.removeTab(tab);
 });
 
 function assertState(result, oneOff, textValue = undefined) {
-  Assert.equal(gURLBar.popup.selectedIndex, result,
-               "Expected result should be selected");
-  Assert.equal(gURLBar.popup.oneOffSearchButtons.selectedButtonIndex, oneOff,
-               "Expected one-off should be selected");
-  if (textValue !== undefined) {
-    Assert.equal(gURLBar.textValue, textValue, "Expected textValue");
-  }
+  Assert.equal(UrlbarTestUtils.getSelectedIndex(window), result,
+    "Expected result should be selected");
+  Assert.equal(UrlbarTestUtils.getOneOffSearchButtons(window).selectedButtonIndex,
+    oneOff, "Expected one-off should be selected");
+    // TODO Bug 1527946: Fix textValue differences for QuantumBar
+    if (UrlbarPrefs.get("quantumbar")) {
+      return;
+    }
+    if (textValue !== undefined) {
+      Assert.equal(gURLBar.textValue, textValue, "Expected textValue");
+    }
 }
-
-async function hidePopup() {
-  EventUtils.synthesizeKey("KEY_Escape");
-  await promisePopupHidden(gURLBar.popup);
-}
--- a/browser/components/urlbar/tests/browser/browser_urlbarOneOffs_settings.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarOneOffs_settings.js
@@ -51,20 +51,16 @@ async function selectSettings(activateFn
     });
 
     Assert.equal(gBrowser.contentWindow.history.state, "paneSearch",
       "Should have opened the search preferences pane");
   });
 }
 
 add_task(async function test_open_settings_with_enter() {
-  // TODO: Bug 1525269 will implement this for QuantumBar.
-  if (UrlbarPrefs.get("quantumbar")) {
-    return;
-  }
   if (!UrlbarPrefs.get("quantumbar")) {
     // The old urlbar bindings can sometimes be in a state where they
     // won't show the one off searches, so force it here.
     gURLBar.popup.toggleOneOffSearches(true);
   }
 
   await selectSettings(() => {
     EventUtils.synthesizeKey("KEY_ArrowUp");
--- a/browser/components/urlbar/tests/browser/browser_urlbarSearchFunction.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarSearchFunction.js
@@ -116,25 +116,22 @@ function assertSearchSuggestionsNotifica
 
 /**
  * Asserts that the one-off search buttons are or aren't visible.
  *
  * @param {boolean} visible
  *        True if they should be visible, false if not.
  */
 function assertOneOffButtonsVisible(visible) {
-  // TODO Bug 1491248: Not implemented for QuantumBar.
+  // TODO Bug 1527934: Not implemented for QuantumBar.
   if (UrlbarPrefs.get("quantumbar")) {
     return;
   }
-  Assert.equal(gURLBar.popup.oneOffSearchesEnabled, visible);
-  Assert.equal(
-    window.getComputedStyle(gURLBar.popup.oneOffSearchButtons.container).display,
-    visible ? "-moz-box" : "none"
-  );
+  Assert.equal(UrlbarTestUtils.getOneOffSearchButtonsVisible(window), visible,
+    "Should show or not the one-off search buttons");
 }
 
 /**
  * Asserts that the urlbar's input value is the given value.  Also asserts that
  * the first (heuristic) result in the popup is a search suggestion whose search
  * query is the given value.
  *
  * @param {string} value
--- a/browser/components/urlbar/tests/browser/browser_urlbarSearchSuggestions.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarSearchSuggestions.js
@@ -78,20 +78,16 @@ add_task(async function plainEnterOnSugg
   await testPressEnterOnSuggestion();
 });
 
 add_task(async function ctrlEnterOnSuggestion() {
   await testPressEnterOnSuggestion("http://www.foofoo.com/", { ctrlKey: true });
 });
 
 add_task(async function copySuggestionText() {
-  // TODO Bug 1525018 - Implement search suggestion handling for copy.
-  if (UrlbarPrefs.get("quantumbar")) {
-    return;
-  }
   gURLBar.focus();
   await promiseAutocompleteResultPopup("foo");
   let [idx, suggestion] = await promiseFirstSuggestion();
   for (let i = 0; i < idx; ++i) {
     EventUtils.synthesizeKey("KEY_ArrowDown");
   }
   gURLBar.select();
   await new Promise((resolve, reject) => waitForClipboard(suggestion, function() {
--- a/browser/components/urlbar/tests/legacy/browser.ini
+++ b/browser/components/urlbar/tests/legacy/browser.ini
@@ -22,24 +22,16 @@ skip-if = (verify && debug && (os == 'wi
 support-files =
   ../browser/Panel.jsm
   ../browser/urlbarAddonIframe.html
   ../browser/urlbarAddonIframe.js
   ../browser/urlbarAddonIframeContentScript.js
 [browser_urlbarAutofillPreserveCase.js]
 [browser_urlbarAutoFillTrimURLs.js]
 [browser_urlbarKeepStateAcrossTabSwitches.js]
-[browser_urlbarOneOffs.js]
-support-files =
-  ../browser/searchSuggestionEngine.xml
-  ../browser/searchSuggestionEngine.sjs
-[browser_urlbarOneOffs_searchSuggestions.js]
-support-files =
-  ../browser/searchSuggestionEngine.xml
-  ../browser/searchSuggestionEngine.sjs
 [browser_urlbarPrivateBrowsingWindowChange.js]
 [browser_urlbarRaceWithTabs.js]
 skip-if = os == "linux" # Bug 1382456
 [browser_urlbarSearchSuggestions_opt-out.js]
 support-files =
   ../browser/searchSuggestionEngine.xml
   ../browser/searchSuggestionEngine.sjs
 [browser_urlbarSearchTelemetry.js]
@@ -113,17 +105,25 @@ support-files =
   ../browser/file_urlbar_edit_dos.html
 [../browser/browser_urlbarDelete.js]
 [../browser/browser_urlbarEnter.js]
 [../browser/browser_urlbarEnterAfterMouseOver.js]
 skip-if = os == "linux" # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s
 [../browser/browser_urlbar_whereToOpen.js]
 [../browser/browser_urlbarFocusedCmdK.js]
 [../browser/browser_urlbarHashChangeProxyState.js]
+[../browser/browser_urlbarOneOffs_searchSuggestions.js]
+support-files =
+  ../browser/searchSuggestionEngine.xml
+  ../browser/searchSuggestionEngine.sjs
 [../browser/browser_urlbarOneOffs_settings.js]
+[../browser/browser_urlbarOneOffs.js]
+support-files =
+  ../browser/searchSuggestionEngine.xml
+  ../browser/searchSuggestionEngine.sjs
 [../browser/browser_urlbarPlaceholder.js]
 support-files =
   ../browser/searchSuggestionEngine.xml
   ../browser/searchSuggestionEngine.sjs
 [../browser/browser_urlbarRevert.js]
 [../browser/browser_urlbarSearchFunction.js]
 [../browser/browser_urlbarSearchSingleWordNotification.js]
 [../browser/browser_urlbarSearchSuggestions.js]
--- a/browser/modules/HomePage.jsm
+++ b/browser/modules/HomePage.jsm
@@ -5,18 +5,16 @@
 /* globals ChromeUtils, Services */
 /* exported HomePage */
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["HomePage"];
 
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
-                               "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 const kPrefName = "browser.startup.homepage";
 
 function getHomepagePref(useDefault) {
   let homePage;
   let prefs = Services.prefs;
   if (useDefault) {
     prefs = prefs.getDefaultBranch(null);
@@ -40,46 +38,24 @@ function getHomepagePref(useDefault) {
     Services.prefs.clearUserPref(kPrefName);
     homePage = getHomepagePref(true);
   }
 
   return homePage;
 }
 
 let HomePage = {
-  get(aWindow) {
-    if (PrivateBrowsingUtils.permanentPrivateBrowsing ||
-        (aWindow && PrivateBrowsingUtils.isWindowPrivate(aWindow))) {
-      return this.getPrivate();
-    }
+  get() {
     return getHomepagePref();
   },
 
   getDefault() {
     return getHomepagePref(true);
   },
 
-  getPrivate() {
-    let homePages = getHomepagePref();
-    if (!homePages.includes("moz-extension")) {
-      return homePages;
-    }
-    // Verify private access and build a new list.
-    let privateHomePages = homePages.split("|").filter(page => {
-      let url = new URL(page);
-      if (url.protocol !== "moz-extension:") {
-        return true;
-      }
-      let policy = WebExtensionPolicy.getByHostname(url.hostname);
-      return policy && policy.privateBrowsingAllowed;
-    });
-    // Extensions may not be ready on startup, fallback to defaults.
-    return privateHomePages.join("|") || this.getDefault();
-  },
-
   get overridden() {
     return Services.prefs.prefHasUserValue(kPrefName);
   },
 
   set(value) {
     Services.prefs.setStringPref(kPrefName, value);
   },
 
--- a/browser/modules/Sanitizer.jsm
+++ b/browser/modules/Sanitizer.jsm
@@ -664,16 +664,17 @@ async function sanitizeInternal(items, a
   }
 }
 
 async function sanitizeOnShutdown(progress) {
   log("Sanitizing on shutdown");
 
   if (Sanitizer.shouldSanitizeOnShutdown) {
     // Need to sanitize upon shutdown
+    progress.advancement = "shutdown-cleaner";
     let itemsToClear = getItemsToClearFromPrefBranch(Sanitizer.PREF_SHUTDOWN_BRANCH);
     await Sanitizer.sanitize(itemsToClear, { progress });
   }
 
   // Clear out QuotaManager storage for principals that have been marked as
   // session only.  The cookie service has special logic that avoids writing
   // such cookies to disk, but QuotaManager always touches disk, so we need to
   // wipe the data on shutdown (or startup if we failed to wipe it at
@@ -693,56 +694,63 @@ async function sanitizeOnShutdown(progre
   // use QuotaManager storage but don't have a specific permission set to
   // ACCEPT_NORMALLY need to be wiped.  Second, the set of origins that have
   // the permission explicitly set to ACCEPT_SESSION need to be wiped.  There
   // are also other ways to think about and accomplish this, but this is what
   // the logic below currently does!
   if (Services.prefs.getIntPref(PREF_COOKIE_LIFETIME,
                                 Ci.nsICookieService.ACCEPT_NORMALLY) == Ci.nsICookieService.ACCEPT_SESSION) {
     log("Session-only configuration detected");
-    let principals = await getAllPrincipals();
-    await maybeSanitizeSessionPrincipals(principals);
+    progress.advancement = "session-only";
+    let principals = await getAllPrincipals(progress);
+    await maybeSanitizeSessionPrincipals(progress, principals);
   }
 
+  progress.advancement = "session-permission";
+
   // Let's see if we have to forget some particular site.
   for (let permission of Services.perms.enumerator) {
     if (permission.type != "cookie" ||
         permission.capability != Ci.nsICookiePermission.ACCESS_SESSION) {
       continue;
     }
 
     // We consider just permissions set for http, https and file URLs.
     if (!isSupportedURI(permission.principal.URI)) {
       continue;
     }
 
     log("Custom session cookie permission detected for: " + permission.principal.URI.spec);
 
     // We use just the URI here, because permissions ignore OriginAttributes.
-    let principals = await getAllPrincipals(permission.principal.URI);
-    await maybeSanitizeSessionPrincipals(principals);
+    let principals = await getAllPrincipals(progress, permission.principal.URI);
+    await maybeSanitizeSessionPrincipals(progress, principals);
   }
 
   if (Sanitizer.shouldSanitizeNewTabContainer) {
+    progress.advancement = "newtab-segregation";
     sanitizeNewTabSegregation();
     removePendingSanitization("newtab-container");
   }
 
   if (Sanitizer.shouldSanitizeOnShutdown) {
     // We didn't crash during shutdown sanitization, so annotate it to avoid
     // sanitizing again on startup.
     removePendingSanitization("shutdown");
     Services.prefs.savePrefFile(null);
   }
+
+  progress.advancement = "done";
 }
 
 // Retrieve the list of nsIPrincipals with site data. If matchUri is not null,
 // it returns only the principals matching that URI, ignoring the
 // OriginAttributes.
-async function getAllPrincipals(matchUri = null) {
+async function getAllPrincipals(progress, matchUri = null) {
+  progress.step = "principals-quota-manager";
   let principals = await new Promise(resolve => {
     quotaManagerService.getUsage(request => {
       if (request.resultCode != Cr.NS_OK) {
         // We are probably shutting down. We don't want to propagate the
         // error, rejecting the promise.
         resolve([]);
         return;
       }
@@ -758,58 +766,67 @@ async function getAllPrincipals(matchUri
         if (!matchUri || Services.eTLD.hasRootDomain(matchUri.host, uri.host)) {
           list.push(principal);
         }
       }
       resolve(list);
     });
   }).catch(() => []);
 
+  progress.step = "principals-service-workers";
   let serviceWorkers = serviceWorkerManager.getAllRegistrations();
   for (let i = 0; i < serviceWorkers.length; i++) {
     let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
     let uri = sw.principal.URI;
     // We don't need to check the scheme. SW are just exposed to http/https URLs.
     if (!matchUri || Services.eTLD.hasRootDomain(matchUri.host, uri.host)) {
       principals.push(sw.principal);
     }
   }
 
   // Let's take the list of unique hosts+OA from cookies.
+  progress.step = "principals-cookies";
   let enumerator = Services.cookies.enumerator;
   let hosts = new Set();
   for (let cookie of enumerator) {
     if (!matchUri || Services.eTLD.hasRootDomain(matchUri.host, cookie.rawHost)) {
       hosts.add(cookie.rawHost + ChromeUtils.originAttributesToSuffix(cookie.originAttributes));
     }
   }
 
+  progress.step = "principals-host-cookie";
   hosts.forEach(host => {
     // Cookies and permissions are handled by origin/host. Doesn't matter if we
     // use http: or https: schema here.
     principals.push(
       Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("https://" + host));
   });
 
+  progress.step = "total-principals:" + principals.length;
   return principals;
 }
 
 // This method receives a list of principals and it checks if some of them or
 // some of their sub-domain need to be sanitize.
-async function maybeSanitizeSessionPrincipals(principals) {
+async function maybeSanitizeSessionPrincipals(progress, principals) {
   log("Sanitizing " + principals.length + " principals");
 
   let promises = [];
 
   principals.forEach(principal => {
-    if (!cookiesAllowedForDomainOrSubDomain(principal)) {
-      promises.push(sanitizeSessionPrincipal(principal));
+    progress.step = "checking-principal";
+    let cookieAllowed = cookiesAllowedForDomainOrSubDomain(principal);
+    progress.step = "principal-checked:" + cookieAllowed;
+
+    if (!cookieAllowed) {
+      promises.push(sanitizeSessionPrincipal(progress, principal));
     }
   });
 
+  progress.step = "promises:" + promises.length;
   return Promise.all(promises);
 }
 
 function cookiesAllowedForDomainOrSubDomain(principal) {
   log("Checking principal: " + principal.URI.spec);
 
   // If we have the 'cookie' permission for this principal, let's return
   // immediately.
@@ -848,25 +865,27 @@ function cookiesAllowedForDomainOrSubDom
       return cookiesAllowedForDomainOrSubDomain(perm.principal);
     }
   }
 
   log("Cookie not allowed.");
   return false;
 }
 
-async function sanitizeSessionPrincipal(principal) {
+async function sanitizeSessionPrincipal(progress, principal) {
   log("Sanitizing principal: " + principal.URI.spec);
 
+  progress.step = "sanitizing";
   await new Promise(resolve => {
     Services.clearData.deleteDataFromPrincipal(principal, true /* user request */,
                                                Ci.nsIClearDataService.CLEAR_DOM_STORAGES |
                                                Ci.nsIClearDataService.CLEAR_COOKIES,
                                                resolve);
   });
+  progress.step = "sanitized";
 }
 
 function sanitizeNewTabSegregation() {
   let identity = ContextualIdentityService.getPrivateIdentity("userContextIdInternal.thumbnail");
   if (identity) {
     Services.clearData.deleteDataFromOriginAttributesPattern({ userContextId: identity.userContextId });
   }
 }
--- a/browser/modules/test/browser/browser.ini
+++ b/browser/modules/test/browser/browser.ini
@@ -28,16 +28,22 @@ skip-if = os != win || (os == win && bit
 [browser_UnsubmittedCrashHandler.js]
 run-if = crashreporter
 [browser_urlBar_zoom.js]
 [browser_UsageTelemetry.js]
 [browser_UsageTelemetry_domains.js]
 [browser_UsageTelemetry_private_and_restore.js]
 skip-if = verify && debug
 [browser_UsageTelemetry_uniqueOriginsVisitedInPast24Hours.js]
+[browser_UsageTelemetry_urlbar_extension.js]
+skip-if = verify && debug && os == linux && bits == 64 # Bug 1528255 - Perma orange on verify linux64.
+[browser_UsageTelemetry_urlbar_places.js]
+skip-if = verify && debug && os == linux && bits == 64 # Bug 1528255 - Perma orange on verify linux64.
+[browser_UsageTelemetry_urlbar_remotetab.js]
+skip-if = verify && debug && os == linux && bits == 64 # Bug 1528255 - Perma orange on verify linux64.
 [browser_UsageTelemetry_urlbar.js]
 support-files =
   usageTelemetrySearchSuggestions.sjs
   usageTelemetrySearchSuggestions.xml
 [browser_UsageTelemetry_searchbar.js]
 support-files =
   usageTelemetrySearchSuggestions.sjs
   usageTelemetrySearchSuggestions.xml
--- a/browser/modules/test/browser/browser_PageActions.js
+++ b/browser/modules/test/browser/browser_PageActions.js
@@ -1459,17 +1459,17 @@ add_task(async function contextMenu() {
   Assert.deepEqual(relatedEvents, [
     ["pageAction", null, {action: "manage"}],
     ["pageAction", null, {action: "manage"}],
   ]);
 
   // urlbar tests that run after this one can break if the mouse is left over
   // the area where the urlbar popup appears, which seems to happen due to the
   // above synthesized mouse events.  Move it over the urlbar.
-  EventUtils.synthesizeMouseAtCenter(gURLBar, { type: "mousemove" });
+  EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, { type: "mousemove" });
   gURLBar.focus();
 });
 
 
 // Tests transient actions.
 add_task(async function transient() {
   let initialActionsInPanel = PageActions.actionsInPanel(window);
 
--- a/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js
+++ b/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js
@@ -1,74 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This file tests urlbar telemetry with search related actions.
+ */
+
 "use strict";
 
 const SCALAR_URLBAR = "browser.engagement.navigation.urlbar";
 
 // The preference to enable suggestions in the urlbar.
 const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches";
 // The name of the search engine used to generate suggestions.
 const SUGGESTION_ENGINE_NAME = "browser_UsageTelemetry usageTelemetrySearchSuggestions.xml";
 const ONEOFF_URLBAR_PREF = "browser.urlbar.oneOffSearches";
 
-ChromeUtils.defineModuleGetter(this, "URLBAR_SELECTED_RESULT_TYPES",
-                               "resource:///modules/BrowserUsageTelemetry.jsm");
-
-ChromeUtils.defineModuleGetter(this, "URLBAR_SELECTED_RESULT_METHODS",
-                               "resource:///modules/BrowserUsageTelemetry.jsm");
-
-ChromeUtils.defineModuleGetter(this, "SearchTelemetry",
-                              "resource:///modules/SearchTelemetry.jsm");
-
-let searchInAwesomebar = async function(inputText, win = window) {
-  await new Promise(r => waitForFocus(r, win));
-  // Write the search query in the urlbar.
-  win.gURLBar.focus();
-  win.gURLBar.value = inputText;
+XPCOMUtils.defineLazyModuleGetters(this, {
+  SearchTelemetry: "resource:///modules/SearchTelemetry.jsm",
+  UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.jsm",
+  URLBAR_SELECTED_RESULT_TYPES: "resource:///modules/BrowserUsageTelemetry.jsm",
+  URLBAR_SELECTED_RESULT_METHODS: "resource:///modules/BrowserUsageTelemetry.jsm",
+});
 
-  // This is not strictly necessary, but some things, like clearing oneoff
-  // buttons status, depend on actual input events that the user would normally
-  // generate.
-  let event = win.document.createEvent("Events");
-  event.initEvent("input", true, true);
-  win.gURLBar.dispatchEvent(event);
-  win.gURLBar.controller.startSearch(inputText);
-
-  // Wait for the popup to show.
-  await BrowserTestUtils.waitForEvent(win.gURLBar.popup, "popupshown");
-  // And then for the search to complete.
-  await BrowserTestUtils.waitForCondition(() => win.gURLBar.controller.searchStatus >=
-                                                Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH);
-};
+function searchInAwesomebar(inputText, win = window) {
+  return UrlbarTestUtils.promiseAutocompleteResultPopup(win, inputText, waitForFocus, true);
+}
 
 /**
  * Click one of the entries in the urlbar suggestion popup.
  *
- * @param {String} entryName
- *        The name of the elemet to click on.
+ * @param {String} resultTitle
+ *        The title of the result to click on.
  * @param {Number} button [optional]
  *        which button to click.
  */
-function clickURLBarSuggestion(entryName, button = 1) {
-  // The entry in the suggestion list should follow the format:
-  // "<search term> <engine name> Search"
-  const expectedSuggestionName = entryName + " " + SUGGESTION_ENGINE_NAME + " Search";
-  return BrowserTestUtils.waitForCondition(() => {
-    for (let child of gURLBar.popup.richlistbox.children) {
-      if (child.label === expectedSuggestionName) {
-        // This entry is the search suggestion we're looking for.
-        if (button == 1)
-          child.click();
-        else if (button == 2) {
-          EventUtils.synthesizeMouseAtCenter(child, {type: "mousedown", button: 2});
-        }
-        return true;
+async function clickURLBarSuggestion(resultTitle, button = 1) {
+  await UrlbarTestUtils.promiseSearchComplete(window);
+
+  const count = UrlbarTestUtils.getResultCount(window);
+  for (let i = 0; i < count; i++) {
+    let result = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
+    if (result.displayed.title == resultTitle) {
+      // This entry is the search suggestion we're looking for.
+      let element = await UrlbarTestUtils.waitForAutocompleteResultAt(window, i);
+      if (button == 1)
+        EventUtils.synthesizeMouseAtCenter(element, {});
+      else if (button == 2) {
+        EventUtils.synthesizeMouseAtCenter(element, {type: "mousedown", button: 2});
       }
+      return;
     }
-    return false;
-  }, "Waiting for the expected suggestion to appear");
+  }
 }
 
 /**
  * Create an engine to generate search suggestions and add it as default
  * for this test.
  */
 async function withNewSearchEngine(taskFn) {
   const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml";
@@ -348,17 +336,17 @@ add_task(async function test_oneOff_clic
   let resultMethodHist = TelemetryTestUtils.getAndClearHistogram("FX_URLBAR_SELECTED_RESULT_METHOD");
 
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
 
   info("Type a query.");
   let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   await searchInAwesomebar("query");
   info("Click the first one-off button.");
-  gURLBar.popup.oneOffSearchButtons.getSelectableButtons(false)[0].click();
+  UrlbarTestUtils.getOneOffSearchButtons(window).getSelectableButtons(false)[0].click();
   await p;
 
   TelemetryTestUtils.assertHistogram(resultMethodHist,
     URLBAR_SELECTED_RESULT_METHODS.click, 1);
 
   BrowserTestUtils.removeTab(tab);
 });
 
@@ -471,17 +459,17 @@ add_task(async function test_suggestion_
 
   await withNewSearchEngine(async function() {
     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
 
     info("Type a query. Suggestions should be generated by the test engine.");
     let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
     await searchInAwesomebar("query");
     info("Select the second result and press Return.");
-    gURLBar.popup.selectedIndex = 1;
+    UrlbarTestUtils.setSelectedIndex(window, 1);
     EventUtils.synthesizeKey("KEY_Enter");
     await p;
 
     TelemetryTestUtils.assertHistogram(resultMethodHist,
       URLBAR_SELECTED_RESULT_METHODS.enterSelection, 1);
 
     BrowserTestUtils.removeTab(tab);
   });
new file mode 100644
--- /dev/null
+++ b/browser/modules/test/browser/browser_UsageTelemetry_urlbar_extension.js
@@ -0,0 +1,134 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This file tests urlbar telemetry with extension actions.
+ */
+
+"use strict";
+
+const SCALAR_URLBAR = "browser.engagement.navigation.urlbar";
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+  UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.jsm",
+  URLBAR_SELECTED_RESULT_TYPES: "resource:///modules/BrowserUsageTelemetry.jsm",
+  URLBAR_SELECTED_RESULT_METHODS: "resource:///modules/BrowserUsageTelemetry.jsm",
+});
+
+function assertSearchTelemetryEmpty(search_hist) {
+  const scalars = TelemetryTestUtils.getProcessScalars("parent", true, false);
+  Assert.equal(Object.keys(scalars).length, 0,
+    `Should not have recorded ${SCALAR_URLBAR}`);
+
+  // Make sure SEARCH_COUNTS contains identical values.
+  TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.urlbar", undefined);
+  TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.alias", undefined);
+
+  // Also check events.
+  let events = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  events = (events.parent || []).filter(e => e[1] == "navigation" && e[2] == "search");
+  Assert.equal(events.length, 0, "Should not have recorded any events");
+}
+
+function snapshotHistograms() {
+  Services.telemetry.clearScalars();
+  Services.telemetry.clearEvents();
+  return {
+    resultIndexHist: TelemetryTestUtils.getAndClearHistogram("FX_URLBAR_SELECTED_RESULT_INDEX"),
+    resultTypeHist: TelemetryTestUtils.getAndClearHistogram("FX_URLBAR_SELECTED_RESULT_TYPE"),
+    resultIndexByTypeHist: TelemetryTestUtils.getAndClearKeyedHistogram("FX_URLBAR_SELECTED_RESULT_INDEX_BY_TYPE"),
+    resultMethodHist: TelemetryTestUtils.getAndClearHistogram("FX_URLBAR_SELECTED_RESULT_METHOD"),
+    search_hist: TelemetryTestUtils.getAndClearKeyedHistogram("SEARCH_COUNTS"),
+  };
+}
+
+function assertHistogramResults(histograms, type, index, method) {
+  TelemetryTestUtils.assertHistogram(histograms.resultIndexHist, index, 1);
+
+  TelemetryTestUtils.assertHistogram(histograms.resultTypeHist,
+    URLBAR_SELECTED_RESULT_TYPES[type], 1);
+
+  TelemetryTestUtils.assertKeyedHistogramValue(histograms.resultIndexByTypeHist,
+    type, index, 1);
+
+  TelemetryTestUtils.assertHistogram(histograms.resultMethodHist,
+    method, 1);
+}
+
+
+add_task(async function setup() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      // Disable search suggestions in the urlbar.
+      ["browser.urlbar.suggest.searches", false],
+      // Clear historical search suggestions to avoid interference from previous
+      // tests.
+      ["browser.urlbar.maxHistoricalSearchSuggestions", 0],
+      // Use the default matching bucket configuration.
+      ["browser.urlbar.matchBuckets", "general:5,suggestion:4"],
+      // Turn autofill off.
+      ["browser.urlbar.autoFill", false],
+    ],
+  });
+
+  // Enable local telemetry recording for the duration of the tests.
+  let oldCanRecord = Services.telemetry.canRecordExtended;
+  Services.telemetry.canRecordExtended = true;
+
+  // Enable event recording for the events tested here.
+  Services.telemetry.setEventRecordingEnabled("navigation", true);
+
+  // Clear history so that history added by previous tests doesn't mess up this
+  // test when it selects results in the urlbar.
+  await PlacesUtils.history.clear();
+  await PlacesUtils.bookmarks.eraseEverything();
+
+  // Make sure to restore the engine once we're done.
+  registerCleanupFunction(async function() {
+    Services.telemetry.canRecordExtended = oldCanRecord;
+    await PlacesUtils.history.clear();
+    await PlacesUtils.bookmarks.eraseEverything();
+    Services.telemetry.setEventRecordingEnabled("navigation", false);
+  });
+});
+
+add_task(async function test_extension() {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "omnibox": {
+        "keyword": "omniboxtest",
+      },
+
+      background() {
+        /* global browser */
+        browser.omnibox.setDefaultSuggestion({
+          description: "doit",
+        });
+        // Just do nothing for this test.
+        browser.omnibox.onInputEntered.addListener(() => {
+        });
+        browser.omnibox.onInputChanged.addListener((text, suggest) => {
+          suggest([]);
+        });
+      },
+    },
+  });
+
+  await extension.startup();
+
+  const histograms = snapshotHistograms();
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+  await UrlbarTestUtils.promiseAutocompleteResultPopup(window, "omniboxtest ",
+    waitForFocus, true);
+  EventUtils.synthesizeKey("KEY_Enter");
+
+  assertSearchTelemetryEmpty(histograms.search_hist);
+  assertHistogramResults(histograms, "extension", 0,
+    URLBAR_SELECTED_RESULT_METHODS.enter);
+
+  await extension.unload();
+  BrowserTestUtils.removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/browser/modules/test/browser/browser_UsageTelemetry_urlbar_places.js
@@ -0,0 +1,263 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This file tests urlbar telemetry with places related actions (e.g. history/
+ * bookmark selection).
+ */
+
+"use strict";
+
+const SCALAR_URLBAR = "browser.engagement.navigation.urlbar";
+
+const TEST_URL = getRootDirectory(gTestPath)
+  .replace("chrome://mochitests/content", "http://mochi.test:8888");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+  UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.jsm",
+  URLBAR_SELECTED_RESULT_TYPES: "resource:///modules/BrowserUsageTelemetry.jsm",
+  URLBAR_SELECTED_RESULT_METHODS: "resource:///modules/BrowserUsageTelemetry.jsm",
+});
+
+function searchInAwesomebar(inputText, win = window) {
+  return UrlbarTestUtils.promiseAutocompleteResultPopup(win, inputText, waitForFocus, true);
+}
+
+function assertSearchTelemetryEmpty(search_hist) {
+  const scalars = TelemetryTestUtils.getProcessScalars("parent", true, false);
+  Assert.equal(Object.keys(scalars).length, 0,
+    `Should not have recorded ${SCALAR_URLBAR}`);
+
+  // Make sure SEARCH_COUNTS contains identical values.
+  TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.urlbar", undefined);
+  TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.alias", undefined);
+
+  // Also check events.
+  let events = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  events = (events.parent || []).filter(e => e[1] == "navigation" && e[2] == "search");
+  Assert.equal(events.length, 0, "Should not have recorded any events");
+}
+
+function snapshotHistograms() {
+  Services.telemetry.clearScalars();
+  Services.telemetry.clearEvents();
+  return {
+    resultIndexHist: TelemetryTestUtils.getAndClearHistogram("FX_URLBAR_SELECTED_RESULT_INDEX"),
+    resultTypeHist: TelemetryTestUtils.getAndClearHistogram("FX_URLBAR_SELECTED_RESULT_TYPE"),
+    resultIndexByTypeHist: TelemetryTestUtils.getAndClearKeyedHistogram("FX_URLBAR_SELECTED_RESULT_INDEX_BY_TYPE"),
+    resultMethodHist: TelemetryTestUtils.getAndClearHistogram("FX_URLBAR_SELECTED_RESULT_METHOD"),
+    search_hist: TelemetryTestUtils.getAndClearKeyedHistogram("SEARCH_COUNTS"),
+  };
+}
+
+function assertHistogramResults(histograms, type, index, method) {
+  TelemetryTestUtils.assertHistogram(histograms.resultIndexHist, index, 1);
+
+  TelemetryTestUtils.assertHistogram(histograms.resultTypeHist,
+    URLBAR_SELECTED_RESULT_TYPES[type], 1);
+
+  TelemetryTestUtils.assertKeyedHistogramValue(histograms.resultIndexByTypeHist,
+    type, index, 1);
+
+  TelemetryTestUtils.assertHistogram(histograms.resultMethodHist,
+    method, 1);
+}
+
+
+add_task(async function setup() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      // Disable search suggestions in the urlbar.
+      ["browser.urlbar.suggest.searches", false],
+      // Clear historical search suggestions to avoid interference from previous
+      // tests.
+      ["browser.urlbar.maxHistoricalSearchSuggestions", 0],
+      // Use the default matching bucket configuration.
+      ["browser.urlbar.matchBuckets", "general:5,suggestion:4"],
+      // Turn autofill off.
+      ["browser.urlbar.autoFill", false],
+    ],
+  });
+
+  // Enable local telemetry recording for the duration of the tests.
+  let oldCanRecord = Services.telemetry.canRecordExtended;
+  Services.telemetry.canRecordExtended = true;
+
+  // Enable event recording for the events tested here.
+  Services.telemetry.setEventRecordingEnabled("navigation", true);
+
+  // Clear history so that history added by previous tests doesn't mess up this
+  // test when it selects results in the urlbar.
+  await PlacesUtils.history.clear();
+  await PlacesUtils.bookmarks.eraseEverything();
+
+  await PlacesUtils.keywords.insert({ keyword: "get",
+                                      url: TEST_URL + "?q=%s" });
+
+  // Make sure to restore the engine once we're done.
+  registerCleanupFunction(async function() {
+    await PlacesUtils.keywords.remove("get");
+    Services.telemetry.canRecordExtended = oldCanRecord;
+    await PlacesUtils.history.clear();
+    await PlacesUtils.bookmarks.eraseEverything();
+    Services.telemetry.setEventRecordingEnabled("navigation", false);
+  });
+});
+
+add_task(async function test_history() {
+  const histograms = snapshotHistograms();
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+  await PlacesTestUtils.addVisits([{
+    uri: "http://example.com",
+    title: "example",
+    transition: Ci.nsINavHistoryService.TRANSITION_TYPED,
+  }]);
+
+  let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  await searchInAwesomebar("example");
+  EventUtils.synthesizeKey("KEY_ArrowDown");
+  EventUtils.synthesizeKey("KEY_Enter");
+  await p;
+
+  assertSearchTelemetryEmpty(histograms.search_hist);
+  assertHistogramResults(histograms, "history", 1,
+    URLBAR_SELECTED_RESULT_METHODS.arrowEnterSelection);
+
+  BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_bookmark() {
+  const histograms = snapshotHistograms();
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+  let bm = await PlacesUtils.bookmarks.insert({
+    url: "http://example.com",
+    title: "example",
+    parentGuid: PlacesUtils.bookmarks.menuGuid,
+  });
+
+  let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  await searchInAwesomebar("example");
+  EventUtils.synthesizeKey("KEY_ArrowDown");
+  EventUtils.synthesizeKey("KEY_Enter");
+  await p;
+
+  assertSearchTelemetryEmpty(histograms.search_hist);
+  assertHistogramResults(histograms, "bookmark", 1,
+    URLBAR_SELECTED_RESULT_METHODS.arrowEnterSelection);
+
+  await PlacesUtils.bookmarks.remove(bm);
+
+  BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_keyword() {
+  const histograms = snapshotHistograms();
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+  let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  await searchInAwesomebar("get example");
+  EventUtils.synthesizeKey("KEY_Enter");
+  await p;
+
+  assertSearchTelemetryEmpty(histograms.search_hist);
+  assertHistogramResults(histograms, "keyword", 0,
+    URLBAR_SELECTED_RESULT_METHODS.enter);
+
+  BrowserTestUtils.removeTab(tab);
+});
+
+
+add_task(async function test_switchtab() {
+  const histograms = snapshotHistograms();
+
+  let homeTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:buildconfig");
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
+
+  let p = BrowserTestUtils.waitForEvent(gBrowser, "TabSwitchDone");
+  await searchInAwesomebar("about:buildconfig");
+  EventUtils.synthesizeKey("KEY_ArrowDown");
+  EventUtils.synthesizeKey("KEY_Enter");
+  await p;
+
+  assertSearchTelemetryEmpty(histograms.search_hist);
+  assertHistogramResults(histograms, "switchtab", 1,
+    URLBAR_SELECTED_RESULT_METHODS.arrowEnterSelection);
+
+  BrowserTestUtils.removeTab(tab);
+  BrowserTestUtils.removeTab(homeTab);
+});
+
+add_task(async function test_tag() {
+  if (UrlbarPrefs.get("quantumbar")) {
+    return;
+  }
+  const histograms = snapshotHistograms();
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+  Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", false);
+
+  PlacesUtils.tagging.tagURI(Services.io.newURI("http://example.com"), ["tag"]);
+
+  let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  await searchInAwesomebar("tag");
+  EventUtils.synthesizeKey("KEY_ArrowDown");
+  EventUtils.synthesizeKey("KEY_Enter");
+  await p;
+
+  assertSearchTelemetryEmpty(histograms.search_hist);
+  assertHistogramResults(histograms, "tag", 1,
+    URLBAR_SELECTED_RESULT_METHODS.arrowEnterSelection);
+
+  PlacesUtils.tagging.untagURI(Services.io.newURI("http://example.com"), ["tag"]);
+  Services.prefs.clearUserPref("browser.urlbar.suggest.bookmark");
+  BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_visitURL() {
+  const histograms = snapshotHistograms();
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+  let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  await searchInAwesomebar("http://example.com");
+  EventUtils.synthesizeKey("KEY_Enter");
+  await p;
+
+  assertSearchTelemetryEmpty(histograms.search_hist);
+  assertHistogramResults(histograms, "visiturl", 0,
+    URLBAR_SELECTED_RESULT_METHODS.enter);
+
+  BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_autofill() {
+  const histograms = snapshotHistograms();
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+  await PlacesTestUtils.addVisits([{
+    uri: "http://example.com/mypage",
+    title: "example",
+    transition: Ci.nsINavHistoryService.TRANSITION_TYPED,
+  }]);
+
+  Services.prefs.setBoolPref("browser.urlbar.autoFill", true);
+
+  let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  await searchInAwesomebar("example.com/my");
+  EventUtils.synthesizeKey("KEY_Enter");
+  await p;
+
+  assertSearchTelemetryEmpty(histograms.search_hist);
+  assertHistogramResults(histograms, "autofill", 0,
+    URLBAR_SELECTED_RESULT_METHODS.enter);
+
+  BrowserTestUtils.removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/browser/modules/test/browser/browser_UsageTelemetry_urlbar_remotetab.js
@@ -0,0 +1,161 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This file tests urlbar telemetry with remote tab action.
+ */
+
+"use strict";
+
+const SCALAR_URLBAR = "browser.engagement.navigation.urlbar";
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+  SyncedTabs: "resource://services-sync/SyncedTabs.jsm",
+  UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.jsm",
+  URLBAR_SELECTED_RESULT_TYPES: "resource:///modules/BrowserUsageTelemetry.jsm",
+  URLBAR_SELECTED_RESULT_METHODS: "resource:///modules/BrowserUsageTelemetry.jsm",
+});
+
+function assertSearchTelemetryEmpty(search_hist) {
+  const scalars = TelemetryTestUtils.getProcessScalars("parent", true, false);
+  Assert.equal(Object.keys(scalars).length, 0,
+    `Should not have recorded ${SCALAR_URLBAR}`);
+
+  // Make sure SEARCH_COUNTS contains identical values.
+  TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.urlbar", undefined);
+  TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.alias", undefined);
+
+  // Also check events.
+  let events = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  events = (events.parent || []).filter(e => e[1] == "navigation" && e[2] == "search");
+  Assert.equal(events.length, 0, "Should not have recorded any events");
+}
+
+function snapshotHistograms() {
+  Services.telemetry.clearScalars();
+  Services.telemetry.clearEvents();
+  return {
+    resultIndexHist: TelemetryTestUtils.getAndClearHistogram("FX_URLBAR_SELECTED_RESULT_INDEX"),
+    resultTypeHist: TelemetryTestUtils.getAndClearHistogram("FX_URLBAR_SELECTED_RESULT_TYPE"),
+    resultIndexByTypeHist: TelemetryTestUtils.getAndClearKeyedHistogram("FX_URLBAR_SELECTED_RESULT_INDEX_BY_TYPE"),
+    resultMethodHist: TelemetryTestUtils.getAndClearHistogram("FX_URLBAR_SELECTED_RESULT_METHOD"),
+    search_hist: TelemetryTestUtils.getAndClearKeyedHistogram("SEARCH_COUNTS"),
+  };
+}
+
+function assertHistogramResults(histograms, type, index, method) {
+  TelemetryTestUtils.assertHistogram(histograms.resultIndexHist, index, 1);
+
+  TelemetryTestUtils.assertHistogram(histograms.resultTypeHist,
+    URLBAR_SELECTED_RESULT_TYPES[type], 1);
+
+  TelemetryTestUtils.assertKeyedHistogramValue(histograms.resultIndexByTypeHist,
+    type, index, 1);
+
+  TelemetryTestUtils.assertHistogram(histograms.resultMethodHist,
+    method, 1);
+}
+
+
+add_task(async function setup() {
+  /* global sinon */
+  Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
+
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      // Disable search suggestions in the urlbar.
+      ["browser.urlbar.suggest.searches", false],
+      // Clear historical search suggestions to avoid interference from previous
+      // tests.
+      ["browser.urlbar.maxHistoricalSearchSuggestions", 0],
+      // Use the default matching bucket configuration.
+      ["browser.urlbar.matchBuckets", "general:5,suggestion:4"],
+      // Turn autofill off.
+      ["browser.urlbar.autoFill", false],
+      // Special prefs for remote tabs.
+      ["services.sync.username", "fake"],
+      ["services.sync.syncedTabs.showRemoteTabs", true],
+    ],
+  });
+
+  // Enable local telemetry recording for the duration of the tests.
+  let oldCanRecord = Services.telemetry.canRecordExtended;
+  Services.telemetry.canRecordExtended = true;
+
+  // Enable event recording for the events tested here.
+  Services.telemetry.setEventRecordingEnabled("navigation", true);
+
+  // Clear history so that history added by previous tests doesn't mess up this
+  // test when it selects results in the urlbar.
+  await PlacesUtils.history.clear();
+  await PlacesUtils.bookmarks.eraseEverything();
+
+  const REMOTE_TAB = {
+    "id": "7cqCr77ptzX3",
+    "type": "client",
+    "lastModified": 1492201200,
+    "name": "zcarter's Nightly on MacBook-Pro-25",
+    "clientType": "desktop",
+    "tabs": [
+      {
+        "type": "tab",
+        "title": "Test Remote",
+        "url": "http://example.com",
+        "icon": UrlbarUtils.ICON.DEFAULT,
+        "client": "7cqCr77ptzX3",
+        "lastUsed": 1452124677,
+      },
+    ],
+  };
+
+  const sandbox = sinon.sandbox.create();
+
+  let originalSyncedTabsInternal = SyncedTabs._internal;
+  SyncedTabs._internal = {
+    isConfiguredToSyncTabs: true,
+    hasSyncedThisSession: true,
+    getTabClients() { return Promise.resolve([]); },
+    syncTabs() { return Promise.resolve(); },
+  };
+
+  // Tell the Sync XPCOM service it is initialized.
+  let weaveXPCService = Cc["@mozilla.org/weave/service;1"]
+                          .getService(Ci.nsISupports)
+                          .wrappedJSObject;
+  let oldWeaveServiceReady = weaveXPCService.ready;
+  weaveXPCService.ready = true;
+
+  sandbox.stub(SyncedTabs._internal, "getTabClients")
+         .callsFake(() => Promise.resolve(Cu.cloneInto([REMOTE_TAB], {})));
+
+  // Make sure to restore the engine once we're done.
+  registerCleanupFunction(async function() {
+    sandbox.restore();
+    weaveXPCService.ready = oldWeaveServiceReady;
+    SyncedTabs._internal = originalSyncedTabsInternal;
+    Services.telemetry.canRecordExtended = oldCanRecord;
+    await PlacesUtils.history.clear();
+    await PlacesUtils.bookmarks.eraseEverything();
+    Services.telemetry.setEventRecordingEnabled("navigation", false);
+    delete window.sinon;
+  });
+});
+
+add_task(async function test_remotetab() {
+  const histograms = snapshotHistograms();
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+  let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  await UrlbarTestUtils.promiseAutocompleteResultPopup(window, "example", waitForFocus, true);
+  EventUtils.synthesizeKey("KEY_ArrowDown");
+  EventUtils.synthesizeKey("KEY_Enter");
+  await p;
+
+  assertSearchTelemetryEmpty(histograms.search_hist);
+  assertHistogramResults(histograms, "remotetab", 1,
+    URLBAR_SELECTED_RESULT_METHODS.arrowEnterSelection);
+
+  BrowserTestUtils.removeTab(tab);
+});
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -399,23 +399,16 @@ toolbarbutton[constrain-size="true"][cui
   width: @panelPaletteIconSize@;
   height: @panelPaletteIconSize@;
   min-width: @panelPaletteIconSize@;
   min-height: @panelPaletteIconSize@;
   margin: 0;
   padding: 0;
 }
 
-#edit-controls@inAnyPanel@ > #copy-button,
-#zoom-controls@inAnyPanel@ > #zoom-reset-button {
-  border-left: none;
-  border-right: none;
-  border-radius: 0;
-}
-
 #zoom-in-button > .toolbarbutton-text,
 #zoom-out-button > .toolbarbutton-text,
 #zoom-reset-button > .toolbarbutton-icon {
   display: none;
 }
 
 .addon-banner-item::after,
 .panel-banner-item::after {
@@ -1245,32 +1238,16 @@ toolbarpaletteitem[place="menu-panel"] >
 #zoom-controls[cui-areatype="toolbar"] > #zoom-reset-button > .toolbarbutton-text {
 %ifdef XP_MACOSX
   min-width: 6ch;
 %else
   min-width: 7ch;
 %endif
 }
 
-#edit-controls@inAnyPanel@ > #cut-button:-moz-locale-dir(ltr),
-#edit-controls@inAnyPanel@ > #paste-button:-moz-locale-dir(rtl),
-#zoom-controls@inAnyPanel@ > #zoom-out-button:-moz-locale-dir(ltr),
-#zoom-controls@inAnyPanel@ > #zoom-in-button:-moz-locale-dir(rtl) {
-  border-top-right-radius: 0;
-  border-bottom-right-radius: 0;
-}
-
-#edit-controls@inAnyPanel@ > #cut-button:-moz-locale-dir(rtl),
-#edit-controls@inAnyPanel@ > #paste-button:-moz-locale-dir(ltr),
-#zoom-controls@inAnyPanel@ > #zoom-out-button:-moz-locale-dir(rtl),
-#zoom-controls@inAnyPanel@ > #zoom-in-button:-moz-locale-dir(ltr) {
-  border-top-left-radius: 0;
-  border-bottom-left-radius: 0;
-}
-
 .toolbaritem-combined-buttons@inAnyPanel@ > separator {
   -moz-appearance: none;
   -moz-box-align: stretch;
   margin: .5em 0;
   width: 1px;
   height: auto;
   background: var(--panel-separator-color);
   transition-property: margin;
--- a/devtools/CODE_OF_CONDUCT.md
+++ b/devtools/CODE_OF_CONDUCT.md
@@ -43,17 +43,17 @@ attention-stealing behaviour is not welc
 These are the policies for upholding our community’s standards of
 conduct. If you feel that a thread needs moderation, please use the
 point of contact for the medium in which you're communicating:
 
 * For one of the IRC channels, contact a channel operator (they
   have an "@" in front of their names);
 * Bugzilla and the dev-developer-tools mailing list, contact
   [inclusion@mozilla.com](mailto:inclusion@mozilla.com);
-* The debugger.html repository on GitHub has [its own code of conduct](https://github.com/devtools-html/debugger.html/blob/master/CODE_OF_CONDUCT.md), but you can also
+* The debugger.html repository on GitHub has [its own code of conduct](https://github.com/firefox-devtools/debugger.html/blob/master/CODE_OF_CONDUCT.md), but you can also
   email [inclusion@mozilla.com](mailto:inclusion@mozilla.com).
 
 Remarks that violate these standards of conduct, including hateful,
 hurtful, oppressive, or exclusionary remarks, are not
 allowed. (Cursing is allowed, but never targeting another user, and
 never in a hateful manner.)
 
 Remarks that moderators find inappropriate, whether listed in the code
--- a/devtools/client/accessibility/components/Accessible.js
+++ b/devtools/client/accessibility/components/Accessible.js
@@ -393,17 +393,17 @@ const isNode = value => value && value.t
 /**
  * Check if a given property is an Accessible actor.
  * @param  {Object?} value A property to check for being an Accessible.
  * @return {Boolean}       A flag that indicates whether a property is an Accessible.
  */
 const isAccessible = value => value && value.typeName === "accessible";
 
 /**
- * While waiting for a reps fix in https://github.com/devtools-html/reps/issues/92,
+ * While waiting for a reps fix in https://github.com/firefox-devtools/reps/issues/92,
  * translate accessibleFront to a grip-like object that can be used with an Accessible
  * rep.
  *
  * @params  {accessibleFront} accessibleFront
  *          The AccessibleFront for which we want to create a grip-like object.
  * @returns {Object} a grip-like object that can be used with Reps.
  */
 const translateAccessibleFrontToGrip = accessibleFront => ({
--- a/devtools/client/inspector/grids/components/GridItem.js
+++ b/devtools/client/inspector/grids/components/GridItem.js
@@ -69,17 +69,17 @@ class GridItem extends PureComponent {
     this.props.onSetGridOverlayColor(this.props.grid.nodeFront, color);
   }
 
   onGridCheckboxClick(e) {
     // If the click was on the svg icon to select the node in the inspector, bail out.
     const originalTarget = e.nativeEvent && e.nativeEvent.explicitOriginalTarget;
     if (originalTarget && originalTarget.namespaceURI === "http://www.w3.org/2000/svg") {
       // We should be able to cancel the click event propagation after the following reps
-      // issue is implemented : https://github.com/devtools-html/reps/issues/95 .
+      // issue is implemented : https://github.com/firefox-devtools/reps/issues/95 .
       e.preventDefault();
       return;
     }
 
     const {
       grid,
       onToggleGridHighlighter,
     } = this.props;
--- a/devtools/client/inspector/layout/components/Accordion.css
+++ b/devtools/client/inspector/layout/components/Accordion.css
@@ -1,17 +1,17 @@
 /* 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 a duplicate from the debugger.html project and has diverged in terms of
  * some of styles that are kept. Any changes to the styles in this file should be synced
  * back to the appropriate styles in the debugger.html project.
- * Create a pull request at https://github.com/devtools-html/debugger.html to sync
+ * Create a pull request at https://github.com/firefox-devtools/debugger.html to sync
  * any changes to the existing styles.
  */
 
 :root {
   --accordion-header-background: var(--theme-toolbar-background);
 }
 
 :root.theme-dark {
--- a/devtools/client/inspector/shared/utils.js
+++ b/devtools/client/inspector/shared/utils.js
@@ -155,17 +155,17 @@ function getSelectorFromGrip(grip) {
  * @return {Promise} A rejected promise
  */
 function promiseWarn(error) {
   console.error(error);
   return promise.reject(error);
 }
 
 /**
- * While waiting for a reps fix in https://github.com/devtools-html/reps/issues/92,
+ * While waiting for a reps fix in https://github.com/firefox-devtools/reps/issues/92,
  * translate nodeFront to a grip-like object that can be used with an ElementNode rep.
  *
  * @params  {NodeFront} nodeFront
  *          The NodeFront for which we want to create a grip-like object.
  * @returns {Object} a grip-like object that can be used with Reps.
  */
 function translateNodeFrontToGrip(nodeFront) {
   const { attributes } = nodeFront;
--- a/devtools/client/netmonitor/README.md
+++ b/devtools/client/netmonitor/README.md
@@ -19,17 +19,17 @@ Files used to run the Network Monitor in
 Files used to run the Network Monitor in the browser tab
 
 * `bin/` files to launch test server.
 * `configs/` dev configs.
 * `launchpad.js` the entry point, equivalent to `index.html`.
 * `webpack.config.js` the webpack config file, including plenty of module alias map to shims and polyfills.
 * `package.json` declare every required packages and available commands.
 
-To run in the browser tab, the Network Monitor needs to get some dependencies from npm module. Check `package.json` to see all dependencies. Check `webpack.config.js` to find the module alias, and check [devtools-core](https://github.com/devtools-html/devtools-core) packages to dive into actual modules used by the Network Monitor and other Devtools.
+To run in the browser tab, the Network Monitor needs to get some dependencies from npm module. Check `package.json` to see all dependencies. Check `webpack.config.js` to find the module alias, and check [devtools-core](https://github.com/firefox-devtools/devtools-core) packages to dive into actual modules used by the Network Monitor and other Devtools.
 
 ### UI
 
 The Network Monitor UI is built using [React](http://searchfox.org/mozilla-central/source/devtools/docs/frontend/react.md) components (in `src/components/`).
 
 * **MonitorPanel** in `MonitorPanel.js` is the root element.
 * Three major container components are
   - **Toolbar** Panel related functions.
--- a/devtools/client/netmonitor/src/components/ParamsPanel.js
+++ b/devtools/client/netmonitor/src/components/ParamsPanel.js
@@ -133,24 +133,23 @@ class ParamsPanel extends Component {
       try {
         json = JSON.parse(postData);
       } catch (error) {
         // Continue regardless of parsing error
       }
 
       if (json) {
         object[JSON_SCOPE_NAME] = sortObjectKeys(json);
-      } else {
-        object[PARAMS_POST_PAYLOAD] = {
-          EDITOR_CONFIG: {
-            text: postData,
-            mode: mimeType.replace(/;.+/, ""),
-          },
-        };
       }
+      object[PARAMS_POST_PAYLOAD] = {
+        EDITOR_CONFIG: {
+          text: postData,
+          mode: mimeType.replace(/;.+/, ""),
+        },
+      };
     } else {
       postData = "";
     }
 
     return (
       div({ className: "panel-container" },
         PropertiesView({
           object,
--- a/devtools/client/netmonitor/test/browser_net_complex-params.js
+++ b/devtools/client/netmonitor/test/browser_net_complex-params.js
@@ -36,31 +36,34 @@ add_task(async function() {
   testParamsTab1("a", "b", '{ "foo": "bar" }', "");
 
   wait = waitForDOM(document, "#params-panel .tree-section", 2);
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[2]);
   await wait;
   testParamsTab1("a", "b", "?foo", "bar");
 
-  wait = waitForDOM(document, "#params-panel tr:not(.tree-section).treeRow", 2);
+  let waitSections, waitSourceEditor;
+  waitSections = waitForDOM(document, "#params-panel tr:not(.tree-section).treeRow", 2);
+  waitSourceEditor = waitForDOM(document, "#params-panel .CodeMirror-code");
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[3]);
-  await wait;
+  await Promise.all([waitSections, waitSourceEditor]);
   testParamsTab2("a", "", '{ "foo": "bar" }', "js");
 
-  wait = waitForDOM(document, "#params-panel tr:not(.tree-section).treeRow", 2);
+  waitSections = waitForDOM(document, "#params-panel tr:not(.tree-section).treeRow", 2);
+  waitSourceEditor = waitForDOM(document, "#params-panel .CodeMirror-code");
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[4]);
-  await wait;
+  await Promise.all([waitSections, waitSourceEditor]);
   testParamsTab2("a", "b", '{ "foo": "bar" }', "js");
 
   // Wait for all tree sections and editor updated by react
-  const waitSections = waitForDOM(document, "#params-panel .tree-section", 2);
-  const waitSourceEditor = waitForDOM(document, "#params-panel .CodeMirror-code");
+  waitSections = waitForDOM(document, "#params-panel .tree-section", 2);
+  waitSourceEditor = waitForDOM(document, "#params-panel .CodeMirror-code");
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[5]);
   await Promise.all([waitSections, waitSourceEditor]);
   testParamsTab2("a", "b", "?foo=bar", "text");
 
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[6]);
   testParamsTab3();
@@ -144,28 +147,27 @@ add_task(async function() {
       "The first form data param value was incorrect.");
   }
 
   function testParamsTab2(queryStringParamName, queryStringParamValue,
                           requestPayload, editorMode) {
     const isJSON = editorMode === "js";
     const tabpanel = document.querySelector("#params-panel");
 
-    is(tabpanel.querySelectorAll(".tree-section").length, 2,
+    is(tabpanel.querySelectorAll(".tree-section").length, isJSON ? 3 : 2,
       "The number of param tree sections displayed in this tabpanel is incorrect.");
     is(tabpanel.querySelectorAll("tr:not(.tree-section).treeRow").length, isJSON ? 2 : 1,
       "The number of param rows displayed in this tabpanel is incorrect.");
     is(tabpanel.querySelectorAll(".empty-notice").length, 0,
       "The empty notice should not be displayed in this tabpanel.");
 
     ok(tabpanel.querySelector(".treeTable"),
       "The request params box should be displayed.");
-    is(tabpanel.querySelector(".CodeMirror-code") === null,
-      isJSON,
-      "The request post data editor should be not displayed.");
+    ok(tabpanel.querySelector(".CodeMirror-code"),
+      "The request post data editor should be displayed.");
 
     const treeSections = tabpanel.querySelectorAll(".tree-section");
 
     is(treeSections[0].querySelector(".treeLabel").textContent,
       L10N.getStr("paramsQueryString"),
       "The query section doesn't have the correct title.");
     is(treeSections[1].querySelector(".treeLabel").textContent,
       isJSON ? L10N.getStr("jsonScopeName") : L10N.getStr("paramsPostPayload"),
@@ -176,30 +178,34 @@ add_task(async function() {
     const values = tabpanel
       .querySelectorAll("tr:not(.treeS-section) .treeValueCell .objectBox");
 
     is(labels[0].textContent, queryStringParamName,
       "The first query string param name was incorrect.");
     is(values[0].textContent, queryStringParamValue,
       "The first query string param value was incorrect.");
 
+    ok(tabpanel.querySelector(".CodeMirror-code").textContent.includes(requestPayload),
+      "The text shown in the source editor is incorrect.");
+
     if (isJSON) {
+      is(treeSections[2].querySelector(".treeLabel").textContent,
+        L10N.getStr("paramsPostPayload"),
+        "The post section doesn't have the correct title.");
+
       const requestPayloadObject = JSON.parse(requestPayload);
       const requestPairs = Object.keys(requestPayloadObject)
         .map(k => [k, requestPayloadObject[k]]);
       for (let i = 1; i < requestPairs.length; i++) {
         const [requestPayloadName, requestPayloadValue] = requestPairs[i];
         is(requestPayloadName, labels[i].textContent,
           "JSON property name " + i + " should be displayed correctly");
         is('"' + requestPayloadValue + '"', values[i].textContent,
           "JSON property value " + i + " should be displayed correctly");
       }
-    } else {
-      ok(document.querySelector(".CodeMirror-code").textContent.includes(requestPayload),
-        "The text shown in the source editor is incorrect.");
     }
   }
 
   function testParamsTab3() {
     const tabpanel = document.querySelector("#params-panel");
 
     is(tabpanel.querySelectorAll(".tree-section").length, 0,
       "The number of param tree sections displayed in this tabpanel is incorrect.");
--- a/devtools/client/netmonitor/test/browser_net_post-data-04.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-04.js
@@ -17,41 +17,48 @@ add_task(async function() {
   const { document, store, windowRequire } = monitor.panelWin;
   const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   // Execute requests.
   await performRequests(monitor, tab, 1);
 
-  // Wait for all tree view updated by react
-  wait = waitForDOM(document, "#params-panel .tree-section");
+  // Wait for all tree view updated by react, Bug 1514750 - wait for editor also
+  const waitSections = waitForDOM(document, "#params-panel .tree-section", 2);
+  const waitSourceEditor = waitForDOM(document, "#params-panel .CodeMirror-code");
   store.dispatch(Actions.toggleNetworkDetails());
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#params-tab"));
-  await wait;
-
+  await Promise.all([waitSections, waitSourceEditor]);
   const tabpanel = document.querySelector("#params-panel");
 
   ok(tabpanel.querySelector(".treeTable"),
     "The request params doesn't have the indended visibility.");
-  ok(tabpanel.querySelector(".editor-mount") === null,
+  // Bug 1514750 - Show JSON params in plain text view also
+  ok(tabpanel.querySelector(".CodeMirror-code"),
     "The request post data doesn't have the indended visibility.");
-
-  is(tabpanel.querySelectorAll(".tree-section").length, 1,
-    "There should be 1 tree sections displayed in this tabpanel.");
+  is(tabpanel.querySelectorAll(".tree-section").length, 2,
+    "There should be 2 tree sections displayed in this tabpanel.");
   is(tabpanel.querySelectorAll(".empty-notice").length, 0,
     "The empty notice should not be displayed in this tabpanel.");
 
-  is(tabpanel.querySelector(".tree-section .treeLabel").textContent,
-    L10N.getStr("jsonScopeName"),
-    "The JSON section doesn't have the correct title.");
+  const treeSections = tabpanel.querySelectorAll(".tree-section");
+
+  is(treeSections[0].querySelector(".treeLabel").textContent,
+    L10N.getStr("jsonScopeName"), "The post section doesn't have the correct title.");
+  is(treeSections[1].querySelector(".treeLabel").textContent,
+    L10N.getStr("paramsPostPayload"),
+    "The post section doesn't have the correct title.");
 
   const labels = tabpanel
     .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
   const values = tabpanel
     .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
 
   is(labels[0].textContent, "a", "The JSON var name was incorrect.");
   is(values[0].textContent, "1", "The JSON var value was incorrect.");
 
+  ok(tabpanel.querySelector(".CodeMirror-code").textContent.includes('{"a":1}'),
+    "The text shown in the source editor is incorrect.");
+
   return teardown(monitor);
 });
--- a/devtools/client/performance-new/components/Description.js
+++ b/devtools/client/performance-new/components/Description.js
@@ -51,18 +51,18 @@ class Description extends PureComponent 
           "https://perf-html.io",
           "perf-html.io"
         ),
         ", a Mozilla performance analysis tool."
       ),
       p(null,
         "This is still a prototype. Join along or file bugs at: ",
         this.renderLink(
-          "https://github.com/devtools-html/perf.html",
-          "github.com/devtools-html/perf.html"
+          "https://github.com/firefox-devtools/perf.html",
+          "github.com/firefox-devtools/perf.html"
         ),
         "."
       )
     );
   }
 }
 
 module.exports = Description;
--- a/devtools/client/performance-new/frame-script.js
+++ b/devtools/client/performance-new/frame-script.js
@@ -57,17 +57,17 @@ function getSymbolTable(debugName, break
       reject,
     });
   });
 }
 
 // The following functions handle the security of cloning the object into the page.
 // The code was taken from the original Gecko Profiler Add-on to maintain
 // compatibility with the existing profile importing mechanism:
-// See: https://github.com/devtools-html/Gecko-Profiler-Addon/blob/78138190b42565f54ce4022a5b28583406489ed2/data/tab-framescript.js
+// See: https://github.com/firefox-devtools/Gecko-Profiler-Addon/blob/78138190b42565f54ce4022a5b28583406489ed2/data/tab-framescript.js
 
 /**
  * Create a promise that can be used in the page.
  */
 function createPromiseInPage(fun, contentGlobal) {
   function funThatClonesObjects(resolve, reject) {
     return fun(result => resolve(Cu.cloneInto(result, contentGlobal)),
                error => reject(Cu.cloneInto(error, contentGlobal)));
--- a/devtools/client/shared/components/SmartTrace.css
+++ b/devtools/client/shared/components/SmartTrace.css
@@ -118,17 +118,17 @@
   color: var(--theme-highlight-blue);
 }
 
 /** Images **/
 
 .frames .img.annotation-logo {
   /* FIXME: In order to display the Framework icons, we need to find a way to share CSS
    * from the debugger, where the background images are defined.
-   * See https://github.com/devtools-html/debugger.html/issues/7782.
+   * See https://github.com/firefox-devtools/debugger.html/issues/7782.
    */
   display: none;
   /*
   background-color:var(--theme-body-color);
   display: inline-block;
   width: 12px;
   height:12px;
   vertical-align: middle;
--- a/devtools/client/shared/components/reps/README
+++ b/devtools/client/shared/components/reps/README
@@ -1,7 +1,7 @@
-Reps are now maintained on GitHub at: https://github.com/devtools-html/debugger.html/tree/master/packages/devtools-reps
+Reps are now maintained on GitHub at: https://github.com/firefox-devtools/debugger.html/tree/master/packages/devtools-reps
 
 All the files in this folder are copied from the debugger.html github repository and
 should not be modified here.
 
 For any issue or feature request on Reps, please log an issue at
-https://github.com/devtools-html/debugger.html/issues with the label "devtools-reps".
+https://github.com/firefox-devtools/debugger.html/issues with the label "devtools-reps".
--- a/devtools/client/shared/components/reps/reps-old.js
+++ b/devtools/client/shared/components/reps/reps-old.js
@@ -2243,17 +2243,17 @@ function arrayIterator(props, grip, max)
   if (gripLength > itemsShown) {
     items.push(ellipsisElement);
   }
 
   return items;
 }
 
 function getEmptySlotsElement(number) {
-  // TODO: Use l10N - See https://github.com/devtools-html/reps/issues/141
+  // TODO: Use l10N - See https://github.com/firefox-devtools/reps/issues/141
   return `<${number} empty slot${number > 1 ? "s" : ""}>`;
 }
 
 function supportsObject(grip, noGrip = false) {
   if (noGrip === true || !isGrip(grip)) {
     return false;
   }
 
@@ -7065,9 +7065,9 @@ function supportsObject(object, noGrip =
 module.exports = {
   rep: wrapRender(Accessible),
   supportsObject
 };
 
 /***/ })
 
 /******/ });
-});
\ No newline at end of file
+});
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -2285,17 +2285,17 @@ function arrayIterator(props, grip, max)
   if (gripLength > itemsShown) {
     items.push(ellipsisElement);
   }
 
   return items;
 }
 
 function getEmptySlotsElement(number) {
-  // TODO: Use l10N - See https://github.com/devtools-html/reps/issues/141
+  // TODO: Use l10N - See https://github.com/firefox-devtools/reps/issues/141
   return `<${number} empty slot${number > 1 ? "s" : ""}>`;
 }
 
 function supportsObject(grip, noGrip = false) {
   if (noGrip === true || !isGrip(grip)) {
     return false;
   }
 
@@ -7215,9 +7215,9 @@ class ObjectInspectorItem extends Compon
   }
 }
 
 module.exports = ObjectInspectorItem;
 
 /***/ })
 
 /******/ });
-});
\ No newline at end of file
+});
--- a/devtools/client/shared/source-map/README
+++ b/devtools/client/shared/source-map/README
@@ -1,12 +1,12 @@
 devtools-source-map is maintained on GitHub at:
 
-https://github.com/devtools-html/debugger.html/tree/master/packages/devtools-source-map
+https://github.com/firefox-devtools/debugger.html/tree/master/packages/devtools-source-map
 
 All the files in this folder are copied from the above repository and
 should not be modified here.
 
 For any issue or feature request on devtools-source-map, please log an issue at:
 
-https://github.com/devtools-html/debugger.html/issues
+https://github.com/firefox-devtools/debugger.html/issues
 
-and label it with "devtools-source-map".
\ No newline at end of file
+and label it with "devtools-source-map".
--- a/devtools/client/shared/unicode-url.js
+++ b/devtools/client/shared/unicode-url.js
@@ -1,24 +1,24 @@
 /* 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/. */
 "use strict";
 
 // This file is a chrome-API-dependent version of the file
 // packages/devtools-modules/src/unicode-url.js at
-// https://github.com/devtools-html/devtools-core, so that this
+// https://github.com/firefox-devtools/devtools-core, so that this
 // chrome-API-dependent version can take advantage of utilizing chrome APIs. But
 // because of this, it isn't intended to be used in Chrome-API-free
 // applications, such as the Launchpad.
 //
 // Please keep in mind that if the feature in this file has changed, don't
 // forget to also change that accordingly in the file
 // packages/devtools-modules/src/unicode-url.js at
-// https://github.com/devtools-html/devtools-core
+// https://github.com/firefox-devtools/devtools-core
 
 const { Cc, Ci } = require("chrome");
 const idnService =
         Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService);
 
 /**
  * Gets a readble Unicode hostname from a hostname.
  *
--- a/devtools/client/shared/webpack/shims/jsterm-stub.js
+++ b/devtools/client/shared/webpack/shims/jsterm-stub.js
@@ -1,19 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { ConsoleCommand } = require("devtools/client/webconsole/types");
 
-function JSTerm(webConsoleFrame) {
-  this.hud = webConsoleFrame;
-  this.hudId = this.hud.hudId;
+function JSTerm(webConsoleUI) {
+  this.ui = webConsoleUI;
+  this.hudId = this.ui.hudId;
   this.historyLoaded = new Promise(r => {
     r();
   });
 }
 
 JSTerm.prototype = {
   SELECTED_FRAME: -1,
 
@@ -67,31 +67,31 @@ JSTerm.prototype = {
       executeString = executeString || this.getInputValue();
       if (!executeString) {
         return;
       }
 
       const message = new ConsoleCommand({
         messageText: executeString,
       });
-      this.hud.proxy.dispatchMessageAdd(message);
+      this.ui.proxy.dispatchMessageAdd(message);
 
       let selectedNodeActor = null;
-      const inspectorSelection = this.hud.owner.getInspectorSelection();
+      const inspectorSelection = this.ui.owner.getInspectorSelection();
       if (inspectorSelection && inspectorSelection.nodeFront) {
         selectedNodeActor = inspectorSelection.nodeFront.actorID;
       }
 
       const onResult = (response) => {
         if (response.error) {
           console.error("Evaluation error " + response.error + ": " +
                         response.message);
           return;
         }
-        this.hud.wrapper.dispatchMessageAdd(response, true).then(resolve);
+        this.ui.wrapper.dispatchMessageAdd(response, true).then(resolve);
       };
 
       const options = {
         frame: this.SELECTED_FRAME,
         selectedNodeActor: selectedNodeActor,
       };
 
       this.requestEvaluation(executeString, options).then(onResult, onResult);
@@ -142,17 +142,17 @@ JSTerm.prototype = {
    * Retrieve the FrameActor ID given a frame depth.
    *
    * @param number frame
    *        Frame depth.
    * @return string|null
    *         The FrameActor ID for the given frame depth.
    */
   getFrameActor(frame) {
-    const state = this.hud.owner.getDebuggerFrames();
+    const state = this.ui.owner.getDebuggerFrames();
     if (!state) {
       return null;
     }
 
     let grip;
     if (frame == this.SELECTED_FRAME) {
       grip = state.frames[state.selected];
     } else {
--- a/devtools/client/webconsole/components/App.js
+++ b/devtools/client/webconsole/components/App.js
@@ -34,19 +34,19 @@ const { div } = dom;
 const isMacOS = Services.appinfo.OS === "Darwin";
 
 /**
  * Console root Application component.
  */
 class App extends Component {
   static get propTypes() {
     return {
-      attachRefToHud: PropTypes.func.isRequired,
+      attachRefToWebConsoleUI: PropTypes.func.isRequired,
       dispatch: PropTypes.func.isRequired,
-      hud: PropTypes.object.isRequired,
+      webConsoleUI: PropTypes.object.isRequired,
       notifications: PropTypes.object,
       onFirstMeaningfulPaint: PropTypes.func.isRequired,
       serviceContainer: PropTypes.object.isRequired,
       closeSplitConsole: PropTypes.func.isRequired,
       jstermCodeMirror: PropTypes.bool,
       currentReverseSearchEntry: PropTypes.string,
       reverseSearchInputVisible: PropTypes.bool,
       reverseSearchInitialValue: PropTypes.string,
@@ -59,35 +59,35 @@ class App extends Component {
     this.onClick = this.onClick.bind(this);
     this.onPaste = this.onPaste.bind(this);
     this.onKeyDown = this.onKeyDown.bind(this);
   }
 
   onKeyDown(event) {
     const {
       dispatch,
-      hud,
+      webConsoleUI,
     } = this.props;
 
     if (
       (!isMacOS && event.key === "F9") ||
       (isMacOS && event.key === "r" && event.ctrlKey === true)
     ) {
-      const initialValue = hud.jsterm && hud.jsterm.getSelectedText();
+      const initialValue = webConsoleUI.jsterm && webConsoleUI.jsterm.getSelectedText();
       dispatch(actions.reverseSearchInputToggle({initialValue}));
       event.stopPropagation();
     }
   }
 
   onClick(event) {
     const target = event.originalTarget || event.target;
     const {
       reverseSearchInputVisible,
       dispatch,
-      hud,
+      webConsoleUI,
     } = this.props;
 
     if (reverseSearchInputVisible === true && !target.closest(".reverse-search")) {
       event.preventDefault();
       event.stopPropagation();
       dispatch(actions.reverseSearchInputToggle());
       return;
     }
@@ -114,40 +114,40 @@ class App extends Component {
 
     // Do not focus if something other than the output region was clicked
     // (including e.g. the clear messages button in toolbar)
     if (!target.closest(".webconsole-app")) {
       return;
     }
 
     // Do not focus if something is selected
-    const selection = hud.document.defaultView.getSelection();
+    const selection = webConsoleUI.document.defaultView.getSelection();
     if (selection && !selection.isCollapsed) {
       return;
     }
 
-    if (hud && hud.jsterm) {
-      hud.jsterm.focus();
+    if (webConsoleUI && webConsoleUI.jsterm) {
+      webConsoleUI.jsterm.focus();
     }
   }
 
   onPaste(event) {
     const {
       dispatch,
-      hud,
+      webConsoleUI,
       notifications,
     } = this.props;
 
     const {
       usageCount,
       CONSOLE_ENTRY_THRESHOLD,
     } = WebConsoleUtils;
 
     // Bail out if self-xss notification is suppressed.
-    if (hud.isBrowserConsole || usageCount >= CONSOLE_ENTRY_THRESHOLD) {
+    if (webConsoleUI.isBrowserConsole || usageCount >= CONSOLE_ENTRY_THRESHOLD) {
       return;
     }
 
     // Stop event propagation, so the clipboard content is *not* inserted.
     event.preventDefault();
     event.stopPropagation();
 
     // Bail out if self-xss notification is already there.
@@ -187,18 +187,18 @@ class App extends Component {
 
     input.addEventListener("keyup", pasteKeyUpHandler);
   }
 
   // Rendering
 
   render() {
     const {
-      attachRefToHud,
-      hud,
+      attachRefToWebConsoleUI,
+      webConsoleUI,
       notifications,
       onFirstMeaningfulPaint,
       serviceContainer,
       closeSplitConsole,
       jstermCodeMirror,
       reverseSearchInitialValue,
     } = this.props;
 
@@ -220,46 +220,44 @@ class App extends Component {
         className: classNames.join(" "),
         onKeyDown: this.onKeyDown,
         onClick: this.onClick,
         ref: node => {
           this.node = node;
         }},
         div({className: "webconsole-flex-wrapper"},
           FilterBar({
-            hidePersistLogsCheckbox: hud.isBrowserConsole,
-            serviceContainer: {
-              attachRefToHud,
-            },
+            hidePersistLogsCheckbox: webConsoleUI.isBrowserConsole,
+            attachRefToWebConsoleUI,
             closeSplitConsole,
           }),
           ConsoleOutput({
             serviceContainer,
             onFirstMeaningfulPaint,
           }),
           NotificationBox({
             id: "webconsole-notificationbox",
             notifications,
           }),
           JSTerm({
-            hud,
+            webConsoleUI,
             serviceContainer,
             onPaste: this.onPaste,
             codeMirrorEnabled: jstermCodeMirror,
           }),
           ReverseSearchInput({
-            hud,
+            webConsoleUI,
             initialValue: reverseSearchInitialValue,
           })
         ),
         SideBar({
           serviceContainer,
         }),
         ConfirmDialog({
-          hud,
+          webConsoleUI,
           serviceContainer,
           codeMirrorEnabled: jstermCodeMirror,
         }),
       )
     );
   }
 }
 
--- a/devtools/client/webconsole/components/ConfirmDialog.js
+++ b/devtools/client/webconsole/components/ConfirmDialog.js
@@ -25,57 +25,57 @@ const utmParams = new URLSearchParams({
 });
 const LEARN_MORE_URL =
   `https://developer.mozilla.org/docs/Tools/Web_Console/Invoke_getters_from_autocomplete?${utmParams}`;
 
 class ConfirmDialog extends Component {
   static get propTypes() {
     return {
       // Console object.
-      hud: PropTypes.object.isRequired,
+      webConsoleUI: PropTypes.object.isRequired,
       // Update autocomplete popup state.
       autocompleteUpdate: PropTypes.func.isRequired,
       autocompleteClear: PropTypes.func.isRequired,
       // Data to be displayed in the confirm dialog.
       getterPath: PropTypes.array,
       serviceContainer: PropTypes.object.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
-    const { hud } = props;
-    hud.confirmDialog = this;
+    const { webConsoleUI } = props;
+    webConsoleUI.confirmDialog = this;
 
     this.cancel = this.cancel.bind(this);
     this.confirm = this.confirm.bind(this);
     this.onLearnMoreClick = this.onLearnMoreClick.bind(this);
   }
 
   componentDidMount() {
-    const doc = this.props.hud.document;
-    const toolbox = gDevTools.getToolbox(this.props.hud.owner.target);
+    const doc = this.props.webConsoleUI.document;
+    const toolbox = gDevTools.getToolbox(this.props.webConsoleUI.owner.target);
     const tooltipDoc = toolbox ? toolbox.doc : doc;
     // The popup will be attached to the toolbox document or HUD document in the case
     // such as the browser console which doesn't have a toolbox.
     this.tooltip = new HTMLTooltip(tooltipDoc, {
       className: "invoke-confirm",
     });
   }
 
   componentDidUpdate() {
     const {getterPath, serviceContainer} = this.props;
 
     if (getterPath) {
       this.tooltip.show(serviceContainer.getJsTermTooltipAnchor(), {y: 5});
       this.tooltip.focus();
     } else {
       this.tooltip.hide();
-      this.props.hud.jsterm.focus();
+      this.props.webConsoleUI.jsterm.focus();
     }
   }
 
   componentDidThrow(e) {
     console.error("Error in ConfirmDialog", e);
     this.setState(state => ({...state, hasError: true}));
   }
 
--- a/devtools/client/webconsole/components/ConsoleOutput.js
+++ b/devtools/client/webconsole/components/ConsoleOutput.js
@@ -45,17 +45,17 @@ function getClosestMessage(visibleMessag
 
 class ConsoleOutput extends Component {
   static get propTypes() {
     return {
       initialized: PropTypes.bool.isRequired,
       messages: PropTypes.object.isRequired,
       messagesUi: PropTypes.array.isRequired,
       serviceContainer: PropTypes.shape({
-        attachRefToHud: PropTypes.func.isRequired,
+        attachRefToWebConsoleUI: PropTypes.func.isRequired,
         openContextMenu: PropTypes.func.isRequired,
         sourceMapService: PropTypes.object,
       }),
       dispatch: PropTypes.func.isRequired,
       timestampsVisible: PropTypes.bool,
       messagesTableData: PropTypes.object.isRequired,
       messagesRepeat: PropTypes.object.isRequired,
       networkMessagesUpdate: PropTypes.object.isRequired,
@@ -69,28 +69,33 @@ class ConsoleOutput extends Component {
   constructor(props) {
     super(props);
     this.onContextMenu = this.onContextMenu.bind(this);
     this.maybeScrollToBottom = this.maybeScrollToBottom.bind(this);
   }
 
   componentDidMount() {
     scrollToBottom(this.outputNode);
-    this.props.serviceContainer.attachRefToHud("outputScroller", this.outputNode);
+    const {
+      serviceContainer,
+      onFirstMeaningfulPaint,
+      dispatch,
+    } = this.props;
+    serviceContainer.attachRefToWebConsoleUI("outputScroller", this.outputNode);
 
     // Waiting for the next paint.
     new Promise(res => requestAnimationFrame(res))
       .then(() => {
-        if (this.props.onFirstMeaningfulPaint) {
-          this.props.onFirstMeaningfulPaint();
+        if (onFirstMeaningfulPaint) {
+          onFirstMeaningfulPaint();
         }
 
         // Dispatching on next tick so we don't block on action execution.
         setTimeout(() => {
-          this.props.dispatch(initialize());
+          dispatch(initialize());
         }, 0);
       });
   }
 
   componentWillUpdate(nextProps, nextState) {
     const outputNode = this.outputNode;
     if (!outputNode || !outputNode.lastChild) {
       // Force a scroll to bottom when messages are added to an empty console.
--- a/devtools/client/webconsole/components/ConsoleTable.js
+++ b/devtools/client/webconsole/components/ConsoleTable.js
@@ -17,17 +17,17 @@ const TABLE_ROW_MAX_ITEMS = 1000;
 const TABLE_COLUMN_MAX_ITEMS = 10;
 
 class ConsoleTable extends Component {
   static get propTypes() {
     return {
       dispatch: PropTypes.func.isRequired,
       parameters: PropTypes.array.isRequired,
       serviceContainer: PropTypes.shape({
-        hudProxy: PropTypes.object.isRequired,
+        proxy: PropTypes.object.isRequired,
       }),
       id: PropTypes.string.isRequired,
       tableData: PropTypes.object,
     };
   }
 
   constructor(props) {
     super(props);
@@ -37,17 +37,17 @@ class ConsoleTable extends Component {
 
   componentWillMount() {
     const {id, dispatch, serviceContainer, parameters} = this.props;
 
     if (!Array.isArray(parameters) || parameters.length === 0) {
       return;
     }
 
-    const client = new ObjectClient(serviceContainer.hudProxy.client, parameters[0]);
+    const client = new ObjectClient(serviceContainer.proxy.client, parameters[0]);
     const dataType = getParametersDataType(parameters);
 
     // Get all the object properties.
     dispatch(actions.messageTableDataGet(id, client, dataType));
   }
 
   getHeaders(columns) {
     const headerItems = [];
--- a/devtools/client/webconsole/components/FilterBar.js
+++ b/devtools/client/webconsole/components/FilterBar.js
@@ -22,19 +22,17 @@ const FilterCheckbox = require("devtools
 
 loader.lazyRequireGetter(this, "PropTypes", "devtools/client/shared/vendor/react-prop-types");
 
 class FilterBar extends Component {
   static get propTypes() {
     return {
       dispatch: PropTypes.func.isRequired,
       filter: PropTypes.object.isRequired,
-      serviceContainer: PropTypes.shape({
-        attachRefToHud: PropTypes.func.isRequired,
-      }).isRequired,
+      attachRefToWebConsoleUI: PropTypes.func.isRequired,
       filterBarVisible: PropTypes.bool.isRequired,
       persistLogs: PropTypes.bool.isRequired,
       hidePersistLogsCheckbox: PropTypes.bool.isRequired,
       filteredMessagesCount: PropTypes.object.isRequired,
       closeButtonVisible: PropTypes.bool,
       closeSplitConsole: PropTypes.func,
     };
   }
@@ -52,17 +50,17 @@ class FilterBar extends Component {
     this.onClickRemoveTextFilter = this.onClickRemoveTextFilter.bind(this);
     this.onSearchInput = this.onSearchInput.bind(this);
     this.onChangePersistToggle = this.onChangePersistToggle.bind(this);
     this.renderFiltersConfigBar = this.renderFiltersConfigBar.bind(this);
     this.renderFilteredMessagesBar = this.renderFilteredMessagesBar.bind(this);
   }
 
   componentDidMount() {
-    this.props.serviceContainer.attachRefToHud(
+    this.props.attachRefToWebConsoleUI(
       "filterBox",
       this.wrapperNode.querySelector(".text-filter")
     );
   }
 
   shouldComponentUpdate(nextProps, nextState) {
     const {
       filter,
--- a/devtools/client/webconsole/components/GripMessageBody.js
+++ b/devtools/client/webconsole/components/GripMessageBody.js
@@ -23,17 +23,16 @@ GripMessageBody.displayName = "GripMessa
 GripMessageBody.propTypes = {
   grip: PropTypes.oneOfType([
     PropTypes.string,
     PropTypes.number,
     PropTypes.object,
   ]).isRequired,
   serviceContainer: PropTypes.shape({
     createElement: PropTypes.func.isRequired,
-    hudProxy: PropTypes.object.isRequired,
     onViewSourceInDebugger: PropTypes.func.isRequired,
   }),
   userProvidedStyle: PropTypes.string,
   useQuotes: PropTypes.bool,
   escapeWhitespace: PropTypes.bool,
   type: PropTypes.string,
   helperType: PropTypes.string,
   maybeScrollToBottom: PropTypes.func,
--- a/devtools/client/webconsole/components/JSTerm.js
+++ b/devtools/client/webconsole/components/JSTerm.js
@@ -49,35 +49,31 @@ const {
   HISTORY_BACK,
   HISTORY_FORWARD,
 } = require("devtools/client/webconsole/constants");
 
 /**
  * Create a JSTerminal (a JavaScript command line). This is attached to an
  * existing HeadsUpDisplay (a Web Console instance). This code is responsible
  * with handling command line input and code evaluation.
- *
- * @constructor
- * @param object webConsoleFrame
- *        The WebConsoleFrame object that owns this JSTerm instance.
  */
 class JSTerm extends Component {
   static get propTypes() {
     return {
       // Append new executed expression into history list (action).
       appendToHistory: PropTypes.func.isRequired,
       // Remove all entries from the history list (action).
       clearHistory: PropTypes.func.isRequired,
       // Returns previous or next value from the history
       // (depending on direction argument).
       getValueFromHistory: PropTypes.func.isRequired,
       // History of executed expression (state).
       history: PropTypes.object.isRequired,
       // Console object.
-      hud: PropTypes.object.isRequired,
+      webConsoleUI: PropTypes.object.isRequired,
       // Needed for opening context menu
       serviceContainer: PropTypes.object.isRequired,
       // Handler for clipboard 'paste' event (also used for 'drop' event, callback).
       onPaste: PropTypes.func,
       codeMirrorEnabled: PropTypes.bool,
       // Update position in the history after executing an expression (action).
       updateHistoryPosition: PropTypes.func.isRequired,
       // Update autocomplete popup state.
@@ -86,21 +82,21 @@ class JSTerm extends Component {
       autocompleteData: PropTypes.object.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     const {
-      hud,
+      webConsoleUI,
     } = props;
 
-    this.hud = hud;
-    this.hudId = this.hud.hudId;
+    this.webConsoleUI = webConsoleUI;
+    this.hudId = this.webConsoleUI.hudId;
 
     this._keyPress = this._keyPress.bind(this);
     this._inputEventHandler = this._inputEventHandler.bind(this);
     this._blurEventHandler = this._blurEventHandler.bind(this);
     this.onContextMenu = this.onContextMenu.bind(this);
     this.imperativeUpdate = this.imperativeUpdate.bind(this);
 
     /**
@@ -111,30 +107,30 @@ class JSTerm extends Component {
 
     this.autocompletePopup = null;
     this.inputNode = null;
     this.completeNode = null;
 
     this._telemetry = new Telemetry();
 
     EventEmitter.decorate(this);
-    hud.jsterm = this;
+    webConsoleUI.jsterm = this;
   }
 
   componentDidMount() {
     const autocompleteOptions = {
       onSelect: this.onAutocompleteSelect.bind(this),
       onClick: this.acceptProposedCompletion.bind(this),
       listId: "webConsole_autocompletePopupListBox",
       position: "bottom",
       autoSelect: true,
     };
 
-    const doc = this.hud.document;
-    const toolbox = gDevTools.getToolbox(this.hud.owner.target);
+    const doc = this.webConsoleUI.document;
+    const toolbox = gDevTools.getToolbox(this.webConsoleUI.owner.target);
     const tooltipDoc = toolbox ? toolbox.doc : doc;
     // The popup will be attached to the toolbox document or HUD document in the case
     // such as the browser console which doesn't have a toolbox.
     this.autocompletePopup = new AutocompletePopup(tooltipDoc, autocompleteOptions);
 
     if (this.props.codeMirrorEnabled) {
       if (this.node) {
         const onArrowUp = () => {
@@ -302,46 +298,45 @@ class JSTerm extends Component {
               this.clearCompletion();
               return "CodeMirror.Pass";
             },
 
             "PageUp": () => {
               if (this.autocompletePopup.isOpen) {
                 this.autocompletePopup.selectPreviousPageItem();
               } else {
-                this.hud.outputScroller.scrollTop = Math.max(
-                  0,
-                  this.hud.outputScroller.scrollTop - this.hud.outputScroller.clientHeight
-                );
+                const {outputScroller} = this.webConsoleUI;
+                const {scrollTop, clientHeight} = outputScroller;
+                outputScroller.scrollTop = Math.max(0, scrollTop - clientHeight);
               }
 
               return null;
             },
 
             "PageDown": () => {
               if (this.autocompletePopup.isOpen) {
                 this.autocompletePopup.selectNextPageItem();
               } else {
-                this.hud.outputScroller.scrollTop = Math.min(
-                  this.hud.outputScroller.scrollHeight,
-                  this.hud.outputScroller.scrollTop + this.hud.outputScroller.clientHeight
-                );
+                const {outputScroller} = this.webConsoleUI;
+                const {scrollTop, scrollHeight, clientHeight} = outputScroller;
+                outputScroller.scrollTop =
+                  Math.min(scrollHeight, scrollTop + clientHeight);
               }
 
               return null;
             },
 
             "Home": () => {
               if (this.autocompletePopup.isOpen) {
                 this.autocompletePopup.selectItemAtIndex(0);
                 return null;
               }
 
               if (!this.getInputValue()) {
-                this.hud.outputScroller.scrollTop = 0;
+                this.webConsoleUI.outputScroller.scrollTop = 0;
                 return null;
               }
 
               if (this.getAutoCompletionText()) {
                 this.clearCompletion();
               }
 
               return "CodeMirror.Pass";
@@ -350,17 +345,18 @@ class JSTerm extends Component {
             "End": () => {
               if (this.autocompletePopup.isOpen) {
                 this.autocompletePopup.selectItemAtIndex(
                   this.autocompletePopup.itemCount - 1);
                 return null;
               }
 
               if (!this.getInputValue()) {
-                this.hud.outputScroller.scrollTop = this.hud.outputScroller.scrollHeight;
+                const {outputScroller} = this.webConsoleUI;
+                outputScroller.scrollTop = outputScroller.scrollHeight;
                 return null;
               }
 
               if (this.getAutoCompletionText()) {
                 this.clearCompletion();
               }
 
               return "CodeMirror.Pass";
@@ -406,17 +402,17 @@ class JSTerm extends Component {
     this.inputBorderSize = this.inputNode
       ? this.inputNode.getBoundingClientRect().height - this.inputNode.clientHeight
       : 0;
 
     // Update the character and chevron width needed for the popup offset calculations.
     this._inputCharWidth = this._getInputCharWidth();
     this._paddingInlineStart = this.editor ? null : this._getInputPaddingInlineStart();
 
-    this.hud.window.addEventListener("blur", this._blurEventHandler);
+    this.webConsoleUI.window.addEventListener("blur", this._blurEventHandler);
     this.lastInputValue && this.setInputValue(this.lastInputValue);
   }
 
   componentWillReceiveProps(nextProps) {
     this.imperativeUpdate(nextProps);
   }
 
   shouldComponentUpdate(nextProps) {
@@ -441,25 +437,25 @@ class JSTerm extends Component {
     }
   }
 
   /**
    * Getter for the element that holds the messages we display.
    * @type Element
    */
   get outputNode() {
-    return this.hud.outputNode;
+    return this.webConsoleUI.outputNode;
   }
 
   /**
    * Getter for the debugger WebConsoleClient.
    * @type object
    */
   get webConsoleClient() {
-    return this.hud.webConsoleClient;
+    return this.webConsoleUI.webConsoleClient;
   }
 
   focus() {
     if (this.editor) {
       this.editor.focus();
     } else if (this.inputNode && !this.inputNode.getAttribute("focused")) {
       this.inputNode.focus();
     }
@@ -495,17 +491,17 @@ class JSTerm extends Component {
   /**
    * The JavaScript evaluation response handler.
    *
    * @private
    * @param {Object} response
    *        The message received from the server.
    */
   async _executeResultCallback(response) {
-    if (!this.hud) {
+    if (!this.webConsoleUI) {
       return null;
     }
 
     if (response.error) {
       console.error("Evaluation error " + response.error + ": " + response.message);
       return null;
     }
 
@@ -523,63 +519,63 @@ class JSTerm extends Component {
     }
     const result = response.result;
     const helperResult = response.helperResult;
     const helperHasRawOutput = !!(helperResult || {}).rawOutput;
 
     if (helperResult && helperResult.type) {
       switch (helperResult.type) {
         case "clearOutput":
-          this.hud.clearOutput();
+          this.webConsoleUI.clearOutput();
           break;
         case "clearHistory":
           this.props.clearHistory();
           break;
         case "inspectObject":
-          this.hud.inspectObjectActor(helperResult.object);
+          this.webConsoleUI.inspectObjectActor(helperResult.object);
           break;
         case "error":
           try {
             errorMessage = l10n.getStr(helperResult.message);
           } catch (ex) {
             errorMessage = helperResult.message;
           }
           break;
         case "help":
-          this.hud.owner.openLink(HELP_URL);
+          this.webConsoleUI.owner.openLink(HELP_URL);
           break;
         case "copyValueToClipboard":
           clipboardHelper.copyString(helperResult.value);
           break;
         case "screenshotOutput":
           const { args, value } = helperResult;
-          const results = await saveScreenshot(this.hud.window, args, value);
+          const results = await saveScreenshot(this.webConsoleUI.window, args, value);
           this.screenshotNotify(results);
           // early return as screenshot notify has dispatched all necessary messages
           return null;
       }
     }
 
     // Hide undefined results coming from JSTerm helper functions.
     if (!errorMessage && result && typeof result == "object" &&
       result.type == "undefined" &&
       helperResult && !helperHasRawOutput) {
       return null;
     }
 
-    if (this.hud.wrapper) {
-      return this.hud.wrapper.dispatchMessageAdd(response, true);
+    if (this.webConsoleUI.wrapper) {
+      return this.webConsoleUI.wrapper.dispatchMessageAdd(response, true);
     }
 
     return null;
   }
 
   screenshotNotify(results) {
     const wrappedResults = results.map(message => ({ message, type: "logMessage" }));
-    this.hud.wrapper.dispatchMessagesAdd(wrappedResults);
+    this.webConsoleUI.wrapper.dispatchMessagesAdd(wrappedResults);
   }
 
   /**
    * Execute a string. Execution happens asynchronously in the content process.
    *
    * @param {String} executeString
    *        The string you want to execute. If this is not provided, the current
    *        user input is used - taken from |this.getInputValue()|.
@@ -596,31 +592,32 @@ class JSTerm extends Component {
     // Append executed expression into the history list.
     this.props.appendToHistory(executeString);
 
     WebConsoleUtils.usageCount++;
     this.setInputValue("");
     this.clearCompletion();
 
     let selectedNodeActor = null;
-    const inspectorSelection = this.hud.owner.getInspectorSelection();
+    const inspectorSelection = this.webConsoleUI.owner.getInspectorSelection();
     if (inspectorSelection && inspectorSelection.nodeFront) {
       selectedNodeActor = inspectorSelection.nodeFront.actorID;
     }
 
     const { ConsoleCommand } = require("devtools/client/webconsole/types");
     const cmdMessage = new ConsoleCommand({
       messageText: executeString,
       timeStamp: Date.now(),
     });
-    this.hud.proxy.dispatchMessageAdd(cmdMessage);
+    this.webConsoleUI.proxy.dispatchMessageAdd(cmdMessage);
 
     let mappedExpressionRes = null;
     try {
-      mappedExpressionRes = await this.hud.owner.getMappedExpression(executeString);
+      mappedExpressionRes =
+        await this.webConsoleUI.owner.getMappedExpression(executeString);
     } catch (e) {
       console.warn("Error when calling getMappedExpression", e);
     }
 
     executeString = mappedExpressionRes ? mappedExpressionRes.expression : executeString;
 
     const options = {
       selectedNodeActor,
@@ -959,58 +956,59 @@ class JSTerm extends Component {
           event.preventDefault();
         }
         break;
 
       case KeyCodes.DOM_VK_PAGE_UP:
         if (this.autocompletePopup.isOpen) {
           this.autocompletePopup.selectPreviousPageItem();
         } else {
-          this.hud.outputScroller.scrollTop =
+          this.webConsoleUI.outputScroller.scrollTop =
             Math.max(0,
-              this.hud.outputScroller.scrollTop -
-              this.hud.outputScroller.clientHeight
+              this.webConsoleUI.outputScroller.scrollTop -
+              this.webConsoleUI.outputScroller.clientHeight
             );
         }
         event.preventDefault();
         break;
 
       case KeyCodes.DOM_VK_PAGE_DOWN:
         if (this.autocompletePopup.isOpen) {
           this.autocompletePopup.selectNextPageItem();
         } else {
-          this.hud.outputScroller.scrollTop =
-            Math.min(this.hud.outputScroller.scrollHeight,
-              this.hud.outputScroller.scrollTop +
-              this.hud.outputScroller.clientHeight
+          this.webConsoleUI.outputScroller.scrollTop =
+            Math.min(this.webConsoleUI.outputScroller.scrollHeight,
+              this.webConsoleUI.outputScroller.scrollTop +
+              this.webConsoleUI.outputScroller.clientHeight
             );
         }
         event.preventDefault();
         break;
 
       case KeyCodes.DOM_VK_HOME:
         if (this.autocompletePopup.isOpen) {
           this.autocompletePopup.selectItemAtIndex(0);
           event.preventDefault();
         } else if (this.getAutoCompletionText()) {
           this.clearCompletion();
         } else if (inputValue.length <= 0) {
-          this.hud.outputScroller.scrollTop = 0;
+          this.webConsoleUI.outputScroller.scrollTop = 0;
           event.preventDefault();
         }
         break;
 
       case KeyCodes.DOM_VK_END:
         if (this.autocompletePopup.isOpen) {
           this.autocompletePopup.selectItemAtIndex(this.autocompletePopup.itemCount - 1);
           event.preventDefault();
         } else if (this.getAutoCompletionText()) {
           this.clearCompletion();
         } else if (inputValue.length <= 0) {
-          this.hud.outputScroller.scrollTop = this.hud.outputScroller.scrollHeight;
+          const {outputScroller} = this.webConsoleUI;
+          outputScroller.scrollTop = outputScroller.scrollHeight;
           event.preventDefault();
         }
         break;
 
       case KeyCodes.DOM_VK_LEFT:
         if (this.autocompletePopup.isOpen || this.getAutoCompletionText()) {
           this.clearCompletion();
         }
@@ -1477,17 +1475,17 @@ class JSTerm extends Component {
     if (!this.inputNode && !this.node) {
       return null;
     }
 
     if (this.editor) {
       return this.editor.defaultCharWidth();
     }
 
-    const doc = this.hud.document;
+    const doc = this.webConsoleUI.document;
     const tempLabel = doc.createElement("span");
     const style = tempLabel.style;
     style.position = "fixed";
     style.padding = "0";
     style.margin = "0";
     style.width = "auto";
     style.color = "transparent";
     WebConsoleUtils.copyTextStyles(this.inputNode, tempLabel);
@@ -1504,57 +1502,57 @@ class JSTerm extends Component {
    *
    * @returns {Number|null}: Width of the icon, or null if the input does not exist.
    */
   _getInputPaddingInlineStart() {
     if (!this.inputNode) {
       return null;
     }
    // Calculate the width of the chevron placed at the beginning of the input box.
-    const doc = this.hud.document;
+    const doc = this.webConsoleUI.document;
 
     return new Number(doc.defaultView
       .getComputedStyle(this.inputNode)
       .paddingInlineStart.replace(/[^0-9.]/g, ""));
   }
 
   onContextMenu(e) {
     this.props.serviceContainer.openEditContextMenu(e);
   }
 
   destroy() {
     this.webConsoleClient.clearNetworkRequests();
-    if (this.hud.outputNode) {
+    if (this.webConsoleUI.outputNode) {
       // We do this because it's much faster than letting React handle the ConsoleOutput
       // unmounting.
-      this.hud.outputNode.innerHTML = "";
+      this.webConsoleUI.outputNode.innerHTML = "";
     }
 
     if (this.autocompletePopup) {
       this.autocompletePopup.destroy();
       this.autocompletePopup = null;
     }
 
     if (this.inputNode) {
       this.inputNode.removeEventListener("keypress", this._keyPress);
       this.inputNode.removeEventListener("input", this._inputEventHandler);
       this.inputNode.removeEventListener("keyup", this._inputEventHandler);
-      this.hud.window.removeEventListener("blur", this._blurEventHandler);
+      this.webConsoleUI.window.removeEventListener("blur", this._blurEventHandler);
     }
 
     if (this.editor) {
       this.editor.destroy();
       this.editor = null;
     }
 
-    this.hud = null;
+    this.webConsoleUI = null;
   }
 
   render() {
-    if (this.props.hud.isBrowserConsole &&
+    if (this.props.webConsoleUI.isBrowserConsole &&
         !Services.prefs.getBoolPref("devtools.chrome.enabled")) {
       return null;
     }
 
     if (this.props.codeMirrorEnabled) {
       return dom.div({
         className: "jsterm-input-container devtools-input devtools-monospace",
         key: "jsterm-container",
--- a/devtools/client/webconsole/components/ReverseSearchInput.js
+++ b/devtools/client/webconsole/components/ReverseSearchInput.js
@@ -23,33 +23,33 @@ loader.lazyRequireGetter(this, "KeyCodes
 
 const Services = require("Services");
 const isMacOS = Services.appinfo.OS === "Darwin";
 
 class ReverseSearchInput extends Component {
   static get propTypes() {
     return {
       dispatch: PropTypes.func.isRequired,
-      hud: PropTypes.object.isRequired,
+      webConsoleUI: PropTypes.object.isRequired,
       reverseSearchResult: PropTypes.string,
       reverseSearchTotalResults: PropTypes.number,
       reverseSearchResultPosition: PropTypes.number,
       visible: PropTypes.bool,
       initialValue: PropTypes.string,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.onInputKeyDown = this.onInputKeyDown.bind(this);
   }
 
   componentDidUpdate(prevProps) {
-    const {jsterm} = this.props.hud;
+    const {jsterm} = this.props.webConsoleUI;
     if (
       prevProps.reverseSearchResult !== this.props.reverseSearchResult
       && this.props.visible
       && this.props.reverseSearchTotalResults > 0
     ) {
       jsterm.setInputValue(this.props.reverseSearchResult);
     }
 
@@ -71,25 +71,25 @@ class ReverseSearchInput extends Compone
       keyCode,
       key,
       ctrlKey,
       shiftKey,
     } = event;
 
     const {
       dispatch,
-      hud,
+      webConsoleUI,
       reverseSearchTotalResults,
     } = this.props;
 
     // On Enter, we trigger an execute.
     if (keyCode === KeyCodes.DOM_VK_RETURN) {
       event.stopPropagation();
       dispatch(actions.reverseSearchInputToggle());
-      hud.jsterm.execute();
+      webConsoleUI.jsterm.execute();
       return;
     }
 
     // On Escape (and Ctrl + c on OSX), we close the reverse search input.
     if (
       keyCode === KeyCodes.DOM_VK_ESCAPE || (
         isMacOS && ctrlKey === true && key.toLowerCase() === "c"
       )
--- a/devtools/client/webconsole/enhancers/actor-releaser.js
+++ b/devtools/client/webconsole/enhancers/actor-releaser.js
@@ -11,23 +11,23 @@ const {
   REMOVED_ACTORS_CLEAR,
 } = require("devtools/client/webconsole/constants");
 
 /**
  * This enhancer is responsible for releasing actors on the backend.
  * When messages with arguments are removed from the store we should also
  * clean up the backend.
  */
-function enableActorReleaser(hud) {
+function enableActorReleaser(webConsoleUI) {
   return next => (reducer, initialState, enhancer) => {
     function releaseActorsEnhancer(state, action) {
       state = reducer(state, action);
 
       const type = action.type;
-      const proxy = hud ? hud.proxy : null;
+      const proxy = webConsoleUI ? webConsoleUI.proxy : null;
       if (
         proxy &&
         ([MESSAGES_ADD, MESSAGES_CLEAR, PRIVATE_MESSAGES_CLEAR].includes(type))
       ) {
         releaseActors(state.messages.removedActors, proxy);
 
         // Reset `removedActors` in message reducer.
         state = reducer(state, {
--- a/devtools/client/webconsole/enhancers/css-error-reporting.js
+++ b/devtools/client/webconsole/enhancers/css-error-reporting.js
@@ -8,20 +8,20 @@ const {
   INITIALIZE,
   FILTER_TOGGLE,
 } = require("devtools/client/webconsole/constants");
 
 /**
  * This is responsible for ensuring that error reporting is enabled if the CSS
  * filter is toggled on.
  */
-function ensureCSSErrorReportingEnabled(hud) {
+function ensureCSSErrorReportingEnabled(webConsoleUI) {
   return next => (reducer, initialState, enhancer) => {
     function ensureErrorReportingEnhancer(state, action) {
-      const proxy = hud ? hud.proxy : null;
+      const proxy = webConsoleUI ? webConsoleUI.proxy : null;
       if (!proxy) {
         return reducer(state, action);
       }
 
       state = reducer(state, action);
       if (!state.filters.css) {
         return state;
       }
--- a/devtools/client/webconsole/enhancers/message-cache-clearing.js
+++ b/devtools/client/webconsole/enhancers/message-cache-clearing.js
@@ -8,22 +8,22 @@ const {
   MESSAGES_CLEAR,
 } = require("devtools/client/webconsole/constants");
 
 /**
  * This enhancer is responsible for clearing the messages caches using the
  * webconsoleClient when the user clear the messages (either by direct UI action, or via
  * `console.clear()`).
  */
-function enableMessagesCacheClearing(hud) {
+function enableMessagesCacheClearing(webConsoleUI) {
   return next => (reducer, initialState, enhancer) => {
     function messagesCacheClearingEnhancer(state, action) {
       state = reducer(state, action);
 
-      const webConsoleClient = hud && hud.proxy ? hud.proxy.webConsoleClient : null;
+      const webConsoleClient = webConsoleUI && webConsoleUI.webConsoleClient;
       if (webConsoleClient && action.type === MESSAGES_CLEAR) {
         webConsoleClient.clearNetworkRequests();
         webConsoleClient.clearMessagesCache();
       }
       return state;
     }
 
     return next(messagesCacheClearingEnhancer, initialState, enhancer);
--- a/devtools/client/webconsole/enhancers/net-provider.js
+++ b/devtools/client/webconsole/enhancers/net-provider.js
@@ -23,21 +23,21 @@ const {
 /**
  * This enhancer is responsible for fetching HTTP details data
  * collected by the backend. The fetch happens on-demand
  * when the user expands network log in order to inspect it.
  *
  * This way we don't slow down the Console logging by fetching.
  * unnecessary data over RDP.
  */
-function enableNetProvider(hud) {
+function enableNetProvider(webConsoleUI) {
   let dataProvider;
   return next => (reducer, initialState, enhancer) => {
     function netProviderEnhancer(state, action) {
-      const proxy = hud ? hud.proxy : null;
+      const proxy = webConsoleUI ? webConsoleUI.proxy : null;
       if (!proxy) {
         return reducer(state, action);
       }
 
       const actions = {
         updateRequest: (id, data, batch) => {
           proxy.dispatchRequestUpdate(id, data);
         },
--- a/devtools/client/webconsole/moz.build
+++ b/devtools/client/webconsole/moz.build
@@ -19,15 +19,15 @@ DevToolsModules(
     'constants.js',
     'hudservice.js',
     'main.js',
     'panel.js',
     'store.js',
     'types.js',
     'utils.js',
     'webconsole-connection-proxy.js',
-    'webconsole-frame.js',
     'webconsole-l10n.js',
+    'webconsole-ui.js',
     'webconsole-wrapper.js',
     'webconsole.js',
 )
 with Files('**'):
     BUG_COMPONENT = ('DevTools', 'Console')
--- a/devtools/client/webconsole/panel.js
+++ b/devtools/client/webconsole/panel.js
@@ -54,17 +54,17 @@ WebConsolePanel.prototype = {
 
       const webConsoleUIWindow = iframe.contentWindow.wrappedJSObject;
       const chromeWindow = iframe.ownerDocument.defaultView;
 
       // Open the Web Console.
       this.hud = await HUDService.openWebConsole(
         this.target, webConsoleUIWindow, chromeWindow);
 
-      // Pipe 'reloaded' event from WebConsoleFrame to WebConsolePanel.
+      // Pipe 'reloaded' event from WebConsoleUI to WebConsolePanel.
       // These events are listened by the Toolbox.
       this.hud.ui.on("reloaded", () => {
         this.emit("reloaded");
       });
 
       this._isReady = true;
       this.emit("ready");
     } catch (e) {
--- a/devtools/client/webconsole/store.js
+++ b/devtools/client/webconsole/store.js
@@ -33,18 +33,18 @@ const enableActorReleaser = require("./e
 const ensureCSSErrorReportingEnabled = require("./enhancers/css-error-reporting");
 const enableNetProvider = require("./enhancers/net-provider");
 const enableMessagesCacheClearing = require("./enhancers/message-cache-clearing");
 
 /**
  * Create and configure store for the Console panel. This is the place
  * where various enhancers and middleware can be registered.
  */
-function configureStore(hud, options = {}) {
-  const prefsService = getPrefsService(hud);
+function configureStore(webConsoleUI, options = {}) {
+  const prefsService = getPrefsService(webConsoleUI);
   const {
     getBoolPref,
     getIntPref,
   } = prefsService;
 
   const logLimit = options.logLimit
     || Math.max(getIntPref("devtools.hud.loglimit"), 1);
   const sidebarToggle = getBoolPref(PREFS.FEATURES.SIDEBAR_TOGGLE);
@@ -92,21 +92,21 @@ function configureStore(hud, options = {
     eventTelemetry.bind(null, options.telemetry, options.sessionId),
   );
 
   return createStore(
     createRootReducer(),
     initialState,
     compose(
       middleware,
-      enableActorReleaser(hud),
+      enableActorReleaser(webConsoleUI),
       enableBatching(),
-      enableNetProvider(hud),
-      enableMessagesCacheClearing(hud),
-      ensureCSSErrorReportingEnabled(hud),
+      enableNetProvider(webConsoleUI),
+      enableMessagesCacheClearing(webConsoleUI),
+      ensureCSSErrorReportingEnabled(webConsoleUI),
     )
   );
 }
 
 function createRootReducer() {
   return function rootReducer(state, action) {
     // We want to compute the new state for all properties except
     // "messages" and "history". These two reducers are handled
--- a/devtools/client/webconsole/test/chrome/test_render_perf.html
+++ b/devtools/client/webconsole/test/chrome/test_render_perf.html
@@ -173,17 +173,17 @@ window.onload = async function() {
     document.getElementById("output"),
     {hud: EventEmitter.decorate({proxy: {}}), focus: () => {}},
     {},
     null,
     document,
   );
   wrapper.init();
 
-  // From https://github.com/devtools-html/perf.html/blob/b73eb73df04c7df51464fa50eeadef3dc7f5d4e2/docs/gecko-profile-format.md#L21
+  // From https://github.com/firefox-devtools/perf.html/blob/b73eb73df04c7df51464fa50eeadef3dc7f5d4e2/docs/gecko-profile-format.md#L21
   const settings = {
     entries: 100000000,
     interval: 1,
     features: ["js"],
     threads: ["GeckoMain"],
   };
   Services.profiler.StartProfiler(
     settings.entries,
--- a/devtools/client/webconsole/test/fixtures/serviceContainer.js
+++ b/devtools/client/webconsole/test/fixtures/serviceContainer.js
@@ -1,18 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 module.exports = {
-  attachRefToHud: () => {},
+  attachRefToWebConsoleUI: () => {},
   canRewind: () => false,
   emitNewMessage: () => {},
-  hudProxy: {
+  proxy: {
     client: {},
     releaseActor: actor => console.log("Release actor", actor),
   },
   onViewSourceInDebugger: () => {},
   onViewSourceInStyleEditor: () => {},
   onViewSourceInScratchpad: () => {},
   openNetworkPanel: () => {},
   sourceMapService: {
--- a/devtools/client/webconsole/test/fixtures/stub-generators/head.js
+++ b/devtools/client/webconsole/test/fixtures/stub-generators/head.js
@@ -430,17 +430,17 @@ async function generateNetworkEventStubs
           toolbox.target.activeConsole.off("networkEvent", onNetworkEvent);
           resolve();
         }
       });
     });
 
     const onNetworkUpdate = new Promise(resolve => {
       let i = 0;
-      ui.jsterm.hud.on("network-message-updated", function onNetworkUpdated(res) {
+      ui.on("network-message-updated", function onNetworkUpdated(res) {
         const updateKey = `${keys[i++]} update`;
         // We cannot ensure the form of the network update packet, some properties
         // might be in another order than in the original packet.
         // Hand-picking only what we need should prevent this.
         const packet = {
           networkInfo: {
             _type: res.networkInfo._type,
             actor: res.networkInfo.actor,
@@ -448,17 +448,17 @@ async function generateNetworkEventStubs
             response: res.networkInfo.response,
             totalTime: res.networkInfo.totalTime,
           },
         };
 
         stubs.packets.push(formatPacket(updateKey, packet));
         stubs.preparedMessages.push(formatNetworkEventStub(updateKey, res));
         if (i === keys.length) {
-          ui.jsterm.hud.off("network-message-updated", onNetworkUpdated);
+          ui.off("network-message-updated", onNetworkUpdated);
           resolve();
         }
       });
     });
 
     await ContentTask.spawn(
       gBrowser.selectedBrowser,
       [key, code],
--- a/devtools/client/webconsole/test/mochitest/browser_console.js
+++ b/devtools/client/webconsole/test/mochitest/browser_console.js
@@ -116,17 +116,17 @@ async function testCPOWInspection(hud) {
   // Directly request evaluation to get an actor for the selected browser.
   // Note that this doesn't actually render a message, and instead allows us
   // us to assert that inspecting an object doesn't throw in the server.
   // This would be done in a mochitest-chrome suite, but that doesn't run in
   // e10s, so it's harder to get ahold of a CPOW.
   const cpowEval = await hud.jsterm.requestEvaluation("gBrowser.selectedBrowser");
   info("Creating an ObjectClient with: " + cpowEval.result.actor);
 
-  const objectClient = new ObjectClient(hud.jsterm.hud.proxy.client, {
+  const objectClient = new ObjectClient(hud.ui.proxy.client, {
     actor: cpowEval.result.actor,
   });
 
   // Before the fix for Bug 1382833, this wouldn't resolve due to a CPOW error
   // in the ObjectActor.
   const prototypeAndProperties = await objectClient.getPrototypeAndProperties();
 
   // Just a sanity check to make sure a valid packet came back
@@ -142,17 +142,17 @@ async function testCPOWInspection(hud) {
   if (!e10sCheck.result) {
     is(cpow.class, "Window", "The object is not a CPOW.");
     return;
   }
 
   is(cpow.class, "CPOW: Window", "The CPOW grip has the right class.");
 
   // Check that various protocol request methods work for the CPOW.
-  const objClient = new ObjectClient(hud.jsterm.hud.proxy.client, cpow);
+  const objClient = new ObjectClient(hud.ui.proxy.client, cpow);
 
   let response = await objClient.getPrototypeAndProperties();
   is(Reflect.ownKeys(response.ownProperties).length, 0, "No property was retrieved.");
   is(response.ownSymbols.length, 0, "No symbol property was retrieved.");
   is(response.prototype.type, "null", "The prototype is null.");
 
   response = await objClient.enumProperties({ignoreIndexedProperties: true});
   let slice = await response.iterator.slice(0, response.iterator.count);
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_selfxss.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_selfxss.js
@@ -23,18 +23,18 @@ add_task(async function() {
   await performTest();
   // And then run it with the CodeMirror-powered one.
   await pushPref("devtools.webconsole.jsterm.codeMirror", true);
   await performTest();
 });
 
 async function performTest() {
   await pushPref("devtools.selfxss.count", 0);
-  const {jsterm} = await openNewTabAndConsole(TEST_URI);
-  const {document} = jsterm.hud;
+  const {jsterm, ui} = await openNewTabAndConsole(TEST_URI);
+  const {document} = ui;
 
   info("Self-xss paste tests");
   WebConsoleUtils.usageCount = 0;
   is(WebConsoleUtils.usageCount, 0, "Test for usage count getter");
 
   // Input some commands to check if usage counting is working
   for (let i = 0; i <= 3; i++) {
     jsterm.setInputValue(i.toString());
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_network_attach.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_network_attach.js
@@ -26,26 +26,24 @@ add_task(async function task() {
   });
 
   info("XHR executed");
 
   await netReady;
 
   info("NetMonitor:PayloadReady received");
 
-  const webconsolePanel = await toolbox.selectTool("webconsole");
-  const { hud } = webconsolePanel;
+  const {hud} = await toolbox.selectTool("webconsole");
 
   const xhrUrl = TEST_PATH + "test-data.json";
   const messageNode = await waitFor(() => findMessage(hud, xhrUrl));
   const urlNode = messageNode.querySelector(".url");
   info("Network message found.");
 
-  const ui = hud.ui;
-  const consoleReady = ui.jsterm.hud.once("network-request-payload-ready");
+  const consoleReady = hud.ui.once("network-request-payload-ready");
 
   // Expand network log
   urlNode.click();
 
   await consoleReady;
 
   info("network-request-payload-ready received");
   await testNetworkMessage(messageNode);
@@ -69,13 +67,13 @@ async function testNetworkMessage(messag
 }
 
 /**
  * Wait until all lazily fetch requests in netmonitor get finished.
  * Otherwise test will be shutdown too early and cause failure.
  */
 async function waitForLazyRequests(toolbox) {
   const { ui } = toolbox.getCurrentPanel().hud;
-  const proxy = ui.jsterm.hud.proxy;
+  const proxy = ui.proxy;
   return waitUntil(() => {
     return !proxy.networkDataProvider.lazyRequestData.size;
   });
 }
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_network_messages_expand.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_network_messages_expand.js
@@ -318,13 +318,13 @@ function expandXhrMessage(node) {
 }
 
 /**
  * Wait until all lazily fetch requests in netmonitor get finished.
  * Otherwise test will be shutdown too early and cause failure.
  */
 async function waitForLazyRequests(toolbox) {
   const {ui} = toolbox.getCurrentPanel().hud;
-  const proxy = ui.jsterm.hud.proxy;
+  const proxy = ui.proxy;
   return waitUntil(() => {
     return !proxy.networkDataProvider.lazyRequestData.size;
   });
 }
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_network_messages_status_code.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_network_messages_status_code.js
@@ -18,17 +18,17 @@ pushPref(XHR_PREF, true);
 
 add_task(async function task() {
   const hud = await openNewTabAndConsole(TEST_URI);
 
   const currentTab = gBrowser.selectedTab;
   const target = await TargetFactory.forTab(currentTab);
   const toolbox = gDevTools.getToolbox(target);
   const {ui} = toolbox.getCurrentPanel().hud;
-  const onNetworkMessageUpdate = ui.jsterm.hud.once("network-message-updated");
+  const onNetworkMessageUpdate = ui.once("network-message-updated");
 
   // Fire an XHR POST request.
   await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
     content.wrappedJSObject.testXhrPost();
   });
 
   info("XHR executed");
   await onNetworkMessageUpdate;
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_trackingprotection_errors.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_trackingprotection_errors.js
@@ -2,50 +2,154 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Load a page with tracking elements that get blocked and make sure that a
 // 'learn more' link shows up in the webconsole.
 
 "use strict";
+requestLongerTimeout(2);
 
-const TEST_URI = "http://tracking.example.org/browser/devtools/client/" +
-                 "webconsole/test/mochitest/" +
-                 "test-trackingprotection-securityerrors.html";
-const LEARN_MORE_URI = "https://developer.mozilla.org/Firefox/Privacy/" +
-                       "Tracking_Protection" + DOCS_GA_PARAMS;
+const TEST_FILE = "browser/devtools/client/webconsole/test/mochitest/" +
+                          "test-trackingprotection-securityerrors.html";
+const TEST_URI = "http://example.com/" + TEST_FILE;
+const TRACKER_URL = "http://tracking.example.org/";
+const BLOCKED_URL = `\u201c${TRACKER_URL}\u201d`;
+
+const COOKIE_BEHAVIOR_PREF = "network.cookie.cookieBehavior";
+const COOKIE_BEHAVIORS = {
+// reject all third-party cookies
+  REJECT_FOREIGN: 1,
+// reject all cookies
+  REJECT: 2,
+// reject third-party cookies unless the eTLD already has at least one cookie
+  LIMIT_FOREIGN: 3,
+// reject trackers
+  REJECT_TRACKER: 4,
+};
 
 const {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
 
 registerCleanupFunction(function() {
   UrlClassifierTestUtils.cleanupTestTrackers();
 });
 
-add_task(async function testMessagesAppear() {
+add_task(async function testContentBlockingMessage() {
   await UrlClassifierTestUtils.addTestTrackers();
+
   await pushPref("privacy.trackingprotection.enabled", true);
+  const hud = await openNewTabAndConsole(TRACKER_URL + TEST_FILE);
 
-  const hud = await openNewTabAndConsole(TEST_URI);
+  info("Test content blocking message");
+  const message = await waitFor(() => findMessage(hud,
+    `The resource at \u201chttp://tracking.example.com/\u201d was blocked because ` +
+    `content blocking is enabled`));
+
+  await testLearnMoreClickOpenNewTab(message,
+    "https://developer.mozilla.org/Firefox/Privacy/Tracking_Protection" + DOCS_GA_PARAMS);
+});
+
+add_task(async function testForeignCookieBlockedMessage() {
+  info("Test foreign cookie blocked message");
+  // We change the pref and open a new window to ensure it will be taken into account.
+  await pushPref(COOKIE_BEHAVIOR_PREF, COOKIE_BEHAVIORS.REJECT_FOREIGN);
+  const {hud, win} = await openNewWindowAndConsole(TEST_URI);
+  const message = await waitFor(() => findMessage(hud,
+    `Request to access cookie or storage on ${BLOCKED_URL} was blocked because we are ` +
+    `blocking all third-party storage access requests and content blocking is enabled`));
+  await testLearnMoreClickOpenNewTab(message, getStorageErrorUrl("CookieBlockedForeign"));
+  win.close();
+});
+
+add_task(async function testLimitForeignCookieBlockedMessage() {
+  info("Test unvisited eTLD foreign cookies blocked message");
+  // We change the pref and open a new window to ensure it will be taken into account.
+  await pushPref(COOKIE_BEHAVIOR_PREF, COOKIE_BEHAVIORS.LIMIT_FOREIGN);
+  const {hud, win} = await openNewWindowAndConsole(TEST_URI);
+
+  const message = await waitFor(() => findMessage(hud,
+    `Request to access cookie or storage on ${BLOCKED_URL} was blocked because we are ` +
+    `blocking all third-party storage access requests and content blocking is enabled`));
+  await testLearnMoreClickOpenNewTab(message, getStorageErrorUrl("CookieBlockedForeign"));
+  win.close();
+});
+
+add_task(async function testAllCookieBlockedMessage() {
+  info("Test all cookies blocked message");
+  // We change the pref and open a new window to ensure it will be taken into account.
+  await pushPref(COOKIE_BEHAVIOR_PREF, COOKIE_BEHAVIORS.REJECT);
+  const {hud, win} = await openNewWindowAndConsole(TEST_URI);
 
   const message = await waitFor(() => findMessage(hud,
-    "The resource at \u201chttp://tracking.example.com/\u201d was " +
-    "blocked because content blocking is enabled"));
+    `Request to access cookie or storage on ${BLOCKED_URL} was blocked because we are ` +
+    `blocking all storage access requests`));
+  await testLearnMoreClickOpenNewTab(message,
+    getStorageErrorUrl("CookieBlockedAll"));
+  win.close();
+});
 
-  await testClickOpenNewTab(hud, message);
+add_task(async function testTrackerCookieBlockedMessage() {
+  info("Test tracker cookie blocked message");
+  // We change the pref and open a new window to ensure it will be taken into account.
+  await pushPref(COOKIE_BEHAVIOR_PREF, COOKIE_BEHAVIORS.REJECT_TRACKER);
+  const {hud, win} = await openNewWindowAndConsole(TEST_URI);
+
+  const message = await waitFor(() => findMessage(hud,
+    `Request to access cookie or storage on ${BLOCKED_URL} was blocked because it came ` +
+    `from a tracker and content blocking is enabled`));
+  await testLearnMoreClickOpenNewTab(message, getStorageErrorUrl("CookieBlockedTracker"));
+  win.close();
 });
 
-async function testClickOpenNewTab(hud, message) {
+add_task(async function testCookieBlockedByPermissionMessage() {
+  info("Test cookie blocked by permission message");
+  // Turn off tracking protection and add a block permission on the URL.
+  await pushPref("privacy.trackingprotection.enabled", false);
+  const p = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(TRACKER_URL);
+  Services.perms.addFromPrincipal(p, "cookie", Ci.nsIPermissionManager.DENY_ACTION);
+
+  const {hud, win} = await openNewWindowAndConsole(TEST_URI);
+  const message = await waitFor(() => findMessage(hud, `Request to access cookies or ` +
+    `storage on ${BLOCKED_URL} was blocked because of custom cookie permission`));
+  await testLearnMoreClickOpenNewTab(message,
+    getStorageErrorUrl("CookieBlockedByPermission"));
+  win.close();
+
+  // Remove the custom permission.
+  Services.perms.removeFromPrincipal(p, "cookie");
+});
+
+async function openNewWindowAndConsole(url) {
+  const win = await openNewBrowserWindow();
+  const tab = await addTab(url, {window: win});
+  win.gBrowser.selectedTab = tab;
+  const hud = await openConsole(tab);
+  return {win, hud};
+}
+
+function getStorageErrorUrl(category) {
+  const BASE_STORAGE_ERROR_URL = "https://developer.mozilla.org/docs/Mozilla/Firefox/" +
+                                 "Privacy/Storage_access_policy/Errors/";
+  const STORAGE_ERROR_URL_PARAMS = new URLSearchParams({
+    utm_source: "devtools",
+    utm_medium: "firefox-cookie-errors",
+    utm_campaign: "default",
+  }).toString();
+  return `${BASE_STORAGE_ERROR_URL}${category}?${STORAGE_ERROR_URL_PARAMS}`;
+}
+
+async function testLearnMoreClickOpenNewTab(message, expectedUrl) {
   info("Clicking on the Learn More link");
 
   const learnMoreLink = message.querySelector(".learn-more-link");
   const linkSimulation = await simulateLinkClick(learnMoreLink);
   checkLink({
     ...linkSimulation,
-    expectedLink: LEARN_MORE_URI,
+    expectedLink: expectedUrl,
     expectedTab: "tab",
   });
 }
 
 function checkLink({ link, where, expectedLink, expectedTab }) {
   is(link, expectedLink, `Clicking the provided link opens ${link}`);
   is(where, expectedTab, `Clicking the provided link opens in expected tab`);
 }
--- a/devtools/client/webconsole/test/mochitest/head.js
+++ b/devtools/client/webconsole/test/mochitest/head.js
@@ -47,19 +47,17 @@ registerCleanupFunction(async function()
   // Reset all filter prefs between tests. First flushPrefEnv in case one of the
   // filter prefs has been pushed for the test
   await SpecialPowers.flushPrefEnv();
   Services.prefs.getChildList("devtools.webconsole.filter").forEach(pref => {
     Services.prefs.clearUserPref(pref);
   });
   const browserConsole = HUDService.getBrowserConsole();
   if (browserConsole) {
-    if (browserConsole.jsterm) {
-      browserConsole.jsterm.hud.clearOutput(true);
-    }
+    browserConsole.ui.clearOutput(true);
     await HUDService.toggleBrowserConsole();
   }
 });
 
 /**
  * Add a new tab and open the toolbox in it, and select the webconsole.
  *
  * @param string url
--- a/devtools/client/webconsole/test/mochitest/test-trackingprotection-securityerrors.html
+++ b/devtools/client/webconsole/test/mochitest/test-trackingprotection-securityerrors.html
@@ -2,11 +2,12 @@
 <!-- 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/. -->
 <html dir="ltr" xml:lang="en-US" lang="en-US">
   <head>
     <meta charset="utf8">
   </head>
   <body>
+    <iframe src="http://tracking.example.org/"></iframe>
     <iframe src="http://tracking.example.com/"></iframe>
   </body>
 </html>
--- a/devtools/client/webconsole/utils/context-menu.js
+++ b/devtools/client/webconsole/utils/context-menu.js
@@ -15,35 +15,35 @@ const clipboardHelper = require("devtool
 const { l10n } = require("devtools/client/webconsole/utils/messages");
 
 loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true);
 loader.lazyRequireGetter(this, "getElementText", "devtools/client/webconsole/utils/clipboard", true);
 
 /**
  * Create a Menu instance for the webconsole.
  *
- * @param {Object} hud
- *        The webConsoleFrame.
+ * @param {WebConsoleUI} webConsoleUI
+ *        The webConsoleUI instance.
  * @param {Element} parentNode
  *        The container of the new console frontend output wrapper.
  * @param {Object} options
  *        - {String} actor (optional) actor id to use for context menu actions
  *        - {String} clipboardText (optional) text to "Copy" if no selection is available
  *        - {String} variableText (optional) which is the textual frontend
  *            representation of the variable
  *        - {Object} message (optional) message object containing metadata such as:
  *          - {String} source
  *          - {String} request
  *        - {Function} openSidebar (optional) function that will open the object
  *            inspector sidebar
  *        - {String} rootActorId (optional) actor id for the root object being clicked on
  *        - {Object} executionPoint (optional) when replaying, the execution point where
  *            this message was logged
  */
-function createContextMenu(hud, parentNode, {
+function createContextMenu(webConsoleUI, parentNode, {
   actor,
   clipboardText,
   variableText,
   message,
   serviceContainer,
   openSidebar,
   rootActorId,
   executionPoint,
@@ -110,19 +110,19 @@ function createContextMenu(hud, parentNo
         }
         this["temp" + i] = _self;
         "temp" + i;
       }`;
       const options = {
         selectedObjectActor: actor,
       };
 
-      hud.jsterm.requestEvaluation(evalString, options).then((res) => {
-        hud.jsterm.focus();
-        hud.jsterm.setInputValue(res.result);
+      webConsoleUI.jsterm.requestEvaluation(evalString, options).then((res) => {
+        webConsoleUI.jsterm.focus();
+        webConsoleUI.jsterm.setInputValue(res.result);
       });
     },
   }));
 
   // Copy message or grip.
   menu.append(new MenuItem({
     id: "console-menu-copy",
     label: l10n.getStr("webconsole.menu.copyMessage.label"),
@@ -145,19 +145,20 @@ function createContextMenu(hud, parentNo
     id: "console-menu-copy-object",
     label: l10n.getStr("webconsole.menu.copyObject.label"),
     accesskey: l10n.getStr("webconsole.menu.copyObject.accesskey"),
     // Disabled if there is no actor and no variable text associated.
     disabled: (!actor && !variableText),
     click: () => {
       if (actor) {
         // The Debugger.Object of the OA will be bound to |_self| during evaluation,
-        hud.jsterm.copyObject(`_self`, { selectedObjectActor: actor }).then((res) => {
-          clipboardHelper.copyString(res.helperResult.value);
-        });
+        webConsoleUI.jsterm.copyObject(`_self`, { selectedObjectActor: actor })
+          .then((res) => {
+            clipboardHelper.copyString(res.helperResult.value);
+          });
       } else {
         clipboardHelper.copyString(variableText);
       }
     },
   }));
 
   // Select all.
   menu.append(new MenuItem({
--- a/devtools/client/webconsole/webconsole-connection-proxy.js
+++ b/devtools/client/webconsole/webconsole-connection-proxy.js
@@ -12,23 +12,23 @@ const l10n = require("devtools/client/we
 const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout";
 // Web Console connection proxy
 
 /**
  * The WebConsoleConnectionProxy handles the connection between the Web Console
  * and the application we connect to through the remote debug protocol.
  *
  * @constructor
- * @param object webConsoleFrame
- *        The WebConsoleFrame object that owns this connection proxy.
+ * @param {WebConsoleUI} webConsoleUI
+ *        A WebConsoleUI instance that owns this connection proxy.
  * @param RemoteTarget target
  *        The target that the console will connect to.
  */
-function WebConsoleConnectionProxy(webConsoleFrame, target) {
-  this.webConsoleFrame = webConsoleFrame;
+function WebConsoleConnectionProxy(webConsoleUI, target) {
+  this.webConsoleUI = webConsoleUI;
   this.target = target;
   this.webConsoleClient = target.activeConsole;
 
   this._onPageError = this._onPageError.bind(this);
   this._onLogMessage = this._onLogMessage.bind(this);
   this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
   this._onVirtualConsoleLog = this._onVirtualConsoleLog.bind(this);
   this._onNetworkEvent = this._onNetworkEvent.bind(this);
@@ -39,22 +39,22 @@ function WebConsoleConnectionProxy(webCo
   this._onCachedMessages = this._onCachedMessages.bind(this);
   this._connectionTimeout = this._connectionTimeout.bind(this);
   this._onLastPrivateContextExited =
     this._onLastPrivateContextExited.bind(this);
 }
 
 WebConsoleConnectionProxy.prototype = {
   /**
-   * The owning Web Console Frame instance.
+   * The owning WebConsoleUI instance.
    *
-   * @see WebConsoleFrame
+   * @see WebConsoleUI
    * @type object
    */
-  webConsoleFrame: null,
+  webConsoleUI: null,
 
   /**
    * The target that the console connects to.
    * @type RemoteTarget
    */
   target: null,
 
   /**
@@ -130,17 +130,17 @@ WebConsoleConnectionProxy.prototype = {
                        this._onLastPrivateContextExited);
     client.addListener("virtualConsoleLog",
                        this._onVirtualConsoleLog);
 
     this.target.on("will-navigate", this._onTabWillNavigate);
     this.target.on("navigate", this._onTabNavigated);
 
     if (this.target.isBrowsingContext) {
-      this.webConsoleFrame.onLocationChange(this.target.url, this.target.title);
+      this.webConsoleUI.onLocationChange(this.target.url, this.target.title);
     }
     this.isAttached = this._attachConsole();
 
     return connPromise;
   },
 
   /**
    * Connection timeout handler.
@@ -185,55 +185,53 @@ WebConsoleConnectionProxy.prototype = {
    *        specific tab we work with.
    */
   _onAttachConsole: async function(response) {
     let saveBodies = Services.prefs.getBoolPref(
       "devtools.netmonitor.saveRequestAndResponseBodies");
 
     // There is no way to view response bodies from the Browser Console, so do
     // not waste the memory.
-    if (this.webConsoleFrame.isBrowserConsole) {
+    if (this.webConsoleUI.isBrowserConsole) {
       saveBodies = false;
     }
 
-    this.webConsoleFrame.setSaveRequestAndResponseBodies(saveBodies);
+    this.webConsoleUI.setSaveRequestAndResponseBodies(saveBodies);
 
     this.webConsoleClient.on("networkEvent", this._onNetworkEvent);
     this.webConsoleClient.on("networkEventUpdate", this._onNetworkEventUpdate);
 
     const msgs = ["PageError", "ConsoleAPI"];
     const cachedMessages = await this.webConsoleClient.getCachedMessages(msgs);
     this._onCachedMessages(cachedMessages);
-
-    this.webConsoleFrame._onUpdateListeners();
   },
 
   /**
    * Dispatch a message add on the new frontend and emit an event for tests.
    */
   dispatchMessageAdd: function(packet) {
-    this.webConsoleFrame.wrapper.dispatchMessageAdd(packet);
+    this.webConsoleUI.wrapper.dispatchMessageAdd(packet);
   },
 
   /**
    * Batched dispatch of messages.
    */
   dispatchMessagesAdd: function(packets) {
-    this.webConsoleFrame.wrapper.dispatchMessagesAdd(packets);
+    this.webConsoleUI.wrapper.dispatchMessagesAdd(packets);
   },
 
   /**
    * Dispatch a message event on the new frontend and emit an event for tests.
    */
   dispatchMessageUpdate: function(networkInfo, response) {
-    this.webConsoleFrame.wrapper.dispatchMessageUpdate(networkInfo, response);
+    this.webConsoleUI.wrapper.dispatchMessageUpdate(networkInfo, response);
   },
 
   dispatchRequestUpdate: function(id, data) {
-    this.webConsoleFrame.wrapper.dispatchRequestUpdate(id, data);
+    this.webConsoleUI.wrapper.dispatchRequestUpdate(id, data);
   },
 
   /**
    * The "cachedMessages" response handler.
    *
    * @private
    * @param object response
    *        The JSON response object received from the server.
@@ -253,17 +251,17 @@ WebConsoleConnectionProxy.prototype = {
     }
 
     const messages =
       response.messages.concat(...this.webConsoleClient.getNetworkEvents());
     messages.sort((a, b) => a.timeStamp - b.timeStamp);
 
     this.dispatchMessagesAdd(messages);
     if (!this.webConsoleClient.hasNativeConsoleAPI) {
-      await this.webConsoleFrame.logWarningAboutReplacedAPI();
+      await this.webConsoleUI.logWarningAboutReplacedAPI();
     }
 
     this.connected = true;
     this._connectDefer.resolve(this);
   },
 
   /**
    * The "pageError" message type handler. We redirect any page errors to the UI
@@ -271,56 +269,56 @@ WebConsoleConnectionProxy.prototype = {
    *
    * @private
    * @param string type
    *        Message type.
    * @param object packet
    *        The message received from the server.
    */
   _onPageError: function(type, packet) {
-    if (!this.webConsoleFrame || packet.from != this.webConsoleClient.actor) {
+    if (!this.webConsoleUI || packet.from != this.webConsoleClient.actor) {
       return;
     }
     this.dispatchMessageAdd(packet);
   },
   /**
    * The "logMessage" message type handler. We redirect any message to the UI
    * for displaying.
    *
    * @private
    * @param string type
    *        Message type.
    * @param object packet
    *        The message received from the server.
    */
   _onLogMessage: function(type, packet) {
-    if (!this.webConsoleFrame || packet.from != this.webConsoleClient.actor) {
+    if (!this.webConsoleUI || packet.from != this.webConsoleClient.actor) {
       return;
     }
     this.dispatchMessageAdd(packet);
   },
   /**
    * The "consoleAPICall" message type handler. We redirect any message to
    * the UI for displaying.
    *
    * @private
    * @param string type
    *        Message type.
    * @param object packet
    *        The message received from the server.
    */
   _onConsoleAPICall: function(type, packet) {
-    if (!this.webConsoleFrame || packet.from != this.webConsoleClient.actor) {
+    if (!this.webConsoleUI || packet.from != this.webConsoleClient.actor) {
       return;
     }
     this.dispatchMessageAdd(packet);
   },
 
   _onVirtualConsoleLog: function(type, packet) {
-    if (!this.webConsoleFrame) {
+    if (!this.webConsoleUI) {
       return;
     }
     this.dispatchMessageAdd({
       type: "consoleAPICall",
       message: {
         executionPoint: packet.executionPoint,
         "arguments": [packet.url + ":" + packet.line, packet.message],
       },
@@ -331,79 +329,79 @@ WebConsoleConnectionProxy.prototype = {
    * The "networkEvent" message type handler. We redirect any message to
    * the UI for displaying.
    *
    * @private
    * @param object networkInfo
    *        The network request information.
    */
   _onNetworkEvent: function(networkInfo) {
-    if (!this.webConsoleFrame) {
+    if (!this.webConsoleUI) {
       return;
     }
     this.dispatchMessageAdd(networkInfo);
   },
   /**
    * The "networkEventUpdate" message type handler. We redirect any message to
    * the UI for displaying.
    *
    * @private
    * @param object response
    *        The update response received from the server.
    */
   _onNetworkEventUpdate: function(response) {
-    if (!this.webConsoleFrame) {
+    if (!this.webConsoleUI) {
       return;
     }
     this.dispatchMessageUpdate(response.networkInfo, response);
   },
   /**
    * The "lastPrivateContextExited" message type handler. When this message is
    * received the Web Console UI is cleared.
    *
    * @private
    * @param string type
    *        Message type.
    * @param object packet
    *        The message received from the server.
    */
   _onLastPrivateContextExited: function(type, packet) {
-    if (this.webConsoleFrame && packet.from == this.webConsoleClient.actor) {
-      this.webConsoleFrame.clearPrivateMessages();
+    if (this.webConsoleUI && packet.from == this.webConsoleClient.actor) {
+      this.webConsoleUI.clearPrivateMessages();
     }
   },
 
   /**
    * The "navigate" event handlers. We redirect any message to the UI for displaying.
    *
    * @private
    * @param object packet
    *        The message received from the server.
    */
   _onTabNavigated: function(packet) {
-    if (!this.webConsoleFrame) {
+    if (!this.webConsoleUI) {
       return;
     }
 
-    this.webConsoleFrame.handleTabNavigated(packet);
+    this.webConsoleUI.handleTabNavigated(packet);
   },
 
   /**
    * The "will-navigate" event handlers. We redirect any message to the UI for displaying.
    *
    * @private
    * @param object packet
    *        The message received from the server.
    */
   _onTabWillNavigate: function(packet) {
-    if (!this.webConsoleFrame) {
+    if (!this.webConsoleUI) {
       return;
     }
 
-    this.webConsoleFrame.handleTabWillNavigate(packet);
+    this.webConsoleUI.handleTabWillNavigate(packet);
   },
 
   /**
    * Release an object actor.
    *
    * @param string actor
    *        The actor ID to send the request to.
    */
@@ -446,16 +444,16 @@ WebConsoleConnectionProxy.prototype = {
     this.webConsoleClient.off("networkEventUpdate", this._onNetworkEventUpdate);
     this.target.off("will-navigate", this._onTabWillNavigate);
     this.target.off("navigate", this._onTabNavigated);
 
     this.client = null;
     this.webConsoleClient = null;
     this.target = null;
     this.connected = false;
-    this.webConsoleFrame = null;
+    this.webConsoleUI = null;
     this._disconnecter.resolve(null);
 
     return this._disconnecter.promise;
   },
 };
 
 exports.WebConsoleConnectionProxy = WebConsoleConnectionProxy;
rename from devtools/client/webconsole/webconsole-frame.js
rename to devtools/client/webconsole/webconsole-ui.js
--- a/devtools/client/webconsole/webconsole-frame.js
+++ b/devtools/client/webconsole/webconsole-ui.js
@@ -18,66 +18,67 @@ const { l10n } = require("devtools/clien
 loader.lazyRequireGetter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm", true);
 
 const ZoomKeys = require("devtools/client/shared/zoom-keys");
 
 const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
 const PREF_SIDEBAR_ENABLED = "devtools.webconsole.sidebarToggle";
 
 /**
- * A WebConsoleFrame instance is an interactive console initialized *per target*
+ * A WebConsoleUI instance is an interactive console initialized *per target*
  * that displays console log data as well as provides an interactive terminal to
  * manipulate the target's document content.
  *
- * The WebConsoleFrame is responsible for the actual Web Console UI
+ * The WebConsoleUI is responsible for the actual Web Console UI
  * implementation.
- *
- * @constructor
- * @param object webConsoleOwner
- *        The WebConsole owner object.
  */
-function WebConsoleFrame(webConsoleOwner) {
-  this.owner = webConsoleOwner;
-  this.hudId = this.owner.hudId;
-  this.isBrowserConsole = this.owner._browserConsole;
-  this.window = this.owner.iframeWindow;
+class WebConsoleUI {
+  /*
+   * @param {object} webConsoleOwner: The WebConsole owner object.
+   */
+  constructor(webConsoleOwner) {
+    this.owner = webConsoleOwner;
+    this.hudId = this.owner.hudId;
+    this.isBrowserConsole = this.owner._browserConsole;
+    this.window = this.owner.iframeWindow;
 
-  this._onToolboxPrefChanged = this._onToolboxPrefChanged.bind(this);
-  this._onPanelSelected = this._onPanelSelected.bind(this);
-  this._onChangeSplitConsoleState = this._onChangeSplitConsoleState.bind(this);
+    this._onToolboxPrefChanged = this._onToolboxPrefChanged.bind(this);
+    this._onPanelSelected = this._onPanelSelected.bind(this);
+    this._onChangeSplitConsoleState = this._onChangeSplitConsoleState.bind(this);
 
-  EventEmitter.decorate(this);
-}
-WebConsoleFrame.prototype = {
+    EventEmitter.decorate(this);
+  }
+
   /**
    * Getter for the debugger WebConsoleClient.
    * @type object
    */
   get webConsoleClient() {
     return this.proxy ? this.proxy.webConsoleClient : null;
-  },
+  }
 
   /**
-   * Initialize the WebConsoleFrame instance.
+   * Initialize the WebConsoleUI instance.
    * @return object
    *         A promise object that resolves once the frame is ready to use.
    */
   async init() {
     this._initUI();
     await this._initConnection();
     await this.wrapper.init();
     // Toggle the timestamp on preference change
     Services.prefs.addObserver(PREF_MESSAGE_TIMESTAMP, this._onToolboxPrefChanged);
     this._onToolboxPrefChanged();
 
     const id = WebConsoleUtils.supportsString(this.hudId);
     if (Services.obs) {
       Services.obs.notifyObservers(id, "web-console-created");
     }
-  },
+  }
+
   destroy() {
     if (this._destroyer) {
       return this._destroyer.promise;
     }
     this._destroyer = defer();
     Services.prefs.removeObserver(PREF_MESSAGE_TIMESTAMP, this._onToolboxPrefChanged);
     this.React = this.ReactDOM = this.FrameView = null;
     if (this.jsterm) {
@@ -100,17 +101,17 @@ WebConsoleFrame.prototype = {
     if (this.proxy) {
       this.proxy.disconnect().then(onDestroy);
       this.proxy = null;
     } else {
       onDestroy();
     }
 
     return this._destroyer.promise;
-  },
+  }
 
   /**
    * Clear the Web Console output.
    *
    * This method emits the "messages-cleared" notification.
    *
    * @param boolean clearStorage
    *        True if you want to clear the console messages storage associated to
@@ -120,52 +121,44 @@ WebConsoleFrame.prototype = {
     if (this.wrapper) {
       this.wrapper.dispatchMessagesClear();
     }
     this.webConsoleClient.clearNetworkRequests();
     if (clearStorage) {
       this.webConsoleClient.clearMessagesCache();
     }
     this.emit("messages-cleared");
-  },
+  }
 
   /**
    * Remove all of the private messages from the Web Console output.
    *
    * This method emits the "private-messages-cleared" notification.
    */
   clearPrivateMessages() {
     if (this.wrapper) {
       this.wrapper.dispatchPrivateMessagesClear();
       this.emit("private-messages-cleared");
     }
-  },
+  }
 
   inspectObjectActor(objectActor) {
     this.wrapper.dispatchMessageAdd({
       helperResult: {
         type: "inspectObject",
         object: objectActor,
       },
     }, true);
     return this.wrapper;
-  },
-
-  _onUpdateListeners() {
-
-  },
+  }
 
   logWarningAboutReplacedAPI() {
     return this.owner.target.logWarningInPage(l10n.getStr("ConsoleAPIDisabled"),
       "ConsoleAPIDisabled");
-  },
-
-  handleNetworkEventUpdate() {
-
-  },
+  }
 
   /**
    * Setter for saving of network request and response bodies.
    *
    * @param boolean value
    *        The new value you want to set.
    */
   async setSaveRequestAndResponseBodies(value) {
@@ -176,27 +169,27 @@ WebConsoleFrame.prototype = {
 
     const newValue = !!value;
     const toSet = {
       "NetworkMonitor.saveRequestAndResponseBodies": newValue,
     };
 
     // Make sure the web console client connection is established first.
     return this.webConsoleClient.setPreferences(toSet);
-  },
+  }
 
   /**
    * Connect to the server using the remote debugging protocol.
    *
    * @private
    * @return object
    *         A promise object that is resolved/reject based on the connection
    *         result.
    */
-  _initConnection: function() {
+  _initConnection() {
     if (this._initDefer) {
       return this._initDefer.promise;
     }
 
     this._initDefer = defer();
     this.proxy = new WebConsoleConnectionProxy(this, this.owner.target);
 
     this.proxy.connect().then(() => {
@@ -204,19 +197,19 @@ WebConsoleFrame.prototype = {
       this._initDefer.resolve(this);
     }, (reason) => {
       // on failure
       // TODO Print a message to console
       this._initDefer.reject(reason);
     });
 
     return this._initDefer.promise;
-  },
+  }
 
-  _initUI: function() {
+  _initUI() {
     this.document = this.window.document;
     this.rootElement = this.document.documentElement;
 
     this.outputNode = this.document.getElementById("app-wrapper");
 
     const toolbox = gDevTools.getToolbox(this.owner.target);
 
     this.wrapper = new this.window.WebConsoleWrapper(
@@ -225,19 +218,19 @@ WebConsoleFrame.prototype = {
     this._initShortcuts();
     this._initOutputSyntaxHighlighting();
 
     if (toolbox) {
       toolbox.on("webconsole-selected", this._onPanelSelected);
       toolbox.on("split-console", this._onChangeSplitConsoleState);
       toolbox.on("select", this._onChangeSplitConsoleState);
     }
-  },
+  }
 
-  _initOutputSyntaxHighlighting: function() {
+  _initOutputSyntaxHighlighting() {
     // Given a DOM node, we syntax highlight identically to how the input field
     // looks. See https://codemirror.net/demo/runmode.html;
     const syntaxHighlightNode = node => {
       const editor = this.jsterm && this.jsterm.editor;
       if (node && editor) {
         node.classList.add("cm-s-mozilla");
         editor.CodeMirror.runMode(node.textContent, "application/javascript", node);
       }
@@ -249,19 +242,19 @@ WebConsoleFrame.prototype = {
     win.customElements.define("syntax-highlighted", class extends win.HTMLElement {
       connectedCallback() {
         if (!this.connected) {
           this.connected = true;
           syntaxHighlightNode(this);
         }
       }
     });
-  },
+  }
 
-  _initShortcuts: function() {
+  _initShortcuts() {
     const shortcuts = new KeyShortcuts({
       window: this.window,
     });
 
     shortcuts.on(l10n.getStr("webconsole.find.key"),
                  event => {
                    this.filterBox.focus();
                    event.preventDefault();
@@ -289,104 +282,104 @@ WebConsoleFrame.prototype = {
     } else if (Services.prefs.getBoolPref(PREF_SIDEBAR_ENABLED)) {
       shortcuts.on("Esc", event => {
         if (!this.jsterm.autocompletePopup || !this.jsterm.autocompletePopup.isOpen) {
           this.wrapper.dispatchSidebarClose();
           this.jsterm.focus();
         }
       });
     }
-  },
+  }
 
   /**
    * Handler for page location changes.
    *
    * @param string uri
    *        New page location.
    * @param string title
    *        New page title.
    */
-  onLocationChange: function(uri, title) {
+  onLocationChange(uri, title) {
     this.contentLocation = uri;
     if (this.owner.onLocationChange) {
       this.owner.onLocationChange(uri, title);
     }
-  },
+  }
 
   /**
    * Release an actor.
    *
    * @private
    * @param string actor
    *        The actor ID you want to release.
    */
-  _releaseObject: function(actor) {
+  _releaseObject(actor) {
     if (this.proxy) {
       this.proxy.releaseActor(actor);
     }
-  },
+  }
 
   /**
    * Called when the message timestamp pref changes.
    */
-  _onToolboxPrefChanged: function() {
+  _onToolboxPrefChanged() {
     const newValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
     this.wrapper.dispatchTimestampsToggle(newValue);
-  },
+  }
 
   /**
    * Sets the focus to JavaScript input field when the web console tab is
    * selected or when there is a split console present.
    * @private
    */
-  _onPanelSelected: function() {
+  _onPanelSelected() {
     this.jsterm.focus();
-  },
+  }
 
-  _onChangeSplitConsoleState: function() {
+  _onChangeSplitConsoleState() {
     this.wrapper.dispatchSplitConsoleCloseButtonToggle();
-  },
+  }
 
   /**
    * Handler for the tabNavigated notification.
    *
    * @param string event
    *        Event name.
    * @param object packet
    *        Notification packet received from the server.
    */
-  handleTabNavigated: async function(packet) {
+  async handleTabNavigated(packet) {
     if (packet.url) {
       this.onLocationChange(packet.url, packet.title);
     }
 
     if (!packet.nativeConsoleAPI) {
       this.logWarningAboutReplacedAPI();
     }
 
     // Wait for completion of any async dispatch before notifying that the console
     // is fully updated after a page reload
     await this.wrapper.waitAsyncDispatches();
     this.emit("reloaded");
-  },
+  }
 
-  handleTabWillNavigate: function(packet) {
+  handleTabWillNavigate(packet) {
     this.wrapper.dispatchTabWillNavigate(packet);
     if (packet.url) {
       this.onLocationChange(packet.url, packet.title);
     }
-  },
-};
+  }
+}
 
 /* This is the same as DevelopmentHelpers.quickRestart, but it runs in all
  * builds (even official). This allows a user to do a restart + session restore
  * with Ctrl+Shift+J (open Browser Console) and then Ctrl+Shift+R (restart).
  */
 function quickRestart() {
   const { Cc, Ci } = require("chrome");
   Services.obs.notifyObservers(null, "startupcache-invalidate");
   const env = Cc["@mozilla.org/process/environment;1"]
             .getService(Ci.nsIEnvironment);
   env.set("MOZ_DISABLE_SAFE_MODE_KEY", "1");
   Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
 }
 
-exports.WebConsoleFrame = WebConsoleFrame;
+exports.WebConsoleUI = WebConsoleUI;
--- a/devtools/client/webconsole/webconsole-wrapper.js
+++ b/devtools/client/webconsole/webconsole-wrapper.js
@@ -23,75 +23,79 @@ const App = createFactory(require("devto
 const ObjectClient = require("devtools/shared/client/object-client");
 const LongStringClient = require("devtools/shared/client/long-string-client");
 loader.lazyRequireGetter(this, "Constants", "devtools/client/webconsole/constants");
 loader.lazyRequireGetter(this, "getElementText", "devtools/client/webconsole/utils/clipboard", true);
 
 let store = null;
 
 class WebConsoleWrapper {
-  constructor(parentNode, hud, toolbox, owner, document) {
+  constructor(parentNode, webConsoleUI, toolbox, owner, document) {
     EventEmitter.decorate(this);
 
     this.parentNode = parentNode;
-    this.hud = hud;
+    this.webConsoleUI = webConsoleUI;
     this.toolbox = toolbox;
     this.owner = owner;
     this.document = document;
 
     this.init = this.init.bind(this);
 
     this.queuedMessageAdds = [];
     this.queuedMessageUpdates = [];
     this.queuedRequestUpdates = [];
     this.throttledDispatchPromise = null;
 
     this.telemetry = new Telemetry();
   }
 
   init() {
     return new Promise((resolve) => {
-      const attachRefToHud = (id, node) => {
-        this.hud[id] = node;
+      const attachRefToWebConsoleUI = (id, node) => {
+        this.webConsoleUI[id] = node;
       };
-      const { hud } = this;
+      const { webConsoleUI } = this;
       const debuggerClient = this.owner.target.client;
 
       const serviceContainer = {
-        attachRefToHud,
+        attachRefToWebConsoleUI,
         emitNewMessage: (node, messageId, timeStamp) => {
-          hud.emit("new-messages", new Set([{
+          webConsoleUI.emit("new-messages", new Set([{
             node,
             messageId,
             timeStamp,
           }]));
         },
-        hudProxy: hud.proxy,
+        proxy: webConsoleUI.proxy,
         openLink: (url, e) => {
-          hud.owner.openLink(url, e);
+          webConsoleUI.owner.openLink(url, e);
         },
         canRewind: () => {
-          if (!(hud.owner && hud.owner.target && hud.owner.target)) {
+          if (!(
+            webConsoleUI.owner
+            && webConsoleUI.owner.target
+            && webConsoleUI.owner.target.traits
+          )) {
             return false;
           }
 
-          return hud.owner.target.traits.canRewind;
+          return webConsoleUI.owner.target.traits.canRewind;
         },
         createElement: nodename => {
           return this.document.createElement(nodename);
         },
         getLongString: (grip) => {
-          return hud.proxy.webConsoleClient.getString(grip);
+          return webConsoleUI.proxy.webConsoleClient.getString(grip);
         },
         requestData(id, type) {
-          return hud.proxy.networkDataProvider.requestData(id, type);
+          return webConsoleUI.proxy.networkDataProvider.requestData(id, type);
         },
         onViewSource(frame) {
-          if (hud && hud.owner && hud.owner.viewSource) {
-            hud.owner.viewSource(frame.url, frame.line);
+          if (webConsoleUI && webConsoleUI.owner && webConsoleUI.owner.viewSource) {
+            webConsoleUI.owner.viewSource(frame.url, frame.line);
           }
         },
         recordTelemetryEvent: (eventName, extra = {}) => {
           this.telemetry.recordEvent(eventName, "webconsole", null, {
             ...extra,
             "session_id": this.toolbox && this.toolbox.sessionId || -1,
           });
         },
@@ -107,17 +111,17 @@ class WebConsoleWrapper {
           if (!actor) {
             return null;
           }
 
           return debuggerClient.release(actor);
         },
 
         getWebConsoleClient: () => {
-          return hud.webConsoleClient;
+          return webConsoleUI.webConsoleClient;
         },
 
         /**
          * Retrieve the FrameActor ID given a frame depth, or the selected one if no
          * frame depth given.
          *
          * @param {Number} frame: optional frame depth.
          * @return {String|null}: The FrameActor ID for the given frame depth (or the
@@ -131,43 +135,43 @@ class WebConsoleWrapper {
 
           const grip = Number.isInteger(frame)
             ? state.frames[frame]
             : state.frames[state.selected];
           return grip ? grip.actor : null;
         },
 
         inputHasSelection: () => {
-          const {editor, inputNode} = hud.jsterm || {};
+          const {editor, inputNode} = webConsoleUI.jsterm || {};
           return editor
             ? !!editor.getSelection()
             : (inputNode && inputNode.selectionStart !== inputNode.selectionEnd);
         },
 
         getInputValue: () => {
-          return hud.jsterm && hud.jsterm.getInputValue();
+          return webConsoleUI.jsterm && webConsoleUI.jsterm.getInputValue();
         },
 
         getInputCursor: () => {
-          return hud.jsterm && hud.jsterm.getSelectionStart();
+          return webConsoleUI.jsterm && webConsoleUI.jsterm.getSelectionStart();
         },
 
         getSelectedNodeActor: () => {
           const inspectorSelection = this.owner.getInspectorSelection();
           if (inspectorSelection && inspectorSelection.nodeFront) {
             return inspectorSelection.nodeFront.actorID;
           }
           return null;
         },
 
         getJsTermTooltipAnchor: () => {
           if (jstermCodeMirror) {
-            return hud.jsterm.node.querySelector(".CodeMirror-cursor");
+            return webConsoleUI.jsterm.node.querySelector(".CodeMirror-cursor");
           }
-          return hud.jsterm.completeNode;
+          return webConsoleUI.jsterm.completeNode;
         },
       };
 
       // Set `openContextMenu` this way so, `serviceContainer` variable
       // is available in the current scope and we can pass it into
       // `createContextMenu` method.
       serviceContainer.openContextMenu = (e, message) => {
         const { screenX, screenY, target } = e;
@@ -195,17 +199,17 @@ class WebConsoleWrapper {
         const sidebarTogglePref = store.getState().prefs.sidebarToggle;
         const openSidebar = sidebarTogglePref ? (messageId) => {
           store.dispatch(actions.showMessageObjectInSidebar(rootActorId, messageId));
         } : null;
 
         const messageData = getMessage(store.getState(), message.messageId);
         const executionPoint = messageData && messageData.executionPoint;
 
-        const menu = createContextMenu(this.hud, this.parentNode, {
+        const menu = createContextMenu(this.webConsoleUI, this.parentNode, {
           actor,
           clipboardText,
           variableText,
           message,
           serviceContainer,
           openSidebar,
           rootActorId,
           executionPoint,
@@ -235,17 +239,17 @@ class WebConsoleWrapper {
           "progress", this.dispatchProgress.bind(this));
 
         Object.assign(serviceContainer, {
           onViewSourceInDebugger: frame => {
             this.toolbox.viewSourceInDebugger(frame.url, frame.line).then(() => {
               this.telemetry.recordEvent("jump_to_source", "webconsole",
                                          null, { "session_id": this.toolbox.sessionId }
               );
-              this.hud.emit("source-in-debugger-opened");
+              this.webConsoleUI.emit("source-in-debugger-opened");
             });
           },
           onViewSourceInScratchpad: frame => this.toolbox.viewSourceInScratchpad(
             frame.url,
             frame.line
           ).then(() => {
             this.telemetry.recordEvent("jump_to_source", "webconsole",
                                        null, { "session_id": this.toolbox.sessionId }
@@ -293,36 +297,36 @@ class WebConsoleWrapper {
 
             return Promise.all([onNodeFrontSet, onInspectorUpdated]);
           },
           jumpToExecutionPoint: executionPoint =>
             this.toolbox.threadClient.timeWarp(executionPoint),
 
           onMessageHover: (type, messageId) => {
             const message = getMessage(store.getState(), messageId);
-            this.hud.emit("message-hover", type, message);
+            this.webConsoleUI.emit("message-hover", type, message);
           },
         });
       }
 
-      store = configureStore(this.hud, {
+      store = configureStore(this.webConsoleUI, {
         // We may not have access to the toolbox (e.g. in the browser console).
         sessionId: this.toolbox && this.toolbox.sessionId || -1,
         telemetry: this.telemetry,
         services: serviceContainer,
       });
 
       const {prefs} = store.getState();
       const jstermCodeMirror = prefs.jstermCodeMirror
         && !Services.appinfo.accessibilityEnabled;
 
       const app = App({
-        attachRefToHud,
+        attachRefToWebConsoleUI,
         serviceContainer,
-        hud,
+        webConsoleUI,
         onFirstMeaningfulPaint: resolve,
         closeSplitConsole: this.closeSplitConsole.bind(this),
         jstermCodeMirror,
       });
 
       // Render the root Application component.
       if (this.parentNode) {
         const provider = createElement(Provider, { store }, app);
@@ -342,21 +346,21 @@ class WebConsoleWrapper {
     let promise;
     // Also, do not expect any update while the panel is in background.
     if (waitForResponse && document.visibilityState === "visible") {
       const timeStampToMatch = packet.message
         ? packet.message.timeStamp
         : packet.timestamp;
 
       promise = new Promise(resolve => {
-        this.hud.on("new-messages", function onThisMessage(messages) {
+        this.webConsoleUI.on("new-messages", function onThisMessage(messages) {
           for (const m of messages) {
             if (m.timeStamp === timeStampToMatch) {
               resolve(m.node);
-              this.hud.off("new-messages", onThisMessage);
+              this.webConsoleUI.off("new-messages", onThisMessage);
               return;
             }
           }
         }.bind(this));
       });
     } else {
       promise = Promise.resolve();
     }
@@ -372,17 +376,17 @@ class WebConsoleWrapper {
   dispatchMessagesClear() {
     // We might still have pending message additions and updates when the clear action is
     // triggered, so we need to flush them to make sure we don't have unexpected behavior
     // in the ConsoleOutput.
     this.queuedMessageAdds = [];
     this.queuedMessageUpdates = [];
     this.queuedRequestUpdates = [];
     store.dispatch(actions.messagesClear());
-    this.hud.emit("messages-cleared");
+    this.webConsoleUI.emit("messages-cleared");
   }
 
   dispatchPrivateMessagesClear() {
     // We might still have pending private message additions when the private messages
     // clear action is triggered. We need to remove any private-window-issued packets from
     // the queue so they won't appear in the output.
 
     // For (network) message updates, we need to check both messages queue and the state
@@ -445,17 +449,17 @@ class WebConsoleWrapper {
     // that networkInfo.updates has all we need.
     // Note that 'requestPostData' is sent only for POST requests, so we need
     // to count with that.
     // 'fetchCacheDescriptor' will also cause a network update and increment
     // the number of networkInfo.updates
     const NUMBER_OF_NETWORK_UPDATE = 8;
 
     let expectedLength = NUMBER_OF_NETWORK_UPDATE;
-    if (this.hud.proxy.webConsoleClient.traits.fetchCacheDescriptor
+    if (this.webConsoleUI.webConsoleClient.traits.fetchCacheDescriptor
       && res.networkInfo.updates.includes("responseCache")) {
       expectedLength++;
     }
     if (res.networkInfo.updates.includes("requestPostData")) {
       expectedLength++;
     }
 
     if (res.networkInfo.updates.length === expectedLength) {
@@ -478,23 +482,23 @@ class WebConsoleWrapper {
 
   dispatchTabWillNavigate(packet) {
     const { ui } = store.getState();
 
     // For the browser console, we receive tab navigation
     // when the original top level window we attached to is closed,
     // but we don't want to reset console history and just switch to
     // the next available window.
-    if (ui.persistLogs || this.hud.isBrowserConsole) {
+    if (ui.persistLogs || this.webConsoleUI.isBrowserConsole) {
       // Add a type in order for this event packet to be identified by
       // utils/messages.js's `transformPacket`
       packet.type = "will-navigate";
       this.dispatchMessageAdd(packet);
     } else {
-      this.hud.webConsoleClient.clearNetworkRequests();
+      this.webConsoleUI.webConsoleClient.clearNetworkRequests();
       this.dispatchMessagesClear();
       store.dispatch({
         type: Constants.WILL_NAVIGATE,
       });
     }
   }
 
   batchedMessageUpdates(info) {
@@ -558,34 +562,34 @@ class WebConsoleWrapper {
             this.toolbox, "enter", "webconsole", null, "message_count", length);
         }
 
         this.queuedMessageAdds = [];
 
         if (this.queuedMessageUpdates.length > 0) {
           this.queuedMessageUpdates.forEach(({ message, res }) => {
             store.dispatch(actions.networkMessageUpdate(message, null, res));
-            this.hud.emit("network-message-updated", res);
+            this.webConsoleUI.emit("network-message-updated", res);
           });
           this.queuedMessageUpdates = [];
         }
         if (this.queuedRequestUpdates.length > 0) {
           this.queuedRequestUpdates.forEach(({ id, data}) => {
             store.dispatch(actions.networkUpdateRequest(id, data));
           });
           this.queuedRequestUpdates = [];
 
           // Fire an event indicating that all data fetched from
           // the backend has been received. This is based on
           // 'FirefoxDataProvider.isQueuePayloadReady', see more
           // comments in that method.
           // (netmonitor/src/connector/firefox-data-provider).
           // This event might be utilized in tests to find the right
           // time when to finish.
-          this.hud.emit("network-request-payload-ready");
+          this.webConsoleUI.emit("network-request-payload-ready");
         }
         done();
       }, 50);
     });
   }
 
   // Should be used for test purpose only.
   getStore() {
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -1,17 +1,17 @@
 /* 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/. */
 
 "use strict";
 
 var Services = require("Services");
 loader.lazyRequireGetter(this, "Utils", "devtools/client/webconsole/utils", true);
-loader.lazyRequireGetter(this, "WebConsoleFrame", "devtools/client/webconsole/webconsole-frame", true);
+loader.lazyRequireGetter(this, "WebConsoleUI", "devtools/client/webconsole/webconsole-ui", true);
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "viewSource", "devtools/client/shared/view-source");
 loader.lazyRequireGetter(this, "openDocLink", "devtools/client/shared/link", true);
 
 var gHudId = 0;
 
 /**
  * A WebConsole instance is an interactive console initialized *per target*
@@ -39,17 +39,17 @@ function WebConsole(target, iframeWindow
   this.target = target;
   this.browserWindow = this.chromeWindow.top;
   this.hudService = hudService;
 
   const element = this.browserWindow.document.documentElement;
   if (element.getAttribute("windowtype") != gDevTools.chromeWindowType) {
     this.browserWindow = this.hudService.currentContext();
   }
-  this.ui = new WebConsoleFrame(this);
+  this.ui = new WebConsoleUI(this);
 }
 
 WebConsole.prototype = {
   iframeWindow: null,
   chromeWindow: null,
   browserWindow: null,
   hudId: null,
   target: null,
@@ -110,18 +110,18 @@ WebConsole.prototype = {
    * @see webconsole.js::JSTerm
    * @type object
    */
   get jsterm() {
     return this.ui ? this.ui.jsterm : null;
   },
 
   /**
-   * Alias for the WebConsoleFrame.setFilterState() method.
-   * @see webconsole.js::WebConsoleFrame.setFilterState()
+   * Alias for the WebConsoleUI.setFilterState() method.
+   * @see webconsole.js::WebConsoleUI.setFilterState()
    */
   setFilterState() {
     this.ui && this.ui.setFilterState.apply(this.ui, arguments);
   },
 
   /**
    * Open a link in a new tab.
    *
--- a/devtools/docs/contributing.md
+++ b/devtools/docs/contributing.md
@@ -19,17 +19,17 @@ Whether you're an external contributor o
 * You [find a bug to work on](./contributing/find-bugs.md) (*note: we use bugs to track 'broken' things, new features and even discussions*).
 * [Work on the bug](./contributing/fixing-bugs.md).
 * [Request a review](./contributing/making-prs.md) for your code.
 * Land the code in the repository.
 * And you've contributed—well done 😀
 
 ## Help with design and UX
 
-If you're more interested in user experience (think: wireframes, workflows, navigations... and not necessarily implementation details), please have a look at the [UX](https://github.com/devtools-html/ux) repository—our friendly designers will be more than happy to welcome you onboard. You can also have a look at the [issues](https://github.com/devtools-html/ux/issues) they are considering right now, to get an idea of how it works.
+If you're more interested in user experience (think: wireframes, workflows, navigations... and not necessarily implementation details), please have a look at the [UX](https://github.com/firefox-devtools/ux) repository—our friendly designers will be more than happy to welcome you onboard. You can also have a look at the [issues](https://github.com/firefox-devtools/ux/issues) they are considering right now, to get an idea of how it works.
 
 ## Help with BUGS! 🐛🐞 <!--TODO: we might want to split this out to another page with more detail, in addition to this introductory section-->
 
 Not less importantly, we also love **when people file bugs**. They help us a lot and are very valuable (specially when they come with reproducible steps, e.g. in the case of crashes or malfunctions). Here is a short [guide on how to file good bugs](./filing-good-bugs.md) if you've never done it before (or if you need a reminder).
 
 Another thing that is super valuable is **reproducing** bugs (to validate they're happening in more than one environment), and also **completing** bugs, i.e. ensuring the bug has steps to reproduce, a test case, etc, as [mentioned on the guide](./filing-good-bugs.md). This saves time for the person(s) who will work on the bug, as then they can jump straight to fixing or implementing whatever is needed, instead of doing research work. If you can do any of these for a given bug, add a comment with the additional data that you found out.
 
 Likewise, if you think that a bug is solved, because you can't reproduce it and doesn't happen any more, this is also useful to know. We can always do with closing more bugs, so please leave a comment detailing as much information as you can provide 😀
--- a/devtools/docs/getting-started/where-is-the-code.md
+++ b/devtools/docs/getting-started/where-is-the-code.md
@@ -1,11 +1,11 @@
 # Where is the code? (or: `mozilla-central` vs `devtools-html`)
 
-Most of the code is hosted in the Firefox repository (we call it `mozilla-central`, often abbreviated as `m-c`), in the [devtools](https://dxr.mozilla.org/mozilla-central/source/devtools) folder. Development of newer pieces of the tools is happening in GitHub, on the [devtools-html](https://github.com/devtools-html/) organisation.
+Most of the code is hosted in the Firefox repository (we call it `mozilla-central`, often abbreviated as `m-c`), in the [devtools](https://dxr.mozilla.org/mozilla-central/source/devtools) folder. Development of some pieces of the tools is happening in GitHub, on the [firefox-devtools](https://github.com/firefox-devtools/) organisation.
 
 <!--TODO: table listing components and locations (m-c vs github)-->
 
 Code in `m-c` takes longer to obtain and build, as it involves checking out Firefox's repository and installing tools such as a compiler to build a version of Firefox in your machine.
 
 On the other hand, the repositories in `devtools-html` are more straightforward if you're used to *the GitHub workflow*: you clone them, and then run `npm install && npm run` or similar. Roughly, you can work with each repository individually, and we periodically generate JavaScript bundles that are then copied into `m-c`.
 
 Even if you only want to work on a tool whose code is on `devtools-html`, you might still need to go through the step of getting and compiling the code from `mozilla-central` in order to do integration work (such as updating a tool bundle).
--- a/devtools/docs/preferences.md
+++ b/devtools/docs/preferences.md
@@ -35,17 +35,17 @@ when running in Launchpad.
 
 DevTools relies on Services.pref to handle preferences. You can access the API docs for
 this service at:
 * [API docs for nsIPrefBranch](https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIPrefBranch)
 * [API docs for nsIPrefService](https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIPrefService)
 
 If you are using Launchpad, note that only a subset of nsIPrefService methods are
 implemented (addObserver and removeObserver). Launchpad relies on a Services shim file
-provided by devtools-module ([code on GitHub](https://github.com/devtools-html/devtools-core/blob/master/packages/devtools-modules/src/Services.js)).
+provided by devtools-module ([code on GitHub](https://github.com/firefox-devtools/devtools-core/blob/master/packages/devtools-modules/src/Services.js)).
 
 ### Requiring Services.pref
 
 To require Services and use Services.pref, you can normally use the following snippet:
 
 ```javascript
 const Services = require("Services");
 ```
@@ -81,23 +81,23 @@ should be available even when the client
 Fennec) the preference should go to devtools/shared/preferences/devtools-shared.js.
 Finally if a preference needs to be available very early during the Firefox startup
 sequence, it should go in devtools/startup/preferences/devtools-startup.js.
 
 ### Projects using Launchpad
 
 At the time of writing this doc, projects using Launchpad have to duplicate the default
 definition of a preference.
-* debugger.html: update [src/utils/prefs.js](https://github.com/devtools-html/debugger.html/blob/master/src/utils/prefs.js)
+* debugger.html: update [src/utils/prefs.js](https://github.com/firefox-devtools/debugger.html/blob/master/src/utils/prefs.js)
 * netmonitor: update [index.js](http://searchfox.org/mozilla-central/source/devtools/client/netmonitor/index.js)
 * webconsole: update [local-dev/index.js](http://searchfox.org/mozilla-central/source/devtools/client/webconsole/local-dev/index.js)
 
 ## Inspect preferences
 
 Depending on the project you are working on, preferences are stored differently but can
 always be inspected.
 
 In Firefox, you can open a tab to about:config and search by preference name.
 
 In Launchpad, preferences are actually saved to localStorage. Open DevTools on your
 Launchpad application and inspect the local storage content. You should see entries
 prefixed by `Services.prefs:`. You will only see preferences where a user-specific value
-has overridden the default value.
\ No newline at end of file
+has overridden the default value.
--- a/devtools/server/actors/errordocs.js
+++ b/devtools/server/actors/errordocs.js
@@ -127,29 +127,48 @@ const CorsErrorDocs = {
   CORSMethodNotFound: "CORSMethodNotFound",
   CORSMissingAllowCredentials: "CORSMissingAllowCredentials",
   CORSPreflightDidNotSucceed: "CORSPreflightDidNotSucceed",
   CORSInvalidAllowMethod: "CORSInvalidAllowMethod",
   CORSInvalidAllowHeader: "CORSInvalidAllowHeader",
   CORSMissingAllowHeaderFromPreflight: "CORSMissingAllowHeaderFromPreflight",
 };
 
+const baseStorageAccessPolicyErrorUrl = "https://developer.mozilla.org/docs/Mozilla/Firefox/Privacy/Storage_access_policy/Errors/";
+const storageAccessPolicyParams =
+  "?utm_source=devtools&utm_medium=firefox-cookie-errors&utm_campaign=default";
+const StorageAccessPolicyErrorDocs = {
+  cookieBlockedPermission: "CookieBlockedByPermission",
+  cookieBlockedTracker: "CookieBlockedTracker",
+  cookieBlockedAll: "CookieBlockedAll",
+  cookieBlockedForeign: "CookieBlockedForeign",
+};
+
 exports.GetURL = (error) => {
   if (!error) {
     return undefined;
   }
 
   const doc = ErrorDocs[error.errorMessageName];
   if (doc) {
     return baseErrorURL + doc + params;
   }
 
   const corsDoc = CorsErrorDocs[error.category];
   if (corsDoc) {
     return baseCorsErrorUrl + corsDoc + corsParams;
   }
 
+  const storageAccessPolicyDoc = StorageAccessPolicyErrorDocs[error.category];
+  if (storageAccessPolicyDoc) {
+    return (
+      baseStorageAccessPolicyErrorUrl +
+      storageAccessPolicyDoc +
+      storageAccessPolicyParams
+    );
+  }
+
   const categoryURL = ErrorCategories[error.category];
   if (categoryURL) {
     return categoryURL + params;
   }
   return undefined;
 };
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -1237,21 +1237,21 @@ WebConsoleActor.prototype =
           if (keyword.startsWith(result.matchProp)) {
             matches.add(keyword);
           }
         }
       }
 
       // Sort the results in order to display lowercased item first (e.g. we want to
       // display `document` then `Document` as we loosely match the user input if the
-      // first letter they typed was lowercase).
+      // first letter was lowercase).
+      const firstMeaningfulCharIndex = isElementAccess ? 1 : 0;
       matches = Array.from(matches).sort((a, b) => {
-        const startingQuoteRegex = /^('|"|`)/;
-        const aFirstMeaningfulChar = startingQuoteRegex.test(a) ? a[1] : a[0];
-        const bFirstMeaningfulChar = startingQuoteRegex.test(b) ? b[1] : b[0];
+        const aFirstMeaningfulChar = a[firstMeaningfulCharIndex];
+        const bFirstMeaningfulChar = b[firstMeaningfulCharIndex];
         const lA = aFirstMeaningfulChar.toLocaleLowerCase() === aFirstMeaningfulChar;
         const lB = bFirstMeaningfulChar.toLocaleLowerCase() === bFirstMeaningfulChar;
         if (lA === lB) {
           return a < b ? -1 : 1;
         }
         return lA ? -1 : 1;
       });
     }
--- a/devtools/shared/webconsole/js-property-provider.js
+++ b/devtools/shared/webconsole/js-property-provider.js
@@ -479,28 +479,26 @@ function JSPropertyProvider({
     if (isElementAccess) {
       // If it's an element access, we need to wrap properties in quotes (either the one
       // the user already typed, or `"`).
       matches = wrapMatchesInQuotes(matches, elementAccessQuote);
     } else if (!isWorker) {
       // If we're not performing an element access, we need to check that the property
       // are suited for a dot access. (Reflect.jsm is not available in worker context yet,
       // see Bug 1507181).
-      matches = new Set([...matches].filter(propertyName => {
-        let valid = true;
+      for (const match of matches) {
         try {
           // In order to know if the property is suited for dot notation, we use Reflect
           // to parse an expression where we try to access the property with a dot. If it
           // throws, this means that we need to do an element access instead.
-          Reflect.parse(`({${propertyName}: true})`);
+          Reflect.parse(`({${match}: true})`);
         } catch (e) {
-          valid = false;
+          matches.delete(match);
         }
-        return valid;
-      }));
+      }
     }
 
     return {isElementAccess, matchProp, matches};
   };
 
   // If the final property is a primitive
   if (typeof obj != "object") {
     return prepareReturnedObject(getMatchedProps(obj, search));
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
@@ -143,16 +143,18 @@ CanonicalBrowsingContext* BrowsingContex
   MOZ_LOG(GetLog(), LogLevel::Debug,
           ("Creating 0x%08" PRIx64 " from IPC (origin=0x%08" PRIx64 ")", aId,
            aOriginProcess ? uint64_t(aOriginProcess->ChildID()) : 0));
 
   RefPtr<BrowsingContext> context;
   if (XRE_IsParentProcess()) {
     context = new CanonicalBrowsingContext(
         aParent, aOpener, aName, aId, aOriginProcess->ChildID(), Type::Content);
+
+    context->Group()->Subscribe(aOriginProcess);
   } else {
     context = new BrowsingContext(aParent, aOpener, aName, aId, Type::Content);
   }
 
   Register(context);
 
   context->Attach();
 
@@ -216,26 +218,26 @@ void BrowsingContext::Detach() {
   RefPtr<BrowsingContext> kungFuDeathGrip(this);
 
   BrowsingContextMap<RefPtr>::Ptr p;
   if (sCachedBrowsingContexts && (p = sCachedBrowsingContexts->lookup(Id()))) {
     MOZ_DIAGNOSTIC_ASSERT(!mParent || !mParent->mChildren.Contains(this));
     MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this));
     sCachedBrowsingContexts->remove(p);
   } else {
-    auto* children = mParent ? &mParent->mChildren : &mGroup->Toplevels();
+    Children& children = mParent ? mParent->mChildren : mGroup->Toplevels();
 
     // TODO(farre): This assert looks extremely fishy, I know, but
     // what we're actually saying is this: if we're detaching, but our
     // parent doesn't have any children, it is because we're being
     // detached by the cycle collector destroying docshells out of
     // order.
-    MOZ_DIAGNOSTIC_ASSERT(children->IsEmpty() || children->Contains(this));
+    MOZ_DIAGNOSTIC_ASSERT(children.IsEmpty() || children.Contains(this));
 
-    children->RemoveElement(this);
+    children.RemoveElement(this);
   }
 
   Group()->Unregister(this);
 
   if (!XRE_IsContentProcess()) {
     return;
   }
 
@@ -287,16 +289,170 @@ void BrowsingContext::SetOpener(Browsing
     return;
   }
 
   auto cc = ContentChild::GetSingleton();
   MOZ_DIAGNOSTIC_ASSERT(cc);
   cc->SendSetOpenerBrowsingContext(this, aOpener);
 }
 
+// FindWithName follows the rules for choosing a browsing context,
+// with the exception of sandboxing for iframes. The implementation
+// for arbitrarily choosing between two browsing contexts with the
+// same name is as follows:
+//
+// 1) The start browsing context, i.e. 'this'
+// 2) Descendants in insertion order
+// 3) The parent
+// 4) Siblings and their children, both in insertion order
+// 5) After this we iteratively follow the parent chain, repeating 3
+//    and 4 until
+// 6) If there is no parent, consider all other top level browsing
+//    contexts and their children, both in insertion order
+//
+// See
+// https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
+BrowsingContext* BrowsingContext::FindWithName(const nsAString& aName) {
+  BrowsingContext* found = nullptr;
+  if (aName.IsEmpty()) {
+    // You can't find a browsing context with an empty name.
+    found = nullptr;
+  } else if (BrowsingContext* special = FindWithSpecialName(aName)) {
+    found = special;
+  } else if (aName.LowerCaseEqualsLiteral("_blank")) {
+    // Just return null. Caller must handle creating a new window with
+    // a blank name.
+    found = nullptr;
+  } else if (BrowsingContext* child = FindWithNameInSubtree(aName, this)) {
+    found = child;
+  } else {
+    BrowsingContext* current = this;
+
+    do {
+      Children* siblings;
+      BrowsingContext* parent = current->mParent;
+
+      if (!parent) {
+        // We've reached the root of the tree, consider browsing
+        // contexts in the same browsing context group.
+        siblings = &mGroup->Toplevels();
+      } else if (parent->NameEquals(aName) && CanAccess(parent) &&
+                 parent->IsActive()) {
+        found = parent;
+        break;
+      } else {
+        siblings = &parent->mChildren;
+      }
+
+      for (BrowsingContext* sibling : *siblings) {
+        if (sibling == current) {
+          continue;
+        }
+
+        if (BrowsingContext* relative =
+                sibling->FindWithNameInSubtree(aName, this)) {
+          found = relative;
+          // Breaks the outer loop
+          parent = nullptr;
+          break;
+        }
+      }
+
+      current = parent;
+    } while (current);
+  }
+
+  // Helpers should perform access control checks, which means that we
+  // only need to assert that we can access found.
+  MOZ_DIAGNOSTIC_ASSERT(!found || CanAccess(found));
+
+  return found;
+}
+
+BrowsingContext* BrowsingContext::FindChildWithName(const nsAString& aName) {
+  if (aName.IsEmpty()) {
+    // You can't find a browsing context with the empty name.
+    return nullptr;
+  }
+
+  for (BrowsingContext* child : mChildren) {
+    if (child->NameEquals(aName) && CanAccess(child) && child->IsActive()) {
+      return child;
+    }
+  }
+
+  return nullptr;
+}
+
+BrowsingContext* BrowsingContext::FindWithSpecialName(const nsAString& aName) {
+  // TODO(farre): Neither BrowsingContext nor nsDocShell checks if the
+  // browsing context pointed to by a special name is active. Should
+  // it be? See Bug 1527913.
+  if (aName.LowerCaseEqualsLiteral("_self")) {
+    return this;
+  }
+
+  if (aName.LowerCaseEqualsLiteral("_parent")) {
+    return mParent && CanAccess(mParent.get()) ? mParent.get() : this;
+  }
+
+  if (aName.LowerCaseEqualsLiteral("_top")) {
+    BrowsingContext* top = TopLevelBrowsingContext();
+
+    return CanAccess(top) ? top : nullptr;
+  }
+
+  return nullptr;
+}
+
+BrowsingContext* BrowsingContext::FindWithNameInSubtree(
+    const nsAString& aName, BrowsingContext* aRequestingContext) {
+  MOZ_DIAGNOSTIC_ASSERT(!aName.IsEmpty());
+
+  if (NameEquals(aName) && aRequestingContext->CanAccess(this) && IsActive()) {
+    return this;
+  }
+
+  for (BrowsingContext* child : mChildren) {
+    if (BrowsingContext* found =
+            child->FindWithNameInSubtree(aName, aRequestingContext)) {
+      return found;
+    }
+  }
+
+  return nullptr;
+}
+
+bool BrowsingContext::CanAccess(BrowsingContext* aContext) {
+  // TODO(farre): Bouncing this to nsDocShell::CanAccessItem is
+  // temporary, we should implement a replacement for this in
+  // BrowsingContext. See Bug 151590.
+  return aContext && nsDocShell::CanAccessItem(aContext->mDocShell, mDocShell);
+}
+
+bool BrowsingContext::IsActive() const {
+  // TODO(farre): Mimicking the bahaviour from
+  // ItemIsActive(nsIDocShellTreeItem* aItem) is temporary, we should
+  // implement a replacement for this using mClosed only. See Bug
+  // 1527321.
+
+  if (!mDocShell) {
+    return mClosed;
+  }
+
+  if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow()) {
+    auto* win = nsGlobalWindowOuter::Cast(window);
+    if (!win->GetClosedOuter()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 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()));
 
   if (sBrowsingContexts) {
     sBrowsingContexts->remove(Id());
@@ -491,32 +647,16 @@ void BrowsingContext::PostMessageMoz(JSC
                                      JS::Handle<JS::Value> aMessage,
                                      const WindowPostMessageOptions& aOptions,
                                      nsIPrincipal& aSubjectPrincipal,
                                      ErrorResult& aError) {
   PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, aOptions.mTransfer,
                  aSubjectPrincipal, aError);
 }
 
-already_AddRefed<BrowsingContext> BrowsingContext::FindChildWithName(
-    const nsAString& aName) {
-  // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1515646 will reimplement
-  //       this on top of the BC tree.
-  MOZ_ASSERT(mDocShell);
-  nsCOMPtr<nsIDocShellTreeItem> child;
-  mDocShell->FindChildWithName(aName, false, true, nullptr, nullptr,
-                               getter_AddRefs(child));
-  nsCOMPtr<nsIDocShell> childDS = do_QueryInterface(child);
-  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);
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -125,32 +125,51 @@ class BrowsingContext : public nsWrapper
   // Determine if the current BrowsingContext was 'cached' by the logic in
   // CacheChildren.
   bool IsCached();
 
   // TODO(farre): We should sync changes from SetName to the parent
   // process. [Bug 1490303]
   void SetName(const nsAString& aName) { mName = aName; }
   const nsString& Name() const { return mName; }
+  void GetName(nsAString& aName) { aName = mName; }
   bool NameEquals(const nsAString& aName) { return mName.Equals(aName); }
 
   bool IsContent() const { return mType == Type::Content; }
 
   uint64_t Id() const { return mBrowsingContextId; }
 
   BrowsingContext* GetParent() { return mParent; }
 
   void GetChildren(nsTArray<RefPtr<BrowsingContext>>& aChildren);
 
   BrowsingContext* GetOpener() const { return mOpener; }
 
   void SetOpener(BrowsingContext* aOpener);
 
   BrowsingContextGroup* Group() { return mGroup; }
 
+  // Using the rules for choosing a browsing context we try to find
+  // the browsing context with the given name in the set of
+  // transitively reachable browsing contexts. Performs access control
+  // with regards to this.
+  // See
+  // https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name.
+  //
+  // BrowsingContext::FindWithName(const nsAString&) is equivalent to
+  // calling nsIDocShellTreeItem::FindItemWithName(aName, nullptr,
+  // nullptr, false, <return value>).
+  BrowsingContext* FindWithName(const nsAString& aName);
+
+  // Find a browsing context in this context's list of
+  // children. Doesn't consider the special names, '_self', '_parent',
+  // '_top', or '_blank'. Performs access control with regard to
+  // 'this'.
+  BrowsingContext* FindChildWithName(const nsAString& aName);
+
   nsISupports* GetParentObject() const;
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   // This function would be called when its corresponding document is activated
   // by user gesture, and we would set the flag in the top level browsing
   // context.
   void NotifyUserGestureActivation();
@@ -199,27 +218,41 @@ class BrowsingContext : public nsWrapper
   void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const nsAString& aTargetOrigin,
                       const Sequence<JSObject*>& aTransfer,
                       nsIPrincipal& aSubjectPrincipal, ErrorResult& aError);
   void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const WindowPostMessageOptions& aOptions,
                       nsIPrincipal& aSubjectPrincipal, ErrorResult& aError);
 
-  already_AddRefed<BrowsingContext> FindChildWithName(const nsAString& aName);
-
   JSObject* WrapObject(JSContext* aCx);
 
  protected:
   virtual ~BrowsingContext();
   BrowsingContext(BrowsingContext* aParent, BrowsingContext* aOpener,
                   const nsAString& aName, uint64_t aBrowsingContextId,
                   Type aType);
 
  private:
+  // Find the special browsing context if aName is '_self', '_parent',
+  // '_top', but not '_blank'. The latter is handled in FindWithName
+  BrowsingContext* FindWithSpecialName(const nsAString& aName);
+
+  // Find a browsing context in the subtree rooted at 'this' Doesn't
+  // consider the special names, '_self', '_parent', '_top', or
+  // '_blank'. Performs access control with regard to
+  // 'aRequestingContext'.
+  BrowsingContext* FindWithNameInSubtree(const nsAString& aName,
+                                         BrowsingContext* aRequestingContext);
+
+  // Performs access control to check that 'this' can access 'aContext'.
+  bool CanAccess(BrowsingContext* aContext);
+
+  bool IsActive() const;
+
   friend class ::nsOuterWindowProxy;
   friend class ::nsGlobalWindowOuter;
   // Update the window proxy object that corresponds to this browsing context.
   // This should be called from the window proxy object's objectMoved hook, if
   // the object mWindowProxy points to was moved by the JS GC.
   void UpdateWindowProxy(JSObject* obj, JSObject* old) {
     if (mWindowProxy) {
       MOZ_ASSERT(mWindowProxy == old);
--- a/docshell/base/BrowsingContextGroup.cpp
+++ b/docshell/base/BrowsingContextGroup.cpp
@@ -6,69 +6,58 @@
 
 #include "mozilla/dom/BrowsingContextGroup.h"
 #include "mozilla/dom/BrowsingContextBinding.h"
 #include "mozilla/dom/BindingUtils.h"
 
 namespace mozilla {
 namespace dom {
 
-StaticAutoPtr<nsTArray<RefPtr<BrowsingContextGroup>>>
-    BrowsingContextGroup::sAllGroups;
-
-/* static */ void BrowsingContextGroup::Init() {
-  if (!sAllGroups) {
-    sAllGroups = new nsTArray<RefPtr<BrowsingContextGroup>>();
-    ClearOnShutdown(&sAllGroups);
-  }
-}
-
 bool BrowsingContextGroup::Contains(BrowsingContext* aBrowsingContext) {
   return aBrowsingContext->Group() == this;
 }
 
 void BrowsingContextGroup::Register(BrowsingContext* aBrowsingContext) {
+  MOZ_DIAGNOSTIC_ASSERT(aBrowsingContext);
   mContexts.PutEntry(aBrowsingContext);
 }
 
 void BrowsingContextGroup::Unregister(BrowsingContext* aBrowsingContext) {
+  MOZ_DIAGNOSTIC_ASSERT(aBrowsingContext);
   mContexts.RemoveEntry(aBrowsingContext);
 }
 
-BrowsingContextGroup::BrowsingContextGroup() {
-  sAllGroups->AppendElement(this);
+void BrowsingContextGroup::Subscribe(ContentParent* aOriginProcess) {
+  MOZ_DIAGNOSTIC_ASSERT(aOriginProcess);
+  mSubscribers.PutEntry(aOriginProcess);
+  aOriginProcess->OnBrowsingContextGroupSubscribe(this);
+}
+
+void BrowsingContextGroup::Unsubscribe(ContentParent* aOriginProcess) {
+  MOZ_DIAGNOSTIC_ASSERT(aOriginProcess);
+  mSubscribers.RemoveEntry(aOriginProcess);
+  aOriginProcess->OnBrowsingContextGroupUnsubscribe(this);
 }
 
 BrowsingContextGroup::~BrowsingContextGroup() {
-  if (sAllGroups) {
-    sAllGroups->RemoveElement(this);
+  for (auto iter = mSubscribers.Iter(); !iter.Done(); iter.Next()) {
+    nsRefPtrHashKey<ContentParent>* entry = iter.Get();
+    entry->GetKey()->OnBrowsingContextGroupUnsubscribe(this);
   }
 }
 
 nsISupports* BrowsingContextGroup::GetParentObject() const {
   return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
 }
 
 JSObject* BrowsingContextGroup::WrapObject(JSContext* aCx,
                                            JS::Handle<JSObject*> aGivenProto) {
   return BrowsingContextGroup_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(BrowsingContextGroup)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowsingContextGroup)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mContexts)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mToplevels)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowsingContextGroup)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContexts)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToplevels)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(BrowsingContextGroup)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(BrowsingContextGroup, mContexts,
+                                      mToplevels, mSubscribers)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(BrowsingContextGroup, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(BrowsingContextGroup, Release)
 
 }  // namespace dom
 }  // namespace mozilla
--- a/docshell/base/BrowsingContextGroup.h
+++ b/docshell/base/BrowsingContextGroup.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_BrowsingContextGroup_h
 #define mozilla_dom_BrowsingContextGroup_h
 
 #include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/StaticPtr.h"
 #include "nsHashKeys.h"
 #include "nsTArray.h"
 #include "nsTHashtable.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
@@ -26,48 +27,54 @@ class BrowsingContext;
 //
 // A BrowsingContext may not hold references to other BrowsingContext objects
 // which are not in the same BrowsingContextGroup.
 class BrowsingContextGroup final : public nsWrapperCache {
  public:
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BrowsingContextGroup)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(BrowsingContextGroup)
 
-  static void Init();
+  typedef nsTHashtable<nsRefPtrHashKey<ContentParent>> ContentParents;
 
   // Interact with the list of BrowsingContexts.
   bool Contains(BrowsingContext* aContext);
   void Register(BrowsingContext* aContext);
   void Unregister(BrowsingContext* aContext);
 
+  // Interact with the list of ContentParents
+  void Subscribe(ContentParent* aOriginProcess);
+  void Unsubscribe(ContentParent* aOriginProcess);
+
+  ContentParents::Iterator ContentParentsIter() { return mSubscribers.Iter(); }
+
   // Get a reference to the list of toplevel contexts in this
   // BrowsingContextGroup.
   BrowsingContext::Children& Toplevels() { return mToplevels; }
   void GetToplevels(BrowsingContext::Children& aToplevels) {
     aToplevels.AppendElements(mToplevels);
   }
 
   nsISupports* GetParentObject() const;
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
-  BrowsingContextGroup();
+  BrowsingContextGroup() = default;
 
  private:
   friend class CanonicalBrowsingContext;
 
   ~BrowsingContextGroup();
 
-  static StaticAutoPtr<nsTArray<RefPtr<BrowsingContextGroup>>> sAllGroups;
-
   // A BrowsingContextGroup contains a series of BrowsingContext objects. They
   // are addressed using a hashtable to avoid linear lookup when adding or
   // removing elements from the set.
   nsTHashtable<nsRefPtrHashKey<BrowsingContext>> mContexts;
 
   // The set of toplevel browsing contexts in the current BrowsingContextGroup.
   BrowsingContext::Children mToplevels;
+
+  ContentParents mSubscribers;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // !defined(mozilla_dom_BrowsingContextGroup_h)
--- a/docshell/base/CanonicalBrowsingContext.cpp
+++ b/docshell/base/CanonicalBrowsingContext.cpp
@@ -25,37 +25,16 @@ CanonicalBrowsingContext::CanonicalBrows
                                                    BrowsingContext::Type aType)
     : BrowsingContext(aParent, aOpener, aName, aBrowsingContextId, aType),
       mProcessId(aProcessId) {
   // You are only ever allowed to create CanonicalBrowsingContexts in the
   // parent process.
   MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
 }
 
-// TODO(farre): CanonicalBrowsingContext::CleanupContexts starts from the
-// list of root BrowsingContexts. This isn't enough when separate
-// BrowsingContext nodes of a BrowsingContext tree, not in a crashing
-// child process, are from that process and thus needs to be
-// cleaned. [Bug 1472108]
-/* static */ void CanonicalBrowsingContext::CleanupContexts(
-    uint64_t aProcessId) {
-  nsTArray<RefPtr<BrowsingContext>> contexts;
-  for (auto& group : *BrowsingContextGroup::sAllGroups) {
-    for (auto& context : group->Toplevels()) {
-      if (Cast(context)->IsOwnedByProcess(aProcessId)) {
-        contexts.AppendElement(context);
-      }
-    }
-  }
-
-  for (auto& context : contexts) {
-    context->Detach();
-  }
-}
-
 /* static */ already_AddRefed<CanonicalBrowsingContext>
 CanonicalBrowsingContext::Get(uint64_t aId) {
   MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
   return BrowsingContext::Get(aId).downcast<CanonicalBrowsingContext>();
 }
 
 /* static */ CanonicalBrowsingContext* CanonicalBrowsingContext::Cast(
     BrowsingContext* aContext) {
--- a/docshell/base/CanonicalBrowsingContext.h
+++ b/docshell/base/CanonicalBrowsingContext.h
@@ -21,17 +21,16 @@ namespace dom {
 
 class WindowGlobalParent;
 
 // CanonicalBrowsingContext is a BrowsingContext living in the parent
 // process, with whatever extra data that a BrowsingContext in the
 // parent needs.
 class CanonicalBrowsingContext final : public BrowsingContext {
  public:
-  static void CleanupContexts(uint64_t aProcessId);
   static already_AddRefed<CanonicalBrowsingContext> Get(uint64_t aId);
   static CanonicalBrowsingContext* Cast(BrowsingContext* aContext);
   static const CanonicalBrowsingContext* Cast(const BrowsingContext* aContext);
 
   bool IsOwnedByProcess(uint64_t aProcessId) const {
     return mProcessId == aProcessId;
   }
   uint64_t OwnerProcessId() const { return mProcessId; }
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -8651,19 +8651,19 @@ nsresult nsDocShell::PerformRetargeting(
 
       return NS_ERROR_CONTENT_BLOCKED;
     }
   }
 
   if ((!targetDocShell || targetDocShell == static_cast<nsIDocShell*>(this))) {
     bool handled;
     rv = MaybeHandleLoadDelegate(aLoadState,
-                                 targetDocShell ?
-                                 nsIBrowserDOMWindow::OPEN_CURRENTWINDOW :
-                                 nsIBrowserDOMWindow::OPEN_NEWWINDOW,
+                                 targetDocShell
+                                     ? nsIBrowserDOMWindow::OPEN_CURRENTWINDOW
+                                     : nsIBrowserDOMWindow::OPEN_NEWWINDOW,
                                  &handled);
     if (NS_FAILED(rv)) {
       return rv;
     }
     if (handled) {
       return NS_OK;
     }
   }
@@ -8682,17 +8682,17 @@ nsresult nsDocShell::PerformRetargeting(
   if (!targetDocShell) {
     // If the docshell's document is sandboxed, only open a new window
     // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
     // (i.e. if allow-popups is specified)
     NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
     Document* doc = mContentViewer->GetDocument();
 
     const bool isDocumentAuxSandboxed =
-      doc && (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
+        doc && (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
 
     if (isDocumentAuxSandboxed) {
       return NS_ERROR_DOM_INVALID_ACCESS_ERR;
     }
 
     nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
     NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
 
@@ -12988,49 +12988,31 @@ nsDocShell::SetOriginAttributesBeforeLoa
   if (!attrs.Init(aCx, aOriginAttributes)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   return SetOriginAttributes(attrs);
 }
 
 NS_IMETHODIMP
-nsDocShell::ResumeRedirectedLoad(uint64_t aIdentifier, int32_t aHistoryIndex) {
+nsDocShell::ResumeRedirectedLoad(uint64_t aIdentifier) {
   RefPtr<nsDocShell> self = this;
   RefPtr<ChildProcessChannelListener> cpcl =
       ChildProcessChannelListener::GetSingleton();
 
   // Call into InternalLoad with the pending channel when it is received.
-  cpcl->RegisterCallback(
-      aIdentifier, [self, aHistoryIndex](nsIChildChannel* aChannel) {
-        RefPtr<nsDocShellLoadState> loadState;
-        nsresult rv = nsDocShellLoadState::CreateFromPendingChannel(
-            aChannel, getter_AddRefs(loadState));
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return;
-        }
-
-        // If we're performing a history load, locate the correct history entry,
-        // and set the relevant bits on our loadState.
-        if (aHistoryIndex >= 0) {
-          nsCOMPtr<nsISHistory> legacySHistory =
-              self->mSessionHistory->LegacySHistory();
-
-          nsCOMPtr<nsISHEntry> entry;
-          rv = legacySHistory->GetEntryAtIndex(aHistoryIndex,
-                                               getter_AddRefs(entry));
-          if (NS_SUCCEEDED(rv)) {
-            legacySHistory->InternalSetRequestedIndex(aHistoryIndex);
-            loadState->SetLoadType(LOAD_HISTORY);
-            loadState->SetSHEntry(entry);
-          }
-        }
-
-        self->InternalLoad(loadState, nullptr, nullptr);
-      });
+  cpcl->RegisterCallback(aIdentifier, [self](nsIChildChannel* aChannel) {
+    RefPtr<nsDocShellLoadState> loadState;
+    nsresult rv = nsDocShellLoadState::CreateFromPendingChannel(
+        aChannel, getter_AddRefs(loadState));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+    self->InternalLoad(loadState, nullptr, nullptr);
+  });
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,
                                 JSContext* aCx) {
   OriginAttributes attrs;
   if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -397,16 +397,17 @@ class nsDocShell final : public nsDocLoa
   nsresult InternalLoad(nsDocShellLoadState* aLoadState,
                         nsIDocShell** aDocShell, nsIRequest** aRequest);
 
  private:  // member functions
   friend class nsDSURIContentListener;
   friend class FramingChecker;
   friend class OnLinkClickEvent;
   friend class nsIDocShell;
+  friend class mozilla::dom::BrowsingContext;
 
   // It is necessary to allow adding a timeline marker wherever a docshell
   // instance is available. This operation happens frequently and needs to
   // be very fast, so instead of using a Map or having to search for some
   // docshell-specific markers storage, a pointer to an `ObservedDocShell` is
   // is stored on docshells directly.
   friend void mozilla::TimelineConsumers::AddConsumer(nsDocShell*);
   friend void mozilla::TimelineConsumers::RemoveConsumer(nsDocShell*);
--- a/docshell/base/nsIWebNavigation.idl
+++ b/docshell/base/nsIWebNavigation.idl
@@ -337,15 +337,11 @@ interface nsIWebNavigation : nsISupports
    * Set an OriginAttributes dictionary in the docShell. This can be done only
    * before loading any content.
    */
   [implicit_jscontext]
   void setOriginAttributesBeforeLoading(in jsval originAttributes);
 
   /**
    * Resume a load which has been redirected from another process.
-   *
-   * A negative |aHistoryIndex| value corresponds to a non-history load being
-   * resumed.
    */
-  void resumeRedirectedLoad(in unsigned long long aLoadIdentifier,
-                            in long aHistoryIndex);
+  void resumeRedirectedLoad(in unsigned long long aLoadIdentifier);
 };
--- a/docshell/build/nsDocShellModule.cpp
+++ b/docshell/build/nsDocShellModule.cpp
@@ -11,17 +11,16 @@
 #include "nsSHEntryShared.h"
 #include "nsSHistory.h"
 
 namespace mozilla {
 
 // The one time initialization for this module
 nsresult InitDocShellModule() {
   mozilla::dom::BrowsingContext::Init();
-  mozilla::dom::BrowsingContextGroup::Init();
   nsresult rv = nsSHistory::Startup();
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 void UnloadDocShellModule() {
   nsSHistory::Shutdown();
--- a/docshell/shistory/nsISHistory.idl
+++ b/docshell/shistory/nsISHistory.idl
@@ -48,24 +48,16 @@ interface nsISHistory: nsISupports
    * A readonly property of the interface that returns
    * the index of the last document that started to load and
    * didn't finished yet. When document finishes the loading
    * value -1 is returned.
    */
   [infallible] readonly attribute long requestedIndex;
 
   /**
-   * Artifically set the |requestedIndex| for this nsISHEntry to the given
-   * index. This is used when resuming a cross-process load from a different
-   * process.
-   */
-  [noscript, notxpcom]
-  void internalSetRequestedIndex(in long aRequestedIndex);
-
-  /**
    * Get the history entry at a given index. Returns non-null on success.
    *
    * @param index             The index value whose entry is requested.
    *                          The oldest entry is located at index == 0.
    * @return                  The found entry; never null.
    */
   nsISHEntry getEntryAtIndex(in long aIndex);
 
--- a/docshell/shistory/nsSHistory.cpp
+++ b/docshell/shistory/nsSHistory.cpp
@@ -621,22 +621,16 @@ nsSHistory::SetIndex(int32_t aIndex) {
 /* Get the requestedIndex */
 NS_IMETHODIMP
 nsSHistory::GetRequestedIndex(int32_t* aResult) {
   MOZ_ASSERT(aResult, "null out param?");
   *aResult = mRequestedIndex;
   return NS_OK;
 }
 
-NS_IMETHODIMP_(void)
-nsSHistory::InternalSetRequestedIndex(int32_t aRequestedIndex) {
-  MOZ_ASSERT(aRequestedIndex >= -1 && aRequestedIndex < Length());
-  mRequestedIndex = aRequestedIndex;
-}
-
 NS_IMETHODIMP
 nsSHistory::GetEntryAtIndex(int32_t aIndex, nsISHEntry** aResult) {
   NS_ENSURE_ARG_POINTER(aResult);
 
   if (aIndex < 0 || aIndex >= Length()) {
     return NS_ERROR_FAILURE;
   }
 
--- a/docshell/test/browser/browser.ini
+++ b/docshell/test/browser/browser.ini
@@ -111,9 +111,11 @@ skip-if = os == 'win' && !debug # bug 13
 [browser_timelineMarkers-02.js]
 skip-if = true # Bug 1220415
 [browser_timelineMarkers-03.js]
 [browser_timelineMarkers-04.js]
 [browser_timelineMarkers-05.js]
 [browser_ua_emulation.js]
 [browser_history_triggeringprincipal_viewsource.js]
 [browser_click_link_within_view_source.js]
-[browser_browsingContext.js]
+[browser_browsingContext-01.js]
+[browser_browsingContext-02.js]
+[browser_browsingContext-03.js]
rename from docshell/test/browser/browser_browsingContext.js
rename to docshell/test/browser/browser_browsingContext-01.js
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/browser_browsingContext-02.js
@@ -0,0 +1,119 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function() {
+  await BrowserTestUtils.withNewTab(
+    {gBrowser, url: "about:blank"}, async function(browser) {
+      const BASE1 = getRootDirectory(gTestPath)
+            .replace("chrome://mochitests/content", "http://example.com");
+      const BASE2 = getRootDirectory(gTestPath)
+            .replace("chrome://mochitests/content", "http://test1.example.com");
+      const URL = BASE1 + "onload_message.html";
+      let sixth = BrowserTestUtils.waitForNewTab(gBrowser, URL + "#sixth", true, true);
+      let seventh = BrowserTestUtils.waitForNewTab(gBrowser, URL + "#seventh", true, true);
+      await ContentTask.spawn(browser, {base1: BASE1, base2: BASE2},
+                              async function({base1, base2}) {
+        let top = content.window;
+        top.name = 'top';
+        top.location.href += "#top";
+
+        let contexts = {
+          top: top.location.href,
+          first: base1 + "dummy_page.html#first",
+          third: base2 + "dummy_page.html#third",
+          second: base1 + "dummy_page.html#second",
+          fourth: base2 + "dummy_page.html#fourth",
+          fifth: base1 + "dummy_page.html#fifth",
+          sixth: base1 + "onload_message.html#sixth",
+          seventh: base1 + "onload_message.html#seventh"
+        };
+
+        function addFrame(target, name) {
+          let doc = (target.contentWindow || target).document;
+          let frame = doc.createElement('iframe');
+          let p = new Promise(resolve => (frame.onload = () =>resolve(frame)));
+          doc.body.appendChild(frame);
+          frame.name = name;
+          frame.src = contexts[name];
+          return p;
+        }
+
+        function addWindow(target, name, {options, resolve}) {
+          var win = target.contentWindow.open(contexts[name], name, options);
+
+          if (resolve) {
+            return new Promise(
+                resolve => target.contentWindow.addEventListener(
+                    'message', () => resolve(win)));
+          } else {
+            return Promise.resolve({name: name});
+          }
+        }
+
+        // We're going to create a tree that looks like the
+        // following.
+        //
+        //           top          sixth    seventh
+        //          /   \
+        //         /     \        /
+        //      first  second
+        //      /   \           /
+        //     /     \
+        //  third  fourth - - -
+        //          /
+        //         /
+        //      fifth
+        //
+        // The idea is to have one top level non-auxiliary browsing
+        // context, five nested, one top level auxiliary with an
+        // opener, and one top level without an opener. Given that
+        // set of related and one unrelated browsing contexts we
+        // wish to confirm that targeting is able to find
+        // appropriate browsing contexts.
+
+
+        function bc(frame) {
+          return (frame.contentWindow || frame).docShell.browsingContext;
+        }
+
+        function reachable(start, targets) {
+          for (let target of targets) {
+            is(bc(start).findWithName(target.name), bc(target),
+               [bc(start).name, 'can reach', target.name].join(' '));
+          }
+        }
+
+        function unreachable(start, target) {
+          is(bc(start).findWithName(target.name), null,
+             [bc(start).name, "can't reach", target.name].join(' '));
+        }
+
+        let first = await addFrame(top, 'first');
+        info('first');
+        let second = await addFrame(top, 'second');
+        info('second');
+        let third = await addFrame(first, 'third');
+        info('third');
+        let fourth = await addFrame(first, 'fourth');
+        info('fourth');
+        let fifth = await addFrame(fourth, 'fifth');
+        info('fifth');
+        let sixth = await addWindow(fourth, 'sixth', { resolve: true });
+        info('sixth');
+        let seventh = await addWindow(fourth, 'seventh', { options: ['noopener'] });
+        info('seventh');
+
+        let frames = [top, first, second, third, fourth, fifth, sixth];
+        for (let start of frames) {
+          reachable(start, frames);
+          unreachable(start, seventh);
+        }
+      });
+
+      for (let tab of await Promise.all([sixth, seventh])) {
+        BrowserTestUtils.removeTab(tab);
+      }
+    });
+});
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/browser_browsingContext-03.js
@@ -0,0 +1,136 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function() {
+  await BrowserTestUtils.withNewTab(
+    {gBrowser, url: "about:blank"}, async function(browser) {
+      const BASE1 = getRootDirectory(gTestPath)
+            .replace("chrome://mochitests/content", "http://example.com");
+      const BASE2 = getRootDirectory(gTestPath)
+            .replace("chrome://mochitests/content", "http://test1.example.com");
+      const URL = BASE1 + "onload_message.html";
+      let sixth = BrowserTestUtils.waitForNewTab(gBrowser, URL + "#sixth", true, true);
+      await ContentTask.spawn(browser, {base1: BASE1, base2: BASE2},
+                              async function({base1, base2}) {
+        let top = content.window;
+        top.name = 'top';
+        top.location.href += "#top";
+
+        let contexts = {
+          top: top.location.href,
+          first: base1 + "dummy_page.html#first",
+          third: base2 + "dummy_page.html#third",
+          second: base1 + "dummy_page.html#second",
+          fourth: base2 + "dummy_page.html#fourth",
+          fifth: base1 + "dummy_page.html#fifth",
+          sixth: base1 + "onload_message.html#sixth",
+        };
+
+        function addFrame(target, name) {
+          let doc = (target.contentWindow || target).document;
+          let frame = doc.createElement('iframe');
+          let p = new Promise(resolve => (frame.onload = () =>resolve(frame)));
+          doc.body.appendChild(frame);
+          frame.name = name;
+          frame.src = contexts[name];
+          return p;
+        }
+
+        function addWindow(target, name) {
+          var win = target.contentWindow.open(contexts[name], name);
+
+          return new Promise(
+              resolve => target.contentWindow.addEventListener(
+                  'message', () => resolve(win)));
+        }
+
+        // Generate all lists of length length with every combination of
+        // values in input
+        function* generate(input, length) {
+          let list = new Array(length);
+
+          function* values(pos) {
+            if (pos >= list.length) {
+              yield list;
+            } else {
+              for (let v of input) {
+                list[pos] = v;
+                yield* values(pos + 1);
+              }
+            }
+          }
+          yield* values(0);
+        }
+
+        // We're going to create a tree that looks like the
+        // follwing.
+        //
+        //           top          sixth
+        //          /   \
+        //         /     \        /
+        //      first  second
+        //      /   \           /
+        //     /     \
+        //  third  fourth - - -
+        //          /
+        //         /
+        //      fifth
+        //
+        // The idea is to have one top level non-auxiliary browsing
+        // context, five nested, one top level auxiliary with an
+        // opener. Given that set of related browsing contexts we
+        // wish to confirm that targeting is semantically equivalent
+        // with how nsIDocShellTreeItem.findItemWithName works. The
+        // trick to ensure that is to give all frames the same name!
+        // and ensure that the find algorithms return the same nodes
+        // in the same order.
+
+        function bc(frame) {
+          return (frame.contentWindow || frame).docShell.browsingContext;
+        }
+
+        let first = await addFrame(top, 'first');
+        let second = await addFrame(top, 'second');
+        let third = await addFrame(first, 'third');
+        let fourth = await addFrame(first, 'fourth');
+        let fifth = await addFrame(fourth, 'fifth');
+        let sixth = await addWindow(fourth, 'sixth');
+
+        let frames = [top, first, second, third, fourth, fifth, sixth];
+        let browsingContexts = frames.map(bc);
+        let docShells = browsingContexts.map(context => context.docShell);
+
+        ok(top.docShell instanceof Ci.nsIDocShellTreeItem,
+           "When we remove nsIDocShellTreeItem this test should be removed");
+
+        // For every browsing context we generate all possible
+        // combinations of names for these browsing contexts using
+        // "dummy" and "target" as possible name.
+        for (let names of generate(["dummy", "target"], docShells.length)) {
+          for (let i = names.length - 1; i >= 0; --i) {
+            docShells[i].name = names[i];
+          }
+
+          for (let i = 0; i < docShells.length; ++i) {
+            let docShell = docShells[i].findItemWithName('target', null, null, false);
+            let browsingContext = browsingContexts[i].findWithName('target');
+            is (docShell ? docShell.browsingContext : null, browsingContext,
+                "findItemWithName should find same browsing context as findWithName");
+          }
+        }
+
+        for (let target of ["_self", "_top", "_parent", "_blank"]) {
+          for (let i = 0; i < docShells.length; ++i) {
+            let docShell = docShells[i].findItemWithName(target, null, null, false);
+            let browsingContext = browsingContexts[i].findWithName(target);
+            is (docShell ? docShell.browsingContext : null, browsingContext,
+                "findItemWithName should find same browsing context as findWithName for " + target);
+          }
+        }
+      });
+
+      BrowserTestUtils.removeTab(await sixth);
+    });
+});
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -22,18 +22,19 @@
 #include "mozilla/KeyframeUtils.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/TypeTraits.h"
 #include "Layers.h"              // For Layer
 #include "nsComputedDOMStyle.h"  // nsComputedDOMStyle::GetComputedStyle
 #include "nsContentUtils.h"
 #include "nsCSSPropertyIDSet.h"
-#include "nsCSSProps.h"           // For nsCSSProps::PropHasFlags
-#include "nsCSSPseudoElements.h"  // For CSSPseudoElementType
+#include "nsCSSProps.h"             // For nsCSSProps::PropHasFlags
+#include "nsCSSPseudoElements.h"    // For CSSPseudoElementType
+#include "nsDOMMutationObserver.h"  // For nsAutoAnimationMutationBatch
 #include "nsIFrame.h"
 #include "nsIPresShell.h"
 #include "nsIScriptError.h"
 #include "nsRefreshDriver.h"
 
 namespace mozilla {
 
 bool PropertyValuePair::operator==(const PropertyValuePair& aOther) const {
@@ -106,17 +107,17 @@ void KeyframeEffect::SetComposite(const 
 
   mEffectOptions.mComposite = aComposite;
 
   if (mAnimation && mAnimation->IsRelevant()) {
     nsNodeUtils::AnimationChanged(mAnimation);
   }
 
   if (mTarget) {
-    RefPtr<ComputedStyle> computedStyle = GetTargetComputedStyle();
+    RefPtr<ComputedStyle> computedStyle = GetTargetComputedStyle(Flush::None);
     if (computedStyle) {
       UpdateProperties(computedStyle);
     }
   }
 }
 
 void KeyframeEffect::NotifySpecifiedTimingUpdated() {
   // Use the same document for a pseudo element and its parent element.
@@ -200,17 +201,17 @@ void KeyframeEffect::SetKeyframes(JSCont
                                   JS::Handle<JSObject*> aKeyframes,
                                   ErrorResult& aRv) {
   nsTArray<Keyframe> keyframes = KeyframeUtils::GetKeyframesFromObject(
       aContext, mDocument, aKeyframes, aRv);
   if (aRv.Failed()) {
     return;
   }
 
-  RefPtr<ComputedStyle> style = GetTargetComputedStyle();
+  RefPtr<ComputedStyle> style = GetTargetComputedStyle(Flush::None);
   SetKeyframes(std::move(keyframes), style);
 }
 
 void KeyframeEffect::SetKeyframes(nsTArray<Keyframe>&& aKeyframes,
                                   const ComputedStyle* aStyle) {
   if (KeyframesEqualIgnoringComputedOffsets(aKeyframes, mKeyframes)) {
     return;
   }
@@ -330,16 +331,21 @@ void KeyframeEffect::UpdateProperties(co
   bool baseStylesChanged = false;
   EnsureBaseStyles(aStyle, properties,
                    !propertiesChanged ? &baseStylesChanged : nullptr);
 
   if (!propertiesChanged) {
     if (baseStylesChanged) {
       RequestRestyle(EffectCompositor::RestyleType::Layer);
     }
+    // Check if we need to update the cumulative change hint because we now have
+    // style data.
+    if (mNeedsStyleData && mTarget && mTarget->mElement->HasServoData()) {
+      CalculateCumulativeChangeHint(aStyle);
+    }
     return;
   }
 
   // Preserve the state of the mIsRunningOnCompositor flag.
   nsCSSPropertyIDSet runningOnCompositorProperties;
 
   for (const AnimationProperty& property : mProperties) {
     if (property.mIsRunningOnCompositor) {
@@ -427,16 +433,19 @@ void KeyframeEffect::EnsureBaseStyle(
 
   if (!hasAdditiveValues) {
     return;
   }
 
   if (!aBaseComputedStyle) {
     Element* animatingElement = EffectCompositor::GetElementToRestyle(
         mTarget->mElement, mTarget->mPseudoType);
+    if (!animatingElement) {
+      return;
+    }
     aBaseComputedStyle = aPresContext->StyleSet()->GetBaseContextForElement(
         animatingElement, aComputedStyle);
   }
   RefPtr<RawServoAnimationValue> baseValue =
       Servo_ComputedValues_ExtractAnimationValue(aBaseComputedStyle,
                                                  aProperty.mProperty)
           .Consume();
   mBaseValues.Put(aProperty.mProperty, baseValue);
@@ -757,33 +766,37 @@ void KeyframeEffect::RequestRestyle(
       nsContentUtils::GetContextForContent(mTarget->mElement);
   if (presContext && mAnimation) {
     presContext->EffectCompositor()->RequestRestyle(
         mTarget->mElement, mTarget->mPseudoType, aRestyleType,
         mAnimation->CascadeLevel());
   }
 }
 
-already_AddRefed<ComputedStyle> KeyframeEffect::GetTargetComputedStyle() const {
+already_AddRefed<ComputedStyle> KeyframeEffect::GetTargetComputedStyle(
+    Flush aFlushType) const {
   if (!GetRenderedDocument()) {
     return nullptr;
   }
 
   MOZ_ASSERT(mTarget,
              "Should only have a document when we have a target element");
 
   nsAtom* pseudo =
       mTarget->mPseudoType < CSSPseudoElementType::Count
           ? nsCSSPseudoElements::GetPseudoAtom(mTarget->mPseudoType)
           : nullptr;
 
   OwningAnimationTarget kungfuDeathGrip(mTarget->mElement,
                                         mTarget->mPseudoType);
 
-  return nsComputedDOMStyle::GetComputedStyle(mTarget->mElement, pseudo);
+  return aFlushType == Flush::Style
+             ? nsComputedDOMStyle::GetComputedStyle(mTarget->mElement, pseudo)
+             : nsComputedDOMStyle::GetComputedStyleNoFlush(mTarget->mElement,
+                                                           pseudo);
 }
 
 #ifdef DEBUG
 void DumpAnimationProperties(
     nsTArray<AnimationProperty>& aAnimationProperties) {
   for (auto& p : aAnimationProperties) {
     printf("%s\n", nsCString(nsCSSProps::GetStringValue(p.mProperty)).get());
     for (auto& s : p.mSegments) {
@@ -889,17 +902,17 @@ void KeyframeEffect::SetTarget(
       nsNodeUtils::AnimationRemoved(mAnimation);
     }
   }
 
   mTarget = newTarget;
 
   if (mTarget) {
     UpdateTargetRegistration();
-    RefPtr<ComputedStyle> computedStyle = GetTargetComputedStyle();
+    RefPtr<ComputedStyle> computedStyle = GetTargetComputedStyle(Flush::None);
     if (computedStyle) {
       UpdateProperties(computedStyle);
     }
 
     MaybeUpdateFrameForCompositor();
 
     RequestRestyle(EffectCompositor::RestyleType::Layer);
 
@@ -1021,17 +1034,17 @@ void KeyframeEffect::GetKeyframes(JSCont
     // The following will flush style but that's ok since if you update
     // a variable's computed value, you expect to see that updated value in the
     // result of getKeyframes().
     //
     // If we don't have a target, the following will return null. In that case
     // we might end up returning variables as-is or empty string. That should be
     // acceptable however, since such a case is rare and this is only
     // short-term (and unshipped) behavior until bug 1391537 is fixed.
-    computedStyle = GetTargetComputedStyle();
+    computedStyle = GetTargetComputedStyle(Flush::Style);
   }
 
   for (const Keyframe& keyframe : mKeyframes) {
     // Set up a dictionary object for the explicit members
     BaseComputedKeyframe keyframeDict;
     if (keyframe.mOffset) {
       keyframeDict.mOffset.SetValue(keyframe.mOffset.value());
     }
@@ -1462,34 +1475,41 @@ void KeyframeEffect::SetPerformanceWarni
 already_AddRefed<ComputedStyle>
 KeyframeEffect::CreateComputedStyleForAnimationValue(
     nsCSSPropertyID aProperty, const AnimationValue& aValue,
     nsPresContext* aPresContext, const ComputedStyle* aBaseComputedStyle) {
   MOZ_ASSERT(aBaseComputedStyle,
              "CreateComputedStyleForAnimationValue needs to be called "
              "with a valid ComputedStyle");
 
-  ServoStyleSet* styleSet = aPresContext->StyleSet();
   Element* elementForResolve = EffectCompositor::GetElementToRestyle(
       mTarget->mElement, mTarget->mPseudoType);
-  MOZ_ASSERT(elementForResolve, "The target element shouldn't be null");
+  // The element may be null if, for example, we target a pseudo-element that no
+  // longer exists.
+  if (!elementForResolve) {
+    return nullptr;
+  }
+
+  ServoStyleSet* styleSet = aPresContext->StyleSet();
   return styleSet->ResolveServoStyleByAddingAnimation(
       elementForResolve, aBaseComputedStyle, aValue.mServo);
 }
 
 void KeyframeEffect::CalculateCumulativeChangeHint(
     const ComputedStyle* aComputedStyle) {
   mCumulativeChangeHint = nsChangeHint(0);
+  mNeedsStyleData = false;
 
   nsPresContext* presContext =
       nsContentUtils::GetContextForContent(mTarget->mElement);
   if (!presContext) {
     // Change hints make no sense if we're not rendered.
     //
     // Actually, we cannot even post them anywhere.
+    mNeedsStyleData = true;
     return;
   }
 
   for (const AnimationProperty& property : mProperties) {
     // For opacity property we don't produce any change hints that are not
     // included in nsChangeHint_Hints_CanIgnoreIfNotVisible so we can throttle
     // opacity animations regardless of the change they produce.  This
     // optimization is particularly important since it allows us to throttle
@@ -1520,23 +1540,25 @@ void KeyframeEffect::CalculateCumulative
             nsChangeHint_UpdateTransformLayer;
         continue;
       }
 
       RefPtr<ComputedStyle> fromContext = CreateComputedStyleForAnimationValue(
           property.mProperty, segment.mFromValue, presContext, aComputedStyle);
       if (!fromContext) {
         mCumulativeChangeHint = ~nsChangeHint_Hints_CanIgnoreIfNotVisible;
+        mNeedsStyleData = true;
         return;
       }
 
       RefPtr<ComputedStyle> toContext = CreateComputedStyleForAnimationValue(
           property.mProperty, segment.mToValue, presContext, aComputedStyle);
       if (!toContext) {
         mCumulativeChangeHint = ~nsChangeHint_Hints_CanIgnoreIfNotVisible;
+        mNeedsStyleData = true;
         return;
       }
 
       uint32_t equalStructs = 0;
       nsChangeHint changeHint =
           fromContext->CalcStyleDifference(*toContext, &equalStructs);
 
       mCumulativeChangeHint |= changeHint;
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -356,17 +356,22 @@ class KeyframeEffect : public AnimationE
   // have changed, or when the target frame might have changed.
   void MaybeUpdateFrameForCompositor();
 
   // Looks up the ComputedStyle associated with the target element, if any.
   // We need to be careful to *not* call this when we are updating the style
   // context. That's because calling GetComputedStyle when we are in the process
   // of building a ComputedStyle may trigger various forms of infinite
   // recursion.
-  already_AddRefed<ComputedStyle> GetTargetComputedStyle() const;
+  enum class Flush {
+    Style,
+    None,
+  };
+  already_AddRefed<ComputedStyle> GetTargetComputedStyle(
+      Flush aFlushType) const;
 
   // A wrapper for marking cascade update according to the current
   // target and its effectSet.
   void MarkCascadeNeedsUpdate();
 
   void EnsureBaseStyles(const ComputedStyle* aComputedValues,
                         const nsTArray<AnimationProperty>& aProperties,
                         bool* aBaseStylesChanged);
@@ -399,16 +404,22 @@ class KeyframeEffect : public AnimationE
   // we need to re-evaluate the cascade of animations when that changes.
   bool mInEffectOnLastAnimationTimingUpdate = false;
 
   // True if this effect is in the EffectSet for its target element. This is
   // used as an optimization to avoid unnecessary hashmap lookups on the
   // EffectSet.
   bool mInEffectSet = false;
 
+  // True if the last time we tried to update the cumulative change hint we
+  // didn't have style data to do so. We set this flag so that the next time
+  // our style context changes we know to update the cumulative change hint even
+  // if our properties haven't changed.
+  bool mNeedsStyleData = false;
+
   // The non-animated values for properties in this effect that contain at
   // least one animation value that is composited with the underlying value
   // (i.e. it uses the additive or accumulate composite mode).
   using BaseValuesHashmap =
       nsRefPtrHashtable<nsUint32HashKey, RawServoAnimationValue>;
   BaseValuesHashmap mBaseValues;
 
  private:
--- a/dom/animation/KeyframeUtils.cpp
+++ b/dom/animation/KeyframeUtils.cpp
@@ -128,22 +128,22 @@ class ComputedOffsetComparator {
 
 // ------------------------------------------------------------------
 //
 // Internal helper method declarations
 //
 // ------------------------------------------------------------------
 
 static void GetKeyframeListFromKeyframeSequence(JSContext* aCx,
-                                                Document* aDocument,
+                                                dom::Document* aDocument,
                                                 JS::ForOfIterator& aIterator,
                                                 nsTArray<Keyframe>& aResult,
                                                 ErrorResult& aRv);
 
-static bool ConvertKeyframeSequence(JSContext* aCx, Document* aDocument,
+static bool ConvertKeyframeSequence(JSContext* aCx, dom::Document* aDocument,
                                     JS::ForOfIterator& aIterator,
                                     nsTArray<Keyframe>& aResult);
 
 static bool GetPropertyValuesPairs(JSContext* aCx,
                                    JS::Handle<JSObject*> aObject,
                                    ListAllowance aAllowLists,
                                    nsTArray<PropertyValuesPair>& aResult);
 
@@ -152,17 +152,17 @@ static bool AppendStringOrStringSequence
                                                 ListAllowance aAllowLists,
                                                 nsTArray<nsString>& aValues);
 
 static bool AppendValueAsString(JSContext* aCx, nsTArray<nsString>& aValues,
                                 JS::Handle<JS::Value> aValue);
 
 static Maybe<PropertyValuePair> MakePropertyValuePair(
     nsCSSPropertyID aProperty, const nsAString& aStringValue,
-    Document* aDocument);
+    dom::Document* aDocument);
 
 static bool HasValidOffsets(const nsTArray<Keyframe>& aKeyframes);
 
 #ifdef DEBUG
 static void MarkAsComputeValuesFailureKey(PropertyValuePair& aPair);
 
 #endif
 
@@ -170,32 +170,32 @@ static nsTArray<ComputedKeyframeValues> 
     const nsTArray<Keyframe>& aKeyframes, dom::Element* aElement,
     const ComputedStyle* aComputedValues);
 
 static void BuildSegmentsFromValueEntries(
     nsTArray<KeyframeValueEntry>& aEntries,
     nsTArray<AnimationProperty>& aResult);
 
 static void GetKeyframeListFromPropertyIndexedKeyframe(
-    JSContext* aCx, Document* aDocument, JS::Handle<JS::Value> aValue,
+    JSContext* aCx, dom::Document* aDocument, JS::Handle<JS::Value> aValue,
     nsTArray<Keyframe>& aResult, ErrorResult& aRv);
 
 static bool HasImplicitKeyframeValues(const nsTArray<Keyframe>& aKeyframes,
-                                      Document* aDocument);
+                                      dom::Document* aDocument);
 
 static void DistributeRange(const Range<Keyframe>& aRange);
 
 // ------------------------------------------------------------------
 //
 // Public API
 //
 // ------------------------------------------------------------------
 
 /* static */ nsTArray<Keyframe> KeyframeUtils::GetKeyframesFromObject(
-    JSContext* aCx, Document* aDocument, JS::Handle<JSObject*> aFrames,
+    JSContext* aCx, dom::Document* aDocument, JS::Handle<JSObject*> aFrames,
     ErrorResult& aRv) {
   MOZ_ASSERT(!aRv.Failed());
 
   nsTArray<Keyframe> keyframes;
 
   if (!aFrames) {
     // The argument was explicitly null meaning no keyframes.
     return keyframes;
@@ -219,17 +219,17 @@ static void DistributeRange(const Range<
   }
 
   if (aRv.Failed()) {
     MOZ_ASSERT(keyframes.IsEmpty(),
                "Should not set any keyframes when there is an error");
     return keyframes;
   }
 
-  if (!Document::AreWebAnimationsImplicitKeyframesEnabled(aCx, nullptr) &&
+  if (!dom::Document::AreWebAnimationsImplicitKeyframesEnabled(aCx, nullptr) &&
       HasImplicitKeyframeValues(keyframes, aDocument)) {
     keyframes.Clear();
     aRv.Throw(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR);
   }
 
   return keyframes;
 }
 
@@ -335,17 +335,17 @@ KeyframeUtils::GetAnimationPropertiesFro
  * @param aDocument The document to use when parsing CSS properties.
  * @param aIterator An already-initialized ForOfIterator for the JS
  *   object to iterate over as a sequence.
  * @param aResult The array into which the resulting Keyframe objects will be
  *   appended.
  * @param aRv Out param to store any errors thrown by this function.
  */
 static void GetKeyframeListFromKeyframeSequence(JSContext* aCx,
-                                                Document* aDocument,
+                                                dom::Document* aDocument,
                                                 JS::ForOfIterator& aIterator,
                                                 nsTArray<Keyframe>& aResult,
                                                 ErrorResult& aRv) {
   MOZ_ASSERT(!aRv.Failed());
   MOZ_ASSERT(aResult.IsEmpty());
 
   // Convert the object in aIterator to a sequence of keyframes producing
   // an array of Keyframe objects.
@@ -370,17 +370,17 @@ static void GetKeyframeListFromKeyframeS
   }
 }
 
 /**
  * Converts a JS object wrapped by the given JS::ForIfIterator to an
  * IDL sequence<Keyframe> and stores the resulting Keyframe objects in
  * aResult.
  */
-static bool ConvertKeyframeSequence(JSContext* aCx, Document* aDocument,
+static bool ConvertKeyframeSequence(JSContext* aCx, dom::Document* aDocument,
                                     JS::ForOfIterator& aIterator,
                                     nsTArray<Keyframe>& aResult) {
   JS::Rooted<JS::Value> value(aCx);
   ErrorResult parseEasingResult;
 
   for (;;) {
     bool done;
     if (!aIterator.next(&value, &done)) {
@@ -584,17 +584,17 @@ static bool AppendStringOrStringSequence
 static bool AppendValueAsString(JSContext* aCx, nsTArray<nsString>& aValues,
                                 JS::Handle<JS::Value> aValue) {
   return ConvertJSValueToString(aCx, aValue, dom::eStringify, dom::eStringify,
                                 *aValues.AppendElement());
 }
 
 static void ReportInvalidPropertyValueToConsole(
     nsCSSPropertyID aProperty, const nsAString& aInvalidPropertyValue,
-    Document* aDoc) {
+    dom::Document* aDoc) {
   const nsString& invalidValue = PromiseFlatString(aInvalidPropertyValue);
   const NS_ConvertASCIItoUTF16 propertyName(
       nsCSSProps::GetStringValue(aProperty));
   const char16_t* params[] = {invalidValue.get(), propertyName.get()};
   nsContentUtils::ReportToConsole(
       nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Animation"), aDoc,
       nsContentUtils::eDOM_PROPERTIES, "InvalidKeyframePropertyValue", params,
       ArrayLength(params));
@@ -607,17 +607,17 @@ static void ReportInvalidPropertyValueTo
  * @param aProperty The CSS property.
  * @param aStringValue The property value to parse.
  * @param aDocument The document to use when parsing.
  * @return The constructed PropertyValuePair, or Nothing() if |aStringValue| is
  *   an invalid property value.
  */
 static Maybe<PropertyValuePair> MakePropertyValuePair(
     nsCSSPropertyID aProperty, const nsAString& aStringValue,
-    Document* aDocument) {
+    dom::Document* aDocument) {
   MOZ_ASSERT(aDocument);
   Maybe<PropertyValuePair> result;
 
   ServoCSSParser::ParsingEnvironment env =
       ServoCSSParser::GetParsingEnvironment(aDocument);
   RefPtr<RawServoDeclarationBlock> servoDeclarationBlock =
       ServoCSSParser::ParseProperty(aProperty, aStringValue, env);
 
@@ -937,17 +937,17 @@ static void BuildSegmentsFromValueEntrie
  * @param aCx The JSContext for |aValue|.
  * @param aDocument The document to use when parsing CSS properties.
  * @param aValue The JS object.
  * @param aResult The array into which the resulting AnimationProperty
  *   objects will be appended.
  * @param aRv Out param to store any errors thrown by this function.
  */
 static void GetKeyframeListFromPropertyIndexedKeyframe(
-    JSContext* aCx, Document* aDocument, JS::Handle<JS::Value> aValue,
+    JSContext* aCx, dom::Document* aDocument, JS::Handle<JS::Value> aValue,
     nsTArray<Keyframe>& aResult, ErrorResult& aRv) {
   MOZ_ASSERT(aValue.isObject());
   MOZ_ASSERT(aResult.IsEmpty());
   MOZ_ASSERT(!aRv.Failed());
 
   // Convert the object to a property-indexed keyframe dictionary to
   // get its explicit dictionary members.
   dom::binding_detail::FastBasePropertyIndexedKeyframe keyframeDict;
@@ -1139,17 +1139,17 @@ static void GetKeyframeListFromPropertyI
  * offsets. The check is not entirely accurate but should detect most common
  * cases.
  *
  * @param aKeyframes The set of keyframes to analyze.
  * @param aDocument The document to use when parsing keyframes so we can
  *   try to detect where we have an invalid value at 0%/100%.
  */
 static bool HasImplicitKeyframeValues(const nsTArray<Keyframe>& aKeyframes,
-                                      Document* aDocument) {
+                                      dom::Document* aDocument) {
   // We are looking to see if that every property referenced in |aKeyframes|
   // has a valid property at offset 0.0 and 1.0. The check as to whether a
   // property is valid or not, however, is not precise. We only check if the
   // property can be parsed, NOT whether it can also be converted to a
   // StyleAnimationValue since doing that requires a target element bound to
   // a document which we might not always have at the point where we want to
   // perform this check.
   //
--- a/dom/animation/test/chrome/test_animation_properties.html
+++ b/dom/animation/test/chrome/test_animation_properties.html
@@ -813,16 +813,21 @@ SpecialPowers.pushPrefEnv(
       ["dom.animations-api.implicit-keyframes.enabled", true],
     ],
   },
   function() {
     gTests.forEach(function(subtest) {
       test(function(t) {
         var div = addDiv(t);
         var animation = div.animate(subtest.frames, 100 * MS_PER_SEC);
+        // Flush styles since getProperties currently does not. Rather, it
+        // returns the actual properties in use at the current time.
+        // However, we want to test what these properties will look like
+        // after the next restyle.
+        getComputedStyle(div).opacity;
         assert_properties_equal(
           animation.effect.getProperties(),
           subtest.expected
         );
       }, subtest.desc);
     });
 
     done();
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/crashtests/1524480-1.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset=utf-8>
+<style>
+div {
+  display: none;
+  width: 100px;
+  height: 100px;
+  background: blue;
+}
+</style>
+<div id=div></div>
+<script>
+async function test() {
+  const animation = div.animate({ transform: ['none', 'none'] }, 1000);
+  animation.cancel();
+
+  await waitForFrame();
+
+  div.style.display = 'block';
+
+  await waitForFrame();
+  await waitForFrame();
+
+  animation.play();
+  await animation.finished;
+
+  document.documentElement.className = "";
+}
+
+function waitForFrame() {
+  return new Promise(resolve => requestAnimationFrame(resolve));
+}
+
+test();
+</script>
+</html>
--- a/dom/animation/test/crashtests/crashtests.list
+++ b/dom/animation/test/crashtests/crashtests.list
@@ -37,8 +37,9 @@ pref(dom.animations-api.core.enabled,tru
 pref(dom.animations-api.implicit-keyframes.enabled,true) load 1373712-1.html
 pref(dom.animations-api.implicit-keyframes.enabled,true) load 1379606-1.html
 load 1393605-1.html
 load 1400022-1.html
 pref(dom.animations-api.core.enabled,true) pref(dom.animations-api.implicit-keyframes.enabled,true) load 1401809.html
 pref(dom.animations-api.core.enabled,true) pref(dom.animations-api.timelines.enabled,true) pref(dom.animations-api.implicit-keyframes.enabled,true) load 1411318-1.html
 pref(dom.animations-api.implicit-keyframes.enabled,true) load 1468294-1.html
 pref(dom.animations-api.implicit-keyframes.enabled,true) load 1467277-1.html
+load 1524480-1.html
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -49,16 +49,17 @@ skip-if = (toolkit == 'android' && debug
 [mozilla/test_restyles.html]
 [mozilla/test_restyling_xhr_doc.html]
 [mozilla/test_set_easing.html]
 [mozilla/test_style_after_finished_on_compositor.html]
 [mozilla/test_transform_limits.html]
 [mozilla/test_transition_finish_on_compositor.html]
 skip-if = toolkit == 'android'
 [mozilla/test_underlying_discrete_value.html]
+[mozilla/test_unstyled.html]
 [mozilla/test_event_listener_leaks.html]
 [style/test_animation-seeking-with-current-time.html]
 [style/test_animation-seeking-with-start-time.html]
 [style/test_animation-setting-effect.html]
 [style/test_composite.html]
 [style/test_interpolation-from-interpolatematrix-to-none.html]
 [style/test_missing-keyframe.html]
 [style/test_missing-keyframe-on-compositor.html]
--- a/dom/animation/test/mozilla/file_restyles.html
+++ b/dom/animation/test/mozilla/file_restyles.html
@@ -1864,12 +1864,43 @@ waitForAllPaints(() => {
       ok(!SpecialPowers.wrap(animation).isRunningOnCompositor);
     }
     ok(!SpecialPowers.DOMWindowUtils.needsFlush(FLUSH_LAYOUT),
        'No further flush layout needed');
 
     await ensureElementRemoval(div);
   });
 
+  add_task(async function restyling_on_create_animation() {
+    const div = addDiv();
+    const docShell = getDocShellForObservingRestylesForWindow(window);
+
+    const animationA = div.animate(
+      { transform: ['none', 'rotate(360deg)'] },
+      100 * MS_PER_SEC
+    );
+    const animationB = div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
+    const animationC = div.animate(
+      { color: ['blue', 'green'] },
+      100 * MS_PER_SEC
+    );
+    const animationD = div.animate(
+      { width: ['100px', '200px'] },
+      100 * MS_PER_SEC
+    );
+    const animationE = div.animate(
+      { height: ['100px', '200px'] },
+      100 * MS_PER_SEC
+    );
+
+    const markers = docShell
+      .popProfileTimelineMarkers()
+      .filter(marker => marker.name === 'Styles' && !marker.isAnimationOnly);
+    docShell.recordProfileTimelineMarkers = false;
+
+    is(markers.length, 0, 'Creating animations should not flush styles');
+
+    await ensureElementRemoval(div);
+  });
 });
 
 </script>
 </body>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/mozilla/test_unstyled.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../testcommon.js"></script>
+<style>
+div.pseudo::before {
+  animation: animation 1s;
+  content: 'content';
+}
+@keyframes animation {
+  to { opacity: 0 }
+}
+</style>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+// Tests for cases where we may not have style data for an element
+
+promise_test(async t => {
+  // Get a CSSPseudoElement
+  const div = addDiv(t, { class: 'pseudo' });
+  const cssAnim = document.getAnimations()[0];
+  const pseudoElem = cssAnim.effect.target;
+
+  // Drop pseudo from styles and flush styles
+  div.classList.remove('pseudo');
+  getComputedStyle(div, '::before').content;
+
+  // Try animating the pseudo's content attribute
+  const contentAnim = pseudoElem.animate(
+    { content: ['none', '"content"'] },
+    { duration: 100 * MS_PER_SEC, fill: 'both' }
+  );
+
+  // Check that the initial value is as expected
+  await contentAnim.ready;
+  assert_equals(getComputedStyle(div, '::before').content, 'none');
+
+  contentAnim.finish();
+
+  // Animating an obsolete pseudo element should NOT cause the pseudo element
+  // to be re-generated. That behavior might change in which case this test
+  // will need to be updated. The most important part of this test, however,
+  // is simply checking that nothing explodes if we try to animate such a
+  // pseudo element.
+
+  assert_equals(getComputedStyle(div, '::before').content, 'none');
+}, 'Animation on an obsolete pseudo element produces expected results');
+
+</script>
+</body>
--- a/dom/animation/test/style/test_missing-keyframe-on-compositor.html
+++ b/dom/animation/test/style/test_missing-keyframe-on-compositor.html
@@ -71,16 +71,18 @@ promise_test(t => {
 
 promise_test(t => {
   var div;
   return useTestRefreshMode(t).then(() => {
     div = addDiv(t, { style: 'opacity: 0.1; transition: opacity 100s linear' });
     getComputedStyle(div).opacity;
 
     div.style.opacity = '0.5';
+    getComputedStyle(div).opacity;
+
     div.animate({ opacity: 1 }, 100 * MS_PER_SEC);
 
     return waitForPaintsFlushed();
   }).then(() => {
     var opacity =
       SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'opacity');
     assert_equals(opacity, '0.1',
                   'The initial opacity value should be the initial value of ' +
@@ -129,16 +131,18 @@ promise_test(t => {
 
 promise_test(t => {
   var div;
   return useTestRefreshMode(t).then(() => {
     div = addDiv(t, { style: 'opacity: 0; transition: opacity 100s linear' });
     getComputedStyle(div).opacity;
 
     div.style.opacity = '0.5';
+    getComputedStyle(div).opacity;
+
     div.animate([{ offset: 0, opacity: 1 }], 100 * MS_PER_SEC);
 
     return waitForPaintsFlushed();
   }).then(() => {
     SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC);
 
     var opacity =
       SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'opacity');
@@ -269,16 +273,18 @@ promise_test(t => {
 promise_test(t => {
   var div;
   return useTestRefreshMode(t).then(() => {
     div = addDiv(t, { style: 'transform: translateX(100px);' +
                              'transition: transform 100s linear' });
     getComputedStyle(div).transform;
 
     div.style.transform = 'translateX(200px)';
+    getComputedStyle(div).transform;
+
     div.animate({ transform: 'translateX(400px)' }, 100 * MS_PER_SEC);
 
     return waitForPaintsFlushed();
   }).then(() => {
     var transform =
       SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
     assert_matrix_equals(transform, 'matrix(1, 0, 0, 1, 100, 0)',
       'The initial transform value should be the initial value of the ' +
@@ -329,16 +335,18 @@ promise_test(t => {
 promise_test(t => {
   var div;
   return useTestRefreshMode(t).then(() => {
     div = addDiv(t, { style: 'transform: translateX(100px);' +
                              'transition: transform 100s linear' });
     getComputedStyle(div).transform;
 
     div.style.transform = 'translateX(200px)';
+    getComputedStyle(div).transform;
+
     div.animate([{ offset: 0, transform: 'translateX(300px)' }],
                 100 * MS_PER_SEC);
 
     return waitForPaintsFlushed();
   }).then(() => {
     SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC);
 
     var transform =
--- a/dom/base/Timeout.cpp
+++ b/dom/base/Timeout.cpp
@@ -18,17 +18,18 @@ Timeout::Timeout()
 #ifdef DEBUG
       mFiringIndex(-1),
 #endif
       mPopupState(PopupBlocker::openAllowed),
       mReason(Reason::eTimeoutOrInterval),
       mNestingLevel(0),
       mCleared(false),
       mRunning(false),
-      mIsInterval(false) {}
+      mIsInterval(false) {
+}
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Timeout)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Timeout)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptHandler)
   if (tmp->isInList()) {
     tmp->remove();
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -444,17 +444,16 @@ nsresult nsFrameLoader::ReallyStartLoadi
         if (iframeReferrerPolicy != net::RP_Unset) {
           referrerPolicy = iframeReferrerPolicy;
         }
       }
       loadState->SetReferrerInfo(new ReferrerInfo(referrer, referrerPolicy));
     }
   }
 
-
   // Default flags:
   int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE;
 
   // Flags for browser frame:
   if (OwnerIsMozBrowserFrame()) {
     flags = nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
             nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
   }
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -3928,17 +3928,17 @@ Nullable<WindowProxyHolder> nsGlobalWind
   }
   return WindowProxyHolder(topBC);
 }
 
 already_AddRefed<BrowsingContext> nsGlobalWindowOuter::GetChildWindow(
     const nsAString& aName) {
   NS_ENSURE_TRUE(mBrowsingContext, nullptr);
 
-  return mBrowsingContext->FindChildWithName(aName);
+  return do_AddRef(mBrowsingContext->FindChildWithName(aName));
 }
 
 bool nsGlobalWindowOuter::DispatchCustomEvent(const nsAString& aEventName) {
   bool defaultActionEnabled = true;
   nsContentUtils::DispatchTrustedEvent(mDoc, ToSupports(this), aEventName,
                                        CanBubble::eYes, Cancelable::eYes,
                                        &defaultActionEnabled);
 
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -2727,23 +2727,28 @@ static void ExtractRectFromOffset(nsIFra
 static nsTextFrame* GetTextFrameForContent(nsIContent* aContent,
                                            bool aFlushLayout) {
   Document* doc = aContent->OwnerDoc();
   nsIPresShell* presShell = doc->GetShell();
   if (!presShell) {
     return nullptr;
   }
 
-  const bool frameWillBeUnsuppressed =
-      presShell->FrameConstructor()->EnsureFrameForTextNodeIsCreatedAfterFlush(
-          static_cast<CharacterData*>(aContent));
+  // Try to un-suppress whitespace if needed, but only if we'll be able to flush
+  // to immediately see the results of the un-suppression. If we can't flush
+  // here, then calling EnsureFrameForTextNodeIsCreatedAfterFlush would be
+  // pointless anyway.
   if (aFlushLayout) {
-    doc->FlushPendingNotifications(FlushType::Layout);
-  } else if (frameWillBeUnsuppressed) {
-    doc->FlushPendingNotifications(FlushType::Frames);
+    const bool frameWillBeUnsuppressed =
+        presShell->FrameConstructor()
+            ->EnsureFrameForTextNodeIsCreatedAfterFlush(
+                static_cast<CharacterData*>(aContent));
+    if (frameWillBeUnsuppressed) {
+      doc->FlushPendingNotifications(FlushType::Layout);
+    }
   }
 
   nsIFrame* frame = aContent->GetPrimaryFrame();
   if (!frame || !frame->IsTextFrame()) {
     return nullptr;
   }
   return static_cast<nsTextFrame*>(frame);
 }
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -2586,17 +2586,17 @@ bool CanvasRenderingContext2D::ParseFilt
   }
 
   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
   if (!presShell) {
     aError.Throw(NS_ERROR_FAILURE);
     return false;
   }
 
-  nsAutoString usedFont; // unused
+  nsAutoString usedFont;  // unused
 
   RefPtr<ComputedStyle> parentStyle = GetFontStyleForServo(
       mCanvasElement, GetFont(), presShell, usedFont, aError);
   if (!parentStyle) {
     return false;
   }
 
   RefPtr<ComputedStyle> style =
--- a/dom/canvas/WebGLExtensionDrawBuffers.cpp
+++ b/dom/canvas/WebGLExtensionDrawBuffers.cpp
@@ -34,17 +34,18 @@ void WebGLExtensionDrawBuffers::DrawBuff
   mContext->DrawBuffers(buffers);
 }
 
 bool WebGLExtensionDrawBuffers::IsSupported(const WebGLContext* webgl) {
   if (webgl->IsWebGL2()) return false;
 
   gl::GLContext* gl = webgl->GL();
   if (gl->IsGLES() && gl->Version() >= 300) {
-    // ANGLE's shader translator can't translate ESSL1 exts to ESSL3. (bug 1524804)
+    // ANGLE's shader translator can't translate ESSL1 exts to ESSL3. (bug
+    // 1524804)
     return false;
   }
   return gl->IsSupported(gl::GLFeature::draw_buffers);
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionDrawBuffers, WEBGL_draw_buffers)
 
 }  // namespace mozilla
--- a/dom/canvas/WebGLExtensionFragDepth.cpp
+++ b/dom/canvas/WebGLExtensionFragDepth.cpp
@@ -18,17 +18,18 @@ WebGLExtensionFragDepth::WebGLExtensionF
 
 WebGLExtensionFragDepth::~WebGLExtensionFragDepth() {}
 
 bool WebGLExtensionFragDepth::IsSupported(const WebGLContext* webgl) {
   if (webgl->IsWebGL2()) return false;
 
   gl::GLContext* gl = webgl->GL();
   if (gl->IsGLES() && gl->Version() >= 300) {
-    // ANGLE's shader translator can't translate ESSL1 exts to ESSL3. (bug 1524804)
+    // ANGLE's shader translator can't translate ESSL1 exts to ESSL3. (bug
+    // 1524804)
     return false;
   }
   return gl->IsSupported(gl::GLFeature::frag_depth);
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionFragDepth, EXT_frag_depth)
 
 }  // namespace mozilla
--- a/dom/chrome-webidl/BrowsingContext.webidl
+++ b/dom/chrome-webidl/BrowsingContext.webidl
@@ -4,16 +4,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 interface nsIDocShell;
 
 [Exposed=Window, ChromeOnly]
 interface BrowsingContext {
   static BrowsingContext? get(unsigned long long aId);
 
+  BrowsingContext? findChildWithName(DOMString name);
+  BrowsingContext? findWithName(DOMString name);
+
+  readonly attribute DOMString name;
+
   readonly attribute BrowsingContext? parent;
 
   sequence<BrowsingContext> getChildren();
 
   readonly attribute nsIDocShell? docShell;
 
   readonly attribute unsigned long long id;
 
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -3684,17 +3684,17 @@ static bool ShouldBlockCustomCursor(nsPr
   }
 
   // We don't want to deal with iframes, just let them do their thing unless
   // they intersect UI.
   //
   // TODO(emilio, bug 1525561): In a fission world, we should have a better way
   // to find the event coordinates relative to the content area.
   nsPresContext* topLevel =
-    aPresContext->GetToplevelContentDocumentPresContext();
+      aPresContext->GetToplevelContentDocumentPresContext();
   if (!topLevel) {
     return false;
   }
 
   nsPoint point = nsLayoutUtils::GetEventCoordinatesRelativeTo(
       aEvent, topLevel->PresShell()->GetRootFrame());
 
   nsSize size(CSSPixel::ToAppUnits(width), CSSPixel::ToAppUnits(height));
--- a/dom/html/HTMLLabelElement.cpp
+++ b/dom/html/HTMLLabelElement.cpp
@@ -68,18 +68,18 @@ nsresult HTMLLabelElement::PostHandleEve
        aVisitor.mEvent->mMessage != eMouseDown) ||
       aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault ||
       !aVisitor.mPresContext ||
       // Don't handle the event if it's already been handled by another label
       aVisitor.mEvent->mFlags.mMultipleActionsPrevented) {
     return NS_OK;
   }
 
-  nsCOMPtr<Element> target = do_QueryInterface(
-      aVisitor.mEvent->GetOriginalDOMEventTarget());
+  nsCOMPtr<Element> target =
+      do_QueryInterface(aVisitor.mEvent->GetOriginalDOMEventTarget());
   if (nsContentUtils::IsInInteractiveHTMLContent(target, this)) {
     return NS_OK;
   }
 
   // Strong ref because event dispatch is going to happen.
   RefPtr<Element> content = GetLabeledElement();
 
   if (content) {
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -34,16 +34,17 @@
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Components.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/DataStorage.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/BrowsingContextGroup.h"
 #include "mozilla/dom/CanonicalBrowsingContext.h"
 #include "mozilla/dom/ClientManager.h"
 #include "mozilla/dom/ClientOpenWindowOpActors.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileCreatorHelper.h"
 #include "mozilla/dom/FileSystemSecurity.h"
@@ -1738,17 +1739,24 @@ void ContentParent::ActorDestroy(ActorDe
   }
 
   mBlobURLs.Clear();
 
 #if defined(XP_WIN32) && defined(ACCESSIBILITY)
   a11y::AccessibleWrap::ReleaseContentProcessIdFor(ChildID());
 #endif
 
-  CanonicalBrowsingContext::CleanupContexts(ChildID());
+  nsTHashtable<nsRefPtrHashKey<BrowsingContextGroup>> groups;
+  mGroups.SwapElements(groups);
+
+  for (auto iter = groups.Iter(); !iter.Done(); iter.Next()) {
+    iter.Get()->GetKey()->Unsubscribe(this);
+  }
+
+  MOZ_DIAGNOSTIC_ASSERT(mGroups.IsEmpty());
 }
 
 bool ContentParent::TryToRecycle() {
   // This life time check should be replaced by a memory health check (memory
   // usage + fragmentation).
   const double kMaxLifeSpan = 5;
   if (mShutdownPending || mCalledKillHard || !IsAlive() ||
       !mRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE) ||
@@ -5815,16 +5823,28 @@ mozilla::ipc::IPCResult ContentParent::R
   if (!BuildClonedMessageDataForParent(cp, messageFromChild, message)) {
     // FIXME Logging?
     return IPC_OK();
   }
   Unused << cp->SendWindowPostMessage(aContext, message, aData);
   return IPC_OK();
 }
 
+void ContentParent::OnBrowsingContextGroupSubscribe(
+    BrowsingContextGroup* aGroup) {
+  MOZ_DIAGNOSTIC_ASSERT(aGroup);
+  mGroups.PutEntry(aGroup);
+}
+
+void ContentParent::OnBrowsingContextGroupUnsubscribe(
+    BrowsingContextGroup* aGroup) {
+  MOZ_DIAGNOSTIC_ASSERT(aGroup);
+  mGroups.RemoveEntry(aGroup);
+}
+
 }  // namespace dom
 }  // namespace mozilla
 
 NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
 
 NS_IMETHODIMP
 ParentIdleListener::Observe(nsISupports*, const char* aTopic,
                             const char16_t* aData) {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -91,16 +91,17 @@ class PJavaScriptParent;
 }  // namespace jsipc
 
 namespace layers {
 struct TextureFactoryIdentifier;
 }  // namespace layers
 
 namespace dom {
 
+class BrowsingContextGroup;
 class Element;
 class TabParent;
 class ClonedMessageData;
 class MemoryReport;
 class TabContext;
 class ContentBridgeParent;
 class GetFilesHelper;
 class MemoryReportRequestHost;
@@ -117,16 +118,17 @@ class ContentParent final : public PCont
                             public mozilla::MemoryReportingProcess {
   typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost;
   typedef mozilla::ipc::OptionalURIParams OptionalURIParams;
   typedef mozilla::ipc::PFileDescriptorSetParent PFileDescriptorSetParent;
   typedef mozilla::ipc::TestShellParent TestShellParent;
   typedef mozilla::ipc::URIParams URIParams;
   typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
   typedef mozilla::dom::ClonedMessageData ClonedMessageData;
+  typedef mozilla::dom::BrowsingContextGroup BrowsingContextGroup;
 
   friend class mozilla::PreallocatedProcessManagerImpl;
   friend class PContentParent;
 #ifdef FUZZING
   friend class mozilla::ipc::ProtocolFuzzerHelper;
 #endif
 
  public:
@@ -1186,16 +1188,19 @@ class ContentParent final : public PCont
   bool CanCommunicateWith(ContentParentId aOtherProcess);
 
   nsresult SaveRecording(nsIFile* aFile, bool* aRetval);
 
   bool IsRecordingOrReplaying() const {
     return mRecordReplayState != eNotRecordingOrReplaying;
   }
 
+  void OnBrowsingContextGroupSubscribe(BrowsingContextGroup* aGroup);
+  void OnBrowsingContextGroupUnsubscribe(BrowsingContextGroup* aGroup);
+
  private:
   // Released in ActorDestroy; deliberately not exposed to the CC.
   RefPtr<ContentParent> mSelfRef;
 
   // If you add strong pointers to cycle collected objects here, be sure to
   // release these objects in ShutDownProcess.  See the comment there for more
   // details.
 
@@ -1321,16 +1326,18 @@ class ContentParent final : public PCont
   static nsDataHashtable<nsUint64HashKey, TabParent*> sNextTabParents;
 
 #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
   // When set to true, indicates that content processes should
   // initialize their sandbox during startup instead of waiting
   // for the SetProcessSandbox IPDL message.
   static bool sEarlySandboxInit;
 #endif
+
+  nsTHashtable<nsRefPtrHashKey<BrowsingContextGroup>> mGroups;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 class ParentIdleListener : public nsIObserver {
   friend class mozilla::dom::ContentParent;
 
--- a/dom/ipc/WindowGlobalChild.h
+++ b/dom/ipc/WindowGlobalChild.h
@@ -15,17 +15,16 @@ class nsGlobalWindowInner;
 class nsDocShell;
 
 namespace mozilla {
 namespace dom {
 
 class BrowsingContext;
 class WindowGlobalParent;
 class JSWindowActorChild;
-class TabChild;
 
 /**
  * Actor for a single nsGlobalWindowInner. This actor is used to communicate
  * information to the parent process asynchronously.
  */
 class WindowGlobalChild : public nsWrapperCache, public PWindowGlobalChild {
   friend class PWindowGlobalChild;
 
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -486,24 +486,23 @@ nsJSChannel::GetURI(nsIURI** aURI) { ret
 
 NS_IMETHODIMP
 nsJSChannel::Open(nsIInputStream** aStream) {
   nsCOMPtr<nsIStreamListener> listener;
   nsresult rv =
       nsContentSecurityManager::doContentSecurityCheck(this, listener);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = mIOThunk->EvaluateScript(
-      mStreamChannel, mPopupState, mExecutionPolicy, mOriginalInnerWindow);
+  rv = mIOThunk->EvaluateScript(mStreamChannel, mPopupState, mExecutionPolicy,
+                                mOriginalInnerWindow);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return mStreamChannel->Open(aStream);
 }
 
-
 NS_IMETHODIMP
 nsJSChannel::AsyncOpen(nsIStreamListener* aListener) {
   NS_ENSURE_ARG(aListener);
 
   nsCOMPtr<nsIStreamListener> listener = aListener;
   nsresult rv =
       nsContentSecurityManager::doContentSecurityCheck(this, listener);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -610,18 +609,17 @@ nsJSChannel::AsyncOpen(nsIStreamListener
     // it asynchronously.
     method = &nsJSChannel::NotifyListener;
     name = "nsJSChannel::NotifyListener";
   }
 
   nsCOMPtr<nsIRunnable> runnable =
       mozilla::NewRunnableMethod(name, this, method);
   nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(mOriginalInnerWindow);
-  rv =
-      window->Dispatch(mozilla::TaskCategory::Other, runnable.forget());
+  rv = window->Dispatch(mozilla::TaskCategory::Other, runnable.forget());
 
   if (NS_FAILED(rv)) {
     loadGroup->RemoveRequest(this, nullptr, rv);
     mIsActive = false;
     CleanupStrongRefs();
   }
   return rv;
 }
--- a/dom/localstorage/ActorsParent.cpp
+++ b/dom/localstorage/ActorsParent.cpp
@@ -3132,18 +3132,18 @@ PBackgroundLSRequestParent* AllocPBackgr
     case LSRequestParams::TLSRequestPrepareDatastoreParams: {
       Maybe<ContentParentId> contentParentId;
 
       uint64_t childID = BackgroundParent::GetChildID(aBackgroundActor);
       if (childID) {
         contentParentId = Some(ContentParentId(childID));
       }
 
-      RefPtr<PrepareDatastoreOp> prepareDatastoreOp = new PrepareDatastoreOp(
-          mainEventTarget, aParams, contentParentId);
+      RefPtr<PrepareDatastoreOp> prepareDatastoreOp =
+          new PrepareDatastoreOp(mainEventTarget, aParams, contentParentId);
 
       if (!gPrepareDatastoreOps) {
         gPrepareDatastoreOps = new PrepareDatastoreOpArray();
       }
       gPrepareDatastoreOps->AppendElement(prepareDatastoreOp);
 
       actor = std::move(prepareDatastoreOp);
 
@@ -5577,18 +5577,17 @@ mozilla::ipc::IPCResult LSRequestBase::R
   return IPC_OK();
 }
 
 /*******************************************************************************
  * PrepareDatastoreOp
  ******************************************************************************/
 
 PrepareDatastoreOp::PrepareDatastoreOp(
-    nsIEventTarget* aMainEventTarget,
-    const LSRequestParams& aParams,
+    nsIEventTarget* aMainEventTarget, const LSRequestParams& aParams,
     const Maybe<ContentParentId>& aContentParentId)
     : LSRequestBase(aMainEventTarget),
       mMainEventTarget(aMainEventTarget),
       mLoadDataOp(nullptr),
       mParams(aParams.get_LSRequestPrepareDatastoreParams()),
       mContentParentId(aContentParentId),
       mPrivateBrowsingId(0),
       mUsage(0),
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -392,21 +392,22 @@ nsresult AudioStream::OpenCubeb(cubeb* a
                         timeDelta.ToMilliseconds());
 
   return NS_OK;
 }
 
 void AudioStream::SetVolume(double aVolume) {
   MOZ_ASSERT(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
 
+#ifdef DEBUG
   {
     MonitorAutoLock mon(mMonitor);
     MOZ_ASSERT(mState != SHUTDOWN, "Don't set volume after shutdown.");
-    MOZ_DIAGNOSTIC_ASSERT(mState != ERRORED, "Don't set volume if stream got error.");
   }
+#endif
 
   if (cubeb_stream_set_volume(mCubebStream.get(),
                               aVolume * CubebUtils::GetVolumeScale()) !=
       CUBEB_OK) {
     LOGE("Could not change volume on cubeb stream.");
   }
 }
 
--- a/dom/media/ipc/RDDProcessHost.cpp
+++ b/dom/media/ipc/RDDProcessHost.cpp
@@ -200,13 +200,13 @@ void RDDProcessHost::KillProcess() { Kil
 void RDDProcessHost::DestroyProcess() {
   // Cancel all tasks. We don't want anything triggering after our caller
   // expects this to go away.
   {
     MonitorAutoLock lock(mMonitor);
     mTaskFactory.RevokeAll();
   }
 
-  MessageLoop::current()->PostTask(NS_NewRunnableFunction(
-      "DestroyProcessRunnable", [this] { Destroy(); }));
+  MessageLoop::current()->PostTask(
+      NS_NewRunnableFunction("DestroyProcessRunnable", [this] { Destroy(); }));
 }
 
 }  // namespace mozilla
--- a/dom/media/mp4/MP4Demuxer.cpp
+++ b/dom/media/mp4/MP4Demuxer.cpp
@@ -69,21 +69,17 @@ class MP4TrackDemuxer : public MediaTrac
   UniquePtr<TrackInfo> mInfo;
   RefPtr<Index> mIndex;
   UniquePtr<SampleIterator> mIterator;
   Maybe<media::TimeUnit> mNextKeyframeTime;
   // Queued samples extracted by the demuxer, but not yet returned.
   RefPtr<MediaRawData> mQueuedSample;
   bool mNeedReIndex;
   bool mNeedSPSForTelemetry;
-  enum CodecType {
-    kH264,
-    kVP9,
-    kOther
-  } mType = kOther;
+  enum CodecType { kH264, kVP9, kOther } mType = kOther;
 };
 
 // Returns true if no SPS was found and search for it should continue.
 bool AccumulateSPSTelemetry(const MediaByteBuffer* aExtradata) {
   SPSData spsdata;
   if (H264::DecodeSPSFromExtraData(aExtradata, spsdata)) {
     uint8_t constraints = (spsdata.constraint_set0_flag ? (1 << 0) : 0) |
                           (spsdata.constraint_set1_flag ? (1 << 1) : 0) |
--- a/dom/media/systemservices/VideoFrameUtils.cpp
+++ b/dom/media/systemservices/VideoFrameUtils.cpp
@@ -9,18 +9,19 @@
 #include "mozilla/ShmemPool.h"
 
 namespace mozilla {
 
 uint32_t VideoFrameUtils::TotalRequiredBufferSize(
     const webrtc::VideoFrame& aVideoFrame) {
   auto i420 = aVideoFrame.video_frame_buffer()->ToI420();
   auto height = i420->height();
-  size_t size = height * i420->StrideY() + ((height + 1) / 2) * i420->StrideU() +
-         ((height + 1) / 2) * i420->StrideV();
+  size_t size = height * i420->StrideY() +
+                ((height + 1) / 2) * i420->StrideU() +
+                ((height + 1) / 2) * i420->StrideV();
   MOZ_RELEASE_ASSERT(size < std::numeric_limits<uint32_t>::max());
   return static_cast<uint32_t>(size);
 }
 
 void VideoFrameUtils::InitFrameBufferProperties(
     const webrtc::VideoFrame& aVideoFrame,
     camera::VideoFrameProperties& aDestProps) {
   // The VideoFrameBuffer image data stored in the accompanying buffer
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -533,18 +533,18 @@ Worklet* AudioContext::GetAudioWorklet(E
 }
 
 bool AudioContext::IsRunning() const {
   return mAudioContextState == AudioContextState::Running;
 }
 
 already_AddRefed<Promise> AudioContext::DecodeAudioData(
     const ArrayBuffer& aBuffer,
-    const Optional<OwningNonNull<DecodeSuccessCallback> >& aSuccessCallback,
-    const Optional<OwningNonNull<DecodeErrorCallback> >& aFailureCallback,
+    const Optional<OwningNonNull<DecodeSuccessCallback>>& aSuccessCallback,
+    const Optional<OwningNonNull<DecodeErrorCallback>>& aFailureCallback,
     ErrorResult& aRv) {
   nsCOMPtr<nsIGlobalObject> parentObject = do_QueryInterface(GetParentObject());
   RefPtr<Promise> promise;
   AutoJSAPI jsapi;
   jsapi.Init();
   JSContext* cx = jsapi.cx();
 
   // CheckedUnwrapStatic is OK, since we know we have an ArrayBuffer.
--- a/dom/media/webaudio/AudioParam.cpp
+++ b/dom/media/webaudio/AudioParam.cpp
@@ -67,19 +67,17 @@ void AudioParam::DisconnectFromGraphAndD
   }
 
   if (mStream) {
     mStream->Destroy();
     mStream = nullptr;
   }
 }
 
-MediaStream* AudioParam::GetStream() const {
-  return mStream;
-}
+MediaStream* AudioParam::GetStream() const { return mStream; }
 
 MediaStream* AudioParam::Stream() {
   if (mStream) {
     return mStream;
   }
 
   AudioNodeEngine* engine = new AudioNodeEngine(nullptr);
   mStream = AudioNodeStream::Create(mNode->Context(), engine,
--- a/dom/security/ReferrerInfo.cpp
+++ b/dom/security/ReferrerInfo.cpp
@@ -13,18 +13,17 @@ namespace mozilla {
 namespace dom {
 
 // Implementation of ClassInfo is required to serialize/deserialize
 NS_IMPL_CLASSINFO(ReferrerInfo, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
                   REFERRERINFO_CID)
 
 NS_IMPL_ISUPPORTS_CI(ReferrerInfo, nsIReferrerInfo, nsISerializable)
 
-ReferrerInfo::ReferrerInfo(nsIURI* aOriginalReferrer,
-                           uint32_t aPolicy,
+ReferrerInfo::ReferrerInfo(nsIURI* aOriginalReferrer, uint32_t aPolicy,
                            bool aSendReferrer)
     : mOriginalReferrer(aOriginalReferrer),
       mPolicy(aPolicy),
       mSendReferrer(aSendReferrer) {}
 
 NS_IMETHODIMP
 ReferrerInfo::GetOriginalReferrer(nsIURI** aOriginalReferrer) {
   *aOriginalReferrer = mOriginalReferrer;
@@ -52,18 +51,17 @@ ReferrerInfo::GetSendReferrer(bool* aSen
 
 NS_IMETHODIMP
 ReferrerInfo::SetSendReferrer(bool aSendReferrer) {
   mSendReferrer = aSendReferrer;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-ReferrerInfo::Init(uint32_t aReferrerPolicy,
-                   bool aSendReferrer,
+ReferrerInfo::Init(uint32_t aReferrerPolicy, bool aSendReferrer,
                    nsIURI* aOriginalReferrer) {
   mPolicy = aReferrerPolicy;
   mSendReferrer = aSendReferrer;
   mOriginalReferrer = aOriginalReferrer;
   return NS_OK;
 }
 
 /* ===== nsISerializable implementation ====== */
--- a/dom/security/ReferrerInfo.h
+++ b/dom/security/ReferrerInfo.h
@@ -29,22 +29,21 @@ namespace dom {
 
 /**
  * ReferrerInfo class holds a original referrer URL, as well as the referrer
  * policy to be applied to this referrer.
  *
  **/
 class ReferrerInfo : public nsIReferrerInfo {
  public:
-  ReferrerInfo () = default;
+  ReferrerInfo() = default;
   explicit ReferrerInfo(nsIURI* aOriginalReferrer,
                         uint32_t aPolicy = mozilla::net::RP_Unset,
                         bool aSendReferrer = true);
 
-
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREFERRERINFO
   NS_DECL_NSISERIALIZABLE
 
  private:
   virtual ~ReferrerInfo() {}
 
   nsCOMPtr<nsIURI> mOriginalReferrer;
--- a/dom/serviceworkers/ServiceWorkerManager.cpp
+++ b/dom/serviceworkers/ServiceWorkerManager.cpp
@@ -767,19 +767,18 @@ RefPtr<ServiceWorkerRegistrationPromise>
   }
 
   RefPtr<ServiceWorkerJobQueue> queue =
       GetOrCreateJobQueue(scopeKey, aScopeURL);
 
   RefPtr<ServiceWorkerResolveWindowPromiseOnRegisterCallback> cb =
       new ServiceWorkerResolveWindowPromiseOnRegisterCallback();
 
-  nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
   RefPtr<ServiceWorkerRegisterJob> job = new ServiceWorkerRegisterJob(
-      principal, aScopeURL, aScriptURL, loadGroup,
+      principal, aScopeURL, aScriptURL,
       static_cast<ServiceWorkerUpdateViaCache>(aUpdateViaCache));
 
   job->AppendResultCallback(cb);
   queue->ScheduleJob(job);
 
   MOZ_ASSERT(NS_IsMainThread());
   Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REGISTRATIONS, 1);
 
@@ -2250,17 +2249,17 @@ void ServiceWorkerManager::SoftUpdateInt
 
   // "If the registration queue for registration is empty, invoke Update
   // algorithm, or its equivalent, with client, registration as its argument."
   // TODO(catalinb): We don't implement the force bypass cache flag.
   // See: https://github.com/slightlyoff/ServiceWorker/issues/759
   RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, aScope);
 
   RefPtr<ServiceWorkerUpdateJob> job = new ServiceWorkerUpdateJob(
-      principal, registration->Scope(), newest->ScriptSpec(), nullptr,
+      principal, registration->Scope(), newest->ScriptSpec(),
       registration->GetUpdateViaCache());
 
   if (aCallback) {
     RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback);
     job->AppendResultCallback(cb);
   }
 
   queue->ScheduleJob(job);
@@ -2337,17 +2336,17 @@ void ServiceWorkerManager::UpdateInterna
     return;
   }
 
   RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, aScope);
 
   // "Invoke Update algorithm, or its equivalent, with client, registration as
   // its argument."
   RefPtr<ServiceWorkerUpdateJob> job = new ServiceWorkerUpdateJob(
-      aPrincipal, registration->Scope(), newest->ScriptSpec(), nullptr,
+      aPrincipal, registration->Scope(), newest->ScriptSpec(),
       registration->GetUpdateViaCache());
 
   RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback);
   job->AppendResultCallback(cb);
 
   queue->ScheduleJob(job);
 }
 
--- a/dom/serviceworkers/ServiceWorkerRegisterJob.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegisterJob.cpp
@@ -9,20 +9,19 @@
 #include "mozilla/dom/WorkerCommon.h"
 #include "ServiceWorkerManager.h"
 
 namespace mozilla {
 namespace dom {
 
 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
     nsIPrincipal* aPrincipal, const nsACString& aScope,
-    const nsACString& aScriptSpec, nsILoadGroup* aLoadGroup,
-    ServiceWorkerUpdateViaCache aUpdateViaCache)
+    const nsACString& aScriptSpec, ServiceWorkerUpdateViaCache aUpdateViaCache)
     : ServiceWorkerUpdateJob(Type::Register, aPrincipal, aScope, aScriptSpec,
-                             aLoadGroup, aUpdateViaCache) {}
+                             aUpdateViaCache) {}
 
 void ServiceWorkerRegisterJob::AsyncExecute() {
   MOZ_ASSERT(NS_IsMainThread());
 
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   if (Canceled() || !swm) {
     FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
     return;
--- a/dom/serviceworkers/ServiceWorkerRegisterJob.h
+++ b/dom/serviceworkers/ServiceWorkerRegisterJob.h
@@ -14,17 +14,16 @@ namespace dom {
 
 // The register job.  This implements the steps in the spec Register algorithm,
 // but then uses ServiceWorkerUpdateJob to implement the Update and Install
 // spec algorithms.
 class ServiceWorkerRegisterJob final : public ServiceWorkerUpdateJob {
  public:
   ServiceWorkerRegisterJob(nsIPrincipal* aPrincipal, const nsACString& aScope,
                            const nsACString& aScriptSpec,
-                           nsILoadGroup* aLoadGroup,
                            ServiceWorkerUpdateViaCache aUpdateViaCache);
 
  private:
   // Implement the Register algorithm steps and then call the parent class
   // Update() to complete the job execution.
   virtual void AsyncExecute() override;
 
   virtual ~ServiceWorkerRegisterJob();
--- a/dom/serviceworkers/ServiceWorkerScriptCache.cpp
+++ b/dom/serviceworkers/ServiceWorkerScriptCache.cpp
@@ -104,17 +104,17 @@ class CompareNetwork final : public nsIS
         mCacheResult(NS_OK),
         mIsMainScript(aIsMainScript),
         mIsFromCache(false) {
     MOZ_ASSERT(aManager);
     MOZ_ASSERT(NS_IsMainThread());
   }
 
   nsresult Initialize(nsIPrincipal* aPrincipal, const nsAString& aURL,
-                      nsILoadGroup* aLoadGroup, Cache* const aCache);
+                      Cache* const aCache);
 
   void Abort();
 
   void NetworkFinish(nsresult aRv);
 
   void CacheFinish(nsresult aRv);
 
   const nsString& URL() const {
@@ -252,17 +252,17 @@ class CompareManager final : public Prom
         mPendingCount(0),
         mOnFailure(OnFailure::DoNothing),
         mAreScriptsEqual(true) {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(aRegistration);
   }
 
   nsresult Initialize(nsIPrincipal* aPrincipal, const nsAString& aURL,
-                      const nsAString& aCacheName, nsILoadGroup* aLoadGroup);
+                      const nsAString& aCacheName);
 
   void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
 
   void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
 
   CacheStorage* CacheStorage_() {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mCacheStorage);
@@ -325,17 +325,17 @@ class CompareManager final : public Prom
     MOZ_DIAGNOSTIC_ASSERT(mState == WaitingForInitialization ||
                           mState == WaitingForScriptOrComparisonResult);
 
     RefPtr<CompareNetwork> cn =
         new CompareNetwork(this, mRegistration, aIsMainScript);
     mCNList.AppendElement(cn);
     mPendingCount += 1;
 
-    nsresult rv = cn->Initialize(mPrincipal, aURL, mLoadGroup, aCache);
+    nsresult rv = cn->Initialize(mPrincipal, aURL, aCache);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     return NS_OK;
   }
 
   void ManageOldCache(JSContext* aCx, JS::Handle<JS::Value> aValue) {
@@ -581,17 +581,16 @@ class CompareManager final : public Prom
   RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
   RefPtr<CompareCallback> mCallback;
   RefPtr<CacheStorage> mCacheStorage;
 
   nsTArray<RefPtr<CompareNetwork>> mCNList;
 
   nsString mURL;
   RefPtr<nsIPrincipal> mPrincipal;
-  RefPtr<nsILoadGroup> mLoadGroup;
 
   // Used for the old cache where saves the old source scripts.
   RefPtr<Cache> mOldCache;
 
   // Only used if the network script has changed and needs to be cached.
   nsString mNewCacheName;
 
   nsCString mMaxScope;
@@ -611,17 +610,16 @@ class CompareManager final : public Prom
   OnFailure mOnFailure;
   bool mAreScriptsEqual;
 };
 
 NS_IMPL_ISUPPORTS0(CompareManager)
 
 nsresult CompareNetwork::Initialize(nsIPrincipal* aPrincipal,
                                     const nsAString& aURL,
-                                    nsILoadGroup* aLoadGroup,
                                     Cache* const aCache) {
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, nullptr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -1139,29 +1137,27 @@ void CompareCache::ManageValueResult(JSC
       Finish(rv, false);
       return;
     }
   }
 }
 
 nsresult CompareManager::Initialize(nsIPrincipal* aPrincipal,
                                     const nsAString& aURL,
-                                    const nsAString& aCacheName,
-                                    nsILoadGroup* aLoadGroup) {
+                                    const nsAString& aCacheName) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(mPendingCount == 0);
   MOZ_DIAGNOSTIC_ASSERT(mState == WaitingForInitialization);
 
   // RAII Cleanup when fails.
   auto guard = MakeScopeExit([&] { Cleanup(); });
 
   mURL = aURL;
   mPrincipal = aPrincipal;
-  mLoadGroup = aLoadGroup;
 
   // Always create a CacheStorage since we want to write the network entry to
   // the cache even if there isn't an existing one.
   AutoJSAPI jsapi;
   jsapi.Init();
   ErrorResult result;
   mCacheStorage = CreateCacheStorage(jsapi.cx(), aPrincipal, result);
   if (NS_WARN_IF(result.Failed())) {
@@ -1329,27 +1325,26 @@ nsresult GenerateCacheName(nsAString& aN
   // NSID_LENGTH counts the null terminator.
   aName.AssignASCII(chars, NSID_LENGTH - 1);
 
   return NS_OK;
 }
 
 nsresult Compare(ServiceWorkerRegistrationInfo* aRegistration,
                  nsIPrincipal* aPrincipal, const nsAString& aCacheName,
-                 const nsAString& aURL, CompareCallback* aCallback,
-                 nsILoadGroup* aLoadGroup) {
+                 const nsAString& aURL, CompareCallback* aCallback) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRegistration);
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(!aURL.IsEmpty());
   MOZ_ASSERT(aCallback);
 
   RefPtr<CompareManager> cm = new CompareManager(aRegistration, aCallback);
 
-  nsresult rv = cm->Initialize(aPrincipal, aURL, aCacheName, aLoadGroup);
+  nsresult rv = cm->Initialize(aPrincipal, aURL, aCacheName);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 }  // namespace serviceWorkerScriptCache
--- a/dom/serviceworkers/ServiceWorkerUpdateJob.cpp
+++ b/dom/serviceworkers/ServiceWorkerUpdateJob.cpp
@@ -146,36 +146,32 @@ class ServiceWorkerUpdateJob::ContinueIn
     mJob->ContinueAfterInstallEvent(mSuccess);
     mJob = nullptr;
     return NS_OK;
   }
 };
 
 ServiceWorkerUpdateJob::ServiceWorkerUpdateJob(
     nsIPrincipal* aPrincipal, const nsACString& aScope,
-    const nsACString& aScriptSpec, nsILoadGroup* aLoadGroup,
-    ServiceWorkerUpdateViaCache aUpdateViaCache)
+    const nsACString& aScriptSpec, ServiceWorkerUpdateViaCache aUpdateViaCache)
     : ServiceWorkerJob(Type::Update, aPrincipal, aScope, aScriptSpec),
-      mLoadGroup(aLoadGroup),
       mUpdateViaCache(aUpdateViaCache),
       mOnFailure(OnFailure::DoNothing) {}
 
 already_AddRefed<ServiceWorkerRegistrationInfo>
 ServiceWorkerUpdateJob::GetRegistration() const {
   MOZ_ASSERT(NS_IsMainThread());
   RefPtr<ServiceWorkerRegistrationInfo> ref = mRegistration;
   return ref.forget();
 }
 
 ServiceWorkerUpdateJob::ServiceWorkerUpdateJob(
     Type aType, nsIPrincipal* aPrincipal, const nsACString& aScope,
-    const nsACString& aScriptSpec, nsILoadGroup* aLoadGroup,
-    ServiceWorkerUpdateViaCache aUpdateViaCache)
+    const nsACString& aScriptSpec, ServiceWorkerUpdateViaCache aUpdateViaCache)
     : ServiceWorkerJob(aType, aPrincipal, aScope, aScriptSpec),
-      mLoadGroup(aLoadGroup),
       mUpdateViaCache(aUpdateViaCache),
       mOnFailure(serviceWorkerScriptCache::OnFailure::DoNothing) {}
 
 ServiceWorkerUpdateJob::~ServiceWorkerUpdateJob() {}
 
 void ServiceWorkerUpdateJob::FailUpdateJob(ErrorResult& aRv) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRv.Failed());
@@ -295,17 +291,17 @@ void ServiceWorkerUpdateJob::Update() {
   if (workerInfo && workerInfo->ScriptSpec().Equals(mScriptSpec)) {
     cacheName = workerInfo->CacheName();
   }
 
   RefPtr<CompareCallback> callback = new CompareCallback(this);
 
   nsresult rv = serviceWorkerScriptCache::Compare(
       mRegistration, mPrincipal, cacheName, NS_ConvertUTF8toUTF16(mScriptSpec),
-      callback, mLoadGroup);
+      callback);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailUpdateJob(rv);
     return;
   }
 }
 
 ServiceWorkerUpdateViaCache ServiceWorkerUpdateJob::GetUpdateViaCache() const {
   return mUpdateViaCache;
--- a/dom/serviceworkers/ServiceWorkerUpdateJob.h
+++ b/dom/serviceworkers/ServiceWorkerUpdateJob.h
@@ -25,27 +25,25 @@ class ServiceWorkerRegistrationInfo;
 // as a different job type.  This is necessary because the register job
 // performs largely the same operations as the update job, but has a few
 // different starting steps.
 class ServiceWorkerUpdateJob : public ServiceWorkerJob {
  public:
   // Construct an update job to be used only for updates.
   ServiceWorkerUpdateJob(nsIPrincipal* aPrincipal, const nsACString& aScope,
                          const nsACString& aScriptSpec,
-                         nsILoadGroup* aLoadGroup,
                          ServiceWorkerUpdateViaCache aUpdateViaCache);
 
   already_AddRefed<ServiceWorkerRegistrationInfo> GetRegistration() const;
 
  protected:
   // Construct an update job that is overriden as another job type.
   ServiceWorkerUpdateJob(Type aType, nsIPrincipal* aPrincipal,
                          const nsACString& aScope,
                          const nsACString& aScriptSpec,
-                         nsILoadGroup* aLoadGroup,
                          ServiceWorkerUpdateViaCache aUpdateViaCache);
 
   virtual ~ServiceWorkerUpdateJob();
 
   // FailUpdateJob() must be called if an update job needs Finish() with
   // an error.
   void FailUpdateJob(ErrorResult& aRv);
 
@@ -85,17 +83,16 @@ class ServiceWorkerUpdateJob : public Se
 
   // Utility method corresponding to the spec Install algorithm.
   void Install();
 
   // Utility method called after the install event is handled.
   void ContinueAfterInstallEvent(bool aInstallEventSuccess);
 
   RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
-  nsCOMPtr<nsILoadGroup> mLoadGroup;
   ServiceWorkerUpdateViaCache mUpdateViaCache;
   serviceWorkerScriptCache::OnFailure mOnFailure;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_serviceworkerupdatejob_h
--- a/dom/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/dom/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -1334,18 +1334,18 @@ nsresult nsWebBrowserPersist::SaveChanne
   // special-cases a file target and creates a file output stream directly.
   // We want to special-case a file source and create a file input stream,
   // but we don't need to do this in the case of a file target.
   nsCOMPtr<nsIFileChannel> fc(do_QueryInterface(aChannel));
   nsCOMPtr<nsIFileURL> fu(do_QueryInterface(aFile));
 
   if (fc && !fu) {
     nsCOMPtr<nsIInputStream> fileInputStream, bufferedInputStream;
-    nsresult rv = NS_MaybeOpenChannelUsingOpen(
-        aChannel, getter_AddRefs(fileInputStream));
+    nsresult rv =
+        NS_MaybeOpenChannelUsingOpen(aChannel, getter_AddRefs(fileInputStream));
     NS_ENSURE_SUCCESS(rv, rv);
     rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedInputStream),
                                    fileInputStream.forget(),
                                    BUFFERED_OUTPUT_SIZE);
     NS_ENSURE_SUCCESS(rv, rv);
     nsAutoCString contentType;
     aChannel->GetContentType(contentType);
     return StartUpload(bufferedInputStream, aFile, contentType);
--- a/gfx/2d/DrawTargetDual.h
+++ b/gfx/2d/DrawTargetDual.h
@@ -36,17 +36,19 @@ namespace gfx {
  * to the first destination DrawTarget (mA) and the snapshot of the second
  * source DrawTarget is used at the source for the second destination
  * DrawTarget (mB). This class facilitates black-background/white-background
  * drawing for per-component alpha extraction for backends which do not support
  * native component alpha.
  */
 class DrawTargetDual : public DrawTarget {
  public:
-  virtual bool IsValid() const override { return mA->IsValid() && mB->IsValid(); };
+  virtual bool IsValid() const override {
+    return mA->IsValid() && mB->IsValid();
+  };
 
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetDual, override)
   DrawTargetDual(DrawTarget *aA, DrawTarget *aB) : mA(aA), mB(aB) {
     mFormat = aA->GetFormat();
   }
 
   virtual DrawTargetType GetType() const override { return mA->GetType(); }
   virtual BackendType GetBackendType() const override {
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -301,22 +301,24 @@ already_AddRefed<GLContext> GLContextEGL
     widget::CompositorWidget* aCompositorWidget, bool aForceAccelerated) {
   nsCString discardFailureId;
   if (!GLLibraryEGL::EnsureInitialized(false, &discardFailureId)) {
     gfxCriticalNote << "Failed to load EGL library 6!";
     return EGL_NO_SURFACE;
   }
 
   MOZ_ASSERT(aCompositorWidget);
-  EGLNativeWindowType window = GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget);
+  EGLNativeWindowType window =
+      GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget);
   if (!window) {
     gfxCriticalNote << "window is null";
     return EGL_NO_SURFACE;
   }
-  const bool useWebRender = aCompositorWidget->GetCompositorOptions().UseWebRender();
+  const bool useWebRender =
+      aCompositorWidget->GetCompositorOptions().UseWebRender();
 
   EGLConfig config;
   if (!CreateConfig(&config, useWebRender)) {
     gfxCriticalNote << "Failed to create EGLConfig!";
     return EGL_NO_SURFACE;
   }
 
   return mozilla::gl::CreateSurfaceFromNativeWindow(window, config);
--- a/gfx/ipc/GPUProcessHost.cpp
+++ b/gfx/ipc/GPUProcessHost.cpp
@@ -207,14 +207,14 @@ void GPUProcessHost::KillProcess() { Kil
 void GPUProcessHost::DestroyProcess() {
   // Cancel all tasks. We don't want anything triggering after our caller
   // expects this to go away.
   {
     MonitorAutoLock lock(mMonitor);
     mTaskFactory.RevokeAll();
   }
 
-  MessageLoop::current()->PostTask(NS_NewRunnableFunction(
-      "DestroyProcessRunnable", [this] { Destroy(); }));
+  MessageLoop::current()->PostTask(
+      NS_NewRunnableFunction("DestroyProcessRunnable", [this] { Destroy(); }));
 }
 
 }  // namespace gfx
 }  // namespace mozilla
--- a/gfx/layers/PersistentBufferProvider.cpp
+++ b/gfx/layers/PersistentBufferProvider.cpp
@@ -73,18 +73,18 @@ already_AddRefed<PersistentBufferProvide
 PersistentBufferProviderBasic::Create(gfx::IntSize aSize,
                                       gfx::SurfaceFormat aFormat,
                                       gfx::BackendType aBackend) {
   RefPtr<DrawTarget> dt =
       gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize,
                                                              aFormat);
 
   if (dt) {
-    // This is simply to ensure the DrawTarget gets initialized, and will detect a
-    // device reset, even if we're on the main thread.
+    // This is simply to ensure the DrawTarget gets initialized, and will detect
+    // a device reset, even if we're on the main thread.
     dt->ClearRect(Rect(0, 0, 0, 0));
   }
 
   if (!dt || !dt->IsValid()) {
     return nullptr;
   }
 
   RefPtr<PersistentBufferProviderBasic> provider =
@@ -341,18 +341,18 @@ PersistentBufferProviderShared::BorrowDr
 
       previous->Unlock();
     }
   }
 
   mDrawTarget = tex->BorrowDrawTarget();
 
   if (mDrawTarget) {
-    // This is simply to ensure the DrawTarget gets initialized, and will detect a
-    // device reset, even if we're on the main thread.
+    // This is simply to ensure the DrawTarget gets initialized, and will detect
+    // a device reset, even if we're on the main thread.
     mDrawTarget->ClearRect(Rect(0, 0, 0, 0));
 
     if (!mDrawTarget->IsValid()) {
       mDrawTarget = nullptr;
     }
   }
 
   RefPtr<gfx::DrawTarget> dt(mDrawTarget);
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1727,21 +1727,21 @@ PWebRenderBridgeParent* CompositorBridge
   MOZ_RELEASE_ASSERT(false);
 #endif
   MOZ_ASSERT(wr::AsLayersId(aPipelineId) == mRootLayerTreeID);
   MOZ_ASSERT(!mWrBridge);
   MOZ_ASSERT(!mCompositor);
   MOZ_ASSERT(!mCompositorScheduler);
   MOZ_ASSERT(mWidget);
 
-#  ifdef XP_WIN
+#ifdef XP_WIN
   if (mWidget && DeviceManagerDx::Get()->CanUseDComp()) {
     mWidget->AsWindows()->EnsureCompositorWindow();
   }
-#  endif
+#endif
 
   RefPtr<widget::CompositorWidget> widget = mWidget;
   wr::WrWindowId windowId = wr::NewWindowId();
   if (mApzUpdater) {
     // If APZ is enabled, we need to register the APZ updater with the window id
     // before the updater thread is created in WebRenderAPI::Create, so
     // that the callback from the updater thread can find the right APZUpdater.
     mApzUpdater->SetWebRenderWindowId(windowId);
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -618,22 +618,24 @@ void ImageBridgeChild::UpdateTextureFact
   // ImageHost is incompatible between WebRender enabled and WebRender disabled.
   // Then drop all ImageContainers' ImageClients during disabling WebRender.
   bool disablingWebRender =
       GetCompositorBackendType() == LayersBackend::LAYERS_WR &&
       aIdentifier.mParentBackend != LayersBackend::LAYERS_WR;
   // D3DTexture might become obsolte. To prevent to use obsoleted D3DTexture,
   // drop all ImageContainers' ImageClients.
 
-  // During re-creating GPU process, there was a period that ImageBridgeChild was
-  // re-created, but ImageBridgeChild::UpdateTextureFactoryIdentifier() was not
-  // called yet. In the period, if ImageBridgeChild::CreateImageClient() is called,
-  // ImageBridgeParent creates incompatible ImageHost than WebRenderImageHost.
-  bool initializingWebRender = GetCompositorBackendType() != LayersBackend::LAYERS_WR &&
-                               aIdentifier.mParentBackend == LayersBackend::LAYERS_WR;
+  // During re-creating GPU process, there was a period that ImageBridgeChild
+  // was re-created, but ImageBridgeChild::UpdateTextureFactoryIdentifier() was
+  // not called yet. In the period, if ImageBridgeChild::CreateImageClient() is
+  // called, ImageBridgeParent creates incompatible ImageHost than
+  // WebRenderImageHost.
+  bool initializingWebRender =
+      GetCompositorBackendType() != LayersBackend::LAYERS_WR &&
+      aIdentifier.mParentBackend == LayersBackend::LAYERS_WR;
 
   bool needsDrop = disablingWebRender || initializingWebRender;
 
 #if defined(XP_WIN)
   RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetImageDevice();
   needsDrop |= !!mImageDevice && mImageDevice != device &&
                GetCompositorBackendType() == LayersBackend::LAYERS_D3D11;
   mImageDevice = device;
--- a/gfx/layers/wr/StackingContextHelper.cpp
+++ b/gfx/layers/wr/StackingContextHelper.cpp
@@ -25,17 +25,18 @@ StackingContextHelper::StackingContextHe
     const StackingContextHelper& aParentSC, const ActiveScrolledRoot* aAsr,
     nsIFrame* aContainerFrame, nsDisplayItem* aContainerItem,
     wr::DisplayListBuilder& aBuilder, const wr::StackingContextParams& aParams,
     const LayoutDeviceRect& aBounds)
     : mBuilder(&aBuilder),
       mScale(1.0f, 1.0f),
       mDeferredTransformItem(aParams.mDeferredTransformItem),
       mIsPreserve3D(aParams.transform_style == wr::TransformStyle::Preserve3D),
-      mRasterizeLocally(aParams.mRasterizeLocally || aParentSC.mRasterizeLocally) {
+      mRasterizeLocally(aParams.mRasterizeLocally ||
+                        aParentSC.mRasterizeLocally) {
   // Compute scale for fallback rendering. We don't try to guess a scale for 3d
   // transformed items
   gfx::Matrix transform2d;
   if (aParams.mBoundTransform &&
       aParams.mBoundTransform->CanDraw2D(&transform2d) &&
       aParams.reference_frame_kind != wr::WrReferenceFrameKind::Perspective &&
       !aParentSC.mIsPreserve3D) {
     mInheritedTransform = transform2d * aParentSC.mInheritedTransform;
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -273,18 +273,18 @@ Maybe<wr::FontInstanceKey> WebRenderBrid
 
   Maybe<wr::FontInstanceOptions> options;
   Maybe<wr::FontInstancePlatformOptions> platformOptions;
   std::vector<FontVariation> variations;
   aScaledFont->GetWRFontInstanceOptions(&options, &platformOptions,
                                         &variations);
 
   aResources->AddFontInstance(
-      instanceKey, fontKey.value(), aScaledFont->GetSize(), options.ptrOr(nullptr),
-      platformOptions.ptrOr(nullptr),
+      instanceKey, fontKey.value(), aScaledFont->GetSize(),
+      options.ptrOr(nullptr), platformOptions.ptrOr(nullptr),
       Range<const FontVariation>(variations.data(), variations.size()));
   if (resources.isSome()) {
     UpdateResources(resources.ref());
   }
 
   mFontInstanceKeys.Put(aScaledFont, instanceKey);
 
   return Some(instanceKey);
--- a/gfx/thebes/gfxFontEntry.cpp
+++ b/gfx/thebes/gfxFontEntry.cpp
@@ -1704,17 +1704,17 @@ void gfxFontFamily::FindFontForChar(Glob
   if (profiler_is_active()) {
     charAndName = nsPrintfCString("\\u%x %s", aMatchData->mCh, mName.get());
   }
 
   AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("gfxFontFamily::FindFontForChar",
                                         LAYOUT, charAndName);
 #endif
 
-  AutoTArray<gfxFontEntry*,4> entries;
+  AutoTArray<gfxFontEntry*, 4> entries;
   FindAllFontsForStyle(aMatchData->mStyle, entries,
                        /*aIgnoreSizeTolerance*/ true);
   if (entries.IsEmpty()) {
     return;
   }
 
   gfxFontEntry* fe = nullptr;
   float distance = INFINITY;
@@ -1731,18 +1731,17 @@ void gfxFontFamily::FindFontForChar(Glob
       LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
 
       if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Debug))) {
         uint32_t unicodeRange = FindCharUnicodeRange(aMatchData->mCh);
         Script script = GetScriptCode(aMatchData->mCh);
         MOZ_LOG(log, LogLevel::Debug,
                 ("(textrun-systemfallback-fonts) char: u+%6.6x "
                  "unicode-range: %d script: %d match: [%s]\n",
-                 aMatchData->mCh, unicodeRange, int(script),
-                 e->Name().get()));
+                 aMatchData->mCh, unicodeRange, int(script), e->Name().get()));
       }
 
       fe = e;
       distance = WeightStyleStretchDistance(fe, aMatchData->mStyle);
       break;
     }
   }
 
--- a/gfx/vr/gfxVRExternal.cpp
+++ b/gfx/vr/gfxVRExternal.cpp
@@ -441,32 +441,32 @@ VRSystemManagerExternal::VRSystemManager
   mShmemFile = NULL;
 #elif defined(MOZ_WIDGET_ANDROID)
   mExternalStructFailed = false;
   mEnumerationCompleted = false;
 #endif
   mDoShutdown = false;
 
 #if defined(XP_WIN)
-  mMutex = CreateMutex(
-    NULL,                   // default security descriptor
-    false,                  // mutex not owned
-    TEXT("mozilla::vr::ShmemMutex"));  // object name
+  mMutex = CreateMutex(NULL,   // default security descriptor
+                       false,  // mutex not owned
+                       TEXT("mozilla::vr::ShmemMutex"));  // object name
 
   if (mMutex == NULL) {
     nsAutoCString msg;
     msg.AppendPrintf("VRSystemManagerExternal CreateMutex error \"%lu\".",
-                      GetLastError());
+                     GetLastError());
     NS_WARNING(msg.get());
     MOZ_ASSERT(false);
     return;
   }
-  // At xpcshell extension tests, it creates multiple VRSystemManagerExternal instances
-  // in plug-contrainer.exe. It causes GetLastError() return `ERROR_ALREADY_EXISTS`.
-  // However, even though `ERROR_ALREADY_EXISTS`, it still returns the same mutex handle.
+  // At xpcshell extension tests, it creates multiple VRSystemManagerExternal
+  // instances in plug-contrainer.exe. It causes GetLastError() return
+  // `ERROR_ALREADY_EXISTS`. However, even though `ERROR_ALREADY_EXISTS`, it
+  // still returns the same mutex handle.
   //
   // https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createmutexa
   MOZ_ASSERT(GetLastError() == 0 || GetLastError() == ERROR_ALREADY_EXISTS);
 #endif  // defined(XP_WIN)
 }
 
 VRSystemManagerExternal::~VRSystemManagerExternal() {
   CloseShmem();
--- a/gfx/vr/gfxVRMutex.h
+++ b/gfx/vr/gfxVRMutex.h
@@ -6,29 +6,24 @@
 
 #ifndef GFX_VR_MUTEX_H
 #define GFX_VR_MUTEX_H
 
 namespace mozilla {
 namespace gfx {
 
 #if defined(XP_WIN)
-class WaitForMutex
-{
-public:
-  explicit WaitForMutex(HANDLE handle)
-    : mHandle(handle)
-    , mStatus(false) {
-
+class WaitForMutex {
+ public:
+  explicit WaitForMutex(HANDLE handle) : mHandle(handle), mStatus(false) {
     MOZ_ASSERT(mHandle);
 
     DWORD dwWaitResult;
-    dwWaitResult = WaitForSingleObject(
-        mHandle,    // handle to mutex
-        INFINITE);  // no time-out interval
+    dwWaitResult = WaitForSingleObject(mHandle,    // handle to mutex
+                                       INFINITE);  // no time-out interval
 
     switch (dwWaitResult) {
       // The thread got ownership of the mutex
       case WAIT_OBJECT_0:
         mStatus = true;
         break;
 
       // The thread got ownership of an abandoned mutex
@@ -40,18 +35,18 @@ public:
         mStatus = false;
         break;
     }
   }
 
   ~WaitForMutex() {
     if (mHandle && !ReleaseMutex(mHandle)) {
       nsAutoCString msg;
-      msg.AppendPrintf("WaitForMutex %d ReleaseMutex error \"%lu\".",
-                        mHandle, GetLastError());
+      msg.AppendPrintf("WaitForMutex %d ReleaseMutex error \"%lu\".", mHandle,
+                       GetLastError());
       NS_WARNING(msg.get());
       MOZ_ASSERT(false, "Failed to release mutex.");
     }
   }
 
   bool GetStatus() { return mStatus; }
 
  private:
--- a/gfx/vr/service/VRService.cpp
+++ b/gfx/vr/service/VRService.cpp
@@ -103,25 +103,23 @@ void VRService::Refresh() {
 }
 
 void VRService::Start() {
 #if defined(XP_WIN)
   // Adding `!XRE_IsParentProcess()` to avoid Win 7 32-bit WebVR tests
   // to OpenMutex when there is no GPU process to create
   // VRSystemManagerExternal and its mutex.
   if (!mMutex && !XRE_IsParentProcess()) {
-     mMutex = OpenMutex(
-        MUTEX_ALL_ACCESS,       // request full access
-        false,                  // handle not inheritable
-        TEXT("mozilla::vr::ShmemMutex"));  // object name
+    mMutex = OpenMutex(MUTEX_ALL_ACCESS,  // request full access
+                       false,             // handle not inheritable
+                       TEXT("mozilla::vr::ShmemMutex"));  // object name
 
     if (mMutex == NULL) {
       nsAutoCString msg;
-      msg.AppendPrintf("VRService OpenMutex error \"%lu\".",
-                       GetLastError());
+      msg.AppendPrintf("VRService OpenMutex error \"%lu\".", GetLastError());
       NS_WARNING(msg.get());
       MOZ_ASSERT(false);
     }
     MOZ_ASSERT(GetLastError() == 0);
   }
 #endif
 
   if (!mServiceThread) {
@@ -456,22 +454,22 @@ void VRService::PushState(const mozilla:
 #if defined(MOZ_WIDGET_ANDROID)
   if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) ==
       0) {
     memcpy((void*)&mAPIShmem->state, &aState, sizeof(VRSystemState));
     pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
   }
 #else
   bool state = true;
-#if defined(XP_WIN)
+#  if defined(XP_WIN)
   if (!XRE_IsParentProcess()) {
     WaitForMutex lock(mMutex);
     state = lock.GetStatus();
   }
-#endif  // defined(XP_WIN)
+#  endif  // defined(XP_WIN)
   if (state) {
     mAPIShmem->generationA++;
     memcpy((void*)&mAPIShmem->state, &aState, sizeof(VRSystemState));
     mAPIShmem->generationB++;
   }
 #endif    // defined(MOZ_WIDGET_ANDROID)
 }
 
@@ -491,22 +489,22 @@ void VRService::PullState(mozilla::gfx::
 #if defined(MOZ_WIDGET_ANDROID)
   if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)) ==
       0) {
     memcpy(&aState, &tmp.geckoState, sizeof(VRBrowserState));
     pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->geckoMutex));
   }
 #else
   bool status = true;
-#if defined(XP_WIN)
+#  if defined(XP_WIN)
   if (!XRE_IsParentProcess()) {
     WaitForMutex lock(mMutex);
     status = lock.GetStatus();
   }
-#endif  // defined(XP_WIN)
+#  endif  // defined(XP_WIN)
   if (status) {
     VRExternalShmem tmp;
     if (mAPIShmem->geckoGenerationA != mBrowserGeneration) {
       memcpy(&tmp, mAPIShmem, sizeof(VRExternalShmem));
       if (tmp.geckoGenerationA == tmp.geckoGenerationB &&
           tmp.geckoGenerationA != 0 && tmp.geckoGenerationA != -1) {
         memcpy(&aState, &tmp.geckoState, sizeof(VRBrowserState));
         mBrowserGeneration = tmp.geckoGenerationA;
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -13,17 +13,17 @@ gleam = "0.6.8"
 log = "0.4"
 nsstring = { path = "../../xpcom/rust/nsstring" } 
 bincode = "1.0"
 uuid = { version = "0.6", features = ["v4"] }
 fxhash = "0.2.1"
 
 [dependencies.webrender]
 path = "../wr/webrender"
-version = "0.59.0"
+version = "0.60.0"
 default-features = false
 features = ["capture", "serialize_program"]
 
 [target.'cfg(target_os = "windows")'.dependencies]
 dwrote = "0.8"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-foundation = "0.6"
--- a/gfx/webrender_bindings/RenderCompositor.cpp
+++ b/gfx/webrender_bindings/RenderCompositor.cpp
@@ -12,32 +12,33 @@
 #include "mozilla/webrender/RenderCompositorOGL.h"
 #include "mozilla/widget/CompositorWidget.h"
 
 #ifdef XP_WIN
 #  include "mozilla/webrender/RenderCompositorANGLE.h"
 #endif
 
 #ifdef MOZ_WAYLAND
-#include "mozilla/webrender/RenderCompositorEGL.h"
+#  include "mozilla/webrender/RenderCompositorEGL.h"
 #endif
 
 namespace mozilla {
 namespace wr {
 
 /* static */ UniquePtr<RenderCompositor> RenderCompositor::Create(
     RefPtr<widget::CompositorWidget>&& aWidget) {
 #ifdef XP_WIN
   if (gfx::gfxVars::UseWebRenderANGLE()) {
     return RenderCompositorANGLE::Create(std::move(aWidget));
   }
 #endif
 
 #ifdef MOZ_WAYLAND
-  UniquePtr<RenderCompositor> eglCompositor = RenderCompositorEGL::Create(aWidget);
+  UniquePtr<RenderCompositor> eglCompositor =
+      RenderCompositorEGL::Create(aWidget);
   if (eglCompositor) {
     return eglCompositor;
   }
 #endif
 
   return RenderCompositorOGL::Create(std::move(aWidget));
 }
 
--- a/gfx/webrender_bindings/RenderCompositorEGL.cpp
+++ b/gfx/webrender_bindings/RenderCompositorEGL.cpp
@@ -16,17 +16,16 @@
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
 
 namespace mozilla {
 namespace wr {
 
 /* static */ UniquePtr<RenderCompositor> RenderCompositorEGL::Create(
     RefPtr<widget::CompositorWidget> aWidget) {
-
   if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
     return nullptr;
   }
 
   RefPtr<gl::GLContext> gl;
   gl = CreateGLContext(aWidget);
   if (!gl) {
     return nullptr;
@@ -35,17 +34,17 @@ namespace wr {
 }
 
 /* static */ already_AddRefed<gl::GLContext>
 RenderCompositorEGL::CreateGLContext(RefPtr<widget::CompositorWidget> aWidget) {
   nsCString discardFailureId;
 
   // Create GLContext with dummy EGLSurface.
   RefPtr<gl::GLContext> gl =
-      //XXX headless context did not work.
+      // XXX headless context did not work.
       gl::GLContextProviderEGL::CreateForCompositorWidget(aWidget, true);
   if (!gl) {
     gfxCriticalNote << "Failed GL context creation for WebRender: "
                     << gfx::hexa(gl.get());
     return nullptr;
   }
 
   if (!gl->MakeCurrent()) {
@@ -65,53 +64,50 @@ RenderCompositorEGL::CreateGLContext(Ref
   if (surface == EGL_NO_SURFACE) {
     gfxCriticalNote << "Failed to create EGLSurface";
   }
   return surface;
 }
 
 RenderCompositorEGL::RenderCompositorEGL(
     RefPtr<gl::GLContext> aGL, RefPtr<widget::CompositorWidget> aWidget)
-    : RenderCompositor(std::move(aWidget)), mGL(aGL), mEGLSurface(EGL_NO_SURFACE) {
+    : RenderCompositor(std::move(aWidget)),
+      mGL(aGL),
+      mEGLSurface(EGL_NO_SURFACE) {
   MOZ_ASSERT(mGL);
 }
 
-RenderCompositorEGL::~RenderCompositorEGL() {
-  DestroyEGLSurface();
-}
+RenderCompositorEGL::~RenderCompositorEGL() { DestroyEGLSurface(); }
 
 bool RenderCompositorEGL::BeginFrame() {
-
-  if (mWidget->AsX11() && mWidget->AsX11()->WaylandRequestsUpdatingEGLSurface()) {
+  if (mWidget->AsX11() &&
+      mWidget->AsX11()->WaylandRequestsUpdatingEGLSurface()) {
     mEGLSurface = CreateEGLSurface(mWidget);
     gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface);
   }
 
   if (!mGL->MakeCurrent()) {
     gfxCriticalNote << "Failed to make render context current, can't draw.";
     return false;
   }
 
   return true;
 }
 
-void RenderCompositorEGL::EndFrame()
-{
+void RenderCompositorEGL::EndFrame() {
   if (mEGLSurface != EGL_NO_SURFACE) {
     mGL->SwapBuffers();
   }
 }
 
 void RenderCompositorEGL::WaitForGPU() {}
 
 void RenderCompositorEGL::Pause() {}
 
-bool RenderCompositorEGL::Resume() {
-  return true;
-}
+bool RenderCompositorEGL::Resume() { return true; }
 
 bool RenderCompositorEGL::MakeCurrent() {
   gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface);
   return gl()->MakeCurrent();
 }
 
 void RenderCompositorEGL::DestroyEGLSurface() {
   auto* egl = gl::GLLibraryEGL::Get();
--- a/gfx/webrender_bindings/RenderCompositorEGL.h
+++ b/gfx/webrender_bindings/RenderCompositorEGL.h
@@ -33,19 +33,19 @@ class RenderCompositorEGL : public Rende
 
   bool MakeCurrent() override;
 
   bool UseANGLE() const override { return false; }
 
   LayoutDeviceIntSize GetBufferSize() override;
 
  protected:
-   static already_AddRefed<gl::GLContext> CreateGLContext(
+  static already_AddRefed<gl::GLContext> CreateGLContext(
       RefPtr<widget::CompositorWidget> aWidget);
-   static EGLSurface CreateEGLSurface(widget::CompositorWidget* aWidget);
+  static EGLSurface CreateEGLSurface(widget::CompositorWidget* aWidget);
 
   void DestroyEGLSurface();
 
   const RefPtr<gl::GLContext> mGL;
   EGLSurface mEGLSurface;
 };
 
 }  // namespace wr
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -328,22 +328,23 @@ struct MOZ_STACK_CLASS StackingContextPa
         aPreserve ? wr::TransformStyle::Preserve3D : wr::TransformStyle::Flat;
   }
 
   nsTArray<wr::FilterOp> mFilters;
   wr::LayoutRect mBounds = wr::ToLayoutRect(LayoutDeviceRect());
   const gfx::Matrix4x4* mBoundTransform = nullptr;
   const gfx::Matrix4x4* mTransformPtr = nullptr;
   Maybe<nsDisplayTransform*> mDeferredTransformItem;
-  // Whether the stacking context is possibly animated. This alters how coordinates
-  // are transformed/snapped to invalidate less when transforms change frequently.
+  // Whether the stacking context is possibly animated. This alters how
+  // coordinates are transformed/snapped to invalidate less when transforms
+  // change frequently.
   bool mAnimated = false;
-  // Whether items should be rasterized in a local space that is (mostly) invariant
-  // to transforms, i.e. disabling subpixel AA and screen space pixel snapping on
-  // text runs that would only make sense in screen space.
+  // Whether items should be rasterized in a local space that is (mostly)
+  // invariant to transforms, i.e. disabling subpixel AA and screen space pixel
+  // snapping on text runs that would only make sense in screen space.
   bool mRasterizeLocally = false;
 };
 
 /// This is a simple C++ wrapper around WrState defined in the rust bindings.
 /// We may want to turn this into a direct wrapper on top of
 /// WebRenderFrameBuilder instead, so the interface may change a bit.
 class DisplayListBuilder {
  public:
--- a/gfx/wr/Cargo.lock
+++ b/gfx/wr/Cargo.lock
@@ -367,17 +367,17 @@ dependencies = [
 
 [[package]]
 name = "direct-composition"
 version = "0.1.0"
 dependencies = [
  "euclid 0.19.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "mozangle 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "webrender 0.59.0",
+ "webrender 0.60.0",
  "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "winit 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "dlib"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1614,17 +1614,17 @@ version = "0.20.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender"
-version = "0.59.0"
+version = "0.60.0"
 dependencies = [
  "app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "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)",
@@ -1652,39 +1652,39 @@ dependencies = [
  "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_profiler 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "webrender_api 0.58.0",
+ "webrender_api 0.60.0",
  "webrender_build 0.0.1",
  "wr_malloc_size_of 0.0.1",
  "ws 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender-examples"
 version = "0.1.0"
 dependencies = [
  "app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.19.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "glutin 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "webrender 0.59.0",
+ "webrender 0.60.0",
  "winit 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender_api"
-version = "0.58.0"
+version = "0.60.0"
 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.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.7 (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)",
  "derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1795,18 +1795,18 @@ dependencies = [
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "mozangle 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "osmesa-src 0.1.1 (git+https://github.com/servo/osmesa-src)",
  "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "webrender 0.59.0",
- "webrender_api 0.58.0",
+ "webrender 0.60.0",
+ "webrender_api 0.60.0",
  "winit 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "ws"
 version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
--- a/gfx/wr/webrender/Cargo.toml
+++ b/gfx/wr/webrender/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender"
-version = "0.59.0"
+version = "0.60.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 description = "A GPU accelerated 2D renderer for web content"
 build = "build.rs"
 
 [features]
 default = ["freetype-lib"]
@@ -41,17 +41,17 @@ png = { optional = true, version = "0.14
 rayon = "1"
 ron = { optional = true, version = "0.1.7" }
 serde = { optional = true, version = "1.0", features = ["serde_derive"] }
 serde_json = { optional = true, version = "1.0" }
 sha2 = "0.8"
 smallvec = "0.6"
 thread_profiler = "0.1.1"
 time = "0.1"
-webrender_api = { version = "0.58.0", path = "../webrender_api" }
+webrender_api = { version = "0.60.0", path = "../webrender_api" }
 webrender_build = { version = "0.0.1", path = "../webrender_build" }
 wr_malloc_size_of = { version = "0.0.1", path = "../wr_malloc_size_of" }
 ws = { optional = true, version = "0.7.3" }
 
 [dependencies.pathfinder_font_renderer]
 git = "https://github.com/pcwalton/pathfinder"
 branch = "webrender"
 optional = true
--- a/gfx/wr/webrender_api/Cargo.toml
+++ b/gfx/wr/webrender_api/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender_api"
-version = "0.58.0"
+version = "0.60.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 description = "Public API for WebRender"
 
 [features]
 nightly = ["euclid/unstable", "serde/unstable"]
 ipc = ["ipc-channel"]
--- a/image/decoders/icon/mac/nsIconChannelCocoa.mm
+++ b/image/decoders/icon/mac/nsIconChannelCocoa.mm
@@ -126,18 +126,17 @@ nsIconChannel::SetOriginalURI(nsIURI* aU
 NS_IMETHODIMP
 nsIconChannel::GetURI(nsIURI** aURI) {
   *aURI = mUrl;
   NS_IF_ADDREF(*aURI);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsIconChannel::Open(nsIInputStream** _retval)
-{
+nsIconChannel::Open(nsIInputStream** _retval) {
   nsCOMPtr<nsIStreamListener> listener;
   nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return MakeInputStream(_retval, false);
 }
 
 nsresult nsIconChannel::ExtractIconInfoFromUrl(nsIFile** aLocalFile, uint32_t* aDesiredImageSize,
@@ -167,32 +166,30 @@ nsresult nsIconChannel::ExtractIconInfoF
 
   *aLocalFile = file;
   NS_IF_ADDREF(*aLocalFile);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsIconChannel::AsyncOpen(nsIStreamListener* aListener)
-{
+nsIconChannel::AsyncOpen(nsIStreamListener* aListener) {
   nsCOMPtr<nsIStreamListener> listener = aListener;
   nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
   if (NS_FAILED(rv)) {
     mCallbacks = nullptr;
     return rv;
   }
 
   MOZ_ASSERT(
       !mLoadInfo || mLoadInfo->GetSecurityMode() == 0 || mLoadInfo->GetInitialSecurityCheckDone() ||
           (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
            nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
       "security flags in loadInfo but doContentSecurityCheck() not called");
 
-
   nsCOMPtr<nsIInputStream> inStream;
   MakeInputStream(getter_AddRefs(inStream), true);
   if (NS_FAILED(rv)) {
     mCallbacks = nullptr;
     return rv;
   }
 
   // Init our stream pump
--- a/image/decoders/icon/win/nsIconChannel.cpp
+++ b/image/decoders/icon/win/nsIconChannel.cpp
@@ -186,17 +186,16 @@ nsresult nsIconChannel::ExtractIconInfoF
 
   nsCOMPtr<nsIFile> file;
   rv = fileURL->GetFile(getter_AddRefs(file));
   if (NS_FAILED(rv) || !file) return NS_OK;
 
   return file->Clone(aLocalFile);
 }
 
-
 NS_IMETHODIMP
 nsIconChannel::AsyncOpen(nsIStreamListener* aListener) {
   nsCOMPtr<nsIStreamListener> listener = aListener;
   nsresult rv =
       nsContentSecurityManager::doContentSecurityCheck(this, listener);
   if (NS_FAILED(rv)) {
     mCallbacks = nullptr;
     return rv;
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -2255,20 +2255,19 @@ nsresult imgLoader::LoadImage(
     nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(newChannel);
     if (timedChannel) {
       timedChannel->SetInitiatorType(initiatorType);
     }
 
     // create the proxy listener
     nsCOMPtr<nsIStreamListener> listener = new ProxyListener(request.get());
 
-    MOZ_LOG(
-        gImgLog, LogLevel::Debug,
-        ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n",
-         this));
+    MOZ_LOG(gImgLog, LogLevel::Debug,
+            ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n",
+             this));
 
     mozilla::net::PredictorLearn(aURI, aInitialDocumentURI,
                                  nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
                                  aLoadGroup);
 
     nsresult openRes = newChannel->AsyncOpen(listener);
 
     if (NS_FAILED(openRes)) {
--- a/intl/l10n/DocumentL10n.cpp
+++ b/intl/l10n/DocumentL10n.cpp
@@ -70,17 +70,17 @@ DocumentL10n::DocumentL10n(Document* aDo
     : mDocument(aDocument), mState(DocumentL10nState::Initialized) {
   mContentSink = do_QueryInterface(aDocument->GetCurrentContentSink());
 }
 
 DocumentL10n::~DocumentL10n() {}
 
 bool DocumentL10n::Init(nsTArray<nsString>& aResourceIds) {
   nsCOMPtr<mozIDOMLocalizationJSM> jsm =
-        do_ImportModule("resource://gre/modules/DOMLocalization.jsm");
+      do_ImportModule("resource://gre/modules/DOMLocalization.jsm");
   MOZ_RELEASE_ASSERT(jsm);
 
   Unused << jsm->GetDOMLocalization(getter_AddRefs(mDOMLocalization));
   MOZ_RELEASE_ASSERT(mDOMLocalization);
 
   nsIGlobalObject* global = mDocument->GetScopeObject();
   if (!global) {
     return false;
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc
@@ -14,17 +14,17 @@
 #include "base/process_util.h"
 #include "base/rand_util.h"
 #include "base/string_util.h"
 #include "base/win_util.h"
 #include "chrome/common/ipc_message_utils.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 
 #ifdef FUZZING
-#include "mozilla/ipc/Faulty.h"
+#  include "mozilla/ipc/Faulty.h"
 #endif
 
 // ChannelImpl is used on the IPC thread, but constructed on a different thread,
 // so it has to hold the nsAutoOwningThread as a pointer, and we need a slightly
 // different macro.
 #ifdef DEBUG
 #  define ASSERT_OWNINGTHREAD(_class)                              \
     if (nsAutoOwningThread* owningThread = _mOwningThread.get()) { \
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -472,17 +472,16 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadI
       aLoadInfo->GetVerifySignedContent(), aLoadInfo->GetEnforceSRI(),
       aLoadInfo->GetForceAllowDataURI(),
       aLoadInfo->GetAllowInsecureRedirectToDataURI(),
       aLoadInfo->GetSkipContentPolicyCheckForWebRequest(),
       aLoadInfo->GetForceInheritPrincipalDropped(),
       aLoadInfo->GetInnerWindowID(), aLoadInfo->GetOuterWindowID(),
       aLoadInfo->GetParentOuterWindowID(), aLoadInfo->GetTopOuterWindowID(),
       aLoadInfo->GetFrameOuterWindowID(), aLoadInfo->GetBrowsingContextID(),
-      aLoadInfo->GetFrameBrowsingContextID(),
       aLoadInfo->GetInitialSecurityCheckDone(),
       aLoadInfo->GetIsInThirdPartyContext(), aLoadInfo->GetIsDocshellReload(),
       aLoadInfo->GetSendCSPViolationEvents(), aLoadInfo->GetOriginAttributes(),
       redirectChainIncludingInternalRedirects, redirectChain,
       ancestorPrincipals, aLoadInfo->AncestorOuterWindowIDs(), ipcClientInfo,
       ipcReservedClientInfo, ipcInitialClientInfo, ipcController,
       aLoadInfo->CorsUnsafeHeaders(), aLoadInfo->GetForcePreflight(),
       aLoadInfo->GetIsPreflight(), aLoadInfo->GetLoadTriggeredFromExternal(),
@@ -630,18 +629,17 @@ nsresult LoadInfoArgsToLoadInfo(
       loadInfoArgs.browserWouldUpgradeInsecureRequests(),
       loadInfoArgs.verifySignedContent(), loadInfoArgs.enforceSRI(),
       loadInfoArgs.forceAllowDataURI(),
       loadInfoArgs.allowInsecureRedirectToDataURI(),
       loadInfoArgs.skipContentPolicyCheckForWebRequest(),
       loadInfoArgs.forceInheritPrincipalDropped(), loadInfoArgs.innerWindowID(),
       loadInfoArgs.outerWindowID(), loadInfoArgs.parentOuterWindowID(),
       loadInfoArgs.topOuterWindowID(), loadInfoArgs.frameOuterWindowID(),
-      loadInfoArgs.browsingContextID(), loadInfoArgs.frameBrowsingContextID(),
-      loadInfoArgs.initialSecurityCheckDone(),
+      loadInfoArgs.browsingContextID(), loadInfoArgs.initialSecurityCheckDone(),
       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(),
--- a/js/public/Id.h
+++ b/js/public/Id.h
@@ -223,16 +223,21 @@ auto MapGCThingTyped(const jsid& id, F&&
   using ReturnType = decltype(f(static_cast<JSString*>(nullptr)));
   return mozilla::Maybe<ReturnType>();
 }
 
 // If the jsid is a GC pointer type, convert to that type and call |f| with the
 // pointer. Return whether this happened.
 template <typename F>
 bool ApplyGCThingTyped(const jsid& id, F&& f) {
-  return MapGCThingTyped(id, [&f](auto t) { f(t); return true; }).isSome();
+  return MapGCThingTyped(id,
+                         [&f](auto t) {
+                           f(t);
+                           return true;
+                         })
+      .isSome();
 }
 
 #undef id
 
 }  // namespace js
 
 #endif /* js_Id_h */
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1274,17 +1274,16 @@ class HeapBase<JS::Value, Wrapper>
     if (arg) {
       setObject(*arg);
     } else {
       setNull();
     }
   }
 };
 
-
 // If the Value is a GC pointer type, call |f| with the pointer cast to that
 // type and return the result wrapped in a Maybe, otherwise return None().
 template <typename F>
 auto MapGCThingTyped(const JS::Value& val, F&& f) {
   if (val.isString()) {
     JSString* str = val.toString();
     MOZ_ASSERT(gc::IsCellPointerValid(str));
     return mozilla::Some(f(str));
@@ -1312,17 +1311,22 @@ auto MapGCThingTyped(const JS::Value& va
   using ReturnType = decltype(f(static_cast<JSObject*>(nullptr)));
   return mozilla::Maybe<ReturnType>();
 }
 
 // If the Value is a GC pointer type, call |f| with the pointer cast to that
 // type. Return whether this happened.
 template <typename F>