Merge mozilla-central to mozilla-inbound
authorarthur.iakab <aiakab@mozilla.com>
Sat, 29 Sep 2018 01:00:35 +0300
changeset 494571 4fb20c215e9c3ea771d8709c5912610d626edb3e
parent 494570 8a7d0b7ce84eb68f2d9832d1bdd0d73103ff8aa9 (current diff)
parent 494447 684521e3af2ff1029d60bb207ae0d75058c3ce1f (diff)
child 494572 6006b0096da29528c0870bc4dfa2a4ccf046daa9
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
browser/base/content/browser.js
browser/base/content/tabbrowser.js
browser/components/urlbar/UrlbarPrefs.jsm
browser/components/urlbar/tests/unit/head.js
mobile/android/geckoview/src/main/AndroidManifest.xml
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5706,16 +5706,17 @@ function onViewToolbarsPopupShowing(aEve
   }
 
   if (showTabStripItems) {
     PlacesCommandHook.updateBookmarkAllTabsCommand();
 
     let haveMultipleTabs = gBrowser.visibleTabs.length > 1;
     document.getElementById("toolbar-context-reloadAllTabs").disabled = !haveMultipleTabs;
 
+    document.getElementById("toolbar-context-selectAllTabs").disabled = gBrowser.allTabsSelected();
     document.getElementById("toolbar-context-undoCloseTab").disabled =
       SessionStore.getClosedTabCount(window) == 0;
     return;
   }
 
   // In some cases, we will exit the above loop with toolbarItem being the
   // xul:document. That has no parentNode, and we should disable the items in
   // this case.
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -110,16 +110,18 @@ xmlns="http://www.w3.org/1999/xhtml"
                 oncommand="gBrowser.reloadTab(TabContextMenu.contextTab);"/>
       <menuitem id="context_reloadSelectedTabs" label="&reloadSelectedTabs.label;" hidden="true"
                 accesskey="&reloadSelectedTabs.accesskey;"
                 oncommand="gBrowser.reloadMultiSelectedTabs();"/>
       <menuitem id="context_toggleMuteTab" oncommand="TabContextMenu.contextTab.toggleMuteAudio();"/>
       <menuitem id="context_toggleMuteSelectedTabs" hidden="true"
                 oncommand="gBrowser.toggleMuteAudioOnMultiSelectedTabs(TabContextMenu.contextTab);"/>
       <menuseparator/>
+      <menuitem id="context_selectAllTabs" label="&selectAllTabs.label;" accesskey="&selectAllTabs.accesskey;"
+                oncommand="gBrowser.selectAllTabs();"/>
       <menuitem id="context_pinTab" label="&pinTab.label;"
                 accesskey="&pinTab.accesskey;"
                 oncommand="gBrowser.pinTab(TabContextMenu.contextTab);"/>
       <menuitem id="context_unpinTab" label="&unpinTab.label;" hidden="true"
                 accesskey="&unpinTab.accesskey;"
                 oncommand="gBrowser.unpinTab(TabContextMenu.contextTab);"/>
       <menuitem id="context_pinSelectedTabs" label="&pinSelectedTabs.label;" hidden="true"
                 accesskey="&pinSelectedTabs.accesskey;"
@@ -416,16 +418,22 @@ xmlns="http://www.w3.org/1999/xhtml"
                 label="&toolbarContextMenu.reloadAllTabs.label;"
                 accesskey="&toolbarContextMenu.reloadAllTabs.accesskey;"/>
       <menuitem id="toolbar-context-bookmarkAllTabs"
                 class="toolbaritem-tabsmenu"
                 contexttype="tabbar"
                 command="Browser:BookmarkAllTabs"
                 label="&toolbarContextMenu.bookmarkAllTabs.label;"
                 accesskey="&toolbarContextMenu.bookmarkAllTabs.accesskey;"/>
+      <menuitem id="toolbar-context-selectAllTabs"
+                class="toolbaritem-tabsmenu"
+                contexttype="tabbar"
+                oncommand="gBrowser.selectAllTabs();"
+                label="&toolbarContextMenu.selectAllTabs.label;"
+                accesskey="&toolbarContextMenu.selectAllTabs.accesskey;"/>
       <menuitem id="toolbar-context-undoCloseTab"
                 class="toolbaritem-tabsmenu"
                 contexttype="tabbar"
                 label="&toolbarContextMenu.undoCloseTab.label;"
                 accesskey="&toolbarContextMenu.undoCloseTab.accesskey;"
                 observes="History:UndoCloseTab"/>
       <menuseparator/>
       <menuseparator id="viewToolbarsMenuSeparator"/>
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -15,19 +15,20 @@ const FAVICON_DEFAULTS = {
   "about:welcome": "chrome://branding/content/icon32.png",
   "about:privatebrowsing": "chrome://browser/skin/privatebrowsing/favicon.svg",
 };
 
 window._gBrowser = {
   init() {
     ChromeUtils.defineModuleGetter(this, "AsyncTabSwitcher",
       "resource:///modules/AsyncTabSwitcher.jsm");
+    ChromeUtils.defineModuleGetter(this, "UrlbarProviderOpenTabs",
+      "resource:///modules/UrlbarProviderOpenTabs.jsm");
 
     XPCOMUtils.defineLazyServiceGetters(this, {
-      _unifiedComplete: ["@mozilla.org/autocomplete/search;1?name=unifiedcomplete", "mozIPlacesAutoComplete"],
       serializationHelper: ["@mozilla.org/network/serialization-helper;1", "nsISerializationHelper"],
       mURIFixup: ["@mozilla.org/docshell/urifixup;1", "nsIURIFixup"],
     });
 
     Services.obs.addObserver(this, "contextual-identity-updated");
 
     Services.els.addSystemEventListener(document, "keydown", this, false);
     if (AppConstants.platform == "macosx") {
@@ -2474,17 +2475,18 @@ window._gBrowser = {
 
       // If the caller opts in, create a lazy browser.
       if (createLazyBrowser) {
         this._createLazyBrowser(t);
 
         if (lazyBrowserURI) {
           // Lazy browser must be explicitly registered so tab will appear as
           // a switch-to-tab candidate in autocomplete.
-          this._unifiedComplete.registerOpenPage(lazyBrowserURI, userContextId);
+          this.UrlbarProviderOpenTabs.registerOpenTab(lazyBrowserURI.spec,
+                                                      userContextId);
           b.registeredOpenURI = lazyBrowserURI;
         }
       } else {
         this._insertBrowser(t, true);
       }
     } catch (e) {
       Cu.reportError("Failed to create tab");
       Cu.reportError(e);
@@ -2942,18 +2944,19 @@ window._gBrowser = {
       browser.webProgress.removeProgressListener(filter);
 
       const listener = this._tabListeners.get(aTab);
       filter.removeProgressListener(listener);
       listener.destroy();
     }
 
     if (browser.registeredOpenURI && !aAdoptedByTab) {
-      this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI,
-        browser.getAttribute("usercontextid") || 0);
+      let userContextId = browser.getAttribute("usercontextid") || 0;
+      this.UrlbarProviderOpenTabs.unregisterOpenTab(browser.registeredOpenURI.spec,
+                                                    userContextId);
       delete browser.registeredOpenURI;
     }
 
     // We are no longer the primary content area.
     browser.removeAttribute("primary");
 
     // Remove this tab as the owner of any other tabs, since it's going away.
     for (let tab of this.tabs) {
@@ -3229,18 +3232,19 @@ window._gBrowser = {
           this._isBusy = true;
       }
 
       this._swapBrowserDocShells(aOurTab, otherBrowser, Ci.nsIBrowser.SWAP_DEFAULT, stateFlags);
     }
 
     // Unregister the previously opened URI
     if (otherBrowser.registeredOpenURI) {
-      this._unifiedComplete.unregisterOpenPage(otherBrowser.registeredOpenURI,
-        otherBrowser.getAttribute("usercontextid") || 0);
+      let userContextId = otherBrowser.getAttribute("usercontextid") || 0;
+      this.UrlbarProviderOpenTabs.unregisterOpenTab(otherBrowser.registeredOpenURI.spec,
+                                                    userContextId);
       delete otherBrowser.registeredOpenURI;
     }
 
     // Handle findbar data (if any)
     let otherFindBar = aOtherTab._findBar;
     if (otherFindBar &&
         otherFindBar.findMode == otherFindBar.FIND_NORMAL) {
       let oldValue = otherFindBar._findField.value;
@@ -3849,16 +3853,26 @@ window._gBrowser = {
       this.removeFromMultiSelectedTabs(tab, false);
     }
     this._lastMultiSelectedTabRef = null;
     if (isLastMultiSelectChange) {
       this.tabContainer._setPositionalAttributes();
     }
   },
 
+  selectAllTabs() {
+    let visibleTabs = this.visibleTabs;
+    gBrowser.addRangeToMultiSelectedTabs(visibleTabs[0],
+                                         visibleTabs[visibleTabs.length - 1]);
+  },
+
+  allTabsSelected() {
+    return this.visibleTabs.every(t => t.multiselected);
+  },
+
   lockClearMultiSelectionOnce() {
     this._clearMultiSelectionLockedOnce = true;
     this._clearMultiSelectionLocked = true;
   },
 
   unlockClearMultiSelection() {
     this._clearMultiSelectionLockedOnce = false;
     this._clearMultiSelectionLocked = false;
@@ -4363,18 +4377,19 @@ window._gBrowser = {
   },
 
   destroy() {
     Services.obs.removeObserver(this, "contextual-identity-updated");
 
     for (let tab of this.tabs) {
       let browser = tab.linkedBrowser;
       if (browser.registeredOpenURI) {
-        this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI,
-          browser.getAttribute("usercontextid") || 0);
+        let userContextId = browser.getAttribute("usercontextid") || 0;
+        this.UrlbarProviderOpenTabs.unregisterOpenTab(browser.registeredOpenURI.spec,
+                                                      userContextId);
         delete browser.registeredOpenURI;
       }
 
       let filter = this._tabFilters.get(tab);
       if (filter) {
         browser.webProgress.removeProgressListener(filter);
 
         let listener = this._tabListeners.get(tab);
@@ -5022,26 +5037,27 @@ class TabProgressListener {
           !isSameDocument) {
         // Removing the tab's image here causes flickering, wait until the load
         // is complete.
         this.mBrowser.mIconURL = null;
       }
 
       let userContextId = this.mBrowser.getAttribute("usercontextid") || 0;
       if (this.mBrowser.registeredOpenURI) {
-        gBrowser._unifiedComplete
-          .unregisterOpenPage(this.mBrowser.registeredOpenURI, userContextId);
+        let uri = this.mBrowser.registeredOpenURI;
+        gBrowser.UrlbarProviderOpenTabs.unregisterOpenTab(uri.spec, userContextId);
         delete this.mBrowser.registeredOpenURI;
       }
       // Tabs in private windows aren't registered as "Open" so
       // that they don't appear as switch-to-tab candidates.
       if (!isBlankPageURL(aLocation.spec) &&
           (!PrivateBrowsingUtils.isWindowPrivate(window) ||
             PrivateBrowsingUtils.permanentPrivateBrowsing)) {
-        gBrowser._unifiedComplete.registerOpenPage(aLocation, userContextId);
+        gBrowser.UrlbarProviderOpenTabs.registerOpenTab(aLocation.spec,
+                                                        userContextId);
         this.mBrowser.registeredOpenURI = aLocation;
       }
 
       if (this.mTab != gBrowser.selectedTab) {
         let tabCacheIndex = gBrowser._tabLayerCache.indexOf(this.mTab);
         if (tabCacheIndex != -1) {
           gBrowser._tabLayerCache.splice(tabCacheIndex, 1);
           gBrowser._getSwitcher().cleanUpTabAfterEviction(this.mTab);
@@ -5360,16 +5376,19 @@ var TabContextMenu = {
       toggleMultiSelectMute.label = gNavigatorBundle.getString("muteSelectedTabs.label");
       toggleMultiSelectMute.accessKey = gNavigatorBundle.getString("muteSelectedTabs.accesskey");
     }
 
     this.contextTab.toggleMuteMenuItem = toggleMute;
     this.contextTab.toggleMultiSelectMuteMenuItem = toggleMultiSelectMute;
     this._updateToggleMuteMenuItems(this.contextTab);
 
+    let selectAllTabs = document.getElementById("context_selectAllTabs");
+    selectAllTabs.disabled = gBrowser.allTabsSelected();
+
     this.contextTab.addEventListener("TabAttrModified", this);
     aPopupMenu.addEventListener("popuphiding", this);
 
     gSync.updateTabContextMenu(aPopupMenu, this.contextTab);
 
     document.getElementById("context_reopenInContainer").hidden =
       !Services.prefs.getBoolPref("privacy.userContext.enabled", false) ||
       PrivateBrowsingUtils.isWindowPrivate(window);
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -118,20 +118,16 @@ var whitelist = [
   // browser/extensions/pdfjs/content/web/viewer.js#7450
   {file: "resource://pdf.js/web/debugger.js"},
 
   // resource://app/modules/translation/TranslationContentHandler.jsm
   {file: "resource://app/modules/translation/BingTranslator.jsm"},
   {file: "resource://app/modules/translation/GoogleTranslator.jsm"},
   {file: "resource://app/modules/translation/YandexTranslator.jsm"},
 
-  // The Quantum Bar files are not in use yet, but we plan to start using them
-  // soon in parallel to the old implementation.
-  {file: "resource://app/modules/UrlbarTokenizer.jsm"},
-
   // Starting from here, files in the whitelist are bugs that need fixing.
   // Bug 1339424 (wontfix?)
   {file: "chrome://browser/locale/taskbar.properties",
    platforms: ["linux", "macosx"]},
   // Bug 1356031 (only used by devtools)
   {file: "chrome://global/skin/icons/error-16.png"},
   // Bug 1348526
   {file: "chrome://global/skin/tree/sort-asc-classic.png", platforms: ["linux"]},
--- a/browser/components/customizableui/test/browser_customization_context_menus.js
+++ b/browser/components/customizableui/test/browser_customization_context_menus.js
@@ -53,16 +53,17 @@ add_task(async function tabstrip_context
   EventUtils.synthesizeMouse(tabstrip, rect.width - 2, 2, {type: "contextmenu", button: 2 });
   await shownPromise;
 
   let closedTabsAvailable = SessionStore.getClosedTabCount(window) == 0;
   info("Closed tabs: " + closedTabsAvailable);
   let expectedEntries = [
     ["#toolbar-context-reloadAllTabs", true],
     ["#toolbar-context-bookmarkAllTabs", true],
+    ["#toolbar-context-selectAllTabs", true],
     ["#toolbar-context-undoCloseTab", !closedTabsAvailable],
     ["---"],
   ];
   if (!isOSX) {
     expectedEntries.push(["#toggle_toolbar-menubar", true]);
   }
   expectedEntries.push(
     ["#toggle_PersonalToolbar", true],
--- a/browser/components/payments/content/paymentDialogFrameScript.js
+++ b/browser/components/payments/content/paymentDialogFrameScript.js
@@ -21,16 +21,18 @@
 /* global Services */
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "FormAutofill",
                                "resource://formautofill/FormAutofill.jsm");
 ChromeUtils.defineModuleGetter(this, "FormAutofillUtils",
                                "resource://formautofill/FormAutofillUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "AppConstants",
+                               "resource://gre/modules/AppConstants.jsm");
 
 const SAVE_CREDITCARD_DEFAULT_PREF = "dom.payments.defaults.saveCreditCard";
 const SAVE_ADDRESS_DEFAULT_PREF = "dom.payments.defaults.saveAddress";
 
 let PaymentFrameScript = {
   init() {
     XPCOMUtils.defineLazyGetter(this, "log", () => {
       let {ConsoleAPI} = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
@@ -98,16 +100,20 @@ let PaymentFrameScript = {
         let prefValues = Cu.cloneInto({
           saveCreditCardDefaultChecked:
             Services.prefs.getBoolPref(SAVE_CREDITCARD_DEFAULT_PREF, false),
           saveAddressDefaultChecked:
             Services.prefs.getBoolPref(SAVE_ADDRESS_DEFAULT_PREF, false),
         }, waivedContent);
         return Cu.cloneInto(prefValues, waivedContent);
       },
+
+      isOfficialBranding() {
+        return AppConstants.MOZILLA_OFFICIAL;
+      },
     };
     waivedContent.PaymentDialogUtils = Cu.cloneInto(PaymentDialogUtils, waivedContent, {
       cloneFunctions: true,
     });
   },
 
   sendToChrome({detail}) {
     let {messageType} = detail;
--- a/browser/components/payments/res/components/accepted-cards.css
+++ b/browser/components/payments/res/components/accepted-cards.css
@@ -1,44 +1,104 @@
 accepted-cards {
   margin: 1em 0;
+  display: flex;
+  flex-wrap: nowrap;
+  align-items: first baseline;
 }
 
 .accepted-cards-label {
   display: inline-block;
-  margin-inline-end: 1em;
-  color: GrayText;
   font-size: smaller;
+  flex: 0 2 content;
+  white-space: nowrap;
 }
 
 .accepted-cards-list {
   display: inline-block;
   list-style-type: none;
   margin: 0;
   padding: 0;
+  flex: 2 1 auto;
 }
 
 .accepted-cards-list > .accepted-cards-item {
   display: inline-block;
-  width: 50px;
-  height: 22px;
+  width: 32px;
+  height: 32px;
   padding: 0;
-  text-align: center;
-  margin-inline-end: 8px;
+  margin: 5px 0;
+  margin-inline-start: 10px;
+  vertical-align: middle;
   background-repeat: no-repeat;
   background-position: center;
-  vertical-align: middle;
+  background-size: contain;
 }
 
 /* placeholders for specific card icons we don't yet have assets for */
-.accepted-cards-item[data-network-id] {
+accepted-cards:not(.branded) .accepted-cards-item[data-network-id] {
+  width: 48px;
+  text-align: center;
   background-image: url("./card-icon.svg");
+  -moz-context-properties: fill-opacity;
+  fill-opacity: 0.5;
 }
-.accepted-cards-item[data-network-id]::after {
+accepted-cards:not(.branded) .accepted-cards-item[data-network-id]::after {
   box-sizing: border-box;
   content: attr(data-network-id);
-  padding: 4px;
+  padding: 8px 4px 0 4px;
   text-align: center;
   font-size: 0.7rem;
-  display: block;
+  display: inline-block;
   overflow: hidden;
   width: 100%;
 }
+
+/*
+  We use .png / @2x.png images where we don't yet have a vector version of a logo
+*/
+.accepted-cards-item[data-network-id="amex"] {
+  background-image: url("chrome://formautofill/content/third-party/cc-logo-amex.png");
+}
+
+.accepted-cards-item[data-network-id="cartebancaire"] {
+  background-image: url("chrome://formautofill/content/third-party/cc-logo-cartebancaire.png");
+}
+
+.accepted-cards-item[data-network-id="diners"] {
+  background-image: url("chrome://formautofill/content/third-party/cc-logo-diners.svg");
+}
+
+.accepted-cards-item[data-network-id="discover"] {
+  background-image: url("chrome://formautofill/content/third-party/cc-logo-discover.png");
+}
+
+.accepted-cards-item[data-network-id="jcb"] {
+  background-image: url("chrome://formautofill/content/third-party/cc-logo-jcb.svg");
+}
+
+.accepted-cards-item[data-network-id="mastercard"] {
+  background-image: url("chrome://formautofill/content/third-party/cc-logo-mastercard.svg");
+}
+
+.accepted-cards-item[data-network-id="mir"] {
+  background-image: url("chrome://formautofill/content/third-party/cc-logo-mir.svg");
+}
+
+.accepted-cards-item[data-network-id="unionpay"] {
+  background-image: url("chrome://formautofill/content/third-party/cc-logo-unionpay.svg");
+}
+
+.accepted-cards-item[data-network-id="visa"] {
+  background-image: url("chrome://formautofill/content/third-party/cc-logo-visa.svg");
+}
+
+@media (min-resolution: 1.1dppx) {
+  .accepted-cards-item[data-network-id="amex"] {
+    background-image: url("chrome://formautofill/content/third-party/cc-logo-amex@2x.png");
+  }
+  .accepted-cards-item[data-network-id="cartebancaire"] {
+    background-image: url("chrome://formautofill/content/third-party/cc-logo-cartebancaire@2x.png");
+  }
+  .accepted-cards-item[data-network-id="discover"] {
+    background-image: url("chrome://formautofill/content/third-party/cc-logo-discover@2x.png");
+  }
+}
--- a/browser/components/payments/res/components/accepted-cards.js
+++ b/browser/components/payments/res/components/accepted-cards.js
@@ -1,14 +1,13 @@
 /* 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/. */
 
 import PaymentStateSubscriberMixin from "../mixins/PaymentStateSubscriberMixin.js";
-import paymentRequest from "../paymentRequest.js";
 /* import-globals-from ../unprivileged-fallbacks.js */
 
 /**
  * <accepted-cards></accepted-cards>
  */
 
 export default class AcceptedCards extends PaymentStateSubscriberMixin(HTMLElement) {
   constructor() {
@@ -25,29 +24,42 @@ export default class AcceptedCards exten
     this.appendChild(this._labelEl);
 
     this._listEl.textContent = "";
     let allNetworks = PaymentDialogUtils.getCreditCardNetworks();
     for (let network of allNetworks) {
       let item = document.createElement("li");
       item.classList.add("accepted-cards-item");
       item.dataset.networkId = network;
+      item.setAttribute("aria-role", "image");
+      item.setAttribute("aria-label", network);
       this._listEl.appendChild(item);
     }
+    let isBranded = PaymentDialogUtils.isOfficialBranding();
+    this.classList.toggle("branded", isBranded);
     this.appendChild(this._listEl);
     // Only call the connected super callback(s) once our markup is fully
     // connected
     super.connectedCallback();
   }
 
   render(state) {
-    let acceptedNetworks = paymentRequest.getAcceptedNetworks(state.request);
-    for (let item of this._listEl.children) {
-      let network = item.dataset.networkId;
-      item.hidden = !(network && acceptedNetworks.includes(network));
+    let basicCardMethod = state.request.paymentMethods
+      .find(method => method.supportedMethods == "basic-card");
+    let merchantNetworks = basicCardMethod && basicCardMethod.data &&
+                           basicCardMethod.data.supportedNetworks;
+    if (merchantNetworks && merchantNetworks.length) {
+      for (let item of this._listEl.children) {
+        let network = item.dataset.networkId;
+        item.hidden = !(network && merchantNetworks.includes(network));
+      }
+      this.hidden = false;
+    } else {
+      // hide the whole list if the merchant didn't specify a preference
+      this.hidden = true;
     }
   }
 
   set label(value) {
     this._labelEl.textContent = value;
   }
 
   get acceptedItems() {
--- a/browser/components/payments/res/components/card-icon.svg
+++ b/browser/components/payments/res/components/card-icon.svg
@@ -1,6 +1,6 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 47 22">
-  <rect x="0" y="0" width="47" height="22" rx="4" ry="4" fill="#000" fill-opacity="0.2">
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 32">
+  <rect x="0" y="0" width="48" height="32" rx="4" ry="4" fill="#000" fill-opacity="context-fill-opacity">
   </rect>
-  <rect x="0" y="5" width="47" height="12" fill="#fff" fill-opacity="1">
+  <rect x="0" y="6" width="48" height="20" fill="#fff" fill-opacity="1">
   </rect>
 </svg>
--- a/browser/components/payments/res/containers/basic-card-form.js
+++ b/browser/components/payments/res/containers/basic-card-form.js
@@ -149,17 +149,16 @@ export default class BasicCardForm exten
       this.saveButton.textContent = editing ? this.dataset.updateButtonLabel :
                                               this.dataset.addButtonLabel;
     }
     this.persistCheckbox.label = this.dataset.persistCheckboxLabel;
     this.persistCheckbox.infoTooltip = this.dataset.persistCheckboxInfoTooltip;
     this.addressAddLink.textContent = this.dataset.addressAddLinkLabel;
     this.addressEditLink.textContent = this.dataset.addressEditLinkLabel;
     this.acceptedCardsList.label = this.dataset.acceptedCardsLabel;
-    this.acceptedCardsList.hidden = editing;
 
     // The next line needs an onboarding check since we don't set previousId
     // when navigating to add/edit directly from the summary page.
     this.backButton.hidden = !page.previousId && page.onboardingWizard;
     this.cancelButton.hidden = !page.onboardingWizard;
 
     let record = {};
     let basicCards = paymentRequest.getBasicCards(state);
--- a/browser/components/payments/res/containers/payment-dialog.js
+++ b/browser/components/payments/res/containers/payment-dialog.js
@@ -369,23 +369,16 @@ export default class PaymentDialog exten
 
     let paymentOptions = request.paymentOptions;
     for (let element of this._shippingRelatedEls) {
       element.hidden = !paymentOptions.requestShipping;
     }
 
     this._renderPayerFields(state);
 
-    // hide the accepted cards list if the merchant didn't specify a preference
-    let basicCardMethod = request.paymentMethods
-      .find(method => method.supportedMethods == "basic-card");
-    let merchantNetworks = basicCardMethod && basicCardMethod.data &&
-                           basicCardMethod.data.supportedNetworks;
-    this._acceptedCardsList.hidden = !(merchantNetworks && merchantNetworks.length);
-
     let isMac = /mac/i.test(navigator.platform);
     for (let manageTextEl of this._manageText.children) {
       manageTextEl.hidden = manageTextEl.dataset.os == "mac" ? !isMac : isMac;
       let link = manageTextEl.querySelector("a");
       // The href is only set to be exposed to accessibility tools so users know what will open.
       // The actual opening happens from the click event listener.
       link.href = "about:preferences#privacy-address-autofill";
     }
--- a/browser/components/payments/res/containers/payment-method-picker.js
+++ b/browser/components/payments/res/containers/payment-method-picker.js
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 import BasicCardOption from "../components/basic-card-option.js";
 import RichPicker from "./rich-picker.js";
 import paymentRequest from "../paymentRequest.js";
+/* import-globals-from ../unprivileged-fallbacks.js */
 
 /**
  * <payment-method-picker></payment-method-picker>
  * Container around add/edit links and <rich-select> with
  * <basic-card-option> listening to savedBasicCards.
  */
 
 export default class PaymentMethodPicker extends RichPicker {
@@ -88,17 +89,21 @@ export default class PaymentMethodPicker
     if (hasMissingFields) {
       return false;
     }
     let selectedOption = this.selectedOption;
     if (!selectedOption) {
       return true;
     }
 
-    let acceptedNetworks = paymentRequest.getAcceptedNetworks(state.request);
+    let basicCardMethod = state.request.paymentMethods
+      .find(method => method.supportedMethods == "basic-card");
+    let merchantNetworks = basicCardMethod && basicCardMethod.data &&
+                           basicCardMethod.data.supportedNetworks;
+    let acceptedNetworks = merchantNetworks || PaymentDialogUtils.getCreditCardNetworks();
     let selectedCard = paymentRequest.getBasicCards(state)[selectedOption.value];
     let isSupported = selectedCard["cc-type"] &&
                       acceptedNetworks.includes(selectedCard["cc-type"]);
     return isSupported;
   }
 
   get selectedStateKey() {
     return this.getAttribute("selected-state-key");
--- a/browser/components/payments/res/debugging.html
+++ b/browser/components/payments/res/debugging.html
@@ -12,16 +12,17 @@
   <body>
     <div>
       <section class="group">
         <button id="refresh">Refresh</button>
         <button id="rerender">Re-render</button>
         <button id="logState">Log state</button>
         <button id="debugFrame" hidden>Debug frame</button>
         <button id="toggleDirectionality">Toggle :dir</button>
+        <button id="toggleBranding">Toggle branding</button>
       </section>
       <section class="group">
         <h1>Requests</h1>
         <button id="setRequest1">Request 1</button>
         <button id="setRequest2">Request 2</button>
         <fieldset id="paymentOptions">
           <legend>Payment Options</legend>
           <label><input type="checkbox" autocomplete="off" name="requestPayerName" id="setRequestPayerName">requestPayerName</label>
--- a/browser/components/payments/res/debugging.js
+++ b/browser/components/payments/res/debugging.js
@@ -514,16 +514,22 @@ let buttonActions = {
       request: Object.assign({}, request, { completeStatus }),
     });
   },
 
   toggleDirectionality() {
     let body = paymentDialog.ownerDocument.body;
     body.dir = body.dir == "rtl" ? "ltr" : "rtl";
   },
+
+  toggleBranding() {
+    for (let container of paymentDialog.querySelectorAll("accepted-cards")) {
+      container.classList.toggle("branded");
+    }
+  },
 };
 
 window.addEventListener("click", function onButtonClick(evt) {
   let id = evt.target.id || evt.target.name;
   if (!id || typeof(buttonActions[id]) != "function") {
     return;
   }
 
--- a/browser/components/payments/res/paymentRequest.js
+++ b/browser/components/payments/res/paymentRequest.js
@@ -276,25 +276,13 @@ var paymentRequest = {
     let addresses = Object.assign({}, state.savedAddresses, state.tempAddresses);
     return addresses;
   },
 
   getBasicCards(state) {
     let cards = Object.assign({}, state.savedBasicCards, state.tempBasicCards);
     return cards;
   },
-
-  getAcceptedNetworks(request) {
-    let basicCardMethod = request.paymentMethods
-      .find(method => method.supportedMethods == "basic-card");
-    let merchantNetworks = basicCardMethod && basicCardMethod.data &&
-                           basicCardMethod.data.supportedNetworks;
-    if (merchantNetworks && merchantNetworks.length) {
-      return merchantNetworks;
-    }
-    // fallback to the complete list if the merchant didn't specify
-    return PaymentDialogUtils.getCreditCardNetworks();
-  },
 };
 
 paymentRequest.init();
 
 export default paymentRequest;
--- a/browser/components/payments/res/unprivileged-fallbacks.js
+++ b/browser/components/payments/res/unprivileged-fallbacks.js
@@ -98,9 +98,12 @@ var PaymentDialogUtils = {
   },
   getDefaultPreferences() {
     let prefValues = {
       saveCreditCardDefaultChecked: false,
       saveAddressDefaultChecked: true,
     };
     return prefValues;
   },
+  isOfficialBranding() {
+    return false;
+  },
 };
--- a/browser/components/payments/test/mochitest/payments_common.js
+++ b/browser/components/payments/test/mochitest/payments_common.js
@@ -106,19 +106,20 @@ SpecialPowers.registerConsoleListener(fu
   if (msg.isWarning || !msg.errorMessage || msg.errorMessage == "paymentRequest.xhtml:") {
     // Ignore warnings and non-errors.
     return;
   }
   if (msg.category == "CSP_CSPViolationWithURI" && msg.errorMessage.includes("at inline")) {
     // Ignore unknown CSP error.
     return;
   }
-  if (msg.message.includes("Security Error: Content at http://mochi.test:8888")) {
+  if (msg.message && msg.message.includes("Security Error: Content at http://mochi.test:8888")) {
     // Check for same-origin policy violations and ignore specific errors
     if (msg.message.includes("icon-credit-card-generic.svg") ||
+        msg.message.includes("accepted-cards.css") ||
         msg.message.includes("editDialog-shared.css") ||
         msg.message.includes("editAddress.css") ||
         msg.message.includes("editDialog.css") ||
         msg.message.includes("editCreditCard.css")) {
       return;
     }
   }
   if (msg.message == "SENTINEL") {
--- a/browser/components/payments/test/mochitest/test_accepted_cards.html
+++ b/browser/components/payments/test/mochitest/test_accepted_cards.html
@@ -25,16 +25,18 @@ Test the accepted-cards element
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
 <script type="module">
 /** Test the accepted-cards component **/
 
+/* global sinon, PaymentDialogUtils */
+
 import "../../res/components/accepted-cards.js";
 import {requestStore} from "../../res/mixins/PaymentStateSubscriberMixin.js";
 let emptyState = requestStore.getState();
 let acceptedElem = document.querySelector("accepted-cards");
 let allNetworks = PaymentDialogUtils.getCreditCardNetworks();
 
 add_task(async function test_reConnected() {
   let itemsCount = acceptedElem.querySelectorAll(".accepted-cards-item").length;
@@ -71,12 +73,41 @@ add_task(async function test_someAccepte
          `Item for the ${network} network expected to be visible`);
     } else {
       ok(acceptedElem.querySelector(`[data-network-id='${network}'][hidden]`),
          `Item for the ${network} network expected to be hidden`);
     }
   }
 });
 
+add_task(async function test_officialBranding() {
+  // verify we get the expected result when isOfficialBranding returns true
+  sinon.stub(PaymentDialogUtils, "isOfficialBranding").callsFake(() => { return true; });
+
+  let container = acceptedElem.parentNode;
+  let removed = container.removeChild(acceptedElem);
+  container.appendChild(removed);
+
+  ok(PaymentDialogUtils.isOfficialBranding.calledOnce,
+     "isOfficialBranding was called");
+  ok(acceptedElem.classList.contains("branded"),
+     "The branded class is added when isOfficialBranding returns true");
+  PaymentDialogUtils.isOfficialBranding.restore();
+
+  // verify we get the expected result when isOfficialBranding returns false
+  sinon.stub(PaymentDialogUtils, "isOfficialBranding").callsFake(() => { return false; });
+
+  // the branded class is toggled in the 'connectedCallback',
+  // so remove and re-add the element to re-evaluate branded-ness
+  removed = container.removeChild(acceptedElem);
+  container.appendChild(removed);
+
+  ok(PaymentDialogUtils.isOfficialBranding.calledOnce,
+     "isOfficialBranding was called");
+  ok(!acceptedElem.classList.contains("branded"),
+     "The branded class is removed when isOfficialBranding returns false");
+  PaymentDialogUtils.isOfficialBranding.restore();
+});
+
 </script>
 
 </body>
 </html>
--- a/browser/components/payments/test/mochitest/test_basic_card_form.html
+++ b/browser/components/payments/test/mochitest/test_basic_card_form.html
@@ -32,16 +32,23 @@ Test the basic-card-form element
 <script type="module">
 /** Test the basic-card-form element **/
 
 /* global sinon */
 
 import BasicCardForm from "../../res/containers/basic-card-form.js";
 
 let display = document.getElementById("display");
+let supportedNetworks = ["discover", "amex"];
+let paymentMethods = [{
+  supportedMethods: "basic-card",
+  data: {
+    supportedNetworks,
+  },
+}];
 
 function checkCCForm(customEl, expectedCard) {
   const CC_PROPERTY_NAMES = [
     "billingAddressGUID",
     "cc-number",
     "cc-name",
     "cc-exp-month",
     "cc-exp-year",
@@ -112,25 +119,30 @@ add_task(async function test_saveButton(
   await form.promiseReady;
   display.appendChild(form);
 
   let address1 = deepClone(PTU.Addresses.TimBL);
   address1.guid = "TimBLGUID";
   let address2 = deepClone(PTU.Addresses.TimBL2);
   address2.guid = "TimBL2GUID";
 
+
   await form.requestStore.setState({
+    request: {
+      paymentMethods,
+    },
     savedAddresses: {
       [address1.guid]: deepClone(address1),
       [address2.guid]: deepClone(address2),
     },
   });
 
   await asyncElementRendered();
 
+  // when merchant provides supportedNetworks, the accepted card list should be visible
   ok(!form.acceptedCardsList.hidden, "Accepted card list should be visible when adding a card");
 
   ok(form.saveButton.disabled, "Save button should initially be disabled");
   fillField(form.form.querySelector("#cc-number"), "4111 1111-1111 1111");
   form.form.querySelector("#cc-name").focus();
   // Check .disabled after .focus() so that it's after both "input" and "change" events.
   ok(form.saveButton.disabled, "Save button should still be disabled without a name");
   sendString("J. Smith");
@@ -344,16 +356,19 @@ add_task(async function test_edit() {
 
   info("test year before current");
   let card1 = deepClone(PTU.BasicCards.JohnDoe);
   card1.guid = "9864798564";
   card1["cc-exp-year"] = 2011;
   card1.billingAddressGUID = address1.guid;
 
   await form.requestStore.setState({
+    request: {
+      paymentMethods,
+    },
     page: {
       id: "basic-card-page",
     },
     "basic-card-page": {
       guid: card1.guid,
     },
     savedAddresses: {
       [address1.guid]: deepClone(address1),
@@ -363,17 +378,17 @@ add_task(async function test_edit() {
     },
   });
   await asyncElementRendered();
   is(form.saveButton.textContent, "Update", "Check label");
   is(form.querySelectorAll(":-moz-ui-invalid").length, 0,
      "Check no fields are visibly invalid on an 'edit' form with a complete card");
   checkCCForm(form, card1);
   ok(!form.saveButton.disabled, "Save button should be enabled upon edit for a valid card");
-  ok(form.acceptedCardsList.hidden, "Accepted card list should be hidden when editing a card");
+  ok(!form.acceptedCardsList.hidden, "Accepted card list should be visible when editing a card");
 
   let requiredElements = [...form.form.elements].filter(e => e.required && !e.disabled);
   ok(requiredElements.length, "There should be at least one required element");
   is(requiredElements.length, 5, "Number of required elements");
   for (let element of requiredElements) {
     if (element.id == "billingAddressGUID") {
       // The billing address has a different layout.
       continue;
new file mode 100644
--- /dev/null
+++ b/browser/components/urlbar/UrlbarMatch.jsm
@@ -0,0 +1,59 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * This module exports a urlbar match class, each representing a single match.
+ * A match is a single search result found by a provider, that can be passed
+ * from the model to the view, through the controller. It is mainly defined by
+ * a type of the match, and a payload, containing the data. A few getters allow
+ * to retrieve information common to all the match types.
+ */
+
+var EXPORTED_SYMBOLS = ["UrlbarMatch"];
+
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
+});
+
+/**
+ * Class used to create a match.
+ */
+class UrlbarMatch {
+  /**
+   * Creates a match.
+   * @param {integer} matchType one of UrlbarUtils.MATCHTYPE.* values
+   * @param {object} payload data for this match. A payload should always
+   *        contain a way to extract a final url to visit. The url getter
+   *        should have a case for each of the types.
+   */
+  constructor(matchType, payload) {
+    this.type = matchType;
+    this.payload = payload;
+  }
+
+  /**
+   * Returns a final destination for this match.
+   * Different kind of matches may have different ways to express this value,
+   * and this is a common getter for all of them.
+   * @returns {string} a url to load when this match is confirmed byt the user.
+   */
+  get url() {
+    switch (this.type) {
+      case UrlbarUtils.MATCH_TYPE.TAB_SWITCH:
+        return this.payload.url;
+    }
+    return "";
+  }
+
+  /**
+   * Returns a title that could be used as a label for this match.
+   * @returns {string} The label to show in a simplified title / url view.
+   */
+  get title() {
+    return "";
+  }
+}
--- a/browser/components/urlbar/UrlbarPrefs.jsm
+++ b/browser/components/urlbar/UrlbarPrefs.jsm
@@ -143,25 +143,25 @@ const PREF_TYPES = new Map([
 // Each bucket is an array containing the following indices:
 //   0: The match type of the acceptable entries.
 //   1: available number of slots in this bucket.
 // There are different matchBuckets definition for different contexts, currently
 // a general one (matchBuckets) and a search one (matchBucketsSearch).
 //
 // First buckets. Anything with an Infinity frecency ends up here.
 const DEFAULT_BUCKETS_BEFORE = [
-  [UrlbarUtils.MATCHTYPE.HEURISTIC, 1],
-  [UrlbarUtils.MATCHTYPE.EXTENSION, UrlbarUtils.MAXIMUM_ALLOWED_EXTENSION_MATCHES - 1],
+  [UrlbarUtils.MATCH_GROUP.HEURISTIC, 1],
+  [UrlbarUtils.MATCH_GROUP.EXTENSION, UrlbarUtils.MAXIMUM_ALLOWED_EXTENSION_MATCHES - 1],
 ];
 // => USER DEFINED BUCKETS WILL BE INSERTED HERE <=
 //
 // Catch-all buckets. Anything remaining ends up here.
 const DEFAULT_BUCKETS_AFTER = [
-  [UrlbarUtils.MATCHTYPE.SUGGESTION, Infinity],
-  [UrlbarUtils.MATCHTYPE.GENERAL, Infinity],
+  [UrlbarUtils.MATCH_GROUP.SUGGESTION, Infinity],
+  [UrlbarUtils.MATCH_GROUP.GENERAL, Infinity],
 ];
 
 /**
  * Preferences class.  The exported object is a singleton instance.
  */
 class Preferences {
   /**
    * Constructor
new file mode 100644
--- /dev/null
+++ b/browser/components/urlbar/UrlbarProviderOpenTabs.jsm
@@ -0,0 +1,213 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * This module exports a provider, returning open tabs matches for the urlbar.
+ * It is also used to register and unregister open tabs.
+ */
+
+var EXPORTED_SYMBOLS = ["UrlbarProviderOpenTabs"];
+
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  Log: "resource://gre/modules/Log.jsm",
+  PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
+  UrlbarMatch: "resource:///modules/UrlbarMatch.jsm",
+  UrlbarProvidersManager: "resource:///modules/UrlbarProvidersManager.jsm",
+  UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
+});
+
+XPCOMUtils.defineLazyGetter(this, "logger",
+  () => Log.repository.getLogger("Places.Urlbar.Provider.OpenTabs"));
+
+/**
+ * Class used to create the provider.
+ */
+class ProviderOpenTabs {
+  constructor() {
+    // Maps the open tabs by userContextId.
+    this.openTabs = new Map();
+    // Maps the running queries by queryContext.
+    this.queries = new Map();
+  }
+
+  /**
+   * Database handle. For performance reasons the temp tables are created and
+   * populated only when a query starts, rather than when a tab is added.
+   * @returns {object} the Sqlite database handle.
+   */
+  async promiseDb() {
+    if (this._db) {
+      return this._db;
+    }
+    let conn = await PlacesUtils.promiseLargeCacheDBConnection();
+    // Create the temp tables to store open pages.
+    UrlbarProvidersManager.runInCriticalSection(async () => {
+      // These should be kept up-to-date with the definition in nsPlacesTables.h.
+      await conn.execute(`
+        CREATE TEMP TABLE IF NOT EXISTS moz_openpages_temp (
+          url TEXT,
+          userContextId INTEGER,
+          open_count INTEGER,
+          PRIMARY KEY (url, userContextId)
+        )
+      `);
+      await conn.execute(`
+        CREATE TEMP TRIGGER IF NOT EXISTS moz_openpages_temp_afterupdate_trigger
+        AFTER UPDATE OF open_count ON moz_openpages_temp FOR EACH ROW
+        WHEN NEW.open_count = 0
+        BEGIN
+          DELETE FROM moz_openpages_temp
+          WHERE url = NEW.url
+            AND userContextId = NEW.userContextId;
+        END
+      `);
+    }).catch(Cu.reportError);
+
+    // Populate the table with the current cache contents...
+    for (let [userContextId, urls] of this.openTabs) {
+      for (let url of urls) {
+        await addToMemoryTable(conn, url, userContextId).catch(Cu.reportError);
+      }
+    }
+    return this._db = conn;
+  }
+
+  /**
+   * Returns the name of this provider.
+   * @returns {string} the name of this provider.
+   */
+  get name() {
+    return "OpenTabs";
+  }
+
+  /**
+   * Returns the type of this provider.
+   * @returns {integer} one of the types from UrlbarProvidersManager.TYPE.*
+   */
+  get type() {
+    return UrlbarUtils.PROVIDER_TYPE.PROFILE;
+  }
+
+  /**
+   * Registers a tab as open.
+   * @param {string} url Address of the tab
+   * @param {integer} userContextId Containers user context id
+   */
+  registerOpenTab(url, userContextId = 0) {
+    if (!this.openTabs.has(userContextId)) {
+      this.openTabs.set(userContextId, []);
+    }
+    this.openTabs.get(userContextId).push(url);
+    if (this._db) {
+      addToMemoryTable(this._db, url, userContextId);
+    }
+  }
+
+  /**
+   * Unregisters a previously registered open tab.
+   * @param {string} url Address of the tab
+   * @param {integer} userContextId Containers user context id
+   */
+  unregisterOpenTab(url, userContextId = 0) {
+    let openTabs = this.openTabs.get(userContextId);
+    if (openTabs) {
+      let index = openTabs.indexOf(url);
+      if (index != -1) {
+        openTabs.splice(index, 1);
+        if (this._db) {
+          removeFromMemoryTable(this._db, url, userContextId);
+        }
+      }
+    }
+  }
+
+  /**
+   * Starts querying.
+   * @param {object} queryContext The query context object
+   * @param {function} addCallback Callback invoked by the provider to add a new
+   *        match.
+   * @returns {Promise} resolved when the query stops.
+   */
+  async startQuery(queryContext, addCallback) {
+    // TODO:
+    //  * properly search and handle tokens, this is just a mock for now.
+    //  * we won't search openTabs like this usually, the history search will
+    //  * coalesce the temp table with its data.
+    logger.info(`Starting query for ${queryContext.searchString}`);
+    let instance = {};
+    this.queries.set(queryContext, instance);
+    let conn = await this.promiseDb();
+    await conn.executeCached(`
+      SELECT url, userContextId
+      FROM moz_openpages_temp
+    `, {}, (row, cancel) => {
+      if (!this.queries.has(queryContext)) {
+        cancel();
+        return;
+      }
+      addCallback(this, new UrlbarMatch(UrlbarUtils.MATCH_TYPE.TAB_SWITCH, {
+        url: row.getResultByName("url"),
+        userContextId: row.getResultByName("userContextId"),
+      }));
+    });
+    // We are done.
+    this.queries.delete(queryContext);
+  }
+
+  /**
+   * Cancels a running query.
+   * @param {object} queryContext The query context object
+   */
+  cancelQuery(queryContext) {
+    logger.info(`Canceling query for ${queryContext.searchString}`);
+    this.queries.delete(queryContext);
+  }
+}
+
+var UrlbarProviderOpenTabs = new ProviderOpenTabs();
+
+/**
+ * Adds an open page to the memory table.
+ * @param {object} conn A Sqlite.jsm database handle
+ * @param {string} url Address of the page
+ * @param {number} userContextId Containers user context id
+ * @returns {Promise} resolved after the addition.
+ */
+async function addToMemoryTable(conn, url, userContextId) {
+  return UrlbarProvidersManager.runInCriticalSection(async () => {
+    await conn.executeCached(`
+      INSERT OR REPLACE INTO moz_openpages_temp (url, userContextId, open_count)
+      VALUES ( :url,
+                :userContextId,
+                IFNULL( ( SELECT open_count + 1
+                          FROM moz_openpages_temp
+                          WHERE url = :url
+                          AND userContextId = :userContextId ),
+                        1
+                      )
+              )
+    `, { url, userContextId });
+  });
+}
+
+/**
+ * Removes an open page from the memory table.
+ * @param {object} conn A Sqlite.jsm database handle
+ * @param {string} url Address of the page
+ * @param {number} userContextId Containers user context id
+ * @returns {Promise} resolved after the removal.
+ */
+async function removeFromMemoryTable(conn, url, userContextId) {
+  return UrlbarProvidersManager.runInCriticalSection(async () => {
+    await conn.executeCached(`
+      UPDATE moz_openpages_temp
+      SET open_count = open_count - 1
+      WHERE url = :url
+        AND userContextId = :userContextId
+    `, { url, userContextId });
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/urlbar/UrlbarProvidersManager.jsm
@@ -0,0 +1,237 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * This module exports a component used to register search providers and manage
+ * the connection between such providers and a UrlbarController.
+ */
+
+var EXPORTED_SYMBOLS = ["UrlbarProvidersManager"];
+
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  Log: "resource://gre/modules/Log.jsm",
+  PlacesUtils: "resource://modules/PlacesUtils.jsm",
+  UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
+  UrlbarTokenizer: "resource:///modules/UrlbarTokenizer.jsm",
+  UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
+});
+
+XPCOMUtils.defineLazyGetter(this, "logger", () =>
+  Log.repository.getLogger("Places.Urlbar.ProvidersManager"));
+
+// List of available local providers, each is implemented in its own jsm module
+// and will track different queries internally by queryContext.
+var localProviderModules = {
+  UrlbarProviderOpenTabs: "resource:///modules/UrlbarProviderOpenTabs.jsm",
+};
+
+/**
+ * Class used to create a manager.
+ * The manager is responsible to keep a list of providers, instantiate query
+ * objects and pass those to the providers.
+ */
+class ProvidersManager {
+  constructor() {
+    // Tracks the available providers.
+    // This is a double map, first it maps by PROVIDER_TYPE, then
+    // registerProvider maps by provider.name: { type: { name: provider }}
+    this.providers = new Map();
+    for (let type of Object.values(UrlbarUtils.PROVIDER_TYPE)) {
+      this.providers.set(type, new Map());
+    }
+    for (let [symbol, module] of Object.entries(localProviderModules)) {
+      let {[symbol]: provider} = ChromeUtils.import(module, {});
+      this.registerProvider(provider);
+    }
+    // Tracks ongoing Query instances by queryContext.
+    this.queries = new Map();
+
+    // Interrupt() allows to stop any running SQL query, some provider may be
+    // running a query that shouldn't be interrupted, and if so it should
+    // bump this through disableInterrupt and enableInterrupt.
+    this.interruptLevel = 0;
+  }
+
+  /**
+   * Registers a provider object with the manager.
+   * @param {object} provider
+   */
+  registerProvider(provider) {
+    logger.info(`Registering provider ${provider.name}`);
+    if (!Object.values(UrlbarUtils.PROVIDER_TYPE).includes(provider.type)) {
+      throw new Error(`Unknown provider type ${provider.type}`);
+    }
+    this.providers.get(provider.type).set(provider.name, provider);
+  }
+
+  /**
+   * Unregisters a previously registered provider object.
+   * @param {object} provider
+   */
+  unregisterProvider(provider) {
+    logger.info(`Unregistering provider ${provider.name}`);
+    this.providers.get(provider.type).delete(provider.name);
+  }
+
+  /**
+   * Starts querying.
+   * @param {object} queryContext The query context object
+   * @param {object} controller a UrlbarController instance
+   */
+  async startQuery(queryContext, controller) {
+    logger.info(`Query start ${queryContext.searchString}`);
+    let query = Object.seal(new Query(queryContext, controller, this.providers));
+    this.queries.set(queryContext, query);
+    await query.start();
+  }
+
+  /**
+   * Cancels a running query.
+   * @param {object} queryContext
+   */
+  cancelQuery(queryContext) {
+    logger.info(`Query cancel ${queryContext.searchString}`);
+    let query = this.queries.get(queryContext);
+    if (!query) {
+      throw new Error("Couldn't find a matching query for the given context");
+    }
+    query.cancel();
+    if (!this.interruptLevel) {
+      try {
+        let db = PlacesUtils.promiseLargeCacheDBConnection();
+        db.interrupt();
+      } catch (ex) {}
+    }
+    this.queries.delete(queryContext);
+  }
+
+  /**
+   * A provider can use this util when it needs to run a SQL query that can't
+   * be interrupted. Otherwise, when a query is canceled any running SQL query
+   * is interrupted abruptly.
+   * @param {function} taskFn a Task to execute in the critical section.
+   */
+  async runInCriticalSection(taskFn) {
+    this.interruptLevel++;
+    try {
+      await taskFn();
+    } finally {
+      this.interruptLevel--;
+    }
+  }
+}
+
+var UrlbarProvidersManager = new ProvidersManager();
+
+/**
+ * Tracks a query status.
+ * Multiple queries can potentially be executed at the same time by different
+ * controllers. Each query has to track its own status and delays separately,
+ * to avoid conflicting with other ones.
+ */
+class Query {
+  /**
+   * Initializes the query object.
+   * @param {object} queryContext
+   *        The query context
+   * @param {object} controller
+   *        The controller to be notified
+   * @param {object} providers
+   *        Map of all the providers by type and name
+   */
+  constructor(queryContext, controller, providers) {
+    this.context = queryContext;
+    this.context.results = [];
+    this.controller = controller;
+    this.providers = providers;
+    // Track the delay timer.
+    this.sleepResolve = Promise.resolve();
+    this.sleepTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    this.started = false;
+    this.canceled = false;
+    this.complete = false;
+  }
+
+  /**
+   * Starts querying.
+   */
+  async start() {
+    if (this.started) {
+      throw new Error("This Query has been started already");
+    }
+    this.started = true;
+    UrlbarTokenizer.tokenize(this.context);
+
+    let promises = [];
+    for (let provider of this.providers.get(UrlbarUtils.PROVIDER_TYPE.IMMEDIATE).values()) {
+      if (this.canceled) {
+        break;
+      }
+      promises.push(provider.startQuery(this.context, this.add));
+    }
+
+    await new Promise(resolve => {
+      let time = UrlbarPrefs.get("delay");
+      this.sleepResolve = resolve;
+      this.sleepTimer.initWithCallback(resolve, time, Ci.nsITimer.TYPE_ONE_SHOT);
+    });
+
+    for (let providerType of [UrlbarUtils.PROVIDER_TYPE.NETWORK,
+                              UrlbarUtils.PROVIDER_TYPE.PROFILE,
+                              UrlbarUtils.PROVIDER_TYPE.EXTENSION]) {
+      for (let provider of this.providers.get(providerType).values()) {
+        if (this.canceled) {
+          break;
+        }
+        promises.push(provider.startQuery(this.context, this.add.bind(this)));
+      }
+    }
+
+    await Promise.all(promises.map(p => p.catch(Cu.reportError)));
+
+    // Nothing should be failing above, since we catch all the promises, thus
+    // this is not in a finally for now.
+    this.complete = true;
+  }
+
+  /**
+   * Cancels this query.
+   * @note Invoking cancel multiple times is a no-op.
+   */
+  cancel() {
+    if (this.canceled) {
+      return;
+    }
+    this.canceled = true;
+    this.sleepTimer.cancel();
+    for (let providers of this.providers.values()) {
+      for (let provider of providers.values()) {
+        provider.cancelQuery(this.context);
+      }
+    }
+    this.sleepResolve();
+  }
+
+  /**
+   * Adds a match returned from a provider to the results set.
+   * @param {object} provider
+   * @param {object} match
+   */
+  add(provider, match) {
+    // Stop returning results as soon as we've been canceled.
+    if (this.canceled) {
+      return;
+    }
+    // TODO:
+    //  * coalesce results in timed chunks: we don't want to notify every single
+    //    result as soon as it arrives, we'll rather collect results for a few
+    //    ms, then send them
+    //  * pass results to a muxer before sending them back to the controller.
+    this.context.results.push(match);
+    this.controller.receiveResults(this.context);
+  }
+}
--- a/browser/components/urlbar/UrlbarUtils.jsm
+++ b/browser/components/urlbar/UrlbarUtils.jsm
@@ -17,21 +17,43 @@ var UrlbarUtils = {
     // Just append new results.
     APPEND: 0,
     // Merge previous and current results if search strings are related.
     MERGE_RELATED: 1,
     // Always merge previous and current results.
     MERGE: 2,
   },
 
-  MATCHTYPE: {
+  // Extensions are allowed to add suggestions if they have registered a keyword
+  // with the omnibox API. This is the maximum number of suggestions an extension
+  // is allowed to add for a given search string.
+  // This value includes the heuristic result.
+  MAXIMUM_ALLOWED_EXTENSION_MATCHES: 6,
+
+  // This is used by UnifiedComplete, the new implementation will use
+  // PROVIDER_TYPE and MATCH_TYPE
+  MATCH_GROUP: {
     HEURISTIC: "heuristic",
     GENERAL: "general",
     SUGGESTION: "suggestion",
     EXTENSION: "extension",
   },
 
-  // Extensions are allowed to add suggestions if they have registered a keyword
-  // with the omnibox API. This is the maximum number of suggestions an extension
-  // is allowed to add for a given search string.
-  // This value includes the heuristic result.
-  MAXIMUM_ALLOWED_EXTENSION_MATCHES: 6,
+  // Defines provider types.
+  PROVIDER_TYPE: {
+    // Should be executed immediately, because it returns heuristic results
+    // that must be handled to the user asap.
+    IMMEDIATE: 1,
+    // Can be delayed, contains results coming from the session or the profile.
+    PROFILE: 2,
+    // Can be delayed, contains results coming from the network.
+    NETWORK: 3,
+    // Can be delayed, contains results coming from unknown sources.
+    EXTENSION: 4,
+  },
+
+  // Defines UrlbarMatch types.
+  MATCH_TYPE: {
+    // Indicates an open tab.
+    // The payload is: { url, userContextId }
+    TAB_SWITCH: 1,
+  },
 };
--- a/browser/components/urlbar/moz.build
+++ b/browser/components/urlbar/moz.build
@@ -3,16 +3,19 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files("**"):
     BUG_COMPONENT = ("Firefox", "Address Bar")
 
 EXTRA_JS_MODULES += [
     'UrlbarController.jsm',
     'UrlbarInput.jsm',
+    'UrlbarMatch.jsm',
     'UrlbarPrefs.jsm',
+    'UrlbarProviderOpenTabs.jsm',
+    'UrlbarProvidersManager.jsm',
     'UrlbarTokenizer.jsm',
     'UrlbarUtils.jsm',
     'UrlbarView.jsm',
 ]
 
 BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
--- a/browser/components/urlbar/tests/unit/head.js
+++ b/browser/components/urlbar/tests/unit/head.js
@@ -9,22 +9,28 @@ var commonFile = do_get_file("../../../.
 if (commonFile) {
   let uri = Services.io.newFileURI(commonFile);
   Services.scriptloader.loadSubScript(uri.spec, this);
 }
 
 // Put any other stuff relative to this test folder below.
 
 ChromeUtils.import("resource:///modules/UrlbarController.jsm");
-ChromeUtils.defineModuleGetter(this, "UrlbarInput",
-                               "resource:///modules/UrlbarInput.jsm");
-ChromeUtils.defineModuleGetter(this, "UrlbarTokenizer",
-                               "resource:///modules/UrlbarTokenizer.jsm");
-ChromeUtils.defineModuleGetter(this, "PlacesTestUtils",
-                               "resource://testing-common/PlacesTestUtils.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  PlacesTestUtils: "resource://testing-common/PlacesTestUtils.jsm",
+  PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
+  UrlbarController: "resource:///modules/UrlbarController.jsm",
+  UrlbarInput: "resource:///modules/UrlbarInput.jsm",
+  UrlbarMatch: "resource:///modules/UrlbarMatch.jsm",
+  UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
+  UrlbarProviderOpenTabs: "resource:///modules/UrlbarProviderOpenTabs.jsm",
+  UrlbarProvidersManager: "resource:///modules/UrlbarProvidersManager.jsm",
+  UrlbarTokenizer: "resource:///modules/UrlbarTokenizer.jsm",
+  UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
+});
 
 // ================================================
 // Load mocking/stubbing library, sinon
 // docs: http://sinonjs.org/releases/v2.3.2/
 // Sinon needs Timer.jsm for setTimeout etc.
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
 /* globals sinon */
copy from browser/components/urlbar/tests/unit/test_QueryContext.js
copy to browser/components/urlbar/tests/unit/test_providerOpenTabs.js
--- a/browser/components/urlbar/tests/unit/test_QueryContext.js
+++ b/browser/components/urlbar/tests/unit/test_providerOpenTabs.js
@@ -1,53 +1,34 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-ChromeUtils.defineModuleGetter(this, "QueryContext",
-  "resource:///modules/UrlbarController.jsm");
-
-add_task(function test_constructor() {
-  Assert.throws(() => new QueryContext(),
-    /Missing or empty searchString provided to QueryContext/,
-    "Should throw with no arguments");
-
-  Assert.throws(() => new QueryContext({
-    searchString: "foo",
-    maxResults: 1,
-    isPrivate: false,
-  }), /Missing or empty lastKey provided to QueryContext/,
-    "Should throw with a missing lastKey parameter");
-
-  Assert.throws(() => new QueryContext({
-    searchString: "foo",
-    lastKey: "b",
-    isPrivate: false,
-  }), /Missing or empty maxResults provided to QueryContext/,
-    "Should throw with a missing maxResults parameter");
+add_task(async function test_openTabs() {
+  const userContextId = 5;
+  const url = "http://foo.mozilla.org/";
+  UrlbarProviderOpenTabs.registerOpenTab(url, userContextId);
+  UrlbarProviderOpenTabs.registerOpenTab(url, userContextId);
+  Assert.equal(UrlbarProviderOpenTabs.openTabs.get(userContextId).length, 2,
+               "Found all the expected tabs");
+  UrlbarProviderOpenTabs.unregisterOpenTab(url, userContextId);
+  Assert.equal(UrlbarProviderOpenTabs.openTabs.get(userContextId).length, 1,
+               "Found all the expected tabs");
 
-  Assert.throws(() => new QueryContext({
-    searchString: "foo",
-    lastKey: "b",
-    maxResults: 1,
-  }), /Missing or empty isPrivate provided to QueryContext/,
-    "Should throw with a missing isPrivate parameter");
+  let context = createContext();
+  let matchCount = 0;
+  let callback = function(provider, match) {
+    matchCount++;
+    Assert.equal(provider, UrlbarProviderOpenTabs, "Got the expected provider");
+    Assert.equal(match.type, UrlbarUtils.MATCH_TYPE.TAB_SWITCH,
+                 "Got the expected match type");
+    Assert.equal(match.url, url, "Got the expected url");
+    Assert.equal(match.title, "", "Got the expected title");
+  };
 
-  let qc = new QueryContext({
-    searchString: "foo",
-    lastKey: "b",
-    maxResults: 1,
-    isPrivate: true,
-    autoFill: false,
-  });
-
-  Assert.equal(qc.searchString, "foo",
-    "Should have saved the correct value for searchString");
-  Assert.equal(qc.lastKey, "b",
-    "Should have saved the correct value for lastKey");
-  Assert.equal(qc.maxResults, 1,
-    "Should have saved the correct value for maxResults");
-  Assert.strictEqual(qc.isPrivate, true,
-    "Should have saved the correct value for isPrivate");
-  Assert.strictEqual(qc.autoFill, false,
-    "Should have saved the correct value for autoFill");
+  await UrlbarProviderOpenTabs.startQuery(context, callback);
+  Assert.equal(matchCount, 1, "Found the expected number of matches");
+  // Sanity check that this doesn't throw.
+  UrlbarProviderOpenTabs.cancelQuery(context);
+  Assert.equal(UrlbarProviderOpenTabs.queries.size, 0,
+    "All the queries have been removed");
 });
copy from browser/components/urlbar/tests/unit/test_QueryContext.js
copy to browser/components/urlbar/tests/unit/test_providersManager.js
--- a/browser/components/urlbar/tests/unit/test_QueryContext.js
+++ b/browser/components/urlbar/tests/unit/test_providersManager.js
@@ -1,53 +1,55 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-ChromeUtils.defineModuleGetter(this, "QueryContext",
-  "resource:///modules/UrlbarController.jsm");
-
-add_task(function test_constructor() {
-  Assert.throws(() => new QueryContext(),
-    /Missing or empty searchString provided to QueryContext/,
-    "Should throw with no arguments");
-
-  Assert.throws(() => new QueryContext({
-    searchString: "foo",
-    maxResults: 1,
-    isPrivate: false,
-  }), /Missing or empty lastKey provided to QueryContext/,
-    "Should throw with a missing lastKey parameter");
-
-  Assert.throws(() => new QueryContext({
-    searchString: "foo",
-    lastKey: "b",
-    isPrivate: false,
-  }), /Missing or empty maxResults provided to QueryContext/,
-    "Should throw with a missing maxResults parameter");
-
-  Assert.throws(() => new QueryContext({
-    searchString: "foo",
-    lastKey: "b",
-    maxResults: 1,
-  }), /Missing or empty isPrivate provided to QueryContext/,
-    "Should throw with a missing isPrivate parameter");
-
-  let qc = new QueryContext({
-    searchString: "foo",
-    lastKey: "b",
-    maxResults: 1,
-    isPrivate: true,
-    autoFill: false,
+add_task(async function test_providers() {
+  // First unregister all the existing providers.
+  for (let providers of UrlbarProvidersManager.providers.values()) {
+    for (let provider of providers.values()) {
+      // While here check all providers have name and type.
+      Assert.ok(Object.values(UrlbarUtils.PROVIDER_TYPE).includes(provider.type),
+        `The provider "${provider.name}" should have a valid type`);
+      Assert.ok(provider.name, "All providers should have a name");
+      UrlbarProvidersManager.unregisterProvider(provider);
+    }
+  }
+  let match = new UrlbarMatch(UrlbarUtils.MATCH_TYPE.TAB_SWITCH, { url: "http://mozilla.org/foo/" });
+  UrlbarProvidersManager.registerProvider({
+    get name() {
+      return "TestProvider";
+    },
+    get type() {
+      return UrlbarUtils.PROVIDER_TYPE.PROFILE;
+    },
+    async startQuery(context, add) {
+      Assert.ok(context, "context is passed-in");
+      Assert.equal(typeof add, "function", "add is a callback");
+      this._context = context;
+      add(this, match);
+    },
+    cancelQuery(context) {
+      Assert.equal(this._context, context, "context is the same");
+    },
   });
 
-  Assert.equal(qc.searchString, "foo",
-    "Should have saved the correct value for searchString");
-  Assert.equal(qc.lastKey, "b",
-    "Should have saved the correct value for lastKey");
-  Assert.equal(qc.maxResults, 1,
-    "Should have saved the correct value for maxResults");
-  Assert.strictEqual(qc.isPrivate, true,
-    "Should have saved the correct value for isPrivate");
-  Assert.strictEqual(qc.autoFill, false,
-    "Should have saved the correct value for autoFill");
+  let context = createContext();
+  let controller = new UrlbarController();
+  let resultsPromise = promiseControllerNotification(controller, "onQueryResults");
+
+  await UrlbarProvidersManager.startQuery(context, controller);
+  // Sanity check that this doesn't throw. It should be a no-op since we await
+  // for startQuery.
+  UrlbarProvidersManager.cancelQuery(context);
+
+  let params = await resultsPromise;
+  Assert.deepEqual(params[0].results, [match]);
 });
+
+add_task(async function test_criticalSection() {
+  // Just a sanity check, this shouldn't throw.
+  await UrlbarProvidersManager.runInCriticalSection(async () => {
+    let db = await PlacesUtils.promiseLargeCacheDBConnection();
+    await db.execute(`PRAGMA page_cache`);
+  });
+});
--- a/browser/components/urlbar/tests/unit/xpcshell.ini
+++ b/browser/components/urlbar/tests/unit/xpcshell.ini
@@ -1,8 +1,10 @@
 [DEFAULT]
 head = head.js
 firefox-appdir = browser
 
+[test_providerOpenTabs.js]
+[test_providersManager.js]
 [test_QueryContext.js]
 [test_tokenizer.js]
 [test_UrlbarController_unit.js]
 [test_UrlbarController_integration.js]
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c51a5be4a0ffd6500c303a1f1ede1e3a84a52f3c
GIT binary patch
literal 1306
zc$@(m1?BpQP)<h;3K|Lk000e1NJLTq0015U0015c1^@s6J20-I000ExNkl<Zcmcgv
zQ<yYK5bU#U+qP}{?&WO$wr$(CZChinZ4H)#xTx&k?AeFO?~9$8s;-ViWOlE1W)o}6
zvzvRDpV`=w5C|7JHLZbZfz~n&T2psoKrZ3b8k%QmO+C;WS`q_t31{5I4Gzai)&<Ae
zYeKI7;v_z!kp-8vwjY7hxOPTkD=uqw#Jb?DCRQOkznSgOdix}P+TtMo%Y+t-b5R%!
zBhOzaP~U%=o{2lVx{+HUYX?yf2&3ET0%-r3vBn#a-}+t{`TjbC#Ps^&7B)H9l{b5?
zuUF82LlIp0)p%i05`r~Rd@?o-&kyj)UfX4PNG++s>%)=}s*U3F@%rBLLo@LFK%YTk
zM*G{WEEEUB;(+$2NpMvg2a34XcYUEmd{tYAT&r*?Q=eH@3uj(6*876c{x(VUT0<)P
zUy}i1Mtc@N)eU;DD}?s@KHSyKjT<{Uq5ZKBgEtkyUmcOO-_U_;4a_)`HCN;lc9lZe
z&oeURx2zjhH9dWIWD2V4qPVS#OUNeeE@jH|#fkfRcu*UT3HkNyoj9Fhdv`>N5WPPt
z73EbCT-(lp3z?}`hbBX7<TgZnP#m*NWqJx*+OaRI601A`oXrT7WAm-$s3AFtRjXk1
z_F`PF6wGvYT#+y5-W!#ICJS@8?=nFAuNe>b_DV$0QW)AlCb{w1Y?HGHei!05h9@JS
zB4(=9?@h@;d^Is$zMs=nPy6IOrD#LJ86)O_aXU+}EiHu8=mh(ficG>wgLORB-zV3d
z#q=(7`c)}}Y+Gsw3+)yBE{=-ON-HC1x+vEGF%KxQR8&P+<5Xxr?vWbBVmN5ktoyyJ
zvG(&`*}Gb)`q#Zu8n&0HoO^7gbSV34ZZ<U{%0CG)z3s9pAIfI3zT>x-z>-siqs^3E
zg-JU~FlBEkrZAn8sALtLvSltmhHor{gB_vF%<~g>l{1T_Cni3fXt5+0!?zY=0@J<1
z?FWNpt;#TbOR-uPjOCtE`3yQY-&TR~bYcvhRPA-RSxGu}8?Ptv#^m5hYb=VEf$-Zt
zJkr~Pit31@k1Zu@+An(*B%SPciTL8?Hd%X(saGwg%${3?8`x6XPkZp%sBFU{Qyh4P
zs#Z6;xsyxO{oT|I^jK4H5dAh3qAn7{=9C~VZDErh|C-9SG19xcxy4Z#t#!acTSYyI
zS^xg5o$6;nW~J0I8iUw!*7o9NHfhI;TiPXSPxkf7c<sHZT>Y*mvGlLZYaPGsl@bJ~
z<O4Q_wdOe?zk=<gX8BU5pJ(NKJ&F1IEsn)sW@hQ&EN)35s_CVyBovk!(>cKzbhORV
zJXwEbL>4u&x1PjI-S}N4$S$ut0yK&Gt>MX1(|fwR(Q#!yPHUhKqi)nfMJ@2fxU~3*
zuWj#?n^48_KsZf-*QnX-Ev&(Zqf<rI*R*knnw9lhTOeoTX|~M|M8fmpBCZZc@!9zO
z4||TzX9xOp@V9z`wc{*)iXwWMdpk2i*g}OfI(f2nV&r^5V5&3KKr$ZjqkX*c266(3
zc=+I#@A-i+cBF@JHKSH-_06Po<R7@sA4Ym<twCZ!Ug(%kqc~fCdL5kmm$W?WWv`XZ
zII*BXoH%&a!0T-SU+69T7baa65(07w=Q)k7|DN02GyUwQ)@g}=;!>Rd14*O&ourBK
QO#lD@07*qoM6N<$f_Wf_R{#J2
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f794641f3eeb81a8d259dd4d63bf9f7aa2304132
GIT binary patch
literal 2311
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..781c6e4958850f450a659931e3f594c789aac270
GIT binary patch
literal 1240
zc$@*)1Sk86P)<h;3K|Lk000e1NJLTq001Na0015c0ssI2%Y!Qf00006bW%=J00000
z003^L2ax~(1dT~VK~#7F?2N&!*jNn2NIh@$+fq8SHIr{&(m;+O2!WV`_{ZPC|Nj4V
zC){yEmfa>4zcB5FPqN(4yYI4bce3x@e&ds*i9fM_zU94-d$V4(5GQn@gpZ;iqM{uw
z96=c<m^UbkGn~;1p3A9eL??FU_2eoyZHcd~anh8jlzg5-3m+<A9c|Y*EY*|DV(i4R
znV!Qg$xlpjg_|h}6kDTZ+J=<o75GN*YpITPkml>{M&#_{$c&qI_6&&`!c`tvEYj9#
zTkhN6jFbbg=4KRz-}524wyoN>ZQHgzsND|gc2wK8t+nmFHp1_0lC|#tKNIW894T3u
zpHY%&DWZW9ueEo0V88<bia4YQ4q_!pAT*o=Hi;ovB>83&Fk4Cph&Y#_0J9<lNupvI
zh&c#zEp`4bo1L~z!xp6ldA1e;ZeW)54gWlA$qT<t|7UI+WMtxV_T2RHeYfIO0p?_7
z#4ry#-XD|aKlAB-eM3&1Od*X~o-^VV&xYZarTH(Nu=g(2rC5VZEt*U6qUDU8TJHPu
z|A)RE2SQy*?w&1`*f@vm(6mEK<!O(76okx-Xcd4LFXVx}id^;nY5T8ZTHC4~Ja)Hh
z_S*_32*F6jvAb`+(-yUX5JoH3TErTJfMMg^yvNp?mlh%wcO9`)X?b1%Mj8|q<UmR&
zEtD1K3;+WKIo2s#HX#j)vonv`w!t&0Wj#Y9<Ck>9wi#qt2(X{Dn%i8MRg;&C^w_hZ
zTuH*HwDk>L_1e!1x(CVD(3-VW3=a@sQ($XBg!OoH;*5``%#~6B1O_t%u&{TibI56Z
z>j!3F^}@*lV(xSNzvs4hb`K-VW<z8I7z*SQhzXwYu^|p<tl7G(m{$D<BjWQJ3#PR9
z+_Lv}tFA<>c|76;|1D^L;G6M_dqyA?FP9N~fFNk$K-Yll=Gr#arXnXRTCg@BOq*Af
zZEsy(gpIRHec9GkMTbB4{jd)rxw7;rAZZr#cmKU~5z>H}KbbwBBq0_1G*|7?Tm~X2
zNm>es$m>O0gW%DBehZ92Dn6Y#@BbyOmKI2Z>`WV4!4gnPfFfA{_@w#U`7`eL<r~9{
zjWedL{i^T(gb*YMsaV=SG^VW&wiTJ6Wt=mj5M%)&@<0CnuXFo*?$~X=oyx1QHr<Z%
z=Cm32{57Uy#Dztm(PW`9vw8-u`+7{P<C0~;2r!fN<i&s{Xe0}fbbQLOGIy@1*{Q6$
zE-&8}s>}7KboKnv+B&zl4?+te*-%w>c3p1v%27{9<jH{m3&-c#J%jxt0Z5aPh!>{J
zr_TXwB70f;V`1n<6a&H8)w9!ow}Cs&Cl?VCGlUZ!@`SVW$7VNV;6TZ<b0#GLB-e%o
z!;4CSVDSM55{l#JvdDS#g4#lwfeX6yP?*iRGg=a&2*Co6LE>gVWfB}s$g9S(!DbIT
z!Sm!NjVUb|61b!?MA%Ii%?h8yB335g8q}&FMne+8N<V)JJH9^4G_yH7mq{48Hi2)z
z^cFW&>F%=R4lCN$o95F&00|(xo||f7Yx(2gKfD3V>Bo;HnFrAT0000<MNUMnLSTYC
Ch*Nt2
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..38158846dd32a73b8633b6523599f684e5c2f1c8
GIT binary patch
literal 3111
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
B>z4ok
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/content/third-party/cc-logo-diners.svg
@@ -0,0 +1,1 @@
+<svg width="36" height="30" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" fill="none"><path d="M19.863 20.068c4.698.022 8.987-3.839 8.987-8.536 0-5.137-4.289-8.688-8.987-8.686h-4.044c-4.755-.002-8.669 3.55-8.669 8.686 0 4.698 3.914 8.559 8.669 8.536h4.044z" fill="#4186CD"/><path d="M15.76 3.535a7.923 7.923 0 0 0 0 15.844 7.923 7.923 0 0 0 0-15.844zm-4.821 7.75c.004-2.122 1.288-3.931 3.1-4.65v9.3c-1.812-.719-3.096-2.527-3.1-4.65zm6.544 4.65v-9.3c1.811.717 3.097 2.527 3.1 4.65-.003 2.123-1.289 3.931-3.1 4.65z" fill="#FFF"/><g fill="#211E1F"><path d="M.65 22.925c0-.71-.375-.663-.733-.671v-.205c.31.015.63.015.94.015.336 0 .79-.015 1.381-.015 2.065 0 3.19 1.365 3.19 2.763 0 .782-.462 2.748-3.286 2.748-.407 0-.782-.016-1.157-.016-.358 0-.71.008-1.068.016v-.205c.478-.048.71-.064.733-.6v-3.83zm.644 3.636c0 .586.437.654.825.654 1.713 0 2.275-1.24 2.275-2.373 0-1.422-.951-2.449-2.48-2.449-.326 0-.476.022-.62.03v4.138zM5.428 27.364h.152c.225 0 .387 0 .387-.25v-2.041c0-.332-.121-.378-.419-.528v-.12c.378-.107.83-.249.861-.272a.301.301 0 0 1 .145-.038c.04 0 .057.046.057.106v2.893c0 .25.177.25.402.25h.137v.196c-.274 0-.556-.015-.845-.015-.29 0-.58.007-.877.015v-.196zm.689-4.627a.36.36 0 0 1-.345-.35c0-.177.169-.338.345-.338.182 0 .344.148.344.337 0 .19-.155.351-.344.351zM7.993 25.117c0-.278-.084-.353-.438-.496v-.143c.325-.106.634-.204.996-.363.022 0 .045.016.045.076v.49c.43-.309.8-.566 1.307-.566.64 0 .867.468.867 1.055v1.944c0 .25.166.25.377.25h.136v.196c-.265 0-.528-.015-.8-.015s-.544.007-.815.015v-.196h.136c.211 0 .362 0 .362-.25v-1.95c0-.43-.263-.642-.694-.642-.241 0-.626.196-.876.362v2.23c0 .25.166.25.378.25h.136v.196c-.264 0-.529-.015-.8-.015-.272 0-.544.007-.816.015v-.196h.137c.21 0 .362 0 .362-.25v-1.997zM11.943 25.569c-.017.072-.017.192 0 .465.049.762.553 1.388 1.212 1.388.453 0 .809-.24 1.113-.537l.115.113c-.38.489-.849.906-1.525.906-1.31 0-1.575-1.236-1.575-1.75 0-1.573 1.089-2.039 1.665-2.039.668 0 1.386.41 1.394 1.26 0 .05 0 .097-.008.145l-.074.049h-2.317zm1.514-.42c.212 0 .237-.077.237-.147 0-.3-.264-.542-.742-.542-.52 0-.877.264-.98.689h1.485zM14.383 27.364h.191c.198 0 .34 0 .34-.25v-2.117c0-.233-.262-.279-.368-.339v-.113c.516-.234.799-.43.863-.43.042 0 .063.023.063.099v.678h.015c.176-.294.474-.777.905-.777.176 0 .402.128.402.4 0 .203-.133.385-.331.385-.22 0-.22-.182-.468-.182-.12 0-.516.174-.516.626v1.77c0 .25.142.25.34.25h.395v.196c-.389-.008-.684-.015-.99-.015-.289 0-.586.007-.84.015v-.196zM17.282 26.668c.102.53.418.98.996.98.465 0 .64-.29.64-.57 0-.948-1.724-.643-1.724-1.935 0-.45.357-1.028 1.226-1.028.252 0 .592.073.9.234l.056.818h-.182c-.079-.505-.355-.795-.862-.795-.316 0-.616.185-.616.53 0 .94 1.834.65 1.834 1.91 0 .53-.42 1.092-1.36 1.092a2.06 2.06 0 0 1-.964-.272l-.087-.924.143-.04zM26.431 23.625h-.192c-.147-.94-.786-1.318-1.649-1.318-.886 0-2.173.618-2.173 2.548 0 1.626 1.11 2.792 2.296 2.792.763 0 1.395-.547 1.55-1.392l.176.048-.177 1.175c-.323.21-1.194.426-1.703.426-1.802 0-2.942-1.214-2.942-3.024 0-1.649 1.41-2.831 2.92-2.831.623 0 1.224.21 1.817.427l.077 1.15zM26.783 27.36h.153c.226 0 .387 0 .387-.253v-4.268c0-.498-.12-.514-.427-.598v-.123c.322-.099.66-.237.83-.33.087-.045.152-.084.176-.084.05 0 .065.046.065.108v5.295c0 .254.177.254.403.254h.136v.199c-.273 0-.555-.016-.845-.016-.29 0-.58.008-.878.016v-.2zM31.775 27.032c0 .136.084.143.214.143.092 0 .206-.007.305-.007v.159c-.328.03-.955.188-1.1.233l-.038-.023v-.61c-.458.37-.81.633-1.353.633-.412 0-.84-.264-.84-.897v-1.93c0-.196-.03-.384-.457-.422v-.143c.275-.007.885-.053.984-.053.085 0 .085.053.085.219v1.944c0 .226 0 .874.664.874.26 0 .604-.195.924-.458v-2.029c0-.15-.366-.233-.64-.309v-.135c.686-.046 1.115-.106 1.19-.106.062 0 .062.053.062.136v2.78zM33.372 24.72c.323-.27.76-.572 1.206-.572.94 0 1.505.804 1.505 1.671 0 1.042-.776 2.085-1.935 2.085-.599 0-.914-.191-1.125-.278l-.243.182-.169-.087a9.26 9.26 0 0 0 .113-1.416v-3.422c0-.518-.122-.534-.43-.621v-.128c.325-.103.664-.246.834-.342.09-.048.154-.088.18-.088.047 0 .064.048.064.112v2.905zm-.044 2.032c0 .301.291.808.834.808.868 0 1.232-.831 1.232-1.535 0-.854-.664-1.565-1.296-1.565-.3 0-.552.19-.77.372v1.92z"/></g></g></svg>
\ No newline at end of file
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..104f9ee2d6b72f02054d858a46647c3facbd8fee
GIT binary patch
literal 1117
zc$@)U1fu(iP)<h;3K|Lk000e1NJLTq001Qb0015c1^@s6t0UKK000CeNkl<ZcmZM<
z(qI(G$;tIcL_{0_;{DXZk&%)60s;aqd3$?50OI?^VlOYR$Li|pc?>{94*wrSpuN4l
zo&n^u!Q`m6wzfL3qXt<>)ebsG)z#It0hlYtI9cu>isoyaJbPxIZQHhO+vdzX+qP}n
zwr#I>yT^Uf6(?)&9kx&1y1)7-IH>O3yRS^xP%BriWZbxMtXQ!Ee}8}c{QOw9Y&nXe
zuypBC1`i(0l`B^eUAuOT{{8ziaNs~beEfvn&xc2=X7h0BK)xK?f!CL>%wMp8pr9Z`
z*RNk^^XAQL-@cu3<IGqr7H-_Q!IUXeuv)ELzI>T`_wS>sDm{AiSow`UQ%-7{Mvfdg
z=-Ra_jT<+nT)A>wym*mZxpJ{Uzj?N7*;u@IF?;v!<?PwBWXO;KDPZKtk(@q$jO)GY
z^RQ|n-ZV_kyJpGp-fSUnfr4z@xDip!nl+g;X%f|{Rijm_R>(bf@7_(CG-)uK&6rFk
zE?v5W%jFWM{(&l3upmM&DpRHm;zWrOC0M(5Ee3;u*|TR85Ew}9+O?THc`|?L&5@lv
zsF9Q}9gKYKYQ(2+I$YBWGjGx;YSyaF^XD(foH;XJzkWp@j<jvtmW2x!a_G<@^5)Hp
zxGzu-9y|aD4kagu>esJNr%s(HRHzVkyPfmr&r_>bEd;1^>Cy=3U;1=l1MiwA!>5lC
zzhOoKtp;rKvhn5FRr2L8h(6ir)~y?&DpjhW$Azg=r*io4VUi|IN}@!Gm^pK1@JtC*
z?%cUqzI-|J=FKB()~xK?w~st|^00dKYUIECN@cTV%|gzVHl8|lnmcEX;X9@vfumBO
zOihbAI|<H%tpN|>w!V^a<K|675(~+ZxN_{+F~o?mW5-IdlHFiX5-_n>dM?4_)2B~z
z0tXKsqGH90lq^|Na)6vGm5F20=G%Ag!~3h)w>81BsWh(Beb9p5Ba%3X<8qBiGAPfG
zPDnoc_U$V$v~S;@qeqW|K=tSq@sZ&1c!F)q!~P?eN>ruEJ)fPTOZB`7V@_#a&v5MO
zhs|oFQ-SZ|7fSPchx6zJ)G4`8MkmK+Nk^w5KgAD}=KbOim}&V?MkPlXmlnlr(4h=*
z45ev!-dx48XCUh2od1E5Pn<$&0iSSP-++BpV-$-?+7t{XPM|c^7x$yX5?<8l1$2-U
zC}UEhnA80Q5I0bo{|DUn_Tt#xA9YqSl(DHpawT@5G{pzctBW|#&c(j2CF;~XC}UFn
z36dt*10w?^Yu{<YlQRgso}xWEi2LMJoEuu<m{l0NMZeLQ<k-fh#AZ$>+tK<stvb|i
zGviyxASETW9N4{ih_mbC>2cr7O835}w$ObKW#;=HiVQ%S>Ar^=`+ZM!P9Wx@8un1*
jdL%2z(#<fG!6*O#TO%;#BC)nn00000NkvXXu0mjfVc|3}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1caaa01995985365773ab655ab8ed2fd09b7dce6
GIT binary patch
literal 2471
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
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/content/third-party/cc-logo-jcb.svg
@@ -0,0 +1,1 @@
+<svg width="30" height="30" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path d="M3.622.575C1.734.575.009 2.278.009 4.188c0 1.051 0 5.212-.002 9.348.346.217 2.01.752 2.68.799 1.466.103 2.375-.381 2.515-1.603l-.007-4.538h3.243v4.434c-.17 2.54-2.399 3.057-5.79 2.887-.877-.046-2.07-.27-2.64-.42L.004 22.94H5.65c1.54 0 3.544-1.439 3.544-3.627V.575H3.622z" id="a"/><linearGradient x1="-.003%" y1="49.999%" x2="100.002%" y2="49.999%" id="b"><stop stop-color="#313477" offset="0%"/><stop stop-color="#0077BC" offset="100%"/></linearGradient><path d="M0 1.564l.007.001V.007L0 .002v1.562z" id="d"/><linearGradient x1="0%" y1="50.019%" x2="1.21%" y2="50.019%" id="e"><stop stop-color="#313477" offset="0%"/><stop stop-color="#0077BC" offset="100%"/></linearGradient><path d="M3.976.575C2.088.575.363 2.278.363 4.188v4.945c1.132-.885 2.958-1.319 5.281-1.14 1.322.102 2.3.286 2.834.445v1.57c-.588-.294-1.748-.73-2.715-.8-2.191-.158-3.342.868-3.342 2.528 0 1.494.888 2.773 3.331 2.602.806-.056 2.148-.525 2.719-.797l.007 1.523c-.492.155-2.02.488-3.458.5-2.165.017-3.694-.443-4.659-1.189L.36 22.941h5.643c1.54 0 3.546-1.439 3.546-3.627V.575H3.976z" id="g"/><linearGradient x1=".004%" y1="49.999%" x2="99.996%" y2="49.999%" id="h"><stop stop-color="#753136" offset="0%"/><stop stop-color="#ED1746" offset="100%"/></linearGradient><path d="M.123.448L.119 2.424l2.21.007c.43 0 .97-.368.97-1.01a.97.97 0 0 0-.967-.973c-.308.003-.8.001-1.245 0L.375.446C.26.446.17.446.123.448" id="j"/><linearGradient x1="0%" y1="50.008%" x2="99.996%" y2="50.008%" id="k"><stop stop-color="#008049" offset="0%"/><stop stop-color="#62BA44" offset="100%"/></linearGradient><path d="M.115.473l-.008 1.8 2.089.014c.346-.007.834-.325.834-.882 0-.567-.426-.95-.88-.939-.296.008-.702.005-1.078.002L.52.465C.333.465.187.467.115.473" id="m"/><linearGradient x1=".022%" y1="49.994%" x2="100.012%" y2="49.994%" id="n"><stop stop-color="#008049" offset="0%"/><stop stop-color="#62BA44" offset="100%"/></linearGradient><path d="M3.694.575C1.806.575.08 2.278.08 4.188L.08 8.164h5.365c1.067 0 2.324.457 2.324 1.754 0 .696-.37 1.485-1.706 1.74v.03c.78 0 2.132.457 2.132 1.833 0 1.423-1.46 1.817-2.243 1.817l-5.873.006-.001 7.597H5.72c1.54 0 3.544-1.439 3.544-3.627V.575H3.694z" id="p"/><linearGradient x1="-.007%" y1="49.999%" x2="100.004%" y2="49.999%" id="q"><stop stop-color="#008049" offset="0%"/><stop stop-color="#62BA44" offset="100%"/></linearGradient></defs><g fill="none" fill-rule="evenodd"><g transform="translate(0 3.013)"><mask id="c" fill="#fff"><use xlink:href="#a"/></mask><path d="M3.622.575C1.734.575.009 2.278.009 4.188c0 1.051 0 5.212-.002 9.348.346.217 2.01.752 2.68.799 1.466.103 2.375-.381 2.515-1.603l-.007-4.538h3.243v4.434c-.17 2.54-2.399 3.057-5.79 2.887-.877-.046-2.07-.27-2.64-.42L.004 22.94H5.65c1.54 0 3.544-1.439 3.544-3.627V.575H3.622z" fill="url(#b)" mask="url(#c)"/></g><g transform="translate(0 16.543)"><mask id="f" fill="#fff"><use xlink:href="#d"/></mask><path d="M0 1.564l.007.001V.007L0 .002v1.562z" fill="url(#e)" mask="url(#f)"/></g><g transform="translate(10 3.013)"><mask id="i" fill="#fff"><use xlink:href="#g"/></mask><path d="M3.976.575C2.088.575.363 2.278.363 4.188v4.945c1.132-.885 2.958-1.319 5.281-1.14 1.322.102 2.3.286 2.834.445v1.57c-.588-.294-1.748-.73-2.715-.8-2.191-.158-3.342.868-3.342 2.528 0 1.494.888 2.773 3.331 2.602.806-.056 2.148-.525 2.719-.797l.007 1.523c-.492.155-2.02.488-3.458.5-2.165.017-3.694-.443-4.659-1.189L.36 22.941h5.643c1.54 0 3.546-1.439 3.546-3.627V.575H3.976z" fill="url(#h)" mask="url(#i)"/></g><g transform="translate(22.353 14.778)"><mask id="l" fill="#fff"><use xlink:href="#j"/></mask><path d="M.123.448L.119 2.424l2.21.007c.43 0 .97-.368.97-1.01a.97.97 0 0 0-.967-.973c-.308.003-.8.001-1.245 0L.375.446C.26.446.17.446.123.448" fill="url(#k)" mask="url(#l)"/></g><g transform="translate(22.353 11.837)"><mask id="o" fill="#fff"><use xlink:href="#m"/></mask><path d="M.115.473l-.008 1.8 2.089.014c.346-.007.834-.325.834-.882 0-.567-.426-.95-.88-.939-.296.008-.702.005-1.078.002L.52.465C.333.465.187.467.115.473" fill="url(#n)" mask="url(#o)"/></g><g transform="translate(20.588 3.013)"><mask id="r" fill="#fff"><use xlink:href="#p"/></mask><path d="M3.694.575C1.806.575.08 2.278.08 4.188L.08 8.164h5.365c1.067 0 2.324.457 2.324 1.754 0 .696-.37 1.485-1.706 1.74v.03c.78 0 2.132.457 2.132 1.833 0 1.423-1.46 1.817-2.243 1.817l-5.873.006-.001 7.597H5.72c1.54 0 3.544-1.439 3.544-3.627V.575H3.694z" fill="url(#q)" mask="url(#r)"/></g></g></svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/content/third-party/cc-logo-mastercard.svg
@@ -0,0 +1,1 @@
+<svg width="38" height="30" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" fill="none"><path d="M7.485 29.258v-1.896a1.125 1.125 0 0 0-1.188-1.2 1.17 1.17 0 0 0-1.061.537 1.109 1.109 0 0 0-.999-.537.998.998 0 0 0-.885.448v-.373h-.657v3.021h.664v-1.662a.708.708 0 0 1 .74-.802c.435 0 .656.284.656.796v1.68h.664v-1.674a.71.71 0 0 1 .74-.802c.448 0 .663.284.663.796v1.68l.663-.012zm9.817-3.02h-1.08v-.917h-.664v.916h-.6v.6h.613v1.391c0 .701.271 1.119 1.049 1.119.29 0 .575-.08.821-.234l-.19-.563a1.213 1.213 0 0 1-.58.17c-.317 0-.437-.201-.437-.505v-1.377h1.074l-.006-.6zm5.605-.076a.891.891 0 0 0-.796.442v-.367h-.65v3.021h.656v-1.693c0-.5.215-.778.632-.778.14-.002.28.024.411.076l.202-.632a1.406 1.406 0 0 0-.467-.082l.012.013zm-8.474.316a2.26 2.26 0 0 0-1.232-.316c-.765 0-1.264.366-1.264.966 0 .493.367.797 1.043.891l.316.045c.36.05.53.145.53.316 0 .234-.24.366-.688.366-.361.01-.715-.1-1.005-.316l-.316.512a2.18 2.18 0 0 0 1.308.392c.872 0 1.378-.41 1.378-.986 0-.575-.398-.809-1.056-.904l-.316-.044c-.284-.038-.511-.095-.511-.297 0-.202.214-.354.575-.354.333.004.659.093.947.26l.291-.531zm17.602-.316a.891.891 0 0 0-.796.442v-.367h-.65v3.021h.656v-1.693c0-.5.215-.778.632-.778.14-.002.28.024.411.076l.202-.632a1.406 1.406 0 0 0-.467-.082l.012.013zm-8.467 1.58a1.526 1.526 0 0 0 1.611 1.58 1.58 1.58 0 0 0 1.087-.36l-.316-.532a1.327 1.327 0 0 1-.79.272.97.97 0 0 1 0-1.934c.286.003.563.099.79.272l.316-.53a1.58 1.58 0 0 0-1.087-.361 1.526 1.526 0 0 0-1.611 1.58v.012zm6.155 0v-1.505h-.658v.367a1.147 1.147 0 0 0-.948-.442 1.58 1.58 0 0 0 0 3.16c.37.013.722-.152.948-.443v.366h.658v-1.504zm-2.446 0a.913.913 0 1 1 .916.966.907.907 0 0 1-.916-.967zm-7.93-1.58a1.58 1.58 0 1 0 .044 3.16c.454.023.901-.124 1.254-.411l-.316-.487c-.25.2-.559.311-.878.316a.837.837 0 0 1-.904-.74h2.243v-.252c0-.948-.587-1.58-1.434-1.58l-.01-.006zm0 .587a.749.749 0 0 1 .764.733h-1.58a.777.777 0 0 1 .803-.733h.012zm16.464.999v-2.724h-.632v1.58a1.147 1.147 0 0 0-.948-.442 1.58 1.58 0 0 0 0 3.16c.369.013.722-.152.948-.443v.366h.632v-1.497zm1.096 1.07a.316.316 0 0 1 .218.086.294.294 0 0 1-.098.487.297.297 0 0 1-.12.025.316.316 0 0 1-.284-.183.297.297 0 0 1 .066-.329.316.316 0 0 1 .228-.085h-.01zm0 .535a.224.224 0 0 0 .165-.07.234.234 0 0 0 0-.316.234.234 0 0 0-.165-.07.237.237 0 0 0-.167.07.234.234 0 0 0 0 .316.234.234 0 0 0 .076.05c.032.015.066.021.101.02h-.01zm.02-.376a.126.126 0 0 1 .082.025c.02.016.03.041.028.066a.076.076 0 0 1-.022.057.11.11 0 0 1-.066.029l.091.104h-.072l-.086-.104h-.028v.104h-.06v-.278l.132-.003zm-.07.054v.075h.07a.066.066 0 0 0 .037 0 .032.032 0 0 0 0-.028.032.032 0 0 0 0-.028.066.066 0 0 0-.038 0l-.07-.02zm-3.476-1.283a.913.913 0 1 1 .917.967.907.907 0 0 1-.917-.967zm-22.19 0v-1.51h-.657v.366a1.147 1.147 0 0 0-.948-.442 1.58 1.58 0 1 0 0 3.16c.369.013.722-.152.948-.443v.366h.657v-1.497zm-2.445 0a.913.913 0 1 1 .916.967.907.907 0 0 1-.922-.967h.006z" fill="#231F20"/><path fill="#FF5F00" d="M14.215 3.22h9.953v17.886h-9.953z"/><path d="M14.847 12.165a11.356 11.356 0 0 1 4.345-8.945 11.375 11.375 0 1 0 0 17.886 11.356 11.356 0 0 1-4.345-8.941z" fill="#EB001B"/><path d="M37.596 12.165a11.375 11.375 0 0 1-18.404 8.941 11.375 11.375 0 0 0 0-17.886 11.375 11.375 0 0 1 18.404 8.941v.004zM36.51 19.265v-.412h.148v-.085h-.376v.085h.161v.412h.066zm.73 0v-.497h-.115l-.132.355-.133-.355h-.101v.497h.082v-.373l.123.323h.086l.123-.323v.376l.066-.003z" fill="#F79E1B"/></g></svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/content/third-party/cc-logo-mir.svg
@@ -0,0 +1,1 @@
+<svg width="36" height="30" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient x1="100%" y1="312.751%" x2=".612%" y2="312.751%" id="a"><stop stop-color="#1E5CD8" offset="0%"/><stop stop-color="#02AFFF" offset="100%"/></linearGradient></defs><g fill-rule="nonzero" fill="none"><path d="M7.812 11.313l-1.326 4.593h-.227l-1.326-4.594A1.823 1.823 0 0 0 3.18 10H0v10h3.184v-5.91h.227L5.234 20H7.51l1.819-5.91h.226V20h3.185V10H9.56c-.81 0-1.522.535-1.75 1.313zM25.442 20h3.204v-2.957h3.223c1.686 0 3.122-.953 3.677-2.293H25.442V20zm-5.676-8.945l-2.241 4.855h-.227V10h-3.184v10h2.703c.712 0 1.357-.414 1.654-1.055l2.242-4.851h.227V20h3.184V10H21.42c-.712 0-1.358.414-1.655 1.055z" fill="#006848"/><path d="M32.186 0c.92 0 1.752.352 2.382.93a3.49 3.49 0 0 1 1.146 2.59c0 .21-.023.417-.058.62H29.74a4.478 4.478 0 0 1-4.272-3.124c-.007-.02-.011-.043-.02-.067-.015-.054-.027-.113-.042-.168A4.642 4.642 0 0 1 25.293 0h6.893z" fill="url(#a)" transform="translate(0 10)"/></g></svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/content/third-party/cc-logo-unionpay.svg
@@ -0,0 +1,1 @@
+<svg width="36" height="30" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="a" d="M0 .04h17.771v22.433H0z"/><path id="c" d="M.134.04h18.093v22.433H.134z"/><path id="e" d="M.202.04h17.77v22.433H.202z"/></defs><g fill="none" fill-rule="evenodd"><g transform="translate(0 3.179)"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><path d="M7.023.04h8.952C17.225.04 18 1.057 17.71 2.31l-4.168 17.893c-.294 1.25-1.545 2.269-2.795 2.269h-8.95c-1.248 0-2.027-1.02-1.736-2.269l4.17-17.893C4.52 1.058 5.771.04 7.022.04" fill="#E21837" mask="url(#b)"/></g><g transform="translate(8.073 3.179)"><mask id="d" fill="#fff"><use xlink:href="#c"/></mask><path d="M7.157.04h10.294c1.25 0 .686 1.018.392 2.271l-4.167 17.893c-.292 1.25-.201 2.269-1.453 2.269H1.93c-1.252 0-2.026-1.02-1.732-2.269L4.363 2.311C4.66 1.058 5.907.04 7.157.04" fill="#00457C" mask="url(#d)"/></g><g transform="translate(17.89 3.179)"><mask id="f" fill="#fff"><use xlink:href="#e"/></mask><path d="M7.224.04h8.952c1.251 0 2.028 1.018 1.734 2.271l-4.166 17.893c-.295 1.25-1.547 2.269-2.798 2.269H2c-1.252 0-2.028-1.02-1.735-2.269L4.432 2.311C4.723 1.058 5.972.04 7.224.04" fill="#007B84" mask="url(#f)"/></g><path d="M26.582 16.428L25.49 20.04h.295l-.228.746h-.292l-.069.23h-1.038l.07-.23H22.12l.21-.69h.215l1.106-3.667.22-.739h1.06l-.111.373s.282-.203.55-.272c.266-.07 1.801-.096 1.801-.096l-.227.734h-.362zm-1.866 0l-.28.923s.315-.142.484-.189c.174-.046.434-.061.434-.061l.203-.673h-.841zm-.42 1.38l-.29.96s.321-.163.492-.215c.174-.039.438-.072.438-.072l.205-.673h-.845zm-.675 2.24h.844l.242-.81h-.841l-.245.81z" fill="#FEFEFE"/><path d="M27.05 15.694h1.13l.012.42c-.008.072.054.106.186.106h.23l-.21.695h-.612c-.528.038-.73-.19-.715-.445l-.022-.776zM27.2 18.993H26.12l.185-.619h1.232l.175-.566h-1.216l.207-.698h3.384l-.21.698h-1.135l-.178.566h1.139l-.19.619h-1.229l-.219.26h.5l.121.78c.014.078.014.13.04.162.025.028.175.042.262.042h.152l-.231.759h-.385c-.058 0-.147-.005-.27-.01-.114-.01-.195-.077-.273-.116a.367.367 0 0 1-.202-.265l-.12-.778-.56.766c-.177.243-.417.428-.824.428h-.782l.205-.677h.3a.484.484 0 0 0 .218-.063.336.336 0 0 0 .166-.138l.816-1.15zM15.397 17.298h2.855l-.211.68H16.9l-.179.581h1.168l-.213.702h-1.167l-.284.945c-.034.104.278.117.39.117l.584-.08-.235.778H15.65c-.106 0-.185-.015-.299-.04a.312.312 0 0 1-.209-.153c-.048-.077-.122-.14-.071-.305l.378-1.25H14.8l.215-.714h.65l.173-.581h-.648l.207-.68zM17.317 16.074h1.171l-.212.712h-1.6l-.173.15c-.075.072-.1.042-.198.094-.09.045-.28.136-.525.136h-.513l.207-.684h.154c.13 0 .219-.012.264-.04a.617.617 0 0 0 .171-.222l.296-.535h1.163l-.205.389zM18.991 15.694h.997l-.146.502s.316-.252.536-.343c.22-.081.716-.154.716-.154l1.615-.01-.55 1.832a2.139 2.139 0 0 1-.269.608.7.7 0 0 1-.271.251 1.02 1.02 0 0 1-.375.126c-.106.008-.27.01-.496.014h-1.556l-.437 1.447c-.042.144-.061.213-.034.252a.18.18 0 0 0 .148.073l.686-.065-.235.794h-.766c-.245 0-.422-.006-.547-.015-.118-.01-.242 0-.325-.063-.07-.063-.18-.147-.177-.231.007-.078.04-.209.09-.389l1.396-4.63zm2.117 1.848h-1.634l-.1.33h1.414c.167-.02.202.004.216-.004l.104-.326zm-1.545-.297s.32-.292.867-.387c.124-.023.9-.015.9-.015l.119-.392h-1.647l-.24.794z" fill="#FEFEFE"/><path d="M21.899 18.648l-.093.44c-.04.137-.073.24-.177.328-.11.093-.237.19-.536.19l-.554.023-.005.497c-.005.14.032.126.054.149.026.025.049.035.073.045l.175-.01.529-.03-.22.726h-.606c-.425 0-.74-.01-.842-.091-.103-.065-.116-.146-.115-.286l.04-1.938h.968l-.014.397h.233c.08 0 .134-.008.167-.03a.175.175 0 0 0 .065-.1l.097-.31h.76zM8.082 8.932c-.033.158-.655 3.024-.656 3.026-.134.58-.231.993-.562 1.26a1 1 0 0 1-.66.23c-.409 0-.646-.203-.687-.587l-.007-.132.124-.781s.652-2.611.769-2.957l.01-.039c-1.27.011-1.495 0-1.51-.02-.009.028-.04.19-.04.19l-.666 2.943-.057.25-.11.816c0 .242.047.44.142.607.303.53 1.168.609 1.657.609.63 0 1.222-.134 1.622-.378.694-.41.875-1.051 1.037-1.62l.075-.293s.672-2.712.786-3.065c.004-.02.006-.03.012-.039-.92.01-1.192 0-1.28-.02M11.798 14.319c-.45-.008-.61-.008-1.135.02l-.02-.04c.045-.2.095-.398.14-.6l.065-.275c.097-.425.191-.92.202-1.072.01-.09.042-.317-.218-.317-.109 0-.223.053-.339.107-.063.226-.19.863-.252 1.153-.13.61-.138.681-.197.983l-.038.041a12.946 12.946 0 0 0-1.159.02l-.024-.046c.089-.362.178-.728.263-1.091.224-.986.278-1.362.338-1.863l.044-.03c.52-.073.647-.088 1.21-.202l.048.053-.087.313c.096-.057.187-.114.283-.163.266-.13.562-.17.724-.17.248 0 .518.069.63.355.107.254.036.567-.104 1.184l-.072.316c-.144.686-.168.812-.25 1.283l-.052.041zM13.627 14.319c-.272-.002-.448-.008-.617-.002-.17.002-.335.01-.588.022l-.013-.022-.016-.024c.069-.26.106-.35.14-.443a3.13 3.13 0 0 0 .128-.449c.08-.345.128-.586.16-.797.037-.204.057-.378.085-.58l.02-.015.02-.02c.27-.037.442-.062.618-.09.177-.023.355-.06.635-.113l.01.024.008.025c-.052.214-.105.427-.156.643-.05.217-.103.43-.15.643-.101.453-.142.623-.166.745-.024.115-.03.178-.069.412l-.025.021-.024.02zM17.67 12.768c.159-.692.036-1.015-.119-1.212-.234-.3-.648-.396-1.078-.396-.258 0-.873.025-1.354.468-.345.32-.505.754-.6 1.17-.098.423-.21 1.186.492 1.47.216.093.528.118.73.118.513 0 1.04-.141 1.436-.561.305-.341.445-.848.494-1.057m-1.18-.05c-.022.117-.124.551-.262.736-.097.136-.21.219-.337.219-.037 0-.26 0-.264-.332-.002-.163.031-.33.072-.512.119-.524.258-.964.616-.964.28 0 .3.328.175.853M28.677 14.365c-.544-.004-.7-.004-1.202.017l-.031-.04c.135-.517.272-1.032.393-1.554.158-.678.194-.966.245-1.363l.041-.033c.54-.077.69-.099 1.252-.203l.016.047c-.103.426-.203.85-.304 1.278-.206.893-.281 1.346-.36 1.813l-.05.038z" fill="#FEFEFE"/><path d="M28.935 12.83c.158-.688-.479-.062-.58-.289-.154-.354-.058-1.072-.683-1.312-.24-.095-.804.027-1.29.469-.34.315-.504.747-.597 1.161-.098.418-.21 1.18.488 1.452.222.095.422.123.624.113.702-.038 1.236-1.098 1.633-1.516.305-.333.358.124.405-.079m-1.074-.05c-.027.112-.13.549-.268.732-.092.13-.311.211-.437.211-.036 0-.257 0-.264-.325a2.225 2.225 0 0 1 .073-.512c.12-.515.258-.95.616-.95.28 0 .4.316.28.843M20.746 14.319a12.427 12.427 0 0 0-1.134.02l-.02-.04c.046-.2.097-.398.144-.6l.061-.275c.099-.425.194-.92.203-1.072.01-.09.042-.317-.216-.317-.113 0-.225.053-.341.107-.062.226-.192.863-.255 1.153-.126.61-.136.681-.193.983l-.04.041a12.904 12.904 0 0 0-1.156.02l-.024-.046c.088-.362.177-.728.262-1.091.224-.986.276-1.362.339-1.863l.04-.03c.52-.073.648-.088 1.212-.202l.043.053-.08.313a4.81 4.81 0 0 1 .281-.163c.264-.13.562-.17.724-.17.244 0 .516.069.632.355.105.254.033.567-.108 1.184l-.07.316c-.15.686-.17.812-.25 1.283l-.054.041zM25.133 10.61c-.079.359-.312.66-.61.806-.247.124-.549.134-.86.134h-.201l.015-.08.37-1.608.011-.082.005-.063.149.015.782.067c.302.117.426.418.34.81m-.487-1.68l-.375.003c-.974.012-1.364.008-1.524-.011l-.04.197-.348 1.618-.874 3.597c.85-.01 1.199-.01 1.345.006.034-.161.23-1.121.232-1.121 0 0 .168-.704.178-.73 0 0 .053-.073.106-.102h.078c.732 0 1.56 0 2.209-.477.441-.328.743-.81.877-1.398.035-.144.061-.315.061-.487 0-.225-.045-.447-.176-.62-.33-.464-.99-.472-1.75-.476M33.124 11.185l-.043-.05c-.556.113-.656.131-1.167.2l-.038.038-.005.024-.002-.009c-.38.877-.37.688-.679 1.378l-.003-.084-.077-1.497-.05-.05c-.581.113-.595.131-1.133.2l-.041.038c-.006.017-.006.037-.01.059l.004.007c.067.344.05.267.118.809.032.266.073.533.105.796.053.44.083.656.147 1.327-.363.6-.449.826-.798 1.352l.022.049c.524-.02.646-.02 1.035-.02l.084-.096c.294-.633 2.531-4.47 2.531-4.47M14.12 11.556c.298-.207.335-.493.085-.641-.254-.15-.7-.102-1 .105-.3.203-.333.49-.08.642.25.146.697.103.994-.106" fill="#FEFEFE"/><path d="M30.554 15.709l-.437.75c-.139.256-.395.447-.803.448l-.696-.012.203-.674h.137c.07 0 .121-.003.16-.023.036-.012.062-.04.09-.08l.258-.409h1.088z" fill="#FEFEFE"/></g></svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/content/third-party/cc-logo-visa.svg
@@ -0,0 +1,1 @@
+<svg width="44" height="30" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path d="M22.8 9.786c-.025-1.96 1.765-3.053 3.113-3.703 1.385-.667 1.85-1.095 1.845-1.691-.01-.913-1.105-1.316-2.13-1.332-1.787-.027-2.826.478-3.652.86L21.332.938c.83-.378 2.364-.708 3.956-.722 3.735 0 6.18 1.824 6.193 4.653.014 3.59-5.02 3.79-4.985 5.395.012.486.481 1.005 1.51 1.138.508.066 1.914.117 3.506-.609l.626 2.884a9.623 9.623 0 0 1-3.329.605c-3.516 0-5.99-1.85-6.01-4.497m15.347 4.248a1.621 1.621 0 0 1-1.514-.998L31.296.428h3.733l.743 2.032h4.561l.431-2.032h3.29l-2.87 13.606h-3.038m.522-3.675l1.077-5.11h-2.95l1.873 5.11m-20.394 3.675L15.33.428h3.557l2.942 13.606h-3.556m-8.965-9.26L7.81 12.648c-.176.879-.87 1.386-1.64 1.386H.116l-.084-.395c1.242-.267 2.654-.697 3.51-1.157.523-.282.672-.527.844-1.196L7.224.428h3.76l5.763 13.606H13.01L9.31 4.774z" id="a"/><linearGradient x1="16.148%" y1="34.401%" x2="85.832%" y2="66.349%" id="b"><stop stop-color="#222357" offset="0%"/><stop stop-color="#254AA5" offset="100%"/></linearGradient></defs><g transform="matrix(1 0 0 -1 0 22.674)" fill="none" fill-rule="evenodd"><mask id="c" fill="#fff"><use xlink:href="#a"/></mask><path fill="url(#b)" fill-rule="nonzero" mask="url(#c)" d="M-4.669 12.849l44.237 16.12L49.63 1.929 5.395-14.19"/></g></svg>
\ No newline at end of file
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -18,16 +18,18 @@
 <!ENTITY appmenu.tooltip                     "Open menu">
 <!ENTITY navbarOverflow.label                "More tools…">
 
 <!-- Tab context menu -->
 <!ENTITY  reloadTab.label                    "Reload Tab">
 <!ENTITY  reloadTab.accesskey                "R">
 <!ENTITY  reloadAllTabs.label                "Reload All Tabs">
 <!ENTITY  reloadAllTabs.accesskey            "A">
+<!ENTITY  selectAllTabs.label                "Select All Tabs">
+<!ENTITY  selectAllTabs.accesskey            "S">
 <!-- LOCALIZATION NOTE (duplicateTab.label): This is a command to duplicate
 a tab (i.e. it is a verb, not adjective). -->
 <!ENTITY  duplicateTab.label                 "Duplicate Tab">
 <!ENTITY  duplicateTab.accesskey             "D">
 <!-- LOCALIZATION NOTE (closeTabsToTheEnd.label): This should indicate the
 direction in which tabs are closed, i.e. locales that use RTL mode should say
 left instead of right. -->
 <!ENTITY  closeTabsToTheEnd.label            "Close Tabs to the Right">
@@ -106,16 +108,18 @@ when there are no windows but Firefox is
 <!ENTITY personalbarCmd.label "Bookmarks Toolbar">
 <!ENTITY personalbarCmd.accesskey "B">
 <!ENTITY bookmarksToolbarItem.label "Bookmarks Toolbar Items">
 
 <!ENTITY toolbarContextMenu.reloadAllTabs.label "Reload All Tabs">
 <!ENTITY toolbarContextMenu.reloadAllTabs.accesskey "A">
 <!ENTITY toolbarContextMenu.bookmarkAllTabs.label "Bookmark All Tabs…">
 <!ENTITY toolbarContextMenu.bookmarkAllTabs.accesskey "T">
+<!ENTITY toolbarContextMenu.selectAllTabs.label "Select All Tabs">
+<!ENTITY toolbarContextMenu.selectAllTabs.accesskey "S">
 <!ENTITY toolbarContextMenu.undoCloseTab.label "Undo Close Tab">
 <!ENTITY toolbarContextMenu.undoCloseTab.accesskey "U">
 
 <!ENTITY pageSourceCmd.label "Page Source">
 <!ENTITY pageSourceCmd.accesskey "o">
 <!ENTITY pageSourceCmd.commandkey "u">
 <!-- LOCALIZATION NOTE (pageSourceCmd.SafariCommandKey should match the
 Option+Command keyboard shortcut letter that Safari and Chrome use for "View
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -239,32 +239,34 @@ nsFrameLoader::LoadFrame(bool aOriginalS
 
   nsAutoString src;
   nsCOMPtr<nsIPrincipal> principal;
 
   bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
                   mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
   if (isSrcdoc) {
     src.AssignLiteral("about:srcdoc");
+    principal = mOwnerContent->NodePrincipal();
   }
   else {
     GetURL(src, getter_AddRefs(principal));
 
     src.Trim(" \t\n\r");
 
     if (src.IsEmpty()) {
       // If the frame is a XUL element and has the attribute 'nodefaultsrc=true'
       // then we will not use 'about:blank' as fallback but return early without
       // starting a load if no 'src' attribute is given (or it's empty).
       if (mOwnerContent->IsXULElement() &&
           mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::nodefaultsrc,
                                      nsGkAtoms::_true, eCaseMatters)) {
         return;
       }
       src.AssignLiteral("about:blank");
+      principal = mOwnerContent->NodePrincipal();
     }
   }
 
   nsIDocument* doc = mOwnerContent->OwnerDoc();
   if (doc->IsStaticDocument()) {
     return;
   }
 
@@ -304,28 +306,23 @@ nsFrameLoader::FireErrorEvent()
     new LoadBlockingAsyncEventDispatcher(mOwnerContent,
                                          NS_LITERAL_STRING("error"),
                                          CanBubble::eNo,
                                          ChromeOnlyDispatch::eNo);
   loadBlockingAsyncDispatcher->PostDOMEvent();
 }
 
 nsresult
-nsFrameLoader::LoadURI(nsIURI* aURI, bool aOriginalSrc)
-{
-  return LoadURI(aURI, nullptr, aOriginalSrc);
-}
-
-nsresult
 nsFrameLoader::LoadURI(nsIURI* aURI, nsIPrincipal* aTriggeringPrincipal,
                        bool aOriginalSrc)
 {
   if (!aURI)
     return NS_ERROR_INVALID_POINTER;
   NS_ENSURE_STATE(!mDestroyCalled && mOwnerContent);
+  MOZ_ASSERT(aTriggeringPrincipal, "Must have an explicit triggeringPrincipal to nsFrameLoader::LoadURI.");
 
   mLoadingOriginalSrc = aOriginalSrc;
 
   nsCOMPtr<nsIDocument> doc = mOwnerContent->OwnerDoc();
 
   nsresult rv;
   // If IsForJSPlugin() returns true then we want to allow the load. We're just
   // loading the source for the implementation of the JS plugin from a URI
@@ -2267,21 +2264,26 @@ nsFrameLoader::MaybeCreateDocShell()
 
 void
 nsFrameLoader::GetURL(nsString& aURI, nsIPrincipal** aTriggeringPrincipal)
 {
   aURI.Truncate();
 
   if (mOwnerContent->IsHTMLElement(nsGkAtoms::object)) {
     mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::data, aURI);
+    nsCOMPtr<nsIPrincipal> prin = mOwnerContent->NodePrincipal();
+    prin.forget(aTriggeringPrincipal);
   } else {
     mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, aURI);
     if (RefPtr<nsGenericHTMLFrameElement> frame = do_QueryObject(mOwnerContent)) {
       nsCOMPtr<nsIPrincipal> prin = frame->GetSrcTriggeringPrincipal();
       prin.forget(aTriggeringPrincipal);
+    } else {
+      nsCOMPtr<nsIPrincipal> prin = mOwnerContent->NodePrincipal();
+      prin.forget(aTriggeringPrincipal);
     }
   }
 }
 
 nsresult
 nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI)
 {
   nsresult rv;
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -125,18 +125,24 @@ public:
    * Start loading the frame. This method figures out what to load
    * from the owner content in the frame loader.
    */
   void LoadFrame(bool aOriginalSrc);
 
   /**
    * Loads the specified URI in this frame. Behaves identically to loadFrame,
    * except that this method allows specifying the URI to load.
+   *
+   * @param aURI The URI to load.
+   * @param aTriggeringPrincipal The triggering principal for the load. May be
+   *        null, in which case the node principal of the owner content will be
+   *        used.
    */
-  nsresult LoadURI(nsIURI* aURI, bool aOriginalSrc);
+  nsresult LoadURI(nsIURI* aURI, nsIPrincipal* aTriggeringPrincipal,
+                   bool aOriginalSrc);
 
   void AddProcessChangeBlockingPromise(mozilla::dom::Promise& aPromise, mozilla::ErrorResult& aRv);
 
   /**
    * Destroy the frame loader and everything inside it. This will
    * clear the weak owner content reference.
    */
   void Destroy();
@@ -440,27 +446,16 @@ private:
   // Swap ourselves with the frameloader aOther, and notify chrome code with
   // a BrowserChangedProcess event.
   bool SwapBrowsersAndNotify(nsFrameLoader* aOther);
 
   // Returns a promise which will be resolved once all of the blockers have
   // resolved which were added during the BrowserWillChangeProcess event.
   already_AddRefed<mozilla::dom::Promise> FireWillChangeProcessEvent();
 
-  /**
-   * Triggers a load of the given URI.
-   *
-   * @param aURI The URI to load.
-   * @param aTriggeringPrincipal The triggering principal for the load. May be
-   *        null, in which case the node principal of the owner content will be
-   *        used.
-   */
-  nsresult LoadURI(nsIURI* aURI, nsIPrincipal* aTriggeringPrincipal,
-                   bool aOriginalSrc);
-
   nsCOMPtr<nsIDocShell> mDocShell;
   nsCOMPtr<nsIURI> mURIToLoad;
   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
   mozilla::dom::Element* mOwnerContent; // WEAK
 
   // After the frameloader has been removed from the DOM but before all of the
   // messages from the frame have been received, we keep a strong reference to
   // our <browser> element.
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -2291,17 +2291,18 @@ nsObjectLoadingContent::LoadObject(bool 
         rv = NS_ERROR_FAILURE;
         break;
       }
 
       nsCString spec;
       handlerURI->GetSpec(spec);
       LOG(("OBJLC [%p]: Loading fake plugin handler (%s)", this, spec.get()));
 
-      rv = mFrameLoader->LoadURI(handlerURI, false);
+      rv = mFrameLoader->LoadURI(handlerURI,
+                                 thisContent->AsElement()->NodePrincipal(), false);
       if (NS_FAILED(rv)) {
         LOG(("OBJLC [%p]: LoadURI() failed for fake handler", this));
         mFrameLoader->Destroy();
         mFrameLoader = nullptr;
       }
     }
     break;
     case eType_Document:
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2990,19 +2990,19 @@ AddForDeferredFinalization(T* aObject)
 
 template<class T>
 static void
 RecordReplayRegisterDeferredFinalize(T* aObject)
 {
   DeferredFinalizer<T>::RecordReplayRegisterDeferredFinalize(aObject);
 }
 
-// This returns T's CC participant if it participates in CC or null if it
-// doesn't. This also returns null for classes that don't inherit from
-// nsISupports (QI should be used to get the participant for those).
+// This returns T's CC participant if it participates in CC and does not inherit
+// from nsISupports. Otherwise, it returns null. QI should be used to get the
+// participant if T inherits from nsISupports.
 template<class T, bool isISupports=IsBaseOf<nsISupports, T>::value>
 class GetCCParticipant
 {
   // Helper for GetCCParticipant for classes that participate in CC.
   template<class U>
   static constexpr nsCycleCollectionParticipant*
   GetHelper(int, typename U::NS_CYCLE_COLLECTION_INNERCLASS* dummy=nullptr)
   {
@@ -3016,17 +3016,17 @@ class GetCCParticipant
     return nullptr;
   }
 
 public:
   static constexpr nsCycleCollectionParticipant*
   Get()
   {
     // Passing int() here will try to call the GetHelper that takes an int as
-    // its firt argument. If T doesn't participate in CC then substitution for
+    // its first argument. If T doesn't participate in CC then substitution for
     // the second argument (with a default value) will fail and because of
     // SFINAE the next best match (the variant taking a double) will be called.
     return GetHelper<T>(int());
   }
 };
 
 template<class T>
 class GetCCParticipant<T, true>
--- a/mobile/android/geckoview/src/main/AndroidManifest.xml
+++ b/mobile/android/geckoview/src/main/AndroidManifest.xml
@@ -1,50 +1,41 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="org.mozilla.geckoview">
 
-    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
 
     <uses-feature
             android:name="android.hardware.location"
             android:required="false"/>
     <uses-feature
             android:name="android.hardware.location.gps"
             android:required="false"/>
-    <uses-feature android:name="android.hardware.touchscreen"/>
+    <uses-feature
+            android:name="android.hardware.touchscreen"
+            android:required="false"/>
     <uses-feature
             android:name="android.hardware.camera"
             android:required="false"/>
     <uses-feature
             android:name="android.hardware.camera.autofocus"
             android:required="false"/>
 
-    <!-- #ifdef MOZ_WEBRTC -->
-    <!--
-         TODO preprocess AndroidManifest.xml so that we can
-         conditionally include WebRTC permissions based on MOZ_WEBRTC.
-    -->
     <uses-feature
             android:name="android.hardware.audio.low_latency"
             android:required="false"/>
-    -->
     <uses-feature
             android:name="android.hardware.microphone"
             android:required="false"/>
-    -->
     <uses-feature
             android:name="android.hardware.camera.any"
             android:required="false"/>
-    -->
-    <!-- #endif -->
-
 
     <!-- GeckoView requires OpenGL ES 2.0 -->
     <uses-feature
             android:glEsVersion="0x00020000"
             android:required="true"/>
 
     <application>
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoNetworkManager.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoNetworkManager.java
@@ -385,16 +385,20 @@ public class GeckoNetworkManager extends
 
     private static int wifiDhcpGatewayAddress(final Context context) {
         if (context == null) {
             return 0;
         }
 
         try {
             WifiManager mgr = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+            if (mgr == null) {
+                return 0;
+            }
+
             DhcpInfo d = mgr.getDhcpInfo();
             if (d == null) {
                 return 0;
             }
 
             return d.gateway;
 
         } catch (Exception ex) {
@@ -411,16 +415,19 @@ public class GeckoNetworkManager extends
      */
     public void handleMessage(final String event, final GeckoBundle message,
                               final EventCallback callback) {
         final Context applicationContext = GeckoAppShell.getApplicationContext();
         switch (event) {
             case "Wifi:Enable":
                 final WifiManager mgr = (WifiManager)
                         applicationContext.getSystemService(Context.WIFI_SERVICE);
+                if (mgr == null) {
+                    return;
+                }
 
                 if (!mgr.isWifiEnabled()) {
                     mgr.setWifiEnabled(true);
                     break;
                 }
 
                 // If Wifi is enabled, maybe you need to select a network
                 Intent intent = new Intent(android.provider.Settings.ACTION_WIFI_SETTINGS);
--- a/netwerk/test/gtest/TestURIMutator.cpp
+++ b/netwerk/test/gtest/TestURIMutator.cpp
@@ -58,21 +58,20 @@ TEST(TestURIMutator, Mutator)
 
   nsCOMPtr<nsIURI> newURI;
   rv = functionSetRef(url2, getter_AddRefs(newURI));
   ASSERT_EQ(rv, NS_OK);
   ASSERT_EQ(newURI->GetSpec(out), NS_OK);
   ASSERT_TRUE(out == NS_LITERAL_CSTRING("https://mozilla.org/path?query#originalRef"));
 
   // This test verifies that we can pass nsIURI** to Finalize.
-  // We need to use the explicit template because it's actually passing getter_AddRefs
   nsCOMPtr<nsIURI> uri2;
   rv = NS_MutateURI(url2)
          .SetQuery(NS_LITERAL_CSTRING("newquery"))
-         .Finalize<nsIURI>(getter_AddRefs(uri2));
+         .Finalize(getter_AddRefs(uri2));
   ASSERT_EQ(rv, NS_OK);
   ASSERT_EQ(uri2->GetSpec(out), NS_OK);
   ASSERT_TRUE(out == NS_LITERAL_CSTRING("https://mozilla.org/path?newquery#newref"));
 
   // This test verifies that we can pass nsIURI** to Finalize.
   // No need to be explicit.
   auto functionSetQuery = [](nsIURI* aURI, nsIURL** aResult) -> nsresult
   {
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -772,37 +772,40 @@ nsNSSComponent::GetEnterpriseRoots(nsIX5
   }
   enterpriseRootsCertList.forget(enterpriseRoots);
   return NS_OK;
 }
 
 class LoadLoadableRootsTask final : public Runnable
 {
 public:
-  explicit LoadLoadableRootsTask(nsNSSComponent* nssComponent,
-                                 bool importEnterpriseRoots,
-                                 uint32_t familySafetyMode)
+  LoadLoadableRootsTask(nsNSSComponent* nssComponent,
+                        bool importEnterpriseRoots,
+                        uint32_t familySafetyMode,
+                        Vector<nsCString>&& possibleLoadableRootsLocations)
     : Runnable("LoadLoadableRootsTask")
     , mNSSComponent(nssComponent)
     , mImportEnterpriseRoots(importEnterpriseRoots)
     , mFamilySafetyMode(familySafetyMode)
+    , mPossibleLoadableRootsLocations(std::move(possibleLoadableRootsLocations))
   {
     MOZ_ASSERT(nssComponent);
   }
 
   ~LoadLoadableRootsTask() = default;
 
   nsresult Dispatch();
 
 private:
   NS_IMETHOD Run() override;
   nsresult LoadLoadableRoots();
   RefPtr<nsNSSComponent> mNSSComponent;
   bool mImportEnterpriseRoots;
   uint32_t mFamilySafetyMode;
+  Vector<nsCString> mPossibleLoadableRootsLocations;
   nsCOMPtr<nsIThread> mThread;
 };
 
 nsresult
 LoadLoadableRootsTask::Dispatch()
 {
   // Can't add 'this' as the event to run, since mThread may not be set yet
   nsresult rv = NS_NewNamedThread("LoadRoots", getter_AddRefs(mThread),
@@ -983,16 +986,18 @@ nsNSSComponent::CheckForSmartCardChanges
   return NS_OK;
 }
 
 // Returns by reference the path to the directory containing the file that has
 // been loaded as MOZ_DLL_PREFIX nss3 MOZ_DLL_SUFFIX.
 static nsresult
 GetNSS3Directory(nsCString& result)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   UniquePRString nss3Path(
     PR_GetLibraryFilePathname(MOZ_DLL_PREFIX "nss3" MOZ_DLL_SUFFIX,
                               reinterpret_cast<PRFuncPtr>(NSS_Initialize)));
   if (!nss3Path) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nss not loaded?"));
     return NS_ERROR_FAILURE;
   }
   nsCOMPtr<nsIFile> nss3File(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
@@ -1027,16 +1032,18 @@ GetNSS3Directory(nsCString& result)
 #endif
 }
 
 // Returns by reference the path to the desired directory, based on the current
 // settings in the directory service.
 static nsresult
 GetDirectoryPath(const char* directoryKey, nsCString& result)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsIProperties> directoryService(
     do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
   if (!directoryService) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("could not get directory service"));
     return NS_ERROR_FAILURE;
   }
   nsCOMPtr<nsIFile> directory;
   nsresult rv = directoryService->Get(directoryKey, NS_GET_IID(nsIFile),
@@ -1055,68 +1062,79 @@ GetDirectoryPath(const char* directoryKe
     return rv;
   }
   return directoryWin->GetNativeCanonicalPath(result);
 #else
   return directory->GetNativePath(result);
 #endif
 }
 
-
-nsresult
-LoadLoadableRootsTask::LoadLoadableRoots()
+// The loadable roots library is probably in the same directory we loaded the
+// NSS shared library from, but in some cases it may be elsewhere. This function
+// enumerates and returns the possible locations as nsCStrings.
+static nsresult
+ListPossibleLoadableRootsLocations(
+  Vector<nsCString>& possibleLoadableRootsLocations)
 {
-  // Find the best Roots module for our purposes.
-  // Prefer the application's installation directory,
-  // but also ensure the library is at least the version we expect.
-  Vector<nsCString> possibleCKBILocations;
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!NS_IsMainThread()) {
+    return NS_ERROR_NOT_SAME_THREAD;
+  }
+
   // First try in the directory where we've already loaded
   // MOZ_DLL_PREFIX nss3 MOZ_DLL_SUFFIX, since that's likely to be correct.
   nsAutoCString nss3Dir;
   nsresult rv = GetNSS3Directory(nss3Dir);
   if (NS_SUCCEEDED(rv)) {
-    if (!possibleCKBILocations.append(std::move(nss3Dir))) {
+    if (!possibleLoadableRootsLocations.append(std::move(nss3Dir))) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   } else {
     // For some reason this fails on android. In any case, we should try with
     // the other potential locations we have.
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
             ("could not determine where nss was loaded from"));
   }
   nsAutoCString currentProcessDir;
   rv = GetDirectoryPath(NS_XPCOM_CURRENT_PROCESS_DIR, currentProcessDir);
   if (NS_SUCCEEDED(rv)) {
-    if (!possibleCKBILocations.append(std::move(currentProcessDir))) {
+    if (!possibleLoadableRootsLocations.append(std::move(currentProcessDir))) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   } else {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
             ("could not get current process directory"));
   }
   nsAutoCString greDir;
   rv = GetDirectoryPath(NS_GRE_DIR, greDir);
   if (NS_SUCCEEDED(rv)) {
-    if (!possibleCKBILocations.append(std::move(greDir))) {
+    if (!possibleLoadableRootsLocations.append(std::move(greDir))) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   } else {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("could not get gre directory"));
   }
   // As a last resort, this will cause the library loading code to use the OS'
   // default library search path.
   nsAutoCString emptyString;
-  if (!possibleCKBILocations.append(std::move(emptyString))) {
+  if (!possibleLoadableRootsLocations.append(std::move(emptyString))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  for (const auto& possibleCKBILocation : possibleCKBILocations) {
-    if (mozilla::psm::LoadLoadableRoots(possibleCKBILocation)) {
-      MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("loaded CKBI from %s",
-                                            possibleCKBILocation.get()));
+  return NS_OK;
+}
+
+nsresult
+LoadLoadableRootsTask::LoadLoadableRoots()
+{
+  for (const auto& possibleLocation : mPossibleLoadableRootsLocations) {
+    if (mozilla::psm::LoadLoadableRoots(possibleLocation)) {
+      MOZ_LOG(gPIPNSSLog,
+              LogLevel::Debug,
+              ("loaded CKBI from %s", possibleLocation.get()));
       return NS_OK;
     }
   }
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("could not load loadable roots"));
   return NS_ERROR_FAILURE;
 }
 
 // Table of pref names and SSL cipher ID
@@ -1482,16 +1500,18 @@ nsNSSComponent::setEnabledTLSVersions()
 
 #if defined(XP_WIN) || (defined(XP_LINUX) && !defined(ANDROID))
 // If the profile directory is on a networked drive, we want to set the
 // environment variable NSS_SDB_USE_CACHE to yes (as long as it hasn't been set
 // before).
 static void
 SetNSSDatabaseCacheModeAsAppropriate()
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsIFile> profileFile;
   nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
                                        getter_AddRefs(profileFile));
   if (NS_FAILED(rv)) {
     // We're probably running without a profile directory, so this is
     // irrelevant.
     return;
   }
@@ -1959,18 +1979,26 @@ nsNSSComponent::InitializeNSS()
     // Set dynamic options from prefs. This has to run after
     // SSL_ConfigServerSessionIDCache.
     setValidationOptions(true, lock);
 
     bool importEnterpriseRoots = Preferences::GetBool(kEnterpriseRootModePref,
                                                       false);
     uint32_t familySafetyMode = Preferences::GetUint(kFamilySafetyModePref,
                                                      kFamilySafetyModeDefault);
+    Vector<nsCString> possibleLoadableRootsLocations;
+    rv = ListPossibleLoadableRootsLocations(possibleLoadableRootsLocations);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
     RefPtr<LoadLoadableRootsTask> loadLoadableRootsTask(
-      new LoadLoadableRootsTask(this, importEnterpriseRoots, familySafetyMode));
+      new LoadLoadableRootsTask(this,
+                                importEnterpriseRoots,
+                                familySafetyMode,
+                                std::move(possibleLoadableRootsLocations)));
     rv = loadLoadableRootsTask->Dispatch();
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     mLoadLoadableRootsTaskDispatched = true;
     return NS_OK;
   }
--- a/taskcluster/ci/toolchain/linux.yml
+++ b/taskcluster/ci/toolchain/linux.yml
@@ -544,17 +544,17 @@ linux64-rust-nightly:
     worker:
         max-run-time: 7200
         env:
             UPLOAD_DIR: artifacts
     run:
         using: toolchain-script
         script: repack_rust.py
         arguments: [
-            '--channel', 'nightly-2018-08-18',
+            '--channel', 'nightly-2018-09-16',
             '--host', 'x86_64-unknown-linux-gnu',
             '--target', 'x86_64-unknown-linux-gnu',
             '--target', 'i686-unknown-linux-gnu',
         ]
         toolchain-artifact: public/build/rustc.tar.xz
 
 linux64-rust-macos-1.29:
     description: "rust repack with macos-cross support"
--- a/taskcluster/ci/toolchain/windows.yml
+++ b/taskcluster/ci/toolchain/windows.yml
@@ -179,17 +179,17 @@ win64-rust-nightly:
         docker-image: {in-tree: toolchain-build}
         max-run-time: 7200
         env:
             UPLOAD_DIR: artifacts
     run:
         using: toolchain-script
         script: repack_rust.py
         arguments: [
-            '--channel', 'nightly-2018-06-23',
+            '--channel', 'nightly-2018-09-16',
             '--host', 'x86_64-pc-windows-msvc',
             '--target', 'x86_64-pc-windows-msvc',
             '--target', 'i686-pc-windows-msvc',
         ]
         toolchain-artifact: public/build/rustc.tar.bz2
 
 win64-node:
     description: "Node repack toolchain build"
--- a/testing/mozbase/mozdebug/mozdebug/mozdebug.py
+++ b/testing/mozbase/mozdebug/mozdebug/mozdebug.py
@@ -12,17 +12,18 @@ import mozinfo
 import sys
 from collections import namedtuple
 from distutils.spawn import find_executable
 from subprocess import check_output
 
 __all__ = ['get_debugger_info',
            'get_default_debugger_name',
            'DebuggerSearch',
-           'get_default_valgrind_args']
+           'get_default_valgrind_args',
+           'DebuggerInfo']
 
 '''
 Map of debugging programs to information about them, like default arguments
 and whether or not they are interactive.
 
 To add support for a new debugger, simply add the relative entry in
 _DEBUGGER_INFO and optionally update the _DEBUGGER_PRIORITIES.
 '''
@@ -68,16 +69,22 @@ To add support for a new debugger, simpl
     'win': ['devenv.exe', 'wdexpress.exe'],
     'linux': ['gdb', 'cgdb', 'lldb'],
     'mac': ['lldb', 'gdb'],
     'android': ['gdb'],
     'unknown': ['gdb']
 }
 
 
+DebuggerInfo = namedtuple(
+    'DebuggerInfo',
+    ['path', 'interactive', 'args', 'requiresEscapedArgs']
+)
+
+
 def _windbg_installation_paths():
     programFilesSuffixes = ['', ' (x86)']
     programFiles = "C:/Program Files"
     # Try the most recent versions first.
     windowsKitsVersions = ['10', '8.1', '8']
 
     for suffix in programFilesSuffixes:
         windowsKitsPrefix = os.path.join(programFiles + suffix,
@@ -171,21 +178,16 @@ def get_debugger_info(debugger, debugger
     debuggerName = os.path.basename(debuggerPath).lower()
 
     def get_debugger_info(type, default):
         if debuggerName in _DEBUGGER_INFO and type in _DEBUGGER_INFO[debuggerName]:
             return _DEBUGGER_INFO[debuggerName][type]
         return default
 
     # Define a namedtuple to access the debugger information from the outside world.
-    DebuggerInfo = namedtuple(
-        'DebuggerInfo',
-        ['path', 'interactive', 'args', 'requiresEscapedArgs']
-    )
-
     debugger_arguments = []
 
     if debuggerArgs:
         # Append the provided debugger arguments at the end of the arguments list.
         debugger_arguments += debuggerArgs.split()
 
     debugger_arguments += get_debugger_info('args', [])
 
--- a/testing/mozbase/moztest/moztest/resolve.py
+++ b/testing/mozbase/moztest/moztest/resolve.py
@@ -23,18 +23,18 @@ MOCHITEST_CHUNK_BY_DIR = 4
 MOCHITEST_TOTAL_CHUNKS = 5
 
 
 def WebglSuite(name):
     return {
         'aliases': (name,),
         'mach_command': 'mochitest',
         'kwargs': {'flavor': 'plain', 'subsuite': name, 'test_paths': None},
-        'task_regex': ['mochitest-' + name + '(?:-e10s)?(?:-1)?$',
-                       'test-verify(?:-gpu)?(?:-e10s)?(?:-1)?$'],
+        'task_regex': ['mochitest-' + name + '($|.*(-1|[^0-9])$)',
+                       'test-verify($|.*(-1|[^0-9])$)'],
     }
 
 
 TEST_SUITES = {
     'cppunittest': {
         'aliases': ('cpp',),
         'mach_command': 'cppunittest',
         'kwargs': {'test_file': None},
@@ -69,133 +69,134 @@ TEST_SUITES = {
         'aliases': ('mn',),
         'mach_command': 'marionette-test',
         'kwargs': {'tests': None},
     },
     'mochitest-a11y': {
         'aliases': ('a11y', 'ally'),
         'mach_command': 'mochitest',
         'kwargs': {'flavor': 'a11y', 'test_paths': None},
-        'task_regex': ['mochitest-a11y(?:-1)?$',
-                       'test-verify(?:-e10s)?(?:-1)?$'],
+        'task_regex': ['mochitest-a11y($|.*(-1|[^0-9])$)',
+                       'test-verify($|.*(-1|[^0-9])$)'],
     },
     'mochitest-browser': {
         'aliases': ('bc', 'browser-chrome'),
         'mach_command': 'mochitest',
         'kwargs': {'flavor': 'browser-chrome', 'test_paths': None},
-        'task_regex': ['mochitest-browser-chrome(?:-e10s)?(?:-1)?$',
-                       'test-verify(?:-e10s)?(?:-1)?$'],
+        'task_regex': ['mochitest-browser-chrome($|.*(-1|[^0-9])$)',
+                       'test-verify($|.*(-1|[^0-9])$)'],
     },
     'mochitest-chrome': {
         'aliases': ('mc',),
         'mach_command': 'mochitest',
         'kwargs': {'flavor': 'chrome', 'test_paths': None},
-        'task_regex': ['mochitest-chrome(?:-e10s)?(?:-1)?$',
-                       'test-verify(?:-e10s)?(?:-1)?$'],
+        'task_regex': ['mochitest-chrome($|.*(-1|[^0-9])$)',
+                       'test-verify($|.*(-1|[^0-9])$)'],
     },
     'mochitest-clipboard': {
         'aliases': ('cl', 'clipboard',),
         'mach_command': 'mochitest',
         'kwargs': {'subsuite': 'clipboard', 'test_paths': None},
-        'task_regex': ['mochitest-clipboard(?:-e10s)?(?:-1)?$',
-                       'test-verify(?:-e10s)?(?:-1)?$'],
+        'task_regex': ['mochitest-clipboard($|.*(-1|[^0-9])$)',
+                       'test-verify($|.*(-1|[^0-9])$)'],
     },
     'mochitest-devtools': {
         'aliases': ('dt', 'devtools-chrome'),
         'mach_command': 'mochitest',
         'kwargs': {'flavor': 'browser-chrome', 'subsuite': 'devtools', 'test_paths': None},
-        'task_regex': ['mochitest-devtools-chrome(?:-e10s)?(?:-1)?$',
-                       'test-verify(?:-e10s)?(?:-1)?$'],
+        'task_regex': ['mochitest-devtools-chrome($|.*(-1|[^0-9])$)',
+                       'test-verify($|.*(-1|[^0-9])$)'],
     },
     'mochitest-gpu': {
         'aliases': ('gpu',),
         'mach_command': 'mochitest',
         'kwargs': {'subsuite': 'gpu', 'test_paths': None},
-        'task_regex': ['mochitest-gpu(?:-e10s)?(?:-1)?$',
-                       'test-verify(?:-gpu)?(?:-e10s)?(?:-1)?$'],
+        'task_regex': ['mochitest-gpu($|.*(-1|[^0-9])$)',
+                       'test-verify($|.*(-1|[^0-9])$)'],
     },
     'mochitest-media': {
         'aliases': ('mpm', 'plain-media'),
         'mach_command': 'mochitest',
         'kwargs': {'flavor': 'plain', 'subsuite': 'media', 'test_paths': None},
-        'task_regex': ['mochitest-media(?:-e10s)?(?:-1)?$',
-                       'test-verify(?:-e10s)?(?:-1)?$'],
+        'task_regex': ['mochitest-media($|.*(-1|[^0-9])$)',
+                       'test-verify($|.*(-1|[^0-9])$)'],
     },
     'mochitest-plain': {
         'aliases': ('mp', 'plain',),
         'mach_command': 'mochitest',
         'kwargs': {'flavor': 'plain', 'test_paths': None},
-        'task_regex': ['mochitest(?:-e10s)?(?:-1)?$',
-                       'test-verify(?:-e10s)?(?:-1)?$'],
+        'task_regex': ['mochitest(?!-a11y|-browser|-chrome|-clip|-devtools|-gpu|-media|-screen)($|.*(-1|[^0-9])$)',  # noqa
+                       'test-verify($|.*(-1|[^0-9])$)'],
     },
     'mochitest-screenshots': {
         'aliases': ('ss', 'screenshots-chrome'),
         'mach_command': 'mochitest',
         'kwargs': {'flavor': 'browser-chrome', 'subsuite': 'screenshots', 'test_paths': None},
-        'task_regex': ['browser-screenshots(?:-e10s)?(?:-1)?$'],
+        'task_regex': ['browser-screenshots($|.*(-1|[^0-9])$)'],
     },
     'mochitest-webgl1-core': WebglSuite('webgl1-core'),
     'mochitest-webgl1-ext': WebglSuite('webgl1-ext'),
     'mochitest-webgl2-core': WebglSuite('webgl2-core'),
     'mochitest-webgl2-ext': WebglSuite('webgl2-ext'),
     'mochitest-webgl2-deqp': WebglSuite('webgl2-deqp'),
     'python': {
         'mach_command': 'python-test',
         'kwargs': {'tests': None},
     },
     'reftest': {
         'aliases': ('rr',),
         'mach_command': 'reftest',
         'kwargs': {'tests': None},
-        'task_regex': ['(opt|debug)-reftest(?:-no-accel|-gpu|-stylo)?(?:-e10s)?(?:-1)?$',
-                       'test-verify-gpu(?:-e10s)?(?:-1)?$'],
+        'task_regex': ['(opt|debug)-reftest($|.*(-1|[^0-9])$)',
+                       'test-verify-gpu($|.*(-1|[^0-9])$)'],
     },
     'robocop': {
         'mach_command': 'robocop',
         'kwargs': {'test_paths': None},
-        'task_regex': ['robocop(?:-e10s)?(?:-1)?$'],
+        'task_regex': ['robocop($|.*(-1|[^0-9])$)'],
     },
     'web-platform-tests': {
         'aliases': ('wpt',),
         'mach_command': 'web-platform-tests',
         'kwargs': {'include': []},
-        'task_regex': ['web-platform-tests(?:-reftests|-wdspec)?(?:-e10s)?(?:-1)?$',
+        'task_regex': ['web-platform-tests($|.*(-1|[^0-9])$)',
                        'test-verify-wpt-e10s'],
     },
     'web-platform-tests-testharness': {
         'aliases': ('wpt',),
         'mach_command': 'web-platform-tests',
         'kwargs': {'include': []},
-        'task_regex': ['web-platform-tests(?:-e10s)?(?:-1)?$',
+        'task_regex': ['web-platform-tests(?!-reftest|-wdspec)($|.*(-1|[^0-9])$)',
                        'test-verify-wpt-e10s'],
     },
     'web-platform-tests-reftest': {
         'aliases': ('wpt',),
         'mach_command': 'web-platform-tests',
         'kwargs': {'include': []},
-        'task_regex': ['web-platform-tests-reftests(?:-e10s)?(?:-1)?$',
+        'task_regex': ['web-platform-tests-reftests($|.*(-1|[^0-9])$)',
                        'test-verify-wpt-e10s'],
     },
     'web-platform-tests-wdspec': {
         'aliases': ('wpt',),
         'mach_command': 'web-platform-tests',
         'kwargs': {'include': []},
-        'task_regex': ['web-platform-tests-wdspec(?:-e10s)?(?:-1)?$',
+        'task_regex': ['web-platform-tests-wdspec($|.*(-1|[^0-9])$)',
                        'test-verify-wpt-e10s'],
     },
     'valgrind': {
         'aliases': ('v',),
         'mach_command': 'valgrind-test',
         'kwargs': {},
     },
     'xpcshell': {
         'aliases': ('x',),
         'mach_command': 'xpcshell-test',
         'kwargs': {'test_file': 'all'},
-        'task_regex': ['xpcshell(?:-1)?$', 'test-verify(?:-1)?$'],
+        'task_regex': ['xpcshell($|.*(-1|[^0-9])$)',
+                       'test-verify($|.*(-1|[^0-9])$)'],
     },
 }
 
 for i in range(1, MOCHITEST_TOTAL_CHUNKS + 1):
     TEST_SUITES['mochitest-%d' % i] = {
         'aliases': ('m%d' % i,),
         'mach_command': 'mochitest',
         'kwargs': {
--- a/testing/mozbase/moztest/tests/test_resolve.py
+++ b/testing/mozbase/moztest/tests/test_resolve.py
@@ -1,29 +1,31 @@
 # 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/.
 # flake8: noqa: E501
 
-from __future__ import absolute_import, unicode_literals
+from __future__ import absolute_import, print_function, unicode_literals
 
 import cPickle as pickle
 import os
+import re
 import shutil
 import tempfile
 import unittest
 
 import mozpack.path as mozpath
 import mozunit
 from mozbuild.base import MozbuildObject
 from mozfile import NamedTemporaryFile
 
 from moztest.resolve import (
     TestMetadata,
     TestResolver,
+    TEST_SUITES,
 )
 
 
 ALL_TESTS = {
     "accessible/tests/mochitest/actions/test_anchors.html": [
         {
             "dir_relpath": "accessible/tests/mochitest/actions",
             "expected": "pass",
@@ -151,16 +153,56 @@ ALL_TESTS = {
         }
    ]
 }
 
 TEST_DEFAULTS = {
     "/firefox/toolkit/mozapps/update/test/unit/xpcshell_updater.ini": {"support-files": "\ndata/**\nxpcshell_updater.ini"}
 }
 
+TASK_LABELS = [
+    'test-linux64/opt-browser-screenshots-1',
+    'test-linux64/opt-browser-screenshots-e10s-1',
+    'test-linux64/opt-marionette',
+    'test-linux64/opt-mochitest',
+    'test-linux64/debug-mochitest-e10s',
+    'test-linux64/opt-mochitest-a11y',
+    'test-linux64/opt-mochitest-browser',
+    'test-linux64/opt-mochitest-browser-chrome',
+    'test-linux64/opt-mochitest-browser-chrome-e10s',
+    'test-linux64/opt-mochitest-browser-chrome-e10s-11',
+    'test-linux64/opt-mochitest-chrome',
+    'test-linux64/opt-mochitest-clipboard',
+    'test-linux64/opt-mochitest-devtools',
+    'test-linux64/opt-mochitest-devtools-chrome',
+    'test-linux64/opt-mochitest-gpu',
+    'test-linux64/opt-mochitest-gpu-e10s',
+    'test-linux64/opt-mochitest-media-e10s-1',
+    'test-linux64/opt-mochitest-media-e10s-11',
+    'test-linux64/opt-mochitest-plain',
+    'test-linux64/opt-mochitest-screenshots-1',
+    'test-linux64/opt-reftest',
+    'test-linux64/debug-reftest-e10s-1',
+    'test-linux64/debug-reftest-e10s-11',
+    'test-linux64/opt-robocop',
+    'test-linux64/opt-robocop-1',
+    'test-linux64/opt-robocop-e10s',
+    'test-linux64/opt-robocop-e10s-1',
+    'test-linux64/opt-robocop-e10s-11',
+    'test-linux64/opt-web-platform-tests-e10s-1',
+    'test-linux64/opt-web-platform-tests-reftests-e10s-1',
+    'test-linux64/opt-web-platform-tests-reftest-e10s-1',
+    'test-linux64/opt-web-platform-tests-wdspec-e10s-1',
+    'test-linux64/opt-web-platform-tests-1',
+    'test-linux64/opt-web-platform-test-e10s-1',
+    'test-linux64/opt-xpcshell',
+    'test-linux64/opt-xpcshell-1',
+    'test-linux64/opt-xpcshell-2',
+]
+
 
 class Base(unittest.TestCase):
     def setUp(self):
         self._temp_files = []
 
     def tearDown(self):
         for f in self._temp_files:
             del f
@@ -336,11 +378,85 @@ class TestTestResolver(Base):
         suites, tests = r.resolve_metadata(['mochitest-a11y', 'browser', 'xpcshell'])
         assert suites == {'mochitest-a11y', 'xpcshell'}
         assert sorted(t['file_relpath'] for t in tests) == [
             'devtools/client/markupview/test/browser_markupview_copy_image_data.js',
             'image/test/browser/browser_bug666317.js',
             'mobile/android/tests/browser/junit3/src/TestDistribution.java',
         ]
 
+    def test_task_regexes(self):
+        """Test the task_regexes defined in TEST_SUITES."""
+
+        test_cases = {
+            'mochitest-browser': [
+                'test-linux64/opt-mochitest-browser-chrome',
+                'test-linux64/opt-mochitest-browser-chrome-e10s',
+            ],
+            'mochitest-chrome': [
+                'test-linux64/opt-mochitest-chrome',
+            ],
+            'mochitest-devtools': [
+                'test-linux64/opt-mochitest-devtools-chrome',
+            ],
+            'mochitest-gpu': [
+                'test-linux64/opt-mochitest-gpu',
+                'test-linux64/opt-mochitest-gpu-e10s',
+            ],
+            'mochitest-media': [
+                'test-linux64/opt-mochitest-media-e10s-1',
+            ],
+            'mochitest-plain': [
+                'test-linux64/opt-mochitest',
+                'test-linux64/debug-mochitest-e10s',
+                # this isn't a real task but the regex would match it if it were
+                'test-linux64/opt-mochitest-plain',
+            ],
+            'mochitest-screenshots': [
+                'test-linux64/opt-browser-screenshots-1',
+                'test-linux64/opt-browser-screenshots-e10s-1',
+            ],
+            'reftest': [
+                'test-linux64/opt-reftest',
+                'test-linux64/debug-reftest-e10s-1',
+            ],
+            'robocop': [
+                'test-linux64/opt-robocop',
+                'test-linux64/opt-robocop-1',
+                'test-linux64/opt-robocop-e10s',
+                'test-linux64/opt-robocop-e10s-1',
+            ],
+            'web-platform-tests': [
+                'test-linux64/opt-web-platform-tests-e10s-1',
+                'test-linux64/opt-web-platform-tests-reftests-e10s-1',
+                'test-linux64/opt-web-platform-tests-reftest-e10s-1',
+                'test-linux64/opt-web-platform-tests-wdspec-e10s-1',
+                'test-linux64/opt-web-platform-tests-1',
+            ],
+            'web-platform-tests-testharness': [
+                'test-linux64/opt-web-platform-tests-e10s-1',
+                'test-linux64/opt-web-platform-tests-1',
+            ],
+            'web-platform-tests-reftest': [
+                'test-linux64/opt-web-platform-tests-reftests-e10s-1',
+            ],
+            'web-platform-tests-wdspec': [
+                'test-linux64/opt-web-platform-tests-wdspec-e10s-1',
+            ],
+            'xpcshell': [
+                'test-linux64/opt-xpcshell',
+                'test-linux64/opt-xpcshell-1',
+            ],
+        }
+
+        regexes = []
+
+        def match_task(task):
+            return any(re.search(pattern, task) for pattern in regexes)
+
+        for suite, expected in sorted(test_cases.items()):
+            print(suite)
+            regexes = TEST_SUITES[suite]['task_regex']
+            assert set(filter(match_task, TASK_LABELS)) == set(expected)
+
 
 if __name__ == '__main__':
     mozunit.main()
--- a/toolkit/components/places/SQLFunctions.h
+++ b/toolkit/components/places/SQLFunctions.h
@@ -45,17 +45,17 @@ namespace places {
  * @param aVisitCount
  *        The number of visits aURL has.
  * @param aTyped
  *        Indicates if aURL is a typed URL or not.  Treated as a boolean.
  * @param aBookmark
  *        Indicates if aURL is a bookmark or not.  Treated as a boolean.
  * @param aOpenPageCount
  *        The number of times aURL has been registered as being open.  (See
- *        mozIPlacesAutoComplete::registerOpenPage.)
+ *        UrlbarProviderOpenTabs::registerOpenTab.)
  * @param aMatchBehavior
  *        The match behavior to use for this search.
  * @param aSearchBehavior
  *        A bitfield dictating the search behavior.
  */
 class MatchAutoCompleteFunction final : public mozIStorageFunction
 {
 public:
--- a/toolkit/components/places/UnifiedComplete.js
+++ b/toolkit/components/places/UnifiedComplete.js
@@ -340,16 +340,18 @@ XPCOMUtils.defineLazyModuleGetters(this,
   OS: "resource://gre/modules/osfile.jsm",
   PlacesRemoteTabsAutocompleteProvider: "resource://gre/modules/PlacesRemoteTabsAutocompleteProvider.jsm",
   PlacesSearchAutocompleteProvider: "resource://gre/modules/PlacesSearchAutocompleteProvider.jsm",
   PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
   ProfileAge: "resource://gre/modules/ProfileAge.jsm",
   Sqlite: "resource://gre/modules/Sqlite.jsm",
   TelemetryStopwatch: "resource://gre/modules/TelemetryStopwatch.jsm",
   UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
+  UrlbarProviderOpenTabs: "resource:///modules/UrlbarProviderOpenTabs.jsm",
+  UrlbarProvidersManager: "resource:///modules/UrlbarProvidersManager.jsm",
   UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
 });
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "syncUsernamePref",
                                       "services.sync.username");
 
 function setTimeout(callback, ms) {
   let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
@@ -364,131 +366,16 @@ function iconHelper(url) {
       "page-icon:" + url : PlacesUtils.favicons.defaultFavicon.spec;
   }
   if (url && url instanceof URL && kProtocolsWithIcons.includes(url.protocol)) {
     return "page-icon:" + url.href;
   }
   return PlacesUtils.favicons.defaultFavicon.spec;
 }
 
-/**
- * Storage object for switch-to-tab entries.
- * This takes care of caching and registering open pages, that will be reused
- * by switch-to-tab queries.  It has an internal cache, so that the Sqlite
- * store is lazy initialized only on first use.
- * It has a simple API:
- *   initDatabase(conn): initializes the temporary Sqlite entities to store data
- *   add(uri): adds a given nsIURI to the store
- *   delete(uri): removes a given nsIURI from the store
- *   shutdown(): stops storing data to Sqlite
- */
-XPCOMUtils.defineLazyGetter(this, "SwitchToTabStorage", () => Object.seal({
-  _conn: null,
-  // Temporary queue used while the database connection is not available.
-  _queue: new Map(),
-  // Whether we are in the process of updating the temp table.
-  _updatingLevel: 0,
-  get updating() {
-    return this._updatingLevel > 0;
-  },
-  async initDatabase(conn) {
-    // To reduce IO use an in-memory table for switch-to-tab tracking.
-    // Note: this should be kept up-to-date with the definition in
-    //       nsPlacesTables.h.
-    await conn.execute(
-      `CREATE TEMP TABLE moz_openpages_temp (
-         url TEXT,
-         userContextId INTEGER,
-         open_count INTEGER,
-         PRIMARY KEY (url, userContextId)
-       )`);
-
-    // Note: this should be kept up-to-date with the definition in
-    //       nsPlacesTriggers.h.
-    await conn.execute(
-      `CREATE TEMPORARY TRIGGER moz_openpages_temp_afterupdate_trigger
-       AFTER UPDATE OF open_count ON moz_openpages_temp FOR EACH ROW
-       WHEN NEW.open_count = 0
-       BEGIN
-         DELETE FROM moz_openpages_temp
-         WHERE url = NEW.url
-           AND userContextId = NEW.userContextId;
-       END`);
-
-    this._conn = conn;
-
-    // Populate the table with the current cache contents...
-    for (let [userContextId, uris] of this._queue) {
-      for (let uri of uris) {
-        this.add(uri, userContextId).catch(Cu.reportError);
-      }
-    }
-
-    // ...then clear it to avoid double additions.
-    this._queue.clear();
-  },
-
-  async add(uri, userContextId) {
-    if (!this._conn) {
-      if (!this._queue.has(userContextId)) {
-        this._queue.set(userContextId, new Set());
-      }
-      this._queue.get(userContextId).add(uri);
-      return;
-    }
-    try {
-      this._updatingLevel++;
-      await this._conn.executeCached(
-        `INSERT OR REPLACE INTO moz_openpages_temp (url, userContextId, open_count)
-          VALUES ( :url,
-                    :userContextId,
-                    IFNULL( ( SELECT open_count + 1
-                              FROM moz_openpages_temp
-                              WHERE url = :url
-                              AND userContextId = :userContextId ),
-                            1
-                          )
-                  )
-        `, { url: uri.spec, userContextId });
-    } finally {
-      this._updatingLevel--;
-    }
-  },
-
-  async delete(uri, userContextId) {
-    if (!this._conn) {
-      if (!this._queue.has(userContextId)) {
-        throw new Error("Unknown userContextId!");
-      }
-
-      this._queue.get(userContextId).delete(uri);
-      if (this._queue.get(userContextId).size == 0) {
-        this._queue.delete(userContextId);
-      }
-      return;
-    }
-    try {
-      this._updatingLevel++;
-      await this._conn.executeCached(
-        `UPDATE moz_openpages_temp
-         SET open_count = open_count - 1
-         WHERE url = :url
-           AND userContextId = :userContextId
-        `, { url: uri.spec, userContextId });
-    } finally {
-      this._updatingLevel--;
-    }
-  },
-
-  shutdown() {
-    this._conn = null;
-    this._queue.clear();
-  },
-}));
-
 // Preloaded Sites related
 
 function PreloadedSite(url, title) {
   this.uri = Services.io.newURI(url);
   this.title = title;
   this._matchTitle = title.toLowerCase();
   this._hasWWW = this.uri.host.startsWith("www.");
   this._hostWithoutWWW = this._hasWWW ? this.uri.host.slice(4)
@@ -702,23 +589,23 @@ function Search(searchString, searchPara
   // Will be set later, if needed.
   result.setDefaultIndex(-1);
   this._result = result;
 
   this._previousSearchMatchTypes = [];
   for (let i = 0; previousResult && i < previousResult.matchCount; ++i) {
     let style = previousResult.getStyleAt(i);
     if (style.includes("heuristic")) {
-      this._previousSearchMatchTypes.push(UrlbarUtils.MATCHTYPE.HEURISTIC);
+      this._previousSearchMatchTypes.push(UrlbarUtils.MATCH_GROUP.HEURISTIC);
     } else if (style.includes("suggestion")) {
-      this._previousSearchMatchTypes.push(UrlbarUtils.MATCHTYPE.SUGGESTION);
+      this._previousSearchMatchTypes.push(UrlbarUtils.MATCH_GROUP.SUGGESTION);
     } else if (style.includes("extension")) {
-      this._previousSearchMatchTypes.push(UrlbarUtils.MATCHTYPE.EXTENSION);
+      this._previousSearchMatchTypes.push(UrlbarUtils.MATCH_GROUP.EXTENSION);
     } else {
-      this._previousSearchMatchTypes.push(UrlbarUtils.MATCHTYPE.GENERAL);
+      this._previousSearchMatchTypes.push(UrlbarUtils.MATCH_GROUP.GENERAL);
     }
   }
 
   // Used to limit the number of adaptive results.
   this._adaptiveCount = 0;
   this._extraAdaptiveRows = [];
 
   // Used to limit the number of remote tab results.
@@ -728,18 +615,18 @@ function Search(searchString, searchPara
   // to check how many "current" matches have been inserted.
   // Indeed this._result.matchCount may include matches from the previous search.
   this._currentMatchCount = 0;
 
   // These are used to avoid adding duplicate entries to the results.
   this._usedURLs = [];
   this._usedPlaceIds = new Set();
 
-  // Counters for the number of matches per MATCHTYPE.
-  this._counts = Object.values(UrlbarUtils.MATCHTYPE)
+  // Counters for the number of matches per MATCH_GROUP.
+  this._counts = Object.values(UrlbarUtils.MATCH_GROUP)
                        .reduce((o, p) => { o[p] = 0; return o; }, {});
 }
 
 Search.prototype = {
   /**
    * Enables the desired AutoComplete behavior.
    *
    * @param type
@@ -871,17 +758,17 @@ Search.prototype = {
   async execute(conn) {
     // A search might be canceled before it starts.
     if (!this.pending)
       return;
 
     // Used by stop() to interrupt an eventual running statement.
     this.interrupt = () => {
       // Interrupt any ongoing statement to run the search sooner.
-      if (!SwitchToTabStorage.updating) {
+      if (!UrlbarProvidersManager.interruptLevel) {
         conn.interrupt();
       }
     };
 
     TelemetryStopwatch.start(TELEMETRY_1ST_RESULT, this);
     TelemetryStopwatch.start(TELEMETRY_6_FIRST_RESULTS, this);
 
     // Since we call the synchronous parseSubmissionURL function later, we must
@@ -916,17 +803,17 @@ Search.prototype = {
     await this._checkPreloadedSitesExpiry();
 
     // Add the first heuristic result, if any.  Set _addingHeuristicFirstMatch
     // to true so that when the result is added, "heuristic" can be included in
     // its style.
     this._addingHeuristicFirstMatch = true;
     let hasHeuristic = await this._matchFirstHeuristicResult(conn);
     this._addingHeuristicFirstMatch = false;
-    this._cleanUpNonCurrentMatches(UrlbarUtils.MATCHTYPE.HEURISTIC);
+    this._cleanUpNonCurrentMatches(UrlbarUtils.MATCH_GROUP.HEURISTIC);
     if (!this.pending)
       return;
 
     // We sleep a little between adding the heuristicFirstMatch and matching
     // any other searches so we aren't kicking off potentially expensive
     // searches on every keystroke.
     // Though, if there's no heuristic result, we start searching immediately,
     // since autocomplete may be waiting for us.
@@ -957,27 +844,27 @@ Search.prototype = {
       // Avoid fetching suggestions if they are not required, private browsing
       // mode is enabled, or the search string may expose sensitive information.
       if (this.hasBehavior("searches") && !this._inPrivateWindow &&
           !this._prohibitSearchSuggestionsFor(searchString)) {
         searchSuggestionsCompletePromise = this._matchSearchSuggestions(searchString);
         if (this.hasBehavior("restrict")) {
           // Wait for the suggestions to be added.
           await searchSuggestionsCompletePromise;
-          this._cleanUpNonCurrentMatches(UrlbarUtils.MATCHTYPE.SUGGESTION);
+          this._cleanUpNonCurrentMatches(UrlbarUtils.MATCH_GROUP.SUGGESTION);
           // We're done if we're restricting to search suggestions.
           // Notify the result completion then stop the search.
           this._autocompleteSearch.finishSearch(true);
           return;
         }
       }
     }
     // In any case, clear previous suggestions.
     searchSuggestionsCompletePromise.then(() => {
-      this._cleanUpNonCurrentMatches(UrlbarUtils.MATCHTYPE.SUGGESTION);
+      this._cleanUpNonCurrentMatches(UrlbarUtils.MATCH_GROUP.SUGGESTION);
     });
 
     // Run the adaptive query first.
     await conn.executeCached(this._adaptiveQuery[0], this._adaptiveQuery[1],
                              this._onResultRow.bind(this));
     if (!this.pending)
       return;
 
@@ -1014,25 +901,25 @@ Search.prototype = {
     // If we have some unused remote tab matches, add them now.
     while (this._extraRemoteTabRows.length &&
           this._currentMatchCount < UrlbarPrefs.get("maxRichResults")) {
       this._addMatch(this._extraRemoteTabRows.shift());
     }
 
     // Ideally we should wait until MATCH_BOUNDARY_ANYWHERE, but that query
     // may be really slow and we may end up showing old results for too long.
-    this._cleanUpNonCurrentMatches(UrlbarUtils.MATCHTYPE.GENERAL);
+    this._cleanUpNonCurrentMatches(UrlbarUtils.MATCH_GROUP.GENERAL);
 
     this._matchAboutPages();
 
     // If we do not have enough results, and our match type is
     // MATCH_BOUNDARY_ANYWHERE, search again with MATCH_ANYWHERE to get more
     // results.
-    let count = this._counts[UrlbarUtils.MATCHTYPE.GENERAL] +
-                this._counts[UrlbarUtils.MATCHTYPE.HEURISTIC];
+    let count = this._counts[UrlbarUtils.MATCH_GROUP.GENERAL] +
+                this._counts[UrlbarUtils.MATCH_GROUP.HEURISTIC];
     if (this._matchBehavior == MATCH_BOUNDARY_ANYWHERE &&
         count < UrlbarPrefs.get("maxRichResults")) {
       this._matchBehavior = MATCH_ANYWHERE;
       for (let [query, params] of [ this._adaptiveQuery,
                                     this._searchQuery ]) {
         await conn.executeCached(query, params, this._onResultRow.bind(this));
         if (!this.pending)
           return;
@@ -1476,32 +1363,32 @@ Search.prototype = {
       return false;
 
     let query = this._originalSearchString;
     this._addSearchEngineMatch(match, query);
     return true;
   },
 
   _addExtensionMatch(content, comment) {
-    let count = this._counts[UrlbarUtils.MATCHTYPE.EXTENSION] +
-                this._counts[UrlbarUtils.MATCHTYPE.HEURISTIC];
+    let count = this._counts[UrlbarUtils.MATCH_GROUP.EXTENSION] +
+                this._counts[UrlbarUtils.MATCH_GROUP.HEURISTIC];
     if (count >= UrlbarUtils.MAXIMUM_ALLOWED_EXTENSION_MATCHES) {
       return;
     }
 
     this._addMatch({
       value: PlacesUtils.mozActionURI("extension", {
         content,
         keyword: this._searchTokens[0],
       }),
       comment,
       icon: "chrome://browser/content/extension.svg",
       style: "action extension",
       frecency: Infinity,
-      type: UrlbarUtils.MATCHTYPE.EXTENSION,
+      type: UrlbarUtils.MATCH_GROUP.EXTENSION,
     });
   },
 
   _addSearchEngineMatch(searchMatch, query, suggestion = "", historical = false) {
     let actionURLParams = {
       engineName: searchMatch.engineName,
       input: suggestion || this._originalSearchString,
       searchQuery: query,
@@ -1516,17 +1403,17 @@ Search.prototype = {
       value,
       comment: searchMatch.engineName,
       icon: searchMatch.iconUrl,
       style: "action searchengine",
       frecency: FRECENCY_DEFAULT,
     };
     if (suggestion) {
       match.style += " suggestion";
-      match.type = UrlbarUtils.MATCHTYPE.SUGGESTION;
+      match.type = UrlbarUtils.MATCH_GROUP.SUGGESTION;
     }
 
     this._addMatch(match);
   },
 
   _matchExtensionSuggestions() {
     let promise = ExtensionSearchHandler.handleSearch(this._searchTokens[0], this._originalSearchString,
       suggestions => {
@@ -1535,17 +1422,17 @@ Search.prototype = {
           this._addExtensionMatch(content, suggestion.description);
         }
       }
     );
     // Remove previous search matches sooner than the maximum timeout, otherwise
     // matches may appear stale for a long time.
     // This is necessary because WebExtensions don't have a method to notify
     // that they are done providing results, so they could be pending forever.
-    setTimeout(() => this._cleanUpNonCurrentMatches(UrlbarUtils.MATCHTYPE.EXTENSION), 100);
+    setTimeout(() => this._cleanUpNonCurrentMatches(UrlbarUtils.MATCH_GROUP.EXTENSION), 100);
 
     // Since the extension has no way to signale when it's done pushing
     // results, we add a timeout racing with the addition.
     let timeoutPromise = new Promise(resolve => {
       setTimeout(resolve, MAXIMUM_ALLOWED_EXTENSION_TIME_MS);
     });
     return Promise.race([timeoutPromise, promise]).catch(Cu.reportError);
   },
@@ -1681,18 +1568,18 @@ Search.prototype = {
         this._addAdaptiveQueryMatch(row);
         break;
       case QUERYTYPE_FILTERED:
         this._addFilteredQueryMatch(row);
         break;
     }
     // If the search has been canceled by the user or by _addMatch, or we
     // fetched enough results, we can stop the underlying Sqlite query.
-    let count = this._counts[UrlbarUtils.MATCHTYPE.GENERAL] +
-                this._counts[UrlbarUtils.MATCHTYPE.HEURISTIC];
+    let count = this._counts[UrlbarUtils.MATCH_GROUP.GENERAL] +
+                this._counts[UrlbarUtils.MATCH_GROUP.HEURISTIC];
     if (!this.pending || count >= UrlbarPrefs.get("maxRichResults")) {
       cancel();
     }
   },
 
   _maybeRestyleSearchMatch(match) {
     // Return if the URL does not represent a search result.
     let parseResult =
@@ -1722,19 +1609,19 @@ Search.prototype = {
     match.style = "action searchengine favicon";
   },
 
   _addMatch(match) {
     if (typeof match.frecency != "number")
       throw new Error("Frecency not provided");
 
     if (this._addingHeuristicFirstMatch)
-      match.type = UrlbarUtils.MATCHTYPE.HEURISTIC;
+      match.type = UrlbarUtils.MATCH_GROUP.HEURISTIC;
     else if (typeof match.type != "string")
-      match.type = UrlbarUtils.MATCHTYPE.GENERAL;
+      match.type = UrlbarUtils.MATCH_GROUP.GENERAL;
 
     // A search could be canceled between a query start and its completion,
     // in such a case ensure we won't notify any result for it.
     if (!this.pending)
       return;
 
     match.style = match.style || "favicon";
 
@@ -1764,17 +1651,17 @@ Search.prototype = {
                                match.finalCompleteValue);
     this._currentMatchCount++;
     this._counts[match.type]++;
 
     if (this._currentMatchCount == 1)
       TelemetryStopwatch.finish(TELEMETRY_1ST_RESULT, this);
     if (this._currentMatchCount == 6)
       TelemetryStopwatch.finish(TELEMETRY_6_FIRST_RESULTS, this);
-    this.notifyResult(true, match.type == UrlbarUtils.MATCHTYPE.HEURISTIC);
+    this.notifyResult(true, match.type == UrlbarUtils.MATCH_GROUP.HEURISTIC);
   },
 
   _getInsertIndexForMatch(match) {
     // Check for duplicates and either discard (by returning -1) the duplicate
     // or suggest to replace the original match, in case the new one is more
     // specific (for example a Remote Tab wins over History, and a Switch to Tab
     // wins over a Remote Tab).
     // Must check both id and url, cause keywords dynamically modify the url.
@@ -1789,17 +1676,17 @@ Search.prototype = {
         // The new entry is a switch/remote tab entry, look for the duplicate
         // among current matches.
         for (let i = 0; i < this._usedURLs.length; ++i) {
           let {key: matchKey, action: matchAction, type: matchType} = this._usedURLs[i];
           if (matchKey == urlMapKey) {
             isDupe = true;
             // Don't replace the match if the existing one is heuristic and the
             // new one is a switchtab, instead also add the switchtab match.
-            if (matchType == UrlbarUtils.MATCHTYPE.HEURISTIC &&
+            if (matchType == UrlbarUtils.MATCH_GROUP.HEURISTIC &&
                 action.type == "switchtab") {
               isDupe = false;
               // Since we allow to insert a dupe in this case, we must continue
               // checking the next matches to be sure we won't insert more than
               // one dupe. For this same reason we must reset isDupe = true for
               // each found dupe.
               continue;
             }
@@ -1825,17 +1712,17 @@ Search.prototype = {
     if (match.placeId)
       this._usedPlaceIds.add(match.placeId);
 
     let index = 0;
     // The buckets change depending on the context, that is currently decided by
     // the first added match (the heuristic one).
     if (!this._buckets) {
       // Convert the buckets to readable objects with a count property.
-      let buckets = match.type == UrlbarUtils.MATCHTYPE.HEURISTIC &&
+      let buckets = match.type == UrlbarUtils.MATCH_GROUP.HEURISTIC &&
                     match.style.includes("searchengine") ? UrlbarPrefs.get("matchBucketsSearch")
                                                          : UrlbarPrefs.get("matchBuckets");
       // - available is the number of available slots in the bucket
       // - insertIndex is the index of the first available slot in the bucket
       // - count is the number of matches in the bucket, note that it also
       //   account for matches from the previous search, while available and
       //   insertIndex don't.
       this._buckets = buckets.map(([type, available]) => ({ type,
@@ -1883,17 +1770,17 @@ Search.prototype = {
     this._usedURLs[index] = {key: urlMapKey, action, type: match.type};
     return { index, replace };
   },
 
   /**
    * Removes matches from a previous search, that are no more returned by the
    * current search
    * @param type
-   *        The UrlbarUtils.MATCHTYPE to clean up.
+   *        The UrlbarUtils.MATCH_GROUP to clean up.
    * @param [optional] notify
    *        Whether to notify a result change.
    */
   _cleanUpNonCurrentMatches(type, notify = true) {
     if (this._previousSearchMatchTypes.length == 0 || !this.pending)
       return;
 
     let index = 0;
@@ -2410,42 +2297,33 @@ UnifiedComplete.prototype = {
 
         try {
            Sqlite.shutdown.addBlocker("Places UnifiedComplete.js closing",
                                       () => {
                                         // Break a possible cycle through the
                                         // previous result, the controller and
                                         // ourselves.
                                         this._currentSearch = null;
-                                        SwitchToTabStorage.shutdown();
                                       });
         } catch (ex) {
           // It's too late to block shutdown.
           throw ex;
         }
-        await SwitchToTabStorage.initDatabase(conn);
+        await UrlbarProviderOpenTabs.promiseDb();
         return conn;
       })().catch(ex => {
         dump("Couldn't get database handle: " + ex + "\n");
         Cu.reportError(ex);
       });
     }
     return this._promiseDatabase;
   },
 
   // mozIPlacesAutoComplete
 
-  registerOpenPage(uri, userContextId) {
-    SwitchToTabStorage.add(uri, userContextId).catch(Cu.reportError);
-  },
-
-  unregisterOpenPage(uri, userContextId) {
-    SwitchToTabStorage.delete(uri, userContextId).catch(Cu.reportError);
-  },
-
   populatePreloadedSiteStorage(json) {
     PreloadedSiteStorage.populate(json);
   },
 
   // nsIAutoCompleteSearch
 
   startSearch(searchString, searchParam, acPreviousResult, listener) {
     // Stop the search in case the controller has not taken care of it.
--- a/toolkit/components/places/mozIPlacesAutoComplete.idl
+++ b/toolkit/components/places/mozIPlacesAutoComplete.idl
@@ -103,44 +103,15 @@ interface mozIPlacesAutoComplete : nsISu
   const long BEHAVIOR_RESTRICT = 1 << 8;
 
   /**
    * Include search suggestions from the currently selected search provider.
    */
   const long BEHAVIOR_SEARCHES = 1 << 9;
 
   /**
-   * Mark a page as being currently open.
-   *
-   * @note Pages will not be automatically unregistered when Private Browsing
-   *       mode is entered or exited.  Therefore, consumers MUST unregister or
-   *       register themselves.
-   *
-   * @param aURI
-   *        The URI to register as an open page.
-   * @param aUserContextId
-   *        The Container Id of the tab.
-   */
-  void registerOpenPage(in nsIURI aURI, in uint32_t aUserContextId);
-
-  /**
-   * Mark a page as no longer being open (either by closing the window or tab,
-   * or by navigating away from that page).
-   *
-   * @note Pages will not be automatically unregistered when Private Browsing
-   *       mode is entered or exited.  Therefore, consumers MUST unregister or
-   *       register themselves.
-   *
-   * @param aURI
-   *        The URI to unregister as an open page.
-   * @param aUserContextId
-   *        The Container Id of the tab.
-   */
-  void unregisterOpenPage(in nsIURI aURI, in uint32_t aUserContextId);
-
-  /**
    * Populate list of Preloaded Sites from JSON.
    *
    * @param sites
    *        Array of [url,title] to populate from.
    */
   void populatePreloadedSiteStorage(in jsval sites);
 };
--- a/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
+++ b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
@@ -20,16 +20,19 @@ ChromeUtils.import("resource://testing-c
   /* import-globals-from ./autofill_tasks.js */
   let file = do_get_file("autofill_tasks.js", false);
   let uri = Services.io.newFileURI(file);
   XPCOMUtils.defineLazyScriptGetter(this, "addAutofillTasks", uri.spec);
 }
 
 // Put any other stuff relative to this test folder below.
 
+ChromeUtils.defineModuleGetter(this, "UrlbarProviderOpenTabs",
+  "resource:///modules/UrlbarProviderOpenTabs.jsm");
+
 const TITLE_SEARCH_ENGINE_SEPARATOR = " \u00B7\u2013\u00B7 ";
 
 async function cleanup() {
   Services.prefs.clearUserPref("browser.urlbar.autocomplete.enabled");
   Services.prefs.clearUserPref("browser.urlbar.autoFill");
   Services.prefs.clearUserPref("browser.urlbar.autoFill.searchEngines");
   let suggestPrefs = [
     "history",
@@ -308,28 +311,24 @@ var addBookmark = async function(aBookma
   }
 
   if (aBookmarkObj.tags) {
     PlacesUtils.tagging.tagURI(aBookmarkObj.uri, aBookmarkObj.tags);
   }
 };
 
 function addOpenPages(aUri, aCount = 1, aUserContextId = 0) {
-  let ac = Cc["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
-             .getService(Ci.mozIPlacesAutoComplete);
   for (let i = 0; i < aCount; i++) {
-    ac.registerOpenPage(aUri, aUserContextId);
+    UrlbarProviderOpenTabs.registerOpenTab(aUri.spec, aUserContextId);
   }
 }
 
 function removeOpenPages(aUri, aCount = 1, aUserContextId = 0) {
-  let ac = Cc["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
-             .getService(Ci.mozIPlacesAutoComplete);
   for (let i = 0; i < aCount; i++) {
-    ac.unregisterOpenPage(aUri, aUserContextId);
+    UrlbarProviderOpenTabs.unregisterOpenTab(aUri.spec, aUserContextId);
   }
 }
 
 /**
  * Strip prefixes from the URI that we don't care about for searching.
  *
  * @param {String} spec
  *        The text to modify.
--- a/tools/lint/eslint/modules.json
+++ b/tools/lint/eslint/modules.json
@@ -205,19 +205,16 @@
   "tokenserverclient.js": ["TokenServerClient", "TokenServerClientError", "TokenServerClientNetworkError", "TokenServerClientServerError"],
   "ToolboxProcess.jsm": ["BrowserToolboxProcess"],
   "tps.jsm": ["ACTIONS", "Addons", "Addresses", "Bookmarks", "CreditCards", "Formdata", "History", "Passwords", "Prefs", "Tabs", "TPS", "Windows"],
   "Translation.jsm": ["Translation", "TranslationTelemetry"],
   "Traversal.jsm": ["TraversalRules", "TraversalHelper"],
   "UpdateTelemetry.jsm": ["AUSTLMY"],
   "UpdateTopLevelContentWindowIDHelper.jsm": ["trackBrowserWindow"],
   "UrlbarController.jsm": ["QueryContext", "UrlbarController"],
-  "UrlbarPrefs.jsm": ["UrlbarPrefs"],
-  "UrlbarTokenizer.jsm": ["UrlbarTokenizer"],
-  "UrlbarUtils.jsm": ["UrlbarUtils"],
   "util.js": ["getChromeWindow", "Utils", "Svc", "SerializableSet"],
   "utils.js": ["btoa", "encryptPayload", "makeIdentityConfig", "makeFxAccountsInternalMock", "configureFxAccountIdentity", "configureIdentity", "SyncTestingInfrastructure", "waitForZeroTimer", "Promise", "MockFxaStorageManager", "AccountState", "sumHistogram", "CommonUtils", "CryptoUtils", "TestingUtils", "promiseZeroTimer", "promiseNamedTimer", "getLoginTelemetryScalar", "syncTestLogging"],
   "Utils.jsm": ["Utils", "Logger", "PivotContext", "PrefCache"],
   "VariablesView.jsm": ["VariablesView", "escapeHTML"],
   "VariablesViewController.jsm": ["VariablesViewController", "StackFrameUtils"],
   "version.jsm": ["VERSION"],
   "vtt.jsm": ["WebVTT"],
   "WebChannel.jsm": ["WebChannel", "WebChannelBroker"],
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -428,16 +428,17 @@ struct CoTaskMemFreePolicy
 {
   void operator()(void* aPtr) {
     ::CoTaskMemFree(aPtr);
   }
 };
 
 SetThreadDpiAwarenessContextProc WinUtils::sSetThreadDpiAwarenessContext = NULL;
 EnableNonClientDpiScalingProc WinUtils::sEnableNonClientDpiScaling = NULL;
+GetSystemMetricsForDpiProc WinUtils::sGetSystemMetricsForDpi = NULL;
 
 /* static */
 void
 WinUtils::Initialize()
 {
   if (IsWin10OrLater()) {
     HMODULE user32Dll = ::GetModuleHandleW(L"user32");
     if (user32Dll) {
@@ -449,16 +450,19 @@ WinUtils::Initialize()
           areDpiAwarenessContextsEqual(getThreadDpiAwarenessContext(),
                                        DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) {
         // Only per-monitor v1 requires these workarounds.
         sEnableNonClientDpiScaling = (EnableNonClientDpiScalingProc)
           ::GetProcAddress(user32Dll, "EnableNonClientDpiScaling");
         sSetThreadDpiAwarenessContext = (SetThreadDpiAwarenessContextProc)
           ::GetProcAddress(user32Dll, "SetThreadDpiAwarenessContext");
       }
+
+      sGetSystemMetricsForDpi = (GetSystemMetricsForDpiProc)
+        ::GetProcAddress(user32Dll, "GetSystemMetricsForDpi");
     }
   }
 }
 
 // static
 LRESULT WINAPI
 WinUtils::NonClientDpiScalingDefWindowProcW(HWND hWnd, UINT msg,
                                             WPARAM wParam, LPARAM lParam)
@@ -666,16 +670,37 @@ WinUtils::MonitorFromRect(const gfx::Rec
     NSToIntRound(dpiScale * rect.Y()),
     NSToIntRound(dpiScale * (rect.XMost())),
     NSToIntRound(dpiScale * (rect.YMost()))
   };
 
   return ::MonitorFromRect(&globalWindowBounds, MONITOR_DEFAULTTONEAREST);
 }
 
+/* static */
+bool
+WinUtils::HasSystemMetricsForDpi()
+{
+  return (sGetSystemMetricsForDpi != NULL);
+}
+
+/* static */
+int
+WinUtils::GetSystemMetricsForDpi(int nIndex, UINT dpi)
+{
+  if (HasSystemMetricsForDpi()) {
+    return sGetSystemMetricsForDpi(nIndex, dpi);
+  } else {
+    double scale = IsPerMonitorDPIAware()
+      ? dpi / SystemDPI()
+      : 1.0;
+    return NSToIntRound(::GetSystemMetrics(nIndex) * scale);
+  }
+}
+
 #ifdef ACCESSIBILITY
 /* static */
 a11y::Accessible*
 WinUtils::GetRootAccessibleForHWND(HWND aHwnd)
 {
   nsWindow* window = GetNSWindowPtr(aHwnd);
   if (!window) {
     return nullptr;
--- a/widget/windows/WinUtils.h
+++ b/widget/windows/WinUtils.h
@@ -98,16 +98,17 @@ typedef enum DPI_AWARENESS {
 
 #if WINVER < 0x0605
 WINUSERAPI DPI_AWARENESS_CONTEXT WINAPI GetThreadDpiAwarenessContext();
 WINUSERAPI BOOL WINAPI
 AreDpiAwarenessContextsEqual(DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT);
 #endif /* WINVER < 0x0605 */
 typedef DPI_AWARENESS_CONTEXT(WINAPI * SetThreadDpiAwarenessContextProc)(DPI_AWARENESS_CONTEXT);
 typedef BOOL(WINAPI * EnableNonClientDpiScalingProc)(HWND);
+typedef int (WINAPI * GetSystemMetricsForDpiProc)(int, UINT);
 
 namespace mozilla {
 enum class PointerCapabilities : uint8_t;
 #if defined(ACCESSIBILITY)
 namespace a11y {
 class Accessible;
 } // namespace a11y
 #endif // defined(ACCESSIBILITY)
@@ -158,16 +159,17 @@ public:
 #endif
 
 class WinUtils
 {
   // Function pointers for APIs that may not be available depending on
   // the Win10 update version -- will be set up in Initialize().
   static SetThreadDpiAwarenessContextProc sSetThreadDpiAwarenessContext;
   static EnableNonClientDpiScalingProc sEnableNonClientDpiScaling;
+  static GetSystemMetricsForDpiProc sGetSystemMetricsForDpi;
 
 public:
   class AutoSystemDpiAware
   {
   public:
     AutoSystemDpiAware()
     {
       if (sSetThreadDpiAwarenessContext) {
@@ -222,16 +224,19 @@ public:
   }
   static double LogToPhysFactor(HDC aDC) {
     return LogToPhysFactor(::WindowFromDC(aDC));
   }
   static int32_t LogToPhys(HMONITOR aMonitor, double aValue);
   static HMONITOR GetPrimaryMonitor();
   static HMONITOR MonitorFromRect(const gfx::Rect& rect);
 
+  static bool HasSystemMetricsForDpi();
+  static int GetSystemMetricsForDpi(int nIndex, UINT dpi);
+
   /**
    * Logging helpers that dump output to prlog module 'Widget', console, and
    * OutputDebugString. Note these output in both debug and release builds.
    */
   static void Log(const char *fmt, ...);
   static void LogW(const wchar_t *fmt, ...);
 
   /**
--- a/widget/windows/nsNativeThemeWin.cpp
+++ b/widget/windows/nsNativeThemeWin.cpp
@@ -2163,19 +2163,30 @@ nsNativeThemeWin::GetWidgetPadding(nsDev
 
   // Content padding
   if (aWidgetType == StyleAppearance::MozWindowTitlebar ||
       aWidgetType == StyleAppearance::MozWindowTitlebarMaximized) {
     aResult->SizeTo(0, 0, 0, 0);
     // XXX Maximized windows have an offscreen offset equal to
     // the border padding. This should be addressed in nsWindow,
     // but currently can't be, see UpdateNonClientMargins.
-    if (aWidgetType == StyleAppearance::MozWindowTitlebarMaximized)
-      aResult->top = GetSystemMetrics(SM_CXFRAME)
-                   + GetSystemMetrics(SM_CXPADDEDBORDER);
+    if (aWidgetType == StyleAppearance::MozWindowTitlebarMaximized) {
+      nsIWidget* rootWidget = nullptr;
+      if (WinUtils::HasSystemMetricsForDpi()) {
+        rootWidget = aFrame->PresContext()->GetRootWidget();
+      }
+      if (rootWidget) {
+        double dpi = rootWidget->GetDPI();
+        aResult->top = WinUtils::GetSystemMetricsForDpi(SM_CXFRAME, dpi)
+                     + WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
+      } else {
+        aResult->top = GetSystemMetrics(SM_CXFRAME)
+                     + GetSystemMetrics(SM_CXPADDEDBORDER);
+      }
+    }
     return ok;
   }
 
   HANDLE theme = GetTheme(aWidgetType);
   if (!theme) {
     ok = ClassicGetWidgetPadding(aContext, aFrame, aWidgetType, aResult);
     ScaleForFrameDPI(aResult, aFrame);
     return ok;