Merge mozilla-central to mozilla-inbound. a=merge CLOSED TREE
authorCiure Andrei <aciure@mozilla.com>
Wed, 10 Jul 2019 12:45:37 +0300
changeset 482053 f3a387c13e2c4ca4783f90d00fb20564e3441933
parent 481888 aad99a66114968eaf2c48a60c8fff46322fd4900 (current diff)
parent 482052 6e5088fb8fd6ac3ad4956ec3f623b525f4cdd81a (diff)
child 482054 db01b84083ba6292edf78c512c061cad8aceacb3
push id113647
push useraciure@mozilla.com
push dateWed, 10 Jul 2019 09:46:39 +0000
treeherdermozilla-inbound@f3a387c13e2c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone70.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. a=merge CLOSED TREE
browser/base/content/test/performance/browser_urlbar_keyed_search_legacy.js
browser/base/content/test/performance/browser_urlbar_search_legacy.js
browser/base/content/test/performance/legacyurlbar/browser.ini
browser/base/content/test/webextensions/browser_update_findUpdates.js
browser/components/preferences/in-content/containers.xul
browser/components/preferences/in-content/home.xul
browser/components/preferences/in-content/main.xul
browser/components/preferences/in-content/privacy.xul
browser/components/preferences/in-content/search.xul
browser/components/preferences/in-content/searchResults.xul
browser/components/preferences/in-content/sync.xul
browser/components/urlbar/tests/legacy/.eslintrc.js
browser/components/urlbar/tests/legacy/browser.ini
browser/components/urlbar/tests/legacy/browser_switchtab_copy.js
browser/components/urlbar/tests/legacy/browser_switchtab_override_keynav.js
browser/components/urlbar/tests/legacy/browser_urlbarAddonIframe.js
browser/components/urlbar/tests/legacy/browser_urlbarSearchSuggestions_opt-out.js
browser/components/urlbar/tests/legacy/head.js
devtools/shared/deprecated-sync-thenables.js
docshell/base/nsILinkHandler.h
dom/events/test/test_submitevent_on_form.html
gfx/wr/wrench/reftests/filters/filter-flood-ref.yaml
gfx/wr/wrench/reftests/filters/filter-flood.yaml
js/src/vm/Debugger-inl.h
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/DebuggerMemory.cpp
js/src/vm/DebuggerMemory.h
memory/fallible/fallible.h
memory/fallible/moz.build
testing/web-platform/meta/content-security-policy/inheritance/blob-url-in-main-window-self-navigate-inherits.sub.html.ini
testing/web-platform/meta/html/semantics/links/following-hyperlinks/activation-behavior.window.js.ini
testing/web-platform/meta/webrtc/RTCDataChannel-bufferedAmount.html.ini
testing/web-platform/meta/xhr/access-control-basic-post-with-non-cors-safelisted-content-type.htm.ini
testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/fennec.py
testing/web-platform/tests/workers/semantics/encodings/001.html.headers
testing/web-platform/tests/workers/semantics/encodings/002.html.headers
toolkit/components/certviewer/content/components/certificate-tab.css
toolkit/components/certviewer/content/components/certificate-tab.js
toolkit/components/certviewer/content/components/handshake-section.css
toolkit/components/certviewer/content/components/handshake-section.js
toolkit/mozapps/extensions/test/browser/browser_bug562890.js
toolkit/mozapps/extensions/test/browser/browser_bug562899.js
toolkit/mozapps/extensions/test/browser/browser_bug562992.js
toolkit/mozapps/extensions/test/browser/browser_bug580298.js
toolkit/mozapps/extensions/test/browser/browser_bug587970.js
toolkit/mozapps/extensions/test/browser/browser_bug590347.js
toolkit/mozapps/extensions/test/browser/browser_bug591663.js
toolkit/mozapps/extensions/test/browser/browser_bug618502.js
toolkit/mozapps/extensions/test/browser/browser_details.js
toolkit/mozapps/extensions/test/browser/browser_discovery_clientid.js
toolkit/mozapps/extensions/test/browser/browser_inlinesettings_browser.js
toolkit/mozapps/extensions/test/browser/browser_langpack_signing.js
toolkit/mozapps/extensions/test/browser/browser_legacy.js
toolkit/mozapps/extensions/test/browser/browser_legacy_pre57.js
toolkit/mozapps/extensions/test/browser/browser_list.js
toolkit/mozapps/extensions/test/browser/browser_manualupdates.js
toolkit/mozapps/extensions/test/browser/browser_plugin_enabled_state_locked.js
toolkit/mozapps/extensions/test/browser/browser_pluginprefs_is_not_disabled.js
toolkit/mozapps/extensions/test/browser/browser_sorting.js
toolkit/mozapps/extensions/test/browser/browser_sorting_plugins.js
toolkit/mozapps/extensions/test/browser/browser_tabsettings.js
toolkit/mozapps/extensions/test/browser/browser_theme_previews.js
toolkit/mozapps/extensions/test/browser/browser_types.js
toolkit/mozapps/extensions/test/browser/browser_uninstalling.js
toolkit/mozapps/extensions/test/browser/browser_webext_options.js
toolkit/mozapps/extensions/test/browser/browser_webext_options_addon_reload.js
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,16 +32,20 @@ exclude = [
   # Excluded because they are used only as dependencies, not top-level targets,
   # so we don't need to vendor their dev-dependencies.
   "dom/webauthn/u2f-hid-rs",
   "gfx/webrender_bindings",
   "media/mp4parse-rust/mp4parse",
   "media/mp4parse-rust/mp4parse_capi",
   "media/mp4parse-rust/mp4parse_fallible",
   "xpcom/rust/gkrust_utils",
+
+  # Exclude temporarily till it is developed independently
+  # TODO(nupur): Remove after this is completed
+  "testing/geckodriver/marionette",
 ]
 
 # Explicitly specify what our profiles use.  The opt-level setting here is
 # a total fiction; see the setup of MOZ_RUST_DEFAULT_FLAGS for what the
 # opt-level setting will be as a result of various other configure flags.
 [profile.dev]
 opt-level = 1
 rpath = false
--- a/accessible/tests/browser/events/browser_test_focus_urlbar.js
+++ b/accessible/tests/browser/events/browser_test_focus_urlbar.js
@@ -26,29 +26,20 @@ function isEventForAutocompleteItem(even
 
 /**
  * Wait for an autocomplete search to finish.
  * This is necessary to ensure predictable results, as these searches are
  * async. Pressing down arrow will use results from the previous input if the
  * search isn't finished yet.
  */
 function waitForSearchFinish() {
-  if (UrlbarPrefs.get("quantumbar")) {
-    return Promise.all([
-      gURLBar.lastQueryContextPromise,
-      BrowserTestUtils.waitForCondition(() => gURLBar.view.isOpen),
-    ]);
-  }
-  return BrowserTestUtils.waitForCondition(
-    () =>
-      gURLBar.popupOpen &&
-      gURLBar.controller.searchStatus >=
-        Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH,
-    "Waiting for search to complete"
-  );
+  return Promise.all([
+    gURLBar.lastQueryContextPromise,
+    BrowserTestUtils.waitForCondition(() => gURLBar.view.isOpen),
+  ]);
 }
 
 // Check that the URL bar manages accessibility focus appropriately.
 async function runTests() {
   registerCleanupFunction(async function() {
     await PlacesUtils.history.clear();
   });
 
@@ -127,19 +118,17 @@ async function runTests() {
   event = await focused;
   testStates(event.accessible, STATE_FOCUSED);
 
   info("Ensuring text box focus on left arrow");
   focused = waitForEvent(EVENT_FOCUS, textBox);
   EventUtils.synthesizeKey("KEY_ArrowLeft");
   await focused;
   testStates(textBox, STATE_FOCUSED);
-  if (UrlbarPrefs.get("quantumbar")) {
-    gURLBar.view.close();
-  }
+  gURLBar.view.close();
   // On Mac, down arrow when not at the end of the field moves to the end.
   // Move back to the end so the next press of down arrow opens the popup.
   EventUtils.synthesizeKey("KEY_ArrowRight");
 
   info("Ensuring autocomplete focus on down arrow (2)");
   focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
   EventUtils.synthesizeKey("KEY_ArrowDown");
   event = await focused;
--- a/accessible/tests/browser/general/browser_test_urlbar.js
+++ b/accessible/tests/browser/general/browser_test_urlbar.js
@@ -20,32 +20,20 @@ add_task(async function testAutocomplete
   await UrlbarTestUtils.promiseAutocompleteResultPopup({
     window,
     waitForFocus,
     value: "a",
   });
 
   info("Waiting for accessibility to be created for the richlistbox");
   let resultsView;
-  if (UrlbarPrefs.get("quantumbar")) {
-    resultsView = gURLBar.view.panel.querySelector("#urlbarView-results");
-    await BrowserTestUtils.waitForCondition(() =>
-      accService.getAccessibleFor(resultsView)
-    );
-  } else {
-    let urlbarPopup = document.getElementById("PopupAutoCompleteRichResult");
-    resultsView = document.getAnonymousElementByAttribute(
-      urlbarPopup,
-      "anonid",
-      "richlistbox"
-    );
-    await BrowserTestUtils.waitForCondition(() =>
-      accService.getAccessibleFor(resultsView)
-    );
-  }
+  resultsView = gURLBar.view.panel.querySelector("#urlbarView-results");
+  await BrowserTestUtils.waitForCondition(() =>
+    accService.getAccessibleFor(resultsView)
+  );
 
   info("Confirming that the special case is handled in XULListboxAccessible");
   let accessible = accService.getAccessibleFor(resultsView);
   is(accessible.role, ROLE_COMBOBOX_LIST, "Right role");
 
   BrowserTestUtils.removeTab(tab);
 });
 
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1835,17 +1835,17 @@ pref("app.normandy.run_interval_seconds"
 pref("app.normandy.shieldLearnMoreUrl", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/shield");
 #ifdef MOZ_DATA_REPORTING
 pref("app.shield.optoutstudies.enabled", true);
 #else
 pref("app.shield.optoutstudies.enabled", false);
 #endif
 
 // Multi-lingual preferences
-#ifdef RELEASE_OR_BETA
+#if defined(RELEASE_OR_BETA) && !defined(MOZ_DEV_EDITION)
 pref("intl.multilingual.enabled", true);
 pref("intl.multilingual.downloadEnabled", true);
 #else
 pref("intl.multilingual.enabled", false);
 // AMO only serves language packs for release and beta versions.
 pref("intl.multilingual.downloadEnabled", false);
 #endif
 
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -524,19 +524,16 @@ toolbar:not(#TabsToolbar) > #personal-bo
 #urlbar,
 .searchbar-textbox {
   /* Setting a width and min-width to let the location & search bars maintain
      a constant width in case they haven't be resized manually. (bug 965772) */
   width: 1px;
   min-width: 1px;
 }
 
-#urlbar[quantumbar="false"] {
-  -moz-binding: url(chrome://browser/content/urlbarBindings.xml#legacy-urlbar);
-}
 
 #urlbar[quantumbar="true"] {
   -moz-binding: url(chrome://browser/content/urlbarBindings.xml#urlbar);
 }
 
 /* Display URLs left-to-right but right aligned in RTL mode. */
 html|input.urlbar-input:-moz-locale-dir(rtl) {
   direction: ltr !important;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -295,119 +295,65 @@ XPCOMUtils.defineLazyGetter(this, "gCust
 XPCOMUtils.defineLazyGetter(this, "gNavToolbox", () => {
   return document.getElementById("navigator-toolbox");
 });
 
 XPCOMUtils.defineLazyGetter(this, "gURLBar", () => gURLBarHandler.urlbar);
 
 /**
  * Tracks the urlbar object, allowing to reinitiate it when necessary, e.g. on
- * customization or when the quantumbar pref changes.
+ * customization.
  */
 var gURLBarHandler = {
-  toggleQuantumBarAttribute() {
-    this.textbox = document.getElementById("urlbar");
-    this.textbox.setAttribute("quantumbar", this.quantumbar);
-  },
-
   /**
    * The urlbar binding or object.
    */
   get urlbar() {
     if (!this._urlbar) {
-      if (this.quantumbar) {
-        this._urlbar = new UrlbarInput({ textbox: this.textbox });
-        if (this._lastValue) {
-          this._urlbar.value = this._lastValue;
-          delete this._lastValue;
-        }
-      } else {
-        this._urlbar = this.textbox;
+      let textbox = document.getElementById("urlbar");
+      this._urlbar = new UrlbarInput({ textbox });
+      if (this._lastValue) {
+        this._urlbar.value = this._lastValue;
+        delete this._lastValue;
       }
       gBrowser.tabContainer.addEventListener("TabSelect", this._urlbar);
     }
     return this._urlbar;
   },
 
   /**
-   * Forwards to gURLBar.formatValue(), if the binding has been applied already.
-   * This is necessary until the Quantum Bar is not the default and we allow
-   * to dynamically switch between it and the legacy implementation, because the
-   * binding is only applied before the initial xul layout.
-   */
-  formatValue() {
-    if (this.quantumbar) {
-      this.urlbar.formatValue();
-    } else if (typeof this.textbox.formatValue == "function") {
-      this.textbox.formatValue();
-    }
-  },
-
-  /**
-   * Invoked when the quantumbar pref changes.
-   */
-  handlePrefChange() {
-    this._updateBinding();
-    this._reset();
-  },
-
-  /**
    * Invoked by CustomizationHandler when a customization starts.
    */
   customizeStart() {
-    if (this._urlbar && this._urlbar.constructor.name == "UrlbarInput") {
+    if (this._urlbar) {
       this._urlbar.removeCopyCutController();
     }
   },
 
   /**
    * Invoked by CustomizationHandler when a customization ends.
    */
   customizeEnd() {
     this._reset();
   },
 
   /**
-   * Rebuilds the textbox binding by detaching the element when necessary.
-   */
-  _updateBinding() {
-    let quantumbarApplied = this.textbox.getAttribute("quantumbar") == "true";
-    if (quantumbarApplied != this.quantumbar) {
-      let placeholder = document.createXULElement("toolbarpaletteitem");
-      let parent = this.textbox.parentNode;
-      parent.replaceChild(placeholder, this.textbox);
-      this.textbox.setAttribute("quantumbar", this.quantumbar);
-      parent.replaceChild(this.textbox, placeholder);
-    }
-  },
-
-  /**
    *  Used to reset the gURLBar value.
    */
   _reset() {
     if (this._urlbar) {
       gBrowser.tabContainer.removeEventListener("TabSelect", this._urlbar);
-      if (this._urlbar.constructor.name == "UrlbarInput") {
-        this._lastValue = this._urlbar.value;
-        this._urlbar.uninit();
-      }
+      this._lastValue = this._urlbar.value;
+      this._urlbar.uninit();
       delete this._urlbar;
       gURLBar = this.urlbar;
     }
   },
 };
 
-XPCOMUtils.defineLazyPreferenceGetter(
-  gURLBarHandler,
-  "quantumbar",
-  "browser.urlbar.quantumbar",
-  false,
-  gURLBarHandler.handlePrefChange.bind(gURLBarHandler)
-);
-
 XPCOMUtils.defineLazyGetter(this, "ReferrerInfo", () =>
   Components.Constructor(
     "@mozilla.org/referrer-info;1",
     "nsIReferrerInfo",
     "init"
   )
 );
 
@@ -1669,18 +1615,19 @@ var gBrowserInit = {
 
   // Used to check if the new window is still adopting an existing tab as its first tab
   // (e.g. from the WebExtensions internals).
   isAdoptingTab() {
     return !!this.getTabToAdopt();
   },
 
   onBeforeInitialXULLayout() {
-    // Dynamically switch on-off the Quantum Bar based on prefs.
-    gURLBarHandler.toggleQuantumBarAttribute();
+    // Turn on QuantumBar. This can be removed once the quantumbar attribute is gone.
+    let urlbar = document.getElementById("urlbar");
+    urlbar.setAttribute("quantumbar", true);
 
     // Set a sane starting width/height for all resolutions on new profiles.
     if (Services.prefs.getBoolPref("privacy.resistFingerprinting")) {
       // When the fingerprinting resistance is enabled, making sure that we don't
       // have a maximum window to interfere with generating rounded window dimensions.
       document.documentElement.setAttribute("sizemode", "normal");
     } else if (!document.documentElement.hasAttribute("width")) {
       const TARGET_WIDTH = 1280;
@@ -5833,17 +5780,17 @@ var XULBrowserWindow = {
       gIdentityHandler.refreshIdentityBlock();
       return;
     }
     this._state = aState;
     this._lastLocation = spec;
 
     // Make sure the "https" part of the URL is striked out or not,
     // depending on the current mixed active content blocking state.
-    gURLBarHandler.formatValue();
+    gURLBar.formatValue();
 
     try {
       uri = Services.uriFixup.createExposableURI(uri);
     } catch (e) {}
     gIdentityHandler.updateIdentity(this._state, uri);
   },
 
   // simulate all change notifications after switching tabs
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -437,19 +437,19 @@ nsContextMenu.prototype = {
           label,
         ])
       );
     }
 
     var shouldShow =
       this.onSaveableLink || isMailtoInternal || this.onPlainTextLink;
     var isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
-    var showContainers = Services.prefs.getBoolPref(
-      "privacy.userContext.enabled"
-    );
+    let showContainers =
+      Services.prefs.getBoolPref("privacy.userContext.enabled") &&
+      ContextualIdentityService.getPublicIdentities().length;
     this.showItem("context-openlink", shouldShow && !isWindowPrivate);
     this.showItem(
       "context-openlinkprivate",
       shouldShow && PrivateBrowsingUtils.enabled
     );
     this.showItem("context-openlinkintab", shouldShow && !inContainer);
     this.showItem("context-openlinkincontainertab", shouldShow && inContainer);
     this.showItem(
--- a/browser/base/content/test/contextMenu/browser_contextmenu.js
+++ b/browser/base/content/test/contextMenu/browser_contextmenu.js
@@ -12,17 +12,19 @@ let LOGIN_FILL_ITEMS = [
     "---",
     null,
     "fill-login-saved-passwords",
     true,
   ],
   null,
 ];
 let hasPocket = Services.prefs.getBoolPref("extensions.pocket.enabled");
-let hasContainers = Services.prefs.getBoolPref("privacy.userContext.enabled");
+let hasContainers =
+  Services.prefs.getBoolPref("privacy.userContext.enabled") &&
+  ContextualIdentityService.getPublicIdentities().length;
 
 const example_base =
   "http://example.com/browser/browser/base/content/test/contextMenu/";
 const chrome_base =
   "chrome://mochitests/content/browser/browser/base/content/test/contextMenu/";
 const head_base =
   "chrome://mochitests/content/browser/browser/base/content/test/contextMenu/";
 
--- a/browser/base/content/test/performance/browser_startup_mainthreadio.js
+++ b/browser/base/content/test/performance/browser_startup_mainthreadio.js
@@ -421,23 +421,16 @@ const startupPhases = {
     },
     {
       path: "XREAppFeat:formautofill@mozilla.org.xpi",
       condition: !WIN,
       stat: 1,
       close: 1,
     },
     {
-      // bug 1545167
-      path: "/etc/mime.types",
-      condition: LINUX,
-      read: 1,
-      close: 1,
-    },
-    {
       // We only hit this for new profiles.
       path: "XREAppDist:distribution.ini",
       condition: WIN,
       stat: 1,
     },
     {
       path: "*WindowsApps/microsoft.windowscommunicationsapps*",
       condition: WIN,
deleted file mode 100644
--- a/browser/base/content/test/performance/browser_urlbar_keyed_search_legacy.js
+++ /dev/null
@@ -1,116 +0,0 @@
-"use strict";
-
-// This tests searching in the legacy urlbar implementation (a.k.a. the
-// awesomebar).
-
-// There are a _lot_ of reflows in this test, and processing them takes
-// time. On slower builds, we need to boost our allowed test time.
-requestLongerTimeout(5);
-
-/**
- * WHOA THERE: We should never be adding new things to EXPECTED_REFLOWS. This
- * is a whitelist that should slowly go away as we improve the performance of
- * the front-end. Instead of adding more reflows to the whitelist, you should
- * be modifying your code to avoid the reflow.
- *
- * See https://developer.mozilla.org/en-US/Firefox/Performance_best_practices_for_Firefox_fe_engineers
- * for tips on how to do that.
- */
-
-/* These reflows happen only the first time the awesomebar panel opens. */
-const EXPECTED_REFLOWS_FIRST_OPEN = [];
-if (
-  AppConstants.platform == "linux" ||
-  AppConstants.platform == "win" ||
-  // macOS 10.14 Mojave (Darwin version 18)
-  AppConstants.isPlatformAndVersionAtLeast("macosx", "18")
-) {
-  EXPECTED_REFLOWS_FIRST_OPEN.push({
-    stack: [
-      "__rebuild@chrome://browser/content/search/search-one-offs.js",
-      /* This is limited to a one-line stack, because the next item is an async
-         function and as such not supported on all trees, according to bug 1501761.
-      "async*set popup@chrome://browser/content/search/search-one-offs.js",
-      "_syncOneOffSearchesEnabled@chrome://browser/content/urlbarBindings.xml",
-      "toggleOneOffSearches@chrome://browser/content/urlbarBindings.xml",
-      "_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
-      "@chrome://browser/content/urlbarBindings.xml",
-      "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
-      "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
-      "openPopup@chrome://global/content/bindings/autocomplete.xml",
-      "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",*/
-    ],
-  });
-}
-EXPECTED_REFLOWS_FIRST_OPEN.push(
-  {
-    stack: [
-      "_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
-      "handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
-      "_reuseAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
-      "_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
-      "_invalidate@chrome://global/content/bindings/autocomplete.xml",
-      "invalidate@chrome://global/content/bindings/autocomplete.xml",
-    ],
-    maxCount: 60, // This number should only ever go down - never up.
-  },
-
-  {
-    stack: [
-      "_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
-      "handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
-      "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
-      "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
-      "openPopup@chrome://global/content/bindings/autocomplete.xml",
-      "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
-    ],
-    maxCount: 6, // This number should only ever go down - never up.
-  },
-
-  // Bug 1359989
-  {
-    stack: [
-      "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
-      "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
-      "openPopup@chrome://global/content/bindings/autocomplete.xml",
-      "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
-    ],
-  }
-);
-
-// These extra reflows happen on beta/release as one of the default bookmarks in
-// bookmarks.html.in has a long URL.
-if (AppConstants.RELEASE_OR_BETA) {
-  EXPECTED_REFLOWS_FIRST_OPEN.push(
-    {
-      stack: [
-        "_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
-        "_onUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
-        "MozAutocompleteRichlistitem/<@chrome://global/content/elements/autocomplete-richlistitem.js",
-      ],
-      maxCount: 6,
-    },
-    {
-      stack: [
-        "_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
-        "_onOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
-        "MozAutocompleteRichlistitem/<@chrome://global/content/elements/autocomplete-richlistitem.js",
-      ],
-      maxCount: 6,
-    },
-    {
-      stack: [
-        "_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
-        "_adjustAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
-        "_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
-        "_invalidate@chrome://global/content/bindings/autocomplete.xml",
-        "invalidate@chrome://global/content/bindings/autocomplete.xml",
-      ],
-      maxCount: 12,
-    }
-  );
-}
-
-add_task(async function awesomebar() {
-  await runUrlbarTest(true, true, EXPECTED_REFLOWS_FIRST_OPEN);
-});
deleted file mode 100644
--- a/browser/base/content/test/performance/browser_urlbar_search_legacy.js
+++ /dev/null
@@ -1,113 +0,0 @@
-"use strict";
-
-// This tests searching in the legacy urlbar implementation (a.k.a. the
-// awesomebar).
-
-// There are a _lot_ of reflows in this test, and processing them takes
-// time. On slower builds, we need to boost our allowed test time.
-requestLongerTimeout(5);
-
-/**
- * WHOA THERE: We should never be adding new things to EXPECTED_REFLOWS. This
- * is a whitelist that should slowly go away as we improve the performance of
- * the front-end. Instead of adding more reflows to the whitelist, you should
- * be modifying your code to avoid the reflow.
- *
- * See https://developer.mozilla.org/en-US/Firefox/Performance_best_practices_for_Firefox_fe_engineers
- * for tips on how to do that.
- */
-
-/* These reflows happen only the first time the awesomebar panel opens. */
-const EXPECTED_REFLOWS_FIRST_OPEN = [];
-if (
-  AppConstants.platform == "linux" ||
-  AppConstants.platform == "win" ||
-  // macOS 10.14 Mojave (Darwin version 18)
-  AppConstants.isPlatformAndVersionAtLeast("macosx", "18")
-) {
-  EXPECTED_REFLOWS_FIRST_OPEN.push({
-    stack: [
-      "__rebuild@chrome://browser/content/search/search-one-offs.js",
-      /* This is limited to a one-line stack, because the next item is an async
-         function and as such not supported on all trees, according to bug 1501761.
-      "async*set popup@chrome://browser/content/search/search-one-offs.js",
-      "_syncOneOffSearchesEnabled@chrome://browser/content/urlbarBindings.xml",
-      "toggleOneOffSearches@chrome://browser/content/urlbarBindings.xml",
-      "_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
-      "@chrome://browser/content/urlbarBindings.xml",
-      "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
-      "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
-      "openPopup@chrome://global/content/bindings/autocomplete.xml",
-      "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",*/
-    ],
-  });
-}
-EXPECTED_REFLOWS_FIRST_OPEN.push(
-  {
-    stack: [
-      "_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
-      "handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
-      "_reuseAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
-      "_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
-      "_invalidate@chrome://global/content/bindings/autocomplete.xml",
-      "invalidate@chrome://global/content/bindings/autocomplete.xml",
-    ],
-    maxCount: 36, // This number should only ever go down - never up.
-  },
-
-  {
-    stack: [
-      "_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
-      "handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
-      "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
-      "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
-      "openPopup@chrome://global/content/bindings/autocomplete.xml",
-      "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
-    ],
-    maxCount: 6, // This number should only ever go down - never up.
-  },
-
-  // Bug 1359989
-  {
-    stack: [
-      "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
-      "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
-      "openPopup@chrome://global/content/bindings/autocomplete.xml",
-      "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
-    ],
-  }
-);
-
-/* These reflows happen everytime the awesomebar panel opens. */
-const EXPECTED_REFLOWS_SECOND_OPEN = [
-  {
-    stack: [
-      "_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
-      "handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
-      "_reuseAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
-      "_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
-      "_invalidate@chrome://global/content/bindings/autocomplete.xml",
-      "invalidate@chrome://global/content/bindings/autocomplete.xml",
-    ],
-    maxCount: 24, // This number should only ever go down - never up.
-  },
-
-  // Bug 1359989
-  {
-    stack: [
-      "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
-      "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
-      "openPopup@chrome://global/content/bindings/autocomplete.xml",
-      "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
-    ],
-  },
-];
-
-add_task(async function awesomebar() {
-  await runUrlbarTest(
-    true,
-    false,
-    EXPECTED_REFLOWS_FIRST_OPEN,
-    EXPECTED_REFLOWS_SECOND_OPEN
-  );
-});
deleted file mode 100644
--- a/browser/base/content/test/performance/legacyurlbar/browser.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[DEFAULT]
-prefs =
-  browser.urlbar.quantumbar=false
-
-[../browser_urlbar_keyed_search_legacy.js]
-skip-if = (os == 'linux') || (os == 'win' && debug) || (os == 'win' && bits == 32) # Disabled on Linux and Windows debug due to perma failures. Bug 1392320. Disabled on Win32 because of intermittent OOM failures (bug 1448241).
-[../browser_urlbar_search_legacy.js]
-skip-if = (debug || ccov) && (os == 'linux' || os == 'win') || (os == 'win' && bits == 32) # Disabled on Linux and Windows debug and ccov due to intermittent timeouts. Bug 1414126, bug 1426611. Disabled on Win32 because of intermittent OOM failures (bug 1448241)
--- a/browser/base/content/test/tabs/browser_tabCloseSpacer.js
+++ b/browser/base/content/test/tabs/browser_tabCloseSpacer.js
@@ -47,30 +47,40 @@ add_task(async function() {
     "using spacer"
   );
   is(downButton.clientWidth, 0, "down button has no width");
   isnot(closingTabsSpacer.clientWidth, 0, "spacer has some width");
 });
 
 async function overflowTabs() {
   let arrowScrollbox = gBrowser.tabContainer.arrowScrollbox;
+  const originalSmoothScroll = arrowScrollbox.smoothScroll;
+  arrowScrollbox.smoothScroll = false;
+  registerCleanupFunction(() => {
+    arrowScrollbox.smoothScroll = originalSmoothScroll;
+  });
+
   let width = ele => ele.getBoundingClientRect().width;
   let tabMinWidth = parseInt(
     getComputedStyle(gBrowser.selectedTab, null).minWidth
   );
   let tabCountForOverflow = Math.ceil(
     (width(arrowScrollbox) / tabMinWidth) * 1.1
   );
   while (gBrowser.tabs.length < tabCountForOverflow) {
     BrowserTestUtils.addTab(gBrowser, "about:blank", {
       skipAnimation: true,
       index: 0,
     });
   }
-  await window.promiseDocumentFlushed(() => {});
+
+  // Make sure scrolling finished.
+  await new Promise(resolve => {
+    arrowScrollbox.addEventListener("scrollend", resolve, { once: true });
+  });
 }
 
 function getLastCloseButton() {
   let lastTab = gBrowser.tabs[gBrowser.tabs.length - 1];
   return lastTab.closeButton;
 }
 
 function getLastCloseButtonLocation() {
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -51,1822 +51,16 @@ file, You can obtain one at http://mozil
                  class="urlbar-history-dropmarker urlbar-icon chromeclass-toolbar-additional"
                  tooltiptext="&urlbar.openHistoryPopup.tooltip;"
                  allowevents="true"
                  xbl:inherits="open,parentfocused=focused,usertyping"/>
       <children includes="hbox"/>
     </content>
   </binding>
 
-  <binding id="legacy-urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
-    <content newlines="stripsurroundingwhitespace"
-             type="autocomplete"
-             autocompletesearch="unifiedcomplete"
-             autocompletesearchparam="enable-actions"
-             autocompletepopup="PopupAutoCompleteRichResult"
-             completeselectedindex="true"
-             tabscrolling="true"
-             ontextentered="this.handleCommand(param);"
-             ontextreverted="return this.handleRevert();">
-      <children includes="box"/>
-      <xul:moz-input-box anonid="moz-input-box"
-                         tooltip="aHTMLTooltip"
-                         class="urlbar-input-box"
-                         flex="1">
-        <children/>
-        <html:input anonid="scheme"
-                    class="urlbar-scheme textbox-input"
-                    required="required"
-                    xbl:inherits="textoverflow,focused"/>
-        <html:input anonid="input"
-                    class="urlbar-input textbox-input"
-                    allowevents="true"
-                    inputmode="mozAwesomebar"
-                    xbl:inherits="value,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,focused,textoverflow"/>
-      </xul:moz-input-box>
-      <xul:image anonid="urlbar-go-button"
-                 class="urlbar-go-button urlbar-icon"
-                 onclick="gURLBar.handleCommand(event);"
-                 tooltiptext="&goEndCap.tooltip;"
-                 xbl:inherits="pageproxystate,parentfocused=focused,usertyping"/>
-      <xul:image anonid="historydropmarker"
-                 class="urlbar-history-dropmarker urlbar-icon chromeclass-toolbar-additional"
-                 tooltiptext="&urlbar.openHistoryPopup.tooltip;"
-                 allowevents="true"
-                 xbl:inherits="open,parentfocused=focused,usertyping"/>
-      <children includes="hbox"/>
-    </content>
-
-    <implementation implements="nsIObserver">
-      <field name="ExtensionSearchHandler" readonly="true">
-        (ChromeUtils.import("resource://gre/modules/ExtensionSearchHandler.jsm", {})).ExtensionSearchHandler;
-      </field>
-
-      <constructor><![CDATA[
-        // UrlbarInput compatibility shims
-        this.document = document;
-        this.window = window;
-        this.textbox = this;
-
-        this._prefs = Cc["@mozilla.org/preferences-service;1"]
-                        .getService(Ci.nsIPrefService)
-                        .getBranch("browser.urlbar.");
-        this._prefs.addObserver("", this);
-
-        this._defaultPrefs = Cc["@mozilla.org/preferences-service;1"]
-                               .getService(Ci.nsIPrefService)
-                               .getDefaultBranch("browser.urlbar.");
-
-        Services.prefs.addObserver("browser.search.suggest.enabled", this);
-        this.browserSearchSuggestEnabled = Services.prefs.getBoolPref("browser.search.suggest.enabled");
-
-        this.openInTab = this._prefs.getBoolPref("openintab");
-        this.clickSelectsAll = this._prefs.getBoolPref("clickSelectsAll");
-        this.doubleClickSelectsAll = this._prefs.getBoolPref("doubleClickSelectsAll");
-        this.completeDefaultIndex = this._prefs.getBoolPref("autoFill");
-        this.urlbarSearchSuggestEnabled = this._prefs.getBoolPref("suggest.searches");
-        this.timeout = this._prefs.getIntPref("delay");
-        this._mayTrimURLs = this._prefs.getBoolPref("trimURLs");
-        this._adoptIntoActiveWindow = this._prefs.getBoolPref("switchTabs.adoptIntoActiveWindow");
-        this._ctrlCanonizesURLs = this._prefs.getBoolPref("ctrlCanonizesURLs");
-        this.inputField.controllers.insertControllerAt(0, this._copyCutController);
-        this.inputField.addEventListener("paste", this);
-        this.inputField.addEventListener("mousedown", this);
-        this.inputField.addEventListener("mouseover", this);
-        this.inputField.addEventListener("overflow", this);
-        this.inputField.addEventListener("underflow", this);
-        this.inputField.addEventListener("scrollend", this);
-        window.addEventListener("resize", this);
-
-        var textBox = document.getAnonymousElementByAttribute(this,
-                                                "anonid", "moz-input-box");
-        // Force the Custom Element to upgrade until Bug 1470242 handles this:
-        customElements.upgrade(textBox);
-        var cxmenu = textBox.menupopup;
-        var pasteAndGo;
-        cxmenu.addEventListener("popupshowing", function() {
-          if (!pasteAndGo)
-            return;
-          var controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
-          var enabled = controller.isCommandEnabled("cmd_paste");
-          if (enabled)
-            pasteAndGo.removeAttribute("disabled");
-          else
-            pasteAndGo.setAttribute("disabled", "true");
-        });
-
-        var insertLocation = cxmenu.firstElementChild;
-        while (insertLocation.nextElementSibling &&
-               insertLocation.getAttribute("cmd") != "cmd_paste")
-          insertLocation = insertLocation.nextElementSibling;
-        if (insertLocation) {
-          pasteAndGo = document.createXULElement("menuitem");
-          let label = Services.strings.createBundle("chrome://browser/locale/browser.properties").
-                                   GetStringFromName("pasteAndGo.label");
-          pasteAndGo.setAttribute("label", label);
-          pasteAndGo.setAttribute("anonid", "paste-and-go");
-          pasteAndGo.setAttribute("oncommand",
-              "gURLBar.select(); goDoCommand('cmd_paste'); gURLBar.handleCommand();");
-          cxmenu.insertBefore(pasteAndGo, insertLocation.nextElementSibling);
-        }
-
-        this.popup.addEventListener("popupshowing", () => {
-          this._enableOrDisableOneOffSearches();
-        }, {capture: true, once: true});
-
-        // history dropmarker open state
-        this.popup.addEventListener("popupshowing", () => {
-          this.setAttribute("open", "true");
-        });
-        this.popup.addEventListener("popuphidden", () => {
-          requestAnimationFrame(() => {
-            this.removeAttribute("open");
-          });
-        });
-      ]]></constructor>
-
-      <destructor><![CDATA[
-        // Somehow, it's possible for the XBL destructor to fire without the
-        // constructor ever having fired. Fix:
-        if (!this._prefs) {
-          return;
-        }
-        this._prefs.removeObserver("", this);
-        this._prefs = null;
-        Services.prefs.removeObserver("browser.search.suggest.enabled", this);
-        try {
-          this.inputField.controllers.removeController(this._copyCutController);
-        } catch (ex) {
-          // Sometimes this fails for unclear reasons; anyway we want to
-          // continue cleaning up.
-          Cu.reportError(ex);
-        }
-        this.inputField.removeEventListener("paste", this);
-        this.inputField.removeEventListener("mousedown", this);
-        this.inputField.removeEventListener("mouseover", this);
-        this.inputField.removeEventListener("overflow", this);
-        this.inputField.removeEventListener("underflow", this);
-        this.inputField.removeEventListener("scrollend", this);
-        window.removeEventListener("resize", this);
-
-        if (this._deferredKeyEventTimeout) {
-          clearTimeout(this._deferredKeyEventTimeout);
-          this._deferredKeyEventTimeout = null;
-        }
-
-        // Null out the one-offs' popup and textbox so that it cleans up its
-        // internal state for both.  Most importantly, it removes the event
-        // listeners that it added to both.
-        this.popup.oneOffSearchButtons.popup = null;
-        this.popup.oneOffSearchButtons.textbox = null;
-
-        this.valueFormatter.uninit();
-      ]]></destructor>
-
-      <field name="valueFormatter" readonly="true">
-        new UrlbarValueFormatter(this);
-      </field>
-
-      <field name="goButton">
-        document.getAnonymousElementByAttribute(this, "anonid", "urlbar-go-button");
-      </field>
-
-      <field name="_value">""</field>
-      <field name="gotResultForCurrentQuery">false</field>
-
-      <!--
-        This is set around HandleHenter so it can be used in handleCommand.
-        It is also used to track whether we must handle a delayed handleEnter,
-        by checking if it has been cleared.
-      -->
-      <field name="handleEnterInstance">null</field>
-
-      <!--
-        Since we never want scrollbars, we always use the maxResults value.
-      -->
-      <property name="maxRows"
-                onget="return this.popup.maxResults;"/>
-
-      <!--
-        Set by focusAndSelectUrlBar to indicate whether the next focus event was
-        initiated by an explicit user action. See the "focus" handler below.
-      -->
-      <field name="userInitiatedFocus">false</field>
-
-      <!--
-        onBeforeValueGet is called by the base-binding's .value getter.
-        It can return an object with a "value" property, to override the
-        return value of the getter.
-      -->
-      <method name="onBeforeValueGet">
-        <body><![CDATA[
-          return { value: this._value };
-        ]]></body>
-      </method>
-
-      <!--
-        onBeforeValueSet is called by the base-binding's .value setter.
-        It should return the value that the setter should use.
-      -->
-      <method name="onBeforeValueSet">
-        <parameter name="aValue"/>
-        <body><![CDATA[
-          this._value = aValue;
-          var returnValue = aValue;
-          var action = this._parseActionUrl(aValue);
-
-          if (action) {
-            switch (action.type) {
-              case "switchtab": // Fall through.
-              case "remotetab": // Fall through.
-              case "visiturl": {
-                returnValue = action.params.displayUrl;
-                break;
-              }
-              case "keyword": // Fall through.
-              case "searchengine": {
-                returnValue = action.params.input;
-                break;
-              }
-              case "extension": {
-                returnValue = action.params.content;
-                break;
-              }
-            }
-          } else {
-            let originalUrl = ReaderMode.getOriginalUrlObjectForDisplay(aValue);
-            if (originalUrl) {
-              returnValue = originalUrl.displaySpec;
-            }
-          }
-
-          // Set the actiontype only if the user is not overriding actions.
-          if (action && this._pressedNoActionKeys.size == 0) {
-            this.setAttribute("actiontype", action.type);
-          } else {
-            this.removeAttribute("actiontype");
-          }
-          return returnValue;
-        ]]></body>
-      </method>
-
-      <method name="onKeyPress">
-        <parameter name="aEvent"/>
-        <parameter name="aOptions"/>
-        <body><![CDATA[
-          switch (aEvent.keyCode) {
-            case KeyEvent.DOM_VK_LEFT:
-            case KeyEvent.DOM_VK_RIGHT:
-            case KeyEvent.DOM_VK_HOME:
-              // Reset the selected index so that nsAutoCompleteController
-              // simply closes the popup without trying to fill anything.
-              this.popup.selectedIndex = -1;
-              break;
-            case KeyEvent.DOM_VK_TAB:
-              this.userSelectionBehavior = "tab";
-              // The user is explicitly making a selection, so the popup
-              // should get accessibility focus.
-              this.popup.richlistbox.suppressMenuItemEvent = false;
-              break;
-            case KeyEvent.DOM_VK_UP:
-            case KeyEvent.DOM_VK_DOWN:
-            case KeyEvent.DOM_VK_PAGE_UP:
-            case KeyEvent.DOM_VK_PAGE_DOWN:
-              if (this.userSelectionBehavior != "tab")
-                this.userSelectionBehavior = "arrow";
-              // The user is explicitly making a selection, so the popup
-              // should get accessibility focus.
-              this.popup.richlistbox.suppressMenuItemEvent = false;
-              break;
-          }
-
-          if (AppConstants.platform == "macosx") {
-            switch (aEvent.key) {
-              case "n":
-              case "p":
-                if (aEvent.ctrlKey) {
-                  // The user is explicitly making a selection, so the popup
-                  // should get accessibility focus.
-                  this.popup.richlistbox.suppressMenuItemEvent = false;
-                }
-                break;
-            }
-          }
-
-          let noDefer = aOptions && aOptions.noDefer;
-          if (!noDefer && this._shouldDeferKeyEvent(aEvent)) {
-            this._deferKeyEvent(aEvent, "onKeyPress");
-            return false;
-          }
-          if (this.popup.popupOpen && this.popup.handleKeyPress(aEvent)) {
-            return true;
-          }
-          return this.handleKeyPress(aEvent, aOptions);
-        ]]></body>
-      </method>
-
-      <!--
-        Search results arrive asynchronously, which means that keypresses may
-        arrive before results do and therefore not have the effect the user
-        intends.  That's especially likely to happen with the down arrow and
-        enter keys due to the one-off search buttons: if the user very quickly
-        pastes something in the input, presses the down arrow key, and then hits
-        enter, they are probably expecting to visit the first result.  But if
-        there are no results, then pressing down and enter will trigger the
-        first one-off button.  To prevent that undesirable behavior, certain
-        keys are buffered and deferred until more results arrive, at which time
-        they're replayed.
-
-        @param  event
-                The key event that should maybe be deferred.
-        @return True if the event should be deferred, false if not.
-       -->
-      <method name="_shouldDeferKeyEvent">
-        <parameter name="event"/>
-        <body><![CDATA[
-          // If any event has been deferred for this search, then defer all
-          // subsequent events so that the user does not experience any
-          // keypresses out of order.  All events will be replayed when
-          // _deferredKeyEventTimeout fires.
-          if (this._deferredKeyEventQueue.length) {
-            return true;
-          }
-
-          // At this point, no events have been deferred for this search, and we
-          // need to decide whether `event` is the first one that should be.
-          if (!this._keyCodesToDefer.has(event.keyCode) &&
-              !(/Mac/.test(navigator.platform) &&
-                event.ctrlKey &&
-                (event.key === "n" || event.key === "p") &&
-                this.popupOpen)) {
-            // Not a key that should trigger deferring.
-            return false;
-          }
-
-          let waitedLongEnough =
-            this._searchStartDate + this._deferredKeyEventTimeoutMs <= Cu.now();
-          if (waitedLongEnough) {
-            // This is a key that we would defer, but enough time has passed
-            // since the start of the search that we don't want to block the
-            // user's keypresses anymore.
-            return false;
-          }
-
-          if (event.keyCode == KeyEvent.DOM_VK_TAB && !this.popupOpen) {
-            // The popup is closed and the user pressed the Tab key.  The
-            // focus should move out of the urlbar immediately.
-            return false;
-          }
-
-          return !this._safeToPlayDeferredKeyEvent(event);
-        ]]></body>
-      </method>
-
-      <!--
-        Returns true if the given deferred key event can be played now without
-        possibly surprising the user.  This depends on the state of the popup,
-        its results, and the type of keypress.  Use this method only after
-        determining that the event should be deferred, or after it's already
-        been deferred and you want to know if it can be played now.
-
-        @param  event
-                The key event.
-        @return True if the event can be played, false if not.
-      -->
-      <method name="_safeToPlayDeferredKeyEvent">
-        <parameter name="event"/>
-        <body><![CDATA[
-          if (event.keyCode == KeyEvent.DOM_VK_RETURN) {
-            return this.popup.selectedIndex != 0 ||
-                   this.gotResultForCurrentQuery;
-          }
-
-          if (!this.gotResultForCurrentQuery || !this.popupOpen) {
-            // We're still waiting on the first result, or the popup hasn't
-            // opened yet, so not safe.
-            return false;
-          }
-
-          let maxResultsRemaining =
-            this.popup.maxResults - this.popup.matchCount;
-          if (maxResultsRemaining == 0) {
-            // The popup can't possibly have any more results, so there's no
-            // need to defer any event now.
-            return true;
-          }
-
-          if (event.keyCode == KeyEvent.DOM_VK_DOWN) {
-            // Don't play the event if the last result is selected so that the
-            // user doesn't accidentally arrow down into the one-off buttons
-            // when they didn't mean to.
-            let lastResultSelected =
-              this.popup.selectedIndex + 1 == this.popup.matchCount;
-            return !lastResultSelected;
-          }
-
-          return true;
-        ]]></body>
-      </method>
-
-      <!--
-        Adds a key event to the deferred event queue.
-
-        @param event
-               The key event to defer.
-        @param methodName
-               The name of the method on `this` to call.  It's expected to take
-               two arguments: the event, and an optional options object:
-               {
-                  noDefer: If true, then the event is being replayed and it
-                           should not be deferred again.
-               }
-      -->
-      <method name="_deferKeyEvent">
-        <parameter name="event"/>
-        <parameter name="methodName"/>
-        <body><![CDATA[
-          // Somehow event.defaultPrevented ends up true for deferred events.
-          // autocomplete ignores defaultPrevented events, which means it would
-          // ignore replayed deferred events if we didn't tell it to bypass
-          // defaultPrevented.  That's the purpose of this expando.  If we could
-          // figure out what's setting defaultPrevented and prevent it, then we
-          // could get rid of this.
-          if (event.urlbarDeferred) {
-            throw new Error("Key event already deferred!");
-          }
-          event.urlbarDeferred = true;
-
-          this._deferredKeyEventQueue.push({
-            methodName,
-            event,
-            searchString: this.mController.searchString,
-          });
-
-          if (!this._deferredKeyEventTimeout) {
-            // Start the timeout that will unconditionally replay all deferred
-            // events when it fires so that, after a certain point, we don't
-            // keep blocking the user's keypresses when nothing else has caused
-            // the events to be replayed.  Do not check whether it's safe to
-            // replay the events because otherwise it may look like we ignored
-            // the user's input.
-            let elapsed = Cu.now() - this._searchStartDate;
-            let remaining = this._deferredKeyEventTimeoutMs - elapsed;
-            this._deferredKeyEventTimeout = setTimeout(() => {
-              this.replayAllDeferredKeyEvents();
-              this._deferredKeyEventTimeout = null;
-            }, Math.max(0, remaining));
-          }
-        ]]></body>
-      </method>
-
-      <!-- The enter key is always deferred, so it's not included here. -->
-      <field name="_keyCodesToDefer">new Set([
-        KeyboardEvent.DOM_VK_RETURN,
-        KeyboardEvent.DOM_VK_DOWN,
-        KeyboardEvent.DOM_VK_TAB,
-      ])</field>
-      <field name="_deferredKeyEventQueue">[]</field>
-      <field name="_deferredKeyEventTimeout">null</field>
-      <field name="_deferredKeyEventTimeoutMs">200</field>
-      <field name="_searchStartDate">0</field>
-
-      <method name="replaySafeDeferredKeyEvents">
-        <body><![CDATA[
-          if (!this._deferredKeyEventQueue.length) {
-            return;
-          }
-          let instance = this._deferredKeyEventQueue[0];
-          if (!this._safeToPlayDeferredKeyEvent(instance.event)) {
-            return;
-          }
-          this._deferredKeyEventQueue.shift();
-          this._replayKeyEventInstance(instance);
-          Services.tm.dispatchToMainThread(() => {
-            this.replaySafeDeferredKeyEvents();
-          });
-        ]]></body>
-      </method>
-
-      <!--
-        Unconditionally replays all deferred key events.  This does not check
-        whether it's safe to replay the events; use replaySafeDeferredKeyEvents
-        for that.  Use this method when you must replay all events so that it
-        does not appear that we ignored the user's input.
-      -->
-      <method name="replayAllDeferredKeyEvents">
-        <body><![CDATA[
-          let instance = this._deferredKeyEventQueue.shift();
-          if (!instance) {
-            return;
-          }
-          this._replayKeyEventInstance(instance);
-          Services.tm.dispatchToMainThread(() => {
-            this.replayAllDeferredKeyEvents();
-          });
-        ]]></body>
-      </method>
-
-      <method name="_replayKeyEventInstance">
-        <parameter name="instance"/>
-        <body><![CDATA[
-          // Safety check: handle only if the search string didn't change.
-          if (this.mController.searchString == instance.searchString) {
-            this[instance.methodName](instance.event, {noDefer: true});
-          }
-        ]]></body>
-      </method>
-
-      <field name="_mayTrimURLs">true</field>
-      <method name="trimValue">
-        <parameter name="aURL"/>
-        <body><![CDATA[
-          // This method must not modify the given URL such that calling
-          // nsIURIFixup::createFixupURI with the result will produce a different URI.
-          return this._mayTrimURLs ? BrowserUtils.trimURL(aURL) : aURL;
-        ]]></body>
-      </method>
-
-      <!--
-        This method tries to apply styling to the text in the input, depending
-        on the text.  See the _format* methods.
-      -->
-      <method name="formatValue">
-        <body><![CDATA[
-          // The editor may not exist if the toolbar is not visible.
-          if (this.editor) {
-            this.valueFormatter.update();
-          }
-        ]]></body>
-      </method>
-
-      <method name="handleRevert">
-        <body><![CDATA[
-          var isScrolling = this.popupOpen;
-
-          gBrowser.userTypedValue = null;
-
-          // don't revert to last valid url unless page is NOT loading
-          // and user is NOT key-scrolling through autocomplete list
-          if (!XULBrowserWindow.isBusy && !isScrolling) {
-            URLBarSetURI(null, true);
-
-            // If the value isn't empty and the urlbar has focus, select the value.
-            if (this.value && this.hasAttribute("focused"))
-              this.select();
-          }
-
-          // tell widget to revert to last typed text only if the user
-          // was scrolling when they hit escape
-          return !isScrolling;
-        ]]></body>
-      </method>
-
-      <method name="_whereToOpen">
-        <parameter name="event"/>
-        <body><![CDATA[
-          let isMouseEvent = event instanceof MouseEvent;
-          let reuseEmpty = !isMouseEvent;
-          let where = undefined;
-          if (!isMouseEvent && event && event.altKey) {
-            // We support using 'alt' to open in a tab, because ctrl/shift
-            // might be used for canonizing URLs:
-            where = event.shiftKey ? "tabshifted" : "tab";
-          } else if (!isMouseEvent && this._ctrlCanonizesURLs && event && event.ctrlKey) {
-            // If we're allowing canonization, and this is a key event with ctrl
-            // pressed, open in current tab to allow ctrl-enter to canonize URL.
-            where = "current";
-          } else {
-            where = whereToOpenLink(event, false, false);
-          }
-          if (this.openInTab) {
-            if (where == "current") {
-              where = "tab";
-            } else if (where == "tab") {
-              where = "current";
-            }
-            reuseEmpty = true;
-          }
-          if (where == "tab" && reuseEmpty && gBrowser.selectedTab.isEmpty) {
-            where = "current";
-          }
-          return where;
-        ]]></body>
-      </method>
-
-      <!--
-        This is ultimately called by the autocomplete controller as the result
-        of handleEnter when the Return key is pressed in the textbox.  Since
-        onPopupClick also calls handleEnter, this is also called as a result in
-        that case.
-
-        @param event
-               The event that triggered the command.
-        @param openUILinkWhere
-               Optional.  The "where" to pass to openTrustedLinkIn.  This method
-               computes the appropriate "where" given the event, but you can
-               use this to override it.
-        @param openUILinkParams
-               Optional.  The parameters to pass to openTrustedLinkIn.  As with
-               "where", this method computes the appropriate parameters, but
-               any parameters you supply here will override those.
-      -->
-      <method name="handleCommand">
-        <parameter name="event"/>
-        <parameter name="openUILinkWhere"/>
-        <parameter name="openUILinkParams"/>
-        <parameter name="triggeringPrincipal"/>
-        <body><![CDATA[
-          let isMouseEvent = event instanceof MouseEvent;
-          if (isMouseEvent && event.button == 2) {
-            // Do nothing for right clicks.
-            return;
-          }
-
-          // Determine whether to use the selected one-off search button.  In
-          // one-off search buttons parlance, "selected" means that the button
-          // has been navigated to via the keyboard.  So we want to use it if
-          // the triggering event is not a mouse click -- i.e., it's a Return
-          // key -- or if the one-off was mouse-clicked.
-          let selectedOneOff = this.popup.oneOffSearchButtons.selectedButton;
-          if (selectedOneOff &&
-              isMouseEvent &&
-              event.originalTarget != selectedOneOff) {
-            selectedOneOff = null;
-          }
-
-          // Do the command of the selected one-off if it's not an engine.
-          if (selectedOneOff && !selectedOneOff.engine) {
-            selectedOneOff.doCommand();
-            return;
-          }
-
-          let where = openUILinkWhere || this._whereToOpen(event);
-
-          let url = this.value;
-          if (!url) {
-            return;
-          }
-
-          BrowserUsageTelemetry.recordLegacyUrlbarSelectedResultMethod(
-            event, this.userSelectionBehavior);
-
-          let mayInheritPrincipal = false;
-          let postData = null;
-          let browser = gBrowser.selectedBrowser;
-          let action = this._parseActionUrl(url);
-
-          if (selectedOneOff && selectedOneOff.engine) {
-            // If there's a selected one-off button then load a search using
-            // the one-off's engine.
-            [url, postData] =
-              this._parseAndRecordSearchEngineLoad(selectedOneOff.engine,
-                                                   this.oneOffSearchQuery,
-                                                   event);
-          } else if (action) {
-            switch (action.type) {
-              case "visiturl":
-                // Unifiedcomplete uses fixupURI to tell if something is a visit
-                // or a search, and passes out the fixedURI as the url param.
-                // By using that uri we would end up passing a different string
-                // to the docshell that may run a different not-found heuristic.
-                // For example, "mozilla/run" would be fixed by unifiedcomplete
-                // to "http://mozilla/run". The docshell, once it can't resolve
-                // mozilla, would note the string has a scheme, and try to load
-                // http://mozilla.com/run instead of searching "mozilla/run".
-                // So, if we have the original input at hand, we pass it through
-                // and let the docshell handle it.
-                if (action.params.input) {
-                  url = action.params.input;
-                  break;
-                }
-                url = action.params.url;
-                break;
-              case "remotetab":
-                url = action.params.url;
-                break;
-              case "keyword":
-                if (action.params.postData) {
-                  postData = UrlbarUtils.getPostDataStream(action.params.postData);
-                }
-                mayInheritPrincipal = true;
-                url = action.params.url;
-                break;
-              case "switchtab":
-                url = action.params.url;
-                if (this.hasAttribute("actiontype")) {
-                  this.handleRevert();
-                  let prevTab = gBrowser.selectedTab;
-                  let loadOpts = {
-                    adoptIntoActiveWindow: this._adoptIntoActiveWindow,
-                  };
-
-                  if (switchToTabHavingURI(url, false, loadOpts) &&
-                      prevTab.isEmpty) {
-                    gBrowser.removeTab(prevTab);
-                  }
-                  return;
-                }
-
-                // Once we get here, we got a switchtab action but the user
-                // bypassed it by pressing shift/meta/ctrl. Those modifiers
-                // might otherwise affect where we open - we always want to
-                // open in the current tab.
-                where = "current";
-                break;
-              case "searchengine":
-                if (selectedOneOff && selectedOneOff.engine) {
-                  // Replace the engine with the selected one-off engine.
-                  action.params.engineName = selectedOneOff.engine.name;
-                }
-                // If the selected result is an @alias offer -- an @alias with
-                // an empty query string -- then instead of loading the engine's
-                // empty search results page, put the @alias in the input so
-                // that the user can type a search query and search directly
-                // from the urlbar.
-                if (action.params.alias &&
-                    action.params.alias.startsWith("@") &&
-                    !action.params.searchQuery) {
-                  this.search(action.params.input);
-                  return;
-                }
-                const actionDetails = {
-                  isSuggestion: !!action.params.searchSuggestion,
-                  alias: action.params.alias,
-                };
-                [url, postData] = this._parseAndRecordSearchEngineLoad(
-                  action.params.engineName,
-                  action.params.searchSuggestion || action.params.searchQuery,
-                  event,
-                  actionDetails
-                );
-                break;
-              case "extension":
-                this.handleRevert();
-                // Give the extension control of handling the command.
-                let searchString = action.params.content;
-                let keyword = action.params.keyword;
-                this.ExtensionSearchHandler.handleInputEntered(keyword, searchString, where);
-                return;
-            }
-          } else {
-            // This is a fallback for add-ons and old testing code that directly
-            // set value and try to confirm it. UnifiedComplete should always
-            // resolve to a valid url.
-            try {
-              url = url.trim();
-              new URL(url);
-            } catch (ex) {
-              let lastLocationChange = browser.lastLocationChange;
-              UrlbarUtils.getShortcutOrURIAndPostData(url).then(data => {
-                if (where != "current" ||
-                    browser.lastLocationChange == lastLocationChange) {
-                  this._loadURL(data.url, browser, data.postData, where,
-                                openUILinkParams, data.mayInheritPrincipal,
-                                triggeringPrincipal);
-                }
-              });
-              return;
-            }
-          }
-
-          this._loadURL(url, browser, postData, where, openUILinkParams,
-                        mayInheritPrincipal, triggeringPrincipal);
-        ]]></body>
-      </method>
-
-      <property name="oneOffSearchQuery">
-        <getter><![CDATA[
-          // If the user has selected a search suggestion, chances are they
-          // want to use the one off search engine to search for that suggestion,
-          // not the string that they manually entered into the location bar.
-          let action = this._parseActionUrl(this.value);
-          if (action && action.type == "searchengine") {
-            return action.params.input;
-          }
-          // this.textValue may be an autofilled string.  Search only with the
-          // portion that the user typed, if any, by preferring the autocomplete
-          // controller's searchString (including handleEnterInstance.searchString).
-          return this.handleEnterSearchString ||
-                 this.mController.searchString ||
-                 this.textValue;
-        ]]></getter>
-      </property>
-
-      <method name="_loadURL">
-        <parameter name="url"/>
-        <parameter name="browser"/>
-        <parameter name="postData"/>
-        <parameter name="openUILinkWhere"/>
-        <parameter name="openUILinkParams"/>
-        <parameter name="mayInheritPrincipal"/>
-        <parameter name="triggeringPrincipal"/>
-        <body><![CDATA[
-          this.value = url;
-          browser.userTypedValue = url;
-          if (gInitialPages.includes(url)) {
-            browser.initialPageLoadedFromUserAction = url;
-          }
-          try {
-            UrlbarUtils.addToUrlbarHistory(url, window);
-          } catch (ex) {
-            // Things may go wrong when adding url to session history,
-            // but don't let that interfere with the loading of the url.
-            Cu.reportError(ex);
-          }
-
-          // Reset DOS mitigations for the basic auth prompt.
-          // TODO: When bug 1498553 is resolved, we should be able to
-          // remove the !triggeringPrincipal condition here.
-          if (!triggeringPrincipal || triggeringPrincipal.isSystemPrincipal) {
-            delete browser.authPromptAbuseCounter;
-          }
-
-          let params = {
-            postData,
-            allowThirdPartyFixup: true,
-            triggeringPrincipal,
-          };
-          if (openUILinkWhere == "current") {
-            params.targetBrowser = browser;
-            params.indicateErrorPageLoad = true;
-            params.allowPinnedTabHostChange = true;
-            params.allowPopups = url.startsWith("javascript:");
-          } else {
-            params.initiatingDoc = document;
-          }
-          params.allowInheritPrincipal = mayInheritPrincipal;
-
-          if (openUILinkParams) {
-            for (let key in openUILinkParams) {
-              params[key] = openUILinkParams[key];
-            }
-          }
-
-          // Focus the content area before triggering loads, since if the load
-          // occurs in a new tab, we want focus to be restored to the content
-          // area when the current tab is re-selected.
-          browser.focus();
-
-          if (openUILinkWhere != "current") {
-            this.handleRevert();
-          }
-
-          try {
-            openTrustedLinkIn(url, openUILinkWhere, params);
-          } catch (ex) {
-            // This load can throw an exception in certain cases, which means
-            // we'll want to replace the URL with the loaded URL:
-            if (ex.result != Cr.NS_ERROR_LOAD_SHOWED_ERRORPAGE) {
-              this.handleRevert();
-            }
-          }
-
-          // Ensure the start of the URL is visible for usability reasons.
-          this.selectionStart = this.selectionEnd = 0;
-        ]]></body>
-      </method>
-
-      <method name="_parseAndRecordSearchEngineLoad">
-        <parameter name="engineOrEngineName"/>
-        <parameter name="query"/>
-        <parameter name="event"/>
-        <parameter name="searchActionDetails"/>
-        <body><![CDATA[
-          let engine =
-            typeof(engineOrEngineName) == "string" ?
-              Services.search.getEngineByName(engineOrEngineName) :
-              engineOrEngineName;
-          let isOneOff = this.popup.oneOffSearchButtons
-              .maybeRecordTelemetry(event);
-          // Infer the type of the event which triggered the search.
-          let eventType = "unknown";
-          if (event instanceof KeyboardEvent) {
-            eventType = "key";
-          } else if (event instanceof MouseEvent) {
-            eventType = "mouse";
-          }
-          // Augment the search action details object.
-          let details = searchActionDetails || {};
-          details.isOneOff = isOneOff;
-          details.type = eventType;
-
-          BrowserSearch.recordSearchInTelemetry(engine, "urlbar", details);
-          let submission = engine.getSubmission(query, null, "keyword");
-          return [submission.uri.spec, submission.postData];
-        ]]></body>
-      </method>
-
-      <method name="maybeCanonizeURL">
-        <parameter name="aTriggeringEvent"/>
-        <parameter name="aUrl"/>
-        <body><![CDATA[
-          // Only add the suffix when the URL bar value isn't already "URL-like",
-          // and only if we get a keyboard event, to match user expectations.
-          if (!/^\s*[^.:\/\s]+(?:\/.*|\s*)$/i.test(aUrl) ||
-              !this._ctrlCanonizesURLs ||
-              !(aTriggeringEvent instanceof KeyboardEvent) ||
-              !aTriggeringEvent.ctrlKey) {
-            return;
-          }
-
-          let suffix = Services.prefs.getCharPref("browser.fixup.alternate.suffix", ".com/");
-          if (!suffix.endsWith("/")) {
-            suffix += "/";
-          }
-
-          // trim leading/trailing spaces (bug 233205)
-          let url = aUrl.trim();
-
-          // Tack www. and suffix on.  If user has appended directories, insert
-          // suffix before them (bug 279035).  Be careful not to get two slashes.
-          let firstSlash = url.indexOf("/");
-          if (firstSlash >= 0) {
-            url = url.substring(0, firstSlash) + suffix +
-                  url.substring(firstSlash + 1);
-          } else {
-            url = url + suffix;
-          }
-
-          this.popup.overrideValue = "http://www." + url;
-        ]]></body>
-      </method>
-
-      <method name="_updateUrlTooltip">
-        <body><![CDATA[
-          if (this.focused || !this._inOverflow) {
-            this.inputField.removeAttribute("title");
-          } else {
-            this.inputField.setAttribute("title", this.value);
-          }
-        ]]></body>
-      </method>
-
-      <!-- Returns:
-           null if there's a security issue and we should do nothing.
-           a URL object if there is one that we're OK with loading,
-           a text value otherwise.
-           -->
-      <method name="_getDroppableItem">
-        <parameter name="aEvent"/>
-        <body><![CDATA[
-          let links;
-          try {
-            links = browserDragAndDrop.dropLinks(aEvent);
-          } catch (ex) {
-            // this is possibly a security exception, in which case we should return
-            // null. Always return null because we can't *know* what exception is
-            // being returned.
-            return null;
-          }
-          // The URL bar automatically handles inputs with newline characters,
-          // so we can get away with treating text/x-moz-url flavours as text/plain.
-          if (links.length > 0 && links[0].url) {
-            let triggeringPrincipal = browserDragAndDrop.getTriggeringPrincipal(aEvent);
-            aEvent.preventDefault();
-            let url = links[0].url;
-            let strippedURL = UrlbarUtils.stripUnsafeProtocolOnPaste(url);
-            if (strippedURL != url) {
-              aEvent.stopImmediatePropagation();
-              return null;
-            }
-            let urlObj;
-            try {
-              // If this throws, urlSecurityCheck would also throw, as that's what it
-              // does with things that don't pass the IO service's newURI constructor
-              // without fixup. It's conceivable we may want to relax this check in
-              // the future (so e.g. www.foo.com gets fixed up), but not right now.
-              urlObj = new URL(url);
-              // If we succeed, try to pass security checks. If this works, return the
-              // URL object. If the *security checks* fail, return null.
-              try {
-                urlSecurityCheck(url,
-                                 triggeringPrincipal,
-                                 Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
-                return urlObj;
-              } catch (ex) {
-                return null;
-              }
-            } catch (ex) {
-              // We couldn't make a URL out of this. Continue on, and return text below.
-            }
-          }
-          return aEvent.dataTransfer.getData("text/unicode");
-        ]]></body>
-      </method>
-
-      <method name="onDragOver">
-        <parameter name="aEvent"/>
-        <body><![CDATA[
-          if (!this._getDroppableItem(aEvent)) {
-            aEvent.dataTransfer.dropEffect = "none";
-          }
-        ]]></body>
-      </method>
-
-      <method name="onDrop">
-        <parameter name="aEvent"/>
-        <body><![CDATA[
-          let droppedItem = this._getDroppableItem(aEvent);
-          let droppedURL = droppedItem instanceof URL ? droppedItem.href : droppedItem;
-          if (droppedURL && (droppedURL !== gBrowser.currentURI.spec)) {
-            let triggeringPrincipal = browserDragAndDrop.getTriggeringPrincipal(aEvent);
-            this.value = droppedURL;
-            SetPageProxyState("invalid");
-            this.focus();
-            this.handleCommand(null, undefined, undefined, triggeringPrincipal);
-            // Force not showing the dropped URI immediately.
-            gBrowser.userTypedValue = null;
-            URLBarSetURI(null, true);
-          }
-        ]]></body>
-      </method>
-
-      <method name="makeURIReadable">
-        <parameter name="aURI"/>
-        <body>
-          <![CDATA[
-            // Avoid copying 'about:reader?url=', and always provide the original URI:
-            // Reader mode ensures we call createExposableURI itself.
-            let readerStrippedURI = ReaderMode.getOriginalUrlObjectForDisplay(aURI.displaySpec);
-            if (readerStrippedURI) {
-              aURI = readerStrippedURI;
-            } else {
-              // Only copy exposable URIs
-              try {
-                aURI = Services.uriFixup.createExposableURI(aURI);
-              } catch (ex) {}
-            }
-            return aURI;
-          ]]>
-        </body>
-      </method>
-
-      <method name="_getSelectedValueForClipboard">
-        <body><![CDATA[
-          // Grab the actual input field's value, not our value, which could
-          // include "moz-action:".
-          var 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;
-          }
-
-          // If the selection doesn't start at the beginning or doesn't span the
-          // full domain or the URL bar is modified or there is no text at all,
-          // 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, "");
-            if (remainder != "" && remainder[0] != "/")
-              return selectedVal;
-          }
-
-          // If the value was filled by a search suggestion, just return it.
-          let action = this._parseActionUrl(this.value);
-          if (action && action.type == "searchengine")
-            return selectedVal;
-
-          let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
-
-          let uri;
-          if (this.getAttribute("pageproxystate") == "valid") {
-            uri = gBrowser.currentURI;
-          } else {
-            // We're dealing with an autocompleted value, create a new URI from that.
-            try {
-              uri = uriFixup.createFixupURI(inputVal, Ci.nsIURIFixup.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") &&
-              !Services.prefs.getBoolPref("browser.urlbar.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);
-          if (spec != trimmedSpec) {
-            // Prepend the portion that trimValue removed from the beginning.
-            // This assumes trimValue will only truncate the URL at
-            // the beginning or end (or both).
-            let trimmedSegments = spec.split(trimmedSpec);
-            selectedVal = trimmedSegments[0] + selectedVal;
-          }
-
-          return selectedVal;
-        ]]></body>
-      </method>
-
-      <field name="_copyCutController"><![CDATA[
-        ({
-          urlbar: this,
-          doCommand(aCommand) {
-            var urlbar = this.urlbar;
-            var val = urlbar._getSelectedValueForClipboard();
-            if (!val)
-              return;
-
-            if (aCommand == "cmd_cut" && this.isCommandEnabled(aCommand)) {
-              let start = urlbar.selectionStart;
-              let end = urlbar.selectionEnd;
-              urlbar.inputField.value = urlbar.inputField.value.substring(0, start) +
-                                        urlbar.inputField.value.substring(end);
-              urlbar.selectionStart = urlbar.selectionEnd = start;
-
-              let event = document.createEvent("UIEvents");
-              event.initUIEvent("input", true, false, window, 0);
-              urlbar.inputField.dispatchEvent(event);
-            }
-
-            Cc["@mozilla.org/widget/clipboardhelper;1"]
-              .getService(Ci.nsIClipboardHelper)
-              .copyString(val);
-          },
-          supportsCommand(aCommand) {
-            switch (aCommand) {
-              case "cmd_copy":
-              case "cmd_cut":
-                return true;
-            }
-            return false;
-          },
-          isCommandEnabled(aCommand) {
-            return this.supportsCommand(aCommand) &&
-                   (aCommand != "cmd_cut" || !this.urlbar.readOnly) &&
-                   this.urlbar.selectionStart < this.urlbar.selectionEnd;
-          },
-          onEvent(aEventName) {},
-        })
-      ]]></field>
-
-      <method name="observe">
-        <parameter name="aSubject"/>
-        <parameter name="aTopic"/>
-        <parameter name="aData"/>
-        <body><![CDATA[
-          if (aTopic == "nsPref:changed") {
-            switch (aData) {
-              case "clickSelectsAll":
-              case "doubleClickSelectsAll":
-                this[aData] = this._prefs.getBoolPref(aData);
-                break;
-              case "autoFill":
-                this.completeDefaultIndex = this._prefs.getBoolPref(aData);
-                break;
-              case "delay":
-                this.timeout = this._prefs.getIntPref(aData);
-                break;
-              case "ctrlCanonizesURLs":
-                this._ctrlCanonizesURLs = this._prefs.getBoolPref(aData);
-                break;
-              case "openintab":
-                this.openInTab = this._prefs.getBoolPref(aData);
-                break;
-              case "browser.search.suggest.enabled":
-                this.browserSearchSuggestEnabled = Services.prefs.getBoolPref(aData);
-                break;
-              case "suggest.searches":
-                this.urlbarSearchSuggestEnabled = this._prefs.getBoolPref(aData);
-              case "userMadeSearchSuggestionsChoice":
-                // Mirror the value for future use, see the comment in the
-                // binding's constructor.
-                this._prefs.setBoolPref("searchSuggestionsChoice",
-                  this.urlbarSearchSuggestEnabled);
-                // Clear the cached value to allow changing conditions in tests.
-                delete this._whichSearchSuggestionsNotification;
-                break;
-              case "trimURLs":
-                this._mayTrimURLs = this._prefs.getBoolPref(aData);
-                break;
-              case "oneOffSearches":
-                this._enableOrDisableOneOffSearches();
-                break;
-              case "maxRichResults":
-                this.popup.maxResults = this._prefs.getIntPref(aData);
-                break;
-              case "switchTabs.adoptIntoActiveWindow":
-                this._adoptIntoActiveWindow =
-                  this._prefs.getBoolPref("switchTabs.adoptIntoActiveWindow");
-                break;
-            }
-          }
-        ]]></body>
-      </method>
-
-      <method name="_enableOrDisableOneOffSearches">
-        <body><![CDATA[
-          this.popup.toggleOneOffSearches(
-            this._prefs.getBoolPref("oneOffSearches"),
-            "pref"
-          );
-        ]]></body>
-      </method>
-
-      <method name="handleEvent">
-        <parameter name="aEvent"/>
-        <body><![CDATA[
-          switch (aEvent.type) {
-            case "paste":
-              let originalPasteData = aEvent.clipboardData.getData("text/plain");
-              if (!originalPasteData) {
-                return;
-              }
-
-              let oldValue = this.inputField.value;
-              let oldStart = oldValue.substring(0, this.inputField.selectionStart);
-              // If there is already non-whitespace content in the URL bar
-              // preceding the pasted content, it's not necessary to check
-              // protocols used by the pasted content:
-              if (oldStart.trim()) {
-                return;
-              }
-              let oldEnd = oldValue.substring(this.inputField.selectionEnd);
-
-              let pasteData = UrlbarUtils.stripUnsafeProtocolOnPaste(originalPasteData);
-              if (originalPasteData != pasteData) {
-                // Unfortunately we're not allowed to set the bits being pasted
-                // so cancel this event:
-                aEvent.preventDefault();
-                aEvent.stopImmediatePropagation();
-
-                this.inputField.value = oldStart + pasteData + oldEnd;
-                // Fix up cursor/selection:
-                let newCursorPos = oldStart.length + pasteData.length;
-                this.inputField.selectionStart = newCursorPos;
-                this.inputField.selectionEnd = newCursorPos;
-              }
-              break;
-            case "mousedown":
-              if (this.doubleClickSelectsAll &&
-                  aEvent.button == 0 && aEvent.detail == 2) {
-                this.editor.selectAll();
-                aEvent.preventDefault();
-              }
-              break;
-            case "mouseover":
-              this._updateUrlTooltip();
-              break;
-            case "overflow": {
-              const targetIsPlaceholder =
-                !aEvent.originalTarget.classList.contains("anonymous-div");
-              // We only care about the non-placeholder text.
-              // This shouldn't be needed, see bug 1487036.
-              if (targetIsPlaceholder) {
-                break;
-              }
-              this._inOverflow = true;
-              this.updateTextOverflow();
-              break;
-            }
-            case "underflow": {
-              const targetIsPlaceholder =
-                !aEvent.originalTarget.classList.contains("anonymous-div");
-              // We only care about the non-placeholder text.
-              // This shouldn't be needed, see bug 1487036.
-              if (targetIsPlaceholder) {
-                break;
-              }
-              this._inOverflow = false;
-              this.updateTextOverflow();
-              this._updateUrlTooltip();
-              break;
-            }
-            case "scrollend":
-              this.updateTextOverflow();
-              break;
-            case "TabSelect":
-              // The autocomplete controller uses heuristic on some internal caches
-              // to handle cases like backspace, autofill or repeated searches.
-              // Ensure to clear those internal caches when switching tabs.
-              this.controller.resetInternalState();
-              break;
-            case "resize":
-              if (aEvent.target == window) {
-                // Close the popup since it would be wrongly sized, we'll
-                // recalculate a proper size on reopening. For example, this may
-                // happen when using special OS resize functions like Win+Arrow.
-                this.closePopup();
-              }
-              break;
-          }
-        ]]></body>
-      </method>
-
-      <method name="updateTextOverflow">
-        <body><![CDATA[
-          if (this._inOverflow) {
-            window.promiseDocumentFlushed(() => {
-              // Check overflow again to ensure it didn't change in the meanwhile.
-              let input = this.inputField;
-              if (input && this._inOverflow) {
-                let side = input.scrollLeft &&
-                           input.scrollLeft == input.scrollLeftMax ? "start" : "end";
-                window.requestAnimationFrame(() => {
-                  // And check once again, since we might have stopped overflowing
-                  // since the promiseDocumentFlushed callback fired.
-                  if (this._inOverflow) {
-                    this.setAttribute("textoverflow", side);
-                  }
-                });
-              }
-            });
-          } else {
-            this.removeAttribute("textoverflow");
-          }
-        ]]></body>
-      </method>
-
-      <!--
-        onBeforeTextValueGet is called by the base-binding's .textValue getter.
-        It should return the value that the getter should use.
-      -->
-      <method name="onBeforeTextValueGet">
-        <body><![CDATA[
-          return { value: this.inputField.value };
-        ]]></body>
-      </method>
-
-      <!--
-        onBeforeTextValueSet is called by the base-binding's .textValue setter.
-        It should return the value that the setter should use.
-      -->
-      <method name="onBeforeTextValueSet">
-        <parameter name="aValue"/>
-        <body><![CDATA[
-          let val = aValue;
-          let uri;
-          try {
-            uri = makeURI(val);
-          } catch (ex) {}
-
-          if (uri) {
-            // Do not touch moz-action URIs at all.  They depend on being
-            // properly encoded and decoded and will break if decoded
-            // unexpectedly.
-            if (!this._parseActionUrl(val)) {
-              val = losslessDecodeURI(uri);
-            }
-          }
-
-          return val;
-        ]]></body>
-      </method>
-
-      <method name="_parseActionUrl">
-        <parameter name="aUrl"/>
-        <body><![CDATA[
-          const MOZ_ACTION_REGEX = /^moz-action:([^,]+),(.*)$/;
-          if (!MOZ_ACTION_REGEX.test(aUrl))
-            return null;
-
-          // URL is in the format moz-action:ACTION,PARAMS
-          // Where PARAMS is a JSON encoded object.
-          let [, type, params] = aUrl.match(MOZ_ACTION_REGEX);
-
-          let action = {
-            type,
-          };
-
-          action.params = JSON.parse(params);
-          for (let key in action.params) {
-            action.params[key] = decodeURIComponent(action.params[key]);
-          }
-
-          if ("url" in action.params) {
-            let uri;
-            try {
-              uri = makeURI(action.params.url);
-              action.params.displayUrl = losslessDecodeURI(uri);
-            } catch (e) {
-              action.params.displayUrl = action.params.url;
-            }
-          }
-
-          return action;
-        ]]></body>
-      </method>
-
-      <property name="_noActionKeys" readonly="true">
-        <getter><![CDATA[
-          if (!this.__noActionKeys) {
-            this.__noActionKeys = new Set([
-              KeyEvent.DOM_VK_ALT,
-              KeyEvent.DOM_VK_SHIFT,
-            ]);
-            let modifier = AppConstants.platform == "macosx" ?
-                           KeyEvent.DOM_VK_META :
-                           KeyEvent.DOM_VK_CONTROL;
-            this.__noActionKeys.add(modifier);
-          }
-          return this.__noActionKeys;
-        ]]></getter>
-      </property>
-
-      <field name="_pressedNoActionKeys"><![CDATA[
-        new Set()
-      ]]></field>
-
-      <method name="_clearNoActions">
-        <parameter name="aURL"/>
-        <body><![CDATA[
-          this._pressedNoActionKeys.clear();
-          this.popup.removeAttribute("noactions");
-          let action = this._parseActionUrl(this._value);
-          if (action)
-            this.setAttribute("actiontype", action.type);
-        ]]></body>
-      </method>
-
-      <method name="onInput">
-        <parameter name="aEvent"/>
-        <body><![CDATA[
-          if (!this.mIgnoreInput && this.mController.input == this) {
-            this._value = this.inputField.value;
-            gBrowser.userTypedValue = this.value;
-            this.valueIsTyped = true;
-            if (this.inputField.value) {
-              this.setAttribute("usertyping", "true");
-            } else {
-              this.removeAttribute("usertyping");
-            }
-            // If the popup already had accessibility focus, bring it back to
-            // the input, since the user is editing.
-            if (!this.popup.richlistbox.suppressMenuItemEvent &&
-                this.popup.richlistbox.currentItem) {
-              this.popup.richlistbox._fireEvent(
-                this.popup.richlistbox.currentItem, "DOMMenuItemInactive");
-            }
-            // The user is typing, so don't give accessibility focus to the
-            // popup, even if an item gets automatically selected.
-            this.popup.richlistbox.suppressMenuItemEvent = true;
-            // Only wait for a result when we are sure to get one.  In some
-            // cases, like when pasting the same exact text, we may not fire
-            // a new search and we won't get a result.
-            this._onInputHandledText = this.mController.handleText();
-            if (this._onInputHandledText) {
-              this.gotResultForCurrentQuery = false;
-              this._searchStartDate = Cu.now();
-              this._deferredKeyEventQueue = [];
-              if (this._deferredKeyEventTimeout) {
-                clearTimeout(this._deferredKeyEventTimeout);
-                this._deferredKeyEventTimeout = null;
-              }
-            }
-          }
-          this.resetActionType();
-        ]]></body>
-      </method>
-
-      <method name="handleEnter">
-        <parameter name="event"/>
-        <parameter name="options"/>
-        <body><![CDATA[
-          // We need to ensure we're using a selected autocomplete result.
-          // A result should automatically be selected by default,
-          // however autocomplete is async and therefore we may not
-          // have a result set relating to the current input yet. If that
-          // happens, we need to mark that when the first result does get added,
-          // it needs to be handled as if enter was pressed with that first
-          // result selected.
-          // If anything other than the default (first) result is selected, then
-          // it must have been manually selected by the human. We let this
-          // explicit choice be used, even if it may be related to a previous
-          // input.
-          // However, if the default result is automatically selected, we
-          // ensure that it corresponds to the current input.
-
-          // Store the current search string so it can be used in handleCommand,
-          // which will be called as a result of mController.handleEnter().
-          this.handleEnterSearchString = this.mController.searchString;
-
-          let noDefer = options && options.noDefer;
-          if (!noDefer && this._shouldDeferKeyEvent(event)) {
-            // Defer the event until the first non-heuristic result comes in.
-            this._deferKeyEvent(event, "handleEnter");
-            return false;
-          }
-
-          let canonizeValue = this.value;
-          if (event.ctrlKey) {
-            let action = this._parseActionUrl(canonizeValue);
-            if (action && "searchSuggestion" in action.params) {
-              canonizeValue = action.params.searchSuggestion;
-            } else if (this.popup.selectedIndex === 0 &&
-                       this.mController.getStyleAt(0).includes("autofill")) {
-              canonizeValue = this.handleEnterSearchString;
-            }
-          }
-          this.maybeCanonizeURL(event, canonizeValue);
-          let handled = this.mController.handleEnter(false, event);
-          this.handleEnterSearchString = null;
-          this.popup.overrideValue = null;
-          return handled;
-        ]]></body>
-      </method>
-
-      <method name="handleDelete">
-        <body><![CDATA[
-          // If the heuristic result is selected, then the autocomplete
-          // controller's handleDelete implementation will remove it, which is
-          // not what we want.  So in that case, call handleText so it acts as
-          // a backspace on the text value instead of removing the result.
-          if (this.popup.selectedIndex == 0 &&
-              this.popup._isFirstResultHeuristic) {
-            this.mController.handleText();
-            return false;
-          }
-          return this.mController.handleDelete();
-        ]]></body>
-      </method>
-
-      <property name="_userMadeSearchSuggestionsChoice" readonly="true">
-        <getter><![CDATA[
-          return this._prefs.getBoolPref("userMadeSearchSuggestionsChoice") ||
-                 this._defaultPrefs.getBoolPref("suggest.searches") != this._prefs.getBoolPref("suggest.searches");
-        ]]></getter>
-      </property>
-
-      <property name="whichSearchSuggestionsNotification" readonly="true">
-        <getter><![CDATA[
-          // Once we return "none" once, we'll always return "none".
-          // If available, use the cached value, rather than running all of the
-          // checks again at every locationbar focus.
-          if (this._whichSearchSuggestionsNotification) {
-            return this._whichSearchSuggestionsNotification;
-          }
-
-          if (this.browserSearchSuggestEnabled && !this.inPrivateContext &&
-              // In any case, if the user made a choice we should not nag him.
-              !this._userMadeSearchSuggestionsChoice) {
-            if (this._defaultPrefs.getBoolPref("suggest.searches") &&
-                this.urlbarSearchSuggestEnabled && // Has not been switched off.
-                this._prefs.getIntPref("timesBeforeHidingSuggestionsHint")) {
-              return "opt-out";
-            }
-          }
-          return this._whichSearchSuggestionsNotification = "none";
-        ]]></getter>
-      </property>
-
-      <method name="updateSearchSuggestionsNotificationImpressions">
-        <parameter name="whichNotification"/>
-        <body><![CDATA[
-          if (whichNotification == "none") {
-            throw new Error("Unexpected notification type");
-          }
-
-          let remaining = this._prefs.getIntPref("timesBeforeHidingSuggestionsHint");
-          if (remaining > 0) {
-            this._prefs.setIntPref("timesBeforeHidingSuggestionsHint", remaining - 1);
-          }
-        ]]></body>
-      </method>
-
-      <method name="maybeShowSearchSuggestionsNotificationOnFocus">
-        <parameter name="mouseFocused"/>
-        <body><![CDATA[
-          let whichNotification = this.whichSearchSuggestionsNotification;
-          if (this._showSearchSuggestionNotificationOnMouseFocus &&
-              mouseFocused) {
-            // Force showing the opt-out notification.
-            this._whichSearchSuggestionsNotification = whichNotification = "opt-out";
-          }
-          if (whichNotification == "opt-out") {
-            try {
-              this.popup.openAutocompletePopup(this, this);
-            } finally {
-              if (mouseFocused) {
-                delete this._whichSearchSuggestionsNotification;
-                this._showSearchSuggestionNotificationOnMouseFocus = false;
-              }
-            }
-          }
-        ]]></body>
-      </method>
-
-      <!--
-        Sets the input's value, starts a search, and opens the popup.
-
-        @param  value
-                The input's value will be set to this value, and the search will
-                use it as its query.
-      -->
-      <method name="search">
-        <parameter name="value"/>
-        <body><![CDATA[
-          // Hide the suggestions notification if the search uses an "@engine"
-          // search engine alias.
-          if (value.trim()[0] == "@") {
-            let which = this.whichSearchSuggestionsNotification;
-            this._whichSearchSuggestionsNotification = "none";
-            this.popup.addEventListener("popuphidden", () => {
-              this._whichSearchSuggestionsNotification = which;
-            }, {once: true});
-          }
-
-          // We want the value to be treated as text that the user typed.  It
-          // should go through the controller.handleText() path in onInput() so
-          // that gBrowser.userTypedValue, this.valueIsTyped, etc. are set and
-          // nsAutoCompleteController::HandleText() is called.  Set this.value
-          // and fire an input event to do that.  (If we set this.textValue we'd
-          // get an input event for free, but it would also set mIgnoreInput,
-          // skipping all of the above requirements.)
-          focusAndSelectUrlBar();
-
-          // If the value is a restricted token, append a space.
-          if (Object.values(UrlbarTokenizer.RESTRICT).includes(value)) {
-            this.inputField.value = value + " ";
-          } else {
-            this.inputField.value = value;
-          }
-
-          // Avoid selecting the text if this method is called twice in a row.
-          this.selectionStart = -1;
-
-          let event = document.createEvent("Events");
-          event.initEvent("input", true, true);
-          this.inputField.dispatchEvent(event);
-
-          // handleText() ignores the value if it's the same as the previous
-          // value, but we want consecutive searches with the same value to be
-          // possible.  If handleText() returned false, then manually start a
-          // new search here.
-          if (!this._onInputHandledText) {
-            this.gotResultForCurrentQuery = false;
-            this.controller.startSearch(value);
-          }
-        ]]></body>
-      </method>
-
-      <method name="removeHiddenFocus">
-        <body><![CDATA[
-          this.classList.remove("hidden-focus");
-        ]]></body>
-      </method>
-
-      <method name="setHiddenFocus">
-        <body><![CDATA[
-          this.classList.add("hidden-focus");
-          this.focus();
-        ]]></body>
-      </method>
-    </implementation>
-
-    <handlers>
-      <handler event="keydown"><![CDATA[
-        if (this._noActionKeys.has(event.keyCode) &&
-            this.popup.selectedIndex >= 0 &&
-            !this._pressedNoActionKeys.has(event.keyCode)) {
-          if (this._pressedNoActionKeys.size == 0) {
-            this.popup.setAttribute("noactions", "true");
-            this.removeAttribute("actiontype");
-          }
-          this._pressedNoActionKeys.add(event.keyCode);
-        }
-      ]]></handler>
-
-      <handler event="keyup"><![CDATA[
-        if (this._noActionKeys.has(event.keyCode) &&
-            this._pressedNoActionKeys.has(event.keyCode)) {
-          this._pressedNoActionKeys.delete(event.keyCode);
-          if (this._pressedNoActionKeys.size == 0)
-            this._clearNoActions();
-        }
-      ]]></handler>
-
-      <handler event="mousedown"><![CDATA[
-        if (event.button == 0) {
-          if (event.originalTarget.getAttribute("anonid") == "historydropmarker") {
-            this.toggleHistoryPopup();
-          }
-
-          // Eventually show the opt-out notification even if the location bar is
-          // empty, focused, and the user clicks on it.
-          if (this.focused && this.textValue == "") {
-            this.maybeShowSearchSuggestionsNotificationOnFocus(true);
-          }
-        }
-      ]]></handler>
-
-      <handler event="focus"><![CDATA[
-        if (event.originalTarget == this.inputField) {
-          this._updateUrlTooltip();
-          this.formatValue();
-          if (this.getAttribute("pageproxystate") != "valid") {
-            UpdatePopupNotificationsVisibility();
-          }
-
-          // We show the opt-out notification when the mouse/keyboard focus the
-          // urlbar, but in any case we want to enforce at least one
-          // notification when the user focuses it with the mouse.
-          let whichNotification = this.whichSearchSuggestionsNotification;
-          if (whichNotification == "opt-out" &&
-              this._showSearchSuggestionNotificationOnMouseFocus === undefined) {
-            this._showSearchSuggestionNotificationOnMouseFocus = true;
-          }
-
-          // Check whether the focus change came from a keyboard/mouse action.
-          let focusMethod = Services.focus.getLastFocusMethod(window);
-          // If it's a focus started by code and the primary user intention was
-          // not to go to the location bar, don't show a notification.
-          if (!focusMethod && !this.userInitiatedFocus) {
-            return;
-          }
-
-          let mouseFocused = !!(focusMethod & Services.focus.FLAG_BYMOUSE);
-          this.maybeShowSearchSuggestionsNotificationOnFocus(mouseFocused);
-        }
-      ]]></handler>
-
-      <handler event="blur"><![CDATA[
-        if (event.originalTarget == this.inputField) {
-          this._clearNoActions();
-          this.formatValue();
-          if (this.getAttribute("pageproxystate") != "valid") {
-            UpdatePopupNotificationsVisibility();
-          }
-        }
-        if (this.ExtensionSearchHandler.hasActiveInputSession()) {
-          this.ExtensionSearchHandler.handleInputCancelled();
-        }
-        if (this._deferredKeyEventTimeout) {
-          clearTimeout(this._deferredKeyEventTimeout);
-          this._deferredKeyEventTimeout = null;
-        }
-        this._deferredKeyEventQueue = [];
-      ]]></handler>
-
-      <handler event="dragstart" phase="capturing"><![CDATA[
-        // Drag only if the gesture starts from the input field.
-        if (this.inputField != event.originalTarget &&
-            !(this.inputField.compareDocumentPosition(event.originalTarget) &
-              Node.DOCUMENT_POSITION_CONTAINED_BY))
-          return;
-
-        // Drag only if the entire value is selected and it's a valid URI.
-        var isFullSelection = this.selectionStart == 0 &&
-                              this.selectionEnd == this.textLength;
-        if (!isFullSelection ||
-            this.getAttribute("pageproxystate") != "valid")
-          return;
-
-        var urlString = gBrowser.selectedBrowser.currentURI.displaySpec;
-        var title = gBrowser.selectedBrowser.contentTitle || urlString;
-        var htmlString = "<a href=\"" + urlString + "\">" + urlString + "</a>";
-
-        var dt = event.dataTransfer;
-        dt.setData("text/x-moz-url", urlString + "\n" + title);
-        dt.setData("text/unicode", urlString);
-        dt.setData("text/html", htmlString);
-
-        dt.effectAllowed = "copyLink";
-        event.stopPropagation();
-      ]]></handler>
-
-      <handler event="dragover" phase="capturing" action="this.onDragOver(event, this);"/>
-      <handler event="drop" phase="capturing" action="this.onDrop(event, this);"/>
-      <handler event="select"><![CDATA[
-        if (!Cc["@mozilla.org/widget/clipboard;1"]
-               .getService(Ci.nsIClipboard)
-               .supportsSelectionClipboard())
-          return;
-
-        if (!window.windowUtils.isHandlingUserInput)
-          return;
-
-        var val = this._getSelectedValueForClipboard();
-        if (!val)
-          return;
-
-        Cc["@mozilla.org/widget/clipboardhelper;1"]
-          .getService(Ci.nsIClipboardHelper)
-          .copyStringToClipboard(val, Ci.nsIClipboard.kSelectionClipboard);
-      ]]></handler>
-    </handlers>
-
-  </binding>
-
   <binding id="urlbar-rich-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-rich-result-popup">
 
     <content ignorekeys="true" level="top" consumeoutsideclicks="never"
              aria-owns="richlistbox">
       <xul:deck anonid="search-suggestions-notification"
                 align="center"
                 role="alert"
                 selectedIndex="0">
--- a/browser/base/moz.build
+++ b/browser/base/moz.build
@@ -31,17 +31,16 @@ BROWSER_CHROME_MANIFESTS += [
     'content/test/keyboard/browser.ini',
     'content/test/menubar/browser.ini',
     'content/test/metaTags/browser.ini',
     'content/test/pageActions/browser.ini',
     'content/test/pageinfo/browser.ini',
     'content/test/performance/browser.ini',
     'content/test/performance/hidpi/browser.ini',
     'content/test/performance/io/browser.ini',
-    'content/test/performance/legacyurlbar/browser.ini',
     'content/test/performance/lowdpi/browser.ini',
     'content/test/permissions/browser.ini',
     'content/test/plugins/browser-rs-blocklist.ini',
     'content/test/plugins/browser.ini',
     'content/test/popupNotifications/browser.ini',
     'content/test/popups/browser.ini',
     'content/test/referrer/browser.ini',
     'content/test/sanitize/browser.ini',
--- a/browser/components/BrowserContentHandler.jsm
+++ b/browser/components/BrowserContentHandler.jsm
@@ -28,16 +28,22 @@ XPCOMUtils.defineLazyModuleGetters(this,
     "resource://gre/modules/remotepagemanager/RemotePageManagerParent.jsm",
 });
 XPCOMUtils.defineLazyServiceGetter(
   this,
   "WindowsUIUtils",
   "@mozilla.org/windows-ui-utils;1",
   "nsIWindowsUIUtils"
 );
+XPCOMUtils.defineLazyServiceGetter(
+  this,
+  "UpdateManager",
+  "@mozilla.org/updates/update-manager;1",
+  "nsIUpdateManager"
+);
 
 XPCOMUtils.defineLazyGetter(this, "gSystemPrincipal", () =>
   Services.scriptSecurityManager.getSystemPrincipal()
 );
 XPCOMUtils.defineLazyGlobalGetters(this, [URL]);
 
 const NEWINSTALL_PAGE = "about:newinstall";
 
@@ -154,39 +160,24 @@ function needHomepageOverride(prefb) {
   }
 
   return OVERRIDE_NONE;
 }
 
 /**
  * Gets the override page for the first run after the application has been
  * updated.
+ * @param  update
+ *         The nsIUpdate for the update that has been applied.
  * @param  defaultOverridePage
  *         The default override page.
  * @return The override page.
  */
-function getPostUpdateOverridePage(defaultOverridePage) {
-  var um = Cc["@mozilla.org/updates/update-manager;1"].getService(
-    Ci.nsIUpdateManager
-  );
-  // The active update should be present when this code is called. If for
-  // whatever reason it isn't fallback to the latest update in the update
-  // history.
-  if (um.activeUpdate) {
-    var update = um.activeUpdate.QueryInterface(Ci.nsIWritablePropertyBag);
-  } else {
-    // If the updates.xml file is deleted then getUpdateAt will throw.
-    try {
-      update = um.getUpdateAt(0).QueryInterface(Ci.nsIWritablePropertyBag);
-    } catch (e) {
-      Cu.reportError("Unable to find update: " + e);
-      return defaultOverridePage;
-    }
-  }
-
+function getPostUpdateOverridePage(update, defaultOverridePage) {
+  update = update.QueryInterface(Ci.nsIWritablePropertyBag);
   let actions = update.getProperty("actions");
   // When the update doesn't specify actions fallback to the original behavior
   // of displaying the default override page.
   if (!actions) {
     return defaultOverridePage;
   }
 
   // The existence of silent or the non-existence of showURL in the actions both
@@ -660,28 +651,30 @@ nsBrowserContentHandler.prototype = {
             // into account because that requires waiting for the session file
             // to be read. If a crash occurs after updating, before restarting,
             // we may open the startPage in addition to restoring the session.
             willRestoreSession = SessionStartup.isAutomaticRestoreEnabled();
 
             overridePage = Services.urlFormatter.formatURLPref(
               "startup.homepage_override_url"
             );
-            if (prefb.prefHasUserValue("app.update.postupdate")) {
-              prefb.clearUserPref("app.update.postupdate");
-              overridePage = getPostUpdateOverridePage(overridePage);
+            let update = UpdateManager.activeUpdate;
+            if (
+              update &&
+              Services.vc.compare(update.appVersion, old_mstone) > 0
+            ) {
+              overridePage = getPostUpdateOverridePage(update, overridePage);
               // Send the update ping to signal that the update was successful.
               UpdatePing.handleUpdateSuccess(old_mstone, old_buildId);
             }
 
             overridePage = overridePage.replace("%OLD_VERSION%", old_mstone);
             break;
           case OVERRIDE_NEW_BUILD_ID:
-            if (prefb.prefHasUserValue("app.update.postupdate")) {
-              prefb.clearUserPref("app.update.postupdate");
+            if (UpdateManager.activeUpdate) {
               // Send the update ping to signal that the update was successful.
               UpdatePing.handleUpdateSuccess(old_mstone, old_buildId);
             }
             break;
         }
       }
     } catch (ex) {}
 
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -1021,17 +1021,17 @@ BrowserGlue.prototype = {
       case "handlersvc-store-initialized":
         // Initialize PdfJs when running in-process and remote. This only
         // happens once since PdfJs registers global hooks. If the PdfJs
         // extension is installed the init method below will be overridden
         // leaving initialization to the extension.
         // parent only: configure default prefs, set up pref observers, register
         // pdf content handler, and initializes parent side message manager
         // shim for privileged api access.
-        PdfJs.init(true);
+        PdfJs.init();
         break;
       case "shield-init-complete":
         this._shieldInitComplete = true;
         this._sendMainPingCentrePing();
         break;
     }
   },
 
--- a/browser/components/enterprisepolicies/tests/browser/browser_policy_override_postupdatepage.js
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_override_postupdatepage.js
@@ -2,17 +2,16 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // This test was based on the test browser_bug538331.js
 
 const UPDATE_PROVIDED_PAGE = "https://default.example.com/";
 const POLICY_PROVIDED_PAGE = "https://policy.example.com/";
 
 const PREF_MSTONE = "browser.startup.homepage_override.mstone";
-const PREF_POSTUPDATE = "app.update.postupdate";
 
 /*
  * The important parts for this test are:
  *  - actions="showURL"
  *  - openURL="${UPDATE_PROVIDED_PAGE}"
  */
 const XML_UPDATE = `<?xml version="1.0"?>
 <updates xmlns="http://www.mozilla.org/2005/app-update">
@@ -23,85 +22,111 @@ const XML_UPDATE = `<?xml version="1.0"?
           serviceURL="https://example.com/" statusText="The Update was successfully installed"
           foregroundDownload="true"
           actions="showURL"
           openURL="${UPDATE_PROVIDED_PAGE}">
     <patch type="complete" URL="http://example.com/" size="775" selected="true" state="succeeded"/>
   </update>
 </updates>`;
 
-const XML_EMPTY = `<?xml version="1.0"?>
-<updates xmlns="http://www.mozilla.org/2005/app-update">
-</updates>`;
-
-let gOriginalMStone = null;
+add_task(async function test_override_postupdate_page() {
+  let originalMstone = Services.prefs.getCharPref(PREF_MSTONE);
+  // Set the preferences needed for the test: they will be cleared up
+  // after it runs.
+  await SpecialPowers.pushPrefEnv({ set: [[PREF_MSTONE, originalMstone]] });
 
-add_task(async function test_override_postupdate_page() {
-  // Remember this to clean-up afterwards
-  if (Services.prefs.prefHasUserValue(PREF_MSTONE)) {
-    gOriginalMStone = Services.prefs.getCharPref(PREF_MSTONE);
-  }
-  Services.prefs.setBoolPref(PREF_POSTUPDATE, true);
+  registerCleanupFunction(async () => {
+    let activeUpdateFile = getActiveUpdateFile();
+    activeUpdateFile.remove(false);
+    reloadUpdateManagerData(true);
+  });
 
   writeUpdatesToXMLFile(XML_UPDATE);
-  reloadUpdateManagerData();
+  reloadUpdateManagerData(false);
 
   is(
     getPostUpdatePage(),
     UPDATE_PROVIDED_PAGE,
-    "Post-update page was provided by update.xml."
+    "Post-update page was provided by active-update.xml."
   );
 
   // Now perform the same action but set the policy to override this page
   await setupPolicyEngineWithJson({
     policies: {
       OverridePostUpdatePage: POLICY_PROVIDED_PAGE,
     },
   });
 
   is(
     getPostUpdatePage(),
     POLICY_PROVIDED_PAGE,
     "Post-update page was provided by policy."
   );
-
-  // Clean-up
-  writeUpdatesToXMLFile(XML_EMPTY);
-  if (gOriginalMStone) {
-    Services.prefs.setCharPref(PREF_MSTONE, gOriginalMStone);
-  }
-  Services.prefs.clearUserPref(PREF_POSTUPDATE);
-  reloadUpdateManagerData();
 });
 
 function getPostUpdatePage() {
   Services.prefs.setCharPref(PREF_MSTONE, "PreviousMilestone");
   return Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler)
     .defaultArgs;
 }
 
-function reloadUpdateManagerData() {
-  // Reloads the update metadata from disk
+/**
+ * Removes the updates.xml file and returns the nsIFile for the
+ * active-update.xml file.
+ *
+ * @return  The nsIFile for the active-update.xml file.
+ */
+function getActiveUpdateFile() {
+  let updateRootDir = Services.dirsvc.get("UpdRootD", Ci.nsIFile);
+  let updatesFile = updateRootDir.clone();
+  updatesFile.append("updates.xml");
+  if (updatesFile.exists()) {
+    // The following is non-fatal.
+    try {
+      updatesFile.remove(false);
+    } catch (e) {}
+  }
+  let activeUpdateFile = updateRootDir.clone();
+  activeUpdateFile.append("active-update.xml");
+  return activeUpdateFile;
+}
+
+/**
+ * Reloads the update xml files.
+ *
+ * @param  skipFiles (optional)
+ *         If true, the update xml files will not be read and the metadata will
+ *         be reset. If false (the default), the update xml files will be read
+ *         to populate the update metadata.
+ */
+function reloadUpdateManagerData(skipFiles = false) {
   Cc["@mozilla.org/updates/update-manager;1"]
     .getService(Ci.nsIUpdateManager)
     .QueryInterface(Ci.nsIObserver)
-    .observe(null, "um-reload-update-data", "");
+    .observe(null, "um-reload-update-data", skipFiles ? "skip-files" : "");
 }
 
+/**
+ * Writes the updates specified to the active-update.xml file.
+ *
+ * @param  aText
+ *         The updates represented as a string to write to the active-update.xml
+ *         file.
+ */
 function writeUpdatesToXMLFile(aText) {
   const PERMS_FILE = 0o644;
 
   const MODE_WRONLY = 0x02;
   const MODE_CREATE = 0x08;
   const MODE_TRUNCATE = 0x20;
 
-  let file = Services.dirsvc.get("UpdRootD", Ci.nsIFile);
-  file.append("updates.xml");
+  let activeUpdateFile = getActiveUpdateFile();
+  if (!activeUpdateFile.exists()) {
+    activeUpdateFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
+  }
   let fos = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
     Ci.nsIFileOutputStream
   );
-  if (!file.exists()) {
-    file.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
-  }
-  fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, PERMS_FILE, 0);
+  let flags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
+  fos.init(activeUpdateFile, flags, PERMS_FILE, 0);
   fos.write(aText, aText.length);
   fos.close();
 }
--- a/browser/components/extensions/test/browser/browser_ext_omnibox.js
+++ b/browser/components/extensions/test/browser/browser_ext_omnibox.js
@@ -261,21 +261,16 @@ add_task(async function() {
     await promiseEvent;
   }
 
   async function testSuggestions(info) {
     extension.sendMessage("set-synchronous", { synchronous: false });
     await extension.awaitMessage("set-synchronous-set");
 
     let text = await startInputSession();
-    if (!UrlbarPrefs.get("quantumbar")) {
-      // TODO Bug 1530338: We can't yet wait for a specific result for the
-      // quantumbar. Therefore we just skip this for now.
-      await waitForResult(0);
-    }
 
     extension.sendMessage(info.test);
     await extension.awaitMessage("test-ready");
 
     await waitForResult(info.suggestions.length - 1);
     // Skip the heuristic result.
     let index = 1;
     for (let { content, description } of info.suggestions) {
rename from browser/components/preferences/in-content/containers.xul
rename to browser/components/preferences/in-content/containers.inc.xul
rename from browser/components/preferences/in-content/home.xul
rename to browser/components/preferences/in-content/home.inc.xul
rename from browser/components/preferences/in-content/main.xul
rename to browser/components/preferences/in-content/main.inc.xul
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -180,23 +180,23 @@
           </hbox>
           <textbox
             is="search-textbox" id="searchInput"
             data-l10n-id="search-input-box"
             data-l10n-attrs="style"
             hidden="true" clickSelectsAll="true"/>
         </hbox>
         <vbox id="mainPrefPane">
-#include searchResults.xul
-#include main.xul
-#include home.xul
-#include search.xul
-#include privacy.xul
-#include containers.xul
-#include sync.xul
+#include searchResults.inc.xul
+#include main.inc.xul
+#include home.inc.xul
+#include search.inc.xul
+#include privacy.inc.xul
+#include containers.inc.xul
+#include sync.inc.xul
         </vbox>
       </vbox>
     </vbox>
   </hbox>
 
   <stack id="dialogStack" hidden="true"/>
   <vbox id="dialogTemplate" class="dialogOverlay" align="center" pack="center" topmost="true" hidden="true">
     <vbox class="dialogBox"
rename from browser/components/preferences/in-content/privacy.xul
rename to browser/components/preferences/in-content/privacy.inc.xul
rename from browser/components/preferences/in-content/search.xul
rename to browser/components/preferences/in-content/search.inc.xul
rename from browser/components/preferences/in-content/searchResults.xul
rename to browser/components/preferences/in-content/searchResults.inc.xul
rename from browser/components/preferences/in-content/sync.xul
rename to browser/components/preferences/in-content/sync.inc.xul
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js
@@ -142,18 +142,16 @@ add_task(async function test_search_hand
   ok(urlBarHasHiddenFocus(win), "url bar has hidden focused");
   var helper = SpecialPowers.Cc[
     "@mozilla.org/widget/clipboardhelper;1"
   ].getService(SpecialPowers.Ci.nsIClipboardHelper);
   helper.copyString("words");
   await new Promise(r =>
     EventUtils.synthesizeKey("v", { accelKey: true }, win, r)
   );
-  // TODO: Bug 1539199 We should be able to wait for search complete for AwesomeBar
-  // as well.
-  if (UrlbarPrefs.get("quantumbar")) {
-    await UrlbarTestUtils.promiseSearchComplete(win);
-  }
+
+  await UrlbarTestUtils.promiseSearchComplete(win);
+
   ok(urlBarHasNormalFocus(win), "url bar has normal focused");
   is(win.gURLBar.value, "@google words", "url bar has search text");
 
   await BrowserTestUtils.closeWindow(win);
 });
--- a/browser/components/sessionstore/test/browser_tabs_in_urlbar.js
+++ b/browser/components/sessionstore/test/browser_tabs_in_urlbar.js
@@ -106,28 +106,25 @@ add_task(async function test_unrestored_
   info("Searching open pages.");
   await UrlbarTestUtils.promiseAutocompleteResultPopup({
     window,
     waitForFocus,
     value: RESTRICT_TOKEN_OPENPAGE,
   });
   const total = UrlbarTestUtils.getResultCount(window);
   info(`Found ${total} matches`);
-  const quantumbar = UrlbarPrefs.get("quantumbar");
 
   // Check to see the expected uris and titles match up (in any order)
   for (let i = 0; i < total; i++) {
     const result = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
     if (result.heuristic) {
       info("Skip heuristic match");
       continue;
     }
-    const url = quantumbar
-      ? result.url
-      : PlacesUtils.parseActionUrl(result.url).params.url;
+    const url = result.url;
     Assert.ok(
       url in tabsForEnsure,
       `Should have the found result '${url}' in the expected list of entries`
     );
     // Remove the found entry from expected results.
     delete tabsForEnsure[url];
   }
   // Make sure there is no reported open page that is not open.
--- a/browser/components/tests/browser/browser_bug538331.js
+++ b/browser/components/tests/browser/browser_bug538331.js
@@ -1,15 +1,14 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-const PREF_POSTUPDATE = "app.update.postupdate";
 const PREF_MSTONE = "browser.startup.homepage_override.mstone";
 const PREF_OVERRIDE_URL = "startup.homepage_override_url";
 
 const DEFAULT_PREF_URL = "http://pref.example.com/";
 const DEFAULT_UPDATE_URL = "http://example.com/";
 
 const XML_EMPTY =
   '<?xml version="1.0"?><updates xmlns=' +
@@ -30,22 +29,20 @@ const XML_SUFFIX =
   '><patch type="complete" URL="http://example.com/" ' +
   'size="775" selected="true" state="succeeded"/>' +
   "</update></updates>";
 
 // nsBrowserContentHandler.js defaultArgs tests
 const BCH_TESTS = [
   {
     description: "no mstone change and no update",
-    noPostUpdatePref: true,
     noMstoneChange: true,
   },
   {
     description: "mstone changed and no update",
-    noPostUpdatePref: true,
     prefURL: DEFAULT_PREF_URL,
   },
   {
     description: "no mstone change and update with 'showURL' for actions",
     actions: "showURL",
     noMstoneChange: true,
   },
   {
@@ -77,74 +74,40 @@ const BCH_TESTS = [
     actions: "silent",
   },
   {
     description: "update with 'silent showURL extra' for actions and openURL",
     actions: "silent showURL extra",
   },
 ];
 
-var gOriginalMStone;
-var gOriginalOverrideURL;
-
-function test() {
-  waitForExplicitFinish();
-
+add_task(async function test_bug538331() {
   // Reset the startup page pref since it may have been set by other tests
   // and we will assume it is default.
   Services.prefs.clearUserPref("browser.startup.page");
 
-  if (Services.prefs.prefHasUserValue(PREF_MSTONE)) {
-    gOriginalMStone = Services.prefs.getCharPref(PREF_MSTONE);
-  }
-
-  if (Services.prefs.prefHasUserValue(PREF_OVERRIDE_URL)) {
-    gOriginalOverrideURL = Services.prefs.getCharPref(PREF_OVERRIDE_URL);
-  }
-
-  testDefaultArgs();
-}
+  let originalMstone = Services.prefs.getCharPref(PREF_MSTONE);
 
-function finish_test() {
-  // Reset browser.startup.homepage_override.mstone to the original value or
-  // clear it if it didn't exist.
-  if (gOriginalMStone) {
-    Services.prefs.setCharPref(PREF_MSTONE, gOriginalMStone);
-  } else if (Services.prefs.prefHasUserValue(PREF_MSTONE)) {
-    Services.prefs.clearUserPref(PREF_MSTONE);
-  }
+  // Set the preferences needed for the test: they will be cleared up
+  // after it runs.
+  await SpecialPowers.pushPrefEnv({
+    set: [[PREF_MSTONE, originalMstone], [PREF_OVERRIDE_URL, DEFAULT_PREF_URL]],
+  });
 
-  // Reset startup.homepage_override_url to the original value or clear it if
-  // it didn't exist.
-  if (gOriginalOverrideURL) {
-    Services.prefs.setCharPref(PREF_OVERRIDE_URL, gOriginalOverrideURL);
-  } else if (Services.prefs.prefHasUserValue(PREF_OVERRIDE_URL)) {
-    Services.prefs.clearUserPref(PREF_OVERRIDE_URL);
-  }
+  registerCleanupFunction(async () => {
+    let activeUpdateFile = getActiveUpdateFile();
+    activeUpdateFile.remove(false);
+    reloadUpdateManagerData(true);
+  });
 
-  writeUpdatesToXMLFile(XML_EMPTY);
-  reloadUpdateManagerData();
-
-  finish();
-}
-
-// Test the defaultArgs returned by nsBrowserContentHandler after an update
-function testDefaultArgs() {
   // Clear any pre-existing override in defaultArgs that are hanging around.
   // This will also set the browser.startup.homepage_override.mstone preference
   // if it isn't already set.
   Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler).defaultArgs;
 
-  let originalMstone = Services.prefs.getCharPref(PREF_MSTONE);
-
-  Services.prefs.setCharPref(PREF_OVERRIDE_URL, DEFAULT_PREF_URL);
-
-  writeUpdatesToXMLFile(XML_EMPTY);
-  reloadUpdateManagerData();
-
   for (let i = 0; i < BCH_TESTS.length; i++) {
     let testCase = BCH_TESTS[i];
     ok(
       true,
       "Test nsBrowserContentHandler " + (i + 1) + ": " + testCase.description
     );
 
     if (testCase.actions) {
@@ -152,17 +115,17 @@ function testDefaultArgs() {
       if (testCase.openURL) {
         actionsXML += ' openURL="' + testCase.openURL + '"';
       }
       writeUpdatesToXMLFile(XML_PREFIX + actionsXML + XML_SUFFIX);
     } else {
       writeUpdatesToXMLFile(XML_EMPTY);
     }
 
-    reloadUpdateManagerData();
+    reloadUpdateManagerData(false);
 
     let noOverrideArgs = Cc["@mozilla.org/browser/clh;1"].getService(
       Ci.nsIBrowserHandler
     ).defaultArgs;
 
     let overrideArgs = "";
     if (testCase.prefURL) {
       overrideArgs = testCase.prefURL;
@@ -175,61 +138,86 @@ function testDefaultArgs() {
     } else if (noOverrideArgs) {
       overrideArgs += "|" + noOverrideArgs;
     }
 
     if (testCase.noMstoneChange === undefined) {
       Services.prefs.setCharPref(PREF_MSTONE, "PreviousMilestone");
     }
 
-    if (testCase.noPostUpdatePref == undefined) {
-      Services.prefs.setBoolPref(PREF_POSTUPDATE, true);
-    }
-
     let defaultArgs = Cc["@mozilla.org/browser/clh;1"].getService(
       Ci.nsIBrowserHandler
     ).defaultArgs;
     is(defaultArgs, overrideArgs, "correct value returned by defaultArgs");
 
     if (testCase.noMstoneChange === undefined || !testCase.noMstoneChange) {
       let newMstone = Services.prefs.getCharPref(PREF_MSTONE);
       is(
         originalMstone,
         newMstone,
         "preference " + PREF_MSTONE + " should have been updated"
       );
     }
+  }
+});
 
-    if (Services.prefs.prefHasUserValue(PREF_POSTUPDATE)) {
-      Services.prefs.clearUserPref(PREF_POSTUPDATE);
-    }
+/**
+ * Removes the updates.xml file and returns the nsIFile for the
+ * active-update.xml file.
+ *
+ * @return  The nsIFile for the active-update.xml file.
+ */
+function getActiveUpdateFile() {
+  let updateRootDir = Services.dirsvc.get("UpdRootD", Ci.nsIFile);
+  let updatesFile = updateRootDir.clone();
+  updatesFile.append("updates.xml");
+  if (updatesFile.exists()) {
+    // The following is non-fatal.
+    try {
+      updatesFile.remove(false);
+    } catch (e) {}
   }
-
-  finish_test();
+  let activeUpdateFile = updateRootDir.clone();
+  activeUpdateFile.append("active-update.xml");
+  return activeUpdateFile;
 }
 
-/* Reloads the update metadata from disk */
-function reloadUpdateManagerData() {
+/**
+ * Reloads the update xml files.
+ *
+ * @param  skipFiles (optional)
+ *         If true, the update xml files will not be read and the metadata will
+ *         be reset. If false (the default), the update xml files will be read
+ *         to populate the update metadata.
+ */
+function reloadUpdateManagerData(skipFiles = false) {
   Cc["@mozilla.org/updates/update-manager;1"]
     .getService(Ci.nsIUpdateManager)
     .QueryInterface(Ci.nsIObserver)
-    .observe(null, "um-reload-update-data", "");
+    .observe(null, "um-reload-update-data", skipFiles ? "skip-files" : "");
 }
 
+/**
+ * Writes the updates specified to the active-update.xml file.
+ *
+ * @param  aText
+ *         The updates represented as a string to write to the active-update.xml
+ *         file.
+ */
 function writeUpdatesToXMLFile(aText) {
   const PERMS_FILE = 0o644;
 
   const MODE_WRONLY = 0x02;
   const MODE_CREATE = 0x08;
   const MODE_TRUNCATE = 0x20;
 
-  let file = Services.dirsvc.get("UpdRootD", Ci.nsIFile);
-  file.append("updates.xml");
+  let activeUpdateFile = getActiveUpdateFile();
+  if (!activeUpdateFile.exists()) {
+    activeUpdateFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
+  }
   let fos = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
     Ci.nsIFileOutputStream
   );
-  if (!file.exists()) {
-    file.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
-  }
-  fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, PERMS_FILE, 0);
+  let flags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
+  fos.init(activeUpdateFile, flags, PERMS_FILE, 0);
   fos.write(aText, aText.length);
   fos.close();
 }
--- a/browser/components/tests/browser/whats_new_page/browser_whats_new_page.js
+++ b/browser/components/tests/browser/whats_new_page/browser_whats_new_page.js
@@ -1,20 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 add_task(async function whats_new_page() {
-  // BrowserContentHandler.jsm should have cleared the app.update.postupdate
-  // preference.
-  ok(
-    !Services.prefs.prefHasUserValue("app.update.postupdate"),
-    "The app.update.postupdate preference should not have a user value"
-  );
   // The test harness will use the current tab and remove the tab's history.
   // Since the page that is tested is opened prior to the test harness taking
   // over the current tab the active-update.xml specifies two pages to open by
   // having 'https://example.com/|https://example.com/' for the value of openURL
   // and then uses the first tab for the test.
   gBrowser.selectedTab = gBrowser.tabs[0];
   // The test harness also changes the page to about:blank so go back to the
   // page that was originally opened.
@@ -29,16 +23,30 @@ add_task(async function whats_new_page()
   );
   is(
     gBrowser.selectedBrowser.currentURI.spec,
     "https://example.com/",
     "The what's new page's url should equal https://example.com/"
   );
   gBrowser.removeTab(gBrowser.selectedTab);
 
+  let um = Cc["@mozilla.org/updates/update-manager;1"].getService(
+    Ci.nsIUpdateManager
+  );
+  await TestUtils.waitForCondition(
+    () => !um.activeUpdate,
+    "Waiting for the active update to be removed"
+  );
+  ok(!um.activeUpdate, "There should not be an active update");
+  await TestUtils.waitForCondition(
+    () => !!um.getUpdateAt(0),
+    "Waiting for the active update to be moved to the update history"
+  );
+  ok(!!um.getUpdateAt(0), "There should be an update in the update history");
+
   // Leave no trace. Since this test modifies its support files put them back in
   // their original state.
   let alternatePath = Services.prefs.getCharPref("app.update.altUpdateDirPath");
   let testRoot = Services.prefs.getCharPref("mochitest.testRoot");
   let relativePath = alternatePath.substring("<test-root>".length);
   if (AppConstants.platform == "win") {
     relativePath = relativePath.replace(/\//g, "\\");
   }
@@ -88,9 +96,13 @@ add_task(async function whats_new_page()
     'type="complete" URL="https://127.0.0.1/complete.mar" ' +
     'selected="true" state="pending"/></update></updates>\n';
   activeUpdateFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
   fos.init(activeUpdateFile, flags, FileUtils.PERMS_FILE, 0);
   fos.write(xmlContents, xmlContents.length);
   fos.close();
 
   updatesFile.remove(false);
+  Cc["@mozilla.org/updates/update-manager;1"]
+    .getService(Ci.nsIUpdateManager)
+    .QueryInterface(Ci.nsIObserver)
+    .observe(null, "um-reload-update-data", "");
 });
--- a/browser/components/urlbar/moz.build
+++ b/browser/components/urlbar/moz.build
@@ -22,11 +22,10 @@ EXTRA_JS_MODULES += [
     'UrlbarView.jsm',
 ]
 
 TESTING_JS_MODULES += [
     'tests/UrlbarTestUtils.jsm',
 ]
 BROWSER_CHROME_MANIFESTS += [
     'tests/browser/browser.ini',
-    'tests/legacy/browser.ini',
 ]
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
--- a/browser/components/urlbar/tests/UrlbarTestUtils.jsm
+++ b/browser/components/urlbar/tests/UrlbarTestUtils.jsm
@@ -6,39 +6,30 @@
 const EXPORTED_SYMBOLS = ["UrlbarTestUtils"];
 
 const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   BrowserTestUtils: "resource://testing-common/BrowserTestUtils.jsm",
-  PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
-  TestUtils: "resource://testing-common/TestUtils.jsm",
-  UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
-  UrlbarTokenizer: "resource:///modules/UrlbarTokenizer.jsm",
   UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
 });
 
 var UrlbarTestUtils = {
   /**
    * Waits to a search to be complete.
    * @param {object} win The window containing the urlbar
-   * @param {function} restoreAnimationsFn optional Function to be used to
-   *        restore animations once done.
    * @returns {Promise} Resolved when done.
    */
-  promiseSearchComplete(win, restoreAnimationsFn = null) {
+  promiseSearchComplete(win) {
     let urlbar = getUrlbarAbstraction(win);
     return BrowserTestUtils.waitForPopupEvent(urlbar.panel, "shown").then(
       async () => {
         await urlbar.promiseSearchComplete();
-        if (typeof restoreAnimations == "function") {
-          restoreAnimationsFn();
-        }
       }
     );
   },
 
   /**
    * Starts a search for a given string and waits for the search to be complete.
    * @param {object} options.window The window containing the urlbar
    * @param {string} options.value the search string
@@ -53,17 +44,16 @@ var UrlbarTestUtils = {
     window,
     value,
     waitForFocus,
     fireInputEvent = false,
     selectionStart = -1,
     selectionEnd = -1,
   } = {}) {
     let urlbar = getUrlbarAbstraction(window);
-    let restoreAnimationsFn = urlbar.disableAnimations();
     await new Promise(resolve => waitForFocus(resolve, window));
     let lastSearchString = urlbar.lastSearchString;
     urlbar.focus();
     urlbar.value = value;
     if (selectionStart >= 0 && selectionEnd >= 0) {
       urlbar.selectionEnd = selectionEnd;
       urlbar.selectionStart = selectionStart;
     }
@@ -73,26 +63,21 @@ var UrlbarTestUtils = {
     } else {
       window.gURLBar.setAttribute("pageproxystate", "invalid");
     }
     // An input event will start a new search, with a couple of exceptions, so
     // be careful not to call startSearch if we fired an input event since that
     // would start two searches.  The first exception is when the new search and
     // old search are the same.  Many tests do consecutive searches with the
     // same string and expect new searches to start, so call startSearch
-    // directly then.  The second exception is when searching with the legacy
-    // urlbar and an empty string.
-    if (
-      !fireInputEvent ||
-      value == lastSearchString ||
-      (!urlbar.quantumbar && !value)
-    ) {
+    // directly then.
+    if (!fireInputEvent || value == lastSearchString) {
       urlbar.startSearch(value, selectionStart, selectionEnd);
     }
-    return this.promiseSearchComplete(window, restoreAnimationsFn);
+    return this.promiseSearchComplete(window);
   },
 
   /**
    * Waits for a result to be added at a certain index. Since we implement lazy
    * results replacement, even if we have a result at an index, it may be
    * related to the previous query, this methods ensures the result is current.
    * @param {object} win The window containing the urlbar
    * @param {number} index The index to look for
@@ -291,44 +276,28 @@ function getUrlbarAbstraction(win) {
  * Quantum Bar being enabled.
  */
 class UrlbarAbstraction {
   constructor(win) {
     if (!win) {
       throw new Error("Must provide a browser window");
     }
     this.urlbar = win.gURLBar;
-    this.quantumbar = UrlbarPrefs.get("quantumbar");
     this.window = win;
     this.window.addEventListener(
       "unload",
       () => {
         this.urlbar = null;
         this.window = null;
       },
       { once: true }
     );
   }
 
   /**
-   * Disable animations.
-   * @returns {function} can be invoked to restore the previous behavior.
-   */
-  disableAnimations() {
-    if (!this.quantumbar) {
-      let dontAnimate = !!this.urlbar.popup.getAttribute("dontanimate");
-      this.urlbar.popup.setAttribute("dontanimate", "true");
-      return () => {
-        this.urlbar.popup.setAttribute("dontanimate", dontAnimate);
-      };
-    }
-    return () => {};
-  }
-
-  /**
    * Focus the input field.
    */
   focus() {
     this.urlbar.inputField.focus();
   }
 
   /**
    * Dispatches an input event to the input field.
@@ -345,277 +314,130 @@ class UrlbarAbstraction {
   set value(val) {
     this.urlbar.value = val;
   }
   get value() {
     return this.urlbar.value;
   }
 
   get lastSearchString() {
-    return this.quantumbar
-      ? this.urlbar._lastSearchString
-      : this.urlbar.controller.searchString;
+    return this.urlbar._lastSearchString;
   }
 
   get panel() {
-    return this.quantumbar ? this.urlbar.panel : this.urlbar.popup;
+    return this.urlbar.panel;
   }
 
   get dropMarker() {
     return this.window.document.getAnonymousElementByAttribute(
       this.urlbar.textbox,
       "anonid",
       "historydropmarker"
     );
   }
 
   get oneOffSearchButtons() {
-    return this.quantumbar
-      ? this.urlbar.view.oneOffSearchButtons
-      : this.urlbar.popup.oneOffSearchButtons;
+    return this.urlbar.view.oneOffSearchButtons;
   }
 
   get oneOffSearchButtonsVisible() {
-    if (!this.quantumbar) {
-      return (
-        this.window.getComputedStyle(this.oneOffSearchButtons.container)
-          .display != "none"
-      );
-    }
-
     return this.oneOffSearchButtons.style.display != "none";
   }
 
   startSearch(text, selectionStart = -1, selectionEnd = -1) {
-    if (this.quantumbar) {
-      this.urlbar.value = text;
-      if (selectionStart >= 0 && selectionEnd >= 0) {
-        this.urlbar.selectionEnd = selectionEnd;
-        this.urlbar.selectionStart = selectionStart;
-      }
-      this.urlbar.setAttribute("pageproxystate", "invalid");
-      this.urlbar.startQuery();
-    } else {
-      // Force the controller to do consecutive searches for the same string by
-      // calling resetInternalState.
-      this.urlbar.controller.resetInternalState();
-      this.urlbar.controller.startSearch(text);
+    this.urlbar.value = text;
+    if (selectionStart >= 0 && selectionEnd >= 0) {
+      this.urlbar.selectionEnd = selectionEnd;
+      this.urlbar.selectionStart = selectionStart;
     }
+    this.urlbar.setAttribute("pageproxystate", "invalid");
+    this.urlbar.startQuery();
   }
 
   promiseSearchComplete() {
-    if (this.quantumbar) {
-      return this.urlbar.lastQueryContextPromise;
-    }
-    return BrowserTestUtils.waitForCondition(
-      () =>
-        this.urlbar.controller.searchStatus >=
-        Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH,
-      "waiting urlbar search to complete",
-      100,
-      50
-    );
+    return this.urlbar.lastQueryContextPromise;
   }
 
   async promiseUserContextId() {
     const defaultId = Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID;
-    if (this.quantumbar) {
-      let context = await this.urlbar.lastQueryContextPromise;
-      return context.userContextId || defaultId;
-    }
-    return this.urlbar.userContextId || defaultId;
+    let context = await this.urlbar.lastQueryContextPromise;
+    return context.userContextId || defaultId;
   }
 
   async promiseResultAt(index) {
-    if (!this.quantumbar) {
-      // In the legacy address bar, old results are replaced when new results
-      // arrive, thus it's possible for a result to be present but refer to
-      // a previous query. This ensures the given result refers to the current
-      // query by checking its query string against the string being searched
-      // for.
-      let searchString = this.urlbar.controller.searchString;
-      await BrowserTestUtils.waitForCondition(
-        () =>
-          this.panel.richlistbox.itemChildren.length > index &&
-          this.panel.richlistbox.itemChildren[index].getAttribute("ac-text") ==
-            searchString.trim(),
-        `Waiting for the autocomplete result for "${searchString}" at [${index}] to appear`
-      );
-      // Ensure the addition is complete, for proper mouse events on the entries.
-      await new Promise(resolve =>
-        this.window.requestIdleCallback(resolve, { timeout: 1000 })
-      );
-      return this.panel.richlistbox.itemChildren[index];
-    }
     // TODO Bug 1530338: Quantum Bar doesn't yet implement lazy results replacement.
     await this.promiseSearchComplete();
     if (index >= this.urlbar.view._rows.length) {
       throw new Error("Not enough results");
     }
     return this.urlbar.view._rows.children[index];
   }
 
   getSelectedElement() {
-    if (this.quantumbar) {
-      return this.urlbar.view._selected || null;
-    }
-    return this.panel.selectedIndex >= 0
-      ? this.panel.richlistbox.itemChildren[this.panel.selectedIndex]
-      : null;
+    return this.urlbar.view._selected || null;
   }
 
   getSelectedIndex() {
-    return this.quantumbar
-      ? this.urlbar.view.selectedIndex
-      : this.panel.selectedIndex;
+    return this.urlbar.view.selectedIndex;
   }
 
   setSelectedIndex(index) {
-    if (!this.quantumbar) {
-      return (this.panel.selectedIndex = index);
-    }
     return (this.urlbar.view.selectedIndex = index);
   }
 
   getResultCount() {
-    return this.quantumbar
-      ? this.urlbar.view._rows.children.length
-      : this.urlbar.controller.matchCount;
+    return this.urlbar.view._rows.children.length;
   }
 
   async getDetailsOfResultAt(index) {
     let element = await this.promiseResultAt(index);
-    function getType(style, action) {
-      if (style.includes("searchengine") || style.includes("suggestions")) {
-        return UrlbarUtils.RESULT_TYPE.SEARCH;
-      } else if (style.includes("extension")) {
-        return UrlbarUtils.RESULT_TYPE.OMNIBOX;
-      } else if (action && action.type == "keyword") {
-        return UrlbarUtils.RESULT_TYPE.KEYWORD;
-      } else if (action && action.type == "remotetab") {
-        return UrlbarUtils.RESULT_TYPE.REMOTE_TAB;
-      } else if (action && action.type == "switchtab") {
-        return UrlbarUtils.RESULT_TYPE.TAB_SWITCH;
-      }
-      return UrlbarUtils.RESULT_TYPE.URL;
-    }
     let details = {};
-    if (this.quantumbar) {
-      let result = element.result;
-      let { url, postData } = UrlbarUtils.getUrlFromResult(result);
-      details.url = url;
-      details.postData = postData;
-      details.type = result.type;
-      details.heuristic = result.heuristic;
-      details.autofill = !!result.autofill;
-      details.image = element.getElementsByClassName(
-        "urlbarView-favicon"
-      )[0].src;
-      details.title = result.title;
-      details.tags = "tags" in result.payload ? result.payload.tags : [];
-      let actions = element.getElementsByClassName("urlbarView-action");
-      let urls = element.getElementsByClassName("urlbarView-url");
-      let typeIcon = element.querySelector(".urlbarView-type-icon");
-      let typeIconStyle = this.window.getComputedStyle(typeIcon);
-      details.displayed = {
-        title: element.getElementsByClassName("urlbarView-title")[0]
-          .textContent,
-        action: actions.length > 0 ? actions[0].textContent : null,
-        url: urls.length > 0 ? urls[0].textContent : null,
-        typeIcon: typeIconStyle["background-image"],
-      };
-      details.element = {
-        action: element.getElementsByClassName("urlbarView-action")[0],
-        row: element,
-        separator: element.getElementsByClassName(
-          "urlbarView-title-separator"
-        )[0],
-        title: element.getElementsByClassName("urlbarView-title")[0],
-        url: element.getElementsByClassName("urlbarView-url")[0],
+    let result = element.result;
+    let { url, postData } = UrlbarUtils.getUrlFromResult(result);
+    details.url = url;
+    details.postData = postData;
+    details.type = result.type;
+    details.heuristic = result.heuristic;
+    details.autofill = !!result.autofill;
+    details.image = element.getElementsByClassName("urlbarView-favicon")[0].src;
+    details.title = result.title;
+    details.tags = "tags" in result.payload ? result.payload.tags : [];
+    let actions = element.getElementsByClassName("urlbarView-action");
+    let urls = element.getElementsByClassName("urlbarView-url");
+    let typeIcon = element.querySelector(".urlbarView-type-icon");
+    let typeIconStyle = this.window.getComputedStyle(typeIcon);
+    details.displayed = {
+      title: element.getElementsByClassName("urlbarView-title")[0].textContent,
+      action: actions.length > 0 ? actions[0].textContent : null,
+      url: urls.length > 0 ? urls[0].textContent : null,
+      typeIcon: typeIconStyle["background-image"],
+    };
+    details.element = {
+      action: element.getElementsByClassName("urlbarView-action")[0],
+      row: element,
+      separator: element.getElementsByClassName(
+        "urlbarView-title-separator"
+      )[0],
+      title: element.getElementsByClassName("urlbarView-title")[0],
+      url: element.getElementsByClassName("urlbarView-url")[0],
+    };
+    if (details.type == UrlbarUtils.RESULT_TYPE.SEARCH) {
+      details.searchParams = {
+        engine: result.payload.engine,
+        keyword: result.payload.keyword,
+        query: result.payload.query,
+        suggestion: result.payload.suggestion,
       };
-      if (details.type == UrlbarUtils.RESULT_TYPE.SEARCH) {
-        details.searchParams = {
-          engine: result.payload.engine,
-          keyword: result.payload.keyword,
-          query: result.payload.query,
-          suggestion: result.payload.suggestion,
-        };
-      } else if (details.type == UrlbarUtils.RESULT_TYPE.KEYWORD) {
-        details.keyword = result.payload.keyword;
-      }
-    } else {
-      details.url = this.urlbar.controller.getFinalCompleteValueAt(index);
-
-      let style = this.urlbar.controller.getStyleAt(index);
-      let action = PlacesUtils.parseActionUrl(
-        this.urlbar.controller.getValueAt(index)
-      );
-      details.type = getType(style, action);
-      details.heuristic = style.includes("heuristic");
-      details.autofill = style.includes("autofill");
-      details.image = element.getAttribute("image");
-      details.title = element.getAttribute("title");
-      details.tags = style.includes("tag")
-        ? [...element.getElementsByClassName("ac-tags")].map(e => e.textContent)
-        : [];
-      let typeIconStyle = this.window.getComputedStyle(element._typeIcon);
-      details.displayed = {
-        title: element._titleText.textContent,
-        action: action ? element._actionText.textContent : "",
-        url: element._urlText.textContent,
-        typeIcon: typeIconStyle.listStyleImage,
-      };
-      details.element = {
-        action: element._actionText,
-        row: element,
-        separator: element._separator,
-        title: element._titleText,
-        url: element._urlText,
-      };
-      if (details.type == UrlbarUtils.RESULT_TYPE.SEARCH && action) {
-        // Strip restriction tokens from input.
-        let query = action.params.input;
-        let restrictTokens = Object.values(UrlbarTokenizer.RESTRICT);
-        if (restrictTokens.includes(query[0])) {
-          query = query.substring(1).trim();
-        } else if (restrictTokens.includes(query[query.length - 1])) {
-          query = query.substring(0, query.length - 1).trim();
-        }
-        details.searchParams = {
-          engine: action.params.engineName,
-          keyword: action.params.alias,
-          query,
-          suggestion: action.params.searchSuggestion,
-        };
-      } else if (details.type == UrlbarUtils.RESULT_TYPE.KEYWORD) {
-        details.keyword = action.params.keyword;
-      }
+    } else if (details.type == UrlbarUtils.RESULT_TYPE.KEYWORD) {
+      details.keyword = result.payload.keyword;
     }
     return details;
   }
 
   async promiseSearchSuggestions() {
-    if (!this.quantumbar) {
-      return TestUtils.waitForCondition(() => {
-        let controller = this.urlbar.controller;
-        let matchCount = controller.matchCount;
-        for (let i = 0; i < matchCount; i++) {
-          let url = controller.getValueAt(i);
-          let action = PlacesUtils.parseActionUrl(url);
-          if (
-            action &&
-            action.type == "searchengine" &&
-            action.params.searchSuggestion
-          ) {
-            return true;
-          }
-        }
-        return false;
-      }, "Waiting for suggestions");
-    }
     // TODO Bug 1530338: Quantum Bar doesn't yet implement lazy results replacement. When
     // we do that, we'll have to be sure the suggestions we find are relevant
     // for the current query. For now let's just wait for the search to be
     // complete.
     return this.promiseSearchComplete().then(context => {
       // Look for search suggestions.
       if (
         !context.results.some(
@@ -628,21 +450,17 @@ class UrlbarAbstraction {
   }
 
   async promisePopupOpen(openFn) {
     await openFn();
     return BrowserTestUtils.waitForPopupEvent(this.panel, "shown");
   }
 
   closePopup() {
-    if (this.quantumbar) {
-      this.urlbar.view.close();
-    } else {
-      this.urlbar.popup.hidePopup();
-    }
+    this.urlbar.view.close();
   }
 
   async promisePopupClose(closeFn) {
     if (closeFn) {
       await closeFn();
     } else {
       this.closePopup();
     }
--- a/browser/components/urlbar/tests/browser/browser_action_searchengine.js
+++ b/browser/components/urlbar/tests/browser/browser_action_searchengine.js
@@ -45,25 +45,21 @@ add_task(async function() {
       engine: "MozSearch",
       keyword: undefined,
       query: "open a search",
       suggestion: undefined,
     },
     "Should have the correct result parameters."
   );
 
-  if (UrlbarPrefs.get("quantumbar")) {
-    Assert.equal(
-      result.image,
-      UrlbarUtils.ICON.SEARCH_GLASS,
-      "Should have the search icon image"
-    );
-  } else {
-    Assert.equal(result.image, "", "Should not have an image defined");
-  }
+  Assert.equal(
+    result.image,
+    UrlbarUtils.ICON.SEARCH_GLASS,
+    "Should have the search icon image"
+  );
 
   let tabPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
   let element = await UrlbarTestUtils.waitForAutocompleteResultAt(window, 0);
   EventUtils.synthesizeMouseAtCenter(element, {}, window);
   await tabPromise;
 
   Assert.equal(
     gBrowser.selectedBrowser.currentURI.spec,
--- a/browser/components/urlbar/tests/browser/browser_autoFill_backspaced.js
+++ b/browser/components/urlbar/tests/browser/browser_autoFill_backspaced.js
@@ -190,22 +190,16 @@ add_task(async function() {
       ["KEY_ArrowLeft", { shiftKey: true }],
       ["KEY_ArrowLeft", { shiftKey: true }],
       ["KEY_ArrowLeft", { shiftKey: true }],
       "KEY_Backspace",
     ],
     type: UrlbarUtils.RESULT_TYPE.URL,
   });
 
-  // The remaining tests fail on awesomebar because it doesn't properly handle
-  // them.
-  if (!UrlbarPrefs.get("quantumbar")) {
-    return;
-  }
-
   await test_autocomplete({
     desc:
       "End and then backspace should delete the backslash and not re-trigger autofill",
     typed: "ex",
     autofilled: "example.com/",
     modified: "example.com",
     keys: [
       AppConstants.platform == "macosx"
--- a/browser/components/urlbar/tests/browser/browser_autoFill_preserve.js
+++ b/browser/components/urlbar/tests/browser/browser_autoFill_preserve.js
@@ -192,23 +192,16 @@ add_task(async function backspaceNoAutof
   await UrlbarTestUtils.promiseSearchComplete(window);
   details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
   Assert.ok(!details.autofill);
   Assert.equal(gURLBar.value, "ExA");
   Assert.equal(gURLBar.selectionStart, "ExA".length);
   Assert.equal(gURLBar.selectionEnd, "ExA".length);
 
   let heuristicValue = "ExA";
-  if (!UrlbarPrefs.get("quantumbar")) {
-    heuristicValue = PlacesUtils.mozActionURI("searchengine", {
-      engineName: "Google",
-      searchQuery: "ExA",
-      input: "ExA",
-    });
-  }
 
   checkKeys([
     ["KEY_ArrowDown", "http://example.com/", 1],
     ["KEY_ArrowDown", "http://mozilla.org/example", 2],
     ["KEY_ArrowDown", "ExA", -1],
     ["KEY_ArrowUp", "http://mozilla.org/example", 2],
     ["KEY_ArrowUp", "http://example.com/", 1],
     ["KEY_ArrowUp", heuristicValue, 0],
--- a/browser/components/urlbar/tests/browser/browser_autoFill_trimURLs.js
+++ b/browser/components/urlbar/tests/browser/browser_autoFill_trimURLs.js
@@ -56,31 +56,21 @@ async function promiseTestResult(test) {
   Assert.equal(
     result.displayed.title,
     test.resultListDisplayTitle,
     `Autocomplete result should have displayed title as expected for search '${
       test.search
     }'`
   );
 
-  if (!UrlbarPrefs.get("quantumbar") && test.resultListActionText == "Visit") {
-    Assert.equal(
-      result.displayed.action,
-      "",
-      `Autocomplete action text should be empty for search '${test.search}'`
-    );
-  } else {
-    Assert.equal(
-      result.displayed.action,
-      test.resultListActionText,
-      `Autocomplete action text should be as expected for search '${
-        test.search
-      }'`
-    );
-  }
+  Assert.equal(
+    result.displayed.action,
+    test.resultListActionText,
+    `Autocomplete action text should be as expected for search '${test.search}'`
+  );
 
   Assert.equal(
     result.type,
     test.resultListType,
     `Autocomplete result should have searchengine for the type for search '${
       test.search
     }'`
   );
--- a/browser/components/urlbar/tests/browser/browser_autoFill_typed.js
+++ b/browser/components/urlbar/tests/browser/browser_autoFill_typed.js
@@ -148,21 +148,16 @@ async function typeAndCheck(values) {
     EventUtils.synthesizeKey(char);
     if (i == 0 && char == "@") {
       // A single "@" doesn't trigger autofill, so skip the checks below.  (It
       // shows all the @ aliases.)
       continue;
     }
     await UrlbarTestUtils.promiseSearchComplete(window);
     let restIsSpaces = !expectedInputValue.substring(i + 1).trim();
-    if (restIsSpaces && !UrlbarPrefs.get("quantumbar")) {
-      // See below.  In addition to that, in awesomebar, after typing the final
-      // character, autofill incorrectly doesn't include the trailing space.
-      expectedInputValue = expectedInputValue.trim();
-    }
     Assert.equal(gURLBar.value, expectedInputValue);
     Assert.equal(gURLBar.selectionStart, i + 1);
     Assert.equal(gURLBar.selectionEnd, expectedInputValue.length);
     if (restIsSpaces) {
       // Autofilled @ aliases have a trailing space.  We should check that the
       // space is autofilled when each preceding character is typed, but once
       // the final non-space char is typed, autofill actually stops and the
       // trailing space is not autofilled.  (Which is maybe not the way it
--- a/browser/components/urlbar/tests/browser/browser_autoFill_undo.js
+++ b/browser/components/urlbar/tests/browser/browser_autoFill_undo.js
@@ -37,15 +37,15 @@ add_task(async function test() {
 
   // Undo the typed x.
   goDoCommand("cmd_undo");
 
   // The text should be restored to ex[ample.com/] (with the part in brackets
   // autofilled and selected).
   await UrlbarTestUtils.promiseSearchComplete(window);
   details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
-  Assert.equal(details.autofill, !UrlbarPrefs.get("quantumbar"));
   Assert.equal(gURLBar.value, "example.com/");
+  Assert.ok(!details.autofill, "Autofill should not be set.");
   Assert.equal(gURLBar.selectionStart, "ex".length);
   Assert.equal(gURLBar.selectionEnd, "example.com/".length);
 
   await PlacesUtils.history.clear();
 });
--- a/browser/components/urlbar/tests/browser/browser_autocomplete_a11y_label.js
+++ b/browser/components/urlbar/tests/browser/browser_autocomplete_a11y_label.js
@@ -61,23 +61,18 @@ add_task(async function switchToTab() {
     result.type,
     UrlbarUtils.RESULT_TYPE.TAB_SWITCH,
     "Should have a switch tab result"
   );
 
   let element = await UrlbarTestUtils.waitForAutocompleteResultAt(window, 1);
   is(
     await getResultText(element),
-    UrlbarPrefs.get("quantumbar")
-      ? // The extra spaces are here due to bug 1550644.
-        "about : about— Switch to Tab"
-      : "about:about about:about Tab",
-    UrlbarPrefs.get("quantumbar")
-      ? "Result a11y label should be: <title>— Switch to Tab"
-      : "Result a11y label should be: <title> <url> Tab"
+    "about : about— Switch to Tab",
+    "Result a11y label should be: <title>— Switch to Tab"
   );
 
   await UrlbarTestUtils.promisePopupClose(window);
   gBrowser.removeTab(tab);
 });
 
 add_task(async function searchSuggestions() {
   let engine = await SearchTestUtils.promiseNewSearchEngine(
@@ -104,17 +99,17 @@ add_task(async function searchSuggestion
     "Should get at least heuristic result + two search suggestions"
   );
   // The first expected search is the search term itself since the heuristic
   // result will come before the search suggestions.
   let expectedSearches = [
     "foo",
     "foofoo",
     // The extra spaces is here due to bug 1550644.
-    UrlbarPrefs.get("quantumbar") ? "foo bar " : "foobar",
+    "foo bar ",
   ];
   for (let i = 0; i < length; i++) {
     let result = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
     if (result.type === UrlbarUtils.RESULT_TYPE.SEARCH) {
       Assert.greaterOrEqual(
         expectedSearches.length,
         0,
         "Should still have expected searches remaining"
@@ -126,24 +121,19 @@ add_task(async function searchSuggestion
       );
       let selected = element.hasAttribute("selected");
       if (!selected) {
         // Simulate the result being selected so we see the expanded text.
         element.toggleAttribute("selected", true);
       }
       Assert.equal(
         await getResultText(element),
-        UrlbarPrefs.get("quantumbar")
-          ? suggestion +
-              "— Search with browser_searchSuggestionEngine searchSuggestionEngine.xml"
-          : suggestion +
-              " browser_searchSuggestionEngine searchSuggestionEngine.xml Search",
-        UrlbarPrefs.get("quantumbar")
-          ? "Result label should be: <search term>— Search with <engine name>"
-          : "Result label should be: <search term> <engine name> Search"
+        suggestion +
+          "— Search with browser_searchSuggestionEngine searchSuggestionEngine.xml",
+        "Result label should be: <search term>— Search with <engine name>"
       );
       if (!selected) {
         element.toggleAttribute("selected", false);
       }
     }
   }
   Assert.ok(expectedSearches.length == 0);
 });
--- a/browser/components/urlbar/tests/browser/browser_autocomplete_tag_star_visibility.js
+++ b/browser/components/urlbar/tests/browser/browser_autocomplete_tag_star_visibility.js
@@ -123,20 +123,19 @@ add_task(async function() {
 
     Assert.equal(
       result.type,
       UrlbarUtils.RESULT_TYPE.URL,
       "Should have a URL result type"
     );
     // The Quantum Bar differs from the legacy urlbar in the fact that, if
     // bookmarks are filtered out, it won't show tags for history results.
-    let expected_tags =
-      UrlbarPrefs.get("quantumbar") && !testcase.expected.typeImageVisible
-        ? []
-        : [testcase.tagName];
+    let expected_tags = !testcase.expected.typeImageVisible
+      ? []
+      : [testcase.tagName];
     Assert.deepEqual(
       result.tags,
       expected_tags,
       "Should have the expected tag"
     );
 
     if (testcase.expected.typeImageVisible) {
       Assert.equal(
--- a/browser/components/urlbar/tests/browser/browser_ime_composition.js
+++ b/browser/components/urlbar/tests/browser/browser_ime_composition.js
@@ -150,21 +150,16 @@ add_task(async function test_composition
     });
   });
   Assert.equal(gURLBar.value, "", "Check urlbar value");
 
   info("Type normally, and hit escape, the popup should be closed.");
   Assert.ok(UrlbarTestUtils.isPopupOpen(window), "Popup should be open");
   EventUtils.synthesizeKey("I", {});
   EventUtils.synthesizeKey("n", {});
-  // The old urlbar may close/reopen the popup and then ESC wouldn't act as
-  // expected.
-  if (!UrlbarPrefs.get("quantumbar")) {
-    await UrlbarTestUtils.promiseSearchComplete(window);
-  }
   await UrlbarTestUtils.promisePopupClose(window, () => {
     EventUtils.synthesizeKey("KEY_Escape", {});
   });
   Assert.equal(gURLBar.value, "In", "Check urlbar value");
   // Clear typed chars.
   EventUtils.synthesizeKey("KEY_Backspace", {});
   EventUtils.synthesizeKey("KEY_Backspace", {});
   Assert.equal(gURLBar.value, "", "Check urlbar value");
--- a/browser/components/urlbar/tests/browser/browser_keyword.js
+++ b/browser/components/urlbar/tests/browser/browser_keyword.js
@@ -9,40 +9,25 @@
 
 async function promise_first_result(inputText) {
   await promiseAutocompleteResultPopup(inputText);
 
   return UrlbarTestUtils.getDetailsOfResultAt(window, 0);
 }
 
 function assertURL(result, expectedUrl, keyword, input, postData) {
-  if (UrlbarPrefs.get("quantumbar")) {
-    Assert.equal(result.url, expectedUrl, "Should have the correct URL");
-    if (postData) {
-      Assert.equal(
-        NetUtil.readInputStreamToString(
-          result.postData,
-          result.postData.available()
-        ),
-        postData,
-        "Should have the correct postData"
-      );
-    }
-  } else {
-    // We need to make a real URI out of this to ensure it's normalised for
-    // comparison.
+  Assert.equal(result.url, expectedUrl, "Should have the correct URL");
+  if (postData) {
     Assert.equal(
-      result.url,
-      PlacesUtils.mozActionURI("keyword", {
-        url: expectedUrl,
-        keyword,
-        input,
-        postData,
-      }),
-      "Expect correct url"
+      NetUtil.readInputStreamToString(
+        result.postData,
+        result.postData.available()
+      ),
+      postData,
+      "Should have the correct postData"
     );
   }
 }
 
 const TEST_URL = `${TEST_BASE_URL}print_postdata.sjs`;
 
 add_task(async function setup() {
   await PlacesUtils.keywords.insert({
@@ -128,26 +113,16 @@ add_task(async function test_keyword_usi
     "Node should contain the name of the bookmark and query"
   );
   Assert.ok(!result.displayed.action, "Should have an empty action");
 
   assertURL(result, TEST_URL + "?q=something", "get", "get something");
 
   let element = await UrlbarTestUtils.waitForAutocompleteResultAt(window, 0);
 
-  if (!UrlbarPrefs.get("quantumbar")) {
-    // QuantumBar doesn't have separate boxes for items.
-    let urlHbox = element._urlText.parentNode.parentNode;
-    Assert.ok(
-      urlHbox.classList.contains("ac-url"),
-      "URL hbox element sanity check"
-    );
-    BrowserTestUtils.is_hidden(urlHbox, "URL element should be hidden");
-  }
-
   // Click on the result
   info("Normal click on result");
   let tabPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   EventUtils.synthesizeMouseAtCenter(element, {});
   await tabPromise;
   Assert.equal(
     tab.linkedBrowser.currentURI.spec,
     TEST_URL + "?q=something",
--- a/browser/components/urlbar/tests/browser/browser_keyword_override.js
+++ b/browser/components/urlbar/tests/browser/browser_keyword_override.js
@@ -34,49 +34,25 @@ add_task(async function() {
   );
   Assert.equal(
     result.displayed.title,
     "example.com: search",
     "Node should contain the name of the bookmark and query"
   );
   Assert.ok(!result.displayed.action, "Should have an empty action");
 
-  if (!UrlbarPrefs.get("quantumbar")) {
-    let element = await UrlbarTestUtils.waitForAutocompleteResultAt(window, 0);
-
-    // QuantumBar doesn't have separate boxes for items.
-    let urlHbox = element._urlText.parentNode.parentNode;
-    Assert.ok(
-      urlHbox.classList.contains("ac-url"),
-      "URL hbox element sanity check"
-    );
-    BrowserTestUtils.is_hidden(urlHbox, "URL element should be hidden");
-  }
-
   info("During override");
   EventUtils.synthesizeKey("VK_SHIFT", { type: "keydown" });
 
   Assert.equal(
     result.type,
     UrlbarUtils.RESULT_TYPE.KEYWORD,
     "Should have a keyword result"
   );
   Assert.equal(
     result.displayed.title,
     "example.com: search",
     "Node should contain the name of the bookmark and query"
   );
   Assert.ok(!result.displayed.action, "Should have an empty action");
 
-  if (!UrlbarPrefs.get("quantumbar")) {
-    let element = await UrlbarTestUtils.waitForAutocompleteResultAt(window, 0);
-
-    // QuantumBar doesn't have separate boxes for items.
-    let urlHbox = element._urlText.parentNode.parentNode;
-    Assert.ok(
-      urlHbox.classList.contains("ac-url"),
-      "URL hbox element sanity check"
-    );
-    BrowserTestUtils.is_hidden(urlHbox, "URL element should be hidden");
-  }
-
   EventUtils.synthesizeKey("VK_SHIFT", { type: "keyup" });
 });
--- a/browser/components/urlbar/tests/browser/browser_keyword_select_and_type.js
+++ b/browser/components/urlbar/tests/browser/browser_keyword_select_and_type.js
@@ -79,28 +79,16 @@ add_task(async function() {
 
   let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
 
   Assert.equal(
     result.type,
     UrlbarUtils.RESULT_TYPE.KEYWORD,
     "Should have a result of type keyword"
   );
-  if (UrlbarPrefs.get("quantumbar")) {
-    Assert.equal(
-      result.url,
-      "http://example.com/?q=ab",
-      "Should have the correct url"
-    );
-  } else {
-    Assert.equal(
-      result.url,
-      PlacesUtils.mozActionURI("keyword", {
-        url: "http://example.com/?q=ab",
-        keyword: "keyword",
-        input: "keyword ab",
-      }),
-      "Should have the correct url"
-    );
-  }
+  Assert.equal(
+    result.url,
+    "http://example.com/?q=ab",
+    "Should have the correct url"
+  );
 
   gBrowser.removeTab(tab);
 });
--- a/browser/components/urlbar/tests/browser/browser_new_tab_urlbar_reset.js
+++ b/browser/components/urlbar/tests/browser/browser_new_tab_urlbar_reset.js
@@ -24,18 +24,10 @@ add_task(async function() {
   await promiseAutocompleteResultPopup("m");
   assertOpen();
 
   BrowserTestUtils.removeTab(tab);
   BrowserTestUtils.removeTab(tab2);
 });
 
 function assertOpen() {
-  if (UrlbarPrefs.get("quantumbar")) {
-    Assert.equal(
-      gURLBar.view.panel.state,
-      "open",
-      "Should be showing the popup"
-    );
-  } else {
-    Assert.ok(gURLBar.popupOpen, "Should be showing the popup");
-  }
+  Assert.equal(gURLBar.view.panel.state, "open", "Should be showing the popup");
 }
--- a/browser/components/urlbar/tests/browser/browser_switchTab_override.js
+++ b/browser/components/urlbar/tests/browser/browser_switchTab_override.js
@@ -70,19 +70,17 @@ add_task(async function test_switchtab_o
     Assert.ok(BrowserTestUtils.is_hidden(label));
   }
 
   registerCleanupFunction(() => {
     // Avoid confusing next tests by leaving a pending keydown.
     EventUtils.synthesizeKey("KEY_Shift", { type: "keyup" });
   });
 
-  let attribute = UrlbarPrefs.get("quantumbar")
-    ? "actionoverride"
-    : "noactions";
+  let attribute = "actionoverride";
   Assert.ok(
     UrlbarTestUtils.getPanel(window).hasAttribute(attribute),
     "We should be overriding"
   );
 
   EventUtils.synthesizeKey("KEY_Enter");
   info(`gURLBar.value = ${gURLBar.value}`);
   await deferred.promise;
--- a/browser/components/urlbar/tests/browser/browser_tabMatchesInAwesomebar.js
+++ b/browser/components/urlbar/tests/browser/browser_tabMatchesInAwesomebar.js
@@ -193,19 +193,16 @@ async function checkAutocompleteResults(
 
     Assert.equal(
       result.type,
       UrlbarUtils.RESULT_TYPE.TAB_SWITCH,
       "Should have a tab switch result"
     );
 
     let url = result.url;
-    if (!UrlbarPrefs.get("quantumbar")) {
-      url = PlacesUtils.parseActionUrl(url).params.url;
-    }
 
     info(`Search for ${url} in open tabs.`);
     let inExpected = url in expected;
     Assert.ok(
       inExpected,
       `${url} was found in autocomplete, was ${
         inExpected ? "" : "not "
       } expected`
--- a/browser/components/urlbar/tests/browser/browser_tabMatchesInAwesomebar_perwindowpb.js
+++ b/browser/components/urlbar/tests/browser/browser_tabMatchesInAwesomebar_perwindowpb.js
@@ -78,20 +78,17 @@ async function runTest(aSourceWindow, aD
     );
   });
   ok(
     !testTab.hasAttribute("busy"),
     "The test tab doesn't have the busy attribute"
   );
 
   // Wait for the Awesomebar popup to appear.
-  // Use a slice to workaround bug 1507755.
-  let searchString = UrlbarPrefs.get("quantumbar")
-    ? TEST_URL
-    : TEST_URL.slice(1);
+  let searchString = TEST_URL;
   await promiseAutocompleteResultPopup(searchString, aDestWindow);
 
   info(`awesomebar popup appeared. aExpectSwitch: ${aExpectSwitch}`);
   // Make sure the last match is selected.
   while (
     UrlbarTestUtils.getSelectedIndex(aDestWindow) <
     UrlbarTestUtils.getResultCount(aDestWindow) - 1
   ) {
--- a/browser/components/urlbar/tests/browser/browser_urlbarDecode.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarDecode.js
@@ -30,26 +30,22 @@ add_task(async function injectJSON() {
   gURLBar.value = "";
   gURLBar.handleRevert();
   gURLBar.blur();
 });
 
 add_task(function losslessDecode() {
   let urlNoScheme = "example.com/\u30a2\u30a4\u30a6\u30a8\u30aa";
   let url = "http://" + urlNoScheme;
-  if (Services.prefs.getBoolPref("browser.urlbar.quantumbar", true)) {
-    const result = new UrlbarResult(
-      UrlbarUtils.RESULT_TYPE.TAB_SWITCH,
-      UrlbarUtils.RESULT_SOURCE.TABS,
-      { url }
-    );
-    gURLBar.setValueFromResult(result);
-  } else {
-    gURLBar.textValue = url;
-  }
+  const result = new UrlbarResult(
+    UrlbarUtils.RESULT_TYPE.TAB_SWITCH,
+    UrlbarUtils.RESULT_SOURCE.TABS,
+    { url }
+  );
+  gURLBar.setValueFromResult(result);
   // Since this is directly setting textValue, it is expected to be trimmed.
   Assert.equal(
     gURLBar.inputField.value,
     urlNoScheme,
     "The string displayed in the textbox should not be escaped"
   );
   gURLBar.value = "";
   gURLBar.handleRevert();
@@ -111,28 +107,16 @@ async function checkInput(inputStr) {
   // URL matches have their param.urls fixed up.
   let fixupInfo = Services.uriFixup.getFixupURIInfo(
     inputStr,
     Ci.nsIURIFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS |
       Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP
   );
   let expectedVisitURL = fixupInfo.fixedURI.spec;
 
-  if (!UrlbarPrefs.get("quantumbar")) {
-    let type = "visiturl";
-    let params = {
-      url: expectedVisitURL,
-      input: inputStr,
-    };
-    for (let key in params) {
-      params[key] = encodeURIComponent(params[key]);
-    }
-    expectedVisitURL = "moz-action:" + type + "," + JSON.stringify(params);
-  }
-
   Assert.equal(result.url, expectedVisitURL, "Should have the correct URL");
   Assert.equal(
     result.title,
     inputStr.replace("\\", "/"),
     "Should have the correct title"
   );
   Assert.equal(
     result.type,
--- a/browser/components/urlbar/tests/browser/browser_urlbarOneOffs.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarOneOffs.js
@@ -33,20 +33,16 @@ 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() {
-  if (!UrlbarPrefs.get("quantumbar")) {
-    gURLBar.popup.toggleOneOffSearches(true);
-  }
-
   gURLBar.focus();
   await UrlbarTestUtils.promisePopupOpen(window, () => {
     EventUtils.synthesizeKey("KEY_ArrowDown");
   });
   await waitForAutocompleteResultAt(gMaxResults - 1);
 
   assertState(-1, -1, "");
 
@@ -55,76 +51,16 @@ add_task(async function history() {
     EventUtils.synthesizeKey("KEY_ArrowDown");
     assertState(
       i,
       -1,
       "example.com/browser_urlbarOneOffs.js/?" + (gMaxResults - i - 1)
     );
   }
 
-  if (!UrlbarPrefs.get("quantumbar")) {
-    // Key down through each one-off.
-    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, "");
-
-    // Once more.  The first result should be selected.
-    EventUtils.synthesizeKey("KEY_ArrowDown");
-    assertState(
-      0,
-      -1,
-      "example.com/browser_urlbarOneOffs.js/?" + (gMaxResults - 1)
-    );
-
-    // Now key up.  Nothing should be selected again.
-    EventUtils.synthesizeKey("KEY_ArrowUp");
-    assertState(-1, -1, "");
-
-    // Key up through each one-off.
-    for (let i = numButtons - 1; i >= 0; i--) {
-      EventUtils.synthesizeKey("KEY_ArrowUp");
-      assertState(-1, i, "");
-    }
-
-    // Key right through each one-off.
-    for (let i = 1; i < numButtons; i++) {
-      EventUtils.synthesizeKey("KEY_ArrowRight");
-      assertState(-1, i, "");
-    }
-
-    // Key left through each one-off.
-    for (let i = numButtons - 2; i >= 0; i--) {
-      EventUtils.synthesizeKey("KEY_ArrowLeft");
-      assertState(-1, i, "");
-    }
-
-    // Key up through each result.
-    for (let i = gMaxResults - 1; i >= 0; i--) {
-      EventUtils.synthesizeKey("KEY_ArrowUp");
-      assertState(
-        i,
-        -1,
-        "example.com/browser_urlbarOneOffs.js/?" + (gMaxResults - i - 1)
-      );
-    }
-
-    // Key up once more.  Nothing should be selected.
-    EventUtils.synthesizeKey("KEY_ArrowUp");
-    assertState(-1, -1, "");
-
-    await hidePopup();
-    return;
-  }
-
   // Key down once more.  Nothing should be selected as one-off buttons
   // should be hidden.
   EventUtils.synthesizeKey("KEY_ArrowDown");
   assertState(-1, -1, "");
 
   await hidePopup();
 });
 
--- a/browser/components/urlbar/tests/browser/browser_urlbarOneOffs_searchSuggestions.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarOneOffs_searchSuggestions.js
@@ -129,20 +129,16 @@ add_task(async function test_clickAfterS
     );
     EventUtils.synthesizeMouseAtCenter(oneOffs[1], {});
     await resultsPromise;
   });
 });
 
 // Selects a non-default one-off engine and then selects a search suggestion.
 add_task(async function test_selectOneOffThenSuggestion() {
-  if (!UrlbarPrefs.get("quantumbar")) {
-    // The legacy address bar doesn't work correctly for this scenario.
-    return;
-  }
   await BrowserTestUtils.withNewTab(gBrowser, async () => {
     let typedValue = "foo";
     await promiseAutocompleteResultPopup(typedValue, window, true);
     await promiseSuggestionsPresent();
     assertState(0, -1, typedValue);
 
     // Select a non-default one-off engine.
     EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true });
--- a/browser/components/urlbar/tests/browser/browser_urlbarOneOffs_settings.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarOneOffs_settings.js
@@ -57,22 +57,16 @@ async function selectSettings(activateFn
         "paneSearch",
         "Should have opened the search preferences pane"
       );
     }
   );
 }
 
 add_task(async function test_open_settings_with_enter() {
-  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");
 
     Assert.ok(
       UrlbarTestUtils.getOneOffSearchButtons(
         window
       ).selectedButton.classList.contains("search-setting-button-compact"),
       "Should have selected the settings button"
--- a/browser/components/urlbar/tests/browser/browser_urlbarSearchFunction.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarSearchFunction.js
@@ -28,17 +28,16 @@ add_task(async function init() {
 add_task(async function basic() {
   let resetNotification = enableSearchSuggestionsNotification();
 
   gURLBar.blur();
   gURLBar.search("basic");
   ok(gURLBar.hasAttribute("focused"), "url bar is focused");
   await assertUrlbarValue("basic");
 
-  assertSearchSuggestionsNotificationVisible(true);
   assertOneOffButtonsVisible(true);
 
   await UrlbarTestUtils.promisePopupClose(window, () =>
     EventUtils.synthesizeKey("KEY_Escape")
   );
 
   resetNotification();
 });
@@ -50,31 +49,29 @@ add_task(async function searchEngineAlia
 
   gURLBar.blur();
   await UrlbarTestUtils.promisePopupOpen(window, () =>
     gURLBar.search("@example")
   );
   ok(gURLBar.hasAttribute("focused"), "url bar is focused");
   await assertUrlbarValue("@example");
 
-  assertSearchSuggestionsNotificationVisible(false);
   assertOneOffButtonsVisible(false);
 
   await UrlbarTestUtils.promisePopupClose(window, () =>
     EventUtils.synthesizeKey("KEY_Escape")
   );
 
   // Open the popup again (by doing another search) to make sure the
   // notification and one-off buttons are shown -- i.e., that we didn't
   // accidentally break them.
   await UrlbarTestUtils.promisePopupOpen(window, () =>
     gURLBar.search("not an engine alias")
   );
   await assertUrlbarValue("not an engine alias");
-  assertSearchSuggestionsNotificationVisible(true);
   assertOneOffButtonsVisible(true);
 
   await UrlbarTestUtils.promisePopupClose(window, () =>
     EventUtils.synthesizeKey("KEY_Escape")
   );
 
   resetNotification();
 });
@@ -86,40 +83,37 @@ add_task(async function searchRestrictio
   gURLBar.blur();
   await UrlbarTestUtils.promisePopupOpen(window, () =>
     gURLBar.search(UrlbarTokenizer.RESTRICT.SEARCH)
   );
   ok(gURLBar.hasAttribute("focused"), "url bar is focused");
   // We always add a whitespace to restrict tokens.
   await assertUrlbarValue(UrlbarTokenizer.RESTRICT.SEARCH + " ");
 
-  assertSearchSuggestionsNotificationVisible(true);
   assertOneOffButtonsVisible(false);
 
   await UrlbarTestUtils.promisePopupClose(window);
 
   resetNotification();
 });
 
 // Calls search() twice with the same value. The popup should reopen.
 add_task(async function searchTwice() {
   let resetNotification = enableSearchSuggestionsNotification();
 
   gURLBar.blur();
   await UrlbarTestUtils.promisePopupOpen(window, () => gURLBar.search("test"));
   ok(gURLBar.hasAttribute("focused"), "url bar is focused");
   await assertUrlbarValue("test");
-  assertSearchSuggestionsNotificationVisible(true);
   assertOneOffButtonsVisible(true);
   await UrlbarTestUtils.promisePopupClose(window);
 
   await UrlbarTestUtils.promisePopupOpen(window, () => gURLBar.search("test"));
   ok(gURLBar.hasAttribute("focused"), "url bar is focused");
   await assertUrlbarValue("test");
-  assertSearchSuggestionsNotificationVisible(true);
   assertOneOffButtonsVisible(true);
   await UrlbarTestUtils.promisePopupClose(window);
 
   resetNotification();
 });
 
 // Calls search() during an IME composition.
 add_task(async function searchIME() {
@@ -141,17 +135,16 @@ add_task(async function searchIME() {
   // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
   await new Promise(resolve => setTimeout(resolve, 1000));
   ok(!UrlbarTestUtils.isPopupOpen(window), "The panel should still be closed");
 
   await UrlbarTestUtils.promisePopupOpen(window, () =>
     EventUtils.synthesizeComposition({ type: "compositioncommitasis" })
   );
 
-  assertSearchSuggestionsNotificationVisible(true);
   assertOneOffButtonsVisible(true);
 
   await UrlbarTestUtils.promisePopupClose(window);
 
   resetNotification();
 });
 
 /**
@@ -171,38 +164,16 @@ function enableSearchSuggestionsNotifica
     } else {
       gURLBar._whichSearchSuggestionsNotification = which;
     }
     Services.prefs.clearUserPref("timesBeforeHidingSuggestionsHint");
   };
 }
 
 /**
- * Asserts that the search-suggestion notification is or isn't visible.
- *
- * @param {boolean} visible
- *        True if it should be visible, false if not.
- */
-function assertSearchSuggestionsNotificationVisible(visible) {
-  // TODO Bug 1525296: Not implemented for QuantumBar.
-  if (UrlbarPrefs.get("quantumbar")) {
-    return;
-  }
-  Assert.equal(
-    gURLBar.popup.classList.contains("showSearchSuggestionsNotification"),
-    visible
-  );
-  Assert.equal(
-    window.getComputedStyle(gURLBar.popup.searchSuggestionsNotification)
-      .display,
-    visible ? "-moz-deck" : "none"
-  );
-}
-
-/**
  * 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) {
   Assert.equal(
     UrlbarTestUtils.getOneOffSearchButtonsVisible(window),
--- a/browser/components/urlbar/tests/browser/browser_urlbarStopSearchOnSelection.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarStopSearchOnSelection.js
@@ -55,21 +55,19 @@ add_task(async function mainTest() {
           2 + TEST_ENGINE_NUM_EXPECTED_RESULTS
         );
       });
 
       // Type a character to start a new search.  The new search should still
       // match the open tab so that the open-tab result appears again.
       EventUtils.synthesizeKey("l");
 
-      // There should be 2 results immediately: heuristic and open tab.  (On
-      // quantumbar, there will be only 2, but on awesomebar, there will be 4
-      // since awesomebar will keep showing the excess old results for a bit.)
+      // There should be 2 results immediately: heuristic and open tab.
       await TestUtils.waitForCondition(() => {
-        return UrlbarTestUtils.getResultCount(window) >= 2;
+        return UrlbarTestUtils.getResultCount(window) == 2;
       });
 
       // Before the search completes, change the selected result.  Pressing only
       // the down arrow key ends up selecting the first one-off on Linux debug
       // builds on the infrastructure for some reason, so arrow back up to
       // select the heuristic result again.  The important thing is to change
       // the selection.  It doesn't matter which result ends up selected.
       EventUtils.synthesizeKey("KEY_ArrowDown");
@@ -97,28 +95,16 @@ add_task(async function mainTest() {
         "ampl",
         "Should have the correct query"
       );
 
       // None of the other results should be "ampl" suggestions, i.e., amplfoo
       // and amplbar should not be in the results.
       let count = UrlbarTestUtils.getResultCount(window);
       for (let i = 1; i < count; i++) {
-        if (!UrlbarPrefs.get("quantumbar")) {
-          // On awesomebar, UrlbarTestUtils.getDetailsOfResultAt() can throw due
-          // to its urlbar.controller.getFinalCompleteValueAt() call.  That's
-          // because the results at this point may contain results from two
-          // different searches.  This only seems to happen on --verify runs.
-          try {
-            gURLBar.controller.getFinalCompleteValueAt(i);
-          } catch (ex) {
-            info("getFinalCompleteValueAt exception: " + ex);
-            continue;
-          }
-        }
         result = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
         Assert.ok(
           result.type != UrlbarUtils.RESULT_TYPE.SEARCH ||
             !["amplfoo", "amplbar"].includes(result.searchParams.suggestion),
           "Suggestions should not contain the typed l char"
         );
       }
     });
--- a/browser/components/urlbar/tests/browser/browser_urlbarTokenAlias.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarTokenAlias.js
@@ -264,22 +264,16 @@ add_task(async function enterAndFillAlia
   for (; ; index++) {
     let details = await UrlbarTestUtils.getDetailsOfResultAt(window, index);
     if (details.searchParams && details.searchParams.keyword == ALIAS) {
       index++;
       break;
     }
   }
 
-  if (!UrlbarPrefs.get("quantumbar")) {
-    // With awesomebar, the first result ends up preselected, so one fewer key
-    // down is needed to reach the engine.
-    index--;
-  }
-
   // Key down to it and press enter.
   EventUtils.synthesizeKey("KEY_ArrowDown", { repeat: index });
   EventUtils.synthesizeKey("KEY_Enter");
 
   // A new search will start and its result should be the alias.
   await promiseSearchComplete();
   await waitForAutocompleteResultAt(0);
   await assertAlias(true);
--- a/browser/components/urlbar/tests/browser/browser_urlbar_remove_match.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbar_remove_match.js
@@ -19,21 +19,17 @@ add_task(async function test_remove_hist
 
   let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
   Assert.equal(result.url, TEST_URL, "Found the expected result");
 
   let expectedResultCount = UrlbarTestUtils.getResultCount(window) - 1;
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
   Assert.equal(UrlbarTestUtils.getSelectedIndex(window), 1);
-  let options =
-    UrlbarPrefs.get("quantumbar") || AppConstants.platform == "macosx"
-      ? { shiftKey: true }
-      : {};
-  EventUtils.synthesizeKey("KEY_Delete", options);
+  EventUtils.synthesizeKey("KEY_Delete", { shiftKey: true });
   await promiseVisitRemoved;
   await TestUtils.waitForCondition(
     () => UrlbarTestUtils.getResultCount(window) == expectedResultCount,
     "Waiting for the result to disappear"
   );
 
   for (let i = 0; i < expectedResultCount; i++) {
     let details = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
@@ -61,21 +57,17 @@ add_task(async function test_remove_book
   });
 
   await promiseAutocompleteResultPopup("from_urlbar");
   let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
   Assert.equal(result.url, TEST_URL, "Found the expected result");
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
   Assert.equal(UrlbarTestUtils.getSelectedIndex(window), 1);
-  let options =
-    UrlbarPrefs.get("quantumbar") || AppConstants.platform == "macosx"
-      ? { shiftKey: true }
-      : {};
-  EventUtils.synthesizeKey("KEY_Delete", options);
+  EventUtils.synthesizeKey("KEY_Delete", { shiftKey: true });
 
   // We don't have an easy way of determining if the event was process or not,
   // so let any event queues clear before testing.
   await new Promise(resolve => setTimeout(resolve, 0));
   await PlacesTestUtils.promiseAsyncUpdates();
 
   Assert.ok(
     await PlacesUtils.bookmarks.fetch({ url: TEST_URL }),
deleted file mode 100644
--- a/browser/components/urlbar/tests/legacy/.eslintrc.js
+++ /dev/null
@@ -1,7 +0,0 @@
-"use strict";
-
-module.exports = {
-  "extends": [
-    "plugin:mozilla/browser-test"
-  ]
-};
deleted file mode 100644
--- a/browser/components/urlbar/tests/legacy/browser.ini
+++ /dev/null
@@ -1,179 +0,0 @@
-[DEFAULT]
-prefs=browser.urlbar.quantumbar=false
-tags=urlbar
-support-files =
-  ../browser/dummy_page.html
-  ../browser/head-common.js
-  head.js
-
-# These tests are ones we are not porting to QuantumBar. The ones in the
-# section underneath this are ones that work in both QuantumBar and the legacy
-# address bar.
-
-# Not porting browser_switchtab_override_keynav.js/browser_switchtab_copy.js
-# to QuantumBar as we no longer have moz-action uris, so they aren't relevant
-# any more.
-[browser_switchtab_copy.js]
-tags = clipboard
-[browser_switchtab_override_keynav.js]
-# Not porting browser_urlbarAddonIframe.js - we'll be implementing new
-# WebExtension APIs
-[browser_urlbarAddonIframe.js]
-support-files =
-  ../browser/Panel.jsm
-  ../browser/urlbarAddonIframe.html
-  ../browser/urlbarAddonIframe.js
-  ../browser/urlbarAddonIframeContentScript.js
-# Not porting browser_urlbarSearchSuggestions_opt-out.js - search suggestion
-# prompt not in QuantumBar
-[browser_urlbarSearchSuggestions_opt-out.js]
-support-files =
-  ../browser/searchSuggestionEngine.xml
-  ../browser/searchSuggestionEngine.sjs
-
-
-# These are tests that are already running with QuantumBar, but we want to run them
-# against both the legacy urlbar and the new QuantumBar. The references in this
-# directory will run them against the old urlbar as per the pref above.
-
-[../browser/browser_action_searchengine.js]
-[../browser/browser_action_searchengine_alias.js]
-[../browser/browser_autocomplete_a11y_label.js]
-skip-if = (verify && !debug && (os == 'win'))
-[../browser/browser_autocomplete_autoselect.js]
-[../browser/browser_autocomplete_cursor.js]
-[../browser/browser_autocomplete_edit_completed.js]
-[../browser/browser_autocomplete_enter_race.js]
-[../browser/browser_autocomplete_no_title.js]
-[../browser/browser_autocomplete_readline_navigation.js]
-skip-if = os != "mac" # Mac only feature
-[../browser/browser_autoFill_backspaced.js]
-[../browser/browser_autoFill_canonize.js]
-[../browser/browser_autoFill_preserve.js]
-[../browser/browser_autoFill_trimURLs.js]
-[../browser/browser_autoFill_typed.js]
-[../browser/browser_autoFill_undo.js]
-[../browser/browser_autocomplete_tag_star_visibility.js]
-[../browser/browser_canonizeURL.js]
-[../browser/browser_dragdropURL.js]
-[../browser/browser_ime_composition.js]
-[../browser/browser_inputHistory.js]
-[../browser/browser_inputHistory_emptystring.js]
-[../browser/browser_keywordBookmarklets.js]
-[../browser/browser_keepStateAcrossTabSwitches.js]
-[../browser/browser_keyword_override.js]
-[../browser/browser_keywordSearch.js]
-[../browser/browser_keywordSearch_postData.js]
-uses-unsafe-cpows = true
-support-files =
-  ../browser/POSTSearchEngine.xml
-[../browser/browser_keyword_select_and_type.js]
-[../browser/browser_keyword.js]
-support-files =
-  ../browser/print_postdata.sjs
-[../browser/browser_URLBarSetURI.js]
-skip-if = (os == "linux" || os == "mac") && debug # bug 970052, bug 970053
-[../browser/browser_locationBarCommand.js]
-[../browser/browser_locationBarExternalLoad.js]
-[../browser/browser_moz_action_link.js]
-[../browser/browser_new_tab_urlbar_reset.js]
-[../browser/browser_pasteAndGo.js]
-tags = clipboard
-[../browser/browser_percent_encoded.js]
-[../browser/browser_populateAfterPushState.js]
-[../browser/browser_privateBrowsingWindowChange.js]
-[../browser/browser_raceWithTabs.js]
-skip-if = os == "linux" # Bug 1533807
-[../browser/browser_redirect_error.js]
-support-files = ../browser/redirect_error.sjs
-[../browser/browser_remotetab.js]
-[../browser/browser_removeUnsafeProtocolsFromURLBarPaste.js]
-tags = clipboard
-[../browser/browser_search_favicon.js]
-[../browser/browser_searchTelemetry.js]
-support-files =
-  ../browser/searchSuggestionEngine.xml
-  ../browser/searchSuggestionEngine.sjs
-[../browser/browser_stop_pending.js]
-support-files =
-  ../browser/slow-page.sjs
-[../browser/browser_switchTab_closesUrlbarPopup.js]
-[../browser/browser_switchTab_decodeuri.js]
-[../browser/browser_switchTab_override.js]
-skip-if = ((os == 'win') && verify && debug)
-[../browser/browser_switchToTab_closes_newtab.js]
-[../browser/browser_switchToTab_fullUrl_repeatedKeydown.js]
-skip-if = true # Bug 1507755
-[../browser/browser_switchToTabHavingURI_aOpenParams.js]
-[../browser/browser_tabMatchesInAwesomebar_perwindowpb.js]
-skip-if = os == 'linux' # Bug 1104755
-[../browser/browser_tabMatchesInAwesomebar.js]
-support-files =
-  ../browser/moz.png
-[../browser/browser_textruns.js]
-[../browser/browser_urlbarAboutHomeLoading.js]
-[../browser/browser_urlbarCopying.js]
-tags = clipboard
-support-files =
-  ../browser/authenticate.sjs
-[../browser/browser_urlbarCutting.js]
-[../browser/browser_urlbarDecode.js]
-[../browser/browser_urlbar_blanking.js]
-support-files =
-  ../browser/file_blank_but_not_blank.html
-[../browser/browser_urlbar_content_opener.js]
-[../browser/browser_urlbar_empty_search.js]
-[../browser/browser_urlbar_locationchange_urlbar_edit_dos.js]
-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_UrlbarInput_overflow_resize.js]
-[../browser/browser_urlbarOneOffs_contextMenu.js]
-support-files =
-  ../browser/searchSuggestionEngine.xml
-  ../browser/searchSuggestionEngine.sjs
-[../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]
-support-files =
-  ../browser/searchSuggestionEngine.xml
-  ../browser/searchSuggestionEngine.sjs
-[../browser/browser_urlbarStop.js]
-[../browser/browser_urlbarStopSearchOnSelection.js]
-support-files =
-  ../browser/searchSuggestionEngineSlow.xml
-  ../browser/searchSuggestionEngine.sjs
-[../browser/browser_urlbarTokenAlias.js]
-[../browser/browser_urlbarUpdateForDomainCompletion.js]
-[../browser/browser_urlbarValueOnTabSwitch.js]
-[../browser/browser_urlbar_searchsettings.js]
-[../browser/browser_urlbar_speculative_connect.js]
-support-files =
-  ../browser/searchSuggestionEngine2.xml
-  ../browser/searchSuggestionEngine.sjs
-[../browser/browser_urlbar_speculative_connect_not_with_client_cert.js]
-[../browser/browser_urlbar_remoteness_switch.js]
-run-if = e10s
-[../browser/browser_urlbar_remove_match.js]
-[../browser/browser_userTypedValue.js]
-support-files = ../browser/file_userTypedValue.html
deleted file mode 100644
--- a/browser/components/urlbar/tests/legacy/browser_switchtab_copy.js
+++ /dev/null
@@ -1,130 +0,0 @@
-/* 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/. */
-
-const TEST_URL = `${TEST_BASE_URL}dummy_page.html`;
-var testActionURL = "moz-action:switchtab," + JSON.stringify({ url: TEST_URL });
-const testURL = gURLBar.trimValue(TEST_URL);
-var testTab;
-
-function runNextTest() {
-  if (tests.length) {
-    let t = tests.shift();
-    waitForClipboard(
-      t.expected,
-      t.setup,
-      function() {
-        t.success();
-        runNextTest();
-      },
-      cleanup
-    );
-  } else {
-    cleanup();
-  }
-}
-
-function cleanup() {
-  gBrowser.removeTab(testTab);
-  finish();
-}
-
-var tests = [
-  {
-    expected: testURL,
-    setup() {
-      gURLBar.value = testActionURL;
-      gURLBar.valueIsTyped = true;
-      is(
-        gURLBar.value,
-        testActionURL,
-        "gURLBar starts with the correct real value"
-      );
-      is(
-        gURLBar.textValue,
-        testURL,
-        "gURLBar starts with the correct display value"
-      );
-
-      // Focus the urlbar so we can select it all & copy
-      gURLBar.focus();
-      gURLBar.select();
-      goDoCommand("cmd_copy");
-    },
-    success() {
-      is(
-        gURLBar.value,
-        testActionURL,
-        "gURLBar.value didn't change when copying"
-      );
-    },
-  },
-  {
-    expected: testURL.substring(0, 10),
-    setup() {
-      // Set selectionStart/End manually and make sure it matches the substring
-      gURLBar.selectionStart = 0;
-      gURLBar.selectionEnd = 10;
-      goDoCommand("cmd_copy");
-    },
-    success() {
-      is(
-        gURLBar.value,
-        testActionURL,
-        "gURLBar.value didn't change when copying"
-      );
-    },
-  },
-  {
-    expected: testURL,
-    setup() {
-      // Setup for cut test...
-      // Select all
-      gURLBar.select();
-      goDoCommand("cmd_cut");
-    },
-    success() {
-      is(gURLBar.value, "", "gURLBar.value is now empty");
-    },
-  },
-  {
-    expected: testURL.substring(testURL.length - 10, testURL.length),
-    setup() {
-      // Reset urlbar value
-      gURLBar.value = testActionURL;
-      gURLBar.valueIsTyped = true;
-      // Sanity check that we have the right value
-      is(
-        gURLBar.value,
-        testActionURL,
-        "gURLBar starts with the correct real value"
-      );
-      is(
-        gURLBar.textValue,
-        testURL,
-        "gURLBar starts with the correct display value"
-      );
-
-      // Now just select part of the value & cut that.
-      gURLBar.selectionStart = testURL.length - 10;
-      gURLBar.selectionEnd = testURL.length;
-      goDoCommand("cmd_cut");
-    },
-    success() {
-      is(
-        gURLBar.value,
-        testURL.substring(0, testURL.length - 10),
-        "gURLBar.value has the correct value"
-      );
-    },
-  },
-];
-
-function test() {
-  waitForExplicitFinish();
-  testTab = BrowserTestUtils.addTab(gBrowser);
-  gBrowser.selectedTab = testTab;
-
-  // Kick off the testing
-  runNextTest();
-}
deleted file mode 100644
--- a/browser/components/urlbar/tests/legacy/browser_switchtab_override_keynav.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/* 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/. */
-
-const TEST_URL = `${TEST_BASE_URL}dummy_page.html`;
-
-add_task(async function test_switchtab_override_keynav() {
-  info("Opening first tab");
-  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
-
-  info("Opening and selecting second tab");
-  let secondTab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser));
-  registerCleanupFunction(() => {
-    try {
-      gBrowser.removeTab(tab);
-      gBrowser.removeTab(secondTab);
-    } catch (ex) {
-      /* tabs may have already been closed in case of failure */
-    }
-    return PlacesUtils.history.clear();
-  });
-
-  gURLBar.focus();
-  gURLBar.value = "dummy_pag";
-  EventUtils.sendString("e");
-  await promiseSearchComplete();
-
-  info("Select second autocomplete popup entry");
-  EventUtils.synthesizeKey("KEY_ArrowDown");
-  ok(/moz-action:switchtab/.test(gURLBar.value), "switch to tab entry found");
-
-  info("Shift+left on switch-to-tab entry");
-
-  EventUtils.synthesizeKey("KEY_Shift", { type: "keydown" });
-  EventUtils.synthesizeKey("KEY_ArrowLeft", { shiftKey: true });
-  EventUtils.synthesizeKey("KEY_Shift", { type: "keyup" });
-
-  ok(
-    !/moz-action:switchtab/.test(gURLBar.inputField.value),
-    "switch to tab should be hidden"
-  );
-});
deleted file mode 100644
--- a/browser/components/urlbar/tests/legacy/browser_urlbarAddonIframe.js
+++ /dev/null
@@ -1,255 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-// The purpose of this test is to test the urlbar popup's add-on iframe.  It has
-// a few parts:
-//
-// (1) This file, a normal browser mochitest.
-// (2) html/js files that are loaded in the urlbar popup's add-on iframe:
-//     urlbarAddonIframe.{html,js}
-// (3) A content script that mediates between the first two parts:
-//     urlbarAddonIframeContentScript.js
-//
-// The main test file (this file) sends messages to the content script, which
-// forwards them as events to the iframe.  These messages tell the iframe js to
-// do various things like call functions on the urlbar API and expect events.
-// In response, the iframe js dispatches ack events to the content script, which
-// forwards them as messages to the main test file.
-//
-// The content script may not be necessary right now since the iframe is not
-// remote.  But this structure ensures that if the iframe is made remote in the
-// future, then the test won't have to change very much, and ideally not at all.
-//
-// Actually there's one other part:
-//
-// (4) The Panel.jsm that's bundled with add-ons that use the iframe.
-//
-// Panel.jsm defines the API that's made available to add-on scripts running in
-// the iframe.  This API is orthogonal to the add-on iframe itself.  You could
-// load any html/js in the iframe, technically.  But the purpose of the iframe
-// is to support this Panel.jsm API, so that's what this test tests.
-
-const PANEL_JSM_BASENAME = "Panel.jsm";
-const IFRAME_BASENAME = "urlbarAddonIframe.html";
-const CONTENT_SCRIPT_BASENAME = "urlbarAddonIframeContentScript.js";
-
-// The iframe's message manager.
-let gMsgMan;
-
-add_task(async function() {
-  let rootDirURL = getRootDirectory(gTestPath);
-  let jsmURL = rootDirURL + PANEL_JSM_BASENAME;
-  let iframeURL = rootDirURL + IFRAME_BASENAME;
-  let contentScriptURL = rootDirURL + CONTENT_SCRIPT_BASENAME;
-
-  let { Panel } = ChromeUtils.import(jsmURL, {});
-  let panel = new Panel(gURLBar.popup, iframeURL);
-  registerCleanupFunction(() => {
-    panel.destroy();
-    Assert.ok(gURLBar.popup._addonIframe === null, "iframe should be gone");
-  });
-
-  let iframe = gURLBar.popup._addonIframe;
-  Assert.ok(!!iframe, "iframe should not be null");
-
-  gMsgMan = iframe.frameLoader.messageManager;
-  gMsgMan.loadFrameScript(contentScriptURL, false);
-
-  await promiseIframeLoad();
-
-  // urlbar.getValue
-  let value = "this value set by the test";
-  gURLBar.value = value;
-  let readValue = await promiseUrlbarFunctionCall("getValue");
-  Assert.equal(readValue, value, "value");
-
-  // urlbar.setValue
-  value = "this value set by the iframe";
-  await promiseUrlbarFunctionCall("setValue", value);
-  Assert.equal(gURLBar.value, value, "setValue");
-
-  // urlbar.getMaxResults
-  let maxResults = gURLBar.popup.maxResults;
-  Assert.equal(typeof maxResults, "number", "Sanity check");
-  let readMaxResults = await promiseUrlbarFunctionCall("getMaxResults");
-  Assert.equal(readMaxResults, maxResults, "getMaxResults");
-
-  // urlbar.setMaxResults
-  let newMaxResults = maxResults + 10;
-  await promiseUrlbarFunctionCall("setMaxResults", newMaxResults);
-  Assert.equal(gURLBar.popup.maxResults, newMaxResults, "setMaxResults");
-  gURLBar.popup.maxResults = maxResults;
-
-  // urlbar.enter
-  value = "http://mochi.test:8888/";
-  await promiseUrlbarFunctionCall("setValue", value);
-  Assert.equal(gURLBar.value, value, "setValue");
-  await promiseUrlbarFunctionCall("enter");
-  let browser = gBrowser.selectedBrowser;
-  await BrowserTestUtils.browserLoaded(browser);
-  Assert.equal(
-    browser.currentURI.spec,
-    value,
-    "enter should have loaded the URL"
-  );
-
-  // input, reset, and result events.  There should always be at least one
-  // result, the heuristic result.
-  value = "test";
-  let promiseValues = await Promise.all([
-    promiseEvent("input")[1],
-    promiseEvent("reset")[1],
-    promiseEvent("result")[1],
-    promiseAutocompleteResultPopup(value, window, true),
-  ]);
-
-  // Check the heuristic result.
-  let result = promiseValues[2];
-  let engineName = (await Services.search.getDefault()).name;
-  Assert.deepEqual(
-    PlacesUtils.parseActionUrl(result.url),
-    {
-      type: "searchengine",
-      params: {
-        engineName,
-        input: "test",
-        searchQuery: "test",
-      },
-    },
-    "result.url"
-  );
-  Assert.ok("action" in result, "result.action");
-  Assert.equal(result.action.type, "searchengine", "result.action.type");
-  Assert.ok("params" in result.action, "result.action.params");
-  Assert.equal(
-    result.action.params.engineName,
-    engineName,
-    "result.action.params.engineName"
-  );
-  Assert.equal(typeof result.image, "string", "result.image");
-  Assert.equal(result.title, engineName, "result.title");
-  Assert.equal(result.type, "action searchengine heuristic", "result.type");
-  Assert.equal(result.text, value, "result.text");
-
-  // keydown event.  promiseEvent sends an async message to the iframe, but
-  // synthesizeKey is sync, so we need to wait until the content JS receives
-  // the message and adds its event listener before synthesizing the key.
-  let keydownPromises = promiseEvent("keydown");
-  await keydownPromises[0];
-  EventUtils.synthesizeKey("KEY_ArrowDown", { type: "keydown" });
-  await keydownPromises[1];
-
-  // urlbar.getPanelHeight
-  let height = iframe.getBoundingClientRect().height;
-  let readHeight = await promiseUrlbarFunctionCall("getPanelHeight");
-  Assert.equal(readHeight, height, "getPanelHeight");
-
-  // urlbar.setPanelHeight
-  let newHeight = height + 100;
-  await promiseUrlbarFunctionCall("setPanelHeight", newHeight);
-  // The height change is animated, so give it time to complete.
-  await TestUtils.waitForCondition(
-    () => Math.round(iframe.getBoundingClientRect().height) == newHeight,
-    "Wait for panel height change after setPanelHeight"
-  ).catch(ex => {
-    info(
-      "Last detected height: " +
-        Math.round(iframe.getBoundingClientRect().height)
-    );
-    throw ex;
-  });
-});
-
-function promiseIframeLoad() {
-  let msgName = "TestIframeLoadAck";
-  return new Promise(resolve => {
-    info("Waiting for iframe load ack");
-    gMsgMan.addMessageListener(msgName, function onMsg(msg) {
-      info("Received iframe load ack");
-      gMsgMan.removeMessageListener(msgName, onMsg);
-      resolve();
-    });
-  });
-}
-
-/**
- * Returns a single promise that's resolved when the content JS has called the
- * function.
- * @returns {Promise}
- */
-function promiseUrlbarFunctionCall(...args) {
-  return promiseMessage("function", args)[0];
-}
-
-/**
- * Returns two promises in an array.  The first is resolved when the content JS
- * has added its event listener.  The second is resolved when the content JS
- * has received the event.
- *
- * @param {string} type
- * @returns {Promise}
- */
-function promiseEvent(type) {
-  return promiseMessage("event", type, 2);
-}
-
-let gNextMessageID = 1;
-
-/**
- * Returns an array of promises, one per ack.  Each is resolved when the content
- * JS acks the message.  numExpectedAcks is the number of acks you expect.
- *
- * @param {string} type
- * @param {string} data
- * @param {number} [numExpectedAcks]
- * @returns {Promise}
- */
-function promiseMessage(type, data, numExpectedAcks = 1) {
-  let testMsgName = "TestMessage";
-  let ackMsgName = "TestMessageAck";
-  let msgID = gNextMessageID++;
-  gMsgMan.sendAsyncMessage(testMsgName, {
-    type,
-    messageID: msgID,
-    data,
-  });
-  let ackPromises = [];
-  for (let i = 0; i < numExpectedAcks; i++) {
-    let ackIndex = i;
-    ackPromises.push(
-      new Promise(resolve => {
-        info(
-          "Waiting for message ack: " +
-            JSON.stringify({
-              type,
-              msgID,
-              ackIndex,
-            })
-        );
-        gMsgMan.addMessageListener(ackMsgName, function onMsg(msg) {
-          // Messages have IDs so that an ack can be correctly paired with the
-          // initial message it's replying to.  It's not an error if the ack's ID
-          // isn't equal to msgID here.  That will happen when multiple messages
-          // have been sent in a single turn of the event loop so that they're all
-          // waiting on acks.  Same goes for ackIndex.
-          if (msg.data.messageID != msgID || msg.data.ackIndex != ackIndex) {
-            return;
-          }
-          info(
-            "Received message ack: " +
-              JSON.stringify({
-                type,
-                msgID: msg.data.messageID,
-                ackIndex,
-              })
-          );
-          gMsgMan.removeMessageListener(ackMsgName, onMsg);
-          resolve(msg.data.data);
-        });
-      })
-    );
-  }
-  return ackPromises;
-}
deleted file mode 100644
--- a/browser/components/urlbar/tests/legacy/browser_urlbarSearchSuggestions_opt-out.js
+++ /dev/null
@@ -1,174 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-/* eslint-disable mozilla/no-arbitrary-setTimeout */
-// The order of the tests here matters!
-
-const SUGGEST_ALL_PREF = "browser.search.suggest.enabled";
-const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches";
-const CHOICE_PREF = "browser.urlbar.userMadeSearchSuggestionsChoice";
-const TIMES_PREF = "browser.urlbar.timesBeforeHidingSuggestionsHint";
-const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
-const ONEOFF_PREF = "browser.urlbar.oneOffSearches";
-const NO_RESULTS_TIMEOUT_MS = 500;
-
-add_task(async function prepare() {
-  let engine = await SearchTestUtils.promiseNewSearchEngine(
-    getRootDirectory(gTestPath) + TEST_ENGINE_BASENAME
-  );
-  let oldDefaultEngine = await Services.search.getDefault();
-  await Services.search.setDefault(engine);
-  let suggestionsEnabled = Services.prefs.getBoolPref(SUGGEST_URLBAR_PREF);
-  let defaults = Services.prefs.getDefaultBranch("browser.urlbar.");
-  let searchSuggestionsDefault = defaults.getBoolPref("suggest.searches");
-  defaults.setBoolPref("suggest.searches", true);
-  let suggestionsChoice = Services.prefs.getBoolPref(CHOICE_PREF);
-  Services.prefs.setBoolPref(CHOICE_PREF, false);
-  let oneOffs = Services.prefs.getBoolPref(ONEOFF_PREF);
-  Services.prefs.setBoolPref(ONEOFF_PREF, true);
-  registerCleanupFunction(async function() {
-    defaults.setBoolPref("suggest.searches", searchSuggestionsDefault);
-    await Services.search.setDefault(oldDefaultEngine);
-    Services.prefs.clearUserPref(SUGGEST_ALL_PREF);
-    Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, suggestionsEnabled);
-    Services.prefs.setBoolPref(CHOICE_PREF, suggestionsChoice);
-    Services.prefs.setBoolPref(ONEOFF_PREF, oneOffs);
-    // Make sure the popup is closed for the next test.
-    gURLBar.blur();
-    Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
-  });
-});
-
-add_task(async function focus() {
-  // Focusing the urlbar should open the popup in order to show the
-  // notification.
-  setupVisibleHint();
-  gURLBar.blur();
-  let popupPromise = promisePopupShown(gURLBar.popup);
-  focusAndSelectUrlBar(true);
-  await popupPromise;
-  Assert.ok(gURLBar.popup.popupOpen, "popup should be open");
-  // There are no results, so wait a bit for *nothing* to appear.
-  await new Promise(resolve => setTimeout(resolve, NO_RESULTS_TIMEOUT_MS));
-  assertVisible(true);
-  assertFooterVisible(false);
-  Assert.equal(gURLBar.popup.matchCount, 0, "popup should have no results");
-
-  // Start searching.
-  EventUtils.sendString("rnd");
-  await promiseSearchComplete();
-  await waitForAutocompleteResultAt(0);
-  await promiseSuggestionsPresent();
-  assertVisible(true);
-  assertFooterVisible(true);
-
-  // Check the Change Options link.
-  let changeOptionsLink = document.getAnonymousElementByAttribute(
-    gURLBar.popup,
-    "id",
-    "search-suggestions-change-settings"
-  );
-  let prefsPromise = BrowserTestUtils.waitForLocationChange(
-    gBrowser,
-    "about:preferences#search"
-  );
-  changeOptionsLink.click();
-  await prefsPromise;
-  Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
-  // The preferences page does fancy stuff with focus, ensure to unload it.
-  await BrowserTestUtils.loadURI(gBrowser.selectedBrowser, "about:blank");
-});
-
-add_task(async function click_on_focused() {
-  // Even if the location bar is already focused, we should still show the popup
-  // and the notification on click.
-  setupVisibleHint();
-  gURLBar.blur();
-  // Won't show the hint since it's not user initiated.
-  gURLBar.focus();
-  await new Promise(resolve => setTimeout(resolve, 1000));
-  Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
-  Assert.ok(gURLBar.focused, "The input field should be focused");
-
-  let popupPromise = promisePopupShown(gURLBar.popup);
-  EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, {});
-  await popupPromise;
-  Assert.ok(gURLBar.popup.popupOpen, "popup should be open");
-  // There are no results, so wait a bit for *nothing* to appear.
-  await new Promise(resolve => setTimeout(resolve, NO_RESULTS_TIMEOUT_MS));
-  assertVisible(true);
-  assertFooterVisible(false);
-  Assert.equal(gURLBar.popup.matchCount, 0, "popup should have no results");
-  gURLBar.blur();
-  Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
-});
-
-add_task(async function new_tab() {
-  // Opening a new tab when the urlbar is unfocused, should focus it but not
-  // open the popup.
-  setupVisibleHint();
-  gURLBar.blur();
-  // openNewForegroundTab doesn't focus the urlbar.
-  await BrowserTestUtils.synthesizeKey(
-    "t",
-    { accelKey: true },
-    gBrowser.selectedBrowser
-  );
-  await new Promise(resolve => setTimeout(resolve, NO_RESULTS_TIMEOUT_MS));
-  Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
-  BrowserTestUtils.removeTab(gBrowser.selectedTab);
-});
-
-add_task(async function privateWindow() {
-  // Since suggestions are disabled in private windows, the notification should
-  // not appear even when suggestions are otherwise enabled.
-  setupVisibleHint();
-  let win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
-  await promiseAutocompleteResultPopup("foo", win);
-  // There are no results, so wait a bit for *nothing* to appear.
-  await new Promise(resolve => setTimeout(resolve, NO_RESULTS_TIMEOUT_MS));
-  assertVisible(false, win);
-  assertFooterVisible(true, win);
-  win.gURLBar.blur();
-  await BrowserTestUtils.closeWindow(win);
-});
-
-add_task(async function enableOutsideNotification() {
-  // Setting the suggest.searches pref outside the notification (e.g., by
-  // ticking the checkbox in the preferences window) should hide it.
-  setupVisibleHint();
-  Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, false);
-  await promiseAutocompleteResultPopup("foo");
-  await waitForAutocompleteResultAt(0);
-  assertVisible(false);
-  assertFooterVisible(true);
-});
-
-add_task(async function userMadeChoice() {
-  // If the user made a choice already, he should not see the hint.
-  setupVisibleHint();
-  Services.prefs.setBoolPref(CHOICE_PREF, true);
-  await promiseAutocompleteResultPopup("foo");
-  await waitForAutocompleteResultAt(0);
-  assertVisible(false);
-  assertFooterVisible(true);
-});
-
-function setupVisibleHint() {
-  Services.prefs.clearUserPref(TIMES_PREF);
-  Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true);
-  // Toggle to reset the whichNotification cache.
-  Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, false);
-  Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, true);
-}
-
-function assertVisible(visible, win = window) {
-  let style = win.getComputedStyle(
-    win.gURLBar.popup.searchSuggestionsNotification
-  );
-  let check = visible ? "notEqual" : "equal";
-  Assert[check](style.display, "none");
-}
-function assertFooterVisible(visible, win = window) {
-  let style = win.getComputedStyle(win.gURLBar.popup.footer);
-  Assert.equal(style.visibility, visible ? "visible" : "collapse");
-}
deleted file mode 100644
--- a/browser/components/urlbar/tests/legacy/head.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/* import-globals-from ../browser/head-common.js */
-Services.scriptloader.loadSubScript(
-  "chrome://mochitests/content/browser/browser/components/urlbar/tests/legacy/head-common.js",
-  this
-);
-
-function promisePopupShown(popup) {
-  return BrowserTestUtils.waitForPopupEvent(popup, "shown");
-}
--- a/browser/extensions/pdfjs/content/PdfJs.jsm
+++ b/browser/extensions/pdfjs/content/PdfJs.jsm
@@ -18,80 +18,43 @@
 var EXPORTED_SYMBOLS = ["PdfJs"];
 
 const PREF_PREFIX = "pdfjs";
 const PREF_DISABLED = PREF_PREFIX + ".disabled";
 const PREF_MIGRATION_VERSION = PREF_PREFIX + ".migrationVersion";
 const PREF_PREVIOUS_ACTION = PREF_PREFIX + ".previousHandler.preferredAction";
 const PREF_PREVIOUS_ASK =
   PREF_PREFIX + ".previousHandler.alwaysAskBeforeHandling";
-const PREF_DISABLED_PLUGIN_TYPES = "plugin.disable_full_page_plugin_for_types";
 const PREF_ENABLED_CACHE_STATE = PREF_PREFIX + ".enabledCache.state";
-const PREF_ENABLED_CACHE_INITIALIZED =
-  PREF_PREFIX + ".enabledCache.initialized";
-const PREF_APP_UPDATE_POSTUPDATE = "app.update.postupdate";
 const TOPIC_PDFJS_HANDLER_CHANGED = "pdfjs:handlerChanged";
-const TOPIC_PLUGINS_LIST_UPDATED = "plugins-list-updated";
-const TOPIC_PLUGIN_INFO_UPDATED = "plugin-info-updated";
 const PDF_CONTENT_TYPE = "application/pdf";
 
 const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 var Svc = {};
 XPCOMUtils.defineLazyServiceGetter(
   Svc,
   "mime",
   "@mozilla.org/mime;1",
   "nsIMIMEService"
 );
-XPCOMUtils.defineLazyServiceGetter(
-  Svc,
-  "pluginHost",
-  "@mozilla.org/plugin/host;1",
-  "nsIPluginHost"
-);
 ChromeUtils.defineModuleGetter(
   this,
   "PdfjsChromeUtils",
   "resource://pdf.js/PdfjsChromeUtils.jsm"
 );
 ChromeUtils.defineModuleGetter(
   this,
   "PdfJsDefaultPreferences",
   "resource://pdf.js/PdfJsDefaultPreferences.jsm"
 );
 
-function getBoolPref(aPref, aDefaultValue) {
-  try {
-    return Services.prefs.getBoolPref(aPref);
-  } catch (ex) {
-    return aDefaultValue;
-  }
-}
-
-function getIntPref(aPref, aDefaultValue) {
-  try {
-    return Services.prefs.getIntPref(aPref);
-  } catch (ex) {
-    return aDefaultValue;
-  }
-}
-
-function isDefaultHandler() {
-  if (Services.appinfo.processType !== Services.appinfo.PROCESS_TYPE_DEFAULT) {
-    throw new Error(
-      "isDefaultHandler should only get called in the parent process."
-    );
-  }
-  return PdfjsChromeUtils.isDefaultHandlerApp();
-}
-
 function initializeDefaultPreferences() {
   var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + ".");
   var defaultValue;
   for (var key in PdfJsDefaultPreferences) {
     defaultValue = PdfJsDefaultPreferences[key];
     switch (typeof defaultValue) {
       case "boolean":
         defaultBranch.setBoolPref(key, defaultValue);
@@ -105,17 +68,17 @@ function initializeDefaultPreferences() 
     }
   }
 }
 
 var PdfJs = {
   QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
   _initialized: false,
 
-  init: function init(remote) {
+  init: function init() {
     if (
       Services.appinfo.processType !== Services.appinfo.PROCESS_TYPE_DEFAULT
     ) {
       throw new Error(
         "PdfJs.init should only get called in the parent process."
       );
     }
     PdfjsChromeUtils.init();
@@ -131,45 +94,39 @@ var PdfJs = {
   },
 
   initPrefs: function initPrefs() {
     if (this._initialized) {
       return;
     }
     this._initialized = true;
 
-    if (!getBoolPref(PREF_DISABLED, true)) {
+    if (!Services.prefs.getBoolPref(PREF_DISABLED, true)) {
       this._migrate();
     }
 
     // Listen for when pdf.js is completely disabled or a different pdf handler
     // is chosen.
     Services.prefs.addObserver(PREF_DISABLED, this);
-    Services.prefs.addObserver(PREF_DISABLED_PLUGIN_TYPES, this);
     Services.obs.addObserver(this, TOPIC_PDFJS_HANDLER_CHANGED);
-    Services.obs.addObserver(this, TOPIC_PLUGINS_LIST_UPDATED);
-    Services.obs.addObserver(this, TOPIC_PLUGIN_INFO_UPDATED);
 
     initializeDefaultPreferences();
   },
 
   uninit: function uninit() {
     if (this._initialized) {
       Services.prefs.removeObserver(PREF_DISABLED, this);
-      Services.prefs.removeObserver(PREF_DISABLED_PLUGIN_TYPES, this);
       Services.obs.removeObserver(this, TOPIC_PDFJS_HANDLER_CHANGED);
-      Services.obs.removeObserver(this, TOPIC_PLUGINS_LIST_UPDATED);
-      Services.obs.removeObserver(this, TOPIC_PLUGIN_INFO_UPDATED);
       this._initialized = false;
     }
   },
 
   _migrate: function migrate() {
     const VERSION = 2;
-    var currentVersion = getIntPref(PREF_MIGRATION_VERSION, 0);
+    var currentVersion = Services.prefs.getIntPref(PREF_MIGRATION_VERSION, 0);
     if (currentVersion >= VERSION) {
       return;
     }
     // Make pdf.js the default pdf viewer on the first migration.
     if (currentVersion < 1) {
       this._becomeHandler();
     }
     if (currentVersion < 2) {
@@ -196,79 +153,32 @@ var PdfJs = {
     let handlerService = Cc[
       "@mozilla.org/uriloader/handler-service;1"
     ].getService(Ci.nsIHandlerService);
 
     // Change and save mime handler settings.
     handlerInfo.alwaysAskBeforeHandling = false;
     handlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally;
     handlerService.store(handlerInfo);
-
-    // Also disable any plugins for pdfs.
-    var stringTypes = "";
-    var types = [];
-    if (prefs.prefHasUserValue(PREF_DISABLED_PLUGIN_TYPES)) {
-      stringTypes = prefs.getCharPref(PREF_DISABLED_PLUGIN_TYPES);
-    }
-    if (stringTypes !== "") {
-      types = stringTypes.split(",");
-    }
-
-    if (!types.includes(PDF_CONTENT_TYPE)) {
-      types.push(PDF_CONTENT_TYPE);
-    }
-    prefs.setCharPref(PREF_DISABLED_PLUGIN_TYPES, types.join(","));
-
-    // Update the category manager in case the plugins are already loaded.
-    Services.catMan.deleteCategoryEntry(
-      "Gecko-Content-Viewers",
-      PDF_CONTENT_TYPE,
-      false
-    );
   },
 
   _isEnabled: function _isEnabled() {
-    var disabled = getBoolPref(PREF_DISABLED, true);
-    if (disabled) {
+    let { processType, PROCESS_TYPE_DEFAULT } = Services.appinfo;
+    if (processType !== PROCESS_TYPE_DEFAULT) {
+      throw new Error(
+        "isEnabled should only get called in the parent process."
+      );
+    }
+
+    if (Services.prefs.getBoolPref(PREF_DISABLED, true)) {
       return false;
     }
 
     // Check if the 'application/pdf' preview handler is configured properly.
-    if (!isDefaultHandler()) {
-      return false;
-    }
-
-    // Check if we have disabled plugin handling of 'application/pdf' in prefs
-    if (Services.prefs.prefHasUserValue(PREF_DISABLED_PLUGIN_TYPES)) {
-      let disabledPluginTypes = Services.prefs
-        .getCharPref(PREF_DISABLED_PLUGIN_TYPES)
-        .split(",");
-      if (disabledPluginTypes.includes(PDF_CONTENT_TYPE)) {
-        return true;
-      }
-    }
-
-    // Check if there is an enabled pdf plugin.
-    // Note: this check is performed last because getPluginTags() triggers
-    // costly plugin list initialization (bug 881575)
-    let tags = Cc["@mozilla.org/plugin/host;1"]
-      .getService(Ci.nsIPluginHost)
-      .getPluginTags();
-    let enabledPluginFound = tags.some(function(tag) {
-      if (tag.disabled) {
-        return false;
-      }
-      let mimeTypes = tag.getMimeTypes();
-      return mimeTypes.some(function(mimeType) {
-        return mimeType === PDF_CONTENT_TYPE;
-      });
-    });
-
-    // Use pdf.js if pdf plugin is not present or disabled
-    return !enabledPluginFound;
+    return PdfjsChromeUtils.isDefaultHandlerApp();
   },
 
   checkEnabled: function checkEnabled() {
     let isEnabled = this._isEnabled();
     // This will be updated any time we observe a dependency changing, since
     // updateRegistration internally calls enabled.
     Services.prefs.setBoolPref(PREF_ENABLED_CACHE_STATE, isEnabled);
     return isEnabled;
@@ -281,28 +191,9 @@ var PdfJs = {
     ) {
       throw new Error(
         "Only the parent process should be observing PDF handler changes."
       );
     }
 
     Services.ppmm.sharedData.set("pdfjs.enabled", this.checkEnabled());
   },
-
-  /**
-   * pdf.js is only enabled if it is both selected as the pdf viewer and if the
-   * global switch enabling it is true.
-   * @return {boolean} Whether or not it's enabled.
-   */
-  get enabled() {
-    if (!Services.prefs.getBoolPref(PREF_ENABLED_CACHE_INITIALIZED, false)) {
-      // If we just updated, and the cache hasn't been initialized, then we
-      // can't assume a default state, and need to synchronously initialize
-      // PdfJs
-      if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_POSTUPDATE)) {
-        this.checkEnabled();
-      }
-
-      Services.prefs.setBoolPref(PREF_ENABLED_CACHE_INITIALIZED, true);
-    }
-    return Services.prefs.getBoolPref(PREF_ENABLED_CACHE_STATE, true);
-  },
 };
--- a/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js
+++ b/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js
@@ -744,51 +744,16 @@ add_task(async function test_suggestion_
       URLBAR_SELECTED_RESULT_METHODS.enterSelection,
       1
     );
 
     BrowserTestUtils.removeTab(tab);
   });
 });
 
-// Selects through mouse right button and press the Return (Enter) key.
-add_task(async function test_suggestion_rightclick() {
-  // TODO Bug 1528250: Decide on support within QuantumBar.
-  if (UrlbarPrefs.get("quantumbar")) {
-    return;
-  }
-  Services.telemetry.clearScalars();
-  let resultMethodHist = TelemetryTestUtils.getAndClearHistogram(
-    "FX_URLBAR_SELECTED_RESULT_METHOD"
-  );
-
-  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("Right click the the second result and then press Return.");
-    await clickURLBarSuggestion("queryfoo", 2);
-    EventUtils.synthesizeKey("KEY_Enter");
-    await p;
-
-    TelemetryTestUtils.assertHistogram(
-      resultMethodHist,
-      URLBAR_SELECTED_RESULT_METHODS.rightClickEnter,
-      1
-    );
-
-    BrowserTestUtils.removeTab(tab);
-  });
-});
-
 add_task(async function test_privateWindow() {
   // Override the search telemetry search provider info to
   // count in-content SEARCH_COUNTs telemetry for our test engine.
   SearchTelemetry.overrideSearchTelemetryForTests({
     example: {
       regexp: "^http://example\\.com/",
       queryParam: "q",
     },
--- a/browser/modules/test/browser/browser_UsageTelemetry_urlbar_places.js
+++ b/browser/modules/test/browser/browser_UsageTelemetry_urlbar_places.js
@@ -262,52 +262,16 @@ add_task(async function test_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"
   );
 
--- a/build/autoconf/icu.m4
+++ b/build/autoconf/icu.m4
@@ -9,17 +9,17 @@ AC_DEFUN([MOZ_CONFIG_ICU], [
 
 MOZ_SYSTEM_ICU=
 MOZ_ARG_WITH_BOOL(system-icu,
 [  --with-system-icu
                           Use system ICU (located with pkgconfig)],
     MOZ_SYSTEM_ICU=1)
 
 if test -n "$MOZ_SYSTEM_ICU"; then
-    PKG_CHECK_MODULES(MOZ_ICU, icu-i18n >= 63.1)
+    PKG_CHECK_MODULES(MOZ_ICU, icu-i18n >= 64.1)
     CFLAGS="$CFLAGS $MOZ_ICU_CFLAGS"
     CXXFLAGS="$CXXFLAGS $MOZ_ICU_CFLAGS"
     AC_DEFINE(MOZ_SYSTEM_ICU)
 fi
 
 AC_SUBST(MOZ_SYSTEM_ICU)
 
 MOZ_ARG_WITH_STRING(intl-api,
--- a/config/check_spidermonkey_style.py
+++ b/config/check_spidermonkey_style.py
@@ -92,16 +92,17 @@ included_inclnames_to_ignore = set([
     'unicode/ucal.h',           # ICU
     'unicode/uchar.h',          # ICU
     'unicode/uclean.h',         # ICU
     'unicode/ucol.h',           # ICU
     'unicode/udat.h',           # ICU
     'unicode/udatpg.h',         # ICU
     'unicode/udisplaycontext.h',  # ICU
     'unicode/uenum.h',          # ICU
+    'unicode/uformattedvalue.h',  # ICU
     'unicode/uloc.h',           # ICU
     'unicode/unistr.h',         # ICU
     'unicode/unorm2.h',         # ICU
     'unicode/unum.h',           # ICU
     'unicode/unumsys.h',        # ICU
     'unicode/upluralrules.h',   # ICU
     'unicode/ureldatefmt.h',    # ICU
     'unicode/ustring.h',        # ICU
--- a/config/system-headers.mozbuild
+++ b/config/system-headers.mozbuild
@@ -1325,16 +1325,17 @@ if CONFIG['MOZ_SYSTEM_ICU']:
         'unicode/ucal.h',
         'unicode/uchar.h',
         'unicode/uclean.h',
         'unicode/ucol.h',
         'unicode/udat.h',
         'unicode/udatpg.h',
         'unicode/udisplaycontext.h',
         'unicode/uenum.h',
+        'unicode/uformattedvalue.h',
         'unicode/unistr.h',
         'unicode/unorm.h',
         'unicode/unum.h',
         'unicode/upluralrules.h',
         'unicode/ureldatefmt.h',
         'unicode/ustring.h',
         'unicode/utypes.h',
     ]
--- a/devtools/client/application/moz.build
+++ b/devtools/client/application/moz.build
@@ -10,8 +10,12 @@ DevToolsModules(
     'application.css',
     'initializer.js',
     'panel.js'
 )
 
 BROWSER_CHROME_MANIFESTS += [
     'test/browser/browser.ini'
 ]
+
+XPCSHELL_TESTS_MANIFESTS += [
+    'test/unit/xpcshell.ini'
+]
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/test/unit/test_page_reducer.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const {
+  updateDomain,
+} = require("devtools/client/application/src/actions/page.js");
+
+const {
+  pageReducer,
+  PageState,
+} = require("devtools/client/application/src/reducers/page-state.js");
+
+add_task(async function() {
+  info("Test page reducer: UPDATE_DOMAIN action");
+  const state = PageState();
+  const action = updateDomain("https://example.com/foo/#bar");
+
+  const newState = pageReducer(state, action);
+  equal(newState.domain, "example.com");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/test/unit/test_workers_reducer.js
@@ -0,0 +1,45 @@
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const {
+  updateCanDebugWorkers,
+  updateWorkers,
+} = require("devtools/client/application/src/actions/workers.js");
+
+const {
+  workersReducer,
+  WorkersState,
+} = require("devtools/client/application/src/reducers/workers-state.js");
+
+add_task(async function() {
+  info("Test workers reducer: UPDATE_CAN_DEBUG_WORKERS action");
+
+  function testUpdateCanDebugWorkers(flagValue) {
+    const state = WorkersState();
+    const action = updateCanDebugWorkers(flagValue);
+    const newState = workersReducer(state, action);
+    equal(
+      newState.canDebugWorkers,
+      flagValue,
+      "canDebugWorkers contains the expected value"
+    );
+  }
+
+  testUpdateCanDebugWorkers(false);
+  testUpdateCanDebugWorkers(true);
+});
+
+add_task(async function() {
+  info("Test workers reducer: UPDATE_WORKERS action");
+
+  const state = WorkersState();
+  const action = updateWorkers([{ foo: "bar" }, { lorem: "ipsum" }]);
+  const newState = workersReducer(state, action);
+  deepEqual(
+    newState.list,
+    [{ foo: "bar" }, { lorem: "ipsum" }],
+    "workers contains the expected list"
+  );
+});
--- a/devtools/client/application/test/unit/xpcshell.ini
+++ b/devtools/client/application/test/unit/xpcshell.ini
@@ -1,6 +1,8 @@
 [DEFAULT]
 tags = devtools
 head = xpcshell-head.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
+[test_page_reducer.js]
+[test_workers_reducer.js]
--- a/devtools/client/debugger/src/utils/prefs.js
+++ b/devtools/client/debugger/src/utils/prefs.js
@@ -64,16 +64,17 @@ if (isDevelopment()) {
   pref("devtools.debugger.features.map-expression-bindings", true);
   pref("devtools.debugger.features.map-await-expression", true);
   pref("devtools.debugger.features.xhr-breakpoints", true);
   pref("devtools.debugger.features.original-blackbox", true);
   pref("devtools.debugger.features.windowless-workers", true);
   pref("devtools.debugger.features.event-listeners-breakpoints", true);
   pref("devtools.debugger.features.log-points", true);
   pref("devtools.debugger.log-actions", true);
+  pref("devtools.debugger.features.overlay-step-buttons", false);
 }
 
 export const prefs = new PrefsHelper("devtools", {
   logging: ["Bool", "debugger.logging"],
   editorWrapping: ["Bool", "debugger.ui.editor-wrapping"],
   alphabetizeOutline: ["Bool", "debugger.alphabetize-outline"],
   autoPrettyPrint: ["Bool", "debugger.auto-pretty-print"],
   clientSourceMapsEnabled: ["Bool", "source-map.client-service.enabled"],
@@ -123,16 +124,17 @@ export const features = new PrefsHelper(
   autocompleteExpression: ["Bool", "autocomplete-expressions"],
   mapExpressionBindings: ["Bool", "map-expression-bindings"],
   mapAwaitExpression: ["Bool", "map-await-expression"],
   componentPane: ["Bool", "component-pane"],
   xhrBreakpoints: ["Bool", "xhr-breakpoints"],
   originalBlackbox: ["Bool", "original-blackbox"],
   eventListenersBreakpoints: ["Bool", "event-listeners-breakpoints"],
   logPoints: ["Bool", "log-points"],
+  showOverlayStepButtons: ["Bool", "debugger.features.overlay-step-buttons"],
 });
 
 export const asyncStore = asyncStoreHelper("debugger", {
   pendingBreakpoints: ["pending-breakpoints", {}],
   tabs: ["tabs", []],
   xhrBreakpoints: ["xhr-breakpoints", []],
   eventListenerBreakpoints: ["event-listener-breakpoints", undefined],
 });
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -578,16 +578,19 @@ Toolbox.prototype = {
       autoBlackBox: false,
       ignoreFrameEnvironment: true,
       pauseOnExceptions: Services.prefs.getBoolPref(
         "devtools.debugger.pause-on-exceptions"
       ),
       ignoreCaughtExceptions: Services.prefs.getBoolPref(
         "devtools.debugger.ignore-caught-exceptions"
       ),
+      showOverlayStepButtons: Services.prefs.getBoolPref(
+        "devtools.debugger.features.overlay-step-buttons"
+      ),
     };
     const [, threadClient] = await this._target.attachThread(threadOptions);
 
     try {
       await threadClient.resume();
     } catch (ex) {
       // Interpret a possible error thrown by ThreadActor.resume
       if (ex.error === "wrongOrder") {
--- a/devtools/client/locales/en-US/webconsole.properties
+++ b/devtools/client/locales/en-US/webconsole.properties
@@ -373,20 +373,27 @@ webconsole.reverseSearch.result.nextButt
 # LOCALIZATION NOTE (webconsole.confirmDialog.getter.label)
 # Label used for the "invoke getter" confirm dialog that appears in the console when
 # a user tries to autocomplete a property with a getter.
 # Example: given the following object `x = {get y() {}}`, when the user types `x.y.`, it
 # would return "Invoke getter y to retrieve the property list?".
 # Parameters: %S is the name of the getter.
 webconsole.confirmDialog.getter.label=Invoke getter %S to retrieve the property list?
 
-# LOCALIZATION NOTE (webconsole.confirmDialog.getter.invokeButtonLabel)
+# LOCALIZATION NOTE (webconsole.confirmDialog.getter.invokeButtonLabelWithShortcut)
 # Label used for the confirm button in the "invoke getter" dialog that appears in the
 # console when a user tries to autocomplete a property with a getter.
-webconsole.confirmDialog.getter.invokeButtonLabel=Invoke
+# A keyboard shortcut will be shown inside the latter pair of brackets.
+webconsole.confirmDialog.getter.invokeButtonLabelWithShortcut=Invoke (%S)
+
+# LOCALIZATION NOTE (webconsole.confirmDialog.getter.closeButton.tooltip)
+# Label used as the tooltip for the close  button in the "invoke getter" dialog that
+# appears in the console when a user tries to autocomplete a property with a getter.
+# A keyboard shortcut will be shown inside the latter pair of brackets.
+webconsole.confirmDialog.getter.closeButton.tooltip=Close (%S)
 
 # LOCALIZATION NOTE (webconsole.cssWarningElements.label)
 # Label for the list of HTML elements matching the selector associated
 # with the CSS warning. Parameters: %S is the CSS selector.
 webconsole.cssWarningElements.label=Elements matching selector: %S
 
 # LOCALIZATION NOTE (webconsole.message.componentDidCatch.label)
 # Label displayed when the webconsole couldn't handle a given packet.
--- a/devtools/client/netmonitor/src/components/TabboxPanel.js
+++ b/devtools/client/netmonitor/src/components/TabboxPanel.js
@@ -51,17 +51,17 @@ class TabboxPanel extends Component {
       activeTabId: PropTypes.string,
       cloneSelectedRequest: PropTypes.func,
       connector: PropTypes.object.isRequired,
       openLink: PropTypes.func,
       request: PropTypes.object,
       selectTab: PropTypes.func.isRequired,
       sourceMapService: PropTypes.object,
       hideToggleButton: PropTypes.bool,
-      toggleNetworkDetails: PropTypes.func.isRequired,
+      toggleNetworkDetails: PropTypes.func,
       openNetworkDetails: PropTypes.func.isRequired,
       showWebSocketsTab: PropTypes.bool,
     };
   }
 
   componentDidMount() {
     this.closeOnEscRef = this.closeOnEsc.bind(this);
     window.addEventListener("keydown", this.closeOnEscRef);
--- a/devtools/client/netmonitor/test/browser_net_copy_as_curl.js
+++ b/devtools/client/netmonitor/test/browser_net_copy_as_curl.js
@@ -43,16 +43,17 @@ add_task(async function() {
 
   const COOKIE_PARTIAL_RESULT = [header("Cookie: bob=true; tom=cool")];
 
   const POST_PAYLOAD = "Plaintext value as a payload";
   const POST_PARTIAL_RESULT = [
     "--data " + quote(POST_PAYLOAD),
     header("Content-Type: text/plain;charset=UTF-8"),
   ];
+  const ORIGIN_RESULT = [header("Origin: http://example.com")];
 
   const HEAD_PARTIAL_RESULT = ["-I"];
 
   // GET request, no cookies (first request)
   await performRequest("GET");
   await testClipboardContent([...SIMPLE_BASE, ...BASE_RESULT]);
   // Check to make sure it is still OK after we view the response (bug#1452442)
   await selectIndexAndWaitForSourceEditor(monitor, 0);
@@ -80,16 +81,17 @@ add_task(async function() {
 
   // POST request
   await performRequest("POST", POST_PAYLOAD);
   await testClipboardContent([
     ...SIMPLE_BASE,
     ...BASE_RESULT,
     ...COOKIE_PARTIAL_RESULT,
     ...POST_PARTIAL_RESULT,
+    ...ORIGIN_RESULT,
   ]);
 
   // HEAD request
   await performRequest("HEAD");
   await testClipboardContent([
     ...SIMPLE_BASE,
     ...BASE_RESULT,
     ...COOKIE_PARTIAL_RESULT,
--- a/devtools/client/preferences/debugger.js
+++ b/devtools/client/preferences/debugger.js
@@ -71,8 +71,9 @@ pref("devtools.debugger.features.async-s
 pref("devtools.debugger.features.skip-pausing", true);
 pref("devtools.debugger.features.autocomplete-expressions", false);
 pref("devtools.debugger.features.map-expression-bindings", true);
 pref("devtools.debugger.features.xhr-breakpoints", true);
 pref("devtools.debugger.features.original-blackbox", true);
 pref("devtools.debugger.features.windowless-workers", true);
 pref("devtools.debugger.features.event-listeners-breakpoints", true);
 pref("devtools.debugger.features.log-points", true);
+pref("devtools.debugger.features.overlay-step-buttons", false);
\ No newline at end of file
--- a/devtools/client/scratchpad/scratchpad.js
+++ b/devtools/client/scratchpad/scratchpad.js
@@ -2301,17 +2301,18 @@ ScratchpadSidebar.prototype = {
             },
             getObjectClient: grip => {
               return new ObjectClient(this._scratchpad.debuggerClient, grip);
             },
             getLongStringClient: actor => {
               return this._scratchpad.webConsoleClient.longString(actor);
             },
             releaseActor: actor => {
-              this._scratchpad.debuggerClient.release(actor);
+              // Ignore release failure, since the object actor may have been already GC.
+              this._scratchpad.debuggerClient.release(actor).catch(() => {});
             },
           });
         }
         this._update(obj).then(resolve);
       };
 
       if (this._sidebar.getCurrentTabID() == "variablesview") {
         onTabReady();
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -7207,17 +7207,18 @@ function rootsChanged(props) {
       data: props
     });
   };
 }
 
 function releaseActors(state, client) {
   const actors = getActors(state);
   for (const actor of actors) {
-    client.releaseActor(actor);
+    // Ignore release failure, since the object actor may have been already GC.
+    client.releaseActor(actor).catch(() => {});
   }
 }
 
 function invokeGetter(node, targetGrip, receiverId, getterName) {
   return async ({ dispatch, client, getState }) => {
     try {
       const objectClient = client.createObjectClient(targetGrip);
       const result = await objectClient.getPropertyValue(getterName, receiverId);
@@ -7580,9 +7581,9 @@ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBP
 		window.classNames = classNames;
 	}
 }());
 
 
 /***/ })
 
 /******/ });
-});
\ No newline at end of file
+});
--- a/devtools/client/shared/test/helper_addons.js
+++ b/devtools/client/shared/test/helper_addons.js
@@ -12,26 +12,16 @@ const DEBUGGER_CHROME_URL =
 const DEBUGGER_CHROME_URI = Services.io.newURI(DEBUGGER_CHROME_URL);
 
 const EventEmitter = require("devtools/shared/event-emitter");
 
 var { AddonManager } = ChromeUtils.import(
   "resource://gre/modules/AddonManager.jsm"
 );
 
-/**
- * Returns a thenable promise
- * @return {Promise}
- */
-function getDeferredPromise() {
-  // Override promise with deprecated-sync-thenables
-  const promise = require("devtools/shared/deprecated-sync-thenables");
-  return promise;
-}
-
 function getAddonURIFromPath(path) {
   const chromeURI = Services.io.newURI(path, null, DEBUGGER_CHROME_URI);
   return chromeRegistry
     .convertChromeURL(chromeURI)
     .QueryInterface(Ci.nsIFileURL);
 }
 
 function addTemporaryAddon(path) {
@@ -39,17 +29,18 @@ function addTemporaryAddon(path) {
   info("Installing addon: " + addonFile.path);
 
   return AddonManager.installTemporaryAddon(addonFile);
 }
 
 function removeAddon(addon) {
   info("Removing addon.");
 
-  const deferred = getDeferredPromise().defer();
+  const defer = require("devtools/shared/defer");
+  const deferred = defer();
 
   const listener = {
     onUninstalled: function(uninstalledAddon) {
       if (uninstalledAddon != addon) {
         return;
       }
       AddonManager.removeAddonListener(listener);
       deferred.resolve();
--- a/devtools/client/shared/test/helper_workers.js
+++ b/devtools/client/shared/test/helper_workers.js
@@ -9,31 +9,22 @@
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/debugger/test/mochitest/helpers.js",
   this
 );
 
 var { DebuggerServer } = require("devtools/server/main");
 var { DebuggerClient } = require("devtools/shared/client/debugger-client");
 var { Toolbox } = require("devtools/client/framework/toolbox");
+loader.lazyRequireGetter(this, "defer", "devtools/shared/defer");
 
 const FRAME_SCRIPT_URL = getRootDirectory(gTestPath) + "code_frame-script.js";
 
 var nextId = 0;
 
-/**
- * Returns a thenable promise
- * @return {Promise}
- */
-function getDeferredPromise() {
-  // Override promise with deprecated-sync-thenables
-  const promise = require("devtools/shared/deprecated-sync-thenables");
-  return promise;
-}
-
 function jsonrpc(tab, method, params) {
   return new Promise(function(resolve, reject) {
     const currentId = nextId++;
     const messageManager = tab.linkedBrowser.messageManager;
     messageManager.sendAsyncMessage("jsonrpc", {
       method: method,
       params: params,
       id: currentId,
@@ -203,17 +194,17 @@ async function initWorkerDebugger(TAB_UR
   };
 }
 
 // Override addTab/removeTab as defined by shared-head, since these have
 // an extra window parameter and add a frame script
 this.addTab = function addTab(url, win) {
   info("Adding tab: " + url);
 
-  const deferred = getDeferredPromise().defer();
+  const deferred = defer();
   const targetWindow = win || window;
   const targetBrowser = targetWindow.gBrowser;
 
   targetWindow.focus();
   const tab = (targetBrowser.selectedTab = BrowserTestUtils.addTab(
     targetBrowser,
     url
   ));
@@ -228,17 +219,17 @@ this.addTab = function addTab(url, win) 
   });
 
   return deferred.promise;
 };
 
 this.removeTab = function removeTab(tab, win) {
   info("Removing tab.");
 
-  const deferred = getDeferredPromise().defer();
+  const deferred = defer();
   const targetWindow = win || window;
   const targetBrowser = targetWindow.gBrowser;
   const tabContainer = targetBrowser.tabContainer;
 
   tabContainer.addEventListener(
     "TabClose",
     function() {
       info("Tab removed and finished closing.");
@@ -255,18 +246,18 @@ async function attachThreadActorForTab(t
   const target = await TargetFactory.forTab(tab);
   await target.attach();
   const [, threadClient] = await target.attachThread();
   await threadClient.resume();
   return { client: target.client, threadClient };
 }
 
 function pushPrefs(...aPrefs) {
-  const deferred = getDeferredPromise().defer();
+  const deferred = defer();
   SpecialPowers.pushPrefEnv({ set: aPrefs }, deferred.resolve);
   return deferred.promise;
 }
 
 function popPrefs() {
-  const deferred = getDeferredPromise().defer();
+  const deferred = defer();
   SpecialPowers.popPrefEnv(deferred.resolve);
   return deferred.promise;
 }
--- a/devtools/client/themes/tooltips.css
+++ b/devtools/client/themes/tooltips.css
@@ -697,28 +697,47 @@ strong {
 /* Tooltip: Invoke getter confirm Tooltip */
 
 .invoke-confirm {
   color: var(--theme-popup-color);
   border: 1px solid rgba(0,0,0, 0.1);
   max-width: 212px;
 }
 
+.invoke-confirm .close-confirm-dialog-button::before {
+  background-image: url("chrome://devtools/skin/images/close.svg");
+}
+
 .invoke-confirm .confirm-label {
   margin: 0;
   padding: 4px;
   background-color: var(--theme-toolbar-background-alt);
+  display: flex;
+  align-items: start;
+}
+
+.invoke-confirm .confirm-label p {
+  margin: 0;
+  padding: 0;
+  flex-grow: 1;
+  hyphens: auto;
 }
 
 .invoke-confirm .emphasized {
   font-family: var(--monospace-font-family);
   font-weight: bold;
   overflow-wrap: break-word;
 }
 
+.invoke-confirm .close-confirm-dialog-button {
+  padding: 0;
+  margin: 0;
+  flex-grow: 0;
+}
+
 .invoke-confirm .confirm-button {
   background-color: var(--theme-selection-background);
   color: white;
   border: none;
   padding: 6px;
   display: block;
   width: 100%;
   text-align: left;
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -1,15 +1,15 @@
 /* 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/. */
 
 /* Webconsole specific theme variables */
 :root {
-  --console-output-line-height: 14px;
+  --console-output-line-height: calc(14 / 11);
   --console-output-vertical-padding: 3px;
   /* Width of the left gutter where icons appear */
   --console-inline-start-gutter: 32px;
   /* Icons perfectly centered in the left gutter "feel" closer to the window
    * edge than to message text. This value pushes them slightly to the right. */
   --console-icon-horizontal-offset: 1px;
 }
 
--- a/devtools/client/webconsole/actions/messages.js
+++ b/devtools/client/webconsole/actions/messages.js
@@ -114,37 +114,37 @@ function messageGetMatchingElements(id, 
       })
       .catch(err => {
         console.error(err);
       });
   };
 }
 
 function messageGetTableData(id, client, dataType) {
-  return ({ dispatch }) => {
-    let fetchObjectActorData;
-    if (["Map", "WeakMap", "Set", "WeakSet"].includes(dataType)) {
-      fetchObjectActorData = cb => client.enumEntries(cb);
-    } else {
-      fetchObjectActorData = cb =>
-        client.enumProperties(
-          {
-            ignoreNonIndexedProperties: dataType === "Array",
-          },
-          cb
-        );
+  return async ({ dispatch }) => {
+    let enumResponse;
+    try {
+      if (["Map", "WeakMap", "Set", "WeakSet"].includes(dataType)) {
+        enumResponse = await client.enumEntries();
+      } else {
+        enumResponse = await client.enumProperties({
+          ignoreNonIndexedProperties: dataType === "Array",
+        });
+      }
+    } catch (e) {
+      if (e.error === "noSuchActor") {
+        return;
+      }
     }
 
-    fetchObjectActorData(enumResponse => {
-      const { iterator } = enumResponse;
-      // eslint-disable-next-line mozilla/use-returnValue
-      iterator.slice(0, iterator.count, sliceResponse => {
-        const { ownProperties } = sliceResponse;
-        dispatch(messageUpdatePayload(id, ownProperties));
-      });
+    const { iterator } = enumResponse;
+    // eslint-disable-next-line mozilla/use-returnValue
+    iterator.slice(0, iterator.count, sliceResponse => {
+      const { ownProperties } = sliceResponse;
+      dispatch(messageUpdatePayload(id, ownProperties));
     });
   };
 }
 
 /**
  * Associate additional data with a message without mutating the original message object.
  *
  * @param {String} id
--- a/devtools/client/webconsole/components/Input/ConfirmDialog.js
+++ b/devtools/client/webconsole/components/Input/ConfirmDialog.js
@@ -76,17 +76,16 @@ class ConfirmDialog extends Component {
     });
   }
 
   componentDidUpdate() {
     const { getterPath, serviceContainer } = this.props;
 
     if (getterPath) {
       this.tooltip.show(serviceContainer.getJsTermTooltipAnchor(), { y: 5 });
-      this.tooltip.focus();
     } else {
       this.tooltip.hide();
       this.props.webConsoleUI.jsterm.focus();
     }
   }
 
   componentDidThrow(e) {
     console.error("Error in ConfirmDialog", e);
@@ -118,65 +117,62 @@ class ConfirmDialog extends Component {
     const { getterPath } = this.props;
     const getterName = getterPath.join(".");
 
     // We deliberately use getStr, and not getFormatStr, because we want getterName to
     // be wrapped in its own span.
     const description = l10n.getStr("webconsole.confirmDialog.getter.label");
     const [descriptionPrefix, descriptionSuffix] = description.split("%S");
 
+    const closeButtonTooltip = l10n.getFormatStr(
+      "webconsole.confirmDialog.getter.closeButton.tooltip",
+      ["Esc"]
+    );
+    const invokeButtonLabel = l10n.getFormatStr(
+      "webconsole.confirmDialog.getter.invokeButtonLabelWithShortcut",
+      ["Tab"]
+    );
+
     const learnMoreElement = dom.a(
       {
         className: "learn-more-link",
+        key: "learn-more-link",
         title: LEARN_MORE_URL.split("?")[0],
         onClick: this.onLearnMoreClick,
       },
       l10n.getStr("webConsoleMoreInfoLabel")
     );
 
     return createPortal(
       [
-        dom.p(
+        dom.div(
           {
             className: "confirm-label",
+            key: "confirm-label",
           },
-          dom.span({}, descriptionPrefix),
-          dom.span({ className: "emphasized" }, getterName),
-          dom.span({}, descriptionSuffix)
+          dom.p(
+            {},
+            dom.span({}, descriptionPrefix),
+            dom.span({ className: "emphasized" }, getterName),
+            dom.span({}, descriptionSuffix)
+          ),
+          dom.button({
+            className: "devtools-button close-confirm-dialog-button",
+            key: "close-button",
+            title: closeButtonTooltip,
+            onClick: this.cancel,
+          })
         ),
         dom.button(
           {
             className: "confirm-button",
-            onBlur: () => this.cancel(),
-            onKeyDown: event => {
-              const { key } = event;
-              if (["Escape", "ArrowLeft", "Backspace"].includes(key)) {
-                this.cancel();
-                event.stopPropagation();
-                return;
-              }
-
-              if (["Tab", "Enter", " "].includes(key)) {
-                this.confirm();
-                event.stopPropagation();
-              }
-
-              if (key === "?") {
-                this.onLearnMoreClick();
-                event.stopPropagation();
-              }
-            },
-            // We can't use onClick because it would respond to Enter and Space keypress.
-            // We don't want that because we have a Ctrl+Space shortcut to force an
-            // autocomplete update; if the ConfirmDialog need to be displayed, since
-            // we automatically focus the button, the keyup on space would fire the onClick
-            // handler.
-            onMouseDown: this.confirm,
+            key: "confirm-button",
+            onClick: this.confirm,
           },
-          l10n.getStr("webconsole.confirmDialog.getter.invokeButtonLabel")
+          invokeButtonLabel
         ),
         learnMoreElement,
       ],
       this.tooltip.panel
     );
   }
 }
 
--- a/devtools/client/webconsole/components/Input/JSTerm.js
+++ b/devtools/client/webconsole/components/Input/JSTerm.js
@@ -277,16 +277,27 @@ class JSTerm extends Component {
             "Ctrl-Enter": onCtrlCmdEnter,
 
             Tab: () => {
               if (this.hasEmptyInput()) {
                 this.editor.codeMirror.getInputField().blur();
                 return false;
               }
 
+              if (
+                this.props.autocompleteData &&
+                this.props.autocompleteData.getterPath
+              ) {
+                this.props.autocompleteUpdate(
+                  true,
+                  this.props.autocompleteData.getterPath
+                );
+                return false;
+              }
+
               const isSomethingSelected = this.editor.somethingSelected();
               const hasSuggestion = this.hasAutocompletionSuggestion();
 
               if (hasSuggestion && !isSomethingSelected) {
                 this.acceptProposedCompletion();
                 return false;
               }
 
@@ -455,23 +466,31 @@ class JSTerm extends Component {
         this.editor.on("changes", this._inputEventHandler);
         this.editor.on("beforeChange", this._onBeforeChange);
         this.editor.appendToLocalElement(this.node);
         const cm = this.editor.codeMirror;
         cm.on("paste", (_, event) => this.props.onPaste(event));
         cm.on("drop", (_, event) => this.props.onPaste(event));
 
         this.node.addEventListener("keydown", event => {
-          if (
-            event.keyCode === KeyCodes.DOM_VK_ESCAPE &&
-            this.autocompletePopup.isOpen
-          ) {
-            this.clearCompletion();
-            event.preventDefault();
-            event.stopPropagation();
+          if (event.keyCode === KeyCodes.DOM_VK_ESCAPE) {
+            if (this.autocompletePopup.isOpen) {
+              this.clearCompletion();
+              event.preventDefault();
+              event.stopPropagation();
+            }
+
+            if (
+              this.props.autocompleteData &&
+              this.props.autocompleteData.getterPath
+            ) {
+              this.props.autocompleteClear();
+              event.preventDefault();
+              event.stopPropagation();
+            }
           }
         });
       }
     } else if (this.inputNode) {
       this.inputNode.addEventListener("keypress", this._keyPress);
       this.inputNode.addEventListener("input", this._inputEventHandler);
       this.inputNode.addEventListener("keyup", this._inputEventHandler);
       this.focus();
@@ -1065,29 +1084,38 @@ class JSTerm extends Component {
         !this.autocompletePopup.isOpen &&
         (event.shiftKey || !Debugger.isCompilableUnit(this._getValue()))
       ) {
         // shift return or incomplete statement
         return;
       }
     }
 
+    const { props } = this;
+
     switch (event.keyCode) {
       case KeyCodes.DOM_VK_ESCAPE:
         if (this.autocompletePopup.isOpen) {
           this.clearCompletion();
           event.preventDefault();
           event.stopPropagation();
+        } else if (
+          props.autocompleteData &&
+          props.autocompleteData.getterPath
+        ) {
+          props.autocompleteClear();
+          event.preventDefault();
+          event.stopPropagation();
         }
         break;
 
       case KeyCodes.DOM_VK_RETURN:
         if (this.hasAutocompletionSuggestion()) {
           this.acceptProposedCompletion();
-        } else if (!this.props.editorMode) {
+        } else if (!props.editorMode) {
           this.execute();
         } else {
           this.insertStringAtCursor("\n");
         }
         event.preventDefault();
         break;
 
       case KeyCodes.DOM_VK_UP:
@@ -1182,16 +1210,22 @@ class JSTerm extends Component {
         }
         this.clearCompletion();
         break;
 
       case KeyCodes.DOM_VK_TAB:
         if (this.hasAutocompletionSuggestion()) {
           this.acceptProposedCompletion();
           event.preventDefault();
+        } else if (
+          props.autocompleteData &&
+          props.autocompleteData.getterPath
+        ) {
+          event.preventDefault();
+          props.autocompleteUpdate(true, props.autocompleteData.getterPath);
         } else if (!this.hasEmptyInput()) {
           if (!event.shiftKey) {
             this.insertStringAtCursor("\t");
           }
           event.preventDefault();
         }
         break;
       default:
@@ -1798,18 +1832,18 @@ function mapStateToProps(state) {
 }
 
 function mapDispatchToProps(dispatch) {
   return {
     appendToHistory: expr => dispatch(historyActions.appendToHistory(expr)),
     clearHistory: () => dispatch(historyActions.clearHistory()),
     updateHistoryPosition: (direction, expression) =>
       dispatch(historyActions.updateHistoryPosition(direction, expression)),
-    autocompleteUpdate: force =>
-      dispatch(autocompleteActions.autocompleteUpdate(force)),
+    autocompleteUpdate: (force, getterPath) =>
+      dispatch(autocompleteActions.autocompleteUpdate(force, getterPath)),
     autocompleteClear: () => dispatch(autocompleteActions.autocompleteClear()),
   };
 }
 
 module.exports = connect(
   mapStateToProps,
   mapDispatchToProps
 )(JSTerm);
--- a/devtools/client/webconsole/components/Output/ConsoleTable.js
+++ b/devtools/client/webconsole/components/Output/ConsoleTable.js
@@ -155,25 +155,30 @@ function getTableItems(data = {}, type, 
   let columns = new Map();
   const items = [];
 
   const addItem = function(item) {
     items.push(item);
     Object.keys(item).forEach(key => addColumn(key));
   };
 
+  const hasValidCustomHeaders =
+    Array.isArray(headers) &&
+    headers.every(
+      header => typeof header === "string" || Number.isInteger(Number(header))
+    );
+
   const addColumn = function(columnIndex) {
     const columnExists = columns.has(columnIndex);
     const hasMaxColumns = columns.size == TABLE_COLUMN_MAX_ITEMS;
-    const hasCustomHeaders = Array.isArray(headers);
 
     if (
       !columnExists &&
       !hasMaxColumns &&
-      (!hasCustomHeaders ||
+      (!hasValidCustomHeaders ||
         headers.includes(columnIndex) ||
         columnIndex === INDEX_NAME)
     ) {
       columns.set(columnIndex, namedIndexes[columnIndex] || columnIndex);
     }
   };
 
   for (let index of Object.keys(data)) {
@@ -217,17 +222,17 @@ function getTableItems(data = {}, type, 
 
     if (items.length === TABLE_ROW_MAX_ITEMS) {
       break;
     }
   }
 
   // Some headers might not be present in the items, so we make sure to
   // return all the headers set by the user.
-  if (Array.isArray(headers)) {
+  if (hasValidCustomHeaders) {
     headers.forEach(header => addColumn(header));
   }
 
   // We want to always have the index column first
   if (columns.has(INDEX_NAME)) {
     const index = columns.get(INDEX_NAME);
     columns.delete(INDEX_NAME);
     columns = new Map([[INDEX_NAME, index], ...columns.entries()]);
--- a/devtools/client/webconsole/components/Output/message-types/NetworkEventMessage.js
+++ b/devtools/client/webconsole/components/Output/message-types/NetworkEventMessage.js
@@ -175,16 +175,21 @@ function NetworkEventMessage({
         connector,
         activeTabId: networkMessageActiveTabId,
         request: networkMessageUpdate,
         sourceMapService: serviceContainer.sourceMapService,
         openLink: serviceContainer.openLink,
         selectTab: tabId => {
           dispatch(actions.selectNetworkMessageTab(tabId));
         },
+        openNetworkDetails: enabled => {
+          if (!enabled) {
+            dispatch(actions.messageClose(id));
+          }
+        },
         hideToggleButton: true,
         showWebSocketsTab: false,
       })
     );
 
   return Message({
     dispatch,
     messageId: id,
--- a/devtools/client/webconsole/test/fixtures/stub-generators/head.js
+++ b/devtools/client/webconsole/test/fixtures/stub-generators/head.js
@@ -405,17 +405,17 @@ async function generateConsoleApiStubs()
       script.remove();
     });
 
     await received;
   }
 
   Services.prefs.clearUserPref(PREFS.FILTER.LOG);
 
-  await closeTabAndToolbox();
+  await closeTabAndToolbox().catch(() => {});
   return formatFile(stubs, "ConsoleMessage");
 }
 
 async function generateCssMessageStubs() {
   const TEST_URI =
     "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-css-message.html";
 
   const stubs = {
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -340,19 +340,20 @@ skip-if = true #	Bug 1404382
 [browser_webconsole_loglimit.js]
 [browser_webconsole_logWarningInPage.js]
 [browser_webconsole_longstring_getter.js]
 [browser_webconsole_longstring.js]
 [browser_webconsole_message_categories.js]
 [browser_webconsole_multiple_windows_and_tabs.js]
 [browser_webconsole_network_attach.js]
 [browser_webconsole_network_exceptions.js]
+[browser_webconsole_network_message_close_on_escape.js]
+[browser_webconsole_network_message_ctrl_click.js]
 [browser_webconsole_network_messages_expand.js]
 skip-if = true  # Bug 1438979
-[browser_webconsole_network_message_ctrl_click.js]
 [browser_webconsole_network_messages_openinnet.js]
 [browser_webconsole_network_messages_resend_request.js]
 [browser_webconsole_network_messages_stacktrace_console_initiated_request.js]
 [browser_webconsole_network_messages_status_code.js]
 [browser_webconsole_network_requests_from_chrome.js]
 [browser_webconsole_network_reset_filter.js]
 [browser_webconsole_nodes_highlight.js]
 [browser_webconsole_nodes_select.js]
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_getters_cache.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_getters_cache.js
@@ -53,22 +53,22 @@ async function performTests() {
   let labelEl = tooltip.querySelector(".confirm-label");
   is(
     labelEl.textContent,
     "Invoke getter foo.bar to retrieve the property list?",
     "Dialog has expected text content"
   );
 
   info(
-    "Check that hitting Enter does invoke the getter and return its properties"
+    "Check that hitting Tab does invoke the getter and return its properties"
   );
   let onPopUpOpen = autocompletePopup.once("popup-opened");
-  EventUtils.synthesizeKey("KEY_Enter");
+  EventUtils.synthesizeKey("KEY_Tab");
   await onPopUpOpen;
-  ok(autocompletePopup.isOpen, "popup is open after Enter");
+  ok(autocompletePopup.isOpen, "popup is open after Tab");
   is(
     getAutocompletePopupLabels(autocompletePopup).join("-"),
     "baz-bloop",
     "popup has expected items"
   );
   checkInputValueAndCursorPosition(hud, "foo.bar.|");
   is(isConfirmDialogOpened(toolbox), false, "confirm tooltip is now closed");
 
@@ -126,9 +126,13 @@ async function performTests() {
   ok(true, "Confirm Dialog is shown after tab navigation");
   tooltip = getConfirmDialog(toolbox);
   labelEl = tooltip.querySelector(".confirm-label");
   is(
     labelEl.textContent,
     "Invoke getter foo.bar to retrieve the property list?",
     "Dialog has expected text content"
   );
+
+  info("Close tooltip");
+  EventUtils.synthesizeKey("KEY_Escape");
+  await waitFor(() => !isConfirmDialogOpened(toolbox));
 }
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_getters_cancel.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_getters_cancel.js
@@ -40,19 +40,18 @@ async function performTests() {
   let labelEl = tooltip.querySelector(".confirm-label");
   is(
     labelEl.textContent,
     "Invoke getter foo.rab to retrieve the property list?",
     "Dialog has expected text content"
   );
 
   info("Check that Escape closes the confirm tooltip");
-  let onConfirmTooltipClosed = waitFor(() => !isConfirmDialogOpened(toolbox));
   EventUtils.synthesizeKey("KEY_Escape");
-  await onConfirmTooltipClosed;
+  await waitFor(() => !isConfirmDialogOpened(toolbox));
 
   info("Check that typing a letter won't show the tooltip");
   const onAutocompleteUpdate = jsterm.once("autocomplete-updated");
   EventUtils.sendString("t");
   await onAutocompleteUpdate;
   is(isConfirmDialogOpened(toolbox), false, "The confirm dialog is not open");
 
   info("Check that Ctrl+space show the confirm tooltip again");
@@ -61,13 +60,31 @@ async function performTests() {
   tooltip = getConfirmDialog(toolbox);
   labelEl = tooltip.querySelector(".confirm-label");
   is(
     labelEl.textContent,
     "Invoke getter foo.rab to retrieve the property list?",
     "Dialog has expected text content"
   );
 
-  info("Check that ArrowLeft closes the confirm tooltip");
-  onConfirmTooltipClosed = waitFor(() => !isConfirmDialogOpened(toolbox));
-  EventUtils.synthesizeKey("KEY_ArrowLeft");
-  await onConfirmTooltipClosed;
+  info("Check that clicking on the close button closes the tooltip");
+  const closeButtonEl = tooltip.querySelector(".close-confirm-dialog-button");
+  is(closeButtonEl.title, "Close (Esc)", "Close button has the expected title");
+  closeButtonEl.click();
+  await waitFor(() => !isConfirmDialogOpened(toolbox));
+  ok(true, "Clicking the close button does close the tooltip");
+
+  info(
+    "Check that the tooltip closes when there's no more reason to display it"
+  );
+  // Open the tooltip again
+  EventUtils.synthesizeKey(" ", { ctrlKey: true });
+  await waitFor(() => isConfirmDialogOpened(toolbox));
+
+  // Adding a space will make the input `foo.rab.t `, which we shouldn't try to
+  // autocomplete.
+  EventUtils.sendString(" ");
+  await waitFor(() => !isConfirmDialogOpened(toolbox));
+  ok(
+    true,
+    "The tooltip is now closed since the input doesn't match a getter name"
+  );
 }
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_getters_confirm.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_getters_confirm.js
@@ -57,32 +57,32 @@ async function performTests() {
   let labelEl = tooltip.querySelector(".confirm-label");
   is(
     labelEl.textContent,
     "Invoke getter window.foo.bar to retrieve the property list?",
     "Dialog has expected text content"
   );
 
   info(
-    "Check that hitting Enter does invoke the getter and return its properties"
+    "Check that hitting Tab does invoke the getter and return its properties"
   );
   let onPopUpOpen = autocompletePopup.once("popup-opened");
-  EventUtils.synthesizeKey("KEY_Enter");
+  EventUtils.synthesizeKey("KEY_Tab");
   await onPopUpOpen;
-  ok(autocompletePopup.isOpen, "popup is open after Enter");
+  ok(autocompletePopup.isOpen, "popup is open after Tab");
   is(
     getAutocompletePopupLabels(autocompletePopup).join("-"),
     "baz-bloop",
     "popup has expected items"
   );
   checkInputValueAndCursorPosition(hud, "window.foo.bar.|");
   is(isConfirmDialogOpened(toolbox), false, "confirm tooltip is now closed");
 
   let onPopUpClose = autocompletePopup.once("popup-closed");
-  EventUtils.synthesizeKey("KEY_Enter");
+  EventUtils.synthesizeKey("KEY_Tab");
   await onPopUpClose;
   checkInputValueAndCursorPosition(hud, "window.foo.bar.baz|");
 
   info(
     "Check that the invoke tooltip is displayed when performing an element access"
   );
   EventUtils.sendString("[");
   await waitFor(() => isConfirmDialogOpened(toolbox));
@@ -136,53 +136,21 @@ async function performTests() {
     "Invoke getter window.foo.rab to retrieve the property list?",
     "Dialog has expected text content"
   );
 
   info(
     "Check clicking the confirm button invokes the getter and return its properties"
   );
   onPopUpOpen = autocompletePopup.once("popup-opened");
-  EventUtils.synthesizeMouseAtCenter(
-    tooltip.querySelector(".confirm-button"),
-    {
-      type: "mousedown",
-    },
-    toolbox.win
-  );
+  tooltip.querySelector(".confirm-button").click();
   await onPopUpOpen;
   ok(
     autocompletePopup.isOpen,
     "popup is open after clicking on the confirm button"
   );
   ok(
     getAutocompletePopupLabels(autocompletePopup).includes("startsWith"),
     "popup has expected items"
   );
   checkInputValueAndCursorPosition(hud, "window.foo.rab.|");
   is(isConfirmDialogOpened(toolbox), false, "confirm tooltip is now closed");
-
-  info("Open the tooltip again");
-  tooltip = await setInputValueForGetterConfirmDialog(
-    toolbox,
-    hud,
-    "window.foo.bar."
-  );
-  labelEl = tooltip.querySelector(".confirm-label");
-  is(
-    labelEl.textContent,
-    "Invoke getter window.foo.bar to retrieve the property list?",
-    "Dialog has expected text content"
-  );
-
-  info("Check that Space invokes the getter and return its properties");
-  onPopUpOpen = autocompletePopup.once("popup-opened");
-  EventUtils.synthesizeKey(" ");
-  await onPopUpOpen;
-  ok(autocompletePopup.isOpen, "popup is open after space");
-  is(
-    getAutocompletePopupLabels(autocompletePopup).join("-"),
-    "baz-bloop",
-    "popup has expected items"
-  );
-  checkInputValueAndCursorPosition(hud, "window.foo.bar.|");
-  is(isConfirmDialogOpened(toolbox), false, "confirm tooltip is now closed");
 }
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_getters_learn_more_link.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_getters_learn_more_link.js
@@ -51,19 +51,19 @@ async function performTests() {
   );
   const learnMoreEl = tooltip.querySelector(".learn-more-link");
   is(learnMoreEl.textContent, "Learn More", `There's a "Learn more" link`);
 
   info(
     `Check that clicking on the "Learn more" link navigates to the expected page`
   );
   const expectedUri = MDN_URL + GA_PARAMS;
-  let { link } = await simulateLinkClick(learnMoreEl);
+  const { link } = await simulateLinkClick(learnMoreEl);
   is(
     link,
     expectedUri,
     `Click on "Learn More" link navigates user to ${expectedUri}`
   );
 
-  info(`Check that hitting "?" navigates to the Learn more target page`);
-  link = (await overrideOpenLink(() => EventUtils.synthesizeKey("?"))).link;
-  is(link, expectedUri, `Hitting "?" navigates user to ${expectedUri}`);
+  info("Close the popup");
+  EventUtils.synthesizeKey("KEY_Escape");
+  await waitFor(() => !isConfirmDialogOpened(toolbox));
 }
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_console_table.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_console_table.js
@@ -133,16 +133,25 @@ add_task(async function() {
       expected: {
         columns: ["(index)", "a", "b", "c", "d", "e"],
         rows: [
           ["0", "null", "false", "undefined", "0", "undefined"],
           ["1", "undefined", "null", "false", "undefined", "0"],
         ],
       },
     },
+    {
+      info: "Testing invalid headers",
+      input: ["apples", "oranges", "bananas"],
+      headers: [[]],
+      expected: {
+        columns: ["(index)", "Values"],
+        rows: [["0", "apples"], ["1", "oranges"], ["2", "bananas"]],
+      },
+    },
   ];
 
   await ContentTask.spawn(gBrowser.selectedBrowser, testCases, function(tests) {
     tests.forEach(test => {
       content.wrappedJSObject.doConsoleTable(test.input, test.headers);
     });
   });
   const nodes = [];
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_network_message_close_on_escape.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_FILE = "test-network-request.html";
+const TEST_PATH =
+  "https://example.com/browser/devtools/client/webconsole/test/mochitest/";
+const TEST_URI = TEST_PATH + TEST_FILE;
+
+add_task(async function task() {
+  await pushPref("devtools.webconsole.filter.netxhr", true);
+  const hud = await openNewTabAndConsole(TEST_URI);
+
+  const currentTab = gBrowser.selectedTab;
+  const target = await TargetFactory.forTab(currentTab);
+  const toolbox = gDevTools.getToolbox(target);
+
+  const xhrUrl = TEST_PATH + "test-data.json";
+  const onMessage = waitForMessage(hud, xhrUrl);
+
+  // Fire an XHR POST request.
+  ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+    content.wrappedJSObject.testXhrGet();
+  });
+
+  const { node: messageNode } = await onMessage;
+  ok(messageNode, "Network message found.");
+
+  // Expand network log
+  info("Click on XHR message to display network detail panel");
+  messageNode.querySelector(".url").click();
+  const headersTab = await waitFor(() =>
+    messageNode.querySelector("#headers-tab")
+  );
+  ok(headersTab, "Headers tab is available");
+
+  info("Focus header tab and hit Escape");
+  headersTab.focus();
+  EventUtils.sendKey("ESCAPE", toolbox.win);
+
+  await waitFor(() => !messageNode.querySelector(".network-info"));
+  ok(true, "The detail panel was closed on escape");
+});
--- 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
@@ -34,29 +34,35 @@ add_task(async function task() {
   info("XHR executed");
   await onNetworkMessageUpdate;
 
   const xhrUrl = TEST_PATH + "test-data.json";
   const messageNode = await waitFor(() => findMessage(hud, xhrUrl));
   const statusCodeNode = messageNode.querySelector(".status-code");
   info("Network message found.");
 
-  ok(statusCodeNode.title, l10n.getStr("webConsoleMoreInfoLabel"));
+  is(
+    statusCodeNode.title,
+    l10n.getStr("webConsoleMoreInfoLabel"),
+    "Status code has the expected tooltip"
+  );
+
   const {
     rightClickMouseEvent,
     rightClickCtrlOrCmdKeyMouseEvent,
   } = getMouseEvents();
 
   const testCases = [
     { clickEvent: null, link: LEARN_MORE_URI, where: "tab" },
     { clickEvent: rightClickMouseEvent, link: null, where: null },
     { clickEvent: rightClickCtrlOrCmdKeyMouseEvent, link: null, where: null },
   ];
 
   for (const testCase of testCases) {
+    info("Test case");
     const { clickEvent } = testCase;
     const onConsoleMenuOpened = [
       rightClickMouseEvent,
       rightClickCtrlOrCmdKeyMouseEvent,
     ].includes(clickEvent)
       ? hud.ui.wrapper.once("menu-open")
       : null;
 
@@ -65,16 +71,17 @@ add_task(async function task() {
       testCase.clickEvent
     );
     is(link, testCase.link, `Clicking the provided link opens ${link}`);
     is(where, testCase.where, `Link opened in correct tab`);
 
     if (onConsoleMenuOpened) {
       info("Check if context menu is opened on right clicking the status-code");
       await onConsoleMenuOpened;
+      ok(true, "Console menu is opened");
     }
   }
 });
 
 function getMouseEvents() {
   const isOSX = Services.appinfo.OS == "Darwin";
 
   const rightClickMouseEvent = new MouseEvent("contextmenu", {
--- a/devtools/client/webconsole/webconsole-connection-proxy.js
+++ b/devtools/client/webconsole/webconsole-connection-proxy.js
@@ -416,17 +416,17 @@ WebConsoleConnectionProxy.prototype = {
   /**
    * Release an object actor.
    *
    * @param string actor
    *        The actor ID to send the request to.
    */
   releaseActor: function(actor) {
     if (this.client) {
-      this.client.release(actor);
+      this.client.release(actor).catch(() => {});
     }
   },
 
   /**
    * Disconnect the Web Console from the remote server.
    *
    * @return object
    *         A promise object that is resolved when disconnect completes.
--- a/devtools/server/actors/highlighters.css
+++ b/devtools/server/actors/highlighters.css
@@ -579,54 +579,101 @@
 }
 
 /* Paused Debugger Overlay */
 
 :-moz-native-anonymous .paused-dbg-root {
   position: fixed;
   top: 0;
   left: 0;
-  width: 100%;
-  height: 100%;
+  zoom: 1;
+  right: 0;
+  bottom: 0;
+
+  width: 100vw;
+  height: 100vh;
 
   display: flex;
   align-items: center;
   flex-direction: column;
 
   /* We don't have access to DevTools themes here, but some of these colors come from the
      themes. Theme variable names are given in comments. */
   --text-color: #585959; /* --theme-body-color-alt */
   --toolbar-background: #fcfcfc; /* --theme-toolbar-background */;
   --toolbar-border: #dde1e4; /* --theme-splitter-color */
-  --toolbar-box-shadow: 0 4px 4px 0 rgba(155, 155, 155, 0.26); /* --rdm-box-shadow */
+  --toolbar-box-shadow: 0 2px 2px 0 rgba(155, 155, 155, 0.26); /* --rdm-box-shadow */
   --overlay-background: #dde1e4a8;
 }
 
 :-moz-native-anonymous .paused-dbg-root[overlay] {
   background-color: var(--overlay-background);
   pointer-events: auto;
 }
 
 :-moz-native-anonymous .paused-dbg-toolbar {
   margin-top: 15px;
-  padding: 4px 5px;
   display: inline-flex;
   -moz-user-select: none;
-  pointer-events: auto;
 
   color: var(--text-color);
   border-radius: 2px;
   box-shadow: var(--toolbar-box-shadow);
   background-color: var(--toolbar-background);
   border: 1px solid var(--toolbar-border);
+  border-radius: 4px;
 
   font: var(--highlighter-font-family);
   font-size: var(--highlighter-font-size);
 }
 
+:-moz-native-anonymous .paused-dbg-toolbar button {
+  margin: 8px 4px 6px 6px;
+  width: 14px;
+  height: 14px;
+  mask-size: contain;
+  mask-repeat: no-repeat;
+  mask-position: center;
+  mask-size: 14px 14px;
+  background-color: var(--text-color);
+
+  border: 0px;
+  -moz-appearance: none;
+}
+
+:-moz-native-anonymous .paused-dbg-toolbar button:hover {
+  cursor: pointer;
+}
+
+:-moz-native-anonymous .paused-dbg-divider {
+  width: 1px;
+  height: 14px;
+  margin-top: 8px;
+  background-color: var(--toolbar-border);
+}
+
+:-moz-native-anonymous button.paused-dbg-step-button {
+  margin-left: 10px;
+  mask-image: url(resource://devtools/client/debugger/images/stepOver.svg);
+}
+
+:-moz-native-anonymous button.paused-dbg-resume-button {
+  margin-right: 10px;
+  mask-image: url(resource://devtools/client/debugger/images/resume.svg);
+}
+
+:-moz-native-anonymous .paused-dbg-reason {
+  padding: 1px 16px;
+  margin: 6px 0px;
+  line-height: 16px;
+  font-size: 14px;
+  font: var(--highlighter-font-family);
+  font-size: var(--highlighter-font-size);
+}
+
 /* Shapes highlighter */
 
 :-moz-native-anonymous .shapes-root {
   pointer-events: none;
 }
 
 :-moz-native-anonymous .shapes-shape-container {
   position: absolute;
--- a/devtools/server/actors/highlighters.js
+++ b/devtools/server/actors/highlighters.js
@@ -252,16 +252,20 @@ exports.HighlighterActor = protocol.Acto
    *
    * Once a node is picked, events will cease, and listeners will be removed.
    */
   _isPicking: false,
   _hoveredNode: null,
   _currentNode: null,
 
   pick: function() {
+    if (this._targetActor.threadActor) {
+      this._targetActor.threadActor.hideOverlay();
+    }
+
     if (this._isPicking) {
       return null;
     }
     this._isPicking = true;
 
     this._preventContentEvent = event => {
       event.stopPropagation();
       event.preventDefault();
@@ -479,16 +483,19 @@ exports.HighlighterActor = protocol.Acto
     this._inspector.walker.emit("highlighter-ready");
   },
 
   _highlighterHidden: function() {
     this._inspector.walker.emit("highlighter-hide");
   },
 
   cancelPick: function() {
+    if (this._targetActor.threadActor) {
+      this._targetActor.threadActor.showOverlay();
+    }
     if (this._isPicking) {
       this._highlighter.hide();
       this._stopPickerListeners();
       this._isPicking = false;
       this._hoveredNode = null;
     }
   },
 });
--- a/devtools/server/actors/highlighters/paused-debugger.js
+++ b/devtools/server/actors/highlighters/paused-debugger.js
@@ -4,25 +4,33 @@
 
 "use strict";
 
 const {
   CanvasFrameAnonymousContentHelper,
   createNode,
 } = require("./utils/markup");
 
+const { LocalizationHelper } = require("devtools/shared/l10n");
+const STRINGS_URI = "devtools/client/shared/locales/debugger.properties";
+const L10N = new LocalizationHelper(STRINGS_URI);
+
 /**
  * The PausedDebuggerOverlay is a class that displays a semi-transparent mask on top of
  * the whole page and a toolbar at the top of the page.
  * This is used to signal to users that script execution is current paused.
  * The toolbar is used to display the reason for the pause in script execution as well as
  * buttons to resume or step through the program.
  */
-function PausedDebuggerOverlay(highlighterEnv) {
+function PausedDebuggerOverlay(highlighterEnv, options) {
   this.env = highlighterEnv;
+  this.showOverlayStepButtons = options.showOverlayStepButtons;
+  this.resume = options.resume;
+  this.stepOver = options.stepOver;
+
   this.markup = new CanvasFrameAnonymousContentHelper(
     highlighterEnv,
     this._buildMarkup.bind(this)
   );
 }
 
 PausedDebuggerOverlay.prototype = {
   typeName: "PausedDebuggerOverlay",
@@ -63,54 +71,105 @@ PausedDebuggerOverlay.prototype = {
       parent: toolbar,
       attributes: {
         id: "reason",
         class: "reason",
       },
       prefix,
     });
 
+    if (this.showOverlayStepButtons) {
+      createNode(window, {
+        parent: toolbar,
+        attributes: {
+          id: "divider",
+          class: "divider",
+        },
+        prefix,
+      });
+
+      createNode(window, {
+        nodeType: "button",
+        parent: toolbar,
+        attributes: {
+          id: "step-button",
+          class: "step-button",
+        },
+        prefix,
+      });
+
+      createNode(window, {
+        nodeType: "button",
+        parent: toolbar,
+        attributes: {
+          id: "resume-button",
+          class: "resume-button",
+        },
+        prefix,
+      });
+    }
+
     return container;
   },
 
   destroy() {
     this.hide();
     this.markup.destroy();
     this.env = null;
   },
 
+  onClick(target) {
+    if (target.id == "paused-dbg-step-button") {
+      this.stepOver();
+    } else if (target.id == "paused-dbg-resume-button") {
+      this.resume();
+    }
+  },
+
+  handleEvent(e) {
+    switch (e.type) {
+      case "click":
+      case "mouseup":
+        this.onClick(e.target);
+        break;
+      case "DOMMouseScroll":
+        // Prevent scrolling. That's because we only took a screenshot of the viewport, so
+        // scrolling out of the viewport wouldn't draw the expected things. In the future
+        // we can take the screenshot again on scroll, but for now it doesn't seem
+        // important.
+        e.preventDefault();
+        break;
+      case "mouseover":
+        console.log(`> mouse over ${e.target.id}`);
+        break;
+    }
+  },
+
   getElement(id) {
     return this.markup.getElement(this.ID_CLASS_PREFIX + id);
   },
 
   show(node, options = {}) {
-    if (this.env.isXUL) {
+    if (this.env.isXUL || !options.reason) {
       return false;
     }
 
     // Show the highlighter's root element.
     const root = this.getElement("root");
     root.removeAttribute("hidden");
-
-    // The page overlay is only shown upon request. Sometimes we just want the toolbar.
-    if (options.onlyToolbar) {
-      root.removeAttribute("overlay");
-    } else {
-      root.setAttribute("overlay", "true");
-    }
+    root.setAttribute("overlay", "true");
 
     // Set the text to appear in the toolbar.
     const toolbar = this.getElement("toolbar");
-    if (options.reason) {
-      this.getElement("reason").setTextContent(options.reason);
-      toolbar.removeAttribute("hidden");
-    } else {
-      toolbar.setAttribute("hidden", "true");
-    }
+    this.getElement("reason").setTextContent(
+      L10N.getStr(`whyPaused.${options.reason}`)
+    );
+    toolbar.removeAttribute("hidden");
 
+    this.env.window.document.setSuppressedEventListener(this);
     return true;
   },
 
   hide() {
     if (this.env.isXUL) {
       return;
     }
 
--- a/devtools/server/actors/thread.js
+++ b/devtools/server/actors/thread.js
@@ -48,16 +48,29 @@ loader.lazyRequireGetter(
 loader.lazyRequireGetter(
   this,
   "FrameActor",
   "devtools/server/actors/frame",
   true
 );
 loader.lazyRequireGetter(this, "throttle", "devtools/shared/throttle", true);
 
+loader.lazyRequireGetter(
+  this,
+  "HighlighterEnvironment",
+  "devtools/server/actors/highlighters",
+  true
+);
+loader.lazyRequireGetter(
+  this,
+  "PausedDebuggerOverlay",
+  "devtools/server/actors/highlighters/paused-debugger",
+  true
+);
+
 /**
  * JSD2 actors.
  */
 
 /**
  * Creates a ThreadActor.
  *
  * ThreadActors manage a JSInspector object and manage execution/inspection
@@ -86,16 +99,17 @@ const ThreadActor = ActorClassWithSpec(t
     this._gripDepth = 0;
     this._threadLifetimePool = null;
     this._parentClosed = false;
     this._scripts = null;
     this._xhrBreakpoints = [];
     this._observingNetwork = false;
     this._activeEventBreakpoints = new Set();
     this._activeEventPause = null;
+    this._pauseOverlay = null;
 
     this._priorPause = null;
 
     this._options = {
       autoBlackBox: false,
     };
 
     this.breakpointActorMap = new BreakpointActorMap(this);
@@ -236,16 +250,20 @@ const ThreadActor = ActorClassWithSpec(t
     const eventLoop = this._threadPauseEventLoops.pop();
     assert(eventLoop, "Should have an event loop.");
     eventLoop.resolve();
     if (this.dbg.replaying) {
       this.dbg.replayPopThreadPause();
     }
   },
 
+  isPaused() {
+    return this._state === "paused";
+  },
+
   /**
    * Remove all debuggees and clear out the thread's sources.
    */
   clearDebuggees: function() {
     if (this._dbg) {
       this.dbg.removeAllDebuggees();
     }
     this._sources = null;
@@ -391,16 +409,50 @@ const ThreadActor = ActorClassWithSpec(t
   },
 
   _setBreakpointsOnAttach(breakpoints) {
     for (const { location, options } of Object.values(breakpoints)) {
       this.setBreakpoint(location, options);
     }
   },
 
+  get pauseOverlay() {
+    if (this._pauseOverlay) {
+      return this._pauseOverlay;
+    }
+
+    const env = new HighlighterEnvironment();
+    env.initFromTargetActor(this._parent);
+    const highlighter = new PausedDebuggerOverlay(env, {
+      showOverlayStepButtons: this._options.showOverlayStepButtons,
+      resume: () => this.onResume({ resumeLimit: null }),
+      stepOver: () => this.onResume({ resumeLimit: { type: "next" } }),
+    });
+    this._pauseOverlay = highlighter;
+    return highlighter;
+  },
+
+  showOverlay() {
+    if (
+      this._parent.on &&
+      this.pauseOverlay &&
+      !this._parent.window.isChromeWindow &&
+      this.isPaused()
+    ) {
+      const reason = this._priorPause.why.type;
+      this.pauseOverlay.show(null, { reason });
+    }
+  },
+
+  hideOverlay(msg) {
+    if (this._parent.on && !this._parent.window.isChromeWindow) {
+      this.pauseOverlay.hide();
+    }
+  },
+
   /**
    * Tell the thread to automatically add a breakpoint on the first line of
    * a given file, when it is first loaded.
    *
    * This is currently only used by the xpcshell test harness, and unless
    * we decide to expand the scope of this feature, we should keep it that way.
    */
   setBreakpointOnLoad(urls) {
@@ -707,16 +759,17 @@ const ThreadActor = ActorClassWithSpec(t
         actor: sourceActor.actorID,
         line: line,
         column: column,
       };
       const pkt = onPacket(packet);
 
       this._priorPause = pkt;
       this.conn.sendActorEvent(this.actorID, "paused", pkt);
+      this.showOverlay();
     } catch (error) {
       reportError(error);
       this.conn.send({
         error: "unknownError",
         message: error.message + "\n" + error.stack,
       });
       return undefined;
     }
@@ -1224,16 +1277,17 @@ const ThreadActor = ActorClassWithSpec(t
     this.conn.removeActorPool(this._pausePool);
 
     this._pausePool = null;
     this._pauseActor = null;
     this._popThreadPause();
     // Tell anyone who cares of the resume (as of now, that's the xpcshell harness and
     // devtools-startup.js when handling the --wait-for-jsdebugger flag)
     this.conn.sendActorEvent(this.actorID, "resumed");
+    this.hideOverlay();
 
     if (Services.obs) {
       Services.obs.notifyObservers(this, "devtools-thread-resumed");
     }
   },
 
   /**
    * Spin up a nested event loop so we can synchronously resolve a promise.
--- a/devtools/server/tests/browser/inspector-helpers.js
+++ b/devtools/server/tests/browser/inspector-helpers.js
@@ -110,30 +110,32 @@ async function assertOwnershipTrees(walk
 function checkMissing({ client }, actorID) {
   return new Promise(resolve => {
     const front = client.getActor(actorID);
     ok(
       !front,
       "Front shouldn't be accessible from the client for actorID: " + actorID
     );
 
-    client.request(
-      {
-        to: actorID,
-        type: "request",
-      },
-      response => {
-        is(
-          response.error,
-          "noSuchActor",
-          "node list actor should no longer be contactable."
-        );
-        resolve(undefined);
-      }
-    );
+    client
+      .request(
+        {
+          to: actorID,
+          type: "request",
+        },
+        response => {
+          is(
+            response.error,
+            "noSuchActor",
+            "node list actor should no longer be contactable."
+          );
+          resolve(undefined);
+        }
+      )
+      .catch(() => {});
   });
 }
 
 // Load mutations aren't predictable, so keep accumulating mutations until
 // the one we're looking for shows up.
 function waitForMutation(walker, test, mutations = []) {
   return new Promise(resolve => {
     for (const change of mutations) {
--- a/devtools/server/tests/mochitest/test_highlighter_paused_debugger.html
+++ b/devtools/server/tests/mochitest/test_highlighter_paused_debugger.html
@@ -20,17 +20,17 @@ window.onload = function() {
   const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
   require("devtools/server/actors/inspector/inspector");
   const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
   const {PausedDebuggerOverlay} = require("devtools/server/actors/highlighters/paused-debugger");
 
   const env = new HighlighterEnvironment();
   env.initFromWindow(window);
 
-  const highlighter = new PausedDebuggerOverlay(env);
+  const highlighter = new PausedDebuggerOverlay(env, {});
   const anonymousContent = highlighter.markup.content;
 
   const id = elementID => `${highlighter.ID_CLASS_PREFIX}${elementID}`;
 
   function isHidden(elementID) {
     const attr = anonymousContent.getAttributeForElement(id(elementID), "hidden");
     return typeof attr === "string" && attr == "true";
   }
@@ -48,42 +48,38 @@ window.onload = function() {
   ok(highlighter.getElement("root"), "The root wrapper element exists");
   ok(highlighter.getElement("toolbar"), "The toolbar element exists");
   ok(highlighter.getElement("reason"), "The reason label element exists");
 
   info("Test that the highlighter is hidden by default");
   ok(isHidden("root"), "The highlighter is hidden");
 
   info("Show the highlighter with overlay and toolbar");
-  let didShow = highlighter.show(null, {"reason": "Paused in debugger"});
+  let didShow = highlighter.show(null, { "reason": "breakpoint" });
   ok(didShow, "Calling show returned true");
   ok(!isHidden("root"), "The highlighter is shown");
   ok(isOverlayShown(), "The overlay is shown");
-  is(getReason(), "Paused in debugger", "The reason displayed in the toolbar is correct");
+  is(
+    getReason(),
+    "Paused on breakpoint",
+    "The reason displayed in the toolbar is correct"
+  );
 
   info("Call show again with another reason");
-  didShow = highlighter.show(null, {"reason": "Paused for another reason"});
+  didShow = highlighter.show(null, {"reason": "debuggerStatement"});
   ok(didShow, "Calling show returned true too");
   ok(!isHidden("root"), "The highlighter is still shown");
-  is(getReason(), "Paused for another reason",
+  is(getReason(), "Paused on debugger statement",
      "The reason displayed in the toolbar is correct again");
   ok(isOverlayShown(), "The overlay is still shown too");
 
   info("Call show again but with no reason");
   highlighter.show();
-  ok(isHidden("toolbar"), "The toolbar is hidden");
   ok(isOverlayShown(), "The overlay is shown however");
 
-  info("Call show again with a reason but no overlay");
-  highlighter.show(null, {reason: "no overlay this time", onlyToolbar: true});
-  ok(!isHidden("toolbar"), "The toolbar is shown this time");
-  is(getReason(), "no overlay this time",
-     "The reason displayed in the toolbar is correct again");
-  ok(!isOverlayShown(), "The overlay is hidden");
-
   info("Hide the highlighter");
   highlighter.hide();
   ok(isHidden("root"), "The highlighter is now hidden");
 
   SimpleTest.finish();
 };
 </script>
 </pre>
--- a/devtools/server/tests/unit/test_client_request.js
+++ b/devtools/server/tests/unit/test_client_request.js
@@ -100,17 +100,17 @@ function test_client_request_promise() {
   const request = gClient.request({
     to: gActorId,
     type: "hello",
   });
 
   request.then(response => {
     Assert.equal(response.from, gActorId);
     Assert.equal(response.hello, "world");
-    checkStack("test_client_request_promise");
+    checkStack("test_client_request_promise/<");
     run_next_test();
   });
 }
 
 function test_client_request_promise_error() {
   // Test that DebuggerClient.request returns a promise that reject when server
   // returns an explicit error message
   const request = gClient.request({
@@ -121,17 +121,17 @@ function test_client_request_promise_err
   request.then(
     () => {
       do_throw("Promise shouldn't be resolved on error");
     },
     response => {
       Assert.equal(response.from, gActorId);
       Assert.equal(response.error, "code");
       Assert.equal(response.message, "human message");
-      checkStack("test_client_request_promise_error");
+      checkStack("test_client_request_promise_error/<");
       run_next_test();
     }
   );
 }
 
 function test_client_request_event_emitter() {
   // Test that DebuggerClient.request returns also an EventEmitter object
   const request = gClient.request({
@@ -237,25 +237,27 @@ function test_client_request_after_close
       run_next_test();
     }
   );
 }
 
 function test_client_request_after_close_callback() {
   // Test that DebuggerClient.request fails after we called client.close()
   // (with callback API)
-  gClient.request(
-    {
-      to: gActorId,
-      type: "hello",
-    },
-    response => {
-      ok(true, "Request failed after client.close");
-      Assert.equal(response.error, "connectionClosed");
-      ok(
-        response.message.match(
-          /'hello' request packet to '.*' can't be sent as the connection is closed./
-        )
-      );
-      run_next_test();
-    }
-  );
+  gClient
+    .request(
+      {
+        to: gActorId,
+        type: "hello",
+      },
+      response => {
+        ok(true, "Request failed after client.close");
+        Assert.equal(response.error, "connectionClosed");
+        ok(
+          response.message.match(
+            /'hello' request packet to '.*' can't be sent as the connection is closed./
+          )
+        );
+        run_next_test();
+      }
+    )
+    .catch(() => info("Caught rejected promise as expected"));
 }
--- a/devtools/server/tests/unit/test_pauselifetime-01.js
+++ b/devtools/server/tests/unit/test_pauselifetime-01.js
@@ -29,36 +29,40 @@ function run_test() {
       gThreadClient = threadClient;
       test_pause_frame();
     });
   });
   do_test_pending();
 }
 
 function test_pause_frame() {
-  gThreadClient.once("paused", function(packet) {
+  gThreadClient.once("paused", async function(packet) {
     const pauseActor = packet.actor;
 
     // Make a bogus request to the pause-lifetime actor.  Should get
     // unrecognized-packet-type (and not no-such-actor).
-    gClient.request({ to: pauseActor, type: "bogusRequest" }, function(
-      response
-    ) {
-      Assert.equal(response.error, "unrecognizedPacketType");
+    try {
+      await gClient.request({ to: pauseActor, type: "bogusRequest" });
+      ok(false, "bogusRequest should throw");
+    } catch (e) {
+      ok(true, "bogusRequest thrown");
+      Assert.equal(e.error, "unrecognizedPacketType");
+    }
 
-      gThreadClient.resume().then(function() {
-        // Now that we've resumed, should get no-such-actor for the
-        // same request.
-        gClient.request({ to: pauseActor, type: "bogusRequest" }, function(
-          response
-        ) {
-          Assert.equal(response.error, "noSuchActor");
-          finishClient(gClient);
-        });
-      });
+    gThreadClient.resume().then(async function() {
+      // Now that we've resumed, should get no-such-actor for the
+      // same request.
+      try {
+        await gClient.request({ to: pauseActor, type: "bogusRequest" });
+        ok(false, "bogusRequest should throw");
+      } catch (e) {
+        ok(true, "bogusRequest thrown");
+        Assert.equal(e.error, "noSuchActor");
+      }
+      finishClient(gClient);
     });
   });
 
   gDebuggee.eval(
     "(" +
       function() {
         function stopMe() {
           debugger;
--- a/devtools/server/tests/unit/test_pauselifetime-02.js
+++ b/devtools/server/tests/unit/test_pauselifetime-02.js
@@ -29,37 +29,43 @@ function run_test() {
       gThreadClient = threadClient;
       test_pause_frame();
     });
   });
   do_test_pending();
 }
 
 function test_pause_frame() {
-  gThreadClient.once("paused", function(packet) {
+  gThreadClient.once("paused", async function(packet) {
     const args = packet.frame.arguments;
     const objActor = args[0].actor;
     Assert.equal(args[0].class, "Object");
     Assert.ok(!!objActor);
 
     // Make a bogus request to the grip actor.  Should get
     // unrecognized-packet-type (and not no-such-actor).
-    gClient.request({ to: objActor, type: "bogusRequest" }, function(response) {
-      Assert.equal(response.error, "unrecognizedPacketType");
+    try {
+      await gClient.request({ to: objActor, type: "bogusRequest" });
+      ok(false, "bogusRequest should throw");
+    } catch (e) {
+      ok(true, "bogusRequest thrown");
+      Assert.equal(e.error, "unrecognizedPacketType");
+    }
 
-      gThreadClient.resume().then(function() {
-        // Now that we've resumed, should get no-such-actor for the
-        // same request.
-        gClient.request({ to: objActor, type: "bogusRequest" }, function(
-          response
-        ) {
-          Assert.equal(response.error, "noSuchActor");
-          finishClient(gClient);
-        });
-      });
+    gThreadClient.resume().then(async function() {
+      // Now that we've resumed, should get no-such-actor for the
+      // same request.
+      try {
+        await gClient.request({ to: objActor, type: "bogusRequest" });
+        ok(false, "bogusRequest should throw");
+      } catch (e) {
+        ok(true, "bogusRequest thrown");
+        Assert.equal(e.error, "noSuchActor");
+      }
+      finishClient(gClient);
     });
   });
 
   gDebuggee.eval(
     "(" +
       function() {
         function stopMe(obj) {
           debugger;
--- a/devtools/server/tests/unit/test_pauselifetime-03.js
+++ b/devtools/server/tests/unit/test_pauselifetime-03.js
@@ -29,42 +29,48 @@ function run_test() {
       gThreadClient = threadClient;
       test_pause_frame();
     });
   });
   do_test_pending();
 }
 
 function test_pause_frame() {
-  gThreadClient.once("paused", function(packet) {
+  gThreadClient.once("paused", async function(packet) {
     const args = packet.frame.arguments;
     const objActor = args[0].actor;
     Assert.equal(args[0].class, "Object");
     Assert.ok(!!objActor);
 
     const objClient = gThreadClient.pauseGrip(args[0]);
     Assert.ok(objClient.valid);
 
     // Make a bogus request to the grip actor.  Should get
     // unrecognized-packet-type (and not no-such-actor).
-    gClient.request({ to: objActor, type: "bogusRequest" }, function(response) {
-      Assert.equal(response.error, "unrecognizedPacketType");
-      Assert.ok(objClient.valid);
+    try {
+      await gClient.request({ to: objActor, type: "bogusRequest" });
+      ok(false, "bogusRequest should throw");
+    } catch (e) {
+      ok(true, "bogusRequest thrown");
+      Assert.equal(e.error, "unrecognizedPacketType");
+    }
+    Assert.ok(objClient.valid);
 
-      gThreadClient.resume().then(function() {
-        // Now that we've resumed, should get no-such-actor for the
-        // same request.
-        gClient.request({ to: objActor, type: "bogusRequest" }, function(
-          response
-        ) {
-          Assert.ok(!objClient.valid);
-          Assert.equal(response.error, "noSuchActor");
-          finishClient(gClient);
-        });
-      });
+    gThreadClient.resume().then(async function() {
+      // Now that we've resumed, should get no-such-actor for the
+      // same request.
+      try {
+        await gClient.request({ to: objActor, type: "bogusRequest" });
+        ok(false, "bogusRequest should throw");
+      } catch (e) {
+        ok(true, "bogusRequest thrown");
+        Assert.equal(e.error, "noSuchActor");
+      }
+      Assert.ok(!objClient.valid);
+      finishClient(gClient);
     });
   });
 
   gDebuggee.eval(
     "(" +
       function() {
         function stopMe(obj) {
           debugger;
--- a/devtools/server/tests/unit/test_threadlifetime-01.js
+++ b/devtools/server/tests/unit/test_threadlifetime-01.js
@@ -29,41 +29,43 @@ function run_test() {
       gThreadClient = threadClient;
       test_thread_lifetime();
     });
   });
   do_test_pending();
 }
 
 function test_thread_lifetime() {
-  gThreadClient.once("paused", function(packet) {
+  gThreadClient.once("paused", async function(packet) {
     const pauseGrip = packet.frame.arguments[0];
 
     // Create a thread-lifetime actor for this object.
-    gClient.request({ to: pauseGrip.actor, type: "threadGrip" }, function(
-      response
-    ) {
-      // Successful promotion won't return an error.
-      Assert.equal(response.error, undefined);
-      gThreadClient.once("paused", function(packet) {
-        // Verify that the promoted actor is returned again.
-        Assert.equal(pauseGrip.actor, packet.frame.arguments[0].actor);
-        // Now that we've resumed, should get unrecognizePacketType for the
-        // promoted grip.
-        gClient.request({ to: pauseGrip.actor, type: "bogusRequest" }, function(
-          response
-        ) {
-          Assert.equal(response.error, "unrecognizedPacketType");
-          gThreadClient.resume().then(function() {
-            finishClient(gClient);
-          });
-        });
+    const response = await gClient.request({
+      to: pauseGrip.actor,
+      type: "threadGrip",
+    });
+    // Successful promotion won't return an error.
+    Assert.equal(response.error, undefined);
+    gThreadClient.once("paused", async function(packet) {
+      // Verify that the promoted actor is returned again.
+      Assert.equal(pauseGrip.actor, packet.frame.arguments[0].actor);
+      // Now that we've resumed, should get unrecognizePacketType for the
+      // promoted grip.
+      try {
+        await gClient.request({ to: pauseGrip.actor, type: "bogusRequest" });
+        ok(false, "bogusRequest should throw");
+      } catch (e) {
+        Assert.equal(e.error, "unrecognizedPacketType");
+        ok(true, "bogusRequest thrown");
+      }
+      gThreadClient.resume().then(function() {
+        finishClient(gClient);
       });
-      gThreadClient.resume();
     });
+    gThreadClient.resume();
   });
 
   gDebuggee.eval(
     "(" +
       function() {
         function stopMe(arg1) {
           debugger;
           debugger;
--- a/devtools/server/tests/unit/test_threadlifetime-02.js
+++ b/devtools/server/tests/unit/test_threadlifetime-02.js
@@ -29,43 +29,48 @@ function run_test() {
       gThreadClient = threadClient;
       test_thread_lifetime();
     });
   });
   do_test_pending();
 }
 
 function test_thread_lifetime() {
-  gThreadClient.once("paused", function(packet) {
+  gThreadClient.once("paused", async function(packet) {
     const pauseGrip = packet.frame.arguments[0];
 
     // Create a thread-lifetime actor for this object.
-    gClient.request({ to: pauseGrip.actor, type: "threadGrip" }, function(
-      response
-    ) {
-      // Successful promotion won't return an error.
-      Assert.equal(response.error, undefined);
-      gThreadClient.once("paused", function(packet) {
-        // Verify that the promoted actor is returned again.
-        Assert.equal(pauseGrip.actor, packet.frame.arguments[0].actor);
-        // Now that we've resumed, release the thread-lifetime grip.
-        gClient.release(pauseGrip.actor, function(response) {
-          gClient.request(
+    const response = await gClient.request({
+      to: pauseGrip.actor,
+      type: "threadGrip",
+    });
+    // Successful promotion won't return an error.
+    Assert.equal(response.error, undefined);
+    gThreadClient.once("paused", function(packet) {
+      // Verify that the promoted actor is returned again.
+      Assert.equal(pauseGrip.actor, packet.frame.arguments[0].actor);
+      // Now that we've resumed, release the thread-lifetime grip.
+      gClient.release(pauseGrip.actor, async function(response) {
+        try {
+          await gClient.request(
             { to: pauseGrip.actor, type: "bogusRequest" },
             function(response) {
               Assert.equal(response.error, "noSuchActor");
               gThreadClient.resume().then(function() {
                 finishClient(gClient);
               });
             }
           );
-        });
+          ok(false, "bogusRequest should throw");
+        } catch (e) {
+          ok(true, "bogusRequest thrown");
+        }
       });
-      gThreadClient.resume();
     });
+    gThreadClient.resume();
   });
 
   gDebuggee.eval(
     "(" +
       function() {
         function stopMe(arg1) {
           debugger;
           debugger;
--- a/devtools/shared/client/debugger-client.js
+++ b/devtools/shared/client/debugger-client.js
@@ -1,16 +1,15 @@
 /* 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 promise = require("devtools/shared/deprecated-sync-thenables");
-
+const defer = require("devtools/shared/defer");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const {
   getStack,
   callFunctionWithAsyncStack,
 } = require("devtools/shared/platform/stack");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {
   ThreadStateTypes,
@@ -179,17 +178,17 @@ DebuggerClient.prototype = {
    *
    * @return Promise
    *         Resolves once connected with an array whose first element
    *         is the application type, by default "browser", and the second
    *         element is the traits object (help figure out the features
    *         and behaviors of the server we connect to. See RootActor).
    */
   connect: function(onConnected) {
-    const deferred = promise.defer();
+    const deferred = defer();
 
     this.once("connected", (applicationType, traits) => {
       this.traits = traits;
       if (onConnected) {
         onConnected(applicationType, traits);
       }
       deferred.resolve([applicationType, traits]);
     });
@@ -204,17 +203,17 @@ DebuggerClient.prototype = {
    * @param onClosed function
    *        If specified, will be called when the debugging connection
    *        has been closed. This parameter is deprecated - please use
    *        the returned Promise.
    * @return Promise
    *         Resolves after the underlying transport is closed.
    */
   close: function(onClosed) {
-    const deferred = promise.defer();
+    const deferred = defer();
     if (onClosed) {
       deferred.promise.then(onClosed);
     }
 
     // Disable detach event notifications, because event handlers will be in a
     // cleared scope by the time they run.
     this._eventsEnabled = false;
 
@@ -322,50 +321,52 @@ DebuggerClient.prototype = {
         "'" +
         type +
         "' request packet to " +
         "'" +
         packet.to +
         "' " +
         "can't be sent as the connection is closed.";
       const resp = { error: "connectionClosed", message: msg };
-      return promise.reject(safeOnResponse(resp));
+      return Promise.reject(safeOnResponse(resp));
     }
 
     const request = new Request(packet);
     request.format = "json";
     request.stack = getStack();
 
     // Implement a Promise like API on the returned object
     // that resolves/rejects on request response
-    const deferred = promise.defer();
-    function listenerJson(resp) {
-      removeRequestListeners();
-      resp = safeOnResponse(resp);
-      if (resp.error) {
-        deferred.reject(resp);
-      } else {
-        deferred.resolve(resp);
+    const promise = new Promise((resolve, reject) => {
+      function listenerJson(resp) {
+        removeRequestListeners();
+        resp = safeOnResponse(resp);
+        if (resp.error) {
+          reject(resp);
+        } else {
+          resolve(resp);
+        }
       }
-    }
-    function listenerBulk(resp) {
-      removeRequestListeners();
-      deferred.resolve(safeOnResponse(resp));
-    }
+      function listenerBulk(resp) {
+        removeRequestListeners();
+        resolve(safeOnResponse(resp));
+      }
 
-    const removeRequestListeners = () => {
-      request.off("json-reply", listenerJson);
-      request.off("bulk-reply", listenerBulk);
-    };
+      const removeRequestListeners = () => {
+        request.off("json-reply", listenerJson);
+        request.off("bulk-reply", listenerBulk);
+      };
 
-    request.on("json-reply", listenerJson);
-    request.on("bulk-reply", listenerBulk);
+      request.on("json-reply", listenerJson);
+      request.on("bulk-reply", listenerBulk);
+    });
 
     this._sendOrQueueRequest(request);
-    request.then = deferred.promise.then.bind(deferred.promise);
+    request.then = promise.then.bind(promise);
+    request.catch = promise.catch.bind(promise);
 
     return request;
   },
 
   /**
    * Transmit streaming data via a bulk request.
    *
    * This method initiates the bulk send process by queuing up the header data.
deleted file mode 100644
--- a/devtools/shared/deprecated-sync-thenables.js
+++ /dev/null
@@ -1,120 +0,0 @@
-/* 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 MODULE IS DEPRECATED. Use DOM Promises instead.
- */
-
-/* eslint-disable */
-
-"use strict";
-
-this.Promise = {};
-
-module.exports = Promise;
-
-function fulfilled(value) {
-  return { then: function then(fulfill) { fulfill(value); } };
-}
-
-function rejected(reason) {
-  return { then: function then(fulfill, reject) { reject(reason); } };
-}
-
-function isPromise(value) {
-  return value && typeof (value.then) === "function";
-}
-
-function defer() {
-  var observers = [];
-  var result = null;
-  var promise = {
-    then: function then(onFulfill, onError) {
-      var deferred = defer();
-
-      function resolve(value) {
-        try {
-          deferred.resolve(onFulfill ? onFulfill(value) : value);
-        } catch (error) {
-          deferred.resolve(rejected(error));
-        }
-      }
-
-      function reject(reason) {
-        try {
-          if (onError) deferred.resolve(onError(reason));
-          else deferred.resolve(rejected(reason));
-        } catch (error) {
-          deferred.resolve(rejected(error));
-        }
-      }
-
-      if (observers) {
-        observers.push({ resolve: resolve, reject: reject });
-      } else {
-        result.then(resolve, reject);
-      }
-
-      return deferred.promise;
-    },
-    catch: function (callback) {
-      return this.then(null, callback);
-    }
-  };
-
-  var deferred = {
-    promise: promise,
-    resolve: function resolve(value) {
-      if (!result) {
-        result = isPromise(value) ? value : fulfilled(value);
-        while (observers.length) {
-          var observer = observers.shift();
-          result.then(observer.resolve, observer.reject);
-        }
-        observers = null;
-      }
-    },
-    reject: function reject(reason) {
-      deferred.resolve(rejected(reason));
-    }
-  };
-
-  return deferred;
-}
-Promise.defer = defer;
-
-function resolve(value) {
-  var deferred = defer();
-  deferred.resolve(value);
-  return deferred.promise;
-}
-Promise.resolve = resolve;
-
-function reject(reason) {
-  var deferred = defer();
-  deferred.reject(reason);
-  return deferred.promise;
-}
-Promise.reject = reject;
-
-var promised = (function () {
-  var call = Function.call;
-  var concat = Array.prototype.concat;
-  function execute(args) { return call.apply(call, args); }
-  function promisedConcat(promises, unknown) {
-    return promises.then(function (values) {
-      return resolve(unknown).then(function (value) {
-        return values.concat([ value ]);
-      });
-    });
-  }
-  return function promised(f) {
-    return function promised() {
-      return concat.apply([ f, this ], arguments).
-        reduce(promisedConcat, resolve([])).
-        then(execute);
-    };
-  };
-})();
-Promise.all = promised(Array);
--- a/devtools/shared/fronts/targets/target-mixin.js
+++ b/devtools/shared/fronts/targets/target-mixin.js
@@ -569,18 +569,23 @@ function TargetMixin(parentClass) {
         }
 
         this._teardownRemoteListeners();
 
         this.threadClient = null;
 
         if (this.isLocalTab) {
           // We started with a local tab and created the client ourselves, so we
-          // should close it.
-          await this._client.close();
+          // should close it. Ignore any errors while closing, since there is
+          // not much that can be done at this point.
+          try {
+            await this._client.close();
+          } catch (e) {
+            console.warn(`Error while closing client: ${e.message}`);
+          }
 
           // Not all targets supports attach/detach. For example content process doesn't.
           // Also ensure that the front is still active before trying to do the request.
         } else if (this.detach && this.actorID) {
           // The client was handed to us, so we are not responsible for closing
           // it. We just need to detach from the tab, if already attached.
           // |detach| may fail if the connection is already dead, so proceed with
           // cleanup directly after this.
--- a/devtools/shared/moz.build
+++ b/devtools/shared/moz.build
@@ -46,17 +46,16 @@ DevToolsModules(
     'async-storage.js',
     'async-utils.js',
     'base-loader.js',
     'builtin-modules.js',
     'constants.js',
     'content-observer.js',
     'debounce.js',
     'defer.js',
-    'deprecated-sync-thenables.js',
     'DevToolsUtils.js',
     'dom-node-constants.js',
     'dom-node-filter-constants.js',
     'event-emitter.js',
     'execution-point-utils.js',
     'extend.js',
     'flags.js',
     'generate-uuid.js',
--- a/docshell/base/moz.build
+++ b/docshell/base/moz.build
@@ -20,19 +20,16 @@ with Files('IHistory.h'):
     BUG_COMPONENT = ('Toolkit', 'Places')
 
 with Files('*LoadContext.*'):
     BUG_COMPONENT = ('Core', 'Networking')
 
 with Files('nsAboutRedirector.*'):
     BUG_COMPONENT = ('Core', 'General')
 
-with Files('nsILinkHandler.*'):
-    BUG_COMPONENT = ('Core', 'DOM: Core & HTML')
-
 with Files('nsIScrollObserver.*'):
     BUG_COMPONENT = ('Core', 'Panning and Zooming')
 
 DIRS += [
     'timeline',
 ]
 
 XPIDL_SOURCES += [
@@ -59,17 +56,16 @@ XPIDL_SOURCES += [
 XPIDL_MODULE = 'docshell'
 
 EXPORTS += [
     'nsCTooltipTextProvider.h',
     'nsDocShell.h',
     'nsDocShellLoadState.h',
     'nsDocShellLoadTypes.h',
     'nsDocShellTreeOwner.h',
-    'nsILinkHandler.h',
     'nsIScrollObserver.h',
     'SerializedLoadContext.h',
 ]
 
 EXPORTS.mozilla += [
     'IHistory.h',
     'LoadContext.h',
 ]
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -552,17 +552,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
   NS_INTERFACE_MAP_ENTRY(nsIScrollable)
   NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
   NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
   NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
   NS_INTERFACE_MAP_ENTRY(nsILoadContext)
-  NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
   NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsINetworkInterceptController,
                                      mInterceptController)
   NS_INTERFACE_MAP_ENTRY(nsIDeprecationWarner)
 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
 
 NS_IMETHODIMP
 nsDocShell::GetInterface(const nsIID& aIID, void** aSink) {
@@ -12635,18 +12634,17 @@ OnLinkClickEvent::OnLinkClickEvent(
       mContent(aContent),
       mPopupState(PopupBlocker::GetPopupControlState()),
       mNoOpenerImplied(aNoOpenerImplied),
       mIsUserTriggered(aIsUserTriggered),
       mIsTrusted(aIsTrusted),
       mTriggeringPrincipal(aTriggeringPrincipal),
       mCsp(aCsp) {}
 
-NS_IMETHODIMP
-nsDocShell::OnLinkClick(
+nsresult nsDocShell::OnLinkClick(
     nsIContent* aContent, nsIURI* aURI, const nsAString& aTargetSpec,
     const nsAString& aFileName, nsIInputStream* aPostDataStream,
     nsIInputStream* aHeadersDataStream, bool aIsUserTriggered, bool aIsTrusted,
     nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp) {
 #ifndef ANDROID
   MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal");
 #endif
   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
@@ -12694,18 +12692,17 @@ nsDocShell::OnLinkClick(
 }
 
 static bool IsElementAnchorOrArea(nsIContent* aContent) {
   // Make sure we are dealing with either an <A> or <AREA> element in the HTML
   // or XHTML namespace.
   return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area);
 }
 
-NS_IMETHODIMP
-nsDocShell::OnLinkClickSync(
+nsresult nsDocShell::OnLinkClickSync(
     nsIContent* aContent, nsIURI* aURI, const nsAString& aTargetSpec,
     const nsAString& aFileName, nsIInputStream* aPostDataStream,
     nsIInputStream* aHeadersDataStream, bool aNoOpenerImplied,
     nsIDocShell** aDocShell, nsIRequest** aRequest, bool aIsUserTriggered,
     nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp) {
   // Initialize the DocShell / Request
   if (aDocShell) {
     *aDocShell = nullptr;
@@ -12879,19 +12876,18 @@ nsDocShell::OnLinkClickSync(
   nsresult rv = InternalLoad(loadState, aDocShell, aRequest);
 
   if (NS_SUCCEEDED(rv)) {
     nsPingListener::DispatchPings(this, aContent, aURI, referrerInfo);
   }
   return rv;
 }
 
-NS_IMETHODIMP
-nsDocShell::OnOverLink(nsIContent* aContent, nsIURI* aURI,
-                       const nsAString& aTargetSpec) {
+nsresult nsDocShell::OnOverLink(nsIContent* aContent, nsIURI* aURI,
+                                const nsAString& aTargetSpec) {
   if (aContent->IsEditable()) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIWebBrowserChrome2> browserChrome2 = do_GetInterface(mTreeOwner);
   nsresult rv = NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIWebBrowserChrome> browserChrome;
@@ -12915,18 +12911,17 @@ nsDocShell::OnOverLink(nsIContent* aCont
     rv = browserChrome2->SetStatusWithContext(nsIWebBrowserChrome::STATUS_LINK,
                                               uStr, aContent);
   } else {
     rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, uStr.get());
   }
   return rv;
 }
 
-NS_IMETHODIMP
-nsDocShell::OnLeaveLink() {
+nsresult nsDocShell::OnLeaveLink() {
   nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
   nsresult rv = NS_ERROR_FAILURE;
 
   if (browserChrome) {
     rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK,
                                   EmptyString().get());
   }
   return rv;
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -24,17 +24,16 @@
 
 #include "nsIAuthPromptProvider.h"
 #include "nsIBaseWindow.h"
 #include "nsIDeprecationWarner.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDOMStorageManager.h"
 #include "nsIInterfaceRequestor.h"
-#include "nsILinkHandler.h"
 #include "nsILoadContext.h"
 #include "nsILoadURIDelegate.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIRefreshURI.h"
 #include "nsIScrollable.h"
 #include "nsIRemoteTab.h"
 #include "nsIWebNavigation.h"
 #include "nsIWebPageDescriptor.h"
@@ -115,17 +114,16 @@ class nsDocShell final : public nsDocLoa
                          public nsIWebNavigation,
                          public nsIBaseWindow,
                          public nsIScrollable,
                          public nsIRefreshURI,
                          public nsIWebProgressListener,
                          public nsIWebPageDescriptor,
                          public nsIAuthPromptProvider,
                          public nsILoadContext,
-                         public nsILinkHandler,
                          public nsIDOMStorageManager,
                          public nsINetworkInterceptController,
                          public nsIDeprecationWarner,
                          public mozilla::SupportsWeakPtr<nsDocShell> {
  public:
   enum InternalLoad : uint32_t {
     INTERNAL_LOAD_FLAGS_NONE = 0x0,
     INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL = 0x1,
@@ -206,36 +204,90 @@ class nsDocShell final : public nsDocLoa
       mozilla::dom::BrowsingContext* aBrowsingContext);
 
   NS_IMETHOD Stop() override {
     // Need this here because otherwise nsIWebNavigation::Stop
     // overrides the docloader's Stop()
     return nsDocLoader::Stop();
   }
 
-  // nsILinkHandler
-  NS_IMETHOD OnLinkClick(nsIContent* aContent, nsIURI* aURI,
-                         const nsAString& aTargetSpec,
-                         const nsAString& aFileName,
-                         nsIInputStream* aPostDataStream,
-                         nsIInputStream* aHeadersDataStream,
-                         bool aIsUserTriggered, bool aIsTrusted,
-                         nsIPrincipal* aTriggeringPrincipal,
-                         nsIContentSecurityPolicy* aCsp) override;
-  NS_IMETHOD OnLinkClickSync(
+  /**
+   * Process a click on a link.
+   *
+   * @param aContent the content object used for triggering the link.
+   * @param aURI a URI object that defines the destination for the link
+   * @param aTargetSpec indicates where the link is targeted (may be an empty
+   *        string)
+   * @param aFileName non-null when the link should be downloaded as the given
+   * file
+   * @param aPostDataStream the POST data to send
+   * @param aHeadersDataStream ??? (only used for plugins)
+   * @param aIsTrusted false if the triggerer is an untrusted DOM event.
+   * @param aTriggeringPrincipal, if not passed explicitly we fall back to
+   *        the document's principal.
+   * @param aCsp, the CSP to be used for the load, that is the CSP of the
+   *        entity responsible for causing the load to occur. Most likely
+   *        this is the CSP of the document that started the load. In case
+   *        aCsp was not passed explicitly we fall back to using
+   *        aContent's document's CSP if that document holds any.
+   */
+  nsresult OnLinkClick(nsIContent* aContent, nsIURI* aURI,
+                       const nsAString& aTargetSpec, const nsAString& aFileName,
+                       nsIInputStream* aPostDataStream,
+                       nsIInputStream* aHeadersDataStream,
+                       bool aIsUserTriggered, bool aIsTrusted,
+                       nsIPrincipal* aTriggeringPrincipal,
+                       nsIContentSecurityPolicy* aCsp);
+  /**
+   * Process a click on a link.
+   *
+   * Works the same as OnLinkClick() except it happens immediately rather than
+   * through an event.
+   *
+   * @param aContent the content object used for triggering the link.
+   * @param aURI a URI obect that defines the destination for the link
+   * @param aTargetSpec indicates where the link is targeted (may be an empty
+   *        string)
+   * @param aFileName non-null when the link should be downloaded as the given
+   * file
+   * @param aPostDataStream the POST data to send
+   * @param aHeadersDataStream ??? (only used for plugins)
+   * @param aNoOpenerImplied if the link implies "noopener"
+   * @param aDocShell (out-param) the DocShell that the request was opened on
+   * @param aRequest the request that was opened
+   * @param aTriggeringPrincipal, if not passed explicitly we fall back to
+   *        the document's principal.
+   * @param aCsp, the CSP to be used for the load, that is the CSP of the
+   *        entity responsible for causing the load to occur. Most likely
+   *        this is the CSP of the document that started the load. In case
+   *        aCsp was not passed explicitly we fall back to using
+   *        aContent's document's CSP if that document holds any.
+   */
+  nsresult OnLinkClickSync(
       nsIContent* aContent, nsIURI* aURI, const nsAString& aTargetSpec,
-      const nsAString& aFileName, nsIInputStream* aPostDataStream = 0,
-      nsIInputStream* aHeadersDataStream = 0, bool aNoOpenerImplied = false,
-      nsIDocShell** aDocShell = 0, nsIRequest** aRequest = 0,
-      bool aIsUserTriggered = false,
+      const nsAString& aFileName, nsIInputStream* aPostDataStream = nullptr,
+      nsIInputStream* aHeadersDataStream = nullptr,
+      bool aNoOpenerImplied = false, nsIDocShell** aDocShell = nullptr,
+      nsIRequest** aRequest = nullptr, bool aIsUserTriggered = false,
       nsIPrincipal* aTriggeringPrincipal = nullptr,
-      nsIContentSecurityPolicy* aCsp = nullptr) override;
-  NS_IMETHOD OnOverLink(nsIContent* aContent, nsIURI* aURI,
-                        const nsAString& aTargetSpec) override;
-  NS_IMETHOD OnLeaveLink() override;
+      nsIContentSecurityPolicy* aCsp = nullptr);
+  /**
+   * Process a mouse-over a link.
+   *
+   * @param aContent the linked content.
+   * @param aURI an URI object that defines the destination for the link
+   * @param aTargetSpec indicates where the link is targeted (it may be an empty
+   *        string)
+   */
+  nsresult OnOverLink(nsIContent* aContent, nsIURI* aURI,
+                      const nsAString& aTargetSpec);
+  /**
+   * Process the mouse leaving a link.
+   */
+  nsresult OnLeaveLink();
 
   // Don't use NS_DECL_NSILOADCONTEXT because some of nsILoadContext's methods
   // are shared with nsIDocShell and can't be declared twice.
   NS_IMETHOD GetAssociatedWindow(mozIDOMWindowProxy**) override;
   NS_IMETHOD GetTopWindow(mozIDOMWindowProxy**) override;
   NS_IMETHOD GetTopFrameElement(mozilla::dom::Element**) override;
   NS_IMETHOD GetNestedFrameId(uint64_t*) override;
   NS_IMETHOD GetIsContent(bool*) override;
deleted file mode 100644
--- a/docshell/base/nsILinkHandler.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef nsILinkHandler_h___
-#define nsILinkHandler_h___
-
-#include "nsISupports.h"
-#include "mozilla/EventForwards.h"
-
-class nsIContent;
-class nsIContentSecurityPolicy;
-class nsIDocShell;
-class nsIInputStream;
-class nsIRequest;
-
-#define NS_ILINKHANDLER_IID                          \
-  {                                                  \
-    0xceb9aade, 0x43da, 0x4f1a, {                    \
-      0xac, 0x8a, 0xc7, 0x09, 0xfb, 0x22, 0x46, 0x64 \
-    }                                                \
-  }
-
-/**
- * Interface used for handling clicks on links
- */
-class nsILinkHandler : public nsISupports {
- public:
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ILINKHANDLER_IID)
-
-  /**
-   * Process a click on a link.
-   *
-   * @param aContent the content for the frame that generated the trigger
-   * @param aURI a URI object that defines the destination for the link
-   * @param aTargetSpec indicates where the link is targeted (may be an empty
-   *        string)
-   * @param aFileName non-null when the link should be downloaded as the given
-   * file
-   * @param aPostDataStream the POST data to send
-   * @param aHeadersDataStream ???
-   * @param aIsTrusted false if the triggerer is an untrusted DOM event.
-   * @param aTriggeringPrincipal, if not passed explicitly we fall back to
-   *        the document's principal.
-   * @param aCsp, the CSP to be used for the load, that is the CSP of the
-   *        entity responsible for causing the load to occur. Most likely
-   *        this is the CSP of the document that started the load. In case
-   *        aCsp was not passed explicitly we fall back to using
-   *        aContent's document's CSP if that document holds any.
-   */
-  NS_IMETHOD OnLinkClick(nsIContent* aContent, nsIURI* aURI,
-                         const nsAString& aTargetSpec,
-                         const nsAString& aFileName,
-                         nsIInputStream* aPostDataStream,
-                         nsIInputStream* aHeadersDataStream,
-                         bool aIsUserTriggered, bool aIsTrusted,
-                         nsIPrincipal* aTriggeringPrincipal,
-                         nsIContentSecurityPolicy* aCsp) = 0;
-
-  /**
-   * Process a click on a link.
-   *
-   * Works the same as OnLinkClick() except it happens immediately rather than
-   * through an event.
-   *
-   * @param aContent the content for the frame that generated the trigger
-   * @param aURI a URI obect that defines the destination for the link
-   * @param aTargetSpec indicates where the link is targeted (may be an empty
-   *        string)
-   * @param aFileName non-null when the link should be downloaded as the given
-   * file
-   * @param aPostDataStream the POST data to send
-   * @param aHeadersDataStream ???
-   * @param aNoOpenerImplied if the link implies "noopener"
-   * @param aDocShell (out-param) the DocShell that the request was opened on
-   * @param aRequest the request that was opened
-   * @param aTriggeringPrincipal, if not passed explicitly we fall back to
-   *        the document's principal.
-   * @param aCsp, the CSP to be used for the load, that is the CSP of the
-   *        entity responsible for causing the load to occur. Most likely
-   *        this is the CSP of the document that started the load. In case
-   *        aCsp was not passed explicitly we fall back to using
-   *        aContent's document's CSP if that document holds any.
-   */
-  NS_IMETHOD OnLinkClickSync(
-      nsIContent* aContent, nsIURI* aURI, const nsAString& aTargetSpec,
-      const nsAString& aFileName, nsIInputStream* aPostDataStream = 0,
-      nsIInputStream* aHeadersDataStream = 0, bool aNoOpenerImplied = false,
-      nsIDocShell** aDocShell = 0, nsIRequest** aRequest = 0,
-      bool aIsUserTriggered = false,
-      nsIPrincipal* aTriggeringPrincipal = nullptr,
-      nsIContentSecurityPolicy* aCsp = nullptr) = 0;
-
-  /**
-   * Process a mouse-over a link.
-   *
-   * @param aContent the linked content.
-   * @param aURI an URI object that defines the destination for the link
-   * @param aTargetSpec indicates where the link is targeted (it may be an empty
-   *        string)
-   */
-  NS_IMETHOD OnOverLink(nsIContent* aContent, nsIURI* aURLSpec,
-                        const nsAString& aTargetSpec) = 0;
-
-  /**
-   * Process the mouse leaving a link.
-   */
-  NS_IMETHOD OnLeaveLink() = 0;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsILinkHandler, NS_ILINKHANDLER_IID)
-
-#endif /* nsILinkHandler_h___ */
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -86,16 +86,17 @@
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/CSPDictionariesBinding.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/FeaturePolicy.h"
 #include "mozilla/dom/FeaturePolicyUtils.h"
 #include "mozilla/dom/FramingChecker.h"
+#include "mozilla/dom/HTMLAllCollection.h"
 #include "mozilla/dom/HTMLSharedElement.h"
 #include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/TreeOrderedArrayInlines.h"
 #include "mozilla/dom/ResizeObserver.h"
 #include "mozilla/dom/ResizeObserverController.h"
 #include "mozilla/dom/ServiceWorkerContainer.h"
 #include "mozilla/dom/ScriptLoader.h"
@@ -1239,16 +1240,17 @@ Document::Document(const char* aContentT
       mDidFireDOMContentLoaded(true),
       mHasScrollLinkedEffect(false),
       mFrameRequestCallbacksScheduled(false),
       mIsTopLevelContentDocument(false),
       mIsContentDocument(false),
       mDidCallBeginLoad(false),
       mAllowPaymentRequest(false),
       mEncodingMenuDisabled(false),
+      mLinksEnabled(true),
       mIsSVGGlyphsDocument(false),
       mInDestructor(false),
       mIsGoingAway(false),
       mInXBLUpdate(false),
       mNeedsReleaseAfterStackRefCntRelease(false),
       mStyleSetFilled(false),
       mQuirkSheetAdded(false),
       mContentEditableSheetAdded(false),
@@ -1998,16 +2000,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplets);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchors);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuppressedEventListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypeDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMidasCommandManager)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAll)
 
   // Traverse all our nsCOMArrays.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
 
   for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
     cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback);
   }
@@ -5626,16 +5629,23 @@ void Document::SetFgColor(const nsAStrin
     body->SetText(aFgColor);
   }
 }
 
 void Document::CaptureEvents() { WarnOnceAbout(Document::eUseOfCaptureEvents); }
 
 void Document::ReleaseEvents() { WarnOnceAbout(Document::eUseOfReleaseEvents); }
 
+HTMLAllCollection* Document::All() {
+  if (!mAll) {
+    mAll = new HTMLAllCollection(this);
+  }
+  return mAll;
+}
+
 nsresult Document::GetSrcdocData(nsAString& aSrcdocData) {
   if (mIsSrcdocDocument) {
     nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
     if (inStrmChan) {
       return inStrmChan->GetSrcdocData(aSrcdocData);
     }
   }
   aSrcdocData = VoidString();
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -180,16 +180,17 @@ class DOMStringList;
 class Element;
 struct ElementCreationOptions;
 class Event;
 class EventTarget;
 class FeaturePolicy;
 class FontFaceSet;
 class FrameRequestCallback;
 class ImageTracker;
+class HTMLAllCollection;
 class HTMLBodyElement;
 class HTMLSharedElement;
 class HTMLImageElement;
 struct LifecycleCallbackArgs;
 class Link;
 class Location;
 class MediaQueryList;
 class GlobalObject;
@@ -1361,16 +1362,19 @@ class Document : public nsINode,
   void EnableEncodingMenu() { mEncodingMenuDisabled = false; }
 
   /**
    * Called to disable client access to cookies through the document.cookie API
    * from user JavaScript code.
    */
   void DisableCookieAccess() { mDisableCookieAccess = true; }
 
+  void SetLinkHandlingEnabled(bool aValue) { mLinksEnabled = aValue; }
+  bool LinkHandlingEnabled() { return mLinksEnabled; }
+
   /**
    * Set compatibility mode for this document
    */
   void SetCompatibilityMode(nsCompatibility aMode);
 
   /**
    * Called to disable client access to document.write() API from user
    * JavaScript code.
@@ -2330,17 +2334,17 @@ class Document : public nsINode,
    * Set the container (docshell) for this document. Virtual so that
    * docshell can call it.
    */
   virtual void SetContainer(nsDocShell* aContainer);
 
   /**
    * Get the container (docshell) for this document.
    */
-  virtual nsISupports* GetContainer() const;
+  nsISupports* GetContainer() const;
 
   /**
    * Get the container's load context for this document.
    */
   nsILoadContext* GetLoadContext() const;
 
   /**
    * Get docshell the for this document.
@@ -3574,16 +3578,18 @@ class Document : public nsINode,
   void GetBgColor(nsAString& aBgColor);
   void SetBgColor(const nsAString& aBgColor);
   void Clear() const {
     // Deprecated
   }
   void CaptureEvents();
   void ReleaseEvents();
 
+  mozilla::dom::HTMLAllCollection* All();
+
   static bool IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject);
   static bool DocumentSupportsL10n(JSContext* aCx, JSObject* aObject);
   static bool IsWebAnimationsEnabled(JSContext* aCx, JSObject* aObject);
   static bool IsWebAnimationsEnabled(CallerType aCallerType);
   static bool IsWebAnimationsGetAnimationsEnabled(JSContext* aCx,
                                                   JSObject* aObject);
   static bool AreWebAnimationsImplicitKeyframesEnabled(JSContext* aCx,
                                                        JSObject* aObject);
@@ -4653,16 +4659,20 @@ class Document : public nsINode,
   bool mDidCallBeginLoad : 1;
 
   // True if the document is allowed to use PaymentRequest.
   bool mAllowPaymentRequest : 1;
 
   // True if the encoding menu should be disabled.
   bool mEncodingMenuDisabled : 1;
 
+  // False if we've disabled link handling for elements inside this document,
+  // true otherwise.
+  bool mLinksEnabled : 1;
+
   // True if this document is for an SVG-in-OpenType font.
   bool mIsSVGGlyphsDocument : 1;
 
   // True if the document is being destroyed.
   bool mInDestructor : 1;
 
   // True if the document has been detached from its content viewer.
   bool mIsGoingAway : 1;
@@ -5203,16 +5213,18 @@ class Document : public nsINode,
   uint32_t mIgnoreOpensDuringUnloadCounter;
 
   nsCOMPtr<nsIDOMXULCommandDispatcher>
       mCommandDispatcher;  // [OWNER] of the focus tracker
 
   RefPtr<XULBroadcastManager> mXULBroadcastManager;
   RefPtr<XULPersist> mXULPersist;
 
+  RefPtr<mozilla::dom::HTMLAllCollection> mAll;
+
   // document lightweight theme for use with :-moz-lwtheme,
   // :-moz-lwtheme-brighttext and :-moz-lwtheme-darktext
   DocumentTheme mDocLWTheme;
 
   // Pres shell resolution saved before entering fullscreen mode.
   float mSavedResolution;
 
   bool mPendingInitialTranslation;
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -25,17 +25,16 @@
 #include "mozilla/gfx/Matrix.h"
 #include "nsAtom.h"
 #include "nsDOMAttributeMap.h"
 #include "nsIContentInlines.h"
 #include "mozilla/dom/NodeInfo.h"
 #include "mozilla/dom/DocumentTimeline.h"
 #include "nsFlexContainerFrame.h"
 #include "nsFocusManager.h"
-#include "nsILinkHandler.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIURL.h"
 #include "nsContainerFrame.h"
 #include "nsIAnonymousContentCreator.h"
 #include "nsPresContext.h"
 #include "nsStyleConsts.h"
 #include "nsString.h"
 #include "nsUnicharUtils.h"
@@ -2167,22 +2166,24 @@ nsresult Element::DispatchClickEvent(nsP
     event.mFlags.Union(*aExtraEventFlags);
   }
 
   return DispatchEvent(aPresContext, &event, aTarget, aFullDispatch, aStatus);
 }
 
 //----------------------------------------------------------------------
 nsresult Element::LeaveLink(nsPresContext* aPresContext) {
-  nsILinkHandler* handler = aPresContext->GetLinkHandler();
-  if (!handler) {
+  if (!aPresContext || !aPresContext->Document()->LinkHandlingEnabled()) {
     return NS_OK;
   }
-
-  return handler->OnLeaveLink();
+  nsIDocShell* shell = aPresContext->Document()->GetDocShell();
+  if (!shell) {
+    return NS_OK;
+  }
+  return nsDocShell::Cast(shell)->OnLeaveLink();
 }
 
 nsresult Element::SetEventHandler(nsAtom* aEventName, const nsAString& aValue,
                                   bool aDefer) {
   Document* ownerDoc = OwnerDoc();
   if (ownerDoc->IsLoadedAsData()) {
     // Make this a no-op rather than throwing an error to avoid
     // the error causing problems setting the attribute.
@@ -2970,17 +2971,16 @@ void Element::Describe(nsAString& aOutDe
 
 bool Element::CheckHandleEventForLinksPrecondition(EventChainVisitor& aVisitor,
                                                    nsIURI** aURI) const {
   if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault ||
       (!aVisitor.mEvent->IsTrusted() &&
        (aVisitor.mEvent->mMessage != eMouseClick) &&
        (aVisitor.mEvent->mMessage != eKeyPress) &&
        (aVisitor.mEvent->mMessage != eLegacyDOMActivate)) ||
-      !aVisitor.mPresContext ||
       aVisitor.mEvent->mFlags.mMultipleActionsPrevented) {
     return false;
   }
 
   // Make sure we actually are a link
   return IsLink(aURI);
 }
 
@@ -3011,17 +3011,17 @@ void Element::GetEventTargetParentForLin
     case eMouseOver:
       aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
       MOZ_FALLTHROUGH;
     case eFocus: {
       InternalFocusEvent* focusEvent = aVisitor.mEvent->AsFocusEvent();
       if (!focusEvent || !focusEvent->mIsRefocus) {
         nsAutoString target;
         GetLinkTarget(target);
-        nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target,
+        nsContentUtils::TriggerLink(this, absURI, target,
                                     /* click */ false, /* isTrusted */ true);
         // Make sure any ancestor links don't also TriggerLink
         aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
       }
       break;
     }
     case eMouseOut:
       aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
@@ -3059,76 +3059,74 @@ nsresult Element::PostHandleEventForLink
   if (!CheckHandleEventForLinksPrecondition(aVisitor, getter_AddRefs(absURI))) {
     return NS_OK;
   }
 
   nsresult rv = NS_OK;
 
   switch (aVisitor.mEvent->mMessage) {
     case eMouseDown: {
-      if (aVisitor.mEvent->AsMouseEvent()->mButton == MouseButton::eLeft) {
-        // don't make the link grab the focus if there is no link handler
-        nsILinkHandler* handler = aVisitor.mPresContext->GetLinkHandler();
-        Document* document = GetComposedDoc();
-        if (handler && document) {
-          nsIFocusManager* fm = nsFocusManager::GetFocusManager();
-          if (fm) {
-            aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
+      if (aVisitor.mEvent->AsMouseEvent()->mButton == MouseButton::eLeft &&
+          OwnerDoc()->LinkHandlingEnabled()) {
+        aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
+
+        if (IsInComposedDoc()) {
+          if (nsIFocusManager* fm = nsFocusManager::GetFocusManager()) {
             RefPtr<Element> kungFuDeathGrip(this);
             fm->SetFocus(kungFuDeathGrip, nsIFocusManager::FLAG_BYMOUSE |
                                               nsIFocusManager::FLAG_NOSCROLL);
           }
-
+        }
+
+        if (aVisitor.mPresContext) {
           EventStateManager::SetActiveManager(
               aVisitor.mPresContext->EventStateManager(), this);
-
-          // OK, we're pretty sure we're going to load, so warm up a speculative
-          // connection to be sure we have one ready when we open the channel.
+        }
+
+        // OK, we're pretty sure we're going to load, so warm up a speculative
+        // connection to be sure we have one ready when we open the channel.
+        if (nsIDocShell* shell = OwnerDoc()->GetDocShell()) {
           nsCOMPtr<nsISpeculativeConnect> sc =
               do_QueryInterface(nsContentUtils::GetIOService());
-          nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(handler);
+          nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(shell);
           sc->SpeculativeConnect(absURI, NodePrincipal(), ir);
         }
       }
     } break;
 
     case eMouseClick: {
       WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
       if (mouseEvent->IsLeftClickEvent()) {
         if (mouseEvent->IsControl() || mouseEvent->IsMeta() ||
             mouseEvent->IsAlt() || mouseEvent->IsShift()) {
           break;
         }
 
         // The default action is simply to dispatch DOMActivate
-        if (RefPtr<PresShell> presShell =
-                aVisitor.mPresContext->GetPresShell()) {
-          // single-click
-          nsEventStatus status = nsEventStatus_eIgnore;
-          // DOMActive event should be trusted since the activation is actually
-          // occurred even if the cause is an untrusted click event.
-          InternalUIEvent actEvent(true, eLegacyDOMActivate, mouseEvent);
-          actEvent.mDetail = 1;
-
-          rv = presShell->HandleDOMEventWithTarget(this, &actEvent, &status);
-          if (NS_SUCCEEDED(rv)) {
-            aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
-          }
+        nsEventStatus status = nsEventStatus_eIgnore;
+        // DOMActive event should be trusted since the activation is actually
+        // occurred even if the cause is an untrusted click event.
+        InternalUIEvent actEvent(true, eLegacyDOMActivate, mouseEvent);
+        actEvent.mDetail = 1;
+
+        rv = EventDispatcher::Dispatch(this, aVisitor.mPresContext, &actEvent,
+                                       nullptr, &status);
+        if (NS_SUCCEEDED(rv)) {
+          aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
         }
       }
       break;
     }
     case eLegacyDOMActivate: {
       if (aVisitor.mEvent->mOriginalTarget == this) {
         nsAutoString target;
         GetLinkTarget(target);
         const InternalUIEvent* activeEvent = aVisitor.mEvent->AsUIEvent();
         MOZ_ASSERT(activeEvent);
-        nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target,
-                                    /* click */ true,
+        nsContentUtils::TriggerLink(this, absURI, target, /* click */ true,
                                     activeEvent->IsTrustable());
         aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
       }
     } break;
 
     case eKeyPress: {
       WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent();
       if (keyEvent && keyEvent->mKeyCode == NS_VK_RETURN) {
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -15,17 +15,16 @@
 
 #include "AttrArray.h"
 #include "DOMIntersectionObserver.h"
 #include "nsAttrValue.h"
 #include "nsAttrValueInlines.h"
 #include "nsChangeHint.h"
 #include "nsContentUtils.h"
 #include "nsDOMAttributeMap.h"
-#include "nsILinkHandler.h"
 #include "nsINodeList.h"
 #include "nsIScrollableFrame.h"
 #include "nsNodeUtils.h"
 #include "nsPresContext.h"
 #include "Units.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/CORSMode.h"
 #include "mozilla/EventForwards.h"
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -33,17 +33,16 @@
 #include "mozilla/dom/NodeInfo.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/ScriptLoader.h"
 #include "mozilla/dom/TouchEvent.h"
 #include "mozilla/dom/Document.h"
 #include "mozilla/dom/DocumentInlines.h"
 #include "nsIDocumentEncoder.h"
 #include "nsFocusManager.h"
-#include "nsILinkHandler.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIURL.h"
 #include "nsNetUtil.h"
 #include "nsIFrame.h"
 #include "nsIAnonymousContentCreator.h"
 #include "nsPresContext.h"
 #include "nsStyleConsts.h"
 #include "nsString.h"
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -5075,34 +5075,32 @@ bool nsContentUtils::CombineResourcePrin
       subsumes) {
     return false;
   }
   *aResourcePrincipal = sSystemPrincipal;
   return true;
 }
 
 /* static */
-void nsContentUtils::TriggerLink(nsIContent* aContent,
-                                 nsPresContext* aPresContext, nsIURI* aLinkURI,
+void nsContentUtils::TriggerLink(nsIContent* aContent, nsIURI* aLinkURI,
                                  const nsString& aTargetSpec, bool aClick,
                                  bool aIsTrusted) {
-  NS_ASSERTION(aPresContext, "Need a nsPresContext");
   MOZ_ASSERT(aLinkURI, "No link URI");
 
-  if (aContent->IsEditable()) {
+  if (aContent->IsEditable() || !aContent->OwnerDoc()->LinkHandlingEnabled()) {
     return;