Merge inbound to mozilla-central. a=merge
authorOana Pop Rus <opoprus@mozilla.com>
Thu, 31 Jan 2019 11:37:38 +0200
changeset 456207 9ee54a21a22ab5beab264bcabe3c8039a27a32e8
parent 456173 efda4de34c171f6acbb19ea1fd07872745df2760 (current diff)
parent 456206 1705824aa93ebc304196ad6456dbb177f4594cdd (diff)
child 456217 9e6b8fb9b9ff9653583bc6b5dedc96103a036ee7
child 456238 39ba22f56d90e00f31b4f0f367c239f86f40965e
push id35474
push useropoprus@mozilla.com
push dateThu, 31 Jan 2019 09:37:52 +0000
treeherdermozilla-central@9ee54a21a22a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone67.0a1
first release with
nightly linux32
9ee54a21a22a / 67.0a1 / 20190131093752 / files
nightly linux64
9ee54a21a22a / 67.0a1 / 20190131093752 / files
nightly mac
9ee54a21a22a / 67.0a1 / 20190131093752 / files
nightly win32
9ee54a21a22a / 67.0a1 / 20190131093752 / files
nightly win64
9ee54a21a22a / 67.0a1 / 20190131093752 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
toolkit/components/passwordmgr/test/mochitest/test_xml_load.html
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -237,16 +237,18 @@ pref("browser.defaultbrowser.notificatio
 
 // 0 = blank, 1 = home (browser.startup.homepage), 2 = last visited page, 3 = resume previous browser session
 // The behavior of option 3 is detailed at: http://wiki.mozilla.org/Session_Restore
 pref("browser.startup.page",                1);
 pref("browser.startup.homepage",            "about:home");
 // Whether we should skip the homepage when opening the first-run page
 pref("browser.startup.firstrunSkipsHomepage", true);
 
+pref("browser.dedicatedprofile.welcome.accounts.endpoint", "https://accounts.firefox.com/");
+
 // Show an about:blank window as early as possible for quick startup feedback.
 // Held to nightly on Linux due to bug 1450626.
 // Disabled on Mac because the bouncing dock icon already provides feedback.
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) && defined(NIGHTLY_BUILD)
 pref("browser.startup.blankWindow", true);
 #else
 pref("browser.startup.blankWindow", false);
 #endif
--- a/browser/base/content/browser-contentblocking.js
+++ b/browser/base/content/browser-contentblocking.js
@@ -80,22 +80,30 @@ var TrackingProtection = {
     if (this.enabled) {
       label = ContentBlocking.showBlockedLabels ? "contentBlocking.trackers.blocking.label" : null;
     } else {
       label = ContentBlocking.showAllowedLabels ? "contentBlocking.trackers.allowed.label" : null;
     }
     this.categoryLabel.textContent = label ? gNavigatorBundle.getString(label) : "";
   },
 
+  // FIXME This must change! Fingerprinting and cryptomining must have theirs
+  // own sections. See bug 1522566.
   isBlocking(state) {
-    return (state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT) != 0;
+    return (state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT) != 0 ||
+           (state & Ci.nsIWebProgressListener.STATE_BLOCKED_FINGERPRINTING_CONTENT) != 0 ||
+           (state & Ci.nsIWebProgressListener.STATE_BLOCKED_CRYPTOMINING_CONTENT) != 0;
   },
 
+  // FIXME This must change! Fingerprinting and cryptomining must have theirs
+  // own sections. See bug 1522566.
   isAllowing(state) {
-    return (state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT) != 0;
+    return (state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT) != 0 ||
+           (state & Ci.nsIWebProgressListener.STATE_LOADED_FINGERPRINTING_CONTENT) != 0 ||
+           (state & Ci.nsIWebProgressListener.STATE_LOADED_CRYPTOMINING_CONTENT) != 0;
   },
 
   isDetected(state) {
     return this.isBlocking(state) || this.isAllowing(state);
   },
 
   async updateSubView() {
     let previousURI = gBrowser.currentURI.spec;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -328,29 +328,46 @@ Object.defineProperty(this, "gFindBarPro
 async function gLazyFindCommand(cmd, ...args) {
   let fb = await gFindBarPromise;
   // We could be closed by now, or the tab with XBL binding could have gone away:
   if (fb && fb[cmd]) {
     fb[cmd].apply(fb, args);
   }
 }
 
+var gPageIcons = {
+  "about:home": "chrome://branding/content/icon32.png",
+  "about:newtab": "chrome://branding/content/icon32.png",
+  "about:welcome": "chrome://branding/content/icon32.png",
+  "about:newinstall": "chrome://branding/content/icon32.png",
+  "about:privatebrowsing": "chrome://browser/skin/privatebrowsing/favicon.svg",
+};
 
 var gInitialPages = [
   "about:blank",
   "about:newtab",
   "about:home",
   "about:privatebrowsing",
   "about:welcomeback",
   "about:sessionrestore",
   "about:welcome",
+  "about:newinstall",
 ];
 
 function isInitialPage(url) {
-  return gInitialPages.includes(url) || url == BROWSER_NEW_TAB_URL;
+  if (!(url instanceof Ci.nsIURI)) {
+    try {
+      url = Services.io.newURI(url);
+    } catch (ex) {
+      return false;
+    }
+  }
+
+  let nonQuery = url.prePath + url.filePath;
+  return gInitialPages.includes(nonQuery) || nonQuery == BROWSER_NEW_TAB_URL;
 }
 
 function browserWindows() {
   return Services.wm.getEnumerator("navigator:browser");
 }
 
 // This is a stringbundle-like interface to gBrowserBundle, formerly a getter for
 // the "bundle_browser" element.
@@ -1289,23 +1306,28 @@ var gBrowserInit = {
     let areas = CustomizableUI.areas;
     areas.splice(areas.indexOf(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL), 1);
     for (let area of areas) {
       let node = document.getElementById(area);
       CustomizableUI.registerToolbarNode(node);
     }
     BrowserSearch.initPlaceHolder();
 
-    // Hack to ensure that the about:home favicon is loaded
+    // Hack to ensure that the various initial pages favicon is loaded
     // instantaneously, to avoid flickering and improve perceived performance.
     this._callWithURIToLoad(uriToLoad => {
-      if (uriToLoad == "about:home" || uriToLoad == "about:newtab" || uriToLoad == "about:welcome") {
-        gBrowser.setIcon(gBrowser.selectedTab, "chrome://branding/content/icon32.png");
-      } else if (uriToLoad == "about:privatebrowsing") {
-        gBrowser.setIcon(gBrowser.selectedTab, "chrome://browser/skin/privatebrowsing/favicon.svg");
+      let url;
+      try {
+        url = Services.io.newURI(uriToLoad);
+      } catch (e) {
+        return;
+      }
+      let nonQuery = url.prePath + url.filePath;
+      if (nonQuery in gPageIcons) {
+        gBrowser.setIcon(gBrowser.selectedTab, gPageIcons[nonQuery]);
       }
     });
 
     this._setInitialFocus();
   },
 
   onLoad() {
     gBrowser.addEventListener("DOMUpdateBlockedPopups", gPopupBlockerObserver);
@@ -2639,17 +2661,17 @@ function URLBarSetURI(aURI, updatePopupN
     let uri = aURI || gBrowser.currentURI;
     // Strip off "wyciwyg://" and passwords for the location bar
     try {
       uri = Services.uriFixup.createExposableURI(uri);
     } catch (e) {}
 
     // Replace initial page URIs with an empty string
     // only if there's no opener (bug 370555).
-    if (isInitialPage(uri.spec) &&
+    if (isInitialPage(uri) &&
         checkEmptyPageOrigin(gBrowser.selectedBrowser, uri)) {
       value = "";
     } else {
       // We should deal with losslessDecodeURI throwing for exotic URIs
       try {
         value = losslessDecodeURI(uri);
       } catch (ex) {
         value = "about:blank";
@@ -4909,17 +4931,18 @@ var XULBrowserWindow = {
   _lastLocationForEvent: null,
 
   // This is called in multiple ways:
   //  1. Due to the nsIWebProgressListener.onContentBlockingEvent notification.
   //  2. Called by tabbrowser.xml when updating the current browser.
   //  3. Called directly during this object's initializations.
   //  4. Due to the nsIWebProgressListener.onLocationChange notification.
   // aRequest will be null always in case 2 and 3, and sometimes in case 1 (for
-  // instance, there won't be a request when STATE_BLOCKED_TRACKING_CONTENT is observed).
+  // instance, there won't be a request when STATE_BLOCKED_TRACKING_CONTENT or
+  // other blocking events are observed).
   onContentBlockingEvent(aWebProgress, aRequest, aEvent, aIsSimulated) {
     // Don't need to do anything if the data we use to update the UI hasn't
     // changed
     let uri = gBrowser.currentURI;
     let spec = uri.spec;
     if (this._event == aEvent &&
         this._lastLocationForEvent == spec) {
       return;
new file mode 100644
--- /dev/null
+++ b/browser/base/content/newInstall.js
@@ -0,0 +1,50 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
+
+function init() {
+  document.querySelector("button").addEventListener("command", () => {
+    window.close();
+  });
+
+  if (navigator.platform == "MacIntel") {
+    hideMenus();
+    window.addEventListener("unload", showMenus);
+  }
+}
+
+let gHidden = [];
+let gCollapsed = [];
+let hiddenDoc = Services.appShell.hiddenDOMWindow.document;
+
+function hideItem(id) {
+  let element = hiddenDoc.getElementById(id);
+  element.hidden = true;
+  gHidden.push(element);
+}
+
+function collapseItem(id) {
+  let element = hiddenDoc.getElementById(id);
+  element.collapsed = true;
+  gCollapsed.push(element);
+}
+
+function hideMenus() {
+  hideItem("macDockMenuNewWindow");
+  hideItem("macDockMenuNewPrivateWindow");
+  collapseItem("aboutName");
+  collapseItem("menu_preferences");
+  collapseItem("menu_mac_services");
+}
+
+function showMenus() {
+  for (let menu of gHidden) {
+    menu.hidden = false;
+  }
+
+  for (let menu of gCollapsed) {
+    menu.collapsed = false;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/newInstall.xul
@@ -0,0 +1,28 @@
+<!-- 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/. -->
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
+%syncBrandDTD;
+<!ENTITY % newInstallDTD SYSTEM "chrome://browser/locale/newInstall.dtd">
+%newInstallDTD;
+]>
+
+<?xml-stylesheet href="chrome://global/skin/"?>
+<?xml-stylesheet href="chrome://browser/skin/newInstall.css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="init()" title="&window.title;" style="&window.style;">
+  <script src="chrome://browser/content/newInstall.js"></script>
+  <hbox align="start" flex="1">
+    <image id="alert" role="presentation"/>
+    <vbox align="end" flex="1">
+      <description class="main-text">&mainText;</description>
+      <description>&sync;</description>
+      <button label="&continue-button;"/>
+    </vbox>
+  </hbox>
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/newInstallPage.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<html>
+<head>
+  <meta http-equiv="Content-Security-Policy" content="connect-src https:; default-src chrome:">
+  <meta name="referrer" content="no-referrer">
+  <link rel="stylesheet" type="text/css" href="chrome://global/skin/in-content/common.css">
+  <link rel="stylesheet" type="text/css" href="chrome://browser/skin/newInstallPage.css">
+  <link rel="localization" href="branding/brand.ftl">
+  <link rel="localization" href="browser/branding/sync-brand.ftl">
+  <link rel="localization" href="browser/newInstallPage.ftl">
+  <link rel="icon" type="image/png" href="chrome://branding/content/icon32.png">
+  <title data-l10n-id="title"></title>
+  <script type="text/javascript" src="chrome://browser/content/newInstallPage.js"></script>
+</head>
+<body>
+  <div id="main">
+    <div id="header">
+      <img role="presentation" src="chrome://branding/content/horizontal-lockup.svg">
+    </div>
+    <div id="content">
+      <div id="info">
+        <h1 data-l10n-id="heading"></h1>
+        <h3 data-l10n-id="changed-title"></h3>
+        <p data-l10n-id="changed-desc-profiles"></p>
+        <p data-l10n-id="changed-desc-dedicated"></p>
+        <p data-l10n-id="lost"></p>
+        <h3 data-l10n-id="options-title"></h3>
+        <p data-l10n-id="options-do-nothing"></p>
+        <p data-l10n-id="options-use-sync"></p>
+        <p>
+          <span data-l10n-id="resources"></span><br>
+          <a data-l10n-id="support-link" href="https://support.mozilla.org/kb/profile-manager-create-and-remove-firefox-profiles" target="_blank" rel="noopener"></a>
+        </p>
+      </div>
+      <form id="sync">
+        <h1 id="sync-header" data-l10n-id="sync-header"></h1>
+        <p id="sync-label"><label data-l10n-id="sync-label" for="sync-input"></label></p>
+        <p id="sync-input-container"><input id="sync-input" type="email" required name="email" placeholder="Email"></p>
+        <p id="sync-terms" data-l10n-id="sync-terms">
+          <a data-l10n-name="terms" href="https://accounts.firefox.com/legal/terms" target="_blank" rel="noopener"></a>
+          <a data-l10n-name="privacy" href="https://accounts.firefox.com/legal/privacy" target="_blank" rel="noopener"></a>
+        </p>
+        <p id="sync-button-container"><button id="sync-button" type="submit" data-l10n-id="sync-button"></button></p>
+        <p id="sync-first" data-l10n-id="sync-first"></p>
+        <p id="sync-learn"><a href="https://support.mozilla.org/kb/how-do-i-set-sync-my-computer" target="_blank" rel="noopener" data-l10n-id="sync-learn"></a></p>
+      </form>
+    </div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/newInstallPage.js
@@ -0,0 +1,68 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const PARAMS = new URL(location).searchParams;
+const ENTRYPOINT = "new-install-page";
+const SOURCE = `new-install-page-${PARAMS.get("channel")}`;
+const CAMPAIGN = "dedicated-profiles";
+const ENDPOINT = PARAMS.get("endpoint");
+const CONTEXT = "fx_desktop_v3";
+
+function appendAccountsParams(url) {
+  url.searchParams.set("entrypoint", ENTRYPOINT);
+  url.searchParams.set("utm_source", SOURCE);
+  url.searchParams.set("utm_campaign", CAMPAIGN);
+}
+
+function appendParams(url, params) {
+  appendAccountsParams(url);
+
+  for (let [key, value] of Object.entries(params)) {
+    url.searchParams.set(key, value);
+  }
+}
+
+async function requestFlowMetrics() {
+  let requestURL = new URL(`${ENDPOINT}metrics-flow`);
+  appendParams(requestURL, {
+    "form_type": "email",
+  });
+
+  let response = await fetch(requestURL, { credentials: "omit" });
+  if (response.status === 200) {
+    return response.json();
+  }
+
+  throw new Error(`Failed to retrieve metrics: ${response.status}`);
+}
+
+async function submitForm(event) {
+  // We never want to submit the form.
+  event.preventDefault();
+
+  let input = document.getElementById("sync-input");
+  input.disabled = true;
+  document.getElementById("sync-button").disabled = true;
+
+  let { flowId, flowBeginTime } = await metrics;
+
+  let requestURL = new URL(ENDPOINT);
+  appendParams(requestURL, {
+    "service": "sync",
+    "action": "email",
+    "utm_campaign": CAMPAIGN,
+    "email": input.value,
+    "flow_id": flowId,
+    "flow_begin_time": flowBeginTime,
+  });
+
+  window.open(requestURL, "_blank", "noopener");
+}
+
+// This must come before the CSP is set or it will be blocked.
+const metrics = requestFlowMetrics();
+
+document.addEventListener("DOMContentLoaded", () => {
+  document.getElementById("sync").addEventListener("submit", submitForm);
+}, { once: true });
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -112,15 +112,19 @@ browser.jar:
 *       content/browser/webrtcIndicator.xul           (content/webrtcIndicator.xul)
         content/browser/webrtcIndicator.js            (content/webrtcIndicator.js)
 #endif
 # the following files are browser-specific overrides
 *       content/browser/license.html                  (/toolkit/content/license.html)
 % override chrome://global/content/license.html chrome://browser/content/license.html
         content/browser/blockedSite.xhtml               (content/blockedSite.xhtml)
         content/browser/blockedSite.js                  (content/blockedSite.js)
+        content/browser/newInstall.xul                (content/newInstall.xul)
+        content/browser/newInstall.js                 (content/newInstall.js)
+        content/browser/newInstallPage.html           (content/newInstallPage.html)
+        content/browser/newInstallPage.js             (content/newInstallPage.js)
 
 % override chrome://global/content/netError.xhtml chrome://browser/content/aboutNetError.xhtml
 
 # L10n resources and overrides.
 % override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties
 % override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd
 % override chrome://mozapps/locale/downloads/settingsChange.dtd chrome://browser/locale/downloads/settingsChange.dtd
new file mode 100644
--- /dev/null
+++ b/browser/branding/aurora/content/horizontal-lockup.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2555.7 381.8"><style>.st1{fill:#061a58}.st4{fill:#2053cf}.st5{fill:#2660eb}.st6{fill:#306dff}.st7{fill:#235adb}.st10{fill:#1f51ca}.st14{fill:#12348d}.st16{fill:#0f2f82}.st17{fill:#05154c}.st18{fill:#163c9e}.st19{fill:#051753}.st20{fill:#061a57}.st21{fill:#081f63}.st22{fill:#123392}.st23{fill:#0c2674}.st24{fill:#081e5f}.st25{fill:#092167}.st26{fill:#163aa2}.st27{fill:#0e2b7e}.st28{fill:#1a45b2}.st29{fill:#133493}.st30{fill:#173fad}.st33{fill:#133595}.st34{fill:#163ba4}.st35{fill:#2257d7}.st39{fill:#0b2572}.st41{fill:#092064}.st42{fill:#030f41}.st44{fill:#0d2a78}.st45{fill:#11338b}.st46{fill:#153a99}.st50{fill:#1741aa}.st55{fill:#031043}.st56{fill:#05164e}.st57{fill:#091f64}.st60{fill:#061853}.st61{fill:#030f42}.st64{fill:#123390}.st66{fill:#11318c}.st70{fill:#05164f}.st74{fill:#031042}.st76{fill:#041245}.st109{opacity:.25}.st110{fill:#fff}</style><path d="M996 168.9h-21.8L951 209.4l-23.1-40.5h-22.7l34.2 52.2-38.6 58.3h21.8l27.5-46.8 27.1 46.8h23.3l-38.4-59.2 33.9-51.3zM530 279.5h19.3V168.9H530v110.6zm63-87.3l-1.9-23.3h-16.5v110.5H594v-57c0-17.2 12.6-36.3 26.4-36.3 3.3 0 6.5.4 9.7 1.3l3.6-18.9c-3.6-.8-7.2-1.3-10.9-1.3-13.4 0-23.7 8.4-29.6 24.9l-.2.1zm-156.3 87.2h19.9v-63.6h47.2v-15.7h-47.2v-49.3h54.5l2.3-15.9h-76.8l.1 144.5zm102.8-151.6c-8 0-13.4 5.7-13.4 13.2 0 7.3 5.5 13 13.4 13 8.2 0 13.6-5.7 13.6-13 .1-7.5-5.4-13.2-13.6-13.2zm312.7 39.5c-31.5 0-49.5 22.8-49.5 57 0 35 17.8 57.7 49.3 57.7 31.3 0 49.3-23.7 49.3-57.9 0-35.1-17.6-56.9-49.1-56.8zm-.2 99.1c-18.5 0-28.5-13.4-28.5-42.2 0-28.9 10.3-41.5 28.7-41.5 18.2 0 28.3 12.6 28.3 41.3 0 29-10.1 42.4-28.5 42.4zm-78.2-105.2c0-10.7 4.2-18.6 16.4-18.6 6.7 0 13.2 1.5 19.3 4.2l6.1-14c-8.8-3.8-15.9-5.7-26.4-5.7-22.5 0-34.6 14.1-34.6 32.6v9.2h-19.7v14.8h19.7v95.7h19.3v-95.7h24.8l2.1-14.9H774l-.2-7.6zm-92 6c-28.5 0-45.9 23.7-45.9 58.1 0 35 18 56.6 48.9 56.6 15.3 0 27.7-5.2 38.6-13.8l-8.4-11.5c-9.7 6.7-17.8 9.6-28.7 9.6-15.9 0-27.9-9.9-29.8-35.4H727c.2-2.5.4-6.1.4-9.9-.1-33.7-15.8-53.7-45.6-53.7zm26.5 49.3h-51.9c1.5-24.5 11.1-33.9 25.8-33.9 17.4 0 26 11.1 26 32.7l.1 1.2zm452-9.7c0 58.7-36.6 72.7-67.7 72.7h-34.5v-144h30.1c36.8 0 72.1 11.5 72.1 71.3zm-82.3 57.1h16.1c22.6 0 45.1-9.6 45.1-57.1 0-48.7-23.6-55.4-46.6-55.4H1078v112.5zm186.6-32.9h-70.2c1.9 25.5 13.8 35.3 29.7 35.3 10.9 0 19-2.9 28.6-9.6l8.4 11.5c-10.9 8.6-23.2 13.8-38.5 13.8-30.7 0-48.7-21.5-48.7-56.4 0-34.3 17.3-58.7 45.8-58.7 29.7 0 45.4 20.7 45.4 54.3-.1 3.8-.3 7.3-.5 9.8zm-18.6-15.4c0-21.5-8.6-33.4-25.9-33.4-14.6 0-24.2 10.2-25.7 34.7h51.6v-1.3zm59.1 63.9l-38-110.1h20.9l28.8 93 28.6-93h20.3l-37.4 110.1h-23.2zm153.5-48.5h-70.2c1.9 25.5 13.8 35.3 29.7 35.3 10.9 0 19-2.9 28.6-9.6l8.4 11.5c-10.9 8.6-23.2 13.8-38.5 13.8-30.7 0-48.7-21.5-48.7-56.4 0-34.3 17.3-58.7 45.8-58.7 29.7 0 45.4 20.7 45.4 54.3-.1 3.8-.3 7.3-.5 9.8zm-18.6-15.4c0-21.5-8.6-33.4-25.9-33.4-14.6 0-24.2 10.2-25.7 34.7h51.6v-1.3zm66 50.6c2.7 0 5-.4 7.1-1.3l5 13.4c-5.2 2.5-10.7 3.8-16.3 3.8-14 0-21.9-8.4-21.9-24.2V125.1l19.2-2.3v134.6c0 5.8 1.9 8.8 6.9 8.8zm114.5-41.8c0 34.1-18 57.7-49.1 57.7-31.4 0-49.1-22.6-49.1-57.5 0-34.1 18-57.7 49.3-57.7 31.4 0 48.9 22.6 48.9 57.5zm-77.5.2c0 28.6 10 42 28.4 42s28.4-13.4 28.4-42.2c0-28.6-10-42-28.2-42-18.4 0-28.6 13.3-28.6 42.2zm190.8-.2c0 33.4-14.8 57.7-43.3 57.7-11.9 0-21.7-4-28.8-12.5v52.2l-19.2 2.3V169.4h16.5l1.5 14.8c8.2-11.3 20.1-17.3 32.6-17.3 29.2 0 40.7 22.8 40.7 57.5zm-20.7 0c0-28.6-8.4-42.2-24.9-42.2-11.7 0-20.7 8.4-26.5 17.1V253c5.6 8.6 14.2 13.4 24.2 13.4 17.6 0 27.2-13.2 27.2-42zm126.5 6.6h-70.2c1.9 25.5 13.8 35.3 29.7 35.3 10.9 0 19-2.9 28.6-9.6l8.4 11.5c-10.9 8.6-23.2 13.8-38.5 13.8-30.7 0-48.7-21.5-48.7-56.4 0-34.3 17.3-58.7 45.8-58.7 29.7 0 45.4 20.7 45.4 54.3-.1 3.8-.3 7.3-.5 9.8zm-18.6-15.4c0-21.5-8.6-33.4-25.9-33.4-14.6 0-24.2 10.2-25.7 34.7h51.6v-1.3zm99.6-47.5l-3.6 18.8c-3.3-.8-5.6-1.3-9.6-1.3-13.8 0-21.7 9.8-26.3 30.9v62.9h-19.2v-110h16.5l1.9 22.4c5.9-16.5 16.1-24.9 29.5-24.9 3.5 0 7.5.4 10.8 1.2zm78.6-16.7v47h48.5v15.9h-48.5v49.3h59.8v15.9h-79.6v-144h78l-2.3 15.9h-55.9zm166.4 128.1h-16.9l-1.9-15.3c-7.1 11.1-18.2 17.8-31.8 17.8-26.8 0-41.6-22.8-41.6-57.1 0-33.6 16.3-58.1 43.3-58.1 13 0 22.4 5.9 29.7 13.6v-57.7l19.2 2.3v154.5zm-19.2-29v-53.9c-6.9-8.8-14.6-14.4-25.5-14.4-16.5 0-26.8 13.8-26.8 42.4 0 29.1 9.2 42.2 25.1 42.2 12.3 0 19.8-6.1 27.2-16.3zm72.1-109.5c0 7.3-5.4 13-13.6 13-7.9 0-13.4-5.6-13.4-13 0-7.5 5.4-13.2 13.4-13.2 8.1 0 13.6 5.6 13.6 13.2zm-23 138.5V169.4h19.2v110.1h-19.2zm80 2.5c-18.6 0-29.7-10.9-29.7-31.3v-66.5h-19.2v-14.8h19.2v-24.9l19.2-2.3v27.2h26.1l-2.1 14.8h-24v65.6c0 11.1 3.6 16.3 13.2 16.3 4.8 0 9.2-1.5 14.6-4.8l7.3 13.2c-7.2 5-15.2 7.5-24.6 7.5zm57.7-141c0 7.3-5.4 13-13.6 13-7.9 0-13.4-5.6-13.4-13 0-7.5 5.4-13.2 13.4-13.2 8.2 0 13.6 5.6 13.6 13.2zm-23 138.5V169.4h19.2v110.1h-19.2zm139.4-55.1c0 34.1-18 57.7-49.1 57.7-31.4 0-49.1-22.6-49.1-57.5 0-34.1 18-57.7 49.3-57.7 31.4 0 48.9 22.6 48.9 57.5zm-77.5.2c0 28.6 10 42 28.4 42s28.4-13.4 28.4-42.2c0-28.6-10-42-28.2-42-18.4 0-28.6 13.3-28.6 42.2zm183.3-24.1v79h-19.2v-76.3c0-16.5-6.7-21.3-17.3-21.3-12.3 0-20.7 7.7-28 19.4v78.2h-19.2V169.4h16.5l1.7 16.3c7.5-11.3 19.2-18.8 33.6-18.8 20.2 0 31.9 12.7 31.9 33.6z" fill="#363959"/><g id="Layer_1-2"><g id="Layer_2-2"><g id="Fox_Lines_-_Outlined"><path class="st1" d="M355.9 169l-7.8-26.2-22.5-42.6L299.8 73l-27.3-19.5-23.7-9.3L220.4 34l-46.9-3-29.5 5.4-24.1 7.4-42.8 25.1-26.7 29.9-24.7 40.1-9.7 34.2-1.8 51.8 9.3 35.4 20.3 40.2 14.4 19.4 19.9 18.3 24.8 16.9 30.7 13.3 51.2 6 26.8.8 39.6-11.2 35.4-19.5 22.7-19.5 17.7-21.1 25-49 5.5-33.5z"/><path fill="#133691" d="M174.4 54.6l-21 20.8 34.2 13.9z"/><path fill="#1943ae" d="M187.6 89.3l-13.2-34.7 38.7 3.1z"/><path class="st4" d="M187.6 89.3l66.3-2-40.8-29.6z"/><path class="st5" d="M253.9 87.3l-40.8-29.6 35.9-.2z"/><path class="st6" d="M253.9 87.3l37.7-2.4L249 57.5z"/><path class="st7" d="M291.6 84.9L249 57.5l21.2-2.3z"/><path class="st5" d="M291.6 84.9l-21.4-29.7L299.8 73z"/><path class="st4" d="M320 103l-28.4-18.1 8.2-11.9z"/><path fill="#1c4abd" d="M291.6 84.9l-4.7 20.3 25.9 10.4z"/><path fill="#1a46b4" d="M291.6 84.9L320 103l-7.2 12.6z"/><path class="st5" d="M253.9 87.3l37.7-2.4-4.7 20.3z"/><path class="st7" d="M253.9 87.3l33 17.9-24.5 28z"/><path class="st10" d="M253.9 87.3l-29.8 27.1 38.3 18.8z"/><path fill="#1f50c9" d="M187.6 89.3l66.3-2-29.8 27.1z"/><path class="st4" d="M262.4 133.2l24.5-28 18 35.1z"/><path fill="#1b48b9" d="M286.9 105.2l18 35.1 7.9-24.7z"/><path fill="#173fa5" d="M304.9 140.3l7.9-24.7 18.9 45.1z"/><path class="st14" d="M312.8 115.6l28.8 21.1-9.9 24z"/><path fill="#143895" d="M312.8 115.6l28.8 21.1L320 103z"/><path class="st16" d="M320 103l5.6-2.8 16 36.5z"/><path class="st17" d="M341.6 136.7l6.5 6.1-22.5-42.6z"/><path class="st18" d="M320 103l5.6-2.8L299.8 73z"/><path class="st19" d="M354.5 189.4l-6.4-46.6 7.8 26.2-1.5 20.4z"/><path class="st20" d="M341.6 136.7l12.9 52.7-6.4-46.6z"/><path class="st17" d="M331.7 160.7l22.8 28.7-12.9-52.7z"/><path class="st21" d="M354.4 189.4l1.5-20.4 1.6 52.4-3-32z"/><path class="st14" d="M350.7 230.8l6.8-9.4-3-32z"/><path class="st22" d="M352 254.9l-5.5 2.6 11-36.1z"/><path class="st23" d="M346.5 257.5l11-36.1-6.8 9.4z"/><path class="st21" d="M350.7 230.8l3.8-41.4-11.7 13.8z"/><path class="st16" d="M346.5 257.5l4.2-26.7-22.6 38z"/><path class="st20" d="M331.7 160.7l22.8 28.7-11.7 13.8z"/><path class="st24" d="M350.7 230.8l-7.9-27.6-11.9 34z"/><path class="st25" d="M328.1 268.8l22.6-38-19.8 6.4z"/><path class="st26" d="M327 303.9l25-49-5.5 2.6z"/><path class="st26" d="M327 303.9l19.5-46.4-18.2 33.8z"/><path class="st27" d="M328.3 291.3l18.2-33.8-18.4 11.3z"/><path class="st22" d="M309.3 325l19-33.7-1.3 12.6z"/><path class="st28" d="M285.3 336.1l24-11.1-22.7 19.5-1.3-8.4"/><path class="st22" d="M309.3 325l19-33.7-43 44.8z"/><path class="st29" d="M285.3 336.1l1.3 8.4-20.1 6.3z"/><path class="st28" d="M251.2 364l15.3-13.2 20.1-6.3z"/><path class="st28" d="M251.2 364l15.3-13.2-49.8 18.6z"/><path class="st28" d="M211.6 375.2l39.6-11.2-34.5 5.4z"/><path class="st28" d="M184.8 374.4l26.8.8 5.1-5.8z"/><path class="st10" d="M133.6 368.4l-3-4.7 54.2 10.7z"/><path class="st30" d="M133.6 368.4l-3-4.7-27.7-8.6zm-55.5-30.2l-19.9-18.3 44.7 35.2z"/><path fill="#1b47b5" d="M58.2 319.9l-14.4-19.4 9.6 3.2z"/><path fill="#163aa3" d="M58.2 319.9l44.7 35.2-14.6-18.7z"/><path class="st30" d="M58.2 319.9l30.1 16.5-34.9-32.7z"/><path class="st33" d="M88.3 336.4l42.3 27.3-27.7-8.6z"/><path class="st34" d="M88.3 336.4l42.3 27.3.7-16.1z"/><path class="st26" d="M153.7 358.1l-23.1 5.6.7-16.1z"/><path class="st4" d="M53.4 303.7l-9.6-3.2-6.3-28.7z"/><path class="st6" d="M43.8 300.5l-20.3-40.2 14 11.5z"/><path class="st10" d="M16.8 217l20.7 54.8-14-11.5"/><path class="st35" d="M23.5 260.3l-9.3-35.4 2.6-7.9z"/><path fill="#11318d" d="M16.8 217l-2.6 7.9 1.8-51.8z"/><path class="st23" d="M25.7 138.9l6.4-2.3L16 173.1z"/><path fill="#102f88" d="M16 173.1l12.2-2.3 3.9-34.2z"/><path class="st33" d="M16.8 217l-.8-43.9 12.2-2.3z"/><path fill="#12328e" d="M28.2 170.8l-.8 37.5-10.6 8.7z"/><path class="st39" d="M50.4 98.8l-18.3 37.8-6.4 2.3z"/><path fill="#020b2e" d="M50.4 98.8l23.4-22.4 3.3-7.5z"/><path class="st41" d="M77.1 68.9L104.9 55 73.8 76.4z"/><path class="st42" d="M77.1 68.9l42.8-25.1-15 11.2-27.8 13.9"/><path class="st24" d="M104.9 55L144 36.4l-24.1 7.4z"/><path fill="#081f61" d="M144 36.4l-15.3 16.5-23.8 2.1z"/><path class="st44" d="M128.7 52.9l36.4-14.6-21.1-1.9z"/><path class="st45" d="M173.5 31L144 36.4l21.1 1.9z"/><path class="st46" d="M201.5 37.9l-36.4.4 8.4-7.3"/><path class="st44" d="M173.5 31l46.9 3-18.9 3.9z"/><path fill="#2053ce" d="M201.5 37.9l18.9-3.9 28.4 10.2z"/><path fill="#1e4dc3" d="M270.2 55.2l-21.4-11 23.7 9.3z"/><path fill="#1e4fc7" d="M270.2 55.2l2.3-1.7L299.8 73z"/><path class="st28" d="M130.6 363.7l54.2 10.7-31.1-16.3z"/><path class="st26" d="M184.8 374.4l-31.1-16.3 30.4-5.7z"/><path class="st28" d="M216.7 369.4l-31.9 5-.7-22z"/><path class="st50" d="M216.7 369.4l-32.6-17 44.9-12.1z"/><path class="st28" d="M266.5 350.8l-49.8 18.6 12.3-29.1"/><path class="st29" d="M266.5 350.8l2.3-29.1-39.8 18.6z"/><path class="st26" d="M266.5 350.8l2.3-29.1 16.5 14.4z"/><path class="st29" d="M285.3 336.1l-16.5-14.4 39.9-26.6z"/><path class="st28" d="M285.3 336.1l23.4-41 19.6-3.8z"/><path class="st25" d="M328.3 291.3l-.2-22.5-19.4 26.3z"/><path fill="#2258d9" d="M248.8 44.2l.2 13.3 21.2-2.3"/><path class="st35" d="M249 57.5l-.2-13.3-47.3-6.3z"/><path class="st35" d="M201.5 37.9l11.6 19.8 35.9-.2z"/><path fill="#1a46b3" d="M201.5 37.9l11.6 19.8-38.7-3.1z"/><path fill="#163c9f" d="M165.1 38.3l9.3 16.3 27.1-16.7z"/><path fill="#103186" d="M128.7 52.9l36.4-14.6 9.3 16.3z"/><path class="st18" d="M153.4 75.4l-24.7-22.5 45.7 1.7z"/><path class="st55" d="M104.9 55L73.8 76.4l14.7 14.2z"/><path class="st42" d="M128.7 52.9L88.5 90.6 104.9 55z"/><path class="st56" d="M88.5 90.6l25.7 4.3 14.5-42z"/><path class="st41" d="M114.2 94.9l14.5-42 24.7 22.5z"/><path class="st42" d="M50.4 98.8l1.5 15.2 21.9-37.6z"/><path class="st57" d="M32.1 136.6l18.3-37.8 1.5 15.2z"/><path fill="#041345" d="M51.9 114l21.9-37.6 14.7 14.2z"/><path fill="#0a226b" d="M50.6 146.6l1.3-32.6-19.8 22.6z"/><path class="st57" d="M50.6 146.6l1.3-32.6 25.8 21z"/><path class="st60" d="M77.7 135l10.8-44.4L51.9 114z"/><path class="st61" d="M77.7 135l10.8-44.4 25.7 4.3z"/><path class="st39" d="M28.2 170.8l3.9-34.2 18.5 10z"/><path fill="#173da9" d="M37.5 271.8l-10.1-63.5-10.6 8.7z"/><path class="st28" d="M53.4 303.7l-15.9-31.9 17 2.4z"/><path class="st50" d="M37.5 271.8l17 2.4-11.9-28z"/><path fill="#10308b" d="M37.5 271.8l-10.1-63.5 15.2 37.9z"/><path class="st64" d="M53.4 303.7l18 3.4-16.9-32.9z"/><path class="st29" d="M53.4 303.7l34.9 32.7-16.9-29.3z"/><path class="st28" d="M88.3 336.4l12.7-12.2-29.6-17.1z"/><path class="st26" d="M88.3 336.4l43 11.2-30.3-23.4z"/><path class="st28" d="M131.3 347.6l22.4 10.5 30.4-5.7z"/><path class="st22" d="M131.3 347.6l14.6-30.6 38.2 35.4z"/><path fill="#102f87" d="M27.4 208.3l24.9-.1-24.1-37.4z"/><path class="st21" d="M28.2 170.8l24.1 37.4-1.7-61.6z"/><path class="st34" d="M27.4 208.3l15.2 37.9 9.7-38z"/><path class="st66" d="M42.6 246.2l9.7-38 12 30.1z"/><path class="st64" d="M54.5 274.2l9.8-35.9-21.7 7.9z"/><path class="st22" d="M54.5 274.2l46.3 28.7-36.5-64.6z"/><path class="st26" d="M71.4 307.1l29.4-4.2-46.3-28.7z"/><path class="st50" d="M101 324.2l-.2-21.3-29.4 4.2z"/><path class="st22" d="M101 324.2l-.2-21.3 45.1 14.1z"/><path class="st22" d="M101 324.2l30.3 23.4 14.6-30.6z"/><path fill="#0e2b80" d="M184.1 352.4l3.9-34.9-42.1-.5z"/><path fill="#143698" d="M184.1 352.4l3.9-34.9 41 22.8z"/><path fill="#0d2878" d="M229 340.3l-1.6-39.2-39.4 16.4z"/><path class="st22" d="M229 340.3l-1.6-39.2 41.4 20.6z"/><path class="st21" d="M227.4 301.1l25.6-23.3 15.8 43.9z"/><path class="st20" d="M268.8 321.7l19.3-58.8-35.1 14.9z"/><path class="st16" d="M308.7 295.1l-39.9 26.6 19.3-58.8z"/><path class="st25" d="M308.7 295.1l19.4-26.3-40-5.9z"/><path class="st70" d="M328.1 268.8l-40-5.9 42.8-25.7z"/><path class="st19" d="M342.8 203.2L315 183.3l15.9 53.9 11.9-34"/><path class="st55" d="M342.8 203.2l-11.1-42.5-16.7 22.6zm-54.7 59.7l42.8-25.7-41.5-18.5z"/><path class="st42" d="M288.1 262.9l-38.8-39.3 3.7 54.2z"/><path class="st25" d="M227.4 301.1l-29.8-26.4 55.4 3.1z"/><path class="st16" d="M227.4 301.1L188 317.5l9.6-42.8z"/><path class="st64" d="M145.9 317l42.1.5-52-34.1z"/><path fill="#0f2d84" d="M145.9 317l-9.9-33.6-35.2 19.5z"/><path class="st27" d="M100.8 302.9l6.6-34.1 28.6 14.6z"/><path fill="#0f2e86" d="M107.4 268.8l-43.1-30.5 36.5 64.6z"/><path class="st21" d="M188 317.5l9.6-42.8-61.6 8.7z"/><path fill="#0a236c" d="M52.3 208.2l12.3-39.6-14-22z"/><path class="st21" d="M50.6 146.6l14 22L77.7 135z"/><path class="st56" d="M77.7 135l54.3-15.9-17.8-24.2z"/><path class="st42" d="M77.7 135l54.3-15.9-28.7 58.7z"/><path class="st70" d="M288.1 262.9l-38.8-39.3 40.1-4.9z"/><path class="st61" d="M330.9 237.2L315 183.3l-25.6 35.4z"/><path class="st42" d="M253 277.8l-44.3-42.9 40.6-11.3z"/><path class="st70" d="M197.6 274.7l55.4 3.1-44.3-42.9z"/><path class="st74" d="M197.6 274.7l-30.3-24.8 41.4-15z"/><path fill="#0f2e82" d="M315 183.3l16.7-22.6-26.8-20.4z"/><path class="st60" d="M77.7 135l25.6 42.8-38.7-9.2z"/><path class="st21" d="M64.6 168.6l38.7 9.2-26.1 41.3z"/><path class="st24" d="M132 119.1l21.4-43.7-39.2 19.5z"/><path class="st57" d="M77.2 219.1l-12.6-50.5-12.3 39.6"/><path class="st66" d="M52.3 208.2l24.9 10.9-12.9 19.2z"/><path class="st27" d="M77.2 219.1l30.2 49.7-43.1-30.5z"/><path class="st25" d="M107.4 268.8l4-60.1-34.2 10.4z"/><path class="st20" d="M107.4 268.8l59.9-18.9-55.9-41.2z"/><path class="st70" d="M167.3 249.9l-5.8-46.5-50.1 5.3z"/><path class="st76" d="M208.7 234.9l-41.4 15-5.8-46.5z"/><path class="st61" d="M161.5 203.4l42.3-15.8 4.9 47.3z"/><path class="st25" d="M136 283.4l61.6-8.7-30.3-24.8z"/><path class="st23" d="M136 283.4l-28.6-14.6 59.9-18.9z"/><path fill="#0c2771" d="M132 119.1l36.7 3.9-15.3-47.6z"/><path class="st46" d="M187.6 89.3l-34.2-13.9 15.3 47.6z"/><path fill="#163da0" d="M187.6 89.3l36.5 25.1-55.4 8.6z"/><path class="st45" d="M168.7 123l54.3 28.9-19.2 35.7z"/><path fill="#163ca0" d="M223 151.9l1.1-37.5-55.4 8.6z"/><path class="st28" d="M223 151.9l1.1-37.5 38.3 18.8z"/><path fill="#0c266e" d="M168.7 123l35.1 64.6-61.5-30.9z"/><path fill="#071b5c" d="M161.5 203.4l42.3-15.8-61.5-30.9z"/><path class="st16" d="M203.8 187.6l54.2-10-35-25.7z"/><path class="st50" d="M262.4 133.2l-4.4 44.4-35-25.7z"/><path class="st46" d="M262.4 133.2l25.6 34.9-30 9.5z"/><path fill="#1945b1" d="M304.9 140.3l-42.5-7.1 25.6 34.9z"/><path fill="#12338c" d="M288 168.1l27 15.2-10.1-43z"/><path fill="#081e60" d="M289.4 218.7l25.6-35.4-27-15.2z"/><path class="st24" d="M289.4 218.7l-1.4-50.6-30 9.5z"/><path class="st20" d="M258 177.6l-8.7 46 40.1-4.9"/><path class="st74" d="M203.8 187.6l54.2-10-8.7 46z"/><path class="st70" d="M208.7 234.9l40.6-11.3-45.5-36z"/><path fill="#05174f" d="M132 119.1l10.3 37.6 26.4-33.7z"/><path class="st60" d="M132 119.1l-28.7 58.7 39-21.1z"/><path class="st42" d="M142.3 156.7l-30.9 52 50.1-5.3z"/><path class="st76" d="M111.4 208.7l30.9-52-39 21.1z"/><path class="st1" d="M77.2 219.1l26.1-41.3 8.1 30.9z"/><path d="M358.3 221.4l-1.5-52.5v-.2l-7.9-26.2v-.1l-22.6-42.6-25.7-27.2-27.3-19.5h-.2l-23.8-9.4-28.4-10.2h-.2l-46.8-3h-.2l-29.5 5.3h-.1L119.7 43h-.2L76.7 68.2 49.8 98.3l-24.7 40.2v.1l-9.7 34.2v.2l-1.9 51.9v.2l9.2 35.3v.1l20.4 40.2v.1l14.4 19.4 19.9 18.3h.1l24.8 16.8h.1l30.6 13.4h.2l51.2 6h.1l26.8.7h.3l39.6-11.2h.1l35.4-19.5h.1l22.8-19.5 17.7-21.1s0-.1.1-.1l25-49v-.1.1l5.5-33.5v-.1c.2.1.3.1.4 0zM78.6 337.6l-5.1-4.7L85 342l-6.4-4.4zm75.2 19.7l-18.1-8.5 42.8 3.9-24.7 4.6zm6.5-154.5l-47.5 5 29.3-49.4 18.2 44.4zm-16.5-44.5l58.1 29.2-39.9 15-18.2-44.2zm17 46l5.5 43.9-52.9-38.9 47.4-5zm4.8 45.3l-57.4 18.1 3.9-57.6 53.5 39.5zm-3.2-44.6l44.5 29.6-39 14.2-5.5-43.8zm.7-1.4l39.9-15 4.6 44.7-44.5-29.7zm61.4-51.6l37-17.5-4.2 41.7-32.8-24.2zm31.6 25.2l-50.9 9.3 18.1-33.5 32.8 24.2zm-52.3 8.7l-33.1-61 51.4 27.3-18.3 33.7zm-1.9-.1l-58.4-29.4 25.1-32 33.3 61.4zm-90.2 20.9l-7.4-28.5 36-19.5-28.6 48zm-5 59.6l-28.4-46.7 32.2-9.8-3.8 56.5zm58.1-14.8l-28.9 30.9-26.5-13.4 55.4-17.5zm42.7-15.4l-10.3 37.2-28.4-23.2 38.7-14zm-2.8-46.8l43 34-38.3 10.6-4.7-44.6zm.9-1.3l51.4-9.4-8.2 43.6-43.2-34.2zm53.3-11.5l4.1-41.2 23.8 32.4-27.9 8.8zm28.4-7.4l1.3 47.2-29.3-38.3 28-8.9zm-63.5-18.4l1-35 35.8 17.6-36.8 17.4zm-1.5-.1l-51.2-27.2 52.2-8.1-1 35.3zm-79.7 4.4l-9.6-35 34.2 3.6-24.6 31.4zm-1.2 1.3L105.1 176l26.7-54.7 9.6 35zm-31 51.9l-31.5 9.6 24.1-38 7.4 28.4zm-5.7 57.8l-39.3-28 11.7-17.5 27.6 45.5zm3.2 4l26.4 13.5-32.5 18 6.1-31.5zm59.5-19.1l28.4 23.2-57.7 8.2 29.3-31.4zm41.7-14.6L251 277l-52.3-3 10.4-37.7zm1.1-1.1l38.5-10.6 3.5 51.3-42-40.7zm48.2-55.7l29.6 38.6-37.7 4.6 8.1-43.2zm30.5-10.1l24.9 14.2-23.7 32.8-1.2-47zm.2-1.6l15.5-25.5 9.2 39.5-24.7-14zm-1.1-1.1l-23.8-32.4 39.5 6.5-15.7 25.9zm-62.6-52.5l27.9-25.4 8 43-35.9-17.6zm-55.2 7.8l17.7-31.5 34.2 23.5-51.9 8zm-37-3.5l20.1-41 14.4 44.6-34.5-3.6zm-30 57.7l-24.4-40.8 51.6-15.1-27.2 55.9zm-25.7 41l-11.8-47.5 36.4 8.6-24.6 38.9zm-13 19.4l-10.8-26.9 22.3 9.8-11.5 17.1zm42 32.6l-6.1 31.6L66.7 241l39.8 28.2zm29 15.4l9.2 31.3-42.1-13.1 32.9-18.2zm61.1-9l-9.2 40.7-49.3-32.3 58.5-8.4zm54.5 2.9L227.4 300l-27.6-24.5 51.3 3zm-.8-52.8l36.5 37-33.1 14.1-3.4-51.1zm.7-1.5l37.6-4.6-1.2 41.5-36.4-36.9zm63.7-39.1l15 50.8-39.1-17.4 24.1-33.4zm-8.6-42.9l24.5 18.6-15.3 20.7-9.2-39.3zm-42.2-9.5l22.8-26.1 16.8 32.7-39.6-6.6zm-1-1.2l-8-42.7 30.7 16.6-22.7 26.1zM224 113.4L190 90l61.8-1.9-27.8 25.3zm-55.1 7.7l-14.3-44.3 31.9 13-17.6 31.3zm-37.1-3.5l-16.5-22.4 36.4-18.1-19.9 40.5zm-1.1 1.1l-50.5 14.8 34-37.3 16.5 22.5zm-29 58l-36-8.6 12.2-31.4 23.8 40zM76 217.8l-22.8-10 11.3-36.3L76 217.8zm-12.7 20L43.7 245l8.7-34.3 10.9 27.1zm35.3 62.9l-43.2-26.8 9.1-33.6 34.1 60.4zm43.9 16.1l-40.8 6.5-.2-19.3 41 12.8zm-5.1-31.6l48 31.5-38.8-.4-9.2-31.1zm60.7-9.1l27.9 24.7-36.9 15.4 9-40.1zm54.6 3.1l14.7 40.9-38.6-19.2 23.9-21.7zm1.3-.9l32.9-14-18.1 55-14.8-41zm36.2-58.4l39.1 17.4-40.3 24.2 1.2-41.6zm26.1-34.6l25.6 18.2-10.9 31.2-14.7-49.4zm-.2-2.1l15.3-20.7 10.1 38.9-25.4-18.2zM305.8 140l7.1-22.2 17 40.5-24.1-18.3zm-1.1-1.7l-16.2-31.6 23.4 9.4-7.2 22.2zm-48.1-50.4l34-2.2-4.3 18.3-29.7-16.1zm-67.3.6l23.9-29.8 38.4 27.9-62.3 1.9zm-1.4-.7l-12.3-32.4 36 2.9-23.7 29.5zm-1.6.2l-31.5-12.8L174.1 56l12.2 32zm-70.8 5.4L129 54.2l23 21-36.5 18.2zm-36.3 38.8l9.9-40.7 23.5 3.9-33.4 36.8zm-14.7 34.7l-12.7-20 24.6-10.5-11.9 30.5zm-.7 1.9l-10.9 34.8-1.5-54.2 12.4 19.4zM51.3 209l-8.9 34.7L28.5 209h22.8zm-23.2-1.5l.7-34.1 22 34.1H28.1zm35.1 32l-8.8 32.4-10.8-25.2 19.6-7.2zm35.4 63l-26.7 3.8-15.3-29.9 42 26.1zm1.4 1.3l.2 19-26.5-15.3 26.3-3.7zm44.6 14.2L131 346.4l-28.2-21.8 41.8-6.6zm1.6.3l35.8 33.1-49.5-4.5 13.7-28.6zm1.8-.5l39.2.4-3.6 32.5-35.6-32.9zm78.7-15.6l1.5 36.7-38.4-21.4 36.9-15.3zm1.5.2l38.8 19.3-37.3 17.5-1.5-36.8zm60.2-37.7l19.3 30.2-37.4 24.9 18.1-55.1zm1.3-.8l37 5.5-18 24.4-19-29.9zm.7-1.4l39.6-23.8-2.6 29.2-37-5.4zm52.3-56.7l7 24.5-17.6 5.7 10.6-30.2zm-9.3-41.8l20.1 25.3-10.3 12.2-9.8-37.5zm-18.8-46.1l26.1 19.1-9 21.6-17.1-40.7zm-26.8-13.2l4.2-18 18.8 27.2-23-9.2zm-33.3-18.2L250 59.1l39.2 25.2-34.7 2.2zm-1.7-.9l-37.4-27.1 32.9-.2 4.5 27.3zm-76-31.6l24.4-15 10.5 17.8-34.9-2.8zm-23.4 20.4l-22.6-20.7 41.8 1.6-19.2 19.1zM113.6 94l-23.5-3.9L127 55.6 113.6 94zm-36.3 39.6l-24-19.5 34.1-21.7-10.1 41.2zm-1.1 1.2l-24.8 10.6 1.2-29.8 23.6 19.2zm-24.8 70.7l-22.3-34.6 20.7-22.4 1.6 57zm-9.6 40.8l-4.2 21.1-8.3-52.5 12.5 31.4zm1.1 2.5l10.5 24.4-14.9-2.1 4.4-22.3zM70 306.1l-15.7-3 1-25.8L70 306.1zm29.7 18.2l-11.2 10.8-15-25.9 26.2 15.1zm1.3.9l26.8 20.7-38-10 11.2-10.7zm87.6-6.5l38.3 21.3-41.9 11.4 3.6-32.7zm79.3 4.2l-2.1 26.8-34.6-9.6 36.7-17.2zm38.5-25.3L285.1 335l-15-13.2 36.3-24.2zm21-26.5l.1 19.6-17 3.3 16.9-22.9zm4.2-33.3l17.3-5.6-19.7 33.3 2.4-27.7zm12-34.4l9.8-11.6-3.2 34.8-6.6-23.2zm-11-42.8l8.8-21.3 11.4 46.9-20.2-25.6zm-18.8-45.2l6.2-10.8 18.7 29.1-24.9-18.3zm-1-1.3l-18.3-26.6 24.5 15.7-6.2 10.9zM251.2 58l18.6-2 18.8 26-37.4-24zm-37.7-1.1l-10.2-17.4 41.8 17.2-31.6.2zm-38.8-3.4L166.4 39l32.3-.3-24 14.8zm-1.7.2l-40.7-1.5 32.4-13 8.3 14.5zM90.9 87.4l14.5-31.6 21.1-1.9-35.6 33.5zm-2.6 1.9L75 76.5l28-19.2-14.7 32zm-1 1.2l-33.1 21.1 19.7-34 13.4 12.9zm-37.4 54.8l-16.6-8.9 17.8-20.2-1.2 29.1zm-.5 1.5l-20.2 21.8 3.5-30.8 16.7 9zm-22.8 61.1l-8.5 7 9.2-37.1-.7 30.1zm.2 1.8l8.7 54.7-17.8-47.2 9.1-7.5zm26.9 65.2l-1 25.7-13.9-27.8 14.9 2.1zm17.2 32.9L85 332.2l-29.1-27.3 15 2.9zm59.6 40.4l-.6 14.1-37.1-24 37.7 9.9zm1.6.6l19.3 9-19.9 4.9.6-13.9zm51.3 4.5l.6 19.8-28-14.7 27.4-5.1zm44.3-11.8l-11.4 26.8-30-15.7 41.4-11.1zm1.8-.3l34.5 9.6-45.9 17.2 11.4-26.8zm39.9-17.9l14.6 12.8-16.6 13 2-25.8zm39.8-27.5l16.9-3.2-37 38.6 20.1-35.4zm19.8-7.5l-.1-19.1 15.6-9.5-15.5 28.6zm1.4-21.8l18.9-31.8-3.5 22.4-15.4 9.4zm24.1-68.8l2.2 23.4-5 6.9 2.8-30.3zM343 139.1l4.3 4.1 4.3 31.4-8.6-35.5zm-21.9-35.8l4.1-2 11.7 26.7-15.8-24.7zm-28.4-18.7l7.1-10.2 17.5 25.9-24.6-15.7zm-1.1-1.1L273 57.8l25.7 15.5-7.1 10.2zm-41.9-26.9l-.1-11.2 17.9 9.3-17.8 1.9zm-1.5-.3l-40.9-16.9 40.8 5.4.1 11.5zm-81-18.8l6.5-5.6 21.6 5.3-28.1.3zm-35.6 13.4l12.7-13.7 17.5 1.6-30.2 12.1zm-3.2 1.2l-19.2 1.8 31.5-15.1-12.3 13.3zM75.6 74.2l2.1-4.8 17.6-8.8-19.7 13.6zm-23.1 37.3L51.2 99l19.2-18.3-17.9 30.8zm-1.4 2.2l-15.9 18.1 14.6-30.2 1.3 12.1zm-23.6 56.5l-10.1 1.9 13.4-30.5-3.3 28.6zm-.4 1.6L17.4 211l-.6-37.3 10.3-1.9zm8.6 97.6l-11.5-9.5-5.5-35.7 17 45.2zm16.2 32.9l-7.4-2.4-4.9-22.1 12.3 24.5zm31.4 30.5l-24.5-13.4-3.8-13.2 28.3 26.6zm4.5 4.2l10.6 13.6L66 325.1 87.8 337zm4 2.5l33.7 21.8-22-6.9-11.7-14.9zm61.7 19.4l26.2 13.7-45.5-9 19.3-4.7zm31.5-5.2l29.3 15.2-28.7 4.6-.6-19.8zm77.7-.7l-11.9 10.3-26.8 4.1 38.7-14.4zm22-15.4l1 6.4-15.4 4.8 14.4-11.2zm39.9-41.3l-15.8 28.1-20.1 9.3 35.9-37.4zm4.4-4.7l10.6-19.6-11.3 26.9.7-7.3zm22.4-60.5l4-5.5-6.4 21.2 2.4-15.7zm3.8-41.8l-.1-.4.4-5.2.4 13.6-.7-8zm-1-7.7l-4-29 5 16.5-1 12.5zm-11.9-45.3l-7.3-16.6 10.2 19.3-2.9-2.7zm-22-34.3l-14.6-21.7 18.6 19.7-4 2zm-48.7-46.8l1-.7 11.2 8-12.2-7.3zm-1.5-.9l-7.6-3.9 8.4 3.3-.8.6zm-64-16.6l14.2-2.9 21.4 7.6-35.6-4.7zm-4.6-.6l-19.4-4.8 32.4 2.1-13 2.7zm-36.7.4l-15.1-1.4 21.1-3.8-6 5.2zM111.6 51l8.6-6.5 13.9-4.3L111.6 51zm-7.1 3.4L97 58.1l11.5-6.8-4 3.1zM73.1 76l-8.9 8.5 10.2-11.4-1.3 2.9zm-41.6 60l-3.9 1.5 15.3-25L31.5 136zm-5.2 3.5l4.3-1.6-10.8 24.5 6.5-22.9zm-10.5 63.7l.2 13.7-.8 2.5.6-16.2zm.7 17.1l3.9 25.1-5.4-20.5 1.5-4.6zm20.3 52l5 22.4-15.9-31.4 10.9 9zm16 32l3.5 11.7-10.4-14 6.9 2.3zm77.3 60.1l1.4 2.3-15.1-6.6 13.7 4.3zm3.9 3.3l-1.8-2.8 32.9 6.5-31.1-3.7zm80.7 2.8l-3.5 3.9-18.1-.5 21.6-3.4zm2.4-.4l21.5-3.4-24.8 7 3.3-3.6zm49.8-18.7l12.6-3.9-22.3 12.3 9.7-8.4zm20.2-8.4l-1-6.4 18.5-8.5-17.5 14.9zm40-48l-.9 8.6-12.1 14.5 13-23.1zm20-36.9l3.1-1.5-14.3 28 11.2-26.5zm4.2-3.7l-3.5 1.7 7-23.2-3.5 21.5z" fill="#58c1ff" fill-opacity=".3"/><path d="M32 281.4c19.9 15.3 81.2 26.4 153.7 26.4s133.6-11.1 153.7-26.3" fill="none" stroke="#f9f9fa" stroke-width="2.323" stroke-linecap="round" stroke-miterlimit="10"/><linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-15216.367" y1="-9138.295" x2="-15114.453" y2="-9138.295" gradientTransform="matrix(.76 .03 -.05 1.12 11406.8 10825.68)"><stop offset="0" stop-color="#0083ff"/><stop offset=".18" stop-color="#0096f6"/><stop offset=".51" stop-color="#00b3e6"/><stop offset=".79" stop-color="#00c6dd"/><stop offset="1" stop-color="#00ccda"/></linearGradient><path d="M299.2 35.6c-9.3 10.8-13.6 35.1-4.2 59.7s23.8 19.3 32.8 44.4c11.9 33.1 6.3 77.7 6.3 77.7s14.3 41.3 24.2-2.6c22-82.4-59.1-159-59.1-179.2z" fill="url(#SVGID_1_)"/><linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="-871.641" y1="-403.589" x2="-945.754" y2="-646.038" gradientTransform="translate(1081.94 686.01)"><stop offset="0" stop-color="#000f40" stop-opacity=".6"/><stop offset=".61" stop-color="#000f40" stop-opacity=".18"/><stop offset="1" stop-color="#000f40" stop-opacity="0"/></linearGradient><path d="M314.8 311.2c-3.7 2.6-7.6 5-11.7 7 5.4-7.9 10.3-16 14.8-24.4 3.7-4.1 7-8 9.7-12.3 1.3-2.1 2.8-4.7 4.4-7.7 9.6-17.4 20.3-45.5 20.6-74.4v-2.2c.1-7.2-.7-14.5-2.2-21.6.1.6.1 1.1.2 1.7-.1-.4-.2-.9-.2-1.3.1.8.3 1.5.4 2.3 2 16.7.6 33.1-6.5 45.1-.1.2-.2.3-.3.5 3.6-18.3 4.9-38.5.8-58.7 0 0-1.6-9.8-13.7-39.7-7-17.2-19.3-31.2-30.2-41.5-9.6-11.8-18.2-19.7-23-24.8-10-10.5-14.2-18.4-15.9-23.6-1.5-.7-20.6-19.3-22.1-20-8.3 12.9-34.5 53.3-22.1 91 5.6 17.1 19.9 34.8 34.9 44.8.7.8 8.9 9.7 12.8 29.9 4 20.8 1.9 37.1-6.4 61.2-9.8 21.1-34.8 42-58.3 44.1-50.2 4.6-68.6-25.2-68.6-25.2 17.9 7.2 37.8 5.7 49.8-1.8 12.2-7.5 19.5-13.1 25.5-10.9 5.9 2.2 10.6-4.2 6.4-10.8-6.6-10.2-18.7-15.5-30.7-13.4-12.2 2-23.3 11.6-39.3 2.3-1-.6-2-1.3-3-2s3.4 1.1 2.4.3c-3.1-1.7-8.6-5.4-10-6.7-.2-.2 2.4.8 2.2.6-14.9-12.3-13-20.6-12.6-25.8.4-4.2 3.1-9.5 7.6-11.7 2.2 1.2 3.6 2.1 3.6 2.1s-.9-1.9-1.4-2.9c.2-.1.3-.1.5-.1 1.8.9 5.8 3.1 7.9 4.5 2.7 1.9 3.6 3.7 3.6 3.7s.7-.4.2-2.1c-.2-.7-1-2.9-3.7-5.1h.2c1.6.9 3.2 2 4.6 3.2.8-2.8 2.1-5.7 1.8-10.9-.2-3.7-.1-4.6-.7-6-.6-1.2.3-1.7 1.3-.4-.2-1-.5-1.9-.9-2.9v-.1c1.3-4.4 26.4-15.7 28.3-17 3-2.1 5.5-4.9 7.4-8.1 1.4-2.2 2.5-5.4 2.7-10.1.1-3.4-1.5-5.7-26.9-8.4-7-.7-11-5.7-13.4-10.4-.4-1-.9-1.9-1.3-2.8-.4-1.1-.7-2.2-1-3.3 4.2-12 11.2-22.1 21.4-29.7.6-.5-2.2.1-1.7-.4.7-.6 4.9-2.3 5.7-2.7 1-.5-4.2-2.7-8.8-2.1-4.7.5-5.7 1.1-8.2 2.1 1-1 4.3-2.4 3.6-2.4-5 .8-11.3 3.7-16.6 7 0-.6.1-1.2.3-1.7-2.5 1.1-8.6 5.3-10.4 9 .1-.7.1-1.4.1-2.1-1.9 1.6-3.6 3.4-5.1 5.4l-.1.1c-14.5-5.8-27.2-6.2-38-3.6-2.4-2.4-3.5-.6-8.9-12.4-.4-.7.3.7 0 0-.9-2.3.5 3 0 0-9 7.1-20.9 15.2-26.6 20.9-.1.2 6.6-1.9 0 0-2.3.7-2.2 2-2.5 14.5-.1.9 0 2-.1 2.9-4.5 5.8-7.6 10.7-8.8 13.2-5.9 10.1-12.4 25.9-18.6 50.9 2.8-6.8 6.1-13.3 10-19.5-5.2 13.3-10.3 34.1-11.3 66.2 1.3-6.6 2.9-13.2 4.9-19.6C22.4 227 27 253.6 37 278.2c3.6 8.8 9.6 22.2 19.7 36.9 32 33.6 77 54.6 126.9 54.6 52.3.5 99.1-22.3 131.2-58.5z" fill="url(#SVGID_2_)"/><linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="210.3" y1="-403.589" x2="136.183" y2="-646.038" gradientTransform="translate(0 686.01)"><stop offset="0" stop-color="#000f43" stop-opacity=".4"/><stop offset=".48" stop-color="#001962" stop-opacity=".17"/><stop offset="1" stop-color="#002079" stop-opacity="0"/></linearGradient><path d="M314.8 311.2c-3.7 2.6-7.6 5-11.7 7 5.4-7.9 10.3-16 14.8-24.4 3.7-4.1 7-8 9.7-12.3 1.3-2.1 2.8-4.7 4.4-7.7 9.6-17.4 20.3-45.5 20.6-74.4v-2.2c.1-7.2-.7-14.5-2.2-21.6.1.6.1 1.1.2 1.7-.1-.4-.2-.9-.2-1.3.1.8.3 1.5.4 2.3 2 16.7.6 33.1-6.5 45.1-.1.2-.2.3-.3.5 3.6-18.3 4.9-38.5.8-58.7 0 0-1.6-9.8-13.7-39.7-7-17.2-19.3-31.2-30.2-41.5-9.6-11.8-18.2-19.7-23-24.8-10-10.5-14.2-18.4-15.9-23.6-1.5-.7-20.6-19.3-22.1-20-8.3 12.9-34.5 53.3-22.1 91 5.6 17.1 19.9 34.8 34.9 44.8.7.8 8.9 9.7 12.8 29.9 4 20.8 1.9 37.1-6.4 61.2-9.8 21.1-34.8 42-58.3 44.1-50.2 4.6-68.6-25.2-68.6-25.2 17.9 7.2 37.8 5.7 49.8-1.8 12.2-7.5 19.5-13.1 25.5-10.9 5.9 2.2 10.6-4.2 6.4-10.8-6.6-10.2-18.7-15.5-30.7-13.4-12.2 2-23.3 11.6-39.3 2.3-1-.6-2-1.3-3-2s3.4 1.1 2.4.3c-3.1-1.7-8.6-5.4-10-6.7-.2-.2 2.4.8 2.2.6-14.9-12.3-13-20.6-12.6-25.8.4-4.2 3.1-9.5 7.6-11.7 2.2 1.2 3.6 2.1 3.6 2.1s-.9-1.9-1.4-2.9c.2-.1.3-.1.5-.1 1.8.9 5.8 3.1 7.9 4.5 2.7 1.9 3.6 3.7 3.6 3.7s.7-.4.2-2.1c-.2-.7-1-2.9-3.7-5.1h.2c1.6.9 3.2 2 4.6 3.2.8-2.8 2.1-5.7 1.8-10.9-.2-3.7-.1-4.6-.7-6-.6-1.2.3-1.7 1.3-.4-.2-1-.5-1.9-.9-2.9v-.1c1.3-4.4 26.4-15.7 28.3-17 3-2.1 5.5-4.9 7.4-8.1 1.4-2.2 2.5-5.4 2.7-10.1.1-3.4-1.5-5.7-26.9-8.4-7-.7-11-5.7-13.4-10.4-.4-1-.9-1.9-1.3-2.8-.4-1.1-.7-2.2-1-3.3 4.2-12 11.2-22.1 21.4-29.7.6-.5-2.2.1-1.7-.4.7-.6 4.9-2.3 5.7-2.7 1-.5-4.2-2.7-8.8-2.1-4.7.5-5.7 1.1-8.2 2.1 1-1 4.3-2.4 3.6-2.4-5 .8-11.3 3.7-16.6 7 0-.6.1-1.2.3-1.7-2.5 1.1-8.6 5.3-10.4 9 .1-.7.1-1.4.1-2.1-1.9 1.6-3.6 3.4-5.1 5.4l-.1.1c-14.5-5.8-27.2-6.2-38-3.6-2.4-2.4-3.5-.6-8.9-12.4-.4-.7.3.7 0 0-.9-2.3.5 3 0 0-9 7.1-20.9 15.2-26.6 20.9-.1.2 6.6-1.9 0 0-2.3.7-2.2 2-2.5 14.5-.1.9 0 2-.1 2.9-4.5 5.8-7.6 10.7-8.8 13.2-5.9 10.1-12.4 25.9-18.6 50.9 2.8-6.8 6.1-13.3 10-19.5-5.2 13.3-10.3 34.1-11.3 66.2 1.3-6.6 2.9-13.2 4.9-19.6C22.4 227 27 253.6 37 278.2c3.6 8.8 9.6 22.2 19.7 36.9 32 33.6 77 54.6 126.9 54.6 52.3.5 99.1-22.3 131.2-58.5z" fill="url(#SVGID_3_)"/><linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-8940.342" y1="-7428.904" x2="-8984.962" y2="-7432.35" gradientTransform="matrix(1.22 .12 -.12 1.22 10370.89 10444.24)"><stop offset="0" stop-color="#002275"/><stop offset="1" stop-color="#005fe7"/></linearGradient><path d="M276.3 335.7c63.1-7.3 91-72.3 55.1-73.6-32.4-1-85 77-55.1 73.6z" fill="url(#SVGID_4_)"/><linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="-8972.421" y1="-7486.118" x2="-8930.628" y2="-7529.526" gradientTransform="matrix(1.22 .12 -.12 1.22 10370.89 10444.24)"><stop offset=".05" stop-color="#005fe7"/><stop offset=".19" stop-color="#005ce1"/><stop offset=".38" stop-color="#0052cf"/><stop offset=".6" stop-color="#0043b2"/><stop offset=".84" stop-color="#002d8a"/><stop offset=".95" stop-color="#002275"/></linearGradient><path d="M336 248.8c43.3-25.2 32-79.8 32-79.8s-16.7 19.5-28.1 50.5c-11.2 30.7-30 44.6-3.9 29.3z" fill="url(#SVGID_5_)"/><linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="-9051.192" y1="-7388.813" x2="-8961.806" y2="-7388.603" gradientTransform="matrix(1.22 .12 -.12 1.22 10370.89 10444.24)"><stop offset="0" stop-color="#002079"/><stop offset=".99" stop-color="#000f40"/></linearGradient><path d="M198.2 367.9c60.5 19.3 112.5-28.3 80.4-44.3-29.1-14.3-109.1 35.2-80.4 44.3z" fill="url(#SVGID_6_)"/><radialGradient id="SVGID_7_" cx="227.125" cy="-536.783" r="290.975" fx="278.777" fy="-584.11" gradientTransform="translate(0 686.01)" gradientUnits="userSpaceOnUse"><stop offset=".16" stop-color="#58c1ff"/><stop offset=".24" stop-color="#4cb8ff"/><stop offset=".39" stop-color="#2ea0ff"/><stop offset=".54" stop-color="#0a84ff"/><stop offset=".76" stop-color="#0060df"/><stop offset=".8" stop-color="#0067de"/><stop offset=".85" stop-color="#007bdd"/><stop offset=".91" stop-color="#009cda"/><stop offset=".98" stop-color="#00c8d7"/></radialGradient><path d="M248.2 8.4l.2-.2c-.1 0-.1.1-.2.2zM340.5 272c1.5-2.1 3.5-8.7 5.2-11.7 10.7-17.2 10.8-31 10.8-31.3 6.5-32.2 5.9-45.4 1.9-69.7-3.2-19.6-17.2-47.7-29.3-61.2-12.5-13.9-3.7-9.4-15.8-19.6-10.6-11.7-20.8-23.3-26.4-28-40.4-33.8-39.5-40.9-38.7-42.2-.1.1-.3.4-.6.6-.5-1.9-.8-3.5-.8-3.5s-22.1 22.1-26.7 58.8c-3 24 6 49 19 65 6.8 8.3 14.4 15.8 22.8 22.5 9.8 14.1 15.2 31.5 15.2 50.3 0 46.9-38.1 85-85 85-6.4 0-12.8-.7-19-2.1-22.2-4.2-35-15.4-41.3-23-3.7-4.3-5.2-7.5-5.2-7.5 19.9 7.1 41.8 5.6 55.2-1.7 13.5-7.5 21.6-13 28.2-10.8 6.5 2.2 11.7-4.1 7-10.7-4.6-6.5-16.4-15.9-34-13.3-13.5 2-25.8 11.5-43.5 2.3-1.1-.6-2.2-1.2-3.3-1.9-1.2-.7 3.8 1 2.6.3-3.4-1.7-9.5-5.3-11.1-6.6-.3-.2 2.7.8 2.4.6-16.5-12.2-14.4-20.4-13.9-25.6.4-4.1 3.4-9.4 8.5-11.6 2.4 1.2 4 2.1 4 2.1s-1-1.9-1.6-2.9c.2-.1.4-.1.6-.1 2 .9 6.4 3.1 8.7 4.5 3 1.9 4 3.6 4 3.6s.8-.4.2-2.1c-.2-.7-1.1-2.9-4.1-5.1h.2c1.8.9 3.5 2 5.1 3.2.8-2.8 2.4-5.6 2-10.8-.2-3.6-.1-4.6-.8-6-.6-1.2.4-1.7 1.5-.4-.2-1-.5-1.9-.9-2.8v-.1c1.4-4.3 29.2-15.5 31.3-16.8 3.3-2 6.1-4.8 8.2-8 1.5-2.2 2.7-5.3 3-10 .1-2.1-.6-3.8-7.9-5.4-4.4-1-11.3-1.9-21.8-2.9-7.7-.7-12.2-5.7-14.8-10.3-.5-1-.9-1.9-1.4-2.8-.5-1-.8-2.1-1.1-3.2 4.6-12.1 12.9-22.4 23.7-29.4.6-.5-2.5.1-1.9-.4.7-.6 5.4-2.3 6.3-2.7 1.1-.5-4.6-2.6-9.7-2.1-5.2.5-6.3 1.1-9 2.1 1.2-1 4.8-2.4 3.9-2.4-5.6.8-12.5 3.7-18.4 7 0-.6.1-1.1.4-1.7-2.8 1-9.5 5.3-11.5 8.9.1-.7.1-1.4.1-2.1-2.1 1.5-4 3.3-5.7 5.3l-.1.1c-16-5.8-30.1-6.2-42-3.6-2.6-2.3-6.8-5.9-12.7-17.6-.4-.7-.6 1.5-.9.8-2.3-5.3-3.7-14.1-3.5-20.1 0 0-4.8 2.2-8.7 11.3-.7 1.6-1.2 2.5-1.7 3.4-.2.3.5-3 .4-2.8-.7 1.2-2.5 2.8-3.2 4.9-.5 1.5-1.3 2.4-1.8 4.4l-.1.2c0-.6.1-2.4 0-2-1.8 3.7-3.4 7.6-4.8 11.6-2.1 7-4.6 16.5-5 28.9-.1.9 0 2-.1 2.8-5 5.7-8.5 10.6-9.8 13.1-6.5 10-13.7 25.7-20.6 50.5 3.1-6.8 6.8-13.3 11-19.4-6 13.2-11.6 33.8-12.7 65.6 1.4-6.6 3.2-13.1 5.4-19.5-1 21.2 1.5 47.5 14.9 77.2 8 17.5 26.3 52.9 71.1 80.6 0 0 15.2 11.3 41.4 19.8 1.9.7 3.9 1.4 5.9 2.1-.6-.3-1.2-.5-1.8-.8 17.4 5.2 35.5 7.9 53.7 7.9 67.8.1 87.8-27.2 87.8-27.2l-.2.1c1-.9 1.9-1.8 2.8-2.8-10.7 10.1-35.1 10.8-44.2 10.1 15.6-4.6 25.8-8.4 45.8-16.1 2.3-.9 4.7-1.9 7.2-3l.8-.4c.5-.2 1-.4 1.5-.7 9.7-4.6 18.8-10.3 27.2-17 20-16 24.4-31.6 26.6-41.9-.3 1-1.3 3.3-2 4.8-5.2 11-16.6 17.8-29 23.6 5.9-7.8 11.4-15.9 16.4-24.2 3.9-3.8 5.1-10.1 8.2-14.3z" fill="url(#SVGID_7_)"/><linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="-7921.492" y1="-8758.143" x2="-7948.991" y2="-8449.04" gradientTransform="matrix(1.23 0 0 1.22 10047.12 10675.26)"><stop offset="0" stop-color="#00feff"/><stop offset=".3" stop-color="#04c9ff"/><stop offset=".61" stop-color="#0897ff"/><stop offset=".77" stop-color="#0a84ff"/><stop offset=".79" stop-color="#0a84ff" stop-opacity=".93"/><stop offset=".84" stop-color="#0a84ff" stop-opacity=".76"/><stop offset=".9" stop-color="#0a84ff" stop-opacity=".49"/><stop offset=".98" stop-color="#0a84ff" stop-opacity=".11"/><stop offset="1" stop-color="#0a84ff" stop-opacity="0"/></linearGradient><path d="M316.1 310.2c8.2-9 15.5-19.3 21-31 14.3-30 36.4-80 19-132.1-13.7-41.2-32.6-63.8-56.6-85.8C260.6 25.5 249.7 9.6 249.7.1c0 0-44.9 50.1-25.5 102.4s59.4 50.3 85.8 104.9c31.1 64.2-25.1 134.2-71.6 153.8 2.8-.6 103.4-23.4 108.6-80.9 0 1-2.3 16.9-30.9 29.9z" fill="url(#SVGID_8_)"/><linearGradient id="SVGID_9_" gradientUnits="userSpaceOnUse" x1="-9156.968" y1="-7557.312" x2="-9104.132" y2="-7557.312" gradientTransform="matrix(1.22 .12 -.12 1.22 10370.89 10444.24)"><stop offset="0" stop-color="#003eaa"/><stop offset=".3" stop-color="#0054c5" stop-opacity=".68"/><stop offset=".78" stop-color="#0076ef" stop-opacity=".19"/><stop offset="1" stop-color="#0083ff" stop-opacity="0"/></linearGradient><path d="M185.9 123.7c.2-3.4-1.6-5.7-29.7-8.3-11.6-1.1-16-11.7-17.3-16.2-4.1 10.7-5.8 21.9-4.9 35.4.6 8.9 6.6 18.4 9.4 24 0 0 .6-.8.9-1.1 5.4-5.6 27.9-14.1 30-15.3 2.3-1.5 11.2-8 11.6-18.5z" fill="url(#SVGID_9_)"/><linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="215.247" y1="-555.957" x2="139.744" y2="-557.118" gradientTransform="translate(0 686.01)"><stop offset="0" stop-color="#00feff" stop-opacity=".25"/><stop offset=".54" stop-color="#2ddfff" stop-opacity=".12"/><stop offset="1" stop-color="#58c1ff" stop-opacity="0"/></linearGradient><path d="M185.9 123.7c.2-3.4-1.6-5.7-29.7-8.3-11.6-1.1-16-11.7-17.3-16.2-4.1 10.7-5.8 21.9-4.9 35.4.6 8.9 6.6 18.4 9.4 24 0 0 .6-.8.9-1.1 5.4-5.6 27.9-14.1 30-15.3 2.3-1.5 11.2-8 11.6-18.5z" fill="url(#SVGID_10_)"/><linearGradient id="SVGID_11_" gradientUnits="userSpaceOnUse" x1="84.739" y1="-1958.197" x2="39.124" y2="-2055.699" gradientTransform="matrix(.99 .1 -.1 .99 -214.77 2052.6)"><stop offset=".19" stop-color="#002275" stop-opacity=".7"/><stop offset=".63" stop-color="#009dc2" stop-opacity=".36"/><stop offset=".94" stop-color="#00feff" stop-opacity=".1"/></linearGradient><path d="M62.3 60.7c-.4-.7-.6 1.5-.9.8-2.3-5.3-3.7-14-3.4-20.1 0 0-4.8 2.2-8.7 11.3-.7 1.6-1.2 2.5-1.7 3.4-.2.3.5-3 .4-2.8-.7 1.2-2.5 2.8-3.2 4.8-.6 1.6-1.3 2.5-1.8 4.6-.2.6.2-2.4 0-2.1-9.2 17.6-11 44.4-10 43.3C52.5 83 74.9 78.1 74.9 78.1c-2.3-1.8-7.5-6.9-12.6-17.4z" fill="url(#SVGID_11_)"/><linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="-209.991" y1="-293.211" x2="-173.556" y2="-251.828" gradientTransform="matrix(.99 .1 -.1 .99 264.37 492.18)"><stop offset="0" stop-color="#002275" stop-opacity=".8"/><stop offset=".07" stop-color="#002479" stop-opacity=".74"/><stop offset=".95" stop-color="#003eaa" stop-opacity="0"/></linearGradient><path d="M136.4 278.9c-27-11.5-57.7-27.8-56.5-64.7 1.6-48.6 45.4-39 45.4-39-1.7.4-6.1 3.5-7.6 6.9-1.7 4.2-4.7 13.7 4.5 23.6 14.4 15.6-29.5 36.9 38.2 77.3 1.6.8-16-.6-24-4.1z" fill="url(#SVGID_12_)"/><linearGradient id="SVGID_13_" gradientUnits="userSpaceOnUse" x1="-140.069" y1="-222.696" x2="-140.053" y2="-243.382" gradientTransform="matrix(.99 .1 -.1 .99 264.37 492.18)"><stop offset="0" stop-color="#002275" stop-opacity=".6"/><stop offset=".24" stop-color="#002275" stop-opacity=".34"/><stop offset=".49" stop-color="#002275" stop-opacity=".15"/><stop offset=".72" stop-color="#002275" stop-opacity=".04"/><stop offset=".93" stop-color="#002275" stop-opacity="0"/></linearGradient><path d="M126.8 254.7c19.1 6.7 41.4 5.5 54.8-1.9 8.9-5 20.4-12.9 27.5-11-6.1-2.4-10.7-3.5-16.3-3.8-.9 0-2.1 0-3.1-.1-2 0-4.1.1-6.1.3-3.4.3-7.3 2.5-10.7 2.1-.2 0 3.4-1.5 3.1-1.4-1.8.4-3.8.5-6 .7-1.3.2-2.5.3-3.8.4-39.9 3.4-73.6-21.6-73.6-21.6-2.9 9.7 12.8 28.8 34.2 36.3z" fill="url(#SVGID_13_)"/><linearGradient id="SVGID_14_" gradientUnits="userSpaceOnUse" x1="221.407" y1="-716.245" x2="340.787" y2="-449.297" gradientTransform="translate(0 686.01)"><stop offset="0" stop-color="#00feff"/><stop offset=".28" stop-color="#03dfff"/><stop offset=".9" stop-color="#0992ff"/><stop offset="1" stop-color="#0a84ff"/></linearGradient><path d="M316 310.5c40.3-39.6 60.7-87.7 52.1-141.7 0 0 3.4 27.7-9.6 56 6.3-27.6 7-62-9.7-97.6C326.6 79.7 290 54.7 276 44.3c-21.2-15.8-29.9-31.9-30.1-35.2-6.3 13-25.5 57.4-2.1 95.6 21.9 35.8 56.5 46.5 80.7 79.4 44.5 60.6-8.5 126.4-8.5 126.4z" fill="url(#SVGID_14_)"/><linearGradient id="SVGID_15_" gradientUnits="userSpaceOnUse" x1="264.593" y1="-519.737" x2="208.541" y2="-369.229" gradientTransform="translate(0 686.01)"><stop offset="0" stop-color="#0060df"/><stop offset=".09" stop-color="#0067e5" stop-opacity=".81"/><stop offset=".21" stop-color="#006eec" stop-opacity=".59"/><stop offset=".33" stop-color="#0075f2" stop-opacity=".41"/><stop offset=".46" stop-color="#007af7" stop-opacity=".26"/><stop offset=".58" stop-color="#007efa" stop-opacity=".15"/><stop offset=".71" stop-color="#0081fd" stop-opacity=".06"/><stop offset=".85" stop-color="#0082fe" stop-opacity=".02"/><stop offset="1" stop-color="#0083ff" stop-opacity="0"/></linearGradient><path d="M310.3 207.4c-14.1-29.1-31.7-41.8-48.3-55.6 1.9 2.7 2.4 3.7 3.5 5.4 14.6 15.6 36.2 53.7 20.6 101.5-29.5 90-147.5 47.6-159.9 35.7 5 52.1 92.1 77 148.9 43.2 32.1-30.5 58.2-82.5 35.2-130.2z" fill="url(#SVGID_15_)"/><linearGradient id="SVGID_16_" gradientUnits="userSpaceOnUse" x1="248.768" y1="-699.276" x2="290.577" y2="-346.786" gradientTransform="translate(0 686.01)"><stop offset="0" stop-color="#75feff"/><stop offset=".28" stop-color="#5adfff"/><stop offset=".9" stop-color="#1692ff"/><stop offset="1" stop-color="#0a84ff"/></linearGradient><path d="M316.2 265.8l.4-1.9c.1-.7.3-1.4.4-2.2.2-1.5.5-3.3.7-5.2.5-4.5.7-9 .6-13.6-.2-5.7-.9-11.4-2.2-16.9-1.5-6.5-3.8-12.9-6.7-18.9-3.3-6.7-7.1-13.2-11.5-19.3-2.2-3.2-4.8-6.3-7.4-9.3s-5.5-6-8.5-8.8-6.1-5.6-9.3-8.4c-3.2-2.7-6.5-5.4-9.8-8.1s-6.6-5.5-9.8-8.4c-3.2-2.9-6.3-5.9-9.2-9.1-2.9-3.2-5.6-6.5-8-10.1l-.9-1.3c-.3-.4-.6-.9-.9-1.3l-1.7-2.7c-.6-.9-1.1-1.8-1.6-2.8l-.8-1.4c-.3-.5-.5-.9-.7-1.4l-1.4-2.8c-.4-1-.9-1.9-1.3-2.9-.9-1.9-1.6-3.8-2.4-5.8l-.3-.7-.2-.7-.5-1.5c-.3-1-.7-1.9-1-2.9l-.8-3-.4-1.5-.3-1.5c-1.6-7.6-2.1-15.4-1.5-23.2.6-7 1.9-14 3.9-20.7 1.8-5.8 4-11.5 6.6-17.1 2.1-4.5 4.5-8.8 7-13.1 2.9-4.8 6.1-9.5 9.6-13.9 1-1.3 2-2.5 3-3.6.1-.1.4-.1.5 0 .1.1.2.3.1.5-.9 1.1-1.9 2.3-2.9 3.6-6.5 8.5-12.1 17.7-16.6 27.4-2.6 5.5-4.7 11.2-6.4 17-2 6.7-3.2 13.6-3.7 20.5-.5 7.6 0 15.3 1.7 22.8.1.2.1.5.1.7l.2.7.4 1.4c.3 1 .5 1.9.8 2.9l1 2.9.5 1.4c.2.5.4.9.5 1.4l1.1 2.8.3.7.3.7.6 1.4.6 1.4c.2.5.4.9.7 1.4l1.4 2.8c3.9 7.5 8.8 14.4 14.5 20.6 2.9 3.1 5.9 6.1 9.1 8.9 3.2 2.8 6.5 5.6 9.7 8.3 3.3 2.7 6.6 5.4 9.8 8.1s6.4 5.5 9.4 8.3c3 2.8 5.9 5.8 8.6 8.8 2.7 3 5.2 6.1 7.5 9.4 4.4 6.1 8.3 12.6 11.6 19.4l.3.6.3.6.6 1.2c.4.8.7 1.6 1.1 2.4l1 2.4.5 1.2c.2.4.3.8.4 1.2l.8 2.4c.1.4.3.8.4 1.2l.4 1.2c.2.8.5 1.5.7 2.3l.6 2.3c1.4 5.7 2.1 11.4 2.4 17.2.2 4.6 0 9.3-.5 13.9-.4 3.9-1 7.1-1.5 9.5-1 4.6-2.3 9.2-3.8 13.6-1.4 4.1-3.1 8.1-4.9 12-1.8 3.7-3.6 7.1-5.4 10.2l-.7 1.2-.7 1.1-1.3 2.1c-.9 1.4-1.8 2.7-2.6 3.9-1.7 2.5-3.3 4.6-4.8 6.5l-2 2.6-1.8 2.1-.8.9-.7.7c-.4.5-.8.9-1.1 1.2-.6.6-.9.9-1 .9 0 0 .2-.4.8-1.1.5-.7 1.4-1.7 2.4-3 2-2.7 5-6.5 8.3-11.6.8-1.3 1.7-2.6 2.5-4 .9-1.4 1.7-2.9 2.6-4.4 1.8-3.1 3.5-6.5 5.2-10.1.4-.9.9-1.8 1.3-2.8l1.2-2.9c.8-2 1.5-4 2.2-6.1.9-3.9 2.1-8.3 3.1-12.8z" fill="url(#SVGID_16_)"/><linearGradient id="SVGID_17_" gradientUnits="userSpaceOnUse" x1="285.773" y1="-528.204" x2="253.837" y2="-393.481" gradientTransform="translate(0 686.01)"><stop offset="0" stop-color="#9ee7ff"/><stop offset=".14" stop-color="#8cdbff" stop-opacity=".88"/><stop offset=".46" stop-color="#5ebdff" stop-opacity=".57"/><stop offset=".92" stop-color="#168cff" stop-opacity=".08"/><stop offset="1" stop-color="#0a84ff" stop-opacity="0"/></linearGradient><path d="M287.2 251.7c.2-.8.5-1.9.8-3.2.3-1.3.7-2.9 1-4.6.2-.9.3-1.8.5-2.8l.4-3.1.1-.8.1-.8.2-1.7c.1-.6.1-1.2.2-1.8l.1-1.8c.3-5.4.2-10.8-.4-16.2l-.1-1.1-.1-1.1-.3-2.2c-.1-.7-.2-1.4-.4-2.2-.1-.7-.2-1.5-.4-2.2l-.5-2.2c-.1-.4-.1-.7-.2-1.1l-.3-1.1-.5-2.2-.6-2.2c-1.6-5.6-3.7-11-6.1-16.3-2.2-4.6-4.7-9.1-7.6-13.3-1.3-1.9-2.6-3.6-3.7-5.1-1.2-1.5-2.3-2.8-3.3-3.9l-.7-.8-.2-.2s-.1-.1-.1-.2l-.1-.1-.2-.4-.8-1.5c-.5-.8-.9-1.4-1.1-1.8-.3-.5-.6-.9-.9-1.3-.1-.2 0-.4.1-.5.2-.1.4-.1.5.1.3.4.6.9.9 1.3.3.4.7 1 1.2 1.9l.9 1.5.2.4.1.1.2.2.7.8c2.6 2.9 5 6 7.2 9.2 2.9 4.3 5.5 8.7 7.7 13.4 2.5 5.3 4.6 10.8 6.3 16.4l.6 2.2.5 2.2.3 1.1.2 1.1.5 2.2c.2.7.3 1.5.4 2.2s.3 1.5.4 2.2l.3 2.2.2 1.1.1 1.1c.6 5.4.8 10.9.5 16.4-.2 4.4-.7 8.8-1.5 13.1-.3 1.8-.7 3.4-1 4.7-.3 1.4-.6 2.5-.8 3.3l-.6 2.4-.7 2.3-.7 2.2c-.2.7-.5 1.4-.7 2.1-1 2.7-2 5-2.9 6.9-1.8 3.8-3.2 5.9-3.4 5.8-.2-.1.9-2.4 2.5-6.3.8-1.9 1.7-4.3 2.5-7 .2-.7.4-1.4.7-2.1l.6-2.2.7-2.3.5-2.4z" fill="url(#SVGID_17_)"/><linearGradient id="SVGID_18_" gradientUnits="userSpaceOnUse" x1="252.249" y1="-692.427" x2="281.507" y2="-470.544" gradientTransform="translate(0 686.01)"><stop offset="0" stop-color="#00feff"/><stop offset=".14" stop-color="#0bf7ff" stop-opacity=".88"/><stop offset=".46" stop-color="#26e4ff" stop-opacity=".57"/><stop offset=".92" stop-color="#51c6ff" stop-opacity=".08"/><stop offset="1" stop-color="#58c1ff" stop-opacity="0"/></linearGradient><path d="M278.5 142.7c-.9-.7-2.1-1.7-3.6-2.9l-2.4-2c-.9-.7-1.8-1.5-2.7-2.4-4.7-4-9.1-8.3-13.3-12.9-5.2-5.7-9.8-11.9-13.8-18.6l-1.5-2.7-1.4-2.8-.4-.7c-.1-.2-.2-.5-.3-.7l-.6-1.4-.6-1.5-.6-1.5-.6-1.5c-.2-.5-.4-1-.5-1.5l-.9-3-.8-3.1c-.1-.5-.3-1-.4-1.5l-.3-1.5c-.8-4.1-1.4-8.3-1.6-12.5-.2-4.1-.1-8.1.2-12.2.3-3.8.8-7.6 1.4-11.3.7-3.6 1.4-7 2.3-10.1 1.4-5.2 3.1-10.4 5.1-15.4 1.6-3.9 2.8-6.6 3.3-7.6.3-.7.7-1.4 1-2.1.1-.2.3-.2.5-.1s.3.3.2.5c-.3.6-.6 1.3-1 2.1-.5 1.1-1.8 3.8-3.3 7.8-2 5-3.7 10.2-5 15.5-1.8 7-3 14.1-3.5 21.2-.3 4-.3 8-.1 12 .3 4.1.8 8.2 1.7 12.3l.3 1.5.2.8.2.8.8 3 1 3c.1.5.3 1 .5 1.5l.6 1.5c.2.5.4 1 .6 1.4l.6 1.4.6 1.4c.1.2.2.5.3.7l.3.7 1.4 2.7 1.5 2.6c3.9 6.5 8.5 12.6 13.6 18.2 4.2 4.5 8.6 8.7 13.2 12.7 1 .8 1.9 1.6 2.7 2.3l2.4 2c1.5 1.2 2.7 2.2 3.7 3l10.4 8.2c3.1 2.5 5.9 4.7 8.1 6.7l3 2.6c.9.8 1.5 1.5 2.1 2 1.2 1.1 1.7 1.8 1.7 1.8-.1.1-.8-.4-2.1-1.4l-5.5-4.3-8.3-6.4c-3.2-2.7-6.7-5.3-10.4-8.3z" fill="url(#SVGID_18_)"/><linearGradient id="SVGID_19_" gradientUnits="userSpaceOnUse" x1="257.596" y1="-679.064" x2="411.738" y2="-438.07" gradientTransform="translate(0 686.01)"><stop offset="0" stop-color="#00feff"/><stop offset=".14" stop-color="#01efff" stop-opacity=".88"/><stop offset=".46" stop-color="#04caff" stop-opacity=".57"/><stop offset=".92" stop-color="#098eff" stop-opacity=".08"/><stop offset="1" stop-color="#0a84ff" stop-opacity="0"/></linearGradient><path d="M359.4 155.5c-.2-.7-.4-1.6-.7-2.5-.3-1-.6-2-.9-3.2l-.6-1.8c-.2-.6-.4-1.3-.7-2l-1.5-4.4-.8-2.4-1-2.5-1-2.6c-.4-.9-.7-1.8-1.1-2.7l-2.4-5.6c-.9-1.9-1.8-3.9-2.8-6l-.4-.8-.4-.8-.8-1.5-1.6-3.1c-.3-.5-.6-1-.9-1.6l-.9-1.5-1.8-3.2-2-3.2-1-1.6c-.2-.3-.3-.5-.5-.8l-.5-.8c-1.4-2.1-2.8-4.3-4.4-6.4-.8-1.1-1.5-2.1-2.3-3.2l-2.4-3.2c-.4-.5-.8-1.1-1.2-1.6l-1.3-1.5-2.6-3.1-2.7-3c-.5-.5-.9-1-1.4-1.5l-1.4-1.5c-.9-1-1.8-2-2.8-2.9l-2.9-2.9c-.9-1-1.9-1.9-2.9-2.8-1-.9-1.9-1.9-2.9-2.8l-5.8-5.4c-3.9-3.6-7.7-7.1-11.3-10.6-7.2-6.9-13.9-13.6-19.4-19.7-5.5-6.1-9.9-11.7-12.7-16.3-1.2-1.9-2.2-3.9-3.1-5.9-.5-1.2-.9-2.3-1.2-3.6-.2-.9-.4-1.9-.4-2.8 0-.2-.2-.3-.4-.3s-.4.1-.4.3c0 1 .2 2 .4 3 .3 1.3.7 2.6 1.2 3.8.9 2.1 2 4.2 3.2 6.1 1.7 2.7 3.5 5.2 5.4 7.7 2.2 2.8 4.6 5.7 7.4 8.8 5.5 6.2 12.1 12.9 19.3 19.8 3.6 3.5 7.4 7 11.2 10.6l5.8 5.4 2.9 2.8c1 .9 1.9 1.9 2.8 2.8l2.8 2.9 2.7 2.9 1.4 1.5c.4.5.9 1 1.3 1.5l2.6 3 2.5 3.1 1.3 1.5 1.2 1.5L327 92c.8 1 1.5 2.1 2.3 3.2 1.5 2.1 2.9 4.2 4.3 6.3.7 1 1.3 2.1 2 3.2l1.9 3.1 1.8 3.2c.6 1 1.2 2.1 1.7 3.1l1.6 3.1.8 1.5c.3.5.5 1 .7 1.5 3.4 7 6.4 14.3 9 21.6l1.6 4.4.3 1 .3 1 .6 1.8c.4 1.2.7 2.2.9 3.2.3 1 .5 1.8.7 2.6l.3 1.1.3 1.1.5 2.2c.3 1.4.6 2.8.8 4.2l.4 2 .3 2c.2 1.3.4 2.6.5 3.9.6 5 .9 9.5 1 13.3.2 7.5-.1 11.9.2 11.9.1 0 .3-1.1.5-3.2s.4-5.1.4-9c.4-10-.8-20-3.3-29.8z" fill="url(#SVGID_19_)"/><linearGradient id="SVGID_20_" gradientUnits="userSpaceOnUse" x1="293.073" y1="-506.202" x2="122.499" y2="-373.183" gradientTransform="translate(0 686.01)"><stop offset="0" stop-color="#9ee7ff"/><stop offset=".14" stop-color="#8cdbff" stop-opacity=".88"/><stop offset=".46" stop-color="#5ebdff" stop-opacity=".57"/><stop offset=".92" stop-color="#168cff" stop-opacity=".08"/><stop offset="1" stop-color="#0a84ff" stop-opacity="0"/></linearGradient><path d="M224.5 279.9c.5-.2 1.2-.5 1.9-.8l2.4-1.1 2.8-1.4.8-.4.8-.4 1.6-.9c1.1-.6 2.3-1.4 3.5-2.1 1.2-.8 2.4-1.6 3.7-2.5.6-.4 1.3-.9 1.9-1.4l1.9-1.5 1.9-1.7c.7-.6 1.3-1.1 1.9-1.7l1.9-1.9c.7-.6 1.3-1.3 1.9-2l1-1c.3-.3.6-.7.9-1.1l1.9-2.2c2.5-3.1 4.9-6.3 7-9.7 2.2-3.6 4.1-7.3 5.7-11.1 1.6-3.9 3-7.9 4.1-12 1-4.1 1.8-8.2 2.2-12.4.2-2.1.4-4.1.4-6.2.1-2 0-4 0-6-.1-3.8-.5-7.6-1.1-11.4-.3-1.8-.7-3.6-1-5.3l-.6-2.5c-.2-.8-.4-1.6-.7-2.4l-.3-1.2c-.1-.4-.2-.8-.4-1.1l-.7-2.2-.8-2.1-.4-1-.4-1-.8-1.9c-.3-.6-.5-1.2-.8-1.7-.2-.6-.5-1.1-.7-1.6l-.7-1.5c-1.3-2.5-2.6-4.9-4.2-7.2l-1.3-1.9c-.1-.1 0-.3.1-.4l.2-.1h-.2l.2-.1c.1-.1.3 0 .4.1l1.3 1.9c1.6 2.4 3 4.9 4.3 7.5 2.5 4.9 4.6 10.1 6.1 15.4.5 1.6.9 3.2 1.3 4.9l.5 2.6c.1.4.2.9.3 1.3l.2 1.4c.6 3.8 1 7.6 1.2 11.5l.1 3v3c0 2.1-.2 4.1-.4 6.2-1.6 16.8-8.2 32.8-19 45.8l-1.9 2.2c-.3.4-.6.7-.9 1.1l-1 1c-.7.7-1.3 1.4-1.9 2l-1.9 1.9c-.3.3-.6.6-1 .9l-1 .9-1.9 1.7-2 1.5c-.6.5-1.3 1-1.9 1.5l-1.9 1.3c-.3.2-.6.4-.9.7l-.9.6c-1.2.8-2.4 1.5-3.5 2.2l-1.7 1-.8.5c-.3.1-.5.3-.8.4l-2.9 1.5c-.9.4-1.7.8-2.4 1.1s-1.4.6-1.9.9c-4.2 1.7-8.5 3.1-12.9 4.1-1 .2-2 .4-2.9.6l-2.8.5-1.3.2-1.3.1-2.3.2c-.7.1-1.5.1-2.1.1l-1.9.1c-1.2.1-2.2 0-3 0-1.6-.1-2.5-.1-2.5-.2s.9-.2 2.6-.4c.8-.1 1.8-.1 3-.3l1.9-.2 1-.1c.4 0 .7-.1 1.1-.2 1.5-.2 3.1-.4 4.8-.8l2.7-.5 2.8-.7c4.2-.8 8.4-2.2 12.4-3.9z" fill="url(#SVGID_20_)"/><linearGradient id="SVGID_21_" gradientUnits="userSpaceOnUse" x1="139.844" y1="-596.216" x2="17.781" y2="-503.105" gradientTransform="translate(0 686.01)"><stop offset="0" stop-color="#00feff"/><stop offset=".14" stop-color="#01efff" stop-opacity=".88"/><stop offset=".46" stop-color="#04caff" stop-opacity=".57"/><stop offset=".92" stop-color="#098eff" stop-opacity=".08"/><stop offset="1" stop-color="#0a84ff" stop-opacity="0"/></linearGradient><path d="M137.2 101.8c.4-1.1.8-2.3 1.4-3.8.5-1.4 1.2-3 2.1-4.8 2-4.2 4.5-8.2 7.4-11.9 3.8-4.9 8.4-9.1 13.5-12.6.7-.5 1.5-.9 2.2-1.4l.6-.3.2-.1h.1l.1-.1h-.2c-.9-.2-1.8-.3-2.7-.5-3.8-.6-7.7-.7-11.5-.3-3.5.3-6.9 1.1-10.2 2.2-1.8.6-3.6 1.4-5.3 2.3-1.8.9-3.6 2-5.4 3-1.8 1-3.7 2.2-5.5 3.4-.5.3-.9.6-1.4.9-.5.3-.9.6-1.4.9-.9.6-1.8 1.3-2.6 2.1l-.6.6-.1.1-.2.2c0 .1-.1.1-.1.2h-.2l-.3.1c-.1 0-.2.1-.2.1-.1 0-.2.1-.3 0l-.6-.2-.8-.3c-1.1-.4-2.2-.7-3.3-1.1-2.2-.7-4.5-1.3-6.8-1.8l-.3-.1-.3-.1c-3.8-.8-7.6-1.4-11.5-1.6-3.9-.2-7.8.1-11.7.9-1.9.3-3.9.8-5.8 1.2-1.9.5-3.8 1-5.7 1.6-3.8 1.2-7.5 2.7-11.1 4.3-7 3.3-13.6 7.5-19.4 12.6-2.8 2.5-5.4 5.1-7.7 8-.6.7-1.1 1.4-1.7 2.1s-1 1.5-1.5 2.2l-1.5 2.2c-.5.7-1 1.4-1.5 2.2l-2.7 4.3-2.5 4.3-1.2 2.1-1.1 2.1c-.4.7-.7 1.4-1.1 2.1l-1 2c-1.3 2.7-2.4 5.3-3.5 7.7-1 2.5-2 4.8-2.8 7S8 150 7.3 151.9c-.7 1.8-1.2 3.5-1.7 5l-.6 2-.5 1.7c-.3 1-.5 1.9-.7 2.4-.4 1.3-.7 2.6-1 3.9-.1.2-.3.3-.4.2-.2 0-.3-.2-.3-.4.3-1.2.6-2.5 1-3.9.2-.6.4-1.5.7-2.6l.5-1.8.7-2.1c.2-.8.5-1.5.8-2.4.3-.9.6-1.7.9-2.7.7-1.9 1.4-3.9 2.2-6.1.9-2.2 1.8-4.6 2.8-7.1l1.7-3.8c.3-.7.6-1.3.9-2l1-2 1-2.1 1.1-2.1 1.1-2.1c.2-.4.4-.7.6-1.1l.6-1.1c.8-1.4 1.6-2.9 2.5-4.3l2.7-4.4c.2-.4.5-.7.7-1.1l.7-1.1 1.5-2.2.8-1.1.8-1.1.8-1.1c.3-.4.6-.7.9-1.1 2.4-2.9 5-5.6 7.8-8.1 2.9-2.5 6-4.9 9.2-7 3.3-2.1 6.8-4.1 10.4-5.7 7.2-3.4 14.9-5.9 22.8-7.3 2-.3 3.9-.6 5.9-.8.5 0 1-.1 1.5-.1s1 0 1.5-.1c1 0 2 0 3 .1 3.9.2 7.8.8 11.6 1.6l.3.1.3.1c2.3.5 4.6 1.1 6.9 1.8 1.1.3 2.2.7 3.3 1.1l.8.3.2.1h.1v-.1l.1-.1.2-.2.6-.6c.9-.8 1.8-1.5 2.7-2.2.5-.3.9-.7 1.4-1 .5-.3.9-.6 1.4-.9 1.9-1.2 3.7-2.3 5.6-3.4 1.8-1.1 3.7-2.1 5.5-3.1 1.8-.9 3.6-1.7 5.5-2.4 3.4-1.2 7-2 10.6-2.3 3.9-.4 7.9-.3 11.8.3.9.1 1.9.3 2.8.5.1 0 .7.1 1.1.2l1.2.3 1.1.3.6.2h.2l-.3.1-1.3.6-.3.1c-.1.1-.2.1-.3.1l-.5.3-.5.3-.3.1-.1.1h-.1l-.6.3c-.4.2-.7.4-1.1.7-.4.2-.7.4-1.1.7-5 3.4-9.4 7.5-13.1 12.2-2.8 3.6-5.3 7.5-7.2 11.6-.9 1.7-1.5 3.3-2.1 4.7-.6 1.4-1 2.7-1.4 3.8-1.3 3.8-2.3 7.7-3.1 11.6-.7 3.6-1.2 7.3-1.4 10.9-.2 3.2-.1 6.5.4 9.7l.2 1.1c.1.4.1.7.2 1.1.2.7.3 1.4.5 2.1s.4 1.3.6 2c.2.6.4 1.3.6 1.9 1.3 3.8 2.8 7.5 4.5 11.1 1.2 2.5 1.9 3.9 1.8 4s-1-1.3-2.4-3.8c-2-3.6-3.7-7.4-5.1-11.2-.4-1.2-.9-2.5-1.3-3.9l-.6-2.1c-.2-.7-.3-1.5-.4-2.3-.5-3.3-.6-6.6-.4-10 .2-3.7.7-7.4 1.4-11 .6-3.3 1.6-7.1 2.9-10.9z" fill="url(#SVGID_21_)"/><g class="st109"><path class="st110" d="M355.3 254.6l1.1-2.4 1.5-3.6 1.8-4.5 1-2.6c.3-.9.7-1.8 1-2.8.7-1.9 1.4-3.9 2-6.1.3-1.1.7-2.1 1-3.2l.9-3.4c.3-1.1.6-2.3.8-3.5l.4-1.8c.1-.6.3-1.2.4-1.8l.7-3.6.2-.9.1-.9.3-1.8c.4-2.4.6-4.8.9-7.2.2-2.4.4-4.7.4-7 .2-4.5.1-8.8-.1-12.5-.2-4.7-.8-9.4-1.6-14-.1-.4-.2-.9-.3-1.3 0-.2.1-.3.3-.4.2-.1.4.1.4.2.1.4.2.9.3 1.3.9 4.7 1.5 9.5 1.7 14.3.1.9.1 1.9.2 2.9l.1 3.1c0 2.1 0 4.3-.1 6.6 0 2.3-.2 4.6-.4 7-.2 2.4-.4 4.8-.8 7.2l-.2 1.8-.1.9-.2.9-.6 3.6c-.1.6-.2 1.2-.4 1.8l-.4 1.8c-.3 1.2-.5 2.3-.8 3.5l-.9 3.4c-.1.6-.3 1.1-.5 1.6l-.5 1.6-.5 1.6-.2.8-.3.8-1 3c-1.9 5.5-4 10.9-6.4 16.1-4.6 10.1-9.2 16.9-9.5 16.6-.2 0 3.8-7.3 8.3-17.1z"/></g><g class="st109"><path class="st110" d="M302.7 335c1.9-1.2 6.5-3.9 12.1-7.9l2.1-1.5 2.2-1.7 1.1-.9 1.1-.9 2.3-1.8 2.3-1.9c.8-.7 1.5-1.3 2.3-2 .7-.7 1.4-1.4 2.2-2.1.7-.7 1.4-1.5 2-2.3 1.3-1.5 2.5-3.1 3.7-4.8 1.1-1.6 2.1-3.2 3.1-4.9 1.7-3 3.2-6.2 4.4-9.5 1.4-3.7 2.4-7.5 3.1-11.4.1-.4.1-.7.2-1.1 0-.2.2-.3.4-.3s.3.2.3.4c0 .3-.1.7-.1 1.1-.6 4-1.6 7.9-3 11.7-1.2 3.3-2.6 6.5-4.3 9.6-.9 1.7-2 3.4-3.1 5-1.1 1.7-2.4 3.3-3.7 4.9-.7.8-1.4 1.5-2 2.3-.7.8-1.4 1.5-2.2 2.2-.8.7-1.5 1.4-2.3 2.1l-2.2 2-2.3 1.9-1.1.9-1.1.9-2.2 1.7c-.7.5-1.4 1.1-2.1 1.5-5.5 4-10.2 6.9-12.2 8.1-8 4.8-14.4 7.2-14.6 6.8-.1-.2 1.4-1.1 3.9-2.5 2.4-1.3 5.9-3.3 9.7-5.6z"/></g><g class="st109"><path class="st110" d="M201.6 380.1l2.6-.1 3.8-.2c2.9-.2 6.4-.5 10.3-1 .5-.1 1-.1 1.5-.2l1.5-.2 3.2-.5 3.3-.6 3.3-.6 3.4-.7 1.7-.4c.6-.1 1.1-.3 1.7-.4l3.4-.9c.3-.1.6-.1.9-.2l.8-.3 1.7-.5c1.1-.4 2.3-.7 3.4-1.1l3.3-1.2 1.6-.6 1.6-.6 3.1-1.3c3.7-1.6 7.4-3.4 10.9-5.4 2.6-1.5 5.1-3.3 7.4-5.2 1.2-1 2.2-2.1 3.1-3.4.1-.2.2-.4.3-.5.1-.2.2-.3.3-.5.1-.2.3-.2.5-.2.2.1.3.2.2.5l-.3.6c-.1.2-.2.4-.4.6-.9 1.3-2 2.6-3.3 3.6-2.3 2-4.8 3.9-7.5 5.4-3.5 2.1-7.2 3.9-10.9 5.6l-3.1 1.3-1.6.7-1.6.6-3.3 1.2c-1.1.4-2.2.8-3.4 1.1l-1.7.6-.8.3-.9.2-3.4 1c-.6.2-1.1.3-1.7.5l-1.7.4-3.4.8c-1.1.2-2.2.4-3.3.7l-3.3.6-3.2.5c-1 .2-2 .3-3 .4-3.9.5-7.5.8-10.4 1.1l-3.8.3-2.7.1c-10.8.5-18.7-.4-18.7-.9s8.1-.5 18.6-1z"/></g><linearGradient id="SVGID_22_" gradientUnits="userSpaceOnUse" x1="57.363" y1="-636.918" x2="51.478" y2="-585.698" gradientTransform="translate(0 686.01)"><stop offset="0" stop-color="#00feff"/><stop offset=".14" stop-color="#01efff" stop-opacity=".88"/><stop offset=".46" stop-color="#04caff" stop-opacity=".57"/><stop offset=".92" stop-color="#098eff" stop-opacity=".08"/><stop offset="1" stop-color="#0a84ff" stop-opacity="0"/></linearGradient><path d="M61.3 62.2l-.7-1.2c-.3-.5-.6-1.1-.9-1.8-.8-1.7-1.4-3.4-1.8-5.2-.6-2.5-1-5-1.2-7.6-.1-.7-.1-1.5-.1-2.3v-.8l-.2.2c-.6.7-1.2 1.3-1.8 2-1.6 1.8-3.3 3.9-5 6.2-1.7 2.3-3.4 4.7-5 7.3-.8 1.3-1.6 2.6-2.3 4l-1.1 2c-.2.3-.3.7-.5 1l-.5 1c-1.3 2.7-2.3 5.6-3.1 8.5-.8 2.8-1.4 5.6-1.9 8.1s-.8 4.9-1.1 7c-.5 4.1-.7 7-.7 7.9l-.1 1.3c0 .2-.2.3-.4.3s-.4-.1-.4-.3v-1.3c0-1 .2-4 .7-8.1.2-2.1.6-4.4 1-7s1.1-5.3 1.8-8.2c.8-3 1.8-5.8 3-8.6l.5-1.1.2-.5.3-.5 1.1-2.1c.8-1.4 1.5-2.7 2.3-4 1.6-2.6 3.3-5.1 5-7.4 1.7-2.3 3.4-4.4 4.9-6.3l1.8-2.1.3-.3.4-.5.8-.9.8-.9.4-.4.1-.1v.5c0 .8-.1 1.7-.1 2.5v1.7c0 .8.1 1.5.1 2.2.2 2.5.6 4.9 1.2 7.3.4 1.7 1 3.3 1.7 4.9.1.3.3.6.4.9l.4.8.7 1.2c1.4 2.3 2.9 4.3 4.3 6.2s2.6 3.4 3.6 4.7c2.1 2.6 3.3 4.2 3.1 4.4-.2.1-1.7-1.2-4-3.7-1.2-1.3-2.5-2.8-3.9-4.6-1.3-2.1-2.8-4.1-4.1-6.3z" fill="url(#SVGID_22_)"/><linearGradient id="SVGID_23_" gradientUnits="userSpaceOnUse" x1="114.237" y1="-522.178" x2="76.679" y2="-447.062" gradientTransform="translate(0 686.01)"><stop offset="0" stop-color="#00feff"/><stop offset=".14" stop-color="#01efff" stop-opacity=".88"/><stop offset=".46" stop-color="#04caff" stop-opacity=".57"/><stop offset=".92" stop-color="#098eff" stop-opacity=".08"/><stop offset="1" stop-color="#0a84ff" stop-opacity="0"/></linearGradient><path d="M80.4 226.1c-.1-.6-.3-1.4-.4-2.5-.1-.5-.2-1.1-.2-1.7l-.2-1.9c-.3-3.3-.3-6.6 0-9.9.4-4.1 1.2-8.2 2.4-12.1.7-2.1 1.5-4.2 2.6-6.2 2.1-4 5-7.6 8.6-10.4l1.3-1c.4-.3.9-.6 1.4-.9l.7-.4c.2-.1.5-.3.7-.4l1.4-.8c3.5-1.8 7.2-3 11.1-3.4 2.9-.3 5.8-.4 8.7-.2 2.3.2 3.8.4 4.4.5.4.1.8.1 1.2.2.2.1.3.3.2.5-.1.1-.2.2-.4.2-.4-.1-.8-.1-1.2-.2-.7-.1-2.2-.3-4.5-.5-2.9-.2-5.7-.1-8.6.3-3.7.5-7.4 1.7-10.7 3.5-.9.5-1.8 1-2.7 1.7-.4.3-.9.6-1.3.9l-1.3 1c-3.4 2.7-6.2 6.2-8.2 10.1-1 1.9-1.8 3.9-2.4 6-1.1 3.8-1.9 7.8-2.2 11.8-.3 3.2-.3 6.4 0 9.6l.2 1.9c.1.6.1 1.1.2 1.6.2 1 .3 1.8.4 2.4.5 2.5 1.1 4.7 1.7 6.7.6 2 1.2 3.8 1.7 5.2 1.1 2.9 1.8 4.6 1.6 4.7-.2.1-1.3-1.5-2.7-4.4-.8-1.7-1.5-3.4-2.1-5.2-.3-1-.7-2.1-1-3.2s-.1-2.2-.4-3.5z" fill="url(#SVGID_23_)"/></g></g></g></svg>
\ No newline at end of file
--- a/browser/branding/aurora/content/jar.mn
+++ b/browser/branding/aurora/content/jar.mn
@@ -10,8 +10,9 @@ browser.jar:
   content/branding/about-wordmark.svg
   content/branding/icon16.png                    (../default16.png)
   content/branding/icon32.png                    (../default32.png)
   content/branding/icon48.png                    (../default48.png)
   content/branding/icon64.png                    (../default64.png)
   content/branding/icon128.png                   (../default128.png)
   content/branding/identity-icons-brand.svg
   content/branding/aboutDialog.css
+  content/branding/horizontal-lockup.svg
new file mode 100644
--- /dev/null
+++ b/browser/branding/nightly/content/horizontal-lockup.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1646.4 381.8"><style>.st0{fill:#363959}</style><path class="st0" d="M996 168.9h-21.8L951 209.4l-23.1-40.5h-22.7l34.2 52.2-38.6 58.3h21.8l27.5-46.8 27.1 46.8h23.3l-38.4-59.2 33.9-51.3zM530 279.5h19.3V168.9H530v110.6zm63-87.3l-1.9-23.3h-16.5v110.5H594v-57c0-17.2 12.6-36.3 26.4-36.3 3.3 0 6.5.4 9.7 1.3l3.6-18.9c-3.6-.8-7.2-1.3-10.9-1.3-13.4 0-23.7 8.4-29.6 24.9l-.2.1zm-156.3 87.2h19.9v-63.6h47.2v-15.7h-47.2v-49.3h54.5l2.3-15.9h-76.8l.1 144.5zm102.8-151.6c-8 0-13.4 5.7-13.4 13.2 0 7.3 5.5 13 13.4 13 8.2 0 13.6-5.7 13.6-13 .1-7.5-5.4-13.2-13.6-13.2zm312.7 39.5c-31.5 0-49.5 22.8-49.5 57 0 35 17.8 57.7 49.3 57.7 31.3 0 49.3-23.7 49.3-57.9 0-35.1-17.6-56.9-49.1-56.8zm-.2 99.1c-18.5 0-28.5-13.4-28.5-42.2 0-28.9 10.3-41.5 28.7-41.5 18.2 0 28.3 12.6 28.3 41.3 0 29-10.1 42.4-28.5 42.4zm-78.2-105.2c0-10.7 4.2-18.6 16.4-18.6 6.7 0 13.2 1.5 19.3 4.2l6.1-14c-8.8-3.8-15.9-5.7-26.4-5.7-22.5 0-34.6 14.1-34.6 32.6v9.2h-19.7v14.8h19.7v95.7h19.3v-95.7h24.8l2.1-14.9H774l-.2-7.6zm-92 6c-28.5 0-45.9 23.7-45.9 58.1 0 35 18 56.6 48.9 56.6 15.3 0 27.7-5.2 38.6-13.8l-8.4-11.5c-9.7 6.7-17.8 9.6-28.7 9.6-15.9 0-27.9-9.9-29.8-35.4H727c.2-2.5.4-6.1.4-9.9-.1-33.7-15.8-53.7-45.6-53.7zm26.5 49.3h-51.9c1.5-24.5 11.1-33.9 25.8-33.9 17.4 0 26 11.1 26 32.7l.1 1.2z"/><g id="Layer_1-2_1_"><g id="Layer_2-2_1_"><g id="Firefox"><linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-15667.935" y1="9459.303" x2="-15565.798" y2="9459.303" gradientTransform="matrix(.76 .03 .05 -1.12 11733.87 11198.95)"><stop offset="0" stop-color="#0083ff"/><stop offset=".1" stop-color="#0092f8"/><stop offset=".31" stop-color="#00abeb"/><stop offset=".52" stop-color="#00bee1"/><stop offset=".75" stop-color="#00c8dc"/><stop offset="1" stop-color="#00ccda"/></linearGradient><path d="M298.8 35.5c-9.3 10.8-13.6 35.2-4.2 59.9s23.9 19.3 32.9 44.5c11.9 33.2 6.4 77.8 6.4 77.8s14.3 41.4 24.2-2.6c22-82.5-59.3-159.2-59.3-179.6z" fill="url(#SVGID_1_)"/><radialGradient id="_Path__2_" cx="-7705.698" cy="9049.584" r="306.995" gradientTransform="matrix(1.23 0 0 -1.22 9720.16 11130.11)" gradientUnits="userSpaceOnUse"><stop offset=".02" stop-color="#005fe7"/><stop offset=".18" stop-color="#0042b4"/><stop offset=".32" stop-color="#002989"/><stop offset=".4" stop-color="#002079"/><stop offset=".47" stop-color="#131d78"/><stop offset=".66" stop-color="#3b1676"/><stop offset=".75" stop-color="#4a1475"/></radialGradient><path id="_Path__1_" d="M185 377.5c95.2 0 172.2-77.5 172.2-173s-77.1-173-172.1-173S13 108.8 13 204.4c-.2 95.7 77 173.1 172 173.1z" fill="url(#_Path__2_)"/><linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="208.796" y1="783.075" x2="134.518" y2="1026.051" gradientTransform="matrix(1 0 0 -1 1 1066)"><stop offset="0" stop-color="#000f43" stop-opacity=".4"/><stop offset=".48" stop-color="#001962" stop-opacity=".17"/><stop offset="1" stop-color="#002079" stop-opacity="0"/></linearGradient><path d="M314.5 311.8c-3.7 2.6-7.6 5-11.7 7.1 5.4-7.9 10.3-16 14.9-24.4 3.7-4.1 7-8 9.8-12.3 1.3-2.1 2.8-4.7 4.4-7.7 9.7-17.4 20.3-45.6 20.6-74.6v-2.2c.1-7.3-.7-14.5-2.2-21.6.1.6.1 1.1.2 1.7-.1-.4-.2-.9-.2-1.3.1.8.3 1.6.4 2.3 2 16.8.6 33.1-6.5 45.2-.1.2-.2.3-.3.5 3.7-18.3 4.9-38.6.8-58.8 0 0-1.6-9.8-13.7-39.7-7-17.2-19.3-31.3-30.3-41.6-9.6-11.9-18.3-19.8-23.1-24.9-10-10.5-14.2-18.5-15.9-23.6-1.5-.7-20.6-19.3-22.1-20-8.3 12.9-34.6 53.4-22.1 91.2 5.7 17.1 20 34.9 34.9 44.9.7.8 8.9 9.7 12.8 29.9 4.1 20.9 1.9 37.2-6.4 61.3-9.8 21.1-34.9 42.1-58.5 44.2-50.3 4.6-68.7-25.3-68.7-25.3 18 7.2 37.9 5.7 49.9-1.8 12.2-7.5 19.6-13.1 25.5-10.9 5.9 2.2 10.6-4.2 6.4-10.8-6.7-10.2-18.8-15.5-30.8-13.4-12.2 2-23.4 11.6-39.3 2.3-1-.6-2-1.3-3-2-1.1-.7 3.4 1.1 2.4.3-3.1-1.7-8.6-5.4-10-6.7-.2-.2 2.4.8 2.2.6-14.9-12.3-13.1-20.6-12.6-25.8.4-4.2 3.1-9.5 7.7-11.7 2.2 1.2 3.6 2.1 3.6 2.1s-.9-1.9-1.5-2.9c.2-.1.3-.1.5-.1 1.8.9 5.8 3.1 7.9 4.5 2.7 1.9 3.6 3.7 3.6 3.7s.7-.4.2-2.1c-.2-.7-1-2.9-3.7-5.1h.2c1.6.9 3.2 2 4.6 3.2.8-2.8 2.1-5.7 1.8-10.9-.2-3.7-.1-4.6-.7-6-.6-1.2.3-1.7 1.3-.4-.2-1-.5-2-.9-2.9v-.1c1.3-4.4 26.5-15.7 28.3-17 3-2.1 5.5-4.9 7.4-8.1 1.4-2.2 2.5-5.4 2.7-10.1.1-3.4-1.5-5.7-27-8.4-7-.7-11.1-5.7-13.4-10.4-.4-1-.9-1.9-1.3-2.8-.4-1.1-.7-2.2-1-3.3 4.2-12 11.2-22.1 21.5-29.8.6-.5-2.2.1-1.7-.4.7-.6 4.9-2.3 5.7-2.7 1-.5-4.2-2.7-8.8-2.1-4.7.5-5.7 1.1-8.2 2.1 1-1 4.3-2.4 3.6-2.4-5 .8-11.3 3.7-16.7 7 0-.6.1-1.2.3-1.7-2.5 1.1-8.6 5.4-10.4 9 .1-.7.1-1.4.1-2.1-1.9 1.6-3.6 3.4-5.1 5.4l-.1.1c-14.5-5.8-27.2-6.2-38-3.6-2.4-2.4-3.5-.6-8.9-12.4-.4-.7.3.7 0 0-.9-2.3.5 3.1 0 0-9 7.1-20.9 15.2-26.6 20.9-.1.2 6.7-1.9 0 0-2.3.7-2.2 2-2.5 14.5-.1.9 0 2-.1 2.9-4.6 5.8-7.7 10.7-8.8 13.3-5.9 10.2-12.4 26-18.7 51 2.8-6.8 6.1-13.3 10-19.5-5.2 13.3-10.3 34.2-11.3 66.3 1.3-6.6 2.9-13.2 4.9-19.7-1.2 26.7 3.4 53.3 13.5 78 3.6 8.9 9.6 22.3 19.8 37 32 33.7 77.2 54.7 127.2 54.7 52.1 0 99-22.8 131.2-59.1z" fill="url(#SVGID_2_)"/><linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="209.796" y1="783.075" x2="135.518" y2="1026.051" gradientTransform="matrix(1 0 0 -1 0 1066)"><stop offset="0" stop-color="#000f43" stop-opacity=".4"/><stop offset=".48" stop-color="#001962" stop-opacity=".17"/><stop offset="1" stop-color="#002079" stop-opacity="0"/></linearGradient><path d="M314.5 311.8c-3.7 2.6-7.6 5-11.7 7.1 5.4-7.9 10.3-16 14.9-24.4 3.7-4.1 7-8 9.8-12.3 1.3-2.1 2.8-4.7 4.4-7.7 9.7-17.4 20.3-45.6 20.6-74.6v-2.2c.1-7.3-.7-14.5-2.2-21.6.1.6.1 1.1.2 1.7-.1-.4-.2-.9-.2-1.3.1.8.3 1.6.4 2.3 2 16.8.6 33.1-6.5 45.2-.1.2-.2.3-.3.5 3.7-18.3 4.9-38.6.8-58.8 0 0-1.6-9.8-13.7-39.7-7-17.2-19.3-31.3-30.3-41.6-9.6-11.9-18.3-19.8-23.1-24.9-10-10.5-14.2-18.5-15.9-23.6-1.5-.7-20.6-19.3-22.1-20-8.3 12.9-34.6 53.4-22.1 91.2 5.7 17.1 20 34.9 34.9 44.9.7.8 8.9 9.7 12.8 29.9 4.1 20.9 1.9 37.2-6.4 61.3-9.8 21.1-34.9 42.1-58.5 44.2-50.3 4.6-68.7-25.3-68.7-25.3 18 7.2 37.9 5.7 49.9-1.8 12.2-7.5 19.6-13.1 25.5-10.9 5.9 2.2 10.6-4.2 6.4-10.8-6.7-10.2-18.8-15.5-30.8-13.4-12.2 2-23.4 11.6-39.3 2.3-1-.6-2-1.3-3-2-1.1-.7 3.4 1.1 2.4.3-3.1-1.7-8.6-5.4-10-6.7-.2-.2 2.4.8 2.2.6-14.9-12.3-13.1-20.6-12.6-25.8.4-4.2 3.1-9.5 7.7-11.7 2.2 1.2 3.6 2.1 3.6 2.1s-.9-1.9-1.5-2.9c.2-.1.3-.1.5-.1 1.8.9 5.8 3.1 7.9 4.5 2.7 1.9 3.6 3.7 3.6 3.7s.7-.4.2-2.1c-.2-.7-1-2.9-3.7-5.1h.2c1.6.9 3.2 2 4.6 3.2.8-2.8 2.1-5.7 1.8-10.9-.2-3.7-.1-4.6-.7-6-.6-1.2.3-1.7 1.3-.4-.2-1-.5-2-.9-2.9v-.1c1.3-4.4 26.5-15.7 28.3-17 3-2.1 5.5-4.9 7.4-8.1 1.4-2.2 2.5-5.4 2.7-10.1.1-3.4-1.5-5.7-27-8.4-7-.7-11.1-5.7-13.4-10.4-.4-1-.9-1.9-1.3-2.8-.4-1.1-.7-2.2-1-3.3 4.2-12 11.2-22.1 21.5-29.8.6-.5-2.2.1-1.7-.4.7-.6 4.9-2.3 5.7-2.7 1-.5-4.2-2.7-8.8-2.1-4.7.5-5.7 1.1-8.2 2.1 1-1 4.3-2.4 3.6-2.4-5 .8-11.3 3.7-16.7 7 0-.6.1-1.2.3-1.7-2.5 1.1-8.6 5.4-10.4 9 .1-.7.1-1.4.1-2.1-1.9 1.6-3.6 3.4-5.1 5.4l-.1.1c-14.5-5.8-27.2-6.2-38-3.6-2.4-2.4-3.5-.6-8.9-12.4-.4-.7.3.7 0 0-.9-2.3.5 3.1 0 0-9 7.1-20.9 15.2-26.6 20.9-.1.2 6.7-1.9 0 0-2.3.7-2.2 2-2.5 14.5-.1.9 0 2-.1 2.9-4.6 5.8-7.7 10.7-8.8 13.3-5.9 10.2-12.4 26-18.7 51 2.8-6.8 6.1-13.3 10-19.5-5.2 13.3-10.3 34.2-11.3 66.3 1.3-6.6 2.9-13.2 4.9-19.7-1.2 26.7 3.4 53.3 13.5 78 3.6 8.9 9.6 22.3 19.8 37 32 33.7 77.2 54.7 127.2 54.7 52.1 0 99-22.8 131.2-59.1z" fill="url(#SVGID_3_)"/><linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-8672.42" y1="7683.359" x2="-8717.133" y2="7686.809" gradientTransform="matrix(1.22 .12 .12 -1.22 10013.36 10723.12)"><stop offset="0" stop-color="#812cc9"/><stop offset="1" stop-color="#005fe7"/></linearGradient><path d="M275.9 336.3c63.2-7.3 91.2-72.4 55.2-73.7-32.4-1.1-85.1 77.1-55.2 73.7z" fill="url(#SVGID_4_)"/><linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="-8704.525" y1="7740.822" x2="-8662.642" y2="7784.325" gradientTransform="matrix(1.22 .12 .12 -1.22 10013.36 10723.12)"><stop offset=".05" stop-color="#005fe7"/><stop offset=".18" stop-color="#065de6"/><stop offset=".35" stop-color="#1856e1"/><stop offset=".56" stop-color="#354adb"/><stop offset=".78" stop-color="#5d3ad1"/><stop offset=".95" stop-color="#812cc9"/></linearGradient><path d="M335.7 249.3c43.5-25.3 32.1-80 32.1-80s-16.8 19.5-28.2 50.6c-11.2 30.8-30.1 44.7-3.9 29.4z" fill="url(#SVGID_5_)"/><linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="-8783.499" y1="7643.23" x2="-8693.914" y2="7643.02" gradientTransform="matrix(1.22 .12 .12 -1.22 10013.36 10723.12)"><stop offset="0" stop-color="#002079"/><stop offset=".99" stop-color="#a238ff"/></linearGradient><path d="M197.7 368.6c60.6 19.3 112.7-28.4 80.6-44.4-29.2-14.3-109.4 35.3-80.6 44.4z" fill="url(#SVGID_6_)"/><radialGradient id="SVGID_7_" cx="249.892" cy="916.931" r="308.051" fx="304.575" fy="866.824" gradientTransform="matrix(1 0 0 -1 0 1066)" gradientUnits="userSpaceOnUse"><stop offset=".2" stop-color="#00fdff"/><stop offset=".26" stop-color="#0af1ff"/><stop offset=".37" stop-color="#23d2ff"/><stop offset=".52" stop-color="#4da0ff"/><stop offset=".69" stop-color="#855bff"/><stop offset=".77" stop-color="#a238ff"/><stop offset=".81" stop-color="#a738fd"/><stop offset=".86" stop-color="#b539f9"/><stop offset=".9" stop-color="#cd39f1"/><stop offset=".96" stop-color="#ee3ae6"/><stop offset=".98" stop-color="#ff3be0"/></radialGradient><path d="M247.7 8.3l.2-.2c0 .1-.1.1-.2.2zm92.5 264.2c1.5-2.1 3.5-8.7 5.2-11.7 10.7-17.3 10.8-31 10.8-31.4 6.5-32.3 5.9-45.5 1.9-69.8-3.2-19.6-17.2-47.8-29.3-61.3-12.5-14-3.7-9.4-15.8-19.6-10.6-11.8-20.9-23.4-26.5-28.1C246 16.8 247 9.6 247.7 8.4c-.1.1-.3.4-.6.6-.5-1.9-.8-3.5-.8-3.5s-22.1 22.1-26.8 59c-3 24.1 6 49.2 19 65.2 6.8 8.3 14.5 15.9 22.9 22.5 9.9 14.2 15.3 31.6 15.3 50.4 0 47-38.2 85.2-85.2 85.2-6.4 0-12.8-.7-19.1-2.1-22.2-4.2-35-15.4-41.4-23.1-3.7-4.4-5.2-7.5-5.2-7.5 19.9 7.1 41.9 5.6 55.3-1.8 13.5-7.5 21.6-13 28.3-10.8 6.5 2.2 11.7-4.1 7.1-10.7-4.6-6.5-16.5-15.9-34.1-13.3-13.5 2-25.9 11.5-43.5 2.3-1.1-.6-2.2-1.2-3.3-1.9-1.2-.7 3.8 1 2.6.3-3.4-1.7-9.5-5.3-11.1-6.6-.3-.2 2.7.8 2.4.6-16.5-12.2-14.5-20.4-14-25.6.4-4.1 3.4-9.4 8.5-11.6 2.4 1.2 4 2.1 4 2.1s-1-1.9-1.6-2.9c.2-.1.4-.1.6-.1 2 .9 6.4 3.1 8.8 4.5 3 1.9 4 3.6 4 3.6s.8-.4.2-2.1c-.2-.7-1.1-2.9-4.1-5.1h.2c1.8.9 3.5 2 5.1 3.2.8-2.8 2.4-5.6 2-10.8-.2-3.6-.1-4.6-.8-6-.6-1.2.4-1.7 1.5-.4-.2-1-.5-1.9-.9-2.8v-.1c1.4-4.3 29.3-15.6 31.3-16.9 3.3-2.1 6.1-4.8 8.2-8 1.6-2.2 2.7-5.3 3-10 .1-2.1-.6-3.8-8-5.4-4.4-1-11.3-1.9-21.9-2.9-7.7-.7-12.3-5.7-14.8-10.3-.5-1-1-1.9-1.4-2.8-.5-1-.8-2.1-1.1-3.2 4.6-12.1 12.9-22.4 23.8-29.5.6-.5-2.5.1-1.9-.4.7-.6 5.5-2.3 6.4-2.7 1.1-.5-4.7-2.7-9.8-2.1-5.2.5-6.3 1.1-9 2.1 1.2-1 4.8-2.4 3.9-2.4-5.6.8-12.5 3.7-18.5 7 0-.6.1-1.1.4-1.7-2.8 1.1-9.6 5.3-11.5 8.9.1-.7.1-1.4.1-2.1-2.1 1.6-4 3.3-5.7 5.3l-.1.1c-16-5.8-30.1-6.2-42.1-3.6-2.6-2.4-6.8-5.9-12.8-17.6-.4-.7-.6 1.5-.9.8-2.3-5.4-3.7-14.1-3.5-20.2 0 0-4.8 2.2-8.7 11.3-.7 1.6-1.2 2.5-1.7 3.4-.2.3.5-3 .4-2.8-.7 1.2-2.5 2.8-3.2 4.9-.5 1.6-1.3 2.4-1.8 4.4l-.1.2c0-.6.1-2.4 0-2-1.9 3.7-3.5 7.6-4.8 11.5-2.1 7-4.6 16.5-5 28.9-.1.9 0 2-.1 2.8-5 5.8-8.5 10.6-9.8 13.2-6.5 10.1-13.7 25.8-20.7 50.6 3.1-6.8 6.8-13.3 11.1-19.4-6 13-11.6 33.7-12.7 65.6 1.4-6.6 3.2-13.1 5.4-19.5-1 21.3 1.5 47.6 14.9 77.4 8 17.5 26.4 53 71.2 80.7 0 0 15.3 11.4 41.5 19.9 1.9.7 3.9 1.4 5.9 2.1-.6-.3-1.2-.5-1.8-.8 17.5 5.2 35.6 7.9 53.9 7.9 68 .1 88-27.2 88-27.2l-.2.1c1-.9 1.9-1.8 2.8-2.8-10.7 10.1-35.2 10.8-44.3 10.1 15.6-4.6 25.9-8.5 45.8-16.1 2.3-.9 4.7-1.9 7.2-3l.8-.4c.5-.2 1-.4 1.5-.7 9.7-4.6 18.9-10.3 27.3-17.1 20.1-16 24.4-31.6 26.7-41.9-.3 1-1.3 3.3-2 4.8-5.2 11.1-16.6 17.8-29.1 23.7 5.9-7.8 11.4-15.9 16.4-24.2 4-4.1 5.2-10.4 8.3-14.6z" fill="url(#SVGID_7_)"/><linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="-7655.339" y1="9137.516" x2="-7683.31" y2="8823.109" gradientTransform="matrix(1.23 0 0 -1.22 9720.16 11130.11)"><stop offset="0" stop-color="#00ec00"/><stop offset=".1" stop-color="#00e244"/><stop offset=".22" stop-color="#00d694"/><stop offset=".31" stop-color="#00cfc7"/><stop offset=".35" stop-color="#00ccda"/><stop offset=".42" stop-color="#0bc2dd" stop-opacity=".92"/><stop offset=".57" stop-color="#29a7e4" stop-opacity=".72"/><stop offset=".77" stop-color="#597df0" stop-opacity=".4"/><stop offset="1" stop-color="#9448ff" stop-opacity="0"/></linearGradient><path d="M315.8 310.8c8.2-9 15.5-19.3 21.1-31 14.3-30.1 36.5-80.2 19-132.4-13.8-41.3-32.7-63.9-56.7-86-39-35.8-49.9-51.8-49.9-61.4 0 0-45 50.2-25.5 102.6s59.5 50.4 86 105.1c31.1 64.3-25.2 134.5-71.8 154.1 2.9-.6 103.6-23.4 108.9-81-.2 1-2.4 17-31.1 30z" fill="url(#SVGID_8_)"/><linearGradient id="SVGID_9_" gradientUnits="userSpaceOnUse" x1="-8889.5" y1="7812.05" x2="-8836.55" y2="7812.05" gradientTransform="matrix(1.22 .12 .12 -1.22 10013.36 10723.12)"><stop offset="0" stop-color="#005fe7"/><stop offset=".46" stop-color="#0071f3" stop-opacity=".51"/><stop offset=".83" stop-color="#007efc" stop-opacity=".14"/><stop offset="1" stop-color="#0083ff" stop-opacity="0"/></linearGradient><path d="M185.3 123.9c.2-3.4-1.6-5.7-29.8-8.3-11.6-1.1-16-11.8-17.4-16.3-4.1 10.7-5.8 21.9-4.9 35.5.6 8.9 6.6 18.4 9.5 24.1 0 0 .6-.8.9-1.1 5.4-5.6 27.9-14.1 30-15.3 2.4-1.6 11.3-8.1 11.7-18.6z" fill="url(#SVGID_9_)"/><radialGradient id="SVGID_10_" cx="-8884.848" cy="7827.773" r="64.396" gradientTransform="matrix(1.22 .12 .12 -1.22 10013.36 10723.12)" gradientUnits="userSpaceOnUse"><stop offset=".63" stop-color="#ffe302" stop-opacity="0"/><stop offset=".67" stop-color="#ffe302" stop-opacity=".05"/><stop offset=".75" stop-color="#ffe302" stop-opacity=".19"/><stop offset=".86" stop-color="#ffe302" stop-opacity=".4"/><stop offset=".99" stop-color="#ffe302" stop-opacity=".7"/></radialGradient><path d="M185.3 123.9c.2-3.4-1.6-5.7-29.8-8.3-11.6-1.1-16-11.8-17.4-16.3-4.1 10.7-5.8 21.9-4.9 35.5.6 8.9 6.6 18.4 9.5 24.1 0 0 .6-.8.9-1.1 5.4-5.6 27.9-14.1 30-15.3 2.4-1.6 11.3-8.1 11.7-18.6z" opacity=".5" fill="url(#SVGID_10_)"/><linearGradient id="SVGID_11_" gradientUnits="userSpaceOnUse" x1="85.362" y1="2192.822" x2="39.644" y2="2290.535" gradientTransform="matrix(.99 .1 .1 -.99 -239.75 2284.79)"><stop offset=".19" stop-color="#4a1475" stop-opacity=".5"/><stop offset=".62" stop-color="#2277ac" stop-opacity=".23"/><stop offset=".94" stop-color="#00ccda" stop-opacity="0"/></linearGradient><path d="M61.4 60.7c-.4-.7-.6 1.5-.9.8-2.3-5.4-3.7-14-3.4-20.2 0 0-4.8 2.2-8.7 11.3-.7 1.6-1.2 2.5-1.7 3.4-.2.3.5-3 .4-2.8-.7 1.2-2.5 2.8-3.2 4.8-.6 1.6-1.3 2.5-1.8 4.6-.2.6.2-2.5 0-2.1-9.2 17.8-10.9 44.7-10 43.5 19.6-20.9 42-25.9 42-25.9-2.4-1.7-7.6-6.8-12.7-17.4z" fill="url(#SVGID_11_)"/><linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="-209.367" y1="527.653" x2="-172.852" y2="486.18" gradientTransform="matrix(.99 .1 .1 -.99 239.39 724.37)"><stop offset=".01" stop-color="#002079" stop-opacity=".5"/><stop offset="1" stop-color="#0083ff" stop-opacity="0"/></linearGradient><path d="M135.7 279.4c-27.1-11.6-57.8-27.8-56.6-64.8 1.6-48.7 45.5-39.1 45.5-39.1-1.7.4-6.1 3.6-7.7 6.9-1.7 4.2-4.7 13.7 4.5 23.6 14.4 15.6-29.6 37 38.3 77.4 1.6 1-16-.5-24-4z" fill="url(#SVGID_12_)"/><linearGradient id="SVGID_13_" gradientUnits="userSpaceOnUse" x1="-139.213" y1="457.058" x2="-139.193" y2="477.788" gradientTransform="matrix(.99 .1 .1 -.99 239.39 724.37)"><stop offset="0" stop-color="#4a1475" stop-opacity=".9"/><stop offset=".18" stop-color="#6720a2" stop-opacity=".6"/><stop offset=".38" stop-color="#812acb" stop-opacity=".34"/><stop offset=".57" stop-color="#9332e8" stop-opacity=".15"/><stop offset=".76" stop-color="#9e36f9" stop-opacity=".04"/><stop offset=".93" stop-color="#a238ff" stop-opacity="0"/></linearGradient><path d="M126.1 255.1c19.2 6.7 41.5 5.5 54.9-1.9 9-5 20.4-13 27.5-11-6.1-2.4-10.8-3.6-16.3-3.8-1 0-2.1 0-3.1-.1-2 0-4.1.1-6.1.3-3.5.3-7.3 2.5-10.8 2.1-.2 0 3.4-1.5 3.1-1.4-1.8.4-3.8.5-6 .7-1.3.2-2.5.3-3.8.4-40 3.4-73.7-21.7-73.7-21.7-2.9 9.8 12.9 29 34.3 36.4z" opacity=".5" fill="url(#SVGID_13_)"/><linearGradient id="SVGID_14_" gradientUnits="userSpaceOnUse" x1="240.706" y1="1051.958" x2="359.301" y2="786.765" gradientTransform="matrix(1 0 0 -1 0 1066)"><stop offset="0" stop-color="#00ec00" stop-opacity="0"/><stop offset=".28" stop-color="#00dc6d" stop-opacity=".5"/><stop offset=".5" stop-color="#00d1bb" stop-opacity=".86"/><stop offset=".6" stop-color="#00ccda"/><stop offset=".68" stop-color="#04c9db"/><stop offset=".75" stop-color="#0fc1df"/><stop offset=".83" stop-color="#23b2e6"/><stop offset=".9" stop-color="#3e9ef0"/><stop offset=".98" stop-color="#6184fc"/><stop offset=".99" stop-color="#6680fe"/></linearGradient><path d="M315.7 311.1c40.4-39.7 60.9-87.9 52.2-142 0 0 3.5 27.7-9.6 56.1 6.3-27.7 7-62.1-9.7-97.8-22.3-47.6-59-72.6-73-83.1-21.2-15.8-30-31.9-30.2-35.3-6.3 13-25.5 57.5-2.1 95.8 22 35.9 56.6 46.6 80.8 79.5 44.7 60.8-8.4 126.8-8.4 126.8z" fill="url(#SVGID_14_)"/><linearGradient id="SVGID_15_" gradientUnits="userSpaceOnUse" x1="264.16" y1="899.455" x2="207.985" y2="748.621" gradientTransform="matrix(1 0 0 -1 0 1066)"><stop offset="0" stop-color="#0083ff"/><stop offset=".04" stop-color="#0083ff" stop-opacity=".92"/><stop offset=".14" stop-color="#0083ff" stop-opacity=".71"/><stop offset=".26" stop-color="#0083ff" stop-opacity=".52"/><stop offset=".37" stop-color="#0083ff" stop-opacity=".36"/><stop offset=".49" stop-color="#0083ff" stop-opacity=".23"/><stop offset=".61" stop-color="#0083ff" stop-opacity=".13"/><stop offset=".73" stop-color="#0083ff" stop-opacity=".06"/><stop offset=".86" stop-color="#0083ff" stop-opacity=".01"/><stop offset="1" stop-color="#0083ff" stop-opacity="0"/></linearGradient><path d="M309.9 207.8c-14.1-29.2-31.7-41.9-48.4-55.7 1.9 2.7 2.4 3.7 3.5 5.4 14.7 15.6 36.3 53.8 20.6 101.7-29.6 90.2-147.9 47.7-160.3 35.8 5 52.2 92.3 77.1 149.2 43.3 32.4-30.6 58.6-82.7 35.4-130.5z" fill="url(#SVGID_15_)"/></g></g></g><path class="st0" d="M1132.9 279.4l-58.5-122.3c1 11.9 2.7 29.5 2.7 56.2v66h-18.6v-144h25.9l59.1 122.5c-.4-3.3-2.7-26.3-2.7-43.9v-78.6h18.6v144h-26.5v.1zM1213 141c0 7.3-5.4 13-13.6 13-7.9 0-13.4-5.6-13.4-13 0-7.5 5.4-13.2 13.4-13.2 8.1 0 13.6 5.7 13.6 13.2zm-23 28.3h19.2v110.1H1190zm105.3 8.4c13 5.9 19.4 15 19.4 27.8 0 21.7-15.7 37.4-42 37.4-5 0-9.4-.6-14-2.1-3.1 2.3-5.4 6.3-5.4 10.2 0 5 3.1 9 14.4 9h17.6c22.6 0 37.6 13 37.6 30.5 0 21.3-17.6 33.4-51.6 33.4-35.9 0-47.2-11.1-47.2-33.4h17.3c0 12.5 5.6 18.4 29.9 18.4 23.8 0 32.2-6.1 32.2-17.1 0-10.5-8.4-15.7-22.2-15.7H1264c-19.6 0-28.4-9.8-28.4-20.9 0-7.1 4.2-14.2 12.1-19.4-12.7-6.7-18.6-16.3-18.6-30.5 0-22.6 18.2-38.5 42.4-38.5 27.4.6 37.4-4 50.4-9.8l5.6 17.3c-9.2 2.9-19.6 3.4-32.2 3.4zm-46.6 27.5c0 14.6 8.2 24.9 23.2 24.9s23.2-9.2 23.2-25.1c0-16.1-7.9-24.5-23.6-24.5-14.8.1-22.8 10.3-22.8 24.7zm172.2-4.8v79h-19.2v-76.3c0-16.5-7.1-21.3-17.6-21.3-11.9 0-20.5 7.7-27.8 19.2v78.4h-19.2V125.2l19.2-2.1v61.4c7.9-10.9 18.8-17.8 32.6-17.8 20.1.1 32 13 32 33.7zm59.4 81.5c-18.6 0-29.7-10.9-29.7-31.3v-66.5h-19.2v-14.8h19.2v-24.9l19.2-2.3v27.2h26.1l-2.1 14.8h-24v65.6c0 11.1 3.6 16.3 13.2 16.3 4.8 0 9.2-1.5 14.6-4.8l7.3 13.2c-7.3 5-15.2 7.5-24.6 7.5zm59.3-15.8c2.7 0 5-.4 7.1-1.3l5 13.4c-5.2 2.5-10.7 3.8-16.3 3.8-14 0-21.9-8.4-21.9-24.2V125l19.2-2.3v134.6c0 5.8 1.9 8.8 6.9 8.8zm18.9 57.9l-2.1-15c22.4-3.8 28.6-12.3 34.9-29.5h-6.5l-37-110.1h20.5l29.5 96.1 28.8-96.1h19.9l-36.8 110.8c-7.8 23.3-20.7 40.4-51.2 43.8z"/></svg>
\ No newline at end of file
--- a/browser/branding/nightly/content/jar.mn
+++ b/browser/branding/nightly/content/jar.mn
@@ -10,8 +10,9 @@ browser.jar:
   content/branding/about-wordmark.svg
   content/branding/icon16.png                    (../default16.png)
   content/branding/icon32.png                    (../default32.png)
   content/branding/icon48.png                    (../default48.png)
   content/branding/icon64.png                    (../default64.png)
   content/branding/icon128.png                   (../default128.png)
   content/branding/identity-icons-brand.svg
   content/branding/aboutDialog.css
+  content/branding/horizontal-lockup.svg
new file mode 100644
--- /dev/null
+++ b/browser/branding/official/content/horizontal-lockup.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1023.49 381.79"><defs><radialGradient id="a" cx="-15255.97" cy="8846.13" fx="-15267.12" r="174.94" gradientTransform="matrix(.76 .03 .05 -1.12 11535.88 10522.65)" gradientUnits="userSpaceOnUse"><stop offset=".1" stop-color="#ffea00"/><stop offset=".17" stop-color="#ffde00"/><stop offset=".28" stop-color="#ffbf00"/><stop offset=".43" stop-color="#ff8e00"/><stop offset=".77" stop-color="#ff272d"/><stop offset=".87" stop-color="#e0255a"/><stop offset=".95" stop-color="#cc2477"/><stop offset="1" stop-color="#c42482"/></radialGradient><radialGradient id="b" cx="-7884.44" cy="8442.2" r="306.99" gradientTransform="matrix(1.23 0 0 -1.23 9918.06 10417.47)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#00ccda"/><stop offset=".22" stop-color="#0083ff"/><stop offset=".26" stop-color="#007af9"/><stop offset=".33" stop-color="#0060e8"/><stop offset=".33" stop-color="#005fe7"/><stop offset=".44" stop-color="#2639ad"/><stop offset=".52" stop-color="#401e84"/><stop offset=".57" stop-color="#4a1475"/></radialGradient><linearGradient id="c" x1="209.78" y1="101.08" x2="135.5" y2="344.05" gradientTransform="matrix(1 0 0 -1 0 384)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#000f43" stop-opacity=".4"/><stop offset=".48" stop-color="#001962" stop-opacity=".17"/><stop offset="1" stop-color="#002079" stop-opacity="0"/></linearGradient><radialGradient id="d" cx="-8792.46" cy="7205.23" r="103.55" gradientTransform="matrix(1.22 .12 .12 -1.22 10221.27 10107.74)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ffea00"/><stop offset=".5" stop-color="#ff272d"/><stop offset="1" stop-color="#c42482"/></radialGradient><radialGradient id="e" cx="-8801.82" cy="7304.52" r="172.92" gradientTransform="matrix(1.22 .12 .12 -1.22 10221.27 10107.74)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ffe900"/><stop offset=".16" stop-color="#ffaf0e"/><stop offset=".32" stop-color="#ff7a1b"/><stop offset=".47" stop-color="#ff4e26"/><stop offset=".62" stop-color="#ff2c2e"/><stop offset=".76" stop-color="#ff1434"/><stop offset=".89" stop-color="#ff0538"/><stop offset="1" stop-color="#ff0039"/></radialGradient><radialGradient id="f" cx="-8777.37" cy="7144.23" r="158.67" gradientTransform="matrix(1.22 .12 .12 -1.22 10221.27 10107.74)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ff272d"/><stop offset=".5" stop-color="#c42482"/><stop offset=".99" stop-color="#620700"/></radialGradient><radialGradient id="g" cx="277.49" cy="231.11" fx="288.343" r="303.48" gradientTransform="matrix(1 0 0 -1 0 384)" gradientUnits="userSpaceOnUse"><stop offset=".16" stop-color="#ffea00"/><stop offset=".23" stop-color="#ffde00"/><stop offset=".37" stop-color="#ffbf00"/><stop offset=".54" stop-color="#ff8e00"/><stop offset=".76" stop-color="#ff272d"/><stop offset=".8" stop-color="#f92433"/><stop offset=".84" stop-color="#e91c45"/><stop offset=".89" stop-color="#cf0e62"/><stop offset=".94" stop-color="#b5007f"/></radialGradient><radialGradient id="h" cx="260.09" cy="371.86" r="345.87" gradientTransform="matrix(1 0 0 -1 0 384)" gradientUnits="userSpaceOnUse"><stop offset=".28" stop-color="#ffea00"/><stop offset=".4" stop-color="#fd0"/><stop offset=".63" stop-color="#ffba00"/><stop offset=".86" stop-color="#ff9100"/><stop offset=".93" stop-color="#ff6711"/><stop offset=".99" stop-color="#ff4a1d"/></radialGradient><linearGradient id="i" x1="-9059.76" y1="7275.74" x2="-8940.63" y2="7306.1" gradientTransform="matrix(1.22 .12 .12 -1.22 10221.27 10107.74)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#c42482" stop-opacity=".5"/><stop offset=".47" stop-color="#ff272d" stop-opacity=".5"/><stop offset=".49" stop-color="#ff2c2c" stop-opacity=".51"/><stop offset=".68" stop-color="#ff7a1a" stop-opacity=".72"/><stop offset=".83" stop-color="#ffb20d" stop-opacity=".87"/><stop offset=".94" stop-color="#ffd605" stop-opacity=".96"/><stop offset="1" stop-color="#ffe302"/></linearGradient><linearGradient id="j" x1="129.91" y1="1613.65" x2="108.74" y2="1667.91" gradientTransform="matrix(.99 .1 .1 -.99 -232.8 1690.84)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#891551" stop-opacity=".6"/><stop offset="1" stop-color="#c42482" stop-opacity="0"/></linearGradient><linearGradient id="k" x1="-156.48" y1="-66.73" x2="-119.96" y2="-108.2" gradientTransform="matrix(.99 .1 .1 -.99 246.33 130.43)" gradientUnits="userSpaceOnUse"><stop offset=".01" stop-color="#891551" stop-opacity=".5"/><stop offset=".48" stop-color="#ff272d" stop-opacity=".5"/><stop offset="1" stop-color="#ff272d" stop-opacity="0"/></linearGradient><linearGradient id="l" x1="-86.15" y1="-137.49" x2="-86.12" y2="-108.48" gradientTransform="matrix(.99 .1 .1 -.99 246.33 130.43)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#c42482"/><stop offset=".08" stop-color="#c42482" stop-opacity=".81"/><stop offset=".21" stop-color="#c42482" stop-opacity=".57"/><stop offset=".33" stop-color="#c42482" stop-opacity=".36"/><stop offset=".45" stop-color="#c42482" stop-opacity=".2"/><stop offset=".56" stop-color="#c42482" stop-opacity=".09"/><stop offset=".67" stop-color="#c42482" stop-opacity=".02"/><stop offset=".77" stop-color="#c42482" stop-opacity="0"/></linearGradient><linearGradient id="m" x1="240.77" y1="370" x2="359.36" y2="104.8" gradientTransform="matrix(1 0 0 -1 0 384)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fff14f"/><stop offset=".27" stop-color="#ffee4c"/><stop offset=".45" stop-color="#ffe643"/><stop offset=".61" stop-color="#ffd834"/><stop offset=".76" stop-color="#ffc41e"/><stop offset=".89" stop-color="#ffab02"/><stop offset=".9" stop-color="#ffa900"/><stop offset=".95" stop-color="#ffa000"/><stop offset="1" stop-color="#ff9100"/></linearGradient><linearGradient id="n" x1="264.18" y1="217.47" x2="208" y2="66.64" gradientTransform="matrix(1 0 0 -1 0 384)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ff8e00"/><stop offset=".04" stop-color="#ff8e00" stop-opacity=".86"/><stop offset=".08" stop-color="#ff8e00" stop-opacity=".73"/><stop offset=".13" stop-color="#ff8e00" stop-opacity=".63"/><stop offset=".18" stop-color="#ff8e00" stop-opacity=".56"/><stop offset=".23" stop-color="#ff8e00" stop-opacity=".51"/><stop offset=".28" stop-color="#ff8e00" stop-opacity=".5"/><stop offset=".39" stop-color="#ff8e00" stop-opacity=".48"/><stop offset=".52" stop-color="#ff8e00" stop-opacity=".42"/><stop offset=".68" stop-color="#ff8e00" stop-opacity=".31"/><stop offset=".84" stop-color="#ff8e00" stop-opacity=".17"/><stop offset="1" stop-color="#ff8e00" stop-opacity="0"/></linearGradient></defs><g data-name="Layer 2"><g data-name="Layer 1"><g data-name="Layer 2"><g data-name="Layer 1-2"><path d="M996.05 168.9h-21.81L951 209.38l-23.12-40.48h-22.66l34.19 52.23-38.6 58.32h21.82l27.48-46.78 27.06 46.78h23.28l-38.39-59.16zm-466 110.55h19.3V168.9h-19.3zm63-87.23l-1.89-23.32h-16.51v110.54H594v-57c0-17.19 12.59-36.29 26.43-36.29a34.57 34.57 0 0 1 9.65 1.26l3.57-18.88a47.14 47.14 0 0 0-10.91-1.26c-13.43 0-23.69 8.37-29.58 24.95zm-156.36 87.22h19.93v-63.56h47.2v-15.73h-47.2v-49.3h54.54l2.31-15.94h-76.78zm102.82-151.62c-8 0-13.43 5.66-13.43 13.22 0 7.34 5.45 13 13.43 13 8.18 0 13.64-5.66 13.64-13 0-7.56-5.46-13.22-13.64-13.22zm312.68 39.44c-31.47 0-49.51 22.83-49.51 57 0 35 17.83 57.69 49.3 57.69 31.26 0 49.3-23.7 49.3-57.9 0-35.05-17.62-56.8-49.09-56.8zm-.19 99.18c-18.46 0-28.53-13.43-28.53-42.16 0-28.95 10.28-41.5 28.74-41.5 18.25 0 28.32 12.55 28.32 41.29-.02 28.93-10.09 42.37-28.53 42.37zm-78.17-105.2c0-10.7 4.2-18.56 16.36-18.56a47.41 47.41 0 0 1 19.3 4.2l6.08-14c-8.81-3.78-15.94-5.66-26.43-5.66-22.45 0-34.61 14.09-34.61 32.55v9.19h-19.74v14.84h19.72v95.65h19.3v-95.66h24.75l2.1-14.89h-26.85zm-92 6c-28.53 0-45.94 23.67-45.94 58.07 0 35 18 56.64 48.88 56.64 15.31 0 27.69-5.24 38.6-13.84L715 256.57c-9.65 6.71-17.83 9.65-28.74 9.65-15.94 0-27.9-9.86-29.79-35.45h70.48c.21-2.52.42-6.08.42-9.86-.07-33.76-15.8-53.66-45.59-53.66zm26.43 49.26h-51.88c1.47-24.54 11.12-33.95 25.8-33.95 17.41 0 26 11.08 26 32.69zm310.1-41.21c0-1.86-1.28-2.8-3.94-2.8H1012v9.39h1.45v-3.83h1.24l2.24 3.83h1.73l-2.55-4.07a2.56 2.56 0 0 0 2.19-2.5zm-4.87 1.55v-3.14h1.14c1.45 0 2.24.38 2.24 1.59s-.79 1.55-2 1.55zm1.42-7.84a8.27 8.27 0 0 0-8.25 8.29v.07a8.11 8.11 0 0 0 8 8.22h.25a8.29 8.29 0 0 0 .58-16.57h-.58zm0 15.33a6.79 6.79 0 0 1-6.87-6.73v-.27a6.91 6.91 0 0 1 6.74-7.08h.13a6.83 6.83 0 0 1 6.94 6.72v.28a6.9 6.9 0 0 1-6.7 7.1h-.31z" fill="#363959"/><path d="M298.87 35.52c-9.29 10.82-13.62 35.17-4.2 59.85s23.86 19.32 32.86 44.49c11.88 33.21 6.35 77.83 6.35 77.83s14.28 41.36 24.24-2.57c22.03-82.53-59.25-159.27-59.25-179.6z" fill="url(#a)"/><path data-name="Path" d="M185.49 378.15c95.15 0 172.23-77.5 172.23-173.06S280.64 32 185.58 32 13.44 109.54 13.44 205.1c-.18 95.65 76.99 173.05 172.05 173.05z" fill="url(#b)"/><path d="M314.54 311.81a95 95 0 0 1-11.69 7.06 271.38 271.38 0 0 0 14.86-24.43 96.25 96.25 0 0 0 9.77-12.28c1.33-2.1 2.84-4.69 4.43-7.69 9.67-17.42 20.33-45.61 20.63-74.57v-2.21a100.07 100.07 0 0 0-2.21-21.69c.08.56.15 1.11.22 1.66-.09-.43-.16-.86-.25-1.29.14.79.26 1.55.38 2.32 2 16.77.57 33.12-6.47 45.18l-.34.51c3.65-18.33 4.87-38.56.81-58.82 0 0-1.62-9.85-13.73-39.75-7-17.21-19.33-31.32-30.26-41.6-9.58-11.85-18.28-19.8-23.08-24.85-10-10.55-14.22-18.46-15.94-23.62-1.5-.75-20.62-19.32-22.14-20-8.35 12.94-34.59 53.42-22.11 91.24 5.66 17.14 20 34.93 35 44.9.66.75 8.91 9.72 12.84 29.94 4.06 20.89 1.92 37.19-6.42 61.3-9.81 21.15-35 42.05-58.48 44.19-50.31 4.57-68.73-25.26-68.73-25.26 18 7.19 37.86 5.68 49.94-1.77s19.55-13.12 25.53-10.92 10.59-4.17 6.38-10.77a30.46 30.46 0 0 0-30.81-13.41c-12.2 2-23.37 11.64-39.35 2.29a31.91 31.91 0 0 1-3-2c-1.05-.69 3.42 1.05 2.38.27a78.76 78.76 0 0 1-10-6.68c-.24-.22 2.41.85 2.18.63-14.94-12.3-13.08-20.62-12.61-25.83.38-4.17 3.09-9.51 7.66-11.68 2.21 1.21 3.58 2.13 3.58 2.13s-.94-1.92-1.45-2.94c.18-.08.35-.06.53-.13 1.81.87 5.81 3.14 7.92 4.53a11.71 11.71 0 0 1 3.62 3.66s.72-.4.19-2.09a10.81 10.81 0 0 0-3.75-5.11h.17a31.76 31.76 0 0 1 4.6 3.2c.77-2.79 2.14-5.7 1.84-10.9-.19-3.66-.1-4.61-.74-6-.58-1.21.32-1.69 1.33-.43a12.49 12.49 0 0 0-.86-2.87v-.09c1.25-4.36 26.48-15.7 28.32-17a26.13 26.13 0 0 0 7.42-8.07c1.4-2.24 2.46-5.37 2.72-10.13.14-3.43-1.46-5.72-27-8.39-7-.69-11.07-5.74-13.4-10.41-.42-1-.86-1.92-1.29-2.83a22 22 0 0 1-1-3.27 61.22 61.22 0 0 1 21.5-29.74c.56-.51-2.24.13-1.68-.38a62.41 62.41 0 0 1 5.74-2.71c1-.46-4.22-2.68-8.82-2.14a22 22 0 0 0-8.18 2.15c1-1 4.34-2.39 3.56-2.38-5 .77-11.32 3.71-16.69 7a4.24 4.24 0 0 1 .32-1.69c-2.5 1.06-8.64 5.35-10.42 9a16.16 16.16 0 0 0 .1-2.09 33.06 33.06 0 0 0-5.11 5.38l-.09.09c-14.49-5.84-27.25-6.22-38-3.6-2.36-2.37-3.51-.64-8.89-12.44-.37-.71.28.7 0 0-.88-2.29.54 3.05 0 0-9.13 7.12-21 15.2-26.72 20.9-.07.23 6.66-1.9 0 0-2.33.67-2.17 2-2.53 14.55-.09 1 0 2-.09 2.86a80.19 80.19 0 0 0-8.84 13.27c-5.89 10.16-12.39 26-18.68 51a129.62 129.62 0 0 1 10-19.56c-5.23 13.3-10.29 34.17-11.3 66.33a186.79 186.79 0 0 1 4.86-19.66 183.64 183.64 0 0 0 13.48 78 190.73 190.73 0 0 0 19.8 37 175.38 175.38 0 0 0 247.93 6.52q5.52-5.25 10.59-11z" fill="url(#c)"/><path d="M275.9 336.28c63.19-7.32 91.17-72.44 55.24-73.72-32.45-1-85.14 77.17-55.24 73.72z" fill="url(#d)"/><path d="M335.69 249.26c43.48-25.3 32.14-80 32.14-80s-16.78 19.49-28.18 50.56c-11.25 30.81-30.09 44.72-3.96 29.44z" fill="url(#e)"/><path d="M197.67 368.62c60.61 19.34 112.71-28.41 80.6-44.36-29.2-14.38-109.37 35.21-80.6 44.36z" fill="url(#f)"/><path d="M340.21 272.46c1.48-2.08 3.47-8.74 5.23-11.72 10.7-17.28 10.78-31 10.78-31.36 6.46-32.29 5.88-45.47 1.9-69.86-3.2-19.63-17.2-47.76-29.32-61.3-12.49-14-3.69-9.41-15.79-19.6-10.6-11.75-20.88-23.39-26.48-28.07C246.07 16.72 247 9.54 247.76 8.31l-.57.64c-.48-1.91-.82-3.52-.82-3.52s-22.12 22.12-26.77 59c-3 24.06 6 49.15 19 65.18a148 148 0 0 0 22.89 22.5c9.86 14.15 15.28 31.62 15.28 50.4a85.3 85.3 0 0 1-104.29 83c-22.21-4.23-35-15.43-41.43-23a41.71 41.71 0 0 1-5.22-7.54c19.9 7.13 41.9 5.64 55.28-1.75s21.64-13 28.26-10.83 11.72-4.14 7.06-10.68-16.45-15.9-34.1-13.3c-13.5 2-25.86 11.55-43.55 2.27a35.6 35.6 0 0 1-3.32-2c-1.17-.69 3.79 1 2.63.26a87 87 0 0 1-11.09-6.6c-.26-.22 2.67.84 2.41.62-16.54-12.2-14.47-20.44-14-25.61a14.85 14.85 0 0 1 8.48-11.59c2.44 1.2 4 2.11 4 2.11s-1-1.91-1.61-2.92c.2-.08.39-.06.58-.13 2 .87 6.43 3.12 8.76 4.49a12.1 12.1 0 0 1 4 3.63s.8-.39.21-2.07a10.78 10.78 0 0 0-4.14-5.07h.19A35.12 35.12 0 0 1 141 179c.85-2.76 2.37-5.65 2-10.81-.2-3.63-.11-4.57-.82-6-.64-1.2.36-1.67 1.47-.42a11.51 11.51 0 0 0-1-2.85v-.09C144.09 154.51 172 143.26 174 142a27.15 27.15 0 0 0 8.22-8c1.55-2.22 2.72-5.33 3-10 .1-2.13-.56-3.81-8-5.42a199.16 199.16 0 0 0-21.9-2.9c-7.72-.68-12.25-5.69-14.83-10.32-.47-1-.95-1.9-1.43-2.8a20.6 20.6 0 0 1-1.1-3.24 61.44 61.44 0 0 1 23.78-29.51c.62-.51-2.48.13-1.86-.38s5.46-2.3 6.35-2.68c1.09-.46-4.67-2.65-9.76-2.12a26.15 26.15 0 0 0-9 2.13c1.15-1 4.8-2.37 3.94-2.36-5.59.77-12.53 3.68-18.47 7a3.7 3.7 0 0 1 .36-1.67c-2.77 1-9.56 5.31-11.54 8.9a16 16 0 0 0 .11-2.08 34.42 34.42 0 0 0-5.67 5.32l-.1.08c-16-5.9-30.14-6.23-42-3.63-2.61-2.35-6.82-5.91-12.76-17.61-.4-.71-.62 1.46-.93.76-2.31-5.36-3.71-14.14-3.48-20.19 0 0-4.78 2.18-8.74 11.28-.74 1.57-1.19 2.44-1.68 3.37-.22.26.49-3 .38-2.81-.69 1.17-2.47 2.79-3.25 4.9-.54 1.56-1.29 2.43-1.77 4.38l-.11.18c0-.57.14-2.36 0-2A91.77 91.77 0 0 0 37 72.16a107.72 107.72 0 0 0-5 28.93c-.09.94 0 2-.1 2.84-5 5.76-8.48 10.64-9.78 13.16-6.53 10.08-13.72 25.78-20.68 50.62a123.6 123.6 0 0 1 11.07-19.4C6.72 161.5 1.12 182.21 0 214.09a170.32 170.32 0 0 1 5.38-19.49A165.08 165.08 0 0 0 20.3 272c8 17.49 26.35 53 71.24 80.74s15.27 11.37 41.52 19.89c1.94.7 3.9 1.4 5.91 2.07-.63-.25-1.24-.52-1.83-.8a187.88 187.88 0 0 0 53.86 7.89c68 .06 88-27.24 88-27.24l-.2.15c1-.9 1.88-1.84 2.77-2.82-10.72 10.13-35.2 10.8-44.35 10.07 15.61-4.58 25.88-8.46 45.85-16.11q3.49-1.3 7.17-3l.8-.36 1.45-.68a135.86 135.86 0 0 0 27.26-17.07c20.06-16 24.42-31.64 26.71-41.94-.32 1-1.31 3.29-2 4.78-5.17 11.05-16.62 17.83-29.06 23.65A268.48 268.48 0 0 0 331.85 287c4.06-4 5.33-10.3 8.36-14.54z" fill="url(#g)"/><path d="M315.8 310.78a128.58 128.58 0 0 0 21.09-31c14.32-30.1 36.45-80.15 19-132.42-13.78-41.31-32.7-63.91-56.72-86-39-35.85-49.89-51.85-49.89-61.36 0 0-45 50.21-25.51 102.58s59.54 50.45 86 105.1C340.93 272 284.58 342.16 238 361.8c2.85-.63 103.58-23.43 108.88-81-.14 1.02-2.41 16.95-31.08 29.98z" fill="url(#h)"/><path d="M185.3 123.91c.15-3.4-1.61-5.69-29.75-8.33-11.58-1.07-16-11.77-17.36-16.27-4.12 10.7-5.82 21.92-4.9 35.49.62 8.89 6.6 18.44 9.46 24 0 0 .63-.82.93-1.13 5.38-5.6 27.91-14.13 30-15.34 2.32-1.43 11.23-7.92 11.62-18.42z" fill="url(#i)"/><path d="M61.42 60.71c-.4-.71-.62 1.46-.93.76a51.68 51.68 0 0 1-3.38-20.18s-4.78 2.18-8.73 11.28c-.74 1.63-1.21 2.53-1.68 3.43-.22.26.49-3 .38-2.81-.69 1.17-2.47 2.79-3.24 4.8A28.16 28.16 0 0 0 42 62.56c-.15.55.15-2.45 0-2.09-9.2 17.78-11 44.66-10 43.53 19.59-20.91 42-25.87 42-25.87-2.39-1.76-7.58-6.84-12.69-17.42z" fill="url(#j)"/><path d="M135.74 279.4c-27.05-11.55-57.81-27.84-56.65-64.85 1.58-48.74 45.45-39.11 45.45-39.11-1.66.4-6.08 3.55-7.65 6.91-1.66 4.2-4.68 13.69 4.48 23.63 14.39 15.59-29.56 37 38.28 77.43 1.71.93-15.9-.55-23.92-4z" fill="url(#k)"/><path d="M126.13 255.11c19.18 6.68 41.53 5.51 54.91-1.88 9-5 20.45-13 27.52-11a48.16 48.16 0 0 0-16.33-3.83 27.82 27.82 0 0 1-3.12-.12 52 52 0 0 0-6.11.33c-3.45.32-7.28 2.5-10.76 2.15-.19 0 3.38-1.46 3.09-1.4-1.84.38-3.85.47-6 .73-1.35.15-2.5.32-3.84.37-40 3.39-73.73-21.66-73.73-21.66-2.87 9.68 12.87 28.83 34.35 36.31z" fill="url(#l)"/><path d="M315.72 311c40.41-39.68 60.86-87.91 52.22-142a114.29 114.29 0 0 1-9.64 56.11c6.29-27.7 7-62.12-9.69-97.77-22.31-47.58-59-72.62-73-83.06-21.22-15.83-30-31.94-30.18-35.27-6.34 13-25.52 57.5-2.06 95.83 22 35.91 56.59 46.57 80.83 79.52 44.65 60.72-8.48 126.64-8.48 126.64z" fill="url(#m)"/><path d="M309.93 207.79c-14.12-29.18-31.75-41.9-48.43-55.7 1.94 2.72 2.42 3.68 3.49 5.43 14.68 15.64 36.32 53.8 20.61 101.7-29.6 90.16-147.88 47.7-160.28 35.78 5 52.18 92.36 77.15 149.22 43.31 32.36-30.63 58.54-82.7 35.39-130.52z" fill="url(#n)"/></g></g></g></g></svg>
\ No newline at end of file
--- a/browser/branding/official/content/jar.mn
+++ b/browser/branding/official/content/jar.mn
@@ -10,8 +10,9 @@ browser.jar:
   content/branding/about-wordmark.svg
   content/branding/icon16.png                    (../default16.png)
   content/branding/icon32.png                    (../default32.png)
   content/branding/icon48.png                    (../default48.png)
   content/branding/icon64.png                    (../default64.png)
   content/branding/icon128.png                   (../default128.png)
   content/branding/identity-icons-brand.svg
   content/branding/aboutDialog.css
+  content/branding/horizontal-lockup.svg
new file mode 100644
--- /dev/null
+++ b/browser/branding/unofficial/content/horizontal-lockup.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1011.2 346"><path d="M497.6 247.9l-58.5-122.3c1 11.9 2.7 29.5 2.7 56.2v66h-18.6v-144h25.9l59.1 122.5c-.4-3.3-2.7-26.3-2.7-43.9v-78.6h18.6v144h-26.5v.1zm80.1-138.4c0 7.3-5.4 13-13.6 13-7.9 0-13.4-5.6-13.4-13 0-7.5 5.4-13.2 13.4-13.2 8.1 0 13.6 5.7 13.6 13.2zm-23 28.3h19.2v110.1h-19.2V137.8zm105.3 8.4c13 5.9 19.4 15 19.4 27.8 0 21.7-15.7 37.4-42 37.4-5 0-9.4-.6-14-2.1-3.1 2.3-5.4 6.3-5.4 10.2 0 5 3.1 9 14.4 9H650c22.6 0 37.6 13 37.6 30.5 0 21.3-17.6 33.4-51.6 33.4-35.9 0-47.2-11.1-47.2-33.4h17.3c0 12.5 5.6 18.4 29.9 18.4 23.8 0 32.2-6.1 32.2-17.1 0-10.5-8.4-15.7-22.2-15.7h-17.3c-19.6 0-28.4-9.8-28.4-20.9 0-7.1 4.2-14.2 12.1-19.4-12.7-6.7-18.6-16.3-18.6-30.5 0-22.6 18.2-38.5 42.4-38.5 27.4.6 37.4-4 50.4-9.8l5.6 17.3c-9.2 2.9-19.6 3.4-32.2 3.4zm-46.6 27.5c0 14.6 8.2 24.9 23.2 24.9s23.2-9.2 23.2-25.1c0-16.1-7.9-24.5-23.6-24.5-14.8.1-22.8 10.3-22.8 24.7zm172.2-4.8v79h-19.2v-76.3c0-16.5-7.1-21.3-17.6-21.3-11.9 0-20.5 7.7-27.8 19.2v78.4h-19.2V93.7l19.2-2.1V153c7.9-10.9 18.8-17.8 32.6-17.8 20.1.1 32 13 32 33.7zm59.4 81.5c-18.6 0-29.7-10.9-29.7-31.3v-66.5h-19.2v-14.8h19.2v-24.9l19.2-2.3v27.2h26.1l-2.1 14.8h-24v65.6c0 11.1 3.6 16.3 13.2 16.3 4.8 0 9.2-1.5 14.6-4.8l7.3 13.2c-7.3 5-15.2 7.5-24.6 7.5zm59.3-15.8c2.7 0 5-.4 7.1-1.3l5 13.4c-5.2 2.5-10.7 3.8-16.3 3.8-14 0-21.9-8.4-21.9-24.2V93.5l19.2-2.3v134.6c0 5.8 1.9 8.8 6.9 8.8zm18.9 57.9l-2.1-15c22.4-3.8 28.6-12.3 34.9-29.5h-6.5l-37-110.1H933l29.5 96.1 28.8-96.1h19.9l-36.8 110.8c-7.8 23.3-20.7 40.4-51.2 43.8z" fill="#363959"/><radialGradient id="a" cx="-7592.893" cy="-8773.69" r="306.995" gradientTransform="matrix(1.23 0 0 1.22 9568.41 10762.02)" gradientUnits="userSpaceOnUse"><stop offset=".02" stop-color="#005fe7"/><stop offset=".18" stop-color="#0042b4"/><stop offset=".32" stop-color="#002989"/><stop offset=".4" stop-color="#002079"/><stop offset=".47" stop-color="#131d78"/><stop offset=".66" stop-color="#3b1676"/><stop offset=".75" stop-color="#4a1475"/></radialGradient><path d="M172 346c95.2 0 172.2-77.5 172.2-173S267.1 0 172.1 0 0 77.3 0 172.9C-.2 268.6 77 346 172 346z" fill="url(#a)"/></svg>
\ No newline at end of file
--- a/browser/branding/unofficial/content/jar.mn
+++ b/browser/branding/unofficial/content/jar.mn
@@ -10,8 +10,9 @@ browser.jar:
   content/branding/about-wordmark.svg
   content/branding/icon16.png                    (../default16.png)
   content/branding/icon32.png                    (../default32.png)
   content/branding/icon48.png                    (../default48.png)
   content/branding/icon64.png                    (../default64.png)
   content/branding/icon128.png                   (../default128.png)
   content/branding/identity-icons-brand.svg
   content/branding/aboutDialog.css
+  content/branding/horizontal-lockup.svg
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -99,16 +99,20 @@ static const RedirEntry kRedirMap[] = {
      "chrome://browser/content/downloads/contentAreaDownloadsView.xul",
      nsIAboutModule::ALLOW_SCRIPT},
     {"reader", "chrome://global/content/reader/aboutReader.html",
      nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
          nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
          nsIAboutModule::HIDE_FROM_ABOUTABOUT},
     {"restartrequired", "chrome://browser/content/aboutRestartRequired.xhtml",
      nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT},
+    {"newinstall", "chrome://browser/content/newInstallPage.html",
+     nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
+         nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+         nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT},
 };
 
 static nsAutoCString GetAboutModuleName(nsIURI* aURI) {
   nsAutoCString path;
   aURI->GetPathQueryRef(path);
 
   int32_t f = path.FindChar('#');
   if (f >= 0) path.SetLength(f);
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -107,16 +107,17 @@ static const mozilla::Module::ContractID
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "preferences", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "downloads", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "reader", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "restartrequired", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "welcome", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "policies", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "pocket-saved", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "pocket-signup", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
+    { NS_ABOUT_MODULE_CONTRACTID_PREFIX "newinstall", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #if defined(XP_WIN)
     { NS_IEHISTORYENUMERATOR_CONTRACTID, &kNS_WINIEHISTORYENUMERATOR_CID },
 #endif
 #if defined(MOZ_WIDGET_COCOA)
     { NS_MACATTRIBUTIONSERVICE_CONTRACTID, &kNS_MACATTRIBUTIONSERVICE_CID },
 #endif
     { nullptr }
     // clang-format on
--- a/browser/components/migration/FirefoxProfileMigrator.js
+++ b/browser/components/migration/FirefoxProfileMigrator.js
@@ -67,17 +67,17 @@ FirefoxProfileMigrator.prototype._getFil
   // copy non-existing files.
   return file.exists() ? file : null;
 };
 
 FirefoxProfileMigrator.prototype.getResources = function(aProfile) {
   let sourceProfileDir = aProfile ? this._getAllProfiles().get(aProfile.id) :
     Cc["@mozilla.org/toolkit/profile-service;1"]
       .getService(Ci.nsIToolkitProfileService)
-      .selectedProfile.rootDir;
+      .currentProfile.rootDir;
   if (!sourceProfileDir || !sourceProfileDir.exists() ||
       !sourceProfileDir.isReadable())
     return null;
 
   // Being a startup-only migrator, we can rely on
   // MigrationUtils.profileStartup being set.
   let currentProfileDir = MigrationUtils.profileStartup.directory;
 
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -16,16 +16,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
   ShellService: "resource:///modules/ShellService.jsm",
   UpdatePing: "resource://gre/modules/UpdatePing.jsm",
 });
 XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
   "@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
 
 XPCOMUtils.defineLazyGetter(this, "gSystemPrincipal",
   () => Services.scriptSecurityManager.getSystemPrincipal());
+XPCOMUtils.defineLazyGlobalGetters(this, [URL]);
 
 function shouldLoadURI(aURI) {
   if (aURI && !aURI.schemeIs("chrome"))
     return true;
 
   dump("*** Preventing external load of chrome: URI into browser window\n");
   dump("    Use --chrome <uri> instead\n");
   return false;
@@ -54,33 +55,47 @@ function resolveURIInternal(aCmdLine, aA
     uri = uriFixup.createFixupURI(aArgument, 0);
   } catch (e) {
     Cu.reportError(e);
   }
 
   return uri;
 }
 
+function getNewInstallPage() {
+  let url = new URL("about:newinstall");
+  let endpoint = Services.prefs.getCharPref("browser.dedicatedprofile.welcome.accounts.endpoint");
+  url.searchParams.set("endpoint", endpoint);
+  url.searchParams.set("channel", AppConstants.MOZ_UPDATE_CHANNEL);
+  return url.toString();
+}
+
 var gFirstWindow = false;
 
 const OVERRIDE_NONE        = 0;
 const OVERRIDE_NEW_PROFILE = 1;
 const OVERRIDE_NEW_MSTONE  = 2;
 const OVERRIDE_NEW_BUILD_ID = 3;
+const OVERRIDE_ALTERNATE_PROFILE = 4;
 /**
  * Determines whether a home page override is needed.
  * Returns:
  *  OVERRIDE_NEW_PROFILE if this is the first run with a new profile.
  *  OVERRIDE_NEW_MSTONE if this is the first run with a build with a different
  *                      Gecko milestone (i.e. right after an upgrade).
  *  OVERRIDE_NEW_BUILD_ID if this is the first run with a new build ID of the
  *                        same Gecko milestone (i.e. after a nightly upgrade).
  *  OVERRIDE_NONE otherwise.
  */
 function needHomepageOverride(prefb) {
+  let pService = Cc["@mozilla.org/toolkit/profile-service;1"].
+                 getService(Ci.nsIToolkitProfileService);
+  if (pService.createdAlternateProfile) {
+    return OVERRIDE_ALTERNATE_PROFILE;
+  }
   var savedmstone = prefb.getCharPref("browser.startup.homepage_override.mstone", "");
 
   if (savedmstone == "ignore")
     return OVERRIDE_NONE;
 
   var mstone = Services.appinfo.platformVersion;
 
   var savedBuildID = prefb.getCharPref("browser.startup.homepage_override.buildID", "");
@@ -183,49 +198,63 @@ function getPostUpdateOverridePage(defau
 function openBrowserWindow(cmdLine, triggeringPrincipal, urlOrUrlList, postData = null,
                            forcePrivate = false) {
   let chromeURL = AppConstants.BROWSER_CHROME_URL;
 
   let args;
   if (!urlOrUrlList) {
     // Just pass in the defaultArgs directly. We'll use system principal on the other end.
     args = [gBrowserContentHandler.defaultArgs];
-  } else if (Array.isArray(urlOrUrlList)) {
-    // There isn't an explicit way to pass a principal here, so we load multiple URLs
-    // with system principal when we get to actually loading them.
-    if (!triggeringPrincipal || !triggeringPrincipal.equals(gSystemPrincipal)) {
-      throw new Error("Can't open multiple URLs with something other than system principal.");
+  } else {
+    let pService = Cc["@mozilla.org/toolkit/profile-service;1"].
+                  getService(Ci.nsIToolkitProfileService);
+    if (cmdLine && cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH &&
+        pService.createdAlternateProfile) {
+      let url = getNewInstallPage();
+      if (Array.isArray(urlOrUrlList)) {
+        urlOrUrlList.unshift(url);
+      } else {
+        urlOrUrlList = [url, urlOrUrlList];
+      }
     }
-    // Passing an nsIArray for the url disables the "|"-splitting behavior.
-    let uriArray = Cc["@mozilla.org/array;1"]
-                     .createInstance(Ci.nsIMutableArray);
-    urlOrUrlList.forEach(function(uri) {
-      var sstring = Cc["@mozilla.org/supports-string;1"]
-                      .createInstance(Ci.nsISupportsString);
-      sstring.data = uri;
-      uriArray.appendElement(sstring);
-    });
-    args = [uriArray];
-  } else {
-    // Always pass at least 3 arguments to avoid the "|"-splitting behavior,
-    // ie. avoid the loadOneOrMoreURIs function.
-    // Also, we need to pass the triggering principal.
-    args = [urlOrUrlList,
-            null, // charset
-            null, // referer
-            postData,
-            undefined, // allowThirdPartyFixup; this would be `false` but that
-                       // needs a conversion. Hopefully bug 1485961 will fix.
-            undefined, // referrer policy
-            undefined, // user context id
-            null, // origin principal
-            triggeringPrincipal];
+
+    if (Array.isArray(urlOrUrlList)) {
+      // There isn't an explicit way to pass a principal here, so we load multiple URLs
+      // with system principal when we get to actually loading them.
+      if (!triggeringPrincipal || !triggeringPrincipal.equals(gSystemPrincipal)) {
+        throw new Error("Can't open multiple URLs with something other than system principal.");
+      }
+      // Passing an nsIArray for the url disables the "|"-splitting behavior.
+      let uriArray = Cc["@mozilla.org/array;1"]
+                      .createInstance(Ci.nsIMutableArray);
+      urlOrUrlList.forEach(function(uri) {
+        var sstring = Cc["@mozilla.org/supports-string;1"]
+                        .createInstance(Ci.nsISupportsString);
+        sstring.data = uri;
+        uriArray.appendElement(sstring);
+      });
+      args = [uriArray];
+    } else {
+      // Always pass at least 3 arguments to avoid the "|"-splitting behavior,
+      // ie. avoid the loadOneOrMoreURIs function.
+      // Also, we need to pass the triggering principal.
+      args = [urlOrUrlList,
+              null, // charset
+              null, // referer
+              postData,
+              undefined, // allowThirdPartyFixup; this would be `false` but that
+                         // needs a conversion. Hopefully bug 1485961 will fix.
+              undefined, // referrer policy
+              undefined, // user context id
+              null, // origin principal
+              triggeringPrincipal];
+    }
   }
 
-  if (cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
+  if (cmdLine && cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
     let win = Services.wm.getMostRecentWindow("navigator:blank");
     if (win) {
       // Remove the windowtype of our blank window so that we don't close it
       // later on when seeing cmdLine.preventDefault is true.
       win.document.documentElement.removeAttribute("windowtype");
 
       if (forcePrivate) {
         win.docShell
@@ -508,16 +537,22 @@ nsBrowserContentHandler.prototype = {
       // URL if we do end up showing an overridePage. This makes it possible
       // to have the overridePage's content vary depending on the version we're
       // upgrading from.
       let old_mstone = Services.prefs.getCharPref("browser.startup.homepage_override.mstone", "unknown");
       let old_buildId = Services.prefs.getCharPref("browser.startup.homepage_override.buildID", "unknown");
       override = needHomepageOverride(prefb);
       if (override != OVERRIDE_NONE) {
         switch (override) {
+          case OVERRIDE_ALTERNATE_PROFILE:
+            // Override the welcome page to explain why the user has a new
+            // profile. nsBrowserGlue.css will be responsible for showing the
+            // modal dialog.
+            overridePage = getNewInstallPage();
+            break;
           case OVERRIDE_NEW_PROFILE:
             // New profile.
             overridePage = Services.urlFormatter.formatURLPref("startup.homepage_welcome_url");
             additionalPage = Services.urlFormatter.formatURLPref("startup.homepage_welcome_url.additional");
             // Turn on 'later run' pages for new profiles.
             LaterRun.enabled = true;
             break;
           case OVERRIDE_NEW_MSTONE:
@@ -570,41 +605,43 @@ nsBrowserContentHandler.prototype = {
         startPage = HomePage.get();
     } catch (e) {
       Cu.reportError(e);
     }
 
     if (startPage == "about:blank")
       startPage = "";
 
-    let skipStartPage = override == OVERRIDE_NEW_PROFILE &&
+    let skipStartPage = ((override == OVERRIDE_NEW_PROFILE) || (override == OVERRIDE_ALTERNATE_PROFILE)) &&
       prefb.getBoolPref("browser.startup.firstrunSkipsHomepage");
     // Only show the startPage if we're not restoring an update session and are
     // not set to skip the start page on this profile
     if (overridePage && startPage && !willRestoreSession && !skipStartPage)
       return overridePage + "|" + startPage;
 
     return overridePage || startPage || "about:blank";
   },
 
   mFeatures: null,
 
   getFeatures: function bch_features(cmdLine) {
     if (this.mFeatures === null) {
       this.mFeatures = "";
 
-      try {
-        var width = cmdLine.handleFlagWithParam("width", false);
-        var height = cmdLine.handleFlagWithParam("height", false);
+      if (cmdLine) {
+        try {
+          var width = cmdLine.handleFlagWithParam("width", false);
+          var height = cmdLine.handleFlagWithParam("height", false);
 
-        if (width)
-          this.mFeatures += ",width=" + width;
-        if (height)
-          this.mFeatures += ",height=" + height;
-      } catch (e) {
+          if (width)
+            this.mFeatures += ",width=" + width;
+          if (height)
+            this.mFeatures += ",height=" + height;
+        } catch (e) {
+        }
       }
 
       // The global PB Service consumes this flag, so only eat it in per-window
       // PB builds.
       if (PrivateBrowsingUtils.isInTemporaryAutoStartMode) {
         this.mFeatures += ",private";
       }
 
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1482,16 +1482,32 @@ BrowserGlue.prototype = {
       if (enabled && !addon.isActive) {
         await addon.enable({allowSystemAddons: true});
       } else if (!enabled && addon.isActive) {
         await addon.disable({allowSystemAddons: true});
       }
     });
   },
 
+  _showNewInstallModal() {
+    // Allow other observers of the same topic to run while we open the dialog.
+    Services.tm.dispatchToMainThread(() => {
+      let win = BrowserWindowTracker.getTopWindow();
+
+      let stack = win.gBrowser.getPanel().querySelector(".browserStack");
+      let mask = win.document.createElementNS(XULNS, "box");
+      mask.setAttribute("id", "content-mask");
+      stack.appendChild(mask);
+
+      Services.ww.openWindow(win, "chrome://browser/content/newInstall.xul",
+                             "_blank", "chrome,modal,resizable=no,centerscreen", null);
+      mask.remove();
+    });
+  },
+
   // All initial windows have opened.
   _onWindowsRestored: function BG__onWindowsRestored() {
     if (this._windowsWereRestored) {
       return;
     }
     this._windowsWereRestored = true;
 
     BrowserUsageTelemetry.init();
@@ -1536,16 +1552,22 @@ BrowserGlue.prototype = {
         this._scheduleArbitrarilyLateIdleTasks();
       }
     };
     this._idleService.addIdleObserver(
       this._lateTasksIdleObserver, LATE_TASKS_IDLE_TIME_SEC);
 
     this._monitorScreenshotsPref();
     this._monitorWebcompatReporterPref();
+
+    let pService = Cc["@mozilla.org/toolkit/profile-service;1"].
+                  getService(Ci.nsIToolkitProfileService);
+    if (pService.createdAlternateProfile) {
+      this._showNewInstallModal();
+    }
   },
 
   /**
    * Use this function as an entry point to schedule tasks that
    * need to run only once after startup, and can be scheduled
    * by using an idle callback.
    *
    * The functions scheduled here will fire from idle callbacks
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -405,36 +405,16 @@ var gMainPane = {
     if (navigator.platform.toLowerCase().startsWith("win")) {
       emeUIEnabled = emeUIEnabled && parseFloat(Services.sysinfo.get("version")) >= 6;
     }
     if (!emeUIEnabled) {
       // Don't want to rely on .hidden for the toplevel groupbox because
       // of the pane hiding/showing code potentially interfering:
       document.getElementById("drmGroup").setAttribute("style", "display: none !important");
     }
-
-    if (AppConstants.MOZ_DEV_EDITION) {
-      let uAppData = OS.Constants.Path.userApplicationDataDir;
-      let ignoreSeparateProfile = OS.Path.join(uAppData, "ignore-dev-edition-profile");
-
-      setEventListener("separateProfileMode", "command", gMainPane.separateProfileModeChange);
-      let separateProfileModeCheckbox = document.getElementById("separateProfileMode");
-      setEventListener("getStarted", "click", gMainPane.onGetStarted);
-
-      OS.File.stat(ignoreSeparateProfile).then(() => separateProfileModeCheckbox.checked = false,
-        () => separateProfileModeCheckbox.checked = true);
-
-      if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
-        document.getElementById("sync-dev-edition-root").hidden = false;
-        fxAccounts.getSignedInUser().then(data => {
-          document.getElementById("getStarted").selectedIndex = data ? 1 : 0;
-        }).catch(Cu.reportError);
-      }
-    }
-
     // Initialize the Firefox Updates section.
     let version = AppConstants.MOZ_APP_VERSION_DISPLAY;
 
     // Include the build ID if this is an "a#" (nightly) build
     if (/a\d+$/.test(version)) {
       let buildID = Services.appinfo.appBuildID;
       let year = buildID.slice(0, 4);
       let month = buildID.slice(4, 6);
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -25,30 +25,16 @@
 </hbox>
 
 <!-- Startup -->
 <groupbox id="startupGroup"
           data-category="paneGeneral"
           hidden="true">
   <label><html:h2 data-l10n-id="startup-header"/></label>
 
-#ifdef MOZ_DEV_EDITION
-  <vbox id="separateProfileBox">
-    <checkbox id="separateProfileMode"
-              data-l10n-id="separate-profile-mode"/>
-    <hbox id="sync-dev-edition-root" align="center" class="indent" hidden="true">
-      <label id="useFirefoxSync" data-l10n-id="use-firefox-sync"/>
-      <deck id="getStarted">
-        <label class="text-link" data-l10n-id="get-started-not-logged-in"/>
-        <label class="text-link" data-l10n-id="get-started-configured"/>
-      </deck>
-    </hbox>
-  </vbox>
-#endif
-
   <vbox id="startupPageBox">
     <checkbox id="browserRestoreSession"
               data-l10n-id="startup-restore-previous-session"/>
     <hbox class="indent">
       <checkbox id="browserRestoreSessionQuitWarning"
                 preference="browser.sessionstore.warnOnQuit"
                 disabled="true"
                 data-l10n-id="startup-restore-warn-on-quit"/>
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/browser/newInstallPage.ftl
@@ -0,0 +1,32 @@
+# 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/.
+
+### For this feature, "installation" is used to mean "this discrete download of
+### Firefox" and "version" is used to mean "the specific revision number of a
+### given Firefox channel". These terms are not synonymous.
+
+title = Important News
+heading = Changes to your { -brand-short-name } profile
+
+changed-title = What changed?
+changed-desc-profiles = This installation of { -brand-short-name } has a new profile. A profile is the set of files where Firefox saves information such as bookmarks, passwords, and user preferences.
+changed-desc-dedicated = In order to make it easier and safer to switch between installations of Firefox (including Firefox, Firefox ESR, Firefox Beta, Firefox Developer Edition, and Firefox Nightly), this installation now has a dedicated profile. It does not automatically share your saved information with other Firefox installations.
+
+lost = <b>You have not lost any personal data or customizations.</b> If you’ve already saved information to Firefox on this computer, it is still available in another Firefox installation.
+
+options-title = What are my options?
+options-do-nothing = If you do nothing, your profile data in { -brand-short-name } will be different from profile data in other installations of Firefox.
+options-use-sync = If you would like all of your profile data to be the same on all installations of Firefox, you can use a { -fxaccount-brand-name } to keep them in sync.
+
+resources = Resources:
+support-link = Using the Profile Manager - Support Article
+
+sync-header = Sign in or create a { -fxaccount-brand-name }
+sync-label = Enter your email
+sync-input =
+  .placeholder = Email
+sync-button = Continue
+sync-terms = By proceeding, you agree to the <a data-l10n-name="terms">Terms of Service</a> and <a data-l10n-name="privacy">Privacy Notice</a>.
+sync-first = First time using { -sync-brand-name }? You'll need to sign in to every installation of Firefox to sync your information.
+sync-learn = Learn more
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/newInstall.dtd
@@ -0,0 +1,15 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- LOCALIZATION NOTE: For this feature, "installation" is used to mean "this
+ discrete download of Firefox" and "version" is used to mean "the specific
+ revision number of a given Firefox channel". These terms are not synonymous.
+-->
+
+<!ENTITY window.title "Important News">
+<!ENTITY window.style "width: 490px">
+<!ENTITY sync "To sync information you’ve already saved to Firefox with this installation of &brandShortName;, sign in with your &syncBrand.fxAccount.label;.">
+<!ENTITY continue-button "Continue">
+
+<!ENTITY mainText "This installation of &brandShortName; has a new profile. It does not share bookmarks, passwords, and user preferences with other installations of Firefox (including Firefox, Firefox ESR, Firefox Beta, Firefox Developer Edition, and Firefox Nightly) on this computer.">
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -55,15 +55,16 @@
     locale/browser-region/region.properties        (%chrome/browser-region/region.properties)
 # the following files are browser-specific overrides
     locale/browser/netError.dtd                (%chrome/overrides/netError.dtd)
     locale/browser/appstrings.properties       (%chrome/overrides/appstrings.properties)
     locale/browser/downloads/settingsChange.dtd  (%chrome/overrides/settingsChange.dtd)
 % locale pdf.js @AB_CD@ %locale/pdfviewer/
     locale/pdfviewer/viewer.properties             (%pdfviewer/viewer.properties)
     locale/pdfviewer/chrome.properties             (%pdfviewer/chrome.properties)
+    locale/browser/newInstall.dtd                  (%chrome/browser/newInstall.dtd)
 
 #ifdef XPI_NAME
 # Bug 1240628, restructure how l10n repacks work with feature addons
 # This is hacky, but ensures the chrome.manifest chain is complete
 [.] chrome.jar:
 % manifest features/chrome.manifest
 #endif
--- a/browser/themes/shared/browser.inc.css
+++ b/browser/themes/shared/browser.inc.css
@@ -271,8 +271,12 @@
 
 #cfr-notification-footer-users {
   opacity: 0.7;
 }
 
 #cfr-notification-footer-spacer {
   flex-grow: 1;
 }
+
+#content-mask {
+  background: rgba(0, 0, 0, 0.5);
+}
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -51,16 +51,18 @@
   skin/classic/browser/fullscreen/insecure.svg                 (../shared/fullscreen/insecure.svg)
   skin/classic/browser/fullscreen/secure.svg                   (../shared/fullscreen/secure.svg)
   skin/classic/browser/connection-secure.svg                   (../shared/identity-block/connection-secure.svg)
   skin/classic/browser/connection-mixed-passive-loaded.svg     (../shared/identity-block/connection-mixed-passive-loaded.svg)
   skin/classic/browser/connection-mixed-active-loaded.svg      (../shared/identity-block/connection-mixed-active-loaded.svg)
   skin/classic/browser/identity-icon.svg                       (../shared/identity-block/identity-icon.svg)
   skin/classic/browser/identity-icon-notice.svg                (../shared/identity-block/identity-icon-notice.svg)
   skin/classic/browser/info.svg                                (../shared/info.svg)
+  skin/classic/browser/newInstall.css                          (../shared/newInstall.css)
+  skin/classic/browser/newInstallPage.css                      (../shared/newInstallPage.css)
 
   skin/classic/browser/illustrations/error-session-restore.svg (../shared/illustrations/error-session-restore.svg)
 
   skin/classic/browser/notification-icons/autoplay-media.svg                (../shared/notification-icons/autoplay-media.svg)
   skin/classic/browser/notification-icons/autoplay-media-detailed.svg       (../shared/notification-icons/autoplay-media-detailed.svg)
   skin/classic/browser/notification-icons/autoplay-media-blocked.svg        (../shared/notification-icons/autoplay-media-blocked.svg)
   skin/classic/browser/notification-icons/camera-blocked.svg                (../shared/notification-icons/camera-blocked.svg)
   skin/classic/browser/notification-icons/camera.svg                        (../shared/notification-icons/camera.svg)
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/newInstall.css
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+window {
+  padding: 20px;
+}
+
+#alert {
+  width: 32px;
+  height: 32px;
+  margin-inline-end: 8px;
+  list-style-image: url("chrome://mozapps/skin/profile/information.svg");
+}
+
+description {
+  margin: 0 0 20px 0;
+  padding: 0;
+}
+
+.main-text {
+  font-size: 133%;
+  font-weight: bold;
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/newInstallPage.css
@@ -0,0 +1,102 @@
+/* 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/. */
+
+h1 {
+  margin: 0 0 40px 0;
+}
+
+h3 {
+  font-size: inherit;
+  margin: 0;
+}
+
+p {
+  margin: 0 0 20px 0;
+}
+
+#main {
+  max-width: 830px;
+  margin: 40px auto 0 auto;
+  padding: 0 5px;
+}
+
+#header {
+  margin-bottom: 40px;
+}
+
+#header > img {
+  height: 52px;
+}
+
+#content {
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+}
+
+#info {
+  flex: 1;
+  max-width: 420px;
+}
+
+#sync {
+  width: 250px;
+  text-align: center;
+}
+
+#sync-header {
+  font-size: 1.9em;
+}
+
+#sync-label {
+  font-size: 113%;
+  font-weight: bolder;
+  margin-bottom: 10px;
+}
+
+#sync-input-container {
+  margin-bottom: 20px;
+}
+
+#sync-input {
+  box-sizing: border-box;
+  width: 100%;
+  height: 40px;
+  padding-inline-start: 20px;
+}
+
+#sync-terms {
+  font-size: 87%;
+  margin-bottom: 20px;
+  color: var(--grey-50);
+}
+
+#sync-terms a,
+#sync-terms a:hover,
+#sync-terms a:visited {
+  color: inherit;
+}
+
+#sync-button-container {
+  margin-bottom: 20px;
+}
+
+#sync-button {
+  box-sizing: border-box;
+  width: 100%;
+  height: 42px;
+  cursor: pointer;
+  padding: 8px 0;
+  margin: 0;
+  color: #FFF !important;
+  background-color: var(--blue-50);
+  border-radius: 4px;
+}
+
+#sync-first, #sync-learn {
+  font-size: 87%;
+  color: var(--grey-50);
+  margin-bottom: 0;
+  text-align: start;
+}
--- a/devtools/client/sourceeditor/autocomplete.js
+++ b/devtools/client/sourceeditor/autocomplete.js
@@ -1,18 +1,19 @@
 /* vim:set ts=2 sw=2 sts=2 et tw=80:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
-const CSSCompleter = require("devtools/client/sourceeditor/css-autocompleter");
-const {KeyCodes} = require("devtools/client/shared/keycodes");
+
+loader.lazyRequireGetter(this, "KeyCodes", "devtools/client/shared/keycodes", true);
+loader.lazyRequireGetter(this, "CSSCompleter", "devtools/client/sourceeditor/css-autocompleter");
 
 const CM_TERN_SCRIPTS = [
   "chrome://devtools/content/sourceeditor/codemirror/addon/tern/tern.js",
   "chrome://devtools/content/sourceeditor/codemirror/addon/hint/show-hint.js",
 ];
 
 const autocompleteMap = new WeakMap();
 
--- a/devtools/shared/system.js
+++ b/devtools/shared/system.js
@@ -175,23 +175,24 @@ function getDeviceName() {
   } catch (e) {
     return null;
   }
 }
 
 function getProfileLocation() {
   // In child processes, we cannot access the profile location.
   try {
+    // For some reason this line must come first or in xpcshell tests
+    // nsXREDirProvider never gets initialised and so the profile service
+    // crashes on initialisation.
     const profd = Services.dirsvc.get("ProfD", Ci.nsIFile);
     const profservice = Cc["@mozilla.org/toolkit/profile-service;1"]
                         .getService(Ci.nsIToolkitProfileService);
-    for (const profile of profservice.profiles) {
-      if (profile.rootDir.path == profd.path) {
-        return profile.name;
-      }
+    if (profservice.currentProfile) {
+      return profservice.currentProfile.name;
     }
 
     return profd.leafName;
   } catch (e) {
     return "";
   }
 }
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -57,16 +57,17 @@
 #include "mozilla/dom/SessionStorageManager.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/ChildSHistory.h"
 #include "mozilla/dom/LoadURIOptionsBinding.h"
 
 #include "mozilla/net/ReferrerPolicy.h"
+#include "mozilla/net/UrlClassifierFeatureFactory.h"
 
 #include "nsIApplicationCacheChannel.h"
 #include "nsIApplicationCacheContainer.h"
 #include "nsIAppShell.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIAuthPrompt.h"
 #include "nsIAuthPrompt2.h"
 #include "nsICachingChannel.h"
@@ -6694,17 +6695,18 @@ nsresult nsDocShell::EndPageLoad(nsIWebP
       DisplayLoadError(aStatus, url, nullptr, aChannel);
       return NS_OK;
     }
 
     // Handle iframe document not loading error because source was
     // a tracking URL. We make a note of this iframe node by including
     // it in a dedicated array of blocked tracking nodes under its parent
     // document. (document of parent window of blocked document)
-    if (isTopFrame == false && aStatus == NS_ERROR_TRACKING_URI) {
+    if (!isTopFrame &&
+        UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aStatus)) {
       // frameElement is our nsIContent to be annotated
       RefPtr<Element> frameElement;
       nsPIDOMWindowOuter* thisWindow = GetWindow();
       if (!thisWindow) {
         return NS_OK;
       }
 
       frameElement = thisWindow->GetFrameElement();
@@ -6720,17 +6722,17 @@ nsresult nsDocShell::EndPageLoad(nsIWebP
       }
 
       RefPtr<Document> parentDoc;
       parentDoc = parentItem->GetDocument();
       if (!parentDoc) {
         return NS_OK;
       }
 
-      parentDoc->AddBlockedTrackingNode(frameElement);
+      parentDoc->AddBlockedNodeByClassifier(frameElement);
 
       return NS_OK;
     }
 
     if (sURIFixup) {
       //
       // Try and make an alternative URI from the old one
       //
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/IdleDeadline.h"
 #include "mozilla/dom/JSWindowActorService.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ReportingHeader.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/dom/WindowBinding.h"  // For IdleRequestCallback/Options
+#include "mozilla/net/UrlClassifierFeatureFactory.h"
 #include "IOActivityMonitor.h"
 #include "nsThreadUtils.h"
 #include "mozJSComponentLoader.h"
 #include "GeckoProfiler.h"
 #include "nsIException.h"
 
 namespace mozilla {
 namespace dom {
@@ -828,10 +829,16 @@ constexpr auto kSkipSelfHosted = JS::Sav
     const GlobalObject& aGlobal, const nsAString& aName,
     const WindowActorOptions& aOptions, ErrorResult& aRv) {
   MOZ_ASSERT(XRE_IsParentProcess());
 
   RefPtr<JSWindowActorService> service = JSWindowActorService::GetSingleton();
   service->RegisterWindowActor(aName, aOptions, aRv);
 }
 
+/* static */ bool ChromeUtils::IsClassifierBlockingErrorCode(
+    GlobalObject& aGlobal, uint32_t aError) {
+  return net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
+      static_cast<nsresult>(aError));
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -174,14 +174,17 @@ class ChromeUtils {
   static double LastExternalProtocolIframeAllowed(GlobalObject& aGlobal);
 
   static void ResetLastExternalProtocolIframeAllowed(GlobalObject& aGlobal);
 
   static void RegisterWindowActor(const GlobalObject& aGlobal,
                                   const nsAString& aName,
                                   const WindowActorOptions& aOptions,
                                   ErrorResult& aRv);
+
+  static bool IsClassifierBlockingErrorCode(GlobalObject& aGlobal,
+                                            uint32_t aError);
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_ChromeUtils__
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -5391,24 +5391,25 @@ already_AddRefed<Attr> Document::CreateA
 void Document::ResolveScheduledSVGPresAttrs() {
   for (auto iter = mLazySVGPresElements.Iter(); !iter.Done(); iter.Next()) {
     SVGElement* svg = iter.Get()->GetKey();
     svg->UpdateContentDeclarationBlock();
   }
   mLazySVGPresElements.Clear();
 }
 
-already_AddRefed<nsSimpleContentList> Document::BlockedTrackingNodes() const {
+already_AddRefed<nsSimpleContentList> Document::BlockedNodesByClassifier()
+    const {
   RefPtr<nsSimpleContentList> list = new nsSimpleContentList(nullptr);
 
-  nsTArray<nsWeakPtr> blockedTrackingNodes;
-  blockedTrackingNodes = mBlockedTrackingNodes;
-
-  for (unsigned long i = 0; i < blockedTrackingNodes.Length(); i++) {
-    nsWeakPtr weakNode = blockedTrackingNodes[i];
+  nsTArray<nsWeakPtr> blockedNodes;
+  blockedNodes = mBlockedNodesByClassifier;
+
+  for (unsigned long i = 0; i < blockedNodes.Length(); i++) {
+    nsWeakPtr weakNode = blockedNodes[i];
     nsCOMPtr<nsIContent> node = do_QueryReferent(weakNode);
     // Consider only nodes to which we have managed to get strong references.
     // Coping with nullptrs since it's expected for nodes to disappear when
     // nobody else is referring to them.
     if (node) {
       list->AppendElement(node);
     }
   }
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -998,16 +998,32 @@ class Document : public nsINode,
    * Get tracking content blocked flag for this document.
    */
   bool GetHasTrackingContentBlocked() {
     return mContentBlockingLog.HasBlockedAnyOfType(
         nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT);
   }
 
   /**
+   * Get fingerprinting content blocked flag for this document.
+   */
+  bool GetHasFingerprintingContentBlocked() {
+    return mContentBlockingLog.HasBlockedAnyOfType(
+        nsIWebProgressListener::STATE_BLOCKED_FINGERPRINTING_CONTENT);
+  }
+
+  /**
+   * Get cryptomining content blocked flag for this document.
+   */
+  bool GetHasCryptominingContentBlocked() {
+    return mContentBlockingLog.HasBlockedAnyOfType(
+        nsIWebProgressListener::STATE_BLOCKED_CRYPTOMINING_CONTENT);
+  }
+
+  /**
    * Get all cookies blocked flag for this document.
    */
   bool GetHasAllCookiesBlocked() {
     return mContentBlockingLog.HasBlockedAnyOfType(
         nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL);
   }
 
   /**
@@ -1040,16 +1056,38 @@ class Document : public nsINode,
   void SetHasTrackingContentBlocked(bool aHasTrackingContentBlocked,
                                     const nsACString& aOriginBlocked) {
     RecordContentBlockingLog(
         aOriginBlocked, nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT,
         aHasTrackingContentBlocked);
   }
 
   /**
+   * Set the fingerprinting content blocked flag for this document.
+   */
+  void SetHasFingerprintingContentBlocked(bool aHasFingerprintingContentBlocked,
+                                          const nsACString& aOriginBlocked) {
+    RecordContentBlockingLog(
+        aOriginBlocked,
+        nsIWebProgressListener::STATE_BLOCKED_FINGERPRINTING_CONTENT,
+        aHasFingerprintingContentBlocked);
+  }
+
+  /**
+   * Set the cryptomining content blocked flag for this document.
+   */
+  void SetHasCryptominingContentBlocked(bool aHasCryptominingContentBlocked,
+                                        const nsACString& aOriginBlocked) {
+    RecordContentBlockingLog(
+        aOriginBlocked,
+        nsIWebProgressListener::STATE_BLOCKED_CRYPTOMINING_CONTENT,
+        aHasCryptominingContentBlocked);
+  }
+
+  /**
    * Set the all cookies blocked flag for this document.
    */
   void SetHasAllCookiesBlocked(bool aHasAllCookiesBlocked,
                                const nsACString& aOriginBlocked) {
     RecordContentBlockingLog(aOriginBlocked,
                              nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL,
                              aHasAllCookiesBlocked);
   }
@@ -1117,16 +1155,54 @@ class Document : public nsINode,
   void SetHasTrackingContentLoaded(bool aHasTrackingContentLoaded,
                                    const nsACString& aOriginBlocked) {
     RecordContentBlockingLog(
         aOriginBlocked, nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT,
         aHasTrackingContentLoaded);
   }
 
   /**
+   * Get fingerprinting content loaded flag for this document.
+   */
+  bool GetHasFingerprintingContentLoaded() {
+    return mContentBlockingLog.HasBlockedAnyOfType(
+        nsIWebProgressListener::STATE_LOADED_FINGERPRINTING_CONTENT);
+  }
+
+  /**
+   * Set the fingerprinting content loaded flag for this document.
+   */
+  void SetHasFingerprintingContentLoaded(bool aHasFingerprintingContentLoaded,
+                                         const nsACString& aOriginBlocked) {
+    RecordContentBlockingLog(
+        aOriginBlocked,
+        nsIWebProgressListener::STATE_LOADED_FINGERPRINTING_CONTENT,
+        aHasFingerprintingContentLoaded);
+  }
+
+  /**
+   * Get cryptomining content loaded flag for this document.
+   */
+  bool GetHasCryptominingContentLoaded() {
+    return mContentBlockingLog.HasBlockedAnyOfType(
+        nsIWebProgressListener::STATE_LOADED_CRYPTOMINING_CONTENT);
+  }
+
+  /**
+   * Set the cryptomining content loaded flag for this document.
+   */
+  void SetHasCryptominingContentLoaded(bool aHasCryptominingContentLoaded,
+                                       const nsACString& aOriginBlocked) {
+    RecordContentBlockingLog(
+        aOriginBlocked,
+        nsIWebProgressListener::STATE_LOADED_CRYPTOMINING_CONTENT,
+        aHasCryptominingContentLoaded);
+  }
+
+  /**
    * Get the sandbox flags for this document.
    * @see nsSandboxFlags.h for the possible flags
    */
   uint32_t GetSandboxFlags() const { return mSandboxFlags; }
 
   /**
    * Get string representation of sandbox flags (null if no flags are set)
    */
@@ -1365,43 +1441,43 @@ class Document : public nsINode,
 
   // Resolve all SVG pres attrs scheduled in ScheduleSVGForPresAttrEvaluation
   void ResolveScheduledSVGPresAttrs();
 
   mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
   mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
   mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const;
 
-  // Returns the size of the mBlockedTrackingNodes array.
+  // Returns the size of the mBlockedNodesByClassifier array.
+  //
+  // This array contains nodes that have been blocked to prevent user tracking,
+  // fingerprinting, cryptomining, etc. They most likely have had their
+  // nsIChannel canceled by the URL classifier (Safebrowsing).
+  //
+  // A script can subsequently use GetBlockedNodesByClassifier()
+  // to get a list of references to these nodes.
+  //
+  // Note:
+  // This expresses how many tracking nodes have been blocked for this document
+  // since its beginning, not how many of them are still around in the DOM tree.
+  // Weak references to blocked nodes are added in the mBlockedNodesByClassifier
+  // array but they are not removed when those nodes are removed from the tree
+  // or even garbage collected.
+  long BlockedNodeByClassifierCount() const {
+    return mBlockedNodesByClassifier.Length();
+  }
+
+  //
+  // Returns strong references to mBlockedNodesByClassifier. (Document.h)
   //
   // This array contains nodes that have been blocked to prevent
   // user tracking. They most likely have had their nsIChannel
   // canceled by the URL classifier (Safebrowsing).
   //
-  // A script can subsequently use GetBlockedTrackingNodes()
-  // to get a list of references to these nodes.
-  //
-  // Note:
-  // This expresses how many tracking nodes have been blocked for this
-  // document since its beginning, not how many of them are still around
-  // in the DOM tree. Weak references to blocked nodes are added in the
-  // mBlockedTrackingNodesArray but they are not removed when those nodes
-  // are removed from the tree or even garbage collected.
-  long BlockedTrackingNodeCount() const {
-    return mBlockedTrackingNodes.Length();
-  }
-
-  //
-  // Returns strong references to mBlockedTrackingNodes. (Document.h)
-  //
-  // This array contains nodes that have been blocked to prevent
-  // user tracking. They most likely have had their nsIChannel
-  // canceled by the URL classifier (Safebrowsing).
-  //
-  already_AddRefed<nsSimpleContentList> BlockedTrackingNodes() const;
+  already_AddRefed<nsSimpleContentList> BlockedNodesByClassifier() const;
 
   // Helper method that returns true if the document has storage-access sandbox
   // flag.
   bool StorageAccessSandboxed() const;
 
  protected:
   friend class nsUnblockOnloadEvent;
 
@@ -3243,28 +3319,28 @@ class Document : public nsINode,
   /**
    * Asserts IsXULDocument, and can't return null.
    * Defined inline in XULDocument.h
    */
   inline mozilla::dom::XULDocument* AsXULDocument();
 
   /*
    * Given a node, get a weak reference to it and append that reference to
-   * mBlockedTrackingNodes. Can be used later on to look up a node in it.
+   * mBlockedNodesByClassifier. Can be used later on to look up a node in it.
    * (e.g., by the UI)
    */
-  void AddBlockedTrackingNode(nsINode* node) {
+  void AddBlockedNodeByClassifier(nsINode* node) {
     if (!node) {
       return;
     }
 
     nsWeakPtr weakNode = do_GetWeakReference(node);
 
     if (weakNode) {
-      mBlockedTrackingNodes.AppendElement(weakNode);
+      mBlockedNodesByClassifier.AppendElement(weakNode);
     }
   }
 
   gfxUserFontSet* GetUserFontSet(bool aFlushUserFontSet = true);
   void FlushUserFontSet();
   void MarkUserFontSetDirty();
   mozilla::dom::FontFaceSet* GetFonts() { return mFontFaceSet; }
 
@@ -4243,17 +4319,17 @@ class Document : public nsINode,
   // Count of live static clones of this document.
   uint32_t mStaticCloneCount;
 
   // Array of nodes that have been blocked to prevent user tracking.
   // They most likely have had their nsIChannel canceled by the URL
   // classifier. (Safebrowsing)
   //
   // Weak nsINode pointers are used to allow nodes to disappear.
-  nsTArray<nsWeakPtr> mBlockedTrackingNodes;
+  nsTArray<nsWeakPtr> mBlockedNodesByClassifier;
 
   // Weak reference to mScriptGlobalObject QI:d to nsPIDOMWindow,
   // updated on every set of mScriptGlobalObject.
   nsPIDOMWindowInner* mWindow;
 
   nsCOMPtr<nsIDocumentEncoder> mCachedEncoder;
 
   struct FrameRequest;
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -5403,16 +5403,40 @@ void nsGlobalWindowOuter::NotifyContentB
           }
         } else if (aEvent ==
                    nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT) {
           doc->SetHasTrackingContentLoaded(aBlocked, origin);
           if (!aBlocked) {
             unblocked = !doc->GetHasTrackingContentLoaded();
           }
         } else if (aEvent == nsIWebProgressListener::
+                                 STATE_BLOCKED_FINGERPRINTING_CONTENT) {
+          doc->SetHasFingerprintingContentBlocked(aBlocked, origin);
+          if (!aBlocked) {
+            unblocked = !doc->GetHasFingerprintingContentBlocked();
+          }
+        } else if (aEvent == nsIWebProgressListener::
+                                 STATE_LOADED_FINGERPRINTING_CONTENT) {
+          doc->SetHasFingerprintingContentLoaded(aBlocked, origin);
+          if (!aBlocked) {
+            unblocked = !doc->GetHasFingerprintingContentLoaded();
+          }
+        } else if (aEvent ==
+                   nsIWebProgressListener::STATE_BLOCKED_CRYPTOMINING_CONTENT) {
+          doc->SetHasCryptominingContentBlocked(aBlocked, origin);
+          if (!aBlocked) {
+            unblocked = !doc->GetHasCryptominingContentBlocked();
+          }
+        } else if (aEvent ==
+                   nsIWebProgressListener::STATE_LOADED_CRYPTOMINING_CONTENT) {
+          doc->SetHasCryptominingContentLoaded(aBlocked, origin);
+          if (!aBlocked) {
+            unblocked = !doc->GetHasCryptominingContentLoaded();
+          }
+        } else if (aEvent == nsIWebProgressListener::
                                  STATE_COOKIES_BLOCKED_BY_PERMISSION) {
           doc->SetHasCookiesBlockedByPermission(aBlocked, origin);
           if (!aBlocked) {
             unblocked = !doc->GetHasCookiesBlockedByPermission();
           }
         } else if (aEvent ==
                    nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) {
           doc->SetHasTrackingCookiesBlocked(aBlocked, origin);
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -46,16 +46,17 @@
 #include "mozAutoDocUpdate.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ImageTracker.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/net/UrlClassifierFeatureFactory.h"
 #include "mozilla/Preferences.h"
 
 #ifdef LoadImage
 // Undefine LoadImage to prevent naming conflict with Windows.
 #  undef LoadImage
 #endif
 
 using namespace mozilla;
@@ -179,26 +180,28 @@ nsImageLoadingContent::Notify(imgIReques
   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     uint32_t reqStatus;
     aRequest->GetImageStatus(&reqStatus);
     /* triage STATUS_ERROR */
     if (reqStatus & imgIRequest::STATUS_ERROR) {
       nsresult errorCode = NS_OK;
       aRequest->GetImageErrorCode(&errorCode);
 
-      /* Handle image not loading error because source was a tracking URL.
+      /* Handle image not loading error because source was a tracking URL (or
+       * fingerprinting, cryptomining, etc).
        * We make a note of this image node by including it in a dedicated
        * array of blocked tracking nodes under its parent document.
        */
-      if (errorCode == NS_ERROR_TRACKING_URI) {
+      if (net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
+              errorCode)) {
         nsCOMPtr<nsIContent> thisNode =
             do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
 
         Document* doc = GetOurOwnerDoc();
-        doc->AddBlockedTrackingNode(thisNode);
+        doc->AddBlockedNodeByClassifier(thisNode);
       }
     }
     nsresult status =
         reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
     return OnLoadComplete(aRequest, status);
   }
 
   if (aType == imgINotificationObserver::DECODE_COMPLETE) {
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -85,16 +85,17 @@
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/widget/IMEData.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/dom/HTMLObjectElementBinding.h"
 #include "mozilla/dom/HTMLEmbedElement.h"
 #include "mozilla/dom/HTMLObjectElement.h"
+#include "mozilla/net/UrlClassifierFeatureFactory.h"
 #include "mozilla/LoadInfo.h"
 #include "nsChannelClassifier.h"
 #include "nsFocusManager.h"
 
 #ifdef XP_WIN
 // Thanks so much, Microsoft! :(
 #  ifdef CreateEvent
 #    undef CreateEvent
@@ -988,17 +989,17 @@ nsObjectLoadingContent::OnStartRequest(n
           NS_LITERAL_STRING(
               " since it was found on an internal Firefox blocklist.");
       console->LogStringMessage(message.get());
     }
     mContentBlockingEnabled = true;
     return NS_ERROR_FAILURE;
   }
 
-  if (status == NS_ERROR_TRACKING_URI) {
+  if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status)) {
     mContentBlockingEnabled = true;
     return NS_ERROR_FAILURE;
   }
 
   if (!success) {
     LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this));
     // If the request fails, we still call LoadObject() to handle fallback
     // content and notifying of failure. (mChannelLoaded && !mChannel) indicates
@@ -1012,24 +1013,25 @@ nsObjectLoadingContent::OnStartRequest(n
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::OnStopRequest(nsIRequest* aRequest,
                                       nsISupports* aContext,
                                       nsresult aStatusCode) {
   AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStopRequest", NETWORK);
 
-  // Handle object not loading error because source was a tracking URL.
+  // Handle object not loading error because source was a tracking URL (or
+  // fingerprinting, cryptomining, etc.).
   // We make a note of this object node by including it in a dedicated
   // array of blocked tracking nodes under its parent document.
-  if (aStatusCode == NS_ERROR_TRACKING_URI) {
+  if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aStatusCode)) {
     nsCOMPtr<nsIContent> thisNode =
         do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
     if (thisNode && thisNode->IsInComposedDoc()) {
-      thisNode->GetComposedDoc()->AddBlockedTrackingNode(thisNode);
+      thisNode->GetComposedDoc()->AddBlockedNodeByClassifier(thisNode);
     }
   }
 
   if (aRequest != mChannel) {
     return NS_BINDING_ABORTED;
   }
 
   mChannel = nullptr;
--- a/dom/chrome-webidl/ChromeUtils.webidl
+++ b/dom/chrome-webidl/ChromeUtils.webidl
@@ -386,16 +386,20 @@ partial namespace ChromeUtils {
   /**
    * For testing purpose we need to reset this value.
    */
   [ChromeOnly]
   void resetLastExternalProtocolIframeAllowed();
 
   [ChromeOnly, Throws]
   void registerWindowActor(DOMString aName, WindowActorOptions aOptions);
+
+  [ChromeOnly]
+  // aError should a nsresult.
+  boolean isClassifierBlockingErrorCode(unsigned long aError);
 };
 
 /**
  * Dictionaries duplicating IPDL types in dom/ipc/DOMTypes.ipdlh
  * Used by requestPerformanceMetrics
  */
 
 dictionary MediaMemoryInfoDictionary {
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -74,16 +74,17 @@
 #include "mozilla/dom/PlayPromise.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/TextTrack.h"
 #include "mozilla/dom/VideoPlaybackQuality.h"
 #include "mozilla/dom/VideoTrack.h"
 #include "mozilla/dom/VideoTrackList.h"
 #include "mozilla/dom/WakeLock.h"
 #include "mozilla/dom/power/PowerManagerService.h"
+#include "mozilla/net/UrlClassifierFeatureFactory.h"
 #include "nsAttrValueInlines.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentUtils.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDisplayList.h"
 #include "nsDocShell.h"
 #include "nsError.h"
 #include "nsGenericHTMLElement.h"
@@ -634,23 +635,25 @@ HTMLMediaElement::MediaLoadListener::OnS
   }
 
   // Don't continue to load if the request failed or has been canceled.
   nsresult status;
   nsresult rv = aRequest->GetStatus(&status);
   NS_ENSURE_SUCCESS(rv, rv);
   if (NS_FAILED(status)) {
     if (element) {
-      // Handle media not loading error because source was a tracking URL.
+      // Handle media not loading error because source was a tracking URL (or
+      // fingerprinting, cryptomining, etc).
       // We make a note of this media node by including it in a dedicated
       // array of blocked tracking nodes under its parent document.
-      if (status == NS_ERROR_TRACKING_URI) {
+      if (net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
+              status)) {
         Document* ownerDoc = element->OwnerDoc();
         if (ownerDoc) {
-          ownerDoc->AddBlockedTrackingNode(element);
+          ownerDoc->AddBlockedNodeByClassifier(element);
         }
       }
       element->NotifyLoadError(
           nsPrintfCString("%u: %s", uint32_t(status), "Request failed"));
     }
     return status;
   }
 
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -23,16 +23,17 @@
 #include "xpcpublic.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIContent.h"
 #include "nsJSUtils.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/SRILogHelper.h"
+#include "mozilla/net/UrlClassifierFeatureFactory.h"
 #include "nsGkAtoms.h"
 #include "nsNetUtil.h"
 #include "nsGlobalWindowInner.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPrincipal.h"
 #include "nsJSPrincipals.h"
@@ -3244,18 +3245,19 @@ void ScriptLoader::ReportErrorToConsole(
   }
 
   bool isScript = !aRequest->IsModuleRequest();
   const char* message;
   if (aResult == NS_ERROR_MALFORMED_URI) {
     message = isScript ? "ScriptSourceMalformed" : "ModuleSourceMalformed";
   } else if (aResult == NS_ERROR_DOM_BAD_URI) {
     message = isScript ? "ScriptSourceNotAllowed" : "ModuleSourceNotAllowed";
-  } else if (aResult == NS_ERROR_TRACKING_URI) {
-    // Tracking protection errors already show their own console messages.
+  } else if (net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
+                 aResult)) {
+    // Blocking classifier error codes already show their own console messages.
     return;
   } else {
     message = isScript ? "ScriptSourceLoadFailed" : "ModuleSourceLoadFailed";
   }
 
   NS_ConvertUTF8toUTF16 url(aRequest->mURI->GetSpecOrDefault());
   const char16_t* params[] = {url.get()};
 
@@ -3280,23 +3282,25 @@ void ScriptLoader::ReportPreloadErrorsTo
       ReportPreloadErrorsToConsole(childRequest);
     }
   }
 }
 
 void ScriptLoader::HandleLoadError(ScriptLoadRequest* aRequest,
                                    nsresult aResult) {
   /*
-   * Handle script not loading error because source was a tracking URL.
+   * Handle script not loading error because source was an tracking URL (or
+   * fingerprinting, cryptoming, etc).
    * We make a note of this script node by including it in a dedicated
    * array of blocked tracking nodes under its parent document.
    */
-  if (aResult == NS_ERROR_TRACKING_URI) {
+  if (net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
+          aResult)) {
     nsCOMPtr<nsIContent> cont = do_QueryInterface(aRequest->Element());
-    mDocument->AddBlockedTrackingNode(cont);
+    mDocument->AddBlockedNodeByClassifier(cont);
   }
 
   if (aRequest->IsModuleRequest() && !aRequest->mIsInline) {
     auto request = aRequest->AsModuleRequest();
     SetModuleFetchFinishedAndResumeWaitingRequests(request, aResult);
   }
 
   if (aRequest->mInDeferList) {
--- a/dom/webidl/HTMLDocument.webidl
+++ b/dom/webidl/HTMLDocument.webidl
@@ -56,24 +56,25 @@ interface HTMLDocument : Document {
   //             the implementation is no-op.
   // XXXbz do we actually need these anymore?
   void                      captureEvents();
   void                      releaseEvents();
 };
 
 partial interface HTMLDocument {
   /*
-   * Number of nodes that have been blocked by
-   * the Safebrowsing API to prevent tracking.
+   * Number of nodes that have been blocked by the Safebrowsing API to prevent
+   * tracking, cryptomining and so on. This method is for testing only.
    */
   [ChromeOnly, Pure]
-  readonly attribute long blockedTrackingNodeCount;
+  readonly attribute long blockedNodeByClassifierCount;
 
   /*
-   * List of nodes that have been blocked by
-   * the Safebrowsing API to prevent tracking.
+   * List of nodes that have been blocked by the Safebrowsing API to prevent
+   * tracking, fingerprinting, cryptomining and so on. This method is for
+   * testing only.
    */
   [ChromeOnly, Pure]
-  readonly attribute NodeList blockedTrackingNodes;
+  readonly attribute NodeList blockedNodesByClassifier;
 
   [ChromeOnly]
   void userInteractionForTesting();
 };
--- a/gfx/layers/wr/ClipManager.cpp
+++ b/gfx/layers/wr/ClipManager.cpp
@@ -11,21 +11,19 @@
 #include "LayersLogging.h"
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "nsDisplayList.h"
 #include "nsStyleStructInlines.h"
 #include "UnitTransforms.h"
 
+// clang-format off
 #define CLIP_LOG(...)
-
 //#define CLIP_LOG(...) printf_stderr("CLIP: " __VA_ARGS__)
-
-// clang-format off
 //#define CLIP_LOG(...) if (XRE_IsContentProcess()) printf_stderr("CLIP: " __VA_ARGS__)
 // clang-format on
 
 namespace mozilla {
 namespace layers {
 
 ClipManager::ClipManager() : mManager(nullptr), mBuilder(nullptr) {}
 
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -11,22 +11,21 @@
 #include "mozilla/webrender/RendererOGL.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/webrender/RenderCompositor.h"
 #include "mozilla/widget/CompositorWidget.h"
 #include "mozilla/layers/SynchronousTask.h"
 #include "TextDrawTarget.h"
 
+// clang-format off
 #define WRDL_LOG(...)
-
 //#define WRDL_LOG(...) printf_stderr("WRDL(%p): " __VA_ARGS__)
-
-//#define WRDL_LOG(...) if (XRE_IsContentProcess()) printf_stderr("WRDL(%p): "
-//__VA_ARGS__)
+//#define WRDL_LOG(...) if (XRE_IsContentProcess()) printf_stderr("WRDL(%p): " __VA_ARGS__)
+// clang-format on
 
 namespace mozilla {
 namespace wr {
 
 using layers::Stringify;
 
 MOZ_DEFINE_MALLOC_SIZE_OF(WebRenderMallocSizeOf)
 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(WebRenderMallocEnclosingSizeOf)
--- a/js/xpconnect/src/xpc.msg
+++ b/js/xpconnect/src/xpc.msg
@@ -233,8 +233,10 @@ XPC_MSG_DEF(NS_ERROR_DOM_NOT_ALLOWED_ERR
 
 /* Codes related to the URIClassifier service */
 XPC_MSG_DEF(NS_ERROR_MALWARE_URI                      , "The URI is malware")
 XPC_MSG_DEF(NS_ERROR_PHISHING_URI                     , "The URI is phishing")
 XPC_MSG_DEF(NS_ERROR_TRACKING_URI                     , "The URI is tracking")
 XPC_MSG_DEF(NS_ERROR_UNWANTED_URI                     , "The URI is unwanted")
 XPC_MSG_DEF(NS_ERROR_BLOCKED_URI                      , "The URI is blocked")
 XPC_MSG_DEF(NS_ERROR_HARMFUL_URI                      , "The URI is harmful")
+XPC_MSG_DEF(NS_ERROR_FINGERPRINTING_URI               , "The URI is fingerprinting")
+XPC_MSG_DEF(NS_ERROR_CRYPTOMINING_URI                 , "The URI is cryptomining")
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -7875,18 +7875,17 @@ bool nsDisplayTransform::CreateWebRender
     // we can just stash the transform on the StackingContextHelper and
     // apply it to any scroll data that are created inside this
     // nsDisplayTransform.
     deferredTransformItem = Some(this);
   }
 
   // If it looks like we're animated, we should rasterize in local space
   // (disabling subpixel-aa and global pixel snapping)
-  bool animated =
-      ActiveLayerTracker::IsStyleMaybeAnimated(Frame(), eCSSProperty_transform);
+  bool animated = Frame()->HasAnimationOfTransform();
 
   wr::StackingContextParams params;
   params.mBoundTransform = &newTransformMatrix;
   params.animation = animationsId ? &prop : nullptr;
   params.mTransformPtr = transformForSC;
   params.is_backface_visible = !BackfaceIsHidden();
   params.mDeferredTransformItem = deferredTransformItem;
   params.mAnimated = animated;
--- a/layout/reftests/details-summary/reftest.list
+++ b/layout/reftests/details-summary/reftest.list
@@ -83,17 +83,17 @@ fuzzy-if(gtkWidget||OSX,0-1,0-20) == mou
 == mouse-click-change-details-to-display-none.html open-single-summary.html
 == mouse-click-change-summary-to-display-none.html mouse-click-change-summary-to-display-none-ref.html
 == mouse-click-move-summary-to-different-details.html mouse-click-move-summary-to-different-details-ref.html
 
 # Dispatch mouse click to out-of-flow details or summary
 == mouse-click-fixed-summary.html open-fixed-summary.html
 == mouse-click-twice-fixed-summary.html fixed-summary.html
 == mouse-click-float-details.html open-float-details.html
-fuzzy(0-1,0-1) == mouse-click-twice-float-details.html float-details.html # Bug 1316430
+fuzzy(0-4,0-1) == mouse-click-twice-float-details.html float-details.html # Bug 1316430
 
 # Dispatch keyboard event to summary
 == key-enter-single-summary.html open-single-summary.html
 == key-enter-open-second-summary.html open-multiple-summary.html
 == key-enter-prevent-default.html single-summary.html
 == key-space-single-summary.html open-single-summary.html
 
 # Generated content bits
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -41,16 +41,17 @@
 #include "nsThreadUtils.h"
 #include "nsGkAtoms.h"
 #include "nsIThreadInternal.h"
 #include "nsINetworkPredictor.h"
 #include "nsStringStream.h"
 #include "mozilla/dom/MediaList.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/URL.h"
+#include "mozilla/net/UrlClassifierFeatureFactory.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/ConsoleReportCollector.h"
 #include "mozilla/ServoUtils.h"
 #include "mozilla/css/StreamLoader.h"
 
@@ -590,30 +591,32 @@ nsresult SheetLoadData::VerifySheetReady
     LOG_WARN(("  No document and not non-document sheet; dropping load"));
     mLoader->SheetComplete(this, NS_BINDING_ABORTED);
     return NS_OK;
   }
 
   if (NS_FAILED(aStatus)) {
     LOG_WARN(
         ("  Load failed: status 0x%" PRIx32, static_cast<uint32_t>(aStatus)));
-    // Handle sheet not loading error because source was a tracking URL.
+    // Handle sheet not loading error because source was a tracking URL (or
+    // fingerprinting, cryptomining, etc).
     // We make a note of this sheet node by including it in a dedicated
     // array of blocked tracking nodes under its parent document.
     //
     // Multiple sheet load instances might be tied to this request,
     // we annotate each one linked to a valid owning element (node).
-    if (aStatus == NS_ERROR_TRACKING_URI) {
+    if (net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
+            aStatus)) {
       Document* doc = mLoader->GetDocument();
       if (doc) {
         for (SheetLoadData* data = this; data; data = data->mNext) {
           // mOwningElement may be null but AddBlockTrackingNode can cope
           nsCOMPtr<nsIContent> content =
               do_QueryInterface(data->mOwningElement);
-          doc->AddBlockedTrackingNode(content);
+          doc->AddBlockedNodeByClassifier(content);
         }
       }
     }
     mLoader->SheetComplete(this, aStatus);
     return NS_OK;
   }
 
   if (!aChannel) {
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -375,16 +375,17 @@ opaque-types = [
     "gfxSize",  # <- union { struct { T width; T height; }; T components[2] };
     "gfxSize_Super",  # Ditto.
     "mozilla::StyleAnimationValue",
     "StyleAnimationValue", # pulls in a whole bunch of stuff we don't need in the bindings
     "mozilla::dom::.*Callback", # Pulls in ErrorResult and other things that
                                 # don't align properly on Linux 32-bit
     "mozilla::SchedulerGroup", # Non-standard-layout packing of field into superclass
     "mozilla::detail::GkAtoms", # https://bugzilla.mozilla.org/show_bug.cgi?id=1517685
+    "mozilla::detail::ThreadLocal.*",
 ]
 
 # All cbindgen-types are in mod "structs::root::mozilla".
 cbindgen-types = [
     { gecko = "StyleAppearance", servo = "values::specified::Appearance" },
     { gecko = "StyleComputedFontStretchRange", servo = "font_face::ComputedFontStretchRange" },
     { gecko = "StyleComputedFontStyleDescriptor", servo = "font_face::ComputedFontStyleDescriptor" },
     { gecko = "StyleComputedFontWeightRange", servo = "font_face::ComputedFontWeightRange" },
--- a/mfbt/ThreadLocal.h
+++ b/mfbt/ThreadLocal.h
@@ -208,23 +208,23 @@ inline void ThreadLocal<T, Storage>::set
   if (!succeeded) {
     MOZ_CRASH();
   }
 }
 
 #if (defined(XP_WIN) || defined(MACOSX_HAS_THREAD_LOCAL)) && \
     !defined(__MINGW32__)
 #  define MOZ_THREAD_LOCAL(TYPE)               \
-    thread_local mozilla::detail::ThreadLocal< \
-        TYPE, mozilla::detail::ThreadLocalNativeStorage>
+    thread_local ::mozilla::detail::ThreadLocal< \
+        TYPE, ::mozilla::detail::ThreadLocalNativeStorage>
 #elif defined(HAVE_THREAD_TLS_KEYWORD)
 #  define MOZ_THREAD_LOCAL(TYPE)           \
-    __thread mozilla::detail::ThreadLocal< \
-        TYPE, mozilla::detail::ThreadLocalNativeStorage>
+    __thread ::mozilla::detail::ThreadLocal< \
+        TYPE, ::mozilla::detail::ThreadLocalNativeStorage>
 #else
 #  define MOZ_THREAD_LOCAL(TYPE) \
-    mozilla::detail::ThreadLocal<TYPE, mozilla::detail::ThreadLocalKeyStorage>
+    ::mozilla::detail::ThreadLocal<TYPE, ::mozilla::detail::ThreadLocalKeyStorage>
 #endif
 
 }  // namespace detail
 }  // namespace mozilla
 
 #endif /* mozilla_ThreadLocal_h */
--- a/netwerk/base/SimpleChannelParent.cpp
+++ b/netwerk/base/SimpleChannelParent.cpp
@@ -24,29 +24,30 @@ bool SimpleChannelParent::Init(const uin
 
 NS_IMETHODIMP
 SimpleChannelParent::SetParentListener(HttpChannelParentListener* aListener) {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
-SimpleChannelParent::NotifyTrackingProtectionDisabled() {
+SimpleChannelParent::NotifyChannelClassifierProtectionDisabled(
+    uint32_t aAcceptedReason) {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SimpleChannelParent::NotifyCookieAllowed() {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
-SimpleChannelParent::NotifyTrackingCookieBlocked(uint32_t aRejectedReason) {
+SimpleChannelParent::NotifyCookieBlocked(uint32_t aRejectedReason) {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SimpleChannelParent::NotifyTrackingResource(bool aIsThirdParty) {
   // Nothing to do.
   return NS_OK;
--- a/netwerk/base/nsIParentChannel.idl
+++ b/netwerk/base/nsIParentChannel.idl
@@ -25,32 +25,32 @@ class HttpChannelParentListener;
 interface nsIParentChannel : nsIStreamListener
 {
   /**
    * Called to set the HttpChannelParentListener object (optional).
    */
   [noscript] void setParentListener(in HttpChannelParentListener listener);
 
   /**
-   * Called to notify the HttpChannelChild that tracking protection was
-   * disabled for this load.
+   * Called to notify the HttpChannelChild that channel classifier protection
+   * was disabled for this load.
    */
-  [noscript] void notifyTrackingProtectionDisabled();
+  [noscript] void notifyChannelClassifierProtectionDisabled(in uint32_t aAcceptedReason);
 
   /**
    * Called to notify the HttpChannelChild that cookie has been allowed for
    * this load.
    */
   [noscript] void notifyCookieAllowed();
 
   /**
    * Called to notify the HttpChannelChild that cookie has been blocked for
    * this load.
    */
-  [noscript] void notifyTrackingCookieBlocked(in uint32_t aRejectedReason);
+  [noscript] void notifyCookieBlocked(in uint32_t aRejectedReason);
 
   /**
    * Called to notify the HttpChannelChild that flash plugin state has changed.
    */
   [noscript] void notifyFlashPluginStateChanged(in nsIHttpChannel_FlashPluginState aState);
 
    /**
    * Called to set matched information when URL matches SafeBrowsing list.
--- a/netwerk/protocol/data/DataChannelParent.cpp
+++ b/netwerk/protocol/data/DataChannelParent.cpp
@@ -24,29 +24,30 @@ bool DataChannelParent::Init(const uint3
 
 NS_IMETHODIMP
 DataChannelParent::SetParentListener(HttpChannelParentListener *aListener) {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DataChannelParent::NotifyTrackingProtectionDisabled() {
+DataChannelParent::NotifyChannelClassifierProtectionDisabled(
+    uint32_t aAcceptedReason) {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DataChannelParent::NotifyCookieAllowed() {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DataChannelParent::NotifyTrackingCookieBlocked(uint32_t aRejectedReason) {
+DataChannelParent::NotifyCookieBlocked(uint32_t aRejectedReason) {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DataChannelParent::NotifyTrackingResource(bool aIsThirdParty) {
   // Nothing to do.
   return NS_OK;
--- a/netwerk/protocol/file/FileChannelParent.cpp
+++ b/netwerk/protocol/file/FileChannelParent.cpp
@@ -24,29 +24,30 @@ bool FileChannelParent::Init(const uint3
 
 NS_IMETHODIMP
 FileChannelParent::SetParentListener(HttpChannelParentListener *aListener) {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
-FileChannelParent::NotifyTrackingProtectionDisabled() {
+FileChannelParent::NotifyChannelClassifierProtectionDisabled(
+    uint32_t aAcceptedReason) {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FileChannelParent::NotifyCookieAllowed() {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
-FileChannelParent::NotifyTrackingCookieBlocked(uint32_t aRejectedReason) {
+FileChannelParent::NotifyCookieBlocked(uint32_t aRejectedReason) {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FileChannelParent::NotifyTrackingResource(bool aIsThirdParty) {
   // Nothing to do.
   return NS_OK;
--- a/netwerk/protocol/ftp/FTPChannelParent.cpp
+++ b/netwerk/protocol/ftp/FTPChannelParent.cpp
@@ -495,29 +495,30 @@ FTPChannelParent::OnDataAvailable(nsIReq
 
 NS_IMETHODIMP
 FTPChannelParent::SetParentListener(HttpChannelParentListener* aListener) {
   // Do not need ptr to HttpChannelParentListener.
   return NS_OK;
 }
 
 NS_IMETHODIMP
-FTPChannelParent::NotifyTrackingProtectionDisabled() {
+FTPChannelParent::NotifyChannelClassifierProtectionDisabled(
+    uint32_t aAcceptedReason) {
   // One day, this should probably be filled in.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FTPChannelParent::NotifyCookieAllowed() {
   // One day, this should probably be filled in.
   return NS_OK;
 }
 
 NS_IMETHODIMP
-FTPChannelParent::NotifyTrackingCookieBlocked(uint32_t aRejectedReason) {
+FTPChannelParent::NotifyCookieBlocked(uint32_t aRejectedReason) {
   // One day, this should probably be filled in.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FTPChannelParent::NotifyTrackingResource(bool aIsThirdParty) {
   // One day, this should probably be filled in.
   return NS_OK;
--- a/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
@@ -286,30 +286,34 @@ IPCResult HttpBackgroundChannelChild::Re
     return IPC_OK();
   }
 
   mChannelChild->ProcessDivertMessages();
 
   return IPC_OK();
 }
 
-IPCResult HttpBackgroundChannelChild::RecvNotifyTrackingProtectionDisabled() {
+IPCResult
+HttpBackgroundChannelChild::RecvNotifyChannelClassifierProtectionDisabled(
+    const uint32_t& aAcceptedReason) {
   LOG(
-      ("HttpBackgroundChannelChild::RecvNotifyTrackingProtectionDisabled "
-       "[this=%p]\n",
-       this));
+      ("HttpBackgroundChannelChild::"
+       "RecvNotifyChannelClassifierProtectionDisabled [this=%p "
+       "aAcceptedReason=%" PRIu32 "]\n",
+       this, aAcceptedReason));
   MOZ_ASSERT(OnSocketThread());
 
   if (NS_WARN_IF(!mChannelChild)) {
     return IPC_OK();
   }
 
-  // NotifyTrackingProtectionDisabled has no order dependency to OnStartRequest.
-  // It this be handled as soon as possible
-  mChannelChild->ProcessNotifyTrackingProtectionDisabled();
+  // NotifyChannelClassifierProtectionDisabled has no order dependency to
+  // OnStartRequest. It this be handled as soon as possible
+  mChannelChild->ProcessNotifyChannelClassifierProtectionDisabled(
+      aAcceptedReason);
 
   return IPC_OK();
 }
 
 IPCResult HttpBackgroundChannelChild::RecvNotifyCookieAllowed() {
   LOG(("HttpBackgroundChannelChild::RecvNotifyCookieAllowed [this=%p]\n",
        this));
   MOZ_ASSERT(OnSocketThread());
@@ -318,28 +322,29 @@ IPCResult HttpBackgroundChannelChild::Re
     return IPC_OK();
   }
 
   mChannelChild->ProcessNotifyCookieAllowed();
 
   return IPC_OK();
 }
 
-IPCResult HttpBackgroundChannelChild::RecvNotifyTrackingCookieBlocked(
+IPCResult HttpBackgroundChannelChild::RecvNotifyCookieBlocked(
     const uint32_t& aRejectedReason) {
-  LOG((
-      "HttpBackgroundChannelChild::RecvNotifyTrackingCookieBlocked [this=%p]\n",
-      this));
+  LOG(
+      ("HttpBackgroundChannelChild::RecvNotifyCookieBlocked [this=%p "
+       "aRejectedReason=%" PRIu32 "]\n",
+       this, aRejectedReason));
   MOZ_ASSERT(OnSocketThread());
 
   if (NS_WARN_IF(!mChannelChild)) {
     return IPC_OK();
   }
 
-  mChannelChild->ProcessNotifyTrackingCookieBlocked(aRejectedReason);
+  mChannelChild->ProcessNotifyCookieBlocked(aRejectedReason);
 
   return IPC_OK();
 }
 
 IPCResult HttpBackgroundChannelChild::RecvNotifyTrackingResource(
     const bool& aIsThirdParty) {
   LOG(
       ("HttpBackgroundChannelChild::RecvNotifyTrackingResource thirdparty=%d "
--- a/netwerk/protocol/http/HttpBackgroundChannelChild.h
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.h
@@ -58,22 +58,22 @@ class HttpBackgroundChannelChild final :
   IPCResult RecvOnStatus(const nsresult& aStatus) override;
 
   IPCResult RecvFlushedForDiversion() override;
 
   IPCResult RecvDivertMessages() override;
 
   IPCResult RecvOnStartRequestSent() override;
 
-  IPCResult RecvNotifyTrackingProtectionDisabled() override;
+  IPCResult RecvNotifyChannelClassifierProtectionDisabled(
+      const uint32_t& aAcceptedReason) override;
 
   IPCResult RecvNotifyCookieAllowed() override;
 
-  IPCResult RecvNotifyTrackingCookieBlocked(
-      const uint32_t& aRejectedReason) override;
+  IPCResult RecvNotifyCookieBlocked(const uint32_t& aRejectedReason) override;
 
   IPCResult RecvNotifyTrackingResource(const bool& aIsThirdParty) override;
 
   IPCResult RecvNotifyFlashPluginStateChanged(
       const nsIHttpChannel::FlashPluginState& aState) override;
 
   IPCResult RecvSetClassifierMatchedInfo(const ClassifierInfo& info) override;
 
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
@@ -310,43 +310,48 @@ bool HttpBackgroundChannelParent::OnDive
   // the OnDataAvailables and OnStopRequest to associated HttpChannelParent.
   if (!SendDivertMessages()) {
     return false;
   }
 
   return true;
 }
 
-bool HttpBackgroundChannelParent::OnNotifyTrackingProtectionDisabled() {
+bool HttpBackgroundChannelParent::OnNotifyChannelClassifierProtectionDisabled(
+    uint32_t aAcceptedReason) {
   LOG(
-      ("HttpBackgroundChannelParent::OnNotifyTrackingProtectionDisabled "
-       "[this=%p]\n",
-       this));
+      ("HttpBackgroundChannelParent::"
+       "OnNotifyChannelClassifierProtectionDisabled [this=%p - "
+       "aAcceptedReason=%" PRIu32 "]\n",
+       this, aAcceptedReason));
   AssertIsInMainProcess();
 
   if (NS_WARN_IF(!mIPCOpened)) {
     return false;
   }
 
   if (!IsOnBackgroundThread()) {
     MutexAutoLock lock(mBgThreadMutex);
+    RefPtr<HttpBackgroundChannelParent> self = this;
     nsresult rv = mBackgroundThread->Dispatch(
-        NewRunnableMethod(
+        NS_NewRunnableFunction(
             "net::HttpBackgroundChannelParent::"
-            "OnNotifyTrackingProtectionDisabled",
-            this,
-            &HttpBackgroundChannelParent::OnNotifyTrackingProtectionDisabled),
+            "OnNotifyChannelClassifierProtectionDisabled",
+            [self, aAcceptedReason]() {
+              self->OnNotifyChannelClassifierProtectionDisabled(
+                  aAcceptedReason);
+            }),
         NS_DISPATCH_NORMAL);
 
     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
 
     return NS_SUCCEEDED(rv);
   }
 
-  return SendNotifyTrackingProtectionDisabled();
+  return SendNotifyChannelClassifierProtectionDisabled(aAcceptedReason);
 }
 
 bool HttpBackgroundChannelParent::OnNotifyCookieAllowed() {
   LOG(("HttpBackgroundChannelParent::OnNotifyCookieAllowed [this=%p]\n", this));
   AssertIsInMainProcess();
 
   if (NS_WARN_IF(!mIPCOpened)) {
     return false;
@@ -364,43 +369,45 @@ bool HttpBackgroundChannelParent::OnNoti
     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
 
     return NS_SUCCEEDED(rv);
   }
 
   return SendNotifyCookieAllowed();
 }
 
-bool HttpBackgroundChannelParent::OnNotifyTrackingCookieBlocked(
+bool HttpBackgroundChannelParent::OnNotifyCookieBlocked(
     uint32_t aRejectedReason) {
-  LOG(("HttpBackgroundChannelParent::OnNotifyTrackingCookieBlocked [this=%p]\n",
-       this));
+  LOG(
+      ("HttpBackgroundChannelParent::OnNotifyCookieBlocked [this=%p "
+       "aRejectedReason=%" PRIu32 "]\n",
+       this, aRejectedReason));
   AssertIsInMainProcess();
 
   if (NS_WARN_IF(!mIPCOpened)) {
     return false;
   }
 
   if (!IsOnBackgroundThread()) {
     MutexAutoLock lock(mBgThreadMutex);
     RefPtr<HttpBackgroundChannelParent> self = this;
     nsresult rv = mBackgroundThread->Dispatch(
         NS_NewRunnableFunction(
-            "net::HttpBackgroundChannelParent::OnNotifyTrackingCookieBlocked",
+            "net::HttpBackgroundChannelParent::OnNotifyCookieBlocked",
             [self, aRejectedReason]() {
-              self->OnNotifyTrackingCookieBlocked(aRejectedReason);
+              self->OnNotifyCookieBlocked(aRejectedReason);
             }),
         NS_DISPATCH_NORMAL);
 
     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
 
     return NS_SUCCEEDED(rv);
   }
 
-  return SendNotifyTrackingCookieBlocked(aRejectedReason);
+  return SendNotifyCookieBlocked(aRejectedReason);
 }
 
 bool HttpBackgroundChannelParent::OnNotifyTrackingResource(bool aIsThirdParty) {
   LOG(
       ("HttpBackgroundChannelParent::OnNotifyTrackingResource thirdparty=%d "
        "[this=%p]\n",
        static_cast<int>(aIsThirdParty), this));
   AssertIsInMainProcess();
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.h
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.h
@@ -58,24 +58,25 @@ class HttpBackgroundChannelParent final 
 
   // To send OnStatus message over background channel.
   bool OnStatus(const nsresult& aStatus);
 
   // To send FlushedForDiversion and DivertMessages messages
   // over background channel.
   bool OnDiversion();
 
-  // To send NotifyTrackingProtectionDisabled message over background channel.
-  bool OnNotifyTrackingProtectionDisabled();
+  // To send NotifyChannelClassifierProtectionDisabled message over background
+  // channel.
+  bool OnNotifyChannelClassifierProtectionDisabled(uint32_t aAcceptedReason);
 
   // To send NotifyCookieAllowed message over background channel.
   bool OnNotifyCookieAllowed();
 
-  // To send NotifyTrackingCookieBlocked message over background channel.
-  bool OnNotifyTrackingCookieBlocked(uint32_t aRejectedReason);
+  // To send NotifyCookieBlocked message over background channel.
+  bool OnNotifyCookieBlocked(uint32_t aRejectedReason);
 
   // To send NotifyTrackingResource message over background channel.
   bool OnNotifyTrackingResource(bool aIsThirdParty);
 
   // To send NotifyFlashPluginStateChanged message over background channel.
   bool OnNotifyFlashPluginStateChanged(nsIHttpChannel::FlashPluginState aState);
 
   // To send SetClassifierMatchedInfo message over background channel.
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -40,16 +40,17 @@
 #include "nsIProtocolProxyService.h"
 #include "nsProxyRelease.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDocShell.h"
 #include "nsINetworkInterceptController.h"
 #include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/PerformanceStorage.h"
+#include "mozilla/net/UrlClassifierFeatureFactory.h"
 #include "mozilla/NullPrincipal.h"
 #include "mozilla/Services.h"
 #include "mozIThirdPartyUtil.h"
 #include "nsStreamUtils.h"
 #include "nsThreadUtils.h"
 #include "nsContentSecurityManager.h"
 #include "nsIChannelEventSink.h"
 #include "nsILoadGroupChild.h"
@@ -4537,18 +4538,20 @@ HttpBaseChannel::GetNativeServerTiming(
     ParseServerTimingHeader(mResponseHead, aServerTiming);
     ParseServerTimingHeader(mResponseTrailers, aServerTiming);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-HttpBaseChannel::CancelForTrackingProtection() {
-  return Cancel(NS_ERROR_TRACKING_URI);
+HttpBaseChannel::CancelByChannelClassifier(nsresult aErrorCode) {
+  MOZ_ASSERT(
+      UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode));
+  return Cancel(aErrorCode);
 }
 
 void HttpBaseChannel::SetIPv4Disabled() { mCaps |= NS_HTTP_DISABLE_IPV4; }
 
 void HttpBaseChannel::SetIPv6Disabled() { mCaps |= NS_HTTP_DISABLE_IPV6; }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -302,17 +302,17 @@ class HttpBaseChannel : public nsHashPro
   NS_IMETHOD GetConnectionInfoHashKey(
       nsACString &aConnectionInfoHashKey) override;
   NS_IMETHOD GetIntegrityMetadata(nsAString &aIntegrityMetadata) override;
   NS_IMETHOD SetIntegrityMetadata(const nsAString &aIntegrityMetadata) override;
   NS_IMETHOD GetLastRedirectFlags(uint32_t *aValue) override;
   NS_IMETHOD SetLastRedirectFlags(uint32_t aValue) override;
   NS_IMETHOD GetNavigationStartTimeStamp(TimeStamp *aTimeStamp) override;
   NS_IMETHOD SetNavigationStartTimeStamp(TimeStamp aTimeStamp) override;
-  NS_IMETHOD CancelForTrackingProtection() override;
+  NS_IMETHOD CancelByChannelClassifier(nsresult aErrorCode) override;
   virtual void SetIPv4Disabled(void) override;
   virtual void SetIPv6Disabled(void) override;
 
   inline void CleanRedirectCacheChainIfNecessary() {
     mRedirectedCachekeys = nullptr;
   }
   NS_IMETHOD HTTPUpgrade(const nsACString &aProtocolName,
                          nsIHttpUpgradeListener *aListener) override;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/extensions/StreamFilterParent.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/net/HttpChannelChild.h"
 #include "mozilla/net/UrlClassifierCommon.h"
+#include "mozilla/net/UrlClassifierFeatureFactory.h"
 
 #include "AltDataOutputStreamChild.h"
 #include "CookieServiceChild.h"
 #include "HttpBackgroundChannelChild.h"
 #include "nsCOMPtr.h"
 #include "nsISupportsPrimitives.h"
 #include "nsContentPolicyUtils.h"
 #include "nsDOMNavigationTiming.h"
@@ -1185,17 +1186,18 @@ void HttpChannelChild::DoOnStopRequest(n
   AUTO_PROFILER_LABEL("HttpChannelChild::DoOnStopRequest", NETWORK);
   LOG(("HttpChannelChild::DoOnStopRequest [this=%p]\n", this));
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mIsPending);
 
   // NB: We use aChannelStatus here instead of mStatus because if there was an
   // nsCORSListenerProxy on this request, it will override the tracking
   // protection's return value.
-  if (aChannelStatus == NS_ERROR_TRACKING_URI ||
+  if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
+          aChannelStatus) ||
       aChannelStatus == NS_ERROR_MALWARE_URI ||
       aChannelStatus == NS_ERROR_UNWANTED_URI ||
       aChannelStatus == NS_ERROR_BLOCKED_URI ||
       aChannelStatus == NS_ERROR_HARMFUL_URI ||
       aChannelStatus == NS_ERROR_PHISHING_URI) {
     nsCString list, provider, fullhash;
 
     nsresult rv = GetMatchedList(list);
@@ -1800,28 +1802,32 @@ class HttpFlushedForDiversionEvent
 void HttpChannelChild::ProcessFlushedForDiversion() {
   LOG(("HttpChannelChild::ProcessFlushedForDiversion [this=%p]\n", this));
   MOZ_ASSERT(OnSocketThread());
   MOZ_RELEASE_ASSERT(mDivertingToParent);
 
   mEventQ->RunOrEnqueue(new HttpFlushedForDiversionEvent(this), true);
 }
 
-void HttpChannelChild::ProcessNotifyTrackingProtectionDisabled() {
-  LOG(("HttpChannelChild::ProcessNotifyTrackingProtectionDisabled [this=%p]\n",
-       this));
+void HttpChannelChild::ProcessNotifyChannelClassifierProtectionDisabled(
+    uint32_t aAcceptedReason) {
+  LOG(
+      ("HttpChannelChild::ProcessNotifyChannelClassifierProtectionDisabled "
+       "[this=%p aAcceptedReason=%" PRIu32 "]\n",
+       this, aAcceptedReason));
   MOZ_ASSERT(OnSocketThread());
 
   RefPtr<HttpChannelChild> self = this;
   nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
   neckoTarget->Dispatch(
       NS_NewRunnableFunction(
-          "UrlClassifierCommon::NotifyTrackingProtectionDisabled",
-          [self]() {
-            UrlClassifierCommon::NotifyTrackingProtectionDisabled(self);
+          "AntiTrackingCommon::NotifyChannelClassifierProtectionDisabled",
+          [self, aAcceptedReason]() {
+            UrlClassifierCommon::NotifyChannelClassifierProtectionDisabled(
+                self, aAcceptedReason);
           }),
       NS_DISPATCH_NORMAL);
 }
 
 void HttpChannelChild::ProcessNotifyCookieAllowed() {
   LOG(("HttpChannelChild::ProcessNotifyCookieAllowed [this=%p]\n", this));
   MOZ_ASSERT(OnSocketThread());
 
@@ -1832,20 +1838,18 @@ void HttpChannelChild::ProcessNotifyCook
           "UrlClassifierCommon::NotifyBlockingDecision",
           [self]() {
             AntiTrackingCommon::NotifyBlockingDecision(
                 self, AntiTrackingCommon::BlockingDecision::eAllow, 0);
           }),
       NS_DISPATCH_NORMAL);
 }
 
-void HttpChannelChild::ProcessNotifyTrackingCookieBlocked(
-    uint32_t aRejectedReason) {
-  LOG(("HttpChannelChild::ProcessNotifyTrackingCookieBlocked [this=%p]\n",
-       this));
+void HttpChannelChild::ProcessNotifyCookieBlocked(uint32_t aRejectedReason) {
+  LOG(("HttpChannelChild::ProcessNotifyCookieBlocked [this=%p]\n", this));
   MOZ_ASSERT(OnSocketThread());
 
   RefPtr<HttpChannelChild> self = this;
   nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
   neckoTarget->Dispatch(
       NS_NewRunnableFunction("AntiTrackingCommon::NotifyBlockingDecision",
                              [self, aRejectedReason]() {
                                AntiTrackingCommon::NotifyBlockingDecision(
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -256,19 +256,20 @@ class HttpChannelChild final : public PH
                                  const nsCString& aData);
   void ProcessOnStopRequest(const nsresult& aStatusCode,
                             const ResourceTimingStruct& aTiming,
                             const nsHttpHeaderArray& aResponseTrailers);
   void ProcessOnProgress(const int64_t& aProgress, const int64_t& aProgressMax);
   void ProcessOnStatus(const nsresult& aStatus);
   void ProcessFlushedForDiversion();
   void ProcessDivertMessages();
-  void ProcessNotifyTrackingProtectionDisabled();
+  void ProcessNotifyChannelClassifierProtectionDisabled(
+      uint32_t aAcceptedReason);
   void ProcessNotifyCookieAllowed();
-  void ProcessNotifyTrackingCookieBlocked(uint32_t aRejectedReason);
+  void ProcessNotifyCookieBlocked(uint32_t aRejectedReason);
   void ProcessNotifyTrackingResource(bool aIsThirdParty);
   void ProcessNotifyFlashPluginStateChanged(
       nsIHttpChannel::FlashPluginState aState);
   void ProcessSetClassifierMatchedInfo(const nsCString& aList,
                                        const nsCString& aProvider,
                                        const nsCString& aFullHash);
 
   // Return true if we need to tell the parent the size of unreported received
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -1781,43 +1781,47 @@ HttpChannelParent::SetParentListener(Htt
              "SetParentListener should only be called for "
              "new HttpChannelParents after a redirect, when "
              "mParentListener is null.");
   mParentListener = aListener;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-HttpChannelParent::NotifyTrackingProtectionDisabled() {
-  LOG(("HttpChannelParent::NotifyTrackingProtectionDisabled [this=%p]\n",
-       this));
+HttpChannelParent::NotifyChannelClassifierProtectionDisabled(
+    uint32_t aAcceptedReason) {
+  LOG(
+      ("HttpChannelParent::NotifyChannelClassifierProtectionDisabled [this=%p "
+       "aAcceptedReason=%" PRIu32 "]\n",
+       this, aAcceptedReason));
   if (!mIPCClosed) {
     MOZ_ASSERT(mBgParent);
-    Unused << NS_WARN_IF(!mBgParent->OnNotifyTrackingProtectionDisabled());
+    Unused << NS_WARN_IF(
+        !mBgParent->OnNotifyChannelClassifierProtectionDisabled(
+            aAcceptedReason));
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpChannelParent::NotifyCookieAllowed() {
   LOG(("HttpChannelParent::NotifyCookieAllowed [this=%p]\n", this));
   if (!mIPCClosed) {
     MOZ_ASSERT(mBgParent);
     Unused << NS_WARN_IF(!mBgParent->OnNotifyCookieAllowed());
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-HttpChannelParent::NotifyTrackingCookieBlocked(uint32_t aRejectedReason) {
-  LOG(("HttpChannelParent::NotifyTrackingCookieBlocked [this=%p]\n", this));
+HttpChannelParent::NotifyCookieBlocked(uint32_t aRejectedReason) {
+  LOG(("HttpChannelParent::NotifyCookieBlocked [this=%p]\n", this));
   if (!mIPCClosed) {
     MOZ_ASSERT(mBgParent);
-    Unused << NS_WARN_IF(
-        !mBgParent->OnNotifyTrackingCookieBlocked(aRejectedReason));
+    Unused << NS_WARN_IF(!mBgParent->OnNotifyCookieBlocked(aRejectedReason));
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
                                             const nsACString& aProvider,
                                             const nsACString& aFullHash) {
--- a/netwerk/protocol/http/PHttpBackgroundChannel.ipdl
+++ b/netwerk/protocol/http/PHttpBackgroundChannel.ipdl
@@ -49,24 +49,25 @@ child:
 
   // Parent has been suspended for diversion; no more events to be enqueued.
   async FlushedForDiversion();
 
   // Child should resume processing the ChannelEventQueue, i.e. diverting any
   // OnDataAvailable and OnStopRequest messages in the queue back to the parent.
   async DivertMessages();
 
-  // Tell the child that tracking protection was disabled for this load.
-  async NotifyTrackingProtectionDisabled();
+  // Tell the child that channel classifier protection was disabled for this
+  // load.
+  async NotifyChannelClassifierProtectionDisabled(uint32_t aAcceptedReason);
 
   // Tell the child that cookies are allowed for this load.
   async NotifyCookieAllowed();
 
   // Tell the child that tracking cookies are blocked for this load.
-  async NotifyTrackingCookieBlocked(uint32_t aRejectedReason);
+  async NotifyCookieBlocked(uint32_t aRejectedReason);
 
   // Tell the child that the resource being loaded is on the tracking
   // protection list.
   async NotifyTrackingResource(bool aIsThirdParty);
 
   // Tell the child that the current channel's document is not allowed to load
   // flash content.
   async NotifyFlashPluginStateChanged(FlashPluginState aState);
--- a/netwerk/protocol/http/TrackingDummyChannel.cpp
+++ b/netwerk/protocol/http/TrackingDummyChannel.cpp
@@ -605,17 +605,17 @@ TrackingDummyChannel::GetNavigationStart
 
 NS_IMETHODIMP
 TrackingDummyChannel::SetNavigationStartTimeStamp(
     TimeStamp aNavigationStartTimeStamp) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-TrackingDummyChannel::CancelForTrackingProtection() {
+TrackingDummyChannel::CancelByChannelClassifier(nsresult aErrorCode) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 void TrackingDummyChannel::SetIPv4Disabled() {}
 
 void TrackingDummyChannel::SetIPv6Disabled() {}
 
 }  // namespace net
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -117,16 +117,17 @@
 #include "nsIMIMEInputStream.h"
 #include "nsIMultiplexInputStream.h"
 #include "../../cache2/CacheFileUtils.h"
 #include "nsINetworkLinkService.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerUtils.h"
 #include "mozilla/net/AsyncUrlChannelClassifier.h"
+#include "mozilla/net/UrlClassifierFeatureFactory.h"
 
 #ifdef MOZ_TASK_TRACER
 #  include "GeckoTaskTracer.h"
 #endif
 
 #ifdef MOZ_GECKO_PROFILER
 #  include "ProfilerMarkerPayload.h"
 #endif
@@ -322,17 +323,17 @@ nsHttpChannel::nsHttpChannel()
       mConcurrentCacheAccess(0),
       mIsPartialRequest(0),
       mHasAutoRedirectVetoNotifier(0),
       mPinCacheContent(0),
       mIsCorsPreflightDone(0),
       mStronglyFramed(false),
       mUsedNetwork(0),
       mAuthConnectionRestartable(0),
-      mTrackingProtectionCancellationPending(0),
+      mChannelClassifierCancellationPending(0),
       mAsyncResumePending(0),
       mPushedStream(nullptr),
       mLocalBlocklist(false),
       mOnTailUnblock(nullptr),
       mWarningReporter(nullptr),
       mIsReadingFromCache(false),
       mFirstResponseSource(RESPONSE_PENDING),
       mRaceCacheWithNetwork(false),
@@ -443,34 +444,37 @@ nsresult nsHttpChannel::PrepareToConnect
       return NS_OK;
     };
     return NS_OK;
   }
 
   return OnBeforeConnect();
 }
 
-void nsHttpChannel::HandleContinueCancelledByTrackingProtection() {
+void nsHttpChannel::HandleContinueCancellingByChannelClassifier(
+    nsresult aErrorCode) {
+  MOZ_ASSERT(
+      UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode));
   MOZ_ASSERT(!mCallOnResume, "How did that happen?");
 
   if (mSuspendCount) {
     LOG(
-        ("Waiting until resume HandleContinueCancelledByTrackingProtection "
+        ("Waiting until resume HandleContinueCancellingByChannelClassifier "
          "[this=%p]\n",
          this));
-    mCallOnResume = [](nsHttpChannel *self) {
-      self->HandleContinueCancelledByTrackingProtection();
+    mCallOnResume = [aErrorCode](nsHttpChannel *self) {
+      self->HandleContinueCancellingByChannelClassifier(aErrorCode);
       return NS_OK;
     };
     return;
   }
 
-  LOG(("nsHttpChannel::HandleContinueCancelledByTrackingProtection [this=%p]\n",
+  LOG(("nsHttpChannel::HandleContinueCancellingByChannelClassifier [this=%p]\n",
        this));
-  ContinueCancelledByTrackingProtection();
+  ContinueCancellingByChannelClassifier(aErrorCode);
 }
 
 void nsHttpChannel::HandleOnBeforeConnect() {
   MOZ_ASSERT(!mCallOnResume, "How did that happen?");
   nsresult rv;
 
   if (mSuspendCount) {
     LOG(("Waiting until resume OnBeforeConnect [this=%p]\n", this));
@@ -5985,41 +5989,48 @@ NS_INTERFACE_MAP_END_INHERITING(HttpBase
 // nsHttpChannel::nsIRequest
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::Cancel(nsresult status) {
   MOZ_ASSERT(NS_IsMainThread());
   // We should never have a pump open while a CORS preflight is in progress.
   MOZ_ASSERT_IF(mPreflightChannel, !mCachePump);
-  MOZ_ASSERT(status != NS_ERROR_TRACKING_URI,
-             "NS_ERROR_TRACKING_URI needs to be handled by "
-             "CancelForTrackingProtection()");
+#ifdef DEBUG
+  if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status)) {
+    MOZ_CRASH_UNSAFE_PRINTF(
+        "Blocking classifier error %" PRIx32
+        " need to be handled by CancelByChannelClassifier()",
+        static_cast<uint32_t>(status));
+  }
+#endif
 
   LOG(("nsHttpChannel::Cancel [this=%p status=%" PRIx32 "]\n", this,
        static_cast<uint32_t>(status)));
   if (mCanceled) {
     LOG(("  ignoring; already canceled\n"));
     return NS_OK;
   }
 
   if (mWaitingForRedirectCallback) {
     LOG(("channel canceled during wait for redirect callback"));
   }
 
   return CancelInternal(status);
 }
 
 NS_IMETHODIMP
-nsHttpChannel::CancelForTrackingProtection() {
+nsHttpChannel::CancelByChannelClassifier(nsresult aErrorCode) {
+  MOZ_ASSERT(
+      UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode));
   MOZ_ASSERT(NS_IsMainThread());
   // We should never have a pump open while a CORS preflight is in progress.
   MOZ_ASSERT_IF(mPreflightChannel, !mCachePump);
 
-  LOG(("nsHttpChannel::CancelForTrackingProtection [this=%p]\n", this));
+  LOG(("nsHttpChannel::CancelByChannelClassifier [this=%p]\n", this));
 
   if (mCanceled) {
     LOG(("  ignoring; already canceled\n"));
     return NS_OK;
   }
 
   // We are being canceled by the channel classifier because of tracking
   // protection, but we haven't yet had a chance to dispatch the
@@ -6040,79 +6051,81 @@ nsHttpChannel::CancelForTrackingProtecti
   // Check if request was cancelled during on-modify-request or on-useragent.
   if (mCanceled) {
     return mStatus;
   }
 
   if (mSuspendCount) {
     LOG(("Waiting until resume in Cancel [this=%p]\n", this));
     MOZ_ASSERT(!mCallOnResume);
-    mTrackingProtectionCancellationPending = 1;
-    mCallOnResume = [](nsHttpChannel *self) {
-      self->HandleContinueCancelledByTrackingProtection();
+    mChannelClassifierCancellationPending = 1;
+    mCallOnResume = [aErrorCode](nsHttpChannel *self) {
+      self->HandleContinueCancellingByChannelClassifier(aErrorCode);
       return NS_OK;
     };
     return NS_OK;
   }
 
   // Check to see if we should redirect this channel elsewhere by
   // nsIHttpChannel.redirectTo API request
   if (mAPIRedirectToURI) {
-    mTrackingProtectionCancellationPending = 1;
+    mChannelClassifierCancellationPending = 1;
     return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
   }
 
-  return CancelInternal(NS_ERROR_TRACKING_URI);
-}
-
-void nsHttpChannel::ContinueCancelledByTrackingProtection() {
+  return CancelInternal(aErrorCode);
+}
+
+void nsHttpChannel::ContinueCancellingByChannelClassifier(nsresult aErrorCode) {
+  MOZ_ASSERT(
+      UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode));
   MOZ_ASSERT(NS_IsMainThread());
   // We should never have a pump open while a CORS preflight is in progress.
   MOZ_ASSERT_IF(mPreflightChannel, !mCachePump);
 
-  LOG(("nsHttpChannel::ContinueCancelledByTrackingProtection [this=%p]\n",
+  LOG(("nsHttpChannel::ContinueCancellingByChannelClassifier [this=%p]\n",
        this));
   if (mCanceled) {
     LOG(("  ignoring; already canceled\n"));
     return;
   }
 
   // Check to see if we should redirect this channel elsewhere by
   // nsIHttpChannel.redirectTo API request
   if (mAPIRedirectToURI) {
     Unused << AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
     return;
   }
 
-  Unused << CancelInternal(NS_ERROR_TRACKING_URI);
+  Unused << CancelInternal(aErrorCode);
 }
 
 nsresult nsHttpChannel::CancelInternal(nsresult status) {
-  bool trackingProtectionCancellationPending =
-      !!mTrackingProtectionCancellationPending;
-  if (status == NS_ERROR_TRACKING_URI) {
-    mTrackingProtectionCancellationPending = 0;
+  bool channelClassifierCancellationPending =
+      !!mChannelClassifierCancellationPending;
+  if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status)) {
+    mChannelClassifierCancellationPending = 0;
   }
 
   mCanceled = true;
   mStatus = status;
   if (mProxyRequest) mProxyRequest->Cancel(status);
   CancelNetworkRequest(status);
   mCacheInputStream.CloseAndRelease();
   if (mCachePump) mCachePump->Cancel(status);
   if (mAuthProvider) mAuthProvider->Cancel(status);
   if (mPreflightChannel) mPreflightChannel->Cancel(status);
   if (mRequestContext && mOnTailUnblock) {
     mOnTailUnblock = nullptr;
     mRequestContext->CancelTailedRequest(this);
     CloseCacheEntry(false);
     Unused << AsyncAbort(status);
-  } else if (trackingProtectionCancellationPending) {
+  } else if (channelClassifierCancellationPending) {
     // If we're coming from an asynchronous path when canceling a channel due
-    // to tracking protection, we need to AsyncAbort the channel now.
+    // to safe-browsing protection, we need to AsyncAbort the channel now.
     Unused << AsyncAbort(status);
   }
   return NS_OK;
 }
 
 void nsHttpChannel::CancelNetworkRequest(nsresult aStatus) {
   if (mTransaction) {
     nsresult rv = gHttpHandler->CancelTransaction(mTransaction, aStatus);
@@ -6624,20 +6637,20 @@ void nsHttpChannel::MaybeStartDNSPrefetc
 
 nsresult nsHttpChannel::BeginConnectActual() {
   if (mCanceled) {
     return mStatus;
   }
 
   AUTO_PROFILER_LABEL("nsHttpChannel::BeginConnectActual", NETWORK);
 
-  if (mTrackingProtectionCancellationPending) {
+  if (mChannelClassifierCancellationPending) {
     LOG(
-        ("Waiting for tracking protection cancellation in BeginConnectActual "
-         "[this=%p]\n",
+        ("Waiting for safe-browsing protection cancellation in "
+         "BeginConnectActual [this=%p]\n",
          this));
     return NS_OK;
   }
 
   MaybeStartDNSPrefetch();
 
   nsresult rv = ContinueBeginConnectWithResult();
   if (NS_FAILED(rv)) {
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -164,17 +164,17 @@ class nsHttpChannel final : public HttpB
   NS_IMETHOD GetEncodedBodySize(uint64_t *aEncodedBodySize) override;
   NS_IMETHOD SwitchProcessTo(mozilla::dom::Promise *aTabParent,
                              uint64_t aIdentifier) override;
   // nsIHttpChannelInternal
   NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) override;
   NS_IMETHOD SetChannelIsForDownload(bool aChannelIsForDownload) override;
   NS_IMETHOD GetNavigationStartTimeStamp(TimeStamp *aTimeStamp) override;
   NS_IMETHOD SetNavigationStartTimeStamp(TimeStamp aTimeStamp) override;
-  NS_IMETHOD CancelForTrackingProtection() override;
+  NS_IMETHOD CancelByChannelClassifier(nsresult aErrorCode) override;
   // nsISupportsPriority
   NS_IMETHOD SetPriority(int32_t value) override;
   // nsIClassOfService
   NS_IMETHOD SetClassFlags(uint32_t inFlags) override;
   NS_IMETHOD AddClassFlags(uint32_t inFlags) override;
   NS_IMETHOD ClearClassFlags(uint32_t inFlags) override;
 
   // nsIResumableChannel
@@ -305,19 +305,19 @@ class nsHttpChannel final : public HttpB
 
   // Directly call |aFunc| if the channel is not canceled and not suspended.
   // Otherwise, set |aFunc| to |mCallOnResume| and wait until the channel
   // resumes.
   nsresult CallOrWaitForResume(
       const std::function<nsresult(nsHttpChannel *)> &aFunc);
 
   bool RequestIsConditional();
-  void HandleContinueCancelledByTrackingProtection();
+  void HandleContinueCancellingByChannelClassifier(nsresult aErrorCode);
   nsresult CancelInternal(nsresult status);
-  void ContinueCancelledByTrackingProtection();
+  void ContinueCancellingByChannelClassifier(nsresult aErrorCode);
 
   // Connections will only be established in this function.
   // (including DNS prefetch and speculative connection.)
   nsresult BeginConnectActual();
   void MaybeStartDNSPrefetch();
 
   // We might synchronously or asynchronously call BeginConnectActual,
   // which includes DNS prefetch and speculative connection, according to
@@ -674,20 +674,20 @@ class nsHttpChannel final : public HttpB
   uint32_t mStronglyFramed : 1;
 
   // true if an HTTP transaction is created for the socket thread
   uint32_t mUsedNetwork : 1;
 
   // the next authentication request can be sent on a whole new connection
   uint32_t mAuthConnectionRestartable : 1;
 
-  // True if the channel classifier has marked the channel to be cancelled
-  // due to the tracking protection rules, but the asynchronous cancellation
+  // True if the channel classifier has marked the channel to be cancelled due
+  // to the safe-browsing classifier rules, but the asynchronous cancellation
   // process hasn't finished yet.
-  uint32_t mTrackingProtectionCancellationPending : 1;
+  uint32_t mChannelClassifierCancellationPending : 1;
 
   // True only when we are between Resume and async fire of mCallOnResume.
   // Used to suspend any newly created pumps in mCallOnResume handler.
   uint32_t mAsyncResumePending : 1;
 
   nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
 
   // Needed for accurate DNS timing
--- a/netwerk/protocol/http/nsIHttpChannelInternal.idl
+++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl
@@ -347,21 +347,21 @@ interface nsIHttpChannelInternal : nsISu
     [noscript, infallible]
     attribute unsigned long lastRedirectFlags;
 
       // This is use to determine the duration since navigation started.
     [noscript] attribute TimeStamp navigationStartTimeStamp;
 
     /**
      * Cancel a channel because we have determined that it needs to be blocked
-     * for tracking protection.  This is an internal API that is meant to be
-     * called by the channel classifier.  Please DO NOT use this API if you don't
-     * know whether you should be using it.
+     * for safe-browsing protection.  This is an internal API that is meant to
+     * be called by the channel classifier.  Please DO NOT use this API if you
+     * don't know whether you should be using it.
      */
-    [noscript] void cancelForTrackingProtection();
+    [noscript] void cancelByChannelClassifier(in nsresult aErrorCode);
 
     /**
      * The channel will be loaded over IPv6, disabling IPv4.
      */
     [noscript, notxpcom, nostdcall]
     void setIPv4Disabled();
 
     /**
--- a/netwerk/url-classifier/UrlClassifierCommon.cpp
+++ b/netwerk/url-classifier/UrlClassifierCommon.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/net/UrlClassifierCommon.h"
 
 #include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/BasePrincipal.h"
+#include "mozilla/net/UrlClassifierFeatureFactory.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozIThirdPartyUtil.h"
 #include "nsContentUtils.h"
 #include "nsIChannel.h"
 #include "nsIClassifiedChannel.h"
 #include "mozilla/dom/Document.h"
 #include "nsIDocShell.h"
 #include "nsIHttpChannel.h"
@@ -43,32 +44,32 @@ LazyLogModule UrlClassifierCommon::sLog(
   nsIPrincipal* loadingPrincipal = channelLoadInfo->LoadingPrincipal();
   if (!loadingPrincipal) {
     return false;
   }
 
   return BasePrincipal::Cast(loadingPrincipal)->AddonAllowsLoad(aURI, true);
 }
 
-/* static */ void UrlClassifierCommon::NotifyTrackingProtectionDisabled(
-    nsIChannel* aChannel) {
+/* static */ void
+UrlClassifierCommon::NotifyChannelClassifierProtectionDisabled(
+    nsIChannel* aChannel, uint32_t aEvent) {
   // Can be called in EITHER the parent or child process.
   nsCOMPtr<nsIParentChannel> parentChannel;
   NS_QueryNotificationCallbacks(aChannel, parentChannel);
   if (parentChannel) {
     // This channel is a parent-process proxy for a child process request.
     // Tell the child process channel to do this instead.
-    parentChannel->NotifyTrackingProtectionDisabled();
+    parentChannel->NotifyChannelClassifierProtectionDisabled(aEvent);
     return;
   }
 
   nsCOMPtr<nsIURI> uriBeingLoaded =
       AntiTrackingCommon::MaybeGetDocumentURIBeingLoaded(aChannel);
-  NotifyChannelBlocked(aChannel, uriBeingLoaded,
-                       nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT);
+  NotifyChannelBlocked(aChannel, uriBeingLoaded, aEvent);
 }
 
 /* static */ void UrlClassifierCommon::NotifyChannelBlocked(
     nsIChannel* aChannel, nsIURI* aURIBeingLoaded, unsigned aBlockedReason) {
   nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
   if (NS_WARN_IF(!thirdPartyUtil)) {
     return;
   }
@@ -147,21 +148,43 @@ LazyLogModule UrlClassifierCommon::sLog(
     if (UC_LOG_ENABLED()) {
       nsCString chanSpec = chanURI->GetSpecOrDefault();
       chanSpec.Truncate(
           std::min(chanSpec.Length(), UrlClassifierCommon::sMaxSpecLength));
       UC_LOG(("nsChannelClassifier: User override on channel[%p] (%s)",
               aChannel, chanSpec.get()));
     }
 
-    // Tracking protection will be disabled so update the security state
-    // of the document and fire a secure change event. If we can't get the
-    // window for the channel, then the shield won't show up so we can't send
-    // an event to the securityUI anyway.
-    UrlClassifierCommon::NotifyTrackingProtectionDisabled(aChannel);
+    // Channel classifier protection will be disabled so update the security
+    // state of the document and fire a secure change event. If we can't get the
+    // window for the channel, then the shield won't show up so we can't send an
+    // event to the securityUI anyway.
+
+    uint32_t event = 0;
+    switch (aBlockingPurpose) {
+      case AntiTrackingCommon::eTrackingProtection:
+        MOZ_FALLTHROUGH;
+      case AntiTrackingCommon::eTrackingAnnotations:
+        event = nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT;
+        break;
+
+      case AntiTrackingCommon::eFingerprinting:
+        event = nsIWebProgressListener::STATE_LOADED_FINGERPRINTING_CONTENT;
+        break;
+
+      case AntiTrackingCommon::eCryptomining:
+        event = nsIWebProgressListener::STATE_LOADED_CRYPTOMINING_CONTENT;
+        break;
+
+      default:
+        MOZ_CRASH("Invalidate blocking purpose.");
+    }
+
+    UrlClassifierCommon::NotifyChannelClassifierProtectionDisabled(aChannel,
+                                                                   event);
 
     return false;
   }
 
   // Tracking protection will be enabled so return without updating
   // the security state. If any channels are subsequently cancelled
   // (page elements blocked) the state will be then updated.
   if (UC_LOG_ENABLED()) {
@@ -218,36 +241,39 @@ LazyLogModule UrlClassifierCommon::sLog(
   auto* pwin = nsPIDOMWindowOuter::From(win);
   nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
   if (!docShell) {
     return NS_OK;
   }
   RefPtr<dom::Document> doc = docShell->GetDocument();
   NS_ENSURE_TRUE(doc, NS_OK);
 
-  unsigned state;
-  if (aErrorCode == NS_ERROR_TRACKING_URI) {
-    state = nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
-  } else {
+  unsigned state =
+      UrlClassifierFeatureFactory::GetClassifierBlockingEventCode(aErrorCode);
+  if (!state) {
     state = nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
   }
 
   UrlClassifierCommon::NotifyChannelBlocked(channel, uriBeingLoaded, state);
 
   // Log a warning to the web console.
   nsCOMPtr<nsIURI> uri;
   channel->GetURI(getter_AddRefs(uri));
   NS_ConvertUTF8toUTF16 spec(uri->GetSpecOrDefault());
   const char16_t* params[] = {spec.get()};
-  const char* message = (aErrorCode == NS_ERROR_TRACKING_URI)
-                            ? "TrackerUriBlocked"
-                            : "UnsafeUriBlocked";
-  nsCString category = (aErrorCode == NS_ERROR_TRACKING_URI)
-                           ? NS_LITERAL_CSTRING("Tracking Protection")
-                           : NS_LITERAL_CSTRING("Safe Browsing");
+  const char* message;
+  nsCString category;
+
+  if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode)) {
+    message = UrlClassifierFeatureFactory::
+        ClassifierBlockingErrorCodeToConsoleMessage(aErrorCode, category);
+  } else {
+    message = "UnsafeUriBlocked";
+    category = NS_LITERAL_CSTRING("Safe Browsing");
+  }
 
   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, category, doc,
                                   nsContentUtils::eNECKO_PROPERTIES, message,
                                   params, ArrayLength(params));
 
   return NS_OK;
 }
 
--- a/netwerk/url-classifier/UrlClassifierCommon.h
+++ b/netwerk/url-classifier/UrlClassifierCommon.h
@@ -22,17 +22,18 @@ namespace net {
 class UrlClassifierCommon final {
  public:
   static const nsCString::size_type sMaxSpecLength;
 
   static LazyLogModule sLog;
 
   static bool AddonMayLoad(nsIChannel* aChannel, nsIURI* aURI);
 
-  static void NotifyTrackingProtectionDisabled(nsIChannel* aChannel);
+  static void NotifyChannelClassifierProtectionDisabled(
+      nsIChannel* aChannel, uint32_t aAcceptedReason);
 
   static bool ShouldEnableClassifier(
       nsIChannel* aChannel,
       AntiTrackingCommon::ContentBlockingAllowListPurpose aBlockingPurpose);
 
   static nsresult SetBlockedContent(nsIChannel* channel, nsresult aErrorCode,
                                     const nsACString& aList,
                                     const nsACString& aProvider,
--- a/netwerk/url-classifier/UrlClassifierFeatureCryptomining.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureCryptomining.cpp
@@ -131,31 +131,29 @@ UrlClassifierFeatureCryptomining::Proces
                                                  const nsACString& aList,
                                                  bool* aShouldContinue) {
   NS_ENSURE_ARG_POINTER(aChannel);
   NS_ENSURE_ARG_POINTER(aShouldContinue);
 
   // This is a blocking feature.
   *aShouldContinue = false;
 
-  UrlClassifierCommon::SetBlockedContent(aChannel, NS_ERROR_TRACKING_URI, aList,
-                                         EmptyCString(), EmptyCString());
+  UrlClassifierCommon::SetBlockedContent(aChannel, NS_ERROR_CRYPTOMINING_URI,
+                                         aList, EmptyCString(), EmptyCString());
 
   UC_LOG(
       ("UrlClassifierFeatureCryptomining::ProcessChannel, cancelling "
        "channel[%p]",
        aChannel));
   nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(aChannel);
 
-  // FIXME: the way we cancel the channel depends on what the UI wants to show.
-  // This needs to change, at some point.
   if (httpChannel) {
-    Unused << httpChannel->CancelForTrackingProtection();
+    Unused << httpChannel->CancelByChannelClassifier(NS_ERROR_CRYPTOMINING_URI);
   } else {
-    Unused << aChannel->Cancel(NS_ERROR_TRACKING_URI);
+    Unused << aChannel->Cancel(NS_ERROR_CRYPTOMINING_URI);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UrlClassifierFeatureCryptomining::GetURIByListType(
     nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
--- a/netwerk/url-classifier/UrlClassifierFeatureFactory.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureFactory.cpp
@@ -200,10 +200,80 @@ UrlClassifierFeatureFactory::CreateFeatu
     const nsACString& aName, const nsTArray<nsCString>& aBlacklistTables,
     const nsTArray<nsCString>& aWhitelistTables) {
   nsCOMPtr<nsIUrlClassifierFeature> feature =
       new UrlClassifierFeatureCustomTables(aName, aBlacklistTables,
                                            aWhitelistTables);
   return feature.forget();
 }
 
+namespace {
+
+struct BlockingErrorCode {
+  nsresult mErrorCode;
+  uint32_t mBlockingEventCode;
+  const char* mConsoleMessage;
+  nsCString mConsoleCategory;
+};
+
+static const BlockingErrorCode sBlockingErrorCodes[] = {
+    {NS_ERROR_TRACKING_URI,
+     nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT,
+     "TrackerUriBlocked", NS_LITERAL_CSTRING("Tracking Protection")},
+    {NS_ERROR_FINGERPRINTING_URI,
+     nsIWebProgressListener::STATE_BLOCKED_FINGERPRINTING_CONTENT,
+     "TrackerUriBlocked", NS_LITERAL_CSTRING("Tracking Protection")},
+    {NS_ERROR_CRYPTOMINING_URI,
+     nsIWebProgressListener::STATE_BLOCKED_CRYPTOMINING_CONTENT,
+     "TrackerUriBlocked", NS_LITERAL_CSTRING("Tracking Protection")},
+};
+
+}  // namespace
+
+/* static */ bool UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
+    nsresult aError) {
+  // In theory we can iterate through the features, but at the moment, we can
+  // just have a simple check here.
+  for (const auto& blockingErrorCode : sBlockingErrorCodes) {
+    if (aError == blockingErrorCode.mErrorCode) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+/* static */ bool UrlClassifierFeatureFactory::IsClassifierBlockingEventCode(
+    uint32_t aEventCode) {
+  for (const auto& blockingErrorCode : sBlockingErrorCodes) {
+    if (aEventCode == blockingErrorCode.mBlockingEventCode) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/* static */ uint32_t
+UrlClassifierFeatureFactory::GetClassifierBlockingEventCode(
+    nsresult aErrorCode) {
+  for (const auto& blockingErrorCode : sBlockingErrorCodes) {
+    if (aErrorCode == blockingErrorCode.mErrorCode) {
+      return blockingErrorCode.mBlockingEventCode;
+    }
+  }
+  return 0;
+}
+
+/* static */ const char*
+UrlClassifierFeatureFactory::ClassifierBlockingErrorCodeToConsoleMessage(
+    nsresult aError, nsACString& aCategory) {
+  for (const auto& blockingErrorCode : sBlockingErrorCodes) {
+    if (aError == blockingErrorCode.mErrorCode) {
+      aCategory = blockingErrorCode.mConsoleCategory;
+      return blockingErrorCode.mConsoleMessage;
+    }
+  }
+
+  return nullptr;
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureFactory.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureFactory.h
@@ -32,14 +32,28 @@ class UrlClassifierFeatureFactory final 
   static already_AddRefed<nsIUrlClassifierFeature> GetFeatureByName(
       const nsACString& aFeatureName);
 
   static void GetFeatureNames(nsTArray<nsCString>& aArray);
 
   static already_AddRefed<nsIUrlClassifierFeature> CreateFeatureWithTables(
       const nsACString& aName, const nsTArray<nsCString>& aBlacklistTables,
       const nsTArray<nsCString>& aWhitelistTables);
+
+  // Returns true if this error is known as one of the blocking error codes.
+  static bool IsClassifierBlockingErrorCode(nsresult aError);
+
+  // Returns true if this event is a known blocking state from
+  // nsIWebProgressListener.
+  static bool IsClassifierBlockingEventCode(uint32_t aEventCode);
+
+  static uint32_t GetClassifierBlockingEventCode(nsresult aErrorCode);
+
+  // This can be called only if IsClassifierBlockingErrorCode(aError) returns
+  // true.
+  static const char* ClassifierBlockingErrorCodeToConsoleMessage(
+      nsresult aError, nsACString& aCategory);
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_net_UrlClassifierFeatureFactory_h
--- a/netwerk/url-classifier/UrlClassifierFeatureFingerprinting.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureFingerprinting.cpp
@@ -133,31 +133,30 @@ UrlClassifierFeatureFingerprinting::Proc
                                                    const nsACString& aList,
                                                    bool* aShouldContinue) {
   NS_ENSURE_ARG_POINTER(aChannel);
   NS_ENSURE_ARG_POINTER(aShouldContinue);
 
   // This is a blocking feature.
   *aShouldContinue = false;
 
-  UrlClassifierCommon::SetBlockedContent(aChannel, NS_ERROR_TRACKING_URI, aList,
-                                         EmptyCString(), EmptyCString());
+  UrlClassifierCommon::SetBlockedContent(aChannel, NS_ERROR_FINGERPRINTING_URI,
+                                         aList, EmptyCString(), EmptyCString());
 
   UC_LOG(
       ("UrlClassifierFeatureFingerprinting::ProcessChannel, cancelling "
        "channel[%p]",
        aChannel));
   nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(aChannel);
 
-  // FIXME: the way we cancel the channel depends on what the UI wants to show.
-  // This needs to change, at some point.
   if (httpChannel) {
-    Unused << httpChannel->CancelForTrackingProtection();
+    Unused << httpChannel->CancelByChannelClassifier(
+        NS_ERROR_FINGERPRINTING_URI);
   } else {
-    Unused << aChannel->Cancel(NS_ERROR_TRACKING_URI);
+    Unused << aChannel->Cancel(NS_ERROR_FINGERPRINTING_URI);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UrlClassifierFeatureFingerprinting::GetURIByListType(
     nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
--- a/netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.cpp
@@ -212,17 +212,18 @@ UrlClassifierFeatureTrackingAnnotation::
 
   SetIsTrackingResourceHelper(aChannel, isThirdPartyWithTopLevelWinURI);
 
   if (isThirdPartyWithTopLevelWinURI) {
     // Even with TP disabled, we still want to show the user that there
     // are unblocked trackers on the site, so notify the UI that we loaded
     // tracking content. UI code can treat this notification differently
     // depending on whether TP is enabled or disabled.
-    UrlClassifierCommon::NotifyTrackingProtectionDisabled(aChannel);
+    UrlClassifierCommon::NotifyChannelClassifierProtectionDisabled(
+        aChannel, nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT);
 
     if (StaticPrefs::privacy_trackingprotection_lower_network_priority()) {
       LowerPriorityHelper(aChannel);
     }
   }
 
   return NS_OK;
 }
--- a/netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.cpp
@@ -143,17 +143,17 @@ UrlClassifierFeatureTrackingProtection::
                                          EmptyCString(), EmptyCString());
 
   UC_LOG(
       ("UrlClassifierFeatureTrackingProtection::ProcessChannel, cancelling "
        "channel[%p]",
        aChannel));
   nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
-    Unused << httpChannel->CancelForTrackingProtection();
+    Unused << httpChannel->CancelByChannelClassifier(NS_ERROR_TRACKING_URI);
   } else {
     Unused << aChannel->Cancel(NS_ERROR_TRACKING_URI);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/url-classifier/nsChannelClassifier.cpp
+++ b/netwerk/url-classifier/nsChannelClassifier.cpp
@@ -24,16 +24,17 @@
 #include "nsIUrlClassifierFeature.h"
 #include "nsPrintfCString.h"
 
 #include "mozilla/Components.h"
 #include "mozilla/ErrorNames.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/net/UrlClassifierCommon.h"
+#include "mozilla/net/UrlClassifierFeatureFactory.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Services.h"
 
 namespace mozilla {
 namespace net {
 
 //
 // MOZ_LOG=nsChannelClassifier:5
@@ -279,17 +280,18 @@ bool nsChannelClassifier::IsHostnameWhit
 
 // Note in the cache entry that this URL was classified, so that future
 // cached loads don't need to be checked.
 void nsChannelClassifier::MarkEntryClassified(nsresult status) {
   // Should only be called in the parent process.
   MOZ_ASSERT(XRE_IsParentProcess());
 
   // Don't cache tracking classifications because we support allowlisting.
-  if (status == NS_ERROR_TRACKING_URI || mIsAllowListed) {
+  if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status) ||
+      mIsAllowListed) {
     return;
   }
 
   if (LOG_ENABLED()) {
     nsAutoCString errorName;
     GetErrorName(status, errorName);
     nsCOMPtr<nsIURI> uri;
     mChannel->GetURI(getter_AddRefs(uri));
@@ -384,17 +386,18 @@ nsresult nsChannelClassifier::SendThreat
 
 NS_IMETHODIMP
 nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode,
                                         const nsACString& aList,
                                         const nsACString& aProvider,
                                         const nsACString& aFullHash) {
   // Should only be called in the parent process.
   MOZ_ASSERT(XRE_IsParentProcess());
-  MOZ_ASSERT(aErrorCode != NS_ERROR_TRACKING_URI);
+  MOZ_ASSERT(
+      !UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode));
 
   if (mSuspendedChannel) {
     nsAutoCString errorName;
     if (LOG_ENABLED() && NS_FAILED(aErrorCode)) {
       GetErrorName(aErrorCode, errorName);
       LOG(("nsChannelClassifier[%p]:OnClassifyComplete %s (suspended channel)",
            this, errorName.get()));
     }
--- a/security/manager/ssl/nsSecureBrowserUIImpl.cpp
+++ b/security/manager/ssl/nsSecureBrowserUIImpl.cpp
@@ -173,16 +173,35 @@ void nsSecureBrowserUIImpl::CheckForCont
   if (doc->GetHasTrackingContentBlocked()) {
     mEvent |= STATE_BLOCKED_TRACKING_CONTENT;
   }
 
   if (doc->GetHasTrackingContentLoaded()) {
     mEvent |= STATE_LOADED_TRACKING_CONTENT;
   }
 
+  // Has fingerprinting content been blocked or loaded?
+  if (doc->GetHasFingerprintingContentBlocked()) {
+    mEvent |= STATE_BLOCKED_FINGERPRINTING_CONTENT;
+  }
+
+  if (doc->GetHasFingerprintingContentLoaded()) {
+    mEvent |= STATE_LOADED_FINGERPRINTING_CONTENT;
+  }
+
+  // Has cryptomining content been blocked or loaded?
+  if (doc->GetHasCryptominingContentBlocked()) {
+    mEvent |= STATE_BLOCKED_CRYPTOMINING_CONTENT;
+  }
+
+  if (doc->GetHasCryptominingContentLoaded()) {
+    mEvent |= STATE_LOADED_CRYPTOMINING_CONTENT;
+  }
+
+  // Other block types.
   if (doc->GetHasCookiesBlockedByPermission()) {
     mEvent |= STATE_COOKIES_BLOCKED_BY_PERMISSION;
   }
 
   if (doc->GetHasTrackingCookiesBlocked()) {
     mEvent |= STATE_COOKIES_BLOCKED_TRACKER;
   }
 
--- a/taskcluster/ci/build/windows.yml
+++ b/taskcluster/ci/build/windows.yml
@@ -1170,17 +1170,16 @@ win64-aarch64-nightly/opt:
         script: mozharness/scripts/fx_desktop_build.py
         config:
             - builds/releng_base_firefox.py
             - builds/taskcluster_base_windows.py
             - taskcluster_nightly.py
         extra-config:
             stage_platform: win64-aarch64
             mozconfig_platform: win64-aarch64
-    run-on-projects: ['mozilla-central', 'trunk', 'try']
     toolchains:
         - win64-clang-cl
         - win64-aarch64-rust
         - win64-cbindgen
         - win64-sccache
         - win64-node
 
 win32-mingwclang/opt:
--- a/taskcluster/taskgraph/target_tasks.py
+++ b/taskcluster/taskgraph/target_tasks.py
@@ -61,17 +61,17 @@ def filter_release_tasks(task, parameter
             'linux-pgo', 'linux64-pgo',
             'win32-pgo', 'win64-pgo',
             ):
         return False
 
     if platform in (
             'linux', 'linux64',
             'macosx64',
-            'win32', 'win64',
+            'win32', 'win64', 'win64-aarch64',
             ):
         if task.attributes['kind'] == 'l10n':
             # This is on-change l10n
             return True
         if task.attributes['build_type'] == 'opt' and \
            task.attributes.get('unittest_suite') != 'talos' and \
            task.attributes.get('unittest_suite') != 'raptor':
             return False
--- a/toolkit/components/aboutperformance/content/aboutPerformance.js
+++ b/toolkit/components/aboutperformance/content/aboutPerformance.js
@@ -252,17 +252,17 @@ var State = {
         return false;
 
       let principal =
         Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("http://" + host);
       let classifier =
         Cc["@mozilla.org/url-classifier/dbservice;1"].getService(Ci.nsIURIClassifier);
       classifier.classify(principal, null, true,
                           (aErrorCode, aList, aProvider, aFullHash) => {
-        this._trackingState.set(host, aErrorCode == Cr.NS_ERROR_TRACKING_URI);
+        this._trackingState.set(host, ChromeUtils.IsClassifierBlockingErrorCode(aErrorCode));
       });
     }
     return this._trackingState.get(host);
   },
 
   getCounters() {
     tabFinder.update();
     // We rebuild the maps during each iteration to make sure that
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -1497,17 +1497,17 @@ nsresult AntiTrackingCommon::IsOnContent
   // Can be called in EITHER the parent or child process.
   if (XRE_IsParentProcess()) {
     nsCOMPtr<nsIParentChannel> parentChannel;
     NS_QueryNotificationCallbacks(aChannel, parentChannel);
     if (parentChannel) {
       // This channel is a parent-process proxy for a child process request.
       // Tell the child process channel to do this instead.
       if (aDecision == BlockingDecision::eBlock) {
-        parentChannel->NotifyTrackingCookieBlocked(aRejectedReason);
+        parentChannel->NotifyCookieBlocked(aRejectedReason);
       } else {
         parentChannel->NotifyCookieAllowed();
       }
     }
     return;
   }
 
   MOZ_ASSERT(XRE_IsContentProcess());
--- a/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js
+++ b/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js
@@ -75,17 +75,17 @@ add_task(async function() {
 
   await promise;
 
   info("Verify the number of tracking nodes found");
   await ContentTask.spawn(browser,
                           { expected: 3,
                           },
                           async function(obj) {
-    is(content.document.blockedTrackingNodeCount, obj.expected, "Expected tracking nodes found");
+    is(content.document.blockedNodeByClassifierCount, obj.expected, "Expected tracking nodes found");
   });
 
   info("Removing the tab");
   BrowserTestUtils.removeTab(tab);
 
   UrlClassifierTestUtils.cleanupTestTrackers();
   await extension.unload();
 });
--- a/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js
+++ b/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js
@@ -70,16 +70,16 @@ add_task(async function() {
 
   await promise;
 
   info("Verify the number of tracking nodes found");
   await ContentTask.spawn(browser,
                           { expected: gExpectedResourcesSeen,
                           },
                           async function(obj) {
-    is(content.document.blockedTrackingNodeCount, obj.expected, "Expected tracking nodes found");
+    is(content.document.blockedNodeByClassifierCount, obj.expected, "Expected tracking nodes found");
   });
 
   info("Removing the tab");
   BrowserTestUtils.removeTab(tab);
 
   UrlClassifierTestUtils.cleanupTestTrackers();
 });
--- a/toolkit/components/telemetry/Scalars.yaml
+++ b/toolkit/components/telemetry/Scalars.yaml
@@ -2351,16 +2351,56 @@ encoding:
     kind: boolean
     notification_emails:
       - hsivonen@mozilla.com
     release_channel_collection: opt-out
     record_in_processes:
       - main
       - content
 
+startup:
+  profile_selection_reason:
+    bug_numbers:
+      - 1522934
+    description: >
+      How the profile was selected during startup. One of the following reasons:
+        unknown:
+          Generally should not happen, set as a default in case no other reason
+          occured.
+        profile-manager:
+          The profile was selected by the profile manager.
+        profile-reset:
+          The profile was selected for reset, normally this would mean a restart.
+        restart:
+          The user restarted the application, the same profile as previous will
+          be used.
+        argument-profile
+          The profile was selected by the --profile command line argument.
+        argument-p
+          The profile was selected by the -p command line argument.
+        firstrun-claimed-default
+          A first run of a dedicated profiles build chose the old default
+          profile to be the default for this install.
+        firstrun-skipped-default:
+          A first run of a dedicated profiles build skipped over the old default
+          profile and created a new profile.
+        firstrun-created-default:
+          A first run of the application created a new profile to use.
+        default:
+          The default profile was selected as normal.
+    expires: "72"
+    keyed: false
+    kind: string
+    notification_emails:
+      - dtownsend@mozilla.com
+      - rtestard@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - main
+
 # The following section is for probes testing the Telemetry system. They will not be
 # submitted in pings and are only used for testing.
 telemetry.test:
   unsigned_int_kind:
     bug_numbers:
       - 1276190
     description: >
       This is a test uint type with a really long description, maybe spanning even multiple
--- a/toolkit/components/telemetry/app/TelemetrySend.jsm
+++ b/toolkit/components/telemetry/app/TelemetrySend.jsm
@@ -50,18 +50,16 @@ const TOPIC_QUIT_APPLICATION_GRANTED = "
 const TOPIC_QUIT_APPLICATION_FORCED = "quit-application-forced";
 const PREF_CHANGED_TOPIC = "nsPref:changed";
 const TOPIC_PROFILE_CHANGE_NET_TEARDOWN = "profile-change-net-teardown";
 
 // Whether the FHR/Telemetry unification features are enabled.
 // Changing this pref requires a restart.
 const IS_UNIFIED_TELEMETRY = Services.prefs.getBoolPref(TelemetryUtils.Preferences.Unified, false);
 
-const PING_FORMAT_VERSION = 4;
-
 const MS_IN_A_MINUTE = 60 * 1000;
 
 const PING_TYPE_OPTOUT = "optout";
 
 // We try to spread "midnight" pings out over this interval.
 const MIDNIGHT_FUZZING_INTERVAL_MS = 60 * MS_IN_A_MINUTE;
 // We delay sending "midnight" pings on this client by this interval.
 const MIDNIGHT_FUZZING_DELAY_MS = Math.random() * MIDNIGHT_FUZZING_INTERVAL_MS;
@@ -1041,17 +1039,17 @@ var TelemetrySendImpl = {
 
     if (success && isPersisted) {
       return TelemetryStorage.removePendingPing(id);
     }
     return Promise.resolve();
   },
 
   _buildSubmissionURL(ping) {
-    const version = isV4PingFormat(ping) ? PING_FORMAT_VERSION : 1;
+    const version = isV4PingFormat(ping) ? AppConstants.TELEMETRY_PING_FORMAT_VERSION : 1;
     return this._server + this._getSubmissionPath(ping) + "?v=" + version;
   },
 
   _getSubmissionPath(ping) {
     // The new ping format contains an "application" section, the old one doesn't.
     let pathComponents;
     if (isV4PingFormat(ping)) {
       // We insert the Ping id in the URL to simplify server handling of duplicated
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/docs/data/downgrade-ping.rst
@@ -0,0 +1,30 @@
+
+"downgrade" ping
+================
+
+This ping is captured when attempting to use a profile that was previously used
+with a newer version of the application.
+
+This ping is submitted directly through the ```pingsender```. The common ping
+data relates to the profile and application that the user attempted to use.
+
+The client ID is submitted with this ping. No environment block is included with
+this ping.
+
+Structure:
+
+.. code-block:: js
+
+    {
+      type: "downgrade",
+      ... common ping data
+      clientId: <UUID>,
+      payload: {
+        lastVersion: "", // The last version of the application that ran this profile
+        hasSync: <bool>, // Whether the profile is signed in to sync
+        hasBinary: <bool>, // Whether the last version of the application is available to run
+        button: <int> // The button the user chose to click from the UI:
+                      //   0 - Quit
+                      //   1 - Create new profile
+      }
+    }
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/telemetry-constants.mozbuild
@@ -0,0 +1,8 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DEFINES['TELEMETRY_PING_FORMAT_VERSION'] = 4
+
--- a/toolkit/components/url-classifier/tests/mochitest/allowlistAnnotatedFrame.html
+++ b/toolkit/components/url-classifier/tests/mochitest/allowlistAnnotatedFrame.html
@@ -22,17 +22,17 @@ function checkLoads() {
   window.parent.is(scriptItem2, "spoiled", "Should not block tracking js 2");
   window.parent.is(imageItem1, "spoiled", "Should not block tracking img 1");
   window.parent.is(imageItem2, "spoiled", "Should not block tracking img 2");
   window.parent.is(frameItem1, "spoiled", "Should not block tracking iframe 1");
   window.parent.is(frameItem2, "spoiled", "Should not block tracking iframe 2");
   window.parent.is(mediaItem1, "loaded", "Should not block tracking video");
   window.parent.is(xhrItem, "loaded", "Should not block tracking XHR");
   window.parent.is(fetchItem, "loaded", "Should not block fetches from tracking domains");
-  window.parent.is(window.document.blockedTrackingNodeCount, 0,
+  window.parent.is(window.document.blockedNodeByClassifierCount, 0,
     "No elements should be blocked");
 
   // End (parent) test.
   window.parent.clearPermissions();
   window.parent.SimpleTest.finish();
 }
 
 var onloadCalled = false;
--- a/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html
+++ b/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html
@@ -49,50 +49,50 @@ function checkLoads() {
   window.parent.is(
     fetchItem, "error", "Should not fetch from tracking URLs");
 
   var elt = document.getElementById("styleCheck");
   var style = document.defaultView.getComputedStyle(elt);
   window.parent.isnot(
     style.visibility, "hidden", "Should not load tracking css");
 
-  window.parent.is(window.document.blockedTrackingNodeCount, badids.length,
+  window.parent.is(window.document.blockedNodeByClassifierCount, badids.length,
     "Should identify all tracking elements");
 
-  var blockedTrackingNodes = window.document.blockedTrackingNodes;
+  var blockedNodes = window.document.blockedNodesByClassifier;
 
-  // Make sure that every node in blockedTrackingNodes exists in the tree
+  // Make sure that every node in blockedNodes exists in the tree
   // (that may not always be the case but do not expect any nodes to disappear
   // from the tree here)
   var allNodeMatch = true;
-  for (let i = 0; i < blockedTrackingNodes.length; i++) {
+  for (let i = 0; i < blockedNodes.length; i++) {
     let nodeMatch = false;
     for (let j = 0; j < badids.length && !nodeMatch; j++) {
       nodeMatch = nodeMatch ||
-        (blockedTrackingNodes[i] == document.getElementById(badids[j]));
+        (blockedNodes[i] == document.getElementById(badids[j]));
     }
 
     allNodeMatch = allNodeMatch && nodeMatch;
   }
   window.parent.ok(allNodeMatch,
     "All annotated nodes are expected in the tree");
 
   // Make sure that every node with a badid (see badids) is found in the
-  // blockedTrackingNodes. This tells us if we are neglecting to annotate
+  // blockedNodes. This tells us if we are neglecting to annotate
   // some nodes
   allNodeMatch = true;
   for (let j = 0; j < badids.length; j++) {
     let nodeMatch = false;
-    for (let i = 0; i < blockedTrackingNodes.length && !nodeMatch; i++) {
+    for (let i = 0; i < blockedNodes.length && !nodeMatch; i++) {
       nodeMatch = nodeMatch ||
-        (blockedTrackingNodes[i] == document.getElementById(badids[j]));
+        (blockedNodes[i] == document.getElementById(badids[j]));
     }
 
     if (!nodeMatch) {
-      console.log(badids[j] + " was not found in blockedTrackingNodes");
+      console.log(badids[j] + " was not found in blockedNodes");
     }
     allNodeMatch = allNodeMatch && nodeMatch;
   }
   window.parent.ok(allNodeMatch,
     "All tracking nodes are expected to be annotated as such");
 
   // Unset prefs, etc.
   window.parent.cleanup();
--- a/toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html
@@ -67,44 +67,44 @@ function checkLoads(aWindow, aBlocked) {
   is(win.document.getElementById("badimage").dataset.touched, aBlocked ? "no" : "yes", "Should not load tracking images");
   is(win.document.getElementById("goodscript").dataset.touched, "yes", "Should load whitelisted tracking javascript");
   is(win.document.getElementById("goodimage").dataset.touched, "yes", "Should load non-blacklisted image");
 
   var elt = win.document.getElementById("styleCheck");
   var style = win.document.defaultView.getComputedStyle(elt);
   isnot(style.visibility, aBlocked ? "hidden" : "", "Should not load tracking css");
 
-  is(win.document.blockedTrackingNodeCount, aBlocked ? badids.length : 0, "Should identify all tracking elements");
+  is(win.document.blockedNodeByClassifierCount, aBlocked ? badids.length : 0, "Should identify all tracking elements");
 
-  var blockedTrackingNodes = win.document.blockedTrackingNodes;
+  var blockedNodes = win.document.blockedNodesByClassifier;
 
-  // Make sure that every node in blockedTrackingNodes exists in the tree
+  // Make sure that every node in blockedNodes exists in the tree
   // (that may not always be the case but do not expect any nodes to disappear
   // from the tree here)
   var allNodeMatch = true;
-  for (let i = 0; i < blockedTrackingNodes.length; i++) {
+  for (let i = 0; i < blockedNodes.length; i++) {
     let nodeMatch = false;
     for (let j = 0; j < badids.length && !nodeMatch; j++) {
       nodeMatch = nodeMatch ||
-        (blockedTrackingNodes[i] == win.document.getElementById(badids[j]));
+        (blockedNodes[i] == win.document.getElementById(badids[j]));
     }
 
     allNodeMatch = allNodeMatch && nodeMatch;
   }
   is(allNodeMatch, true, "All annotated nodes are expected in the tree");
 
   // Make sure that every node with a badid (see badids) is found in the
-  // blockedTrackingNodes. This tells us if we are neglecting to annotate
+  // blockedNodes. This tells us if we are neglecting to annotate
   // some nodes
   allNodeMatch = true;
   for (let j = 0; j < badids.length; j++) {
     let nodeMatch = false;
-    for (let i = 0; i < blockedTrackingNodes.length && !nodeMatch; i++) {
+    for (let i = 0; i < blockedNodes.length && !nodeMatch; i++) {
       nodeMatch = nodeMatch ||
-        (blockedTrackingNodes[i] == win.document.getElementById(badids[j]));
+        (blockedNodes[i] == win.document.getElementById(badids[j]));
     }
 
     allNodeMatch = allNodeMatch && nodeMatch;
   }
   is(allNodeMatch, aBlocked, "All tracking nodes are expected to be annotated as such");
 }
 
 SpecialPowers.pushPrefEnv(
--- a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.html
@@ -56,55 +56,55 @@ function testOnWindow(contentPage) {
 var alwaysbadids = [
   "badscript",
 ];
 
 function checkLoads(aWindow, aWhitelisted, tpEnabled) {
   var win = aWindow.content;
   if (!tpEnabled) {
     is(win.document.getElementById("badscript").dataset.touched, "yes", "Should load tracking javascript");
-    is(win.document.blockedTrackingNodeCount, 0, "Should not identify any tracking elements");
+    is(win.document.blockedNodeByClassifierCount, 0, "Should not identify any tracking elements");
     return;
   }
 
   is(win.document.getElementById("badscript").dataset.touched, "no", "Should not load tracking javascript");
   is(win.document.getElementById("goodscript").dataset.touched, aWhitelisted ? "yes" : "no", "Should load whitelisted tracking javascript");
 
   var badids = alwaysbadids.slice();
   if (!aWhitelisted) {
     badids.push("goodscript");
   }
-  is(win.document.blockedTrackingNodeCount, badids.length, "Should identify all tracking elements");
+  is(win.document.blockedNodeByClassifierCount, badids.length, "Should identify all tracking elements");
 
-  var blockedTrackingNodes = win.document.blockedTrackingNodes;
+  var blockedNodes = win.document.blockedNodesByClassifier;
 
-  // Make sure that every node in blockedTrackingNodes exists in the tree
+  // Make sure that every node in blockedNodes exists in the tree
   // (that may not always be the case but do not expect any nodes to disappear
   // from the tree here)
   var allNodeMatch = true;
-  for (let i = 0; i < blockedTrackingNodes.length; i++) {
+  for (let i = 0; i < blockedNodes.length; i++) {
     let nodeMatch = false;
     for (let j = 0; j < badids.length && !nodeMatch; j++) {
       nodeMatch = nodeMatch ||
-        (blockedTrackingNodes[i] == win.document.getElementById(badids[j]));
+        (blockedNodes[i] == win.document.getElementById(badids[j]));
     }
 
     allNodeMatch = allNodeMatch && nodeMatch;
   }
   is(allNodeMatch, true, "All annotated nodes are expected in the tree");
 
   // Make sure that every node with a badid (see badids) is found in the
-  // blockedTrackingNodes. This tells us if we are neglecting to annotate
+  // blockedNodes. This tells us if we are neglecting to annotate
   // some nodes
   allNodeMatch = true;
   for (let j = 0; j < badids.length; j++) {
     let nodeMatch = false;
-    for (let i = 0; i < blockedTrackingNodes.length && !nodeMatch; i++) {
+    for (let i = 0; i < blockedNodes.length && !nodeMatch; i++) {
       nodeMatch = nodeMatch ||
-        (blockedTrackingNodes[i] == win.document.getElementById(badids[j]));
+        (blockedNodes[i] == win.document.getElementById(badids[j]));
     }
 
     allNodeMatch = allNodeMatch && nodeMatch;
   }
   is(allNodeMatch, true, "All tracking nodes are expected to be annotated as such");
 }
 
 SpecialPowers.pushPrefEnv(
--- a/toolkit/content/aboutProfiles.js
+++ b/toolkit/content/aboutProfiles.js
@@ -9,55 +9,28 @@ const {XPCOMUtils} = ChromeUtils.import(
 
 XPCOMUtils.defineLazyServiceGetter(
   this,
   "ProfileService",
   "@mozilla.org/toolkit/profile-service;1",
   "nsIToolkitProfileService"
 );
 
-// nsIToolkitProfileService.selectProfile can be used only during the selection
-// of the profile in the ProfileManager. If we are showing about:profiles in a
-// tab, the selectedProfile returns the default profile.
-// In this function we use the ProfD to find the current profile.
-function findCurrentProfile() {
-  let cpd;
-  try {
-    cpd = Services.dirsvc.get("ProfD", Ci.nsIFile);
-  } catch (e) {}
-
-  if (cpd) {
-    for (let profile of ProfileService.profiles) {
-      if (profile.rootDir.path == cpd.path) {
-        return profile;
-      }
-    }
-  }
-
-  // selectedProfile can throw if nothing is selected or if the selected profile
-  // has been deleted.
-  try {
-    return ProfileService.selectedProfile;
-  } catch (e) {
-    return null;
-  }
-}
-
 function refreshUI() {
   let parent = document.getElementById("profiles");
   while (parent.firstChild) {
     parent.firstChild.remove();
   }
 
   let defaultProfile;
   try {
     defaultProfile = ProfileService.defaultProfile;
   } catch (e) {}
 
-  let currentProfile = findCurrentProfile();
+  let currentProfile = ProfileService.currentProfile;
 
   for (let profile of ProfileService.profiles) {
     let isCurrentProfile = profile == currentProfile;
     let isInUse = isCurrentProfile;
     if (!isInUse) {
       try {
         let lock = profile.lock({});
         lock.unlock();
@@ -192,20 +165,20 @@ function display(profileData) {
     };
     div.appendChild(runButton);
   }
 
   let sep = document.createElement("hr");
   div.appendChild(sep);
 }
 
+// This is called from the createProfileWizard.xul dialog.
 function CreateProfile(profile) {
-  ProfileService.selectedProfile = profile;
-  ProfileService.flush();
-  refreshUI();
+  // The wizard created a profile, just make it the default.
+  defaultProfile(profile);
 }
 
 function createProfileWizard() {
   // This should be rewritten in HTML eventually.
   window.openDialog("chrome://mozapps/content/profile/createProfileWizard.xul",
                     "", "centerscreen,chrome,modal,titlebar",
                     ProfileService);
 }
@@ -264,40 +237,36 @@ async function removeProfile(profile) {
       return;
     }
 
     if (buttonPressed == 2) {
       deleteFiles = true;
     }
   }
 
-  // If we are deleting the selected or the default profile we must choose a
-  // different one.
-  let isSelected = false;
-  try {
-    isSelected = ProfileService.selectedProfile == profile;
-  } catch (e) {}
-
+  // If we are deleting the default profile we must choose a different one.
   let isDefault = false;
   try {
     isDefault = ProfileService.defaultProfile == profile;
   } catch (e) {}
 
-  if (isSelected || isDefault) {
+  if (isDefault) {
     for (let p of ProfileService.profiles) {
       if (profile == p) {
         continue;
       }
 
-      if (isSelected) {
-        ProfileService.selectedProfile = p;
-      }
-
       if (isDefault) {
-        ProfileService.defaultProfile = p;
+        try {
+          ProfileService.defaultProfile = p;
+        } catch (e) {
+          // This can happen on dev-edition if a non-default profile is in use.
+          // In such a case the next time that dev-edition is started it will
+          // find no default profile and just create a new one.
+        }
       }
 
       break;
     }
   }
 
   try {
     profile.removeInBackground(deleteFiles);
@@ -310,20 +279,29 @@ async function removeProfile(profile) {
     Services.prompt.alert(window, title, msg);
     return;
   }
 
   ProfileService.flush();
   refreshUI();
 }
 
-function defaultProfile(profile) {
-  ProfileService.defaultProfile = profile;
-  ProfileService.selectedProfile = profile;
-  ProfileService.flush();
+async function defaultProfile(profile) {
+  try {
+    ProfileService.defaultProfile = profile;
+    ProfileService.flush();
+  } catch (e) {
+    // This can happen on dev-edition.
+    let [title, msg] = await document.l10n.formatValues([
+        { id: "profiles-cannot-set-as-default-title" },
+        { id: "profiles-cannot-set-as-default-message" },
+    ]);
+
+    Services.prompt.alert(window, title, msg);
+  }
   refreshUI();
 }
 
 function openProfile(profile) {
   let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
                      .createInstance(Ci.nsISupportsPRBool);
   Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
 
--- a/toolkit/content/aboutProfiles.xhtml
+++ b/toolkit/content/aboutProfiles.xhtml
@@ -7,16 +7,17 @@
 <!DOCTYPE html>
 
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
     <title data-l10n-id="profiles-title"></title>
     <link rel="icon" type="image/png" id="favicon" href="chrome://branding/content/icon32.png" />
     <link rel="stylesheet" href="chrome://mozapps/skin/aboutProfiles.css" type="text/css" />
     <script type="application/javascript" src="chrome://global/content/aboutProfiles.js" />
+    <link rel="localization" href="branding/brand.ftl" />
     <link rel="localization" href="toolkit/about/aboutProfiles.ftl" />
 </head>
   <body id="body" class="wide-container">
     <div id="action-box" class="notice-box">
       <h3 data-l10n-id="profiles-restart-title"></h3>
       <button id="restart-in-safe-mode-button" data-l10n-id="profiles-restart-in-safe-mode"></button>
       <button id="restart-button" data-l10n-id="profiles-restart-normal"></button>
     </div>
new file mode 100644
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/mozapps/profile/profileDowngrade.dtd
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<!-- LOCALIZATION NOTE:
+ This UI can be most easily shown by modifying the version in compatibility.ini
+ to a newer version and then starting Firefox.
+ For this feature, "installation" is used to mean "this discrete download of
+ Firefox" and "version" is used to mean "the specific revision number of a
+ given Firefox channel". These terms are not synonymous.
+-->
+<!ENTITY window.title "You’ve launched an older version of Firefox">
+<!ENTITY window.style "width: 490px;">
+
+<!ENTITY window.nosync "Using an older version of Firefox can corrupt bookmarks and browsing history already saved to an existing Firefox profile. To protect your information, create a new profile for this installation of &brandShortName;.">
+<!ENTITY window.sync "Using an older version of Firefox can corrupt bookmarks and browsing history already saved to an existing Firefox profile. To protect your information, create a new profile for this installation of &brandShortName;. You can always sign in with a &syncBrand.fxAccount.label; to sync your bookmarks and browsing history between profiles.">
+
+<!ENTITY window.create "Create New Profile">
+<!ENTITY window.quit-win "Exit">
+<!ENTITY window.quit-nonwin "Quit">
--- a/toolkit/locales/en-US/toolkit/about/aboutProfiles.ftl
+++ b/toolkit/locales/en-US/toolkit/about/aboutProfiles.ftl
@@ -26,16 +26,19 @@ profiles-localdir = Local Directory
 profiles-current-profile = This is the profile in use and it cannot be deleted.
 profiles-in-use-profile = This profile is in use in another application and it cannot be deleted.
 
 profiles-rename = Rename
 profiles-remove = Remove
 profiles-set-as-default = Set as default profile
 profiles-launch-profile = Launch profile in new browser
 
+profiles-cannot-set-as-default-title = Unable to set default
+profiles-cannot-set-as-default-message = The default profile cannot be changed for { -brand-short-name }.
+
 profiles-yes = yes
 profiles-no = no
 
 profiles-rename-profile-title = Rename Profile
 # Variables:
 #   $name (String) - Name of the profile
 profiles-rename-profile = Rename profile { $name }
 
--- a/toolkit/locales/jar.mn
+++ b/toolkit/locales/jar.mn
@@ -78,16 +78,19 @@
   locale/@AB_CD@/mozapps/extensions/extensions.properties         (%chrome/mozapps/extensions/extensions.properties)
   locale/@AB_CD@/mozapps/extensions/blocklist.dtd                 (%chrome/mozapps/extensions/blocklist.dtd)
 #endif
   locale/@AB_CD@/mozapps/handling/handling.dtd                    (%chrome/mozapps/handling/handling.dtd)
   locale/@AB_CD@/mozapps/handling/handling.properties             (%chrome/mozapps/handling/handling.properties)
   locale/@AB_CD@/mozapps/profile/createProfileWizard.dtd          (%chrome/mozapps/profile/createProfileWizard.dtd)
   locale/@AB_CD@/mozapps/profile/profileSelection.properties      (%chrome/mozapps/profile/profileSelection.properties)
   locale/@AB_CD@/mozapps/profile/profileSelection.dtd             (%chrome/mozapps/profile/profileSelection.dtd)
+#ifdef MOZ_BLOCK_PROFILE_DOWNGRADE
+  locale/@AB_CD@/mozapps/profile/profileDowngrade.dtd             (%chrome/mozapps/profile/profileDowngrade.dtd)
+#endif
 #ifndef MOZ_FENNEC
   locale/@AB_CD@/mozapps/update/updates.dtd                       (%chrome/mozapps/update/updates.dtd)
   locale/@AB_CD@/mozapps/update/updates.properties                (%chrome/mozapps/update/updates.properties)
 #endif
 % locale pluginproblem @AB_CD@ %locale/@AB_CD@/pluginproblem/
   locale/@AB_CD@/pluginproblem/pluginproblem.dtd                  (%chrome/pluginproblem/pluginproblem.dtd)
 % locale alerts @AB_CD@ %locale/@AB_CD@/alerts/
   locale/@AB_CD@/alerts/alert.dtd                                (%chrome/alerts/alert.dtd)
--- a/toolkit/modules/AppConstants.jsm
+++ b/toolkit/modules/AppConstants.jsm
@@ -341,9 +341,11 @@ this.AppConstants = Object.freeze({
 #endif
 
   MOZ_CODE_COVERAGE:
 #ifdef MOZ_CODE_COVERAGE
     true,
 #else
     false,
 #endif
+
+  TELEMETRY_PING_FORMAT_VERSION: @TELEMETRY_PING_FORMAT_VERSION@,
 });
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -1,14 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+include('../components/telemetry/telemetry-constants.mozbuild')
+
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'General')
 
 with Files('addons/**'):
     BUG_COMPONENT = ('WebExtensions', 'General')
 
 with Files('docs/**'):
     BUG_COMPONENT = ('Toolkit', 'Async Tooling')
--- a/toolkit/moz.build
+++ b/toolkit/moz.build
@@ -66,9 +66,8 @@ with Files('library/**'):
 with Files('mozapps/installer/windows/**'):
     BUG_COMPONENT = ('Toolkit', 'NSIS Installer')
 
 with Files('mozapps/preferences/**'):
     BUG_COMPONENT = ('Toolkit', 'Preferences')
 
 with Files('pluginproblem/**'):
     BUG_COMPONENT = ('Core', 'Plug-ins')
-
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/content/profileDowngrade.js
@@ -0,0 +1,29 @@
+/* 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/. */
+
+let gParams;
+
+function init() {
+  /*
+   * The C++ code passes a dialog param block using its integers as in and out
+   * arguments for this UI. The following are the uses of the integers:
+   *
+   *  0: A set of flags from nsIToolkitProfileService.downgradeUIFlags.
+   *  1: A return argument, one of nsIToolkitProfileService.downgradeUIChoice.
+   */
+  gParams = window.arguments[0].QueryInterface(Ci.nsIDialogParamBlock);
+  let hasSync = gParams.GetInt(0) & Ci.nsIToolkitProfileService.hasSync;
+
+  document.getElementById("sync").hidden = !hasSync;
+  document.getElementById("nosync").hidden = hasSync;
+}
+
+function quit() {
+  gParams.SetInt(1, Ci.nsIToolkitProfileService.quit);
+}
+
+function createProfile() {
+  gParams.SetInt(1, Ci.nsIToolkitProfileService.createNewProfile);
+  window.close();
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/content/profileDowngrade.xul
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+<?xml-stylesheet href="chrome://mozapps/skin/profile/profileDowngrade.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
+%syncBrandDTD;
+<!ENTITY % profileDTD SYSTEM "chrome://mozapps/locale/profile/profileDowngrade.dtd">
+%profileDTD;
+]>
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="&window.title;" onload="init()" style="&window.style;"
+        buttonlabelextra1="&window.create;" ondialogextra1="createProfile()"
+#ifdef XP_WIN
+        buttonlabelaccept="&window.quit-win;"
+#else
+        buttonlabelaccept="&window.quit-nonwin;"
+#endif
+        ondialogaccept="quit()" ondialogcancel="quit()"
+        buttons="accept,extra1" buttonpack="end">
+
+  <script type="application/javascript" src="profileDowngrade.js"/>
+
+  <hbox flex="1" align="start">
+    <image id="info" role="presentation"/>
+    <vbox flex="1">
+      <description id="nosync">&window.nosync;</description>
+      <description id="sync">&window.sync;</description>
+    </vbox>
+  </hbox>
+
+</dialog>
--- a/toolkit/profile/content/profileSelection.js
+++ b/toolkit/profile/content/profileSelection.js
@@ -34,17 +34,17 @@ function startup() {
     for (let profile of gProfileService.profiles.entries(I.nsIToolkitProfile)) {
       var listitem = profilesElement.appendItem(profile.name, "");
 
       var tooltiptext =
         gProfileManagerBundle.getFormattedString("profileTooltip", [profile.name, profile.rootDir.path]);
       listitem.setAttribute("tooltiptext", tooltiptext);
       listitem.profile = profile;
       try {
-        if (profile === gProfileService.selectedProfile) {
+        if (profile === gProfileService.defaultProfile) {
           setTimeout(function(a) {
             profilesElement.ensureElementIsVisible(a);
             profilesElement.selectItem(a);
           }, 0, listitem);
         }
       } catch (e) { }
     }
 
@@ -88,26 +88,28 @@ function acceptDialog() {
     var locked =
       gProfileManagerBundle.getFormattedString("profileLocked2", [appName, selectedProfile.profile.name, appName]);
     Services.prompt.alert(window, lockedTitle, locked);
 
     return false;
   }
   gDialogParams.objects.insertElementAt(profileLock.nsIProfileLock, 0);
 
-  gProfileService.selectedProfile = selectedProfile.profile;
-  gProfileService.defaultProfile = selectedProfile.profile;
+  try {
+    gProfileService.defaultProfile = selectedProfile.profile;
+  } catch (e) {
+    // This can happen on dev-edition. We'll still restart with the selected
+    // profile based on the lock's directories.
+  }
   updateStartupPrefs();
 
   gDialogParams.SetInt(0, 1);
   /* Bug 257777 */
   gDialogParams.SetInt(1, document.getElementById("offlineState").checked ? 1 : 0);
 
-  gDialogParams.SetString(0, selectedProfile.profile.name);
-
   return true;
 }
 
 function exitDialog() {
   updateStartupPrefs();
 
   return true;
 }
--- a/toolkit/profile/jar.mn
+++ b/toolkit/profile/jar.mn
@@ -2,8 +2,12 @@
 # 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/.
 
 toolkit.jar:
   content/mozapps/profile/createProfileWizard.js  (content/createProfileWizard.js)
 * content/mozapps/profile/createProfileWizard.xul (content/createProfileWizard.xul)
   content/mozapps/profile/profileSelection.js     (content/profileSelection.js)
   content/mozapps/profile/profileSelection.xul    (content/profileSelection.xul)
+#ifdef MOZ_BLOCK_PROFILE_DOWNGRADE
+  content/mozapps/profile/profileDowngrade.js     (content/profileDowngrade.js)
+* content/mozapps/profile/profileDowngrade.xul    (content/profileDowngrade.xul)
+#endif
--- a/toolkit/profile/nsIToolkitProfileService.idl
+++ b/toolkit/profile/nsIToolkitProfileService.idl
@@ -8,41 +8,60 @@
 interface nsISimpleEnumerator;
 interface nsIFile;
 interface nsIToolkitProfile;
 interface nsIProfileLock;
 
 [scriptable, builtinclass, uuid(1947899b-f369-48fa-89da-f7c37bb1e6bc)]
 interface nsIToolkitProfileService : nsISupports
 {
+    /**
+     * When a downgrade is detected UI is presented to the user to ask how to
+     * proceed. These flags are used to pass some information to the UI.
+     */
+    cenum downgradeUIFlags: 8 {
+        hasSync = 1,
+    };
+
+    /**
+     * When a downgrade is detected UI is presented to the user to ask how to
+     * proceed. These are the possible options the user can choose.
+     */
+    cenum downgradeUIChoice: 8 {
+        quit = 0,
+        createNewProfile = 1,
+    };
+
     attribute boolean startWithLastProfile;
 
     readonly attribute nsISimpleEnumerator /*nsIToolkitProfile*/ profiles;
 
     /**
-     * The currently selected profile (the one used or about to be used by the
-     * browser).
+     * The profile currently in use if it is a named profile. This will return
+     * null if the current profile path doesn't match a profile in the database.
      */
-    attribute nsIToolkitProfile selectedProfile;
+    readonly attribute nsIToolkitProfile currentProfile;
 
     /**
-     * The default profile (the one used or about to be used by the
-     * browser if no other profile is specified at runtime). This is the profile
-     * marked with Default=1 in profiles.ini and is usually the same as
-     * selectedProfile, except on Developer Edition.
-     *
-     * Developer Edition uses a profile named "dev-edition-default" as the
-     * default profile (which it creates if it doesn't exist), unless a special
-     * empty file named "ignore-dev-edition-profile" is present next to
-     * profiles.ini. In that case Developer Edition behaves the same as any
-     * other build of Firefox.
+     * The default profile for this build.
+     * On startup this is the profile selected unless overridden by command line
+     * arguments or environment variables. Setting this will change the profile
+     * used by default the next time the application is started.
+     * Attempting to change the default may throw an exception on builds that do
+     * not support changing the default profile, such as developer edition.
      */
     attribute nsIToolkitProfile defaultProfile;
 
     /**
+     * True if during startup a new profile was created for this install instead
+     * of using the profile that was the default for older versions.
+     */
+    readonly attribute boolean createdAlternateProfile;
+
+    /**
      * Selects or creates a profile to use based on the profiles database, any
      * environment variables and any command line arguments. Will not create
      * a profile if aIsResetting is true. The profile is selected based on this
      * order of preference:
      * * Environment variables (set when restarting the application).
      * * --profile command line argument.
      * * --createprofile command line argument (this also causes the app to exit).
      * * -p command line argument.
@@ -67,27 +86,47 @@ interface nsIToolkitProfileService : nsI
      */
     nsIToolkitProfile getProfileByName(in AUTF8String aName);
 
     /**
      * Create a new profile.
      *
      * The profile temporary directory will be chosen based on where the
      * profile directory is located.
-     * 
+     *
+     * If a profile with the given name already exists it will be returned
+     * instead of creating a new profile.
+     *
      * @param aRootDir
      *        The profile directory. May be null, in which case a suitable
      *        default will be chosen based on the profile name.
      * @param aName
      *        The profile name.
      */
     nsIToolkitProfile createProfile(in nsIFile aRootDir,
                                     in AUTF8String aName);
 
     /**
+     * Create a new profile with a unique name.
+     *
+     * As above however the name given will be altered to make it a unique
+     * profile name.
+     *
+     * @param aRootDir
+     *        The profile directory. May be null, in which case a suitable
+     *        default will be chosen based on the profile name.
+     * @param aNamePrefix
+     *        The prefix to use for the profile name. If unused this will be
+     *        used as the profile name otherwise additional characters will be
+     *        added to make the name unique.
+     */
+    nsIToolkitProfile createUniqueProfile(in nsIFile aRootDir,
+                                          in AUTF8String aNamePrefix);
+
+    /**
      * Returns the number of profiles.
      * @return the number of profiles.
      */
     readonly attribute unsigned long profileCount;
 
     /**
      * Flush the profiles list file.
      */
--- a/toolkit/profile/nsToolkitProfileService.cpp
+++ b/toolkit/profile/nsToolkitProfileService.cpp
@@ -24,29 +24,33 @@
 #include "nsIFile.h"
 
 #ifdef XP_MACOSX
 #  include <CoreFoundation/CoreFoundation.h>
 #  include "nsILocalFileMac.h"
 #endif
 
 #include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
 #include "nsNetCID.h"
 #include "nsXULAppAPI.h"
 #include "nsThreadUtils.h"
 
 #include "nsIRunnable.h"
-#include "nsINIParser.h"
 #include "nsXREDirProvider.h"
 #include "nsAppRunner.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsNativeCharsetUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Sprintf.h"
+#include "nsPrintfCString.h"
+#include "mozilla/UniquePtr.h"
+#include "nsIToolkitShellService.h"
+#include "mozilla/Telemetry.h"
 
 using namespace mozilla;
 
 #define DEV_EDITION_NAME "dev-edition-default"
 #define DEFAULT_NAME "default"
 
 nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
                                    nsIFile* aLocalDir, nsToolkitProfile* aPrev)
@@ -83,18 +87,36 @@ nsToolkitProfile::GetName(nsACString& aR
   aResult = mName;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsToolkitProfile::SetName(const nsACString& aName) {
   NS_ASSERTION(nsToolkitProfileService::gService, "Where did my service go?");
 
+  if (mName.Equals(aName)) {
+    return NS_OK;
+  }
+
+  // Changing the name from the dev-edition default profile name makes this
+  // profile no longer the dev-edition default.
+  if (mName.EqualsLiteral(DEV_EDITION_NAME) &&
+      nsToolkitProfileService::gService->mDevEditionDefault == this) {
+    nsToolkitProfileService::gService->mDevEditionDefault = nullptr;
+  }
+
   mName = aName;
 
+  // Setting the name to the dev-edition default profile name will cause this
+  // profile to become the dev-edition default.
+  if (aName.EqualsLiteral(DEV_EDITION_NAME) &&
+      !nsToolkitProfileService::gService->mDevEditionDefault) {
+    nsToolkitProfileService::gService->mDevEditionDefault = this;
+  }
+
   return NS_OK;
 }
 
 nsresult nsToolkitProfile::RemoveInternal(bool aRemoveFiles,
                                           bool aInBackground) {
   NS_ASSERTION(nsToolkitProfileService::gService, "Whoa, my service is gone.");
 
   if (mLock) return NS_ERROR_FILE_IS_LOCKED;
@@ -142,18 +164,25 @@ nsresult nsToolkitProfile::RemoveInterna
   else
     nsToolkitProfileService::gService->mFirst = mNext;
 
   if (mNext) mNext->mPrev = mPrev;
 
   mPrev = nullptr;
   mNext = nullptr;
 
-  if (nsToolkitProfileService::gService->mChosen == this)
-    nsToolkitProfileService::gService->mChosen = nullptr;
+  if (nsToolkitProfileService::gService->mNormalDefault == this) {
+    nsToolkitProfileService::gService->mNormalDefault = nullptr;
+  }
+  if (nsToolkitProfileService::gService->mDevEditionDefault == this) {
+    nsToolkitProfileService::gService->mDevEditionDefault = nullptr;
+  }
+  if (nsToolkitProfileService::gService->mDedicatedProfile == this) {
+    nsToolkitProfileService::gService->SetDefaultProfile(nullptr);
+  }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsToolkitProfile::Remove(bool removeFiles) {
   return RemoveInternal(removeFiles, false /* in background */);
 }
@@ -260,74 +289,284 @@ nsToolkitProfileLock::~nsToolkitProfileL
   }
 }
 
 nsToolkitProfileService* nsToolkitProfileService::gService = nullptr;
 
 NS_IMPL_ISUPPORTS(nsToolkitProfileService, nsIToolkitProfileService)
 
 nsToolkitProfileService::nsToolkitProfileService()
-    : mStartupProfileSelected(false), mStartWithLast(true), mIsFirstRun(true) {
+    : mStartupProfileSelected(false),
+      mStartWithLast(true),
+      mIsFirstRun(true),
+      mUseDevEditionProfile(false),
+#ifdef MOZ_DEDICATED_PROFILES
+      mUseDedicatedProfile(!IsSnapEnvironment()),
+#else
+      mUseDedicatedProfile(false),
+#endif
+      mCreatedAlternateProfile(false),
+      mStartupReason(NS_LITERAL_STRING("unknown")) {
+#ifdef MOZ_DEV_EDITION
+  mUseDevEditionProfile = true;
+#endif
   gService = this;
 }
 
 nsToolkitProfileService::~nsToolkitProfileService() { gService = nullptr; }
 
+void nsToolkitProfileService::RecordStartupTelemetry() {
+  if (!mStartupProfileSelected) {
+    return;
+  }
+
+  ScalarSet(mozilla::Telemetry::ScalarID::STARTUP_PROFILE_SELECTION_REASON,
+            mStartupReason);
+}
+
+// Tests whether the passed profile was last used by this install.
+bool nsToolkitProfileService::IsProfileForCurrentInstall(
+    nsIToolkitProfile* aProfile) {
+  nsCOMPtr<nsIFile> profileDir;
+  nsresult rv = aProfile->GetRootDir(getter_AddRefs(profileDir));
+  NS_ENSURE_SUCCESS(rv, false);
+
+  nsCOMPtr<nsIFile> compatFile;
+  rv = profileDir->Clone(getter_AddRefs(compatFile));
+  NS_ENSURE_SUCCESS(rv, false);
+
+  rv = compatFile->Append(NS_LITERAL_STRING("compatibility.ini"));
+  NS_ENSURE_SUCCESS(rv, false);
+
+  nsINIParser compatData;
+  rv = compatData.Init(compatFile);
+  // If the file is missing then either this is an empty profile (likely
+  // generated by bug 1518591) or it is from an ancient version. We'll opt to
+  // use it in this case.
+  if (NS_FAILED(rv)) {
+    return true;
+  }
+
+  /**
+   * In xpcshell gDirServiceProvider doesn't have all the correct directories
+   * set so using NS_GetSpecialDirectory works better there. But in a normal
+   * app launch the component registry isn't initialized so
+   * NS_GetSpecialDirectory doesn't work. So we have to use two different
+   * paths to support testing.
+   */
+  nsCOMPtr<nsIFile> currentGreDir;
+  rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(currentGreDir));
+  if (rv == NS_ERROR_NOT_INITIALIZED) {
+    currentGreDir = gDirServiceProvider->GetGREDir();
+    MOZ_ASSERT(currentGreDir, "No GRE dir found.");
+  } else if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  nsCString greDirPath;
+  rv = compatData.GetString("Compatibility", "LastPlatformDir", greDirPath);
+  // If this string is missing then this profile is from an ancient version.
+  // We'll opt to use it in this case.
+  if (NS_FAILED(rv)) {
+    return true;
+  }
+
+  nsCOMPtr<nsIFile> greDir;
+  rv = NS_NewNativeLocalFile(EmptyCString(), false, getter_AddRefs(greDir));
+  NS_ENSURE_SUCCESS(rv, false);
+
+  rv = greDir->SetPersistentDescriptor(greDirPath);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  bool equal;
+  rv = greDir->Equals(currentGreDir, &equal);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  return equal;
+}
+
+/**
+ * Used the first time an install with dedicated profile support runs. Decides
+ * whether to mark the passed profile as the default for this install.
+ *
+ * The goal is to reduce disruption but ideally end up with the OS default
+ * install using the old default profile.
+ *
+ * If the decision is to use the profile then it will be unassigned as the
+ * dedicated default for other installs.
+ *
+ * We won't attempt to use the profile if it was last used by a different
+ * install.
+ *
+ * If the profile is currently in use by an install that was either the OS
+ * default install or the profile has been explicitely chosen by some other
+ * means then we won't use it.
+ *
+ * Returns true if we chose to make the profile the new dedicated default.
+ */
+bool nsToolkitProfileService::MaybeMakeDefaultDedicatedProfile(
+    nsIToolkitProfile* aProfile) {
+  nsresult rv;
+
+  // If the profile was last used by a different install then we won't use it.
+  if (!IsProfileForCurrentInstall(aProfile)) {
+    return false;
+  }
+
+  nsCString descriptor;
+  rv = GetProfileDescriptor(aProfile, descriptor, nullptr);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  // Get a list of all the installs.
+  nsTArray<nsCString> installs = GetKnownInstalls();
+
+  // Cache the installs that use the profile.
+  nsTArray<nsCString> inUseInstalls;
+
+  // See if the profile is already in use by an install that hasn't locked it.
+  for (uint32_t i = 0; i < installs.Length(); i++) {
+    const nsCString& install = installs[i];
+
+    nsCString path;
+    rv = mInstallData.GetString(install.get(), "Default", path);
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+
+    // Is this install using the profile we care about?
+    if (!descriptor.Equals(path)) {
+      continue;
+    }
+
+    // Is this profile locked to this other install?
+    nsCString isLocked;
+    rv = mInstallData.GetString(install.get(), "Locked", isLocked);
+    if (NS_SUCCEEDED(rv) && isLocked.Equals("1")) {
+      return false;
+    }
+
+    inUseInstalls.AppendElement(install);
+  }
+
+  // At this point we've decided to take the profile. Strip it from other
+  // installs.
+  for (uint32_t i = 0; i < inUseInstalls.Length(); i++) {
+    // Removing the default setting entirely will make the install go through
+    // the first run process again at startup and create itself a new profile.
+    mInstallData.DeleteString(inUseInstalls[i].get(), "Default");
+  }
+
+  // Set this as the default profile for this install.
+  SetDefaultProfile(aProfile);
+
+  bool isDefaultApp = false;
+
+  nsCOMPtr<nsIToolkitShellService> shell =
+      do_GetService(NS_TOOLKITSHELLSERVICE_CONTRACTID);
+  if (shell) {
+    rv = shell->IsDefaultApplication(&isDefaultApp);
+    // If the shell component is following XPCOM rules then this shouldn't be
+    // needed, but let's be safe.
+    if (NS_FAILED(rv)) {
+      isDefaultApp = false;
+    }
+  }
+
+  if (!isDefaultApp) {
+    // SetDefaultProfile will have locked this profile to this install so no
+    // other installs will steal it, but this was auto-selected so we want to
+    // unlock it so that the OS default install can take it at a later time.
+    mInstallData.DeleteString(mInstallHash.get(), "Locked");
+  }
+
+  // Persist the changes.
+  Flush();
+
+  return true;
+}
+
 nsresult nsToolkitProfileService::Init() {
   NS_ASSERTION(gDirServiceProvider, "No dirserviceprovider!");
   nsresult rv;
 
   rv = nsXREDirProvider::GetUserAppDataDirectory(getter_AddRefs(mAppData));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = nsXREDirProvider::GetUserLocalDataDirectory(getter_AddRefs(mTempData));
   NS_ENSURE_SUCCESS(rv, rv);
 
+  nsCString installProfilePath;
+
+  if (mUseDedicatedProfile) {
+    // Load the dedicated profiles database.
+    rv = mAppData->Clone(getter_AddRefs(mInstallFile));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mInstallFile->AppendNative(NS_LITERAL_CSTRING("installs.ini"));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsString installHash;
+    rv = gDirServiceProvider->GetInstallHash(installHash);
+    NS_ENSURE_SUCCESS(rv, rv);
+    CopyUTF16toUTF8(installHash, mInstallHash);
+
+    rv = mInstallData.Init(mInstallFile);
+    if (NS_SUCCEEDED(rv)) {
+      // Try to find the descriptor for the default profile for this install.
+      rv = mInstallData.GetString(mInstallHash.get(), "Default",
+                                  installProfilePath);
+      // Not having a value means this install doesn't appear in installs.ini so
+      // this is the first run for this install.
+      mIsFirstRun = NS_FAILED(rv);
+    }
+  }
+
   rv = mAppData->Clone(getter_AddRefs(mListFile));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mListFile->AppendNative(NS_LITERAL_CSTRING("profiles.ini"));
   NS_ENSURE_SUCCESS(rv, rv);
 
+  nsINIParser parser;
+
   bool exists;
   rv = mListFile->IsFile(&exists);
-  if (NS_FAILED(rv) || !exists) {
-    return NS_OK;
+  if (NS_SUCCEEDED(rv) && exists) {
+    rv = parser.Init(mListFile);
+    // Init does not fail on parsing errors, only on OOM/really unexpected
+    // conditions.
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
   }
 
-  int64_t size;
-  rv = mListFile->GetFileSize(&size);
-  if (NS_FAILED(rv) || !size) {
-    return NS_OK;
-  }
-
-  nsINIParser parser;
-  rv = parser.Init(mListFile);
-  // Init does not fail on parsing errors, only on OOM/really unexpected
-  // conditions.
-  if (NS_FAILED(rv)) return rv;
-
   nsAutoCString buffer;
   rv = parser.GetString("General", "StartWithLastProfile", buffer);
   if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("0")) mStartWithLast = false;
 
   nsToolkitProfile* currentProfile = nullptr;
 
 #ifdef MOZ_DEV_EDITION
-  nsCOMPtr<nsIFile> ignoreSeparateProfile;
-  rv = mAppData->Clone(getter_AddRefs(ignoreSeparateProfile));
+  nsCOMPtr<nsIFile> ignoreDevEditionProfile;
+  rv = mAppData->Clone(getter_AddRefs(ignoreDevEditionProfile));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = ignoreDevEditionProfile->AppendNative(
+      NS_LITERAL_CSTRING("ignore-dev-edition-profile"));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  bool shouldIgnoreSeparateProfile;
+  rv = ignoreDevEditionProfile->Exists(&shouldIgnoreSeparateProfile);
   if (NS_FAILED(rv)) return rv;
 
-  rv = ignoreSeparateProfile->AppendNative(
-      NS_LITERAL_CSTRING("ignore-dev-edition-profile"));
-  if (NS_FAILED(rv)) return rv;
-
-  bool shouldIgnoreSeparateProfile;
-  rv = ignoreSeparateProfile->Exists(&shouldIgnoreSeparateProfile);
-  if (NS_FAILED(rv)) return rv;
+  mUseDevEditionProfile = !shouldIgnoreSeparateProfile;
 #endif
 
   nsCOMPtr<nsIToolkitProfile> autoSelectProfile;
 
   unsigned int nonDevEditionProfiles = 0;
   unsigned int c = 0;
   for (c = 0; true; ++c) {
     nsAutoCString profileID("Profile");
@@ -377,52 +616,49 @@ nsresult nsToolkitProfileService::Init()
     }
 
     currentProfile =
         new nsToolkitProfile(name, rootDir, localDir, currentProfile);
     NS_ENSURE_TRUE(currentProfile, NS_ERROR_OUT_OF_MEMORY);
 
     rv = parser.GetString(profileID.get(), "Default", buffer);
     if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("1")) {
-      mDefault = currentProfile;
+      mNormalDefault = currentProfile;
+    }
+
+    // Is this the default profile for this install?
+    if (mUseDedicatedProfile && !mDedicatedProfile &&
+        installProfilePath.Equals(filePath)) {
+      // Found a profile for this install.
+      mDedicatedProfile = currentProfile;
     }
 
     if (name.EqualsLiteral(DEV_EDITION_NAME)) {
-#ifdef MOZ_DEV_EDITION
-      // Use the dev-edition-default profile if this is an Aurora build and
-      // ignore-dev-edition-profile is not present.
-      if (!shouldIgnoreSeparateProfile) {
-        mChosen = currentProfile;
-      }
-#endif
+      mDevEditionDefault = currentProfile;
     } else {
       nonDevEditionProfiles++;
       autoSelectProfile = currentProfile;
     }
   }
 
   // If there is only one non-dev-edition profile then mark it as the default.
-  if (!mDefault && nonDevEditionProfiles == 1) {
-    mDefault = autoSelectProfile;
+  if (!mNormalDefault && nonDevEditionProfiles == 1) {
+    mNormalDefault = autoSelectProfile;
   }
 
-  // Normally having no non-dev-edition builds suggests this is the first run.
-  mIsFirstRun = nonDevEditionProfiles == 0;
-
-#ifdef MOZ_DEV_EDITION
-  if (!shouldIgnoreSeparateProfile) {
-    // Except when using the separate dev-edition profile, in which case not
-    // finding it means this is a first run.
-    mIsFirstRun = !mChosen;
-  } else {
-    mChosen = mDefault;
+  if (!mUseDedicatedProfile) {
+    if (mUseDevEditionProfile) {
+      // When using the separate dev-edition profile not finding it means this
+      // is a first run.
+      mIsFirstRun = !mDevEditionDefault;
+    } else {
+      // If there are no normal profiles then this is a first run.
+      mIsFirstRun = nonDevEditionProfiles == 0;
+    }
   }
-#else
-  mChosen = mDefault;
-#endif
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsToolkitProfileService::SetStartWithLastProfile(bool aValue) {
   if (mStartWithLast != aValue) {
     mStartWithLast = aValue;
@@ -457,47 +693,105 @@ nsToolkitProfileService::ProfileEnumerat
 
   NS_ADDREF(*aResult = mCurrent);
 
   mCurrent = mCurrent->mNext;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsToolkitProfileService::GetSelectedProfile(nsIToolkitProfile** aResult) {
-  if (!mChosen && mFirst && !mFirst->mNext)  // only one profile
-    mChosen = mFirst;
-
-  if (!mChosen) return NS_ERROR_FAILURE;
-
-  NS_ADDREF(*aResult = mChosen);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsToolkitProfileService::SetSelectedProfile(nsIToolkitProfile* aProfile) {
-  if (mChosen != aProfile) {
-    mChosen = aProfile;
-  }
+nsToolkitProfileService::GetCurrentProfile(nsIToolkitProfile** aResult) {
+  NS_IF_ADDREF(*aResult = mCurrent);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsToolkitProfileService::GetDefaultProfile(nsIToolkitProfile** aResult) {
-  if (!mDefault) return NS_ERROR_FAILURE;
+  if (mUseDedicatedProfile) {
+    NS_IF_ADDREF(*aResult = mDedicatedProfile);
+    return NS_OK;
+  }
 
-  NS_ADDREF(*aResult = mDefault);
+  if (mUseDevEditionProfile) {
+    NS_IF_ADDREF(*aResult = mDevEditionDefault);
+    return NS_OK;
+  }
+
+  NS_IF_ADDREF(*aResult = mNormalDefault);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsToolkitProfileService::SetDefaultProfile(nsIToolkitProfile* aProfile) {
-  if (mDefault != aProfile) {
-    mDefault = aProfile;
+  if (mUseDedicatedProfile) {
+    if (mDedicatedProfile != aProfile) {
+      if (!aProfile) {
+        // Setting this to the empty string means no profile will be found on
+        // startup but we'll recognise that this install has been used
+        // previously.
+        mInstallData.SetString(mInstallHash.get(), "Default", "");
+      } else {
+        nsCString profilePath;
+        nsresult rv = GetProfileDescriptor(aProfile, profilePath, nullptr);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        mInstallData.SetString(mInstallHash.get(), "Default",
+                               profilePath.get());
+      }
+      mDedicatedProfile = aProfile;
+
+      // Some kind of choice has happened here, lock this profile to this
+      // install.
+      mInstallData.SetString(mInstallHash.get(), "Locked", "1");
+    }
+    return NS_OK;
+  }
+
+  if (mUseDevEditionProfile && aProfile != mDevEditionDefault) {
+    // The separate profile is hardcoded.
+    return NS_ERROR_FAILURE;
   }
+
+  mNormalDefault = aProfile;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsToolkitProfileService::GetCreatedAlternateProfile(bool* aResult) {
+  *aResult = mCreatedAlternateProfile;
+  return NS_OK;
+}
+
+// Gets the profile root directory descriptor for storing in profiles.ini or
+// installs.ini.
+nsresult nsToolkitProfileService::GetProfileDescriptor(
+    nsIToolkitProfile* aProfile, nsACString& aDescriptor, bool* aIsRelative) {
+  nsCOMPtr<nsIFile> profileDir;
+  nsresult rv = aProfile->GetRootDir(getter_AddRefs(profileDir));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // if the profile dir is relative to appdir...
+  bool isRelative;
+  rv = mAppData->Contains(profileDir, &isRelative);
+
+  nsCString profilePath;
+  if (NS_SUCCEEDED(rv) && isRelative) {
+    // we use a relative descriptor
+    rv = profileDir->GetRelativeDescriptor(mAppData, profilePath);
+  } else {
+    // otherwise, a persistent descriptor
+    rv = profileDir->GetPersistentDescriptor(profilePath);
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aDescriptor.Assign(profilePath);
+  if (aIsRelative) {
+    *aIsRelative = isRelative;
+  }
+
   return NS_OK;
 }
 
 /**
  * An implementation of SelectStartupProfile callable from JavaScript via XPCOM.
  * See nsIToolkitProfileService.idl.
  */
 NS_IMETHODIMP
@@ -516,16 +810,22 @@ nsToolkitProfileService::SelectStartupPr
     allocated[i].reset(ToNewCString(aArgv[i]));
     argv[i] = allocated[i].get();
   }
   argv[argc] = nullptr;
 
   nsresult rv = SelectStartupProfile(&argc, argv.get(), aIsResetting, aRootDir,
                                      aLocalDir, aProfile, aDidCreate);
 
+  // Since we were called outside of the normal startup path record the
+  // telemetry now.
+  if (NS_SUCCEEDED(rv)) {
+    RecordStartupTelemetry();
+  }
+
   return rv;
 }
 
 /**
  * Selects or creates a profile to use based on the profiles database, any
  * environment variables and any command line arguments. Will not create
  * a profile if aIsResetting is true. The profile is selected based on this
  * order of preference:
@@ -550,36 +850,44 @@ nsresult nsToolkitProfileService::Select
     return NS_ERROR_ALREADY_INITIALIZED;
   }
 
   mStartupProfileSelected = true;
   *aDidCreate = false;
 
   nsresult rv;
   const char* arg;
-  nsCOMPtr<nsIToolkitProfile> profile;
 
   // Use the profile specified in the environment variables (generally from an
   // app initiated restart).
   nsCOMPtr<nsIFile> lf = GetFileFromEnv("XRE_PROFILE_PATH");
   if (lf) {
     nsCOMPtr<nsIFile> localDir = GetFileFromEnv("XRE_PROFILE_LOCAL_PATH");
     if (!localDir) {
       localDir = lf;
     }
 
+    if (EnvHasValue("XRE_RESTARTED_BY_PROFILE_MANAGER")) {
+      mStartupReason = NS_LITERAL_STRING("profile-manager");
+    } else if (aIsResetting) {
+      mStartupReason = NS_LITERAL_STRING("profile-reset");
+    } else {
+      mStartupReason = NS_LITERAL_STRING("restart");
+    }
+
     // Clear out flags that we handled (or should have handled!) last startup.
     const char* dummy;
     CheckArg(*aArgc, aArgv, "p", &dummy);
     CheckArg(*aArgc, aArgv, "profile", &dummy);
     CheckArg(*aArgc, aArgv, "profilemanager");
 
-    GetProfileByDir(lf, localDir, aProfile);
+    GetProfileByDir(lf, localDir, getter_AddRefs(mCurrent));
     lf.forget(aRootDir);
     localDir.forget(aLocalDir);
+    NS_IF_ADDREF(*aProfile = mCurrent);
     return NS_OK;
   }
 
   // Check the -profile command line argument. It accepts a single argument that
   // gives the path to use for the profile.
   ArgResult ar = CheckArg(*aArgc, aArgv, "profile", &arg,
                           CheckArgFlag::CheckOSInt | CheckArgFlag::RemoveArg);
   if (ar == ARG_BAD) {
@@ -605,36 +913,40 @@ nsresult nsToolkitProfileService::Select
       if (!isDir) {
         PR_fprintf(
             PR_STDERR,
             "Error: argument --profile requires a path to a directory\n");
         return NS_ERROR_FAILURE;
       }
     }
 
+    mStartupReason = NS_LITERAL_STRING("argument-profile");
+
     // If a profile path is specified directly on the command line, then
     // assume that the temp directory is the same as the given directory.
-    GetProfileByDir(lf, lf, aProfile);
+    GetProfileByDir(lf, lf, getter_AddRefs(mCurrent));
     NS_ADDREF(*aRootDir = lf);
     lf.forget(aLocalDir);
+    NS_IF_ADDREF(*aProfile = mCurrent);
     return NS_OK;
   }
 
   // Check the -createprofile command line argument. It accepts a single
   // argument that is either the name for the new profile or the name followed
   // by the path to use.
   ar = CheckArg(*aArgc, aArgv, "createprofile", &arg,
                 CheckArgFlag::CheckOSInt | CheckArgFlag::RemoveArg);
   if (ar == ARG_BAD) {
     PR_fprintf(PR_STDERR,
                "Error: argument --createprofile requires a profile name\n");
     return NS_ERROR_FAILURE;
   }
   if (ar) {
     const char* delim = strchr(arg, ' ');
+    nsCOMPtr<nsIToolkitProfile> profile;
     if (delim) {
       nsCOMPtr<nsIFile> lf;
       rv = NS_NewNativeLocalFile(nsDependentCString(delim + 1), true,
                                  getter_AddRefs(lf));
       if (NS_FAILED(rv)) {
         PR_fprintf(PR_STDERR, "Error: profile path not valid.\n");
         return rv;
       }
@@ -676,21 +988,24 @@ nsresult nsToolkitProfileService::Select
     ar = CheckArg(*aArgc, aArgv, "osint");
     if (ar == ARG_FOUND) {
       PR_fprintf(
           PR_STDERR,
           "Error: argument -p is invalid when argument --osint is specified\n");
       return NS_ERROR_FAILURE;
     }
 
-    rv = GetProfileByName(nsDependentCString(arg), getter_AddRefs(profile));
+    rv = GetProfileByName(nsDependentCString(arg), getter_AddRefs(mCurrent));
     if (NS_SUCCEEDED(rv)) {
-      profile->GetRootDir(aRootDir);
-      profile->GetLocalDir(aLocalDir);
-      profile.forget(aProfile);
+      mStartupReason = NS_LITERAL_STRING("argument-p");
+
+      mCurrent->GetRootDir(aRootDir);
+      mCurrent->GetLocalDir(aLocalDir);
+
+      NS_ADDREF(*aProfile = mCurrent);
       return NS_OK;
     }
 
     return NS_ERROR_SHOW_PROFILE_MANAGER;
   }
 
   ar = CheckArg(*aArgc, aArgv, "profilemanager", (const char**)nullptr,
                 CheckArgFlag::CheckOSInt | CheckArgFlag::RemoveArg);
@@ -708,69 +1023,191 @@ nsresult nsToolkitProfileService::Select
   if (mIsFirstRun) {
     if (aIsResetting) {
       // We don't want to create a fresh profile when we're attempting a
       // profile reset so just bail out here, the calling code will handle it.
       *aProfile = nullptr;
       return NS_OK;
     }
 
-    // create a default profile
-    nsresult rv = CreateProfile(nullptr,  // choose a default dir for us
-#ifdef MOZ_DEV_EDITION
-                                NS_LITERAL_CSTRING(DEV_EDITION_NAME),
-#else
-                                NS_LITERAL_CSTRING(DEFAULT_NAME),
-#endif
-                                getter_AddRefs(mChosen));
+    if (mUseDedicatedProfile) {
+      // This is the first run of a dedicated profile install. We have to decide
+      // whether to use the default profile used by non-dedicated-profile
+      // installs or to create a new profile.
+
+      // Find what would have been the default profile for old installs.
+      nsCOMPtr<nsIToolkitProfile> profile = mNormalDefault;
+      if (mUseDevEditionProfile) {
+        profile = mDevEditionDefault;
+      }
+
+      if (profile && MaybeMakeDefaultDedicatedProfile(profile)) {
+        mStartupReason = NS_LITERAL_STRING("firstrun-claimed-default");
+
+        mCurrent = profile;
+        profile->GetRootDir(aRootDir);
+        profile->GetLocalDir(aLocalDir);
+        profile.forget(aProfile);
+        return NS_OK;
+      }
+
+      // We're going to create a new profile for this install. If there was a
+      // potential previous default to use then the user may be confused over
+      // why we're not using that anymore so set a flag for the front-end to use
+      // to notify the user about what has happened.
+      mCreatedAlternateProfile = !!profile;
+    }
+
+    // Create a new default profile
+    nsAutoCString name;
+    if (mUseDedicatedProfile) {
+      name.AssignLiteral("default-" NS_STRINGIFY(MOZ_UPDATE_CHANNEL));
+    } else if (mUseDevEditionProfile) {
+      name.AssignLiteral(DEV_EDITION_NAME);
+    } else {
+      name.AssignLiteral(DEFAULT_NAME);
+    }
+
+    rv = CreateUniqueProfile(nullptr, name, getter_AddRefs(mCurrent));
     if (NS_SUCCEEDED(rv)) {
-#ifdef MOZ_DEV_EDITION
-      // If the only profile is the new dev-edition-profile then older versions
-      // may try to auto-select it. Create a default profile for them to use
-      // instead.
-      if (mFirst && !mFirst->mNext) {
+      if (mUseDedicatedProfile) {
+        SetDefaultProfile(mCurrent);
+      } else if (mUseDevEditionProfile) {
+        mDevEditionDefault = mCurrent;
+      } else {
+        mNormalDefault = mCurrent;
+      }
+
+      // If there is only one profile and it isn't meant to be the profile that
+      // older versions of Firefox use then we must create a default profile
+      // for older versions of Firefox to avoid the existing profile being
+      // auto-selected.
+      if ((mUseDedicatedProfile || mUseDevEditionProfile) && mFirst &&
+          !mFirst->mNext) {
         CreateProfile(nullptr, NS_LITERAL_CSTRING(DEFAULT_NAME),
-                      getter_AddRefs(mDefault));
+                      getter_AddRefs(mNormalDefault));
       }
-#else
-      SetDefaultProfile(mChosen);
-#endif
+
       Flush();
 
-      mChosen->GetRootDir(aRootDir);
-      mChosen->GetLocalDir(aLocalDir);
-      NS_ADDREF(*aProfile = mChosen);
+      if (mCreatedAlternateProfile) {
+        mStartupReason = NS_LITERAL_STRING("firstrun-skipped-default");
+      } else {
+        mStartupReason = NS_LITERAL_STRING("firstrun-created-default");
+      }
+
+      // Use the new profile.
+      mCurrent->GetRootDir(aRootDir);
+      mCurrent->GetLocalDir(aLocalDir);
+      NS_ADDREF(*aProfile = mCurrent);
 
       *aDidCreate = true;
       return NS_OK;
     }
   }
 
-  // There are multiple profiles available.
+  // We've been told not to use the selected profile automatically.
   if (!mStartWithLast) {
     return NS_ERROR_SHOW_PROFILE_MANAGER;
   }
 
-  // GetSelectedProfile will auto-select the only profile if there's just one
-  GetSelectedProfile(getter_AddRefs(profile));
+  GetDefaultProfile(getter_AddRefs(mCurrent));
 
   // None of the profiles was marked as default (generally only happens if the
   // user modifies profiles.ini manually). Let the user choose.
-  if (!profile) {
+  if (!mCurrent) {
     return NS_ERROR_SHOW_PROFILE_MANAGER;
   }
 
+  mStartupReason = NS_LITERAL_STRING("default");
+
   // Use the selected profile.
-  profile->GetRootDir(aRootDir);
-  profile->GetLocalDir(aLocalDir);
-  profile.forget(aProfile);
+  mCurrent->GetRootDir(aRootDir);
+  mCurrent->GetLocalDir(aLocalDir);
+  NS_ADDREF(*aProfile = mCurrent);
+
+  return NS_OK;
+}
+
+/**
+ * Creates a new profile for reset and mark it as the current profile.
+ */
+nsresult nsToolkitProfileService::CreateResetProfile(
+    nsIToolkitProfile** aNewProfile) {
+  nsAutoCString oldProfileName;
+  mCurrent->GetName(oldProfileName);
+
+  nsCOMPtr<nsIToolkitProfile> newProfile;
+  // Make the new profile name the old profile (or "default-") + the time in
+  // seconds since epoch for uniqueness.
+  nsAutoCString newProfileName;
+  if (!oldProfileName.IsEmpty()) {
+    newProfileName.Assign(oldProfileName);
+    newProfileName.Append("-");
+  } else {
+    newProfileName.AssignLiteral("default-");
+  }
+  newProfileName.AppendPrintf("%" PRId64, PR_Now() / 1000);
+  nsresult rv = CreateProfile(nullptr,  // choose a default dir for us
+                              newProfileName, getter_AddRefs(newProfile));
+  if (NS_FAILED(rv)) return rv;
+
+  rv = Flush();
+  if (NS_FAILED(rv)) return rv;
+
+  mCurrent = newProfile;
+  newProfile.forget(aNewProfile);
 
   return NS_OK;
 }
 
+/**
+ * This is responsible for deleting the old profile, copying its name to the
+ * current profile and if the old profile was default making the new profile
+ * default as well.
+ */
+nsresult nsToolkitProfileService::ApplyResetProfile(
+    nsIToolkitProfile* aOldProfile) {
+  // If the old profile would have been the default for old installs then mark
+  // the new profile as such.
+  if (mNormalDefault == aOldProfile) {
+    mNormalDefault = mCurrent;
+  }
+
+  if (mUseDedicatedProfile && mDedicatedProfile == aOldProfile) {
+    bool wasLocked = false;
+    nsCString val;
+    if (NS_SUCCEEDED(
+            mInstallData.GetString(mInstallHash.get(), "Locked", val))) {
+      wasLocked = val.Equals("1");
+    }
+
+    SetDefaultProfile(mCurrent);
+
+    // Make the locked state match if necessary.
+    if (!wasLocked) {
+      mInstallData.DeleteString(mInstallHash.get(), "Locked");
+    }
+  }
+
+  nsCString name;
+  nsresult rv = aOldProfile->GetName(name);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aOldProfile->Remove(false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Switching the name will make this the default for dev-edition if
+  // appropriate.
+  rv = mCurrent->SetName(name);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return Flush();
+}
+
 NS_IMETHODIMP
 nsToolkitProfileService::GetProfileByName(const nsACString& aName,
                                           nsIToolkitProfile** aResult) {
   nsToolkitProfile* curP = mFirst;
   while (curP) {
     if (curP->mName.Equals(aName)) {
       NS_ADDREF(*aResult = curP);
       return NS_OK;
@@ -820,16 +1257,38 @@ static void SaltProfileName(nsACString& 
   char salt[9];
   NS_MakeRandomString(salt, 8);
   salt[8] = '.';
 
   aName.Insert(salt, 0, 9);
 }
 
 NS_IMETHODIMP
+nsToolkitProfileService::CreateUniqueProfile(nsIFile* aRootDir,
+                                             const nsACString& aNamePrefix,
+                                             nsIToolkitProfile** aResult) {
+  nsCOMPtr<nsIToolkitProfile> profile;
+  nsresult rv = GetProfileByName(aNamePrefix, getter_AddRefs(profile));
+  if (NS_FAILED(rv)) {
+    return CreateProfile(aRootDir, aNamePrefix, aResult);
+  }
+
+  uint32_t suffix = 1;
+  while (true) {
+    nsPrintfCString name("%s-%d", PromiseFlatCString(aNamePrefix).get(),
+                         suffix);
+    rv = GetProfileByName(name, getter_AddRefs(profile));
+    if (NS_FAILED(rv)) {
+      return CreateProfile(aRootDir, name, aResult);
+    }
+    suffix++;
+  }
+}
+
+NS_IMETHODIMP
 nsToolkitProfileService::CreateProfile(nsIFile* aRootDir,
                                        const nsACString& aName,
                                        nsIToolkitProfile** aResult) {
   nsresult rv = GetProfileByName(aName, aResult);
   if (NS_SUCCEEDED(rv)) {
     return rv;
   }
 
@@ -907,27 +1366,76 @@ nsToolkitProfileService::CreateProfile(n
   // We created a new profile dir. Let's store a creation timestamp.
   // Note that this code path does not apply if the profile dir was
   // created prior to launching.
   rv = CreateTimesInternal(rootDir);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsToolkitProfile* last = mFirst.get();
   if (last) {
-    while (last->mNext) last = last->mNext;
+    while (last->mNext) {
+      last = last->mNext;
+    }
   }
 
   nsCOMPtr<nsIToolkitProfile> profile =
       new nsToolkitProfile(aName, rootDir, localDir, last);
   if (!profile) return NS_ERROR_OUT_OF_MEMORY;
 
+  if (aName.Equals(DEV_EDITION_NAME)) {
+    mDevEditionDefault = profile;
+  }
+
   profile.forget(aResult);
   return NS_OK;
 }
 
+/**
+ * Snaps (https://snapcraft.io/) use a different installation directory for
+ * every version of an application. Since dedicated profiles uses the
+ * installation directory to determine which profile to use this would lead
+ * snap users getting a new profile on every application update.
+ *
+ * However the only way to have multiple installation of a snap is to install
+ * a new snap instance. Different snap instances have different user data
+ * directories and so already will not share profiles, in fact one instance
+ * will not even be able to see the other instance's profiles since
+ * profiles.ini will be stored in different places.
+ *
+ * So we can just disable dedicated profile support in this case and revert
+ * back to the old method of just having a single default profile and still
+ * get essentially the same benefits as dedicated profiles provides.
+ */
+bool nsToolkitProfileService::IsSnapEnvironment() {
+  return !!PR_GetEnv("SNAP_NAME");
+}
+
+struct FindInstallsClosure {
+  nsINIParser* installData;
+  nsTArray<nsCString>* installs;
+};
+
+static bool FindInstalls(const char* aSection, void* aClosure) {
+  FindInstallsClosure* closure = static_cast<FindInstallsClosure*>(aClosure);
+
+  nsCString install(aSection);
+  closure->installs->AppendElement(install);
+
+  return true;
+}
+
+nsTArray<nsCString> nsToolkitProfileService::GetKnownInstalls() {
+  nsTArray<nsCString> result;
+  FindInstallsClosure closure = {&mInstallData, &result};
+
+  mInstallData.GetSections(&FindInstalls, &closure);
+
+  return result;
+}
+
 nsresult nsToolkitProfileService::CreateTimesInternal(nsIFile* aProfileDir) {
   nsresult rv = NS_ERROR_FAILURE;
   nsCOMPtr<nsIFile> creationLog;
   rv = aProfileDir->Clone(getter_AddRefs(creationLog));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = creationLog->AppendNative(NS_LITERAL_CSTRING("times.json"));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -963,21 +1471,27 @@ nsToolkitProfileService::GetProfileCount
     profile = profile->mNext;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsToolkitProfileService::Flush() {
+  nsresult rv;
+
+  if (mUseDedicatedProfile) {
+    rv = mInstallData.WriteToFile(mInstallFile);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
   // Errors during writing might cause unhappy semi-written files.
   // To avoid this, write the entire thing to a buffer, then write
   // that buffer to disk.
 
-  nsresult rv;
   uint32_t pCount = 0;
   nsToolkitProfile* cur;
 
   for (cur = mFirst; cur != nullptr; cur = cur->mNext) ++pCount;
 
   uint32_t length;
   const int bufsize = 100 + MAXPATHLEN * pCount;
   auto buffer = MakeUnique<char[]>(bufsize);
@@ -990,39 +1504,29 @@ nsToolkitProfileService::Flush() {
                   "StartWithLastProfile=%s\n\n",
                   mStartWithLast ? "1" : "0");
 
   nsAutoCString path;
   cur = mFirst;
   pCount = 0;
 
   while (cur) {
-    // if the profile dir is relative to appdir...
     bool isRelative;
-    rv = mAppData->Contains(cur->mRootDir, &isRelative);
-    if (NS_SUCCEEDED(rv) && isRelative) {
-      // we use a relative descriptor
-      rv = cur->mRootDir->GetRelativeDescriptor(mAppData, path);
-    } else {
-      // otherwise, a persistent descriptor
-      rv = cur->mRootDir->GetPersistentDescriptor(path);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
+    nsresult rv = GetProfileDescriptor(cur, path, &isRelative);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     pos +=
         snprintf(pos, end - pos,
                  "[Profile%u]\n"
                  "Name=%s\n"
                  "IsRelative=%s\n"
                  "Path=%s\n",
                  pCount, cur->mName.get(), isRelative ? "1" : "0", path.get());
 
-    nsCOMPtr<nsIToolkitProfile> profile;
-    rv = this->GetDefaultProfile(getter_AddRefs(profile));
-    if (NS_SUCCEEDED(rv) && profile == cur) {
+    if (cur == mNormalDefault) {
       pos += snprintf(pos, end - pos, "Default=1\n");
     }
 
     pos += snprintf(pos, end - pos, "\n");
 
     cur = cur->mNext;
     ++pCount;
   }
--- a/toolkit/profile/nsToolkitProfileService.h
+++ b/toolkit/profile/nsToolkitProfileService.h
@@ -8,16 +8,17 @@
 #ifndef nsToolkitProfileService_h
 #define nsToolkitProfileService_h
 
 #include "nsIToolkitProfileService.h"
 #include "nsIToolkitProfile.h"
 #include "nsIFactory.h"
 #include "nsSimpleEnumerator.h"
 #include "nsProfileLock.h"
+#include "nsINIParser.h"
 
 class nsToolkitProfile final : public nsIToolkitProfile {
  public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSITOOLKITPROFILE
 
   friend class nsToolkitProfileService;
   RefPtr<nsToolkitProfile> mNext;
@@ -71,40 +72,82 @@ class nsToolkitProfileFactory final : pu
 class nsToolkitProfileService final : public nsIToolkitProfileService {
  public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSITOOLKITPROFILESERVICE
 
   nsresult SelectStartupProfile(int* aArgc, char* aArgv[], bool aIsResetting,
                                 nsIFile** aRootDir, nsIFile** aLocalDir,
                                 nsIToolkitProfile** aProfile, bool* aDidCreate);
+  nsresult CreateResetProfile(nsIToolkitProfile** aNewProfile);
+  nsresult ApplyResetProfile(nsIToolkitProfile* aOldProfile);
+  void RecordStartupTelemetry();
 
  private:
   friend class nsToolkitProfile;
   friend class nsToolkitProfileFactory;
   friend nsresult NS_NewToolkitProfileService(nsIToolkitProfileService**);
 
   nsToolkitProfileService();
   ~nsToolkitProfileService();
 
   nsresult Init();
 
   nsresult CreateTimesInternal(nsIFile* profileDir);
   void GetProfileByDir(nsIFile* aRootDir, nsIFile* aLocalDir,
                        nsIToolkitProfile** aResult);
 
+  nsresult GetProfileDescriptor(nsIToolkitProfile* aProfile,
+                                nsACString& aDescriptor, bool* aIsRelative);
+  bool IsProfileForCurrentInstall(nsIToolkitProfile* aProfile);
+  void ClearProfileFromOtherInstalls(nsIToolkitProfile* aProfile);
+  bool MaybeMakeDefaultDedicatedProfile(nsIToolkitProfile* aProfile);
+  bool IsSnapEnvironment();
+
+  // Returns the known install hashes from the installs database. Modifying the
+  // installs database is safe while iterating the returned array.
+  nsTArray<nsCString> GetKnownInstalls();
+
+  // Tracks whether SelectStartupProfile has been called.
   bool mStartupProfileSelected;
+  // The first profile in a linked list of profiles loaded from profiles.ini.
   RefPtr<nsToolkitProfile> mFirst;
-  nsCOMPtr<nsIToolkitProfile> mChosen;
-  nsCOMPtr<nsIToolkitProfile> mDefault;
+  // The profile selected for use at startup, if it exists in profiles.ini.
+  nsCOMPtr<nsIToolkitProfile> mCurrent;
+  // The profile selected for this install in installs.ini.
+  nsCOMPtr<nsIToolkitProfile> mDedicatedProfile;
+  // The default profile used by non-dev-edition builds.
+  nsCOMPtr<nsIToolkitProfile> mNormalDefault;
+  // The profile used if mUseDevEditionProfile is true (the default on
+  // dev-edition builds).
+  nsCOMPtr<nsIToolkitProfile> mDevEditionDefault;
+  // The directory that holds profiles.ini and profile directories.
   nsCOMPtr<nsIFile> mAppData;
+  // The directory that holds the cache files for profiles.
   nsCOMPtr<nsIFile> mTempData;
+  // The location of profiles.ini.
   nsCOMPtr<nsIFile> mListFile;
+  // The location of installs.ini.
+  nsCOMPtr<nsIFile> mInstallFile;
+  // The data loaded from installs.ini.
+  nsINIParser mInstallData;
+  // The install hash for the currently running install.
+  nsCString mInstallHash;
+  // Whether to start with the selected profile by default.
   bool mStartWithLast;
+  // True if during startup it appeared that this is the first run.
   bool mIsFirstRun;
+  // True if the default profile is the separate dev-edition-profile.
+  bool mUseDevEditionProfile;
+  // True if this install should use a dedicated default profile.
+  const bool mUseDedicatedProfile;
+  // True if during startup no dedicated profile was already selected, an old
+  // default profile existed but was rejected so a new profile was created.
+  bool mCreatedAlternateProfile;
+  nsString mStartupReason;
 
   static nsToolkitProfileService* gService;
 
   class ProfileEnumerator final : public nsSimpleEnumerator {
    public:
     NS_DECL_NSISIMPLEENUMERATOR
 
     const nsID& DefaultInterface() override {
--- a/toolkit/profile/xpcshell/head.js
+++ b/toolkit/profile/xpcshell/head.js
@@ -2,94 +2,165 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
 const { FileUtils } = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
 const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
 const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+const { TelemetryTestUtils } = ChromeUtils.import("resource://testing-common/TelemetryTestUtils.jsm");
 
 const NS_ERROR_START_PROFILE_MANAGER = 0x805800c9;
 
 let gProfD = do_get_profile();
 let gDataHome = gProfD.clone();
 gDataHome.append("data");
 gDataHome.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
 let gDataHomeLocal = gProfD.clone();
 gDataHomeLocal.append("local");
 gDataHomeLocal.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
 
 let xreDirProvider = Cc["@mozilla.org/xre/directory-provider;1"].
                      getService(Ci.nsIXREDirProvider);
 xreDirProvider.setUserDataDirectory(gDataHome, false);
 xreDirProvider.setUserDataDirectory(gDataHomeLocal, true);
 
+let gIsDefaultApp = false;
+
+const ShellService = {
+  register() {
+    let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+
+    let factory = {
+      createInstance(outer, iid) {
+        if (outer != null) {
+          throw Cr.NS_ERROR_NO_AGGREGATION;
+        }
+
+        return ShellService.QueryInterface(iid);
+      },
+    };
+
+    registrar.registerFactory(this.ID, "ToolkitShellService", this.CONTRACT, factory);
+  },
+
+  isDefaultApplication() {
+    return gIsDefaultApp;
+  },
+
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIToolkitShellService]),
+  ID: Components.ID("{ce724e0c-ed70-41c9-ab31-1033b0b591be}"),
+  CONTRACT: "@mozilla.org/toolkit/shell-service;1",
+};
+
+ShellService.register();
+
+let gIsSnap = false;
+
+function simulateSnapEnvironment() {
+  let env = Cc["@mozilla.org/process/environment;1"].
+          getService(Ci.nsIEnvironment);
+  env.set("SNAP_NAME", "foo");
+
+  gIsSnap = true;
+}
+
 function getProfileService() {
   return Cc["@mozilla.org/toolkit/profile-service;1"].
          getService(Ci.nsIToolkitProfileService);
 }
 
 let PROFILE_DEFAULT = "default";
 if (AppConstants.MOZ_DEV_EDITION) {
   PROFILE_DEFAULT = "dev-edition-default";
 }
 
+let DEDICATED_NAME = `default-${AppConstants.MOZ_UPDATE_CHANNEL}`;
+
 /**
  * Creates a random profile path for use.
  */
 function makeRandomProfileDir(name) {
   let file = gDataHome.clone();
   file.append(name);
   file.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
   return file;
 }
 
 /**
  * A wrapper around nsIToolkitProfileService.selectStartupProfile to make it
  * a bit nicer to use from JS.
  */
 function selectStartupProfile(args = [], isResetting = false) {
+  let service = getProfileService();
   let rootDir = {};
   let localDir = {};
   let profile = {};
-  let didCreate = getProfileService().selectStartupProfile(["xpcshell", ...args], isResetting,
-                                                           rootDir, localDir, profile);
+  let didCreate = service.selectStartupProfile(["xpcshell", ...args], isResetting,
+                                               rootDir, localDir, profile);
 
   if (profile.value) {
     Assert.ok(rootDir.value.equals(profile.value.rootDir), "Should have matched the root dir.");
     Assert.ok(localDir.value.equals(profile.value.localDir), "Should have matched the local dir.");
+    Assert.equal(service.currentProfile, profile.value, "Should have marked the profile as the current profile.");
+  } else {
+    Assert.ok(!service.currentProfile, "Should be no current profile.");
   }
 
   return {
     rootDir: rootDir.value,
     localDir: localDir.value,
     profile: profile.value,
     didCreate,
   };
 }
 
 function testStartsProfileManager(args = [], isResetting = false) {
   try {
     selectStartupProfile(args, isResetting);
     Assert.ok(false, "Should have started the profile manager");
+    checkStartupReason();
   } catch (e) {
     Assert.equal(e.result, NS_ERROR_START_PROFILE_MANAGER, "Should have started the profile manager");
   }
 }
 
 function safeGet(ini, section, key) {
   try {
     return ini.getString(section, key);
   } catch (e) {
     return null;
   }
 }
 
 /**
+ * Writes a compatibility.ini file that marks the give profile directory as last
+ * used by the given install path.
+ */
+function writeCompatibilityIni(dir, appDir = FileUtils.getDir("CurProcD", []),
+                                    greDir = FileUtils.getDir("GreD", [])) {
+  let target = dir.clone();
+  target.append("compatibility.ini");
+
+  let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
+                getService(Ci.nsIINIParserFactory);
+  let ini = factory.createINIParser().QueryInterface(Ci.nsIINIParserWriter);
+
+  // The profile service doesn't care about these so just use fixed values
+  ini.setString("Compatibility", "LastVersion", "64.0a1_20180919123806/20180919123806");
+  ini.setString("Compatibility", "LastOSABI", "Darwin_x86_64-gcc3");
+
+  ini.setString("Compatibility", "LastPlatformDir", greDir.persistentDescriptor);
+  ini.setString("Compatibility", "LastAppDir", appDir.persistentDescriptor);
+
+  ini.writeFile(target);
+}
+
+/**
  * Writes a profiles.ini based on the passed profile data.
  * profileData should contain two properties, options and profiles.
  * options contains a single property, startWithLastProfile.
  * profiles is an array of profiles each containing name, path and default
  * properties.
  */
 function writeProfilesIni(profileData) {
   let target = gDataHome.clone();
@@ -126,17 +197,19 @@ function writeProfilesIni(profileData) {
  * because the order is irrelevant and it makes testing easier if we can make
  * that assumption.
  */
 function readProfilesIni() {
   let target = gDataHome.clone();
   target.append("profiles.ini");
 
   let profileData = {
-    options: {},
+    options: {
+      startWithLastProfile: true,
+    },
     profiles: [],
   };
 
   if (!target.exists()) {
     return profileData;
   }
 
   let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
@@ -170,52 +243,139 @@ function readProfilesIni() {
   }
 
   profileData.profiles.sort((a, b) => a.name.localeCompare(b.name));
 
   return profileData;
 }
 
 /**
+ * Writes an installs.ini based on the supplied data. Should be an object with
+ * keys for every installation hash each mapping to an object. Each object
+ * should have a default property for the relative path to the profile.
+ */
+function writeInstallsIni(installData) {
+  let target = gDataHome.clone();
+  target.append("installs.ini");
+
+  const { installs = {} } = installData;
+
+  let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
+                getService(Ci.nsIINIParserFactory);
+  let ini = factory.createINIParser(null).QueryInterface(Ci.nsIINIParserWriter);
+
+  for (let hash of Object.keys(installs)) {
+    ini.setString(hash, "Default", installs[hash].default);
+    if ("locked" in installs[hash]) {
+      ini.setString(hash, "Locked", installs[hash].locked ? "1" : "0");
+    }
+  }
+
+  ini.writeFile(target);
+}
+
+/**
+ * Reads installs.ini into a structure like that used in the above function.
+ */
+function readInstallsIni() {
+  let target = gDataHome.clone();
+  target.append("installs.ini");
+
+  let installData = {
+    installs: {},
+  };
+
+  if (!target.exists()) {
+    return installData;
+  }
+
+  let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
+                getService(Ci.nsIINIParserFactory);
+  let ini = factory.createINIParser(target);
+
+  let sections = ini.getSections();
+  while (sections.hasMore()) {
+    let hash = sections.getNext();
+    if (hash != "General") {
+      installData.installs[hash] = {
+        default: safeGet(ini, hash, "Default"),
+        locked: safeGet(ini, hash, "Locked") == 1,
+      };
+    }
+  }
+
+  return installData;
+}
+
+/**
  * Checks that the profile service seems to have the right data in it compared
  * to profile and install data structured as in the above functions.
  */
-function checkProfileService(profileData = readProfilesIni()) {
+function checkProfileService(profileData = readProfilesIni(), installData = readInstallsIni()) {
   let service = getProfileService();
 
   let serviceProfiles = Array.from(service.profiles);
 
   Assert.equal(serviceProfiles.length, profileData.profiles.length, "Should be the same number of profiles.");
 
   // Sort to make matching easy.
   serviceProfiles.sort((a, b) => a.name.localeCompare(b.name));
   profileData.profiles.sort((a, b) => a.name.localeCompare(b.name));
 
-  let defaultProfile = null;
+  let hash = xreDirProvider.getInstallHash();
+  let defaultPath = hash in installData.installs ? installData.installs[hash].default : null;
+  let dedicatedProfile = null;
+  let snapProfile = null;
 
   for (let i = 0; i < serviceProfiles.length; i++) {
     let serviceProfile = serviceProfiles[i];
     let expectedProfile = profileData.profiles[i];
 
     Assert.equal(serviceProfile.name, expectedProfile.name, "Should have the same name.");
 
     let expectedPath = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
     expectedPath.setRelativeDescriptor(gDataHome, expectedProfile.path);
     Assert.equal(serviceProfile.rootDir.path, expectedPath.path, "Should have the same path.");
 
+    if (expectedProfile.path == defaultPath) {
+      dedicatedProfile = serviceProfile;
+    }
+
     if (AppConstants.MOZ_DEV_EDITION) {
       if (expectedProfile.name == PROFILE_DEFAULT) {
-        defaultProfile = serviceProfile;
+        snapProfile = serviceProfile;
       }
     } else if (expectedProfile.default) {
-      defaultProfile = serviceProfile;
+      snapProfile = serviceProfile;
     }
   }
 
-  let selectedProfile = null;
-  try {
-    selectedProfile = service.selectedProfile;
-  } catch (e) {
-    // GetSelectedProfile throws when there are no profiles.
+  if (gIsSnap) {
+    Assert.equal(service.defaultProfile, snapProfile, "Should have seen the right profile selected.");
+  } else {
+    Assert.equal(service.defaultProfile, dedicatedProfile, "Should have seen the right profile selected.");
+  }
+}
+
+/**
+ * Asynchronously reads an nsIFile from disk.
+ */
+async function readFile(file) {
+  let decoder = new TextDecoder();
+  let data = await OS.File.read(file.path);
+  return decoder.decode(data);
+}
+
+function checkStartupReason(expected = undefined) {
+  const tId = "startup.profile_selection_reason";
+  let scalars = TelemetryTestUtils.getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT);
+
+  if (expected === undefined) {
+    Assert.ok(!(tId in scalars), "Startup telemetry should not have been recorded.");
+    return;
   }
 
-  Assert.equal(selectedProfile, defaultProfile, "Should have seen the right profile selected.");
+  if (tId in scalars) {
+    Assert.equal(scalars[tId], expected, "Should have seen the right startup reason.");
+  } else {
+    Assert.ok(false, "Startup telemetry should have been recorded.");
+  }
 }
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/xpcshell/test_claim_locked.js
@@ -0,0 +1,46 @@
+/*
+ * Tests that an old-style default profile already locked to a different install
+ * isn't claimed by this install.
+ */
+
+add_task(async () => {
+  let defaultProfile = makeRandomProfileDir("default");
+
+  writeCompatibilityIni(defaultProfile);
+
+  writeProfilesIni({
+    profiles: [{
+      name: PROFILE_DEFAULT,
+      path: defaultProfile.leafName,
+      default: true,
+    }],
+  });
+
+  let hash = xreDirProvider.getInstallHash();
+  writeProfilesIni({
+    installs: {
+      other: {
+        default: defaultProfile.leafName,
+        locked: true,
+      },
+    },
+  });
+
+  let { profile: selectedProfile, didCreate } = selectStartupProfile();
+
+  let profileData = readProfilesIni();
+  let installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 2, "Should have the right number of profiles.");
+
+  Assert.equal(Object.keys(installData.installs).length, 1, "Should be two known installs.");
+  Assert.notEqual(installData.installs[hash].default, defaultProfile.leafName, "Should not have marked the original default profile as the default for this install.");
+  Assert.ok(installData.installs[hash].locked, "Should have locked as we created this profile for this install.");
+
+  checkProfileService(profileData, installData);
+
+  Assert.ok(didCreate, "Should have created a new profile.");
+  Assert.ok(!selectedProfile.rootDir.equals(defaultProfile), "Should be using a different directory.");
+  Assert.equal(selectedProfile.name, DEDICATED_NAME);
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/xpcshell/test_clean.js
@@ -0,0 +1,110 @@
+/*
+ * Tests from a clean state.
+ * Then does some testing that creating new profiles and marking them as
+ * selected works.
+ */
+
+add_task(async () => {
+  let service = getProfileService();
+
+  let target = gDataHome.clone();
+  target.append("profiles.ini");
+  Assert.ok(!target.exists(), "profiles.ini should not exist yet.");
+  target.leafName = "installs.ini";
+  Assert.ok(!target.exists(), "installs.ini should not exist yet.");
+
+  // Create a new profile to use.
+  let newProfile = service.createProfile(null, "dedicated");
+  service.flush();
+
+  let profileData = readProfilesIni();
+  let installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
+
+  let profile = profileData.profiles[0];
+  Assert.equal(profile.name, "dedicated", "Should have the right name.");
+  Assert.ok(!profile.default, "Should not be marked as the old-style default.");
+
+  // The new profile hasn't been marked as the default yet!
+  Assert.equal(Object.keys(installData.installs).length, 0, "Should be no defaults for installs yet.");
+
+  checkProfileService(profileData, installData);
+
+  service.defaultProfile = newProfile;
+  service.flush();
+
+  profileData = readProfilesIni();
+  installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
+
+  profile = profileData.profiles[0];
+  Assert.equal(profile.name, "dedicated", "Should have the right name.");
+  Assert.ok(!profile.default, "Should not be marked as the old-style default.");
+
+  let hash = xreDirProvider.getInstallHash();
+  Assert.equal(Object.keys(installData.installs).length, 1, "Should be only one known install.");
+  Assert.equal(installData.installs[hash].default, profileData.profiles[0].path, "Should have marked the new profile as the default for this install.");
+
+  checkProfileService(profileData, installData);
+
+  let otherProfile = service.createProfile(null, "another");
+  service.defaultProfile = otherProfile;
+
+  service.flush();
+
+  profileData = readProfilesIni();
+  installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 2, "Should have the right number of profiles.");
+
+  profile = profileData.profiles[0];
+  Assert.equal(profile.name, "another", "Should have the right name.");
+  Assert.ok(!profile.default, "Should not be marked as the old-style default.");
+
+  profile = profileData.profiles[1];
+  Assert.equal(profile.name, "dedicated", "Should have the right name.");
+  Assert.ok(!profile.default, "Should not be marked as the old-style default.");
+
+  Assert.equal(Object.keys(installData.installs).length, 1, "Should be only one known install.");
+  Assert.equal(installData.installs[hash].default, profileData.profiles[0].path, "Should have marked the new profile as the default for this install.");
+
+  checkProfileService(profileData, installData);
+
+  newProfile.remove(true);
+  service.flush();
+
+  profileData = readProfilesIni();
+  installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
+
+  profile = profileData.profiles[0];
+  Assert.equal(profile.name, "another", "Should have the right name.");
+  Assert.ok(!profile.default, "Should not be marked as the old-style default.");
+
+  Assert.equal(Object.keys(installData.installs).length, 1, "Should be only one known install.");
+  Assert.equal(installData.installs[hash].default, profileData.profiles[0].path, "Should have marked the new profile as the default for this install.");
+
+  checkProfileService(profileData, installData);
+
+  otherProfile.remove(true);
+  service.flush();
+
+  profileData = readProfilesIni();
+  installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 0, "Should have the right number of profiles.");
+
+  // We leave a reference to the missing profile to stop us trying to steal the
+  // old-style default profile on next startup.
+  Assert.equal(Object.keys(installData.installs).length, 1, "Should be only one known install.");
+
+  checkProfileService(profileData, installData);
+});
--- a/toolkit/profile/xpcshell/test_create_default.js
+++ b/toolkit/profile/xpcshell/test_create_default.js
@@ -1,19 +1,33 @@
 /*
  * Tests that from an empty database a default profile is created.
  */
 
 add_task(async () => {
   let service = getProfileService();
+  let { profile, didCreate } = selectStartupProfile();
 
-  let { profile, didCreate } = selectStartupProfile();
-  checkProfileService();
+  checkStartupReason("firstrun-created-default");
+
+  let profileData = readProfilesIni();
+  let installData = readInstallsIni();
+  checkProfileService(profileData, installData);
 
   Assert.ok(didCreate, "Should have created a new profile.");
-  if (AppConstants.MOZ_DEV_EDITION) {
-    Assert.equal(service.profileCount, 2, "Should be two profiles.");
-  } else {
-    Assert.equal(service.profileCount, 1, "Should be only one profile.");
-    Assert.equal(profile, service.selectedProfile, "Should now be the selected profile.");
-  }
-  Assert.equal(profile.name, PROFILE_DEFAULT, "Should have created a new profile with the right name.");
+  Assert.equal(profile, service.defaultProfile, "Should now be the default profile.");
+  Assert.equal(profile.name, DEDICATED_NAME, "Should have created a new profile with the right name.");
+  Assert.ok(!service.createdAlternateProfile, "Should not have created an alternate profile.");
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 2, "Should have the right number of profiles.");
+
+  profile = profileData.profiles[0];
+  Assert.equal(profile.name, "default", "Should have the right name.");
+  Assert.ok(profile.default, "Should be marked as the old-style default.");
+
+  profile = profileData.profiles[1];
+  Assert.equal(profile.name, DEDICATED_NAME, "Should have the right name.");
+  Assert.ok(!profile.default, "Should not be marked as the old-style default.");
+
+  let hash = xreDirProvider.getInstallHash();
+  Assert.ok(installData.installs[hash].locked, "Should have locked the profile");
 });
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/xpcshell/test_lock.js
@@ -0,0 +1,43 @@
+/*
+ * Tests that when the default application claims the old-style default profile
+ * it locks it to itself.
+ */
+
+add_task(async () => {
+  gIsDefaultApp = true;
+  let defaultProfile = makeRandomProfileDir("default");
+
+  writeCompatibilityIni(defaultProfile);
+
+  writeProfilesIni({
+    profiles: [{
+      name: "default",
+      path: defaultProfile.leafName,
+      default: true,
+    }],
+  });
+
+  let { profile: selectedProfile, didCreate } = selectStartupProfile();
+
+  let hash = xreDirProvider.getInstallHash();
+  let profileData = readProfilesIni();
+  let installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
+
+  let profile = profileData.profiles[0];
+  Assert.equal(profile.name, "default", "Should have the right name.");
+  Assert.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
+  Assert.ok(profile.default, "Should be marked as the old-style default.");
+
+  Assert.equal(Object.keys(installData.installs).length, 1, "Should be only one known install.");
+  Assert.equal(installData.installs[hash].default, defaultProfile.leafName, "Should have marked the original default profile as the default for this install.");
+  Assert.ok(installData.installs[hash].locked, "Should have locked as we're the default app.");
+
+  checkProfileService(profileData, installData);
+
+  Assert.ok(!didCreate, "Should not have created a new profile.");
+  Assert.ok(selectedProfile.rootDir.equals(defaultProfile), "Should be using the right directory.");
+  Assert.equal(selectedProfile.name, "default");
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/xpcshell/test_new_default.js
@@ -0,0 +1,74 @@
+/*
+ * Tests that an old-style default profile previously used by this build gets
+ * updated to a dedicated profile for this build.
+ */
+
+add_task(async () => {
+  let mydefaultProfile = makeRandomProfileDir("mydefault");
+  let defaultProfile = makeRandomProfileDir("default");
+  let devDefaultProfile = makeRandomProfileDir("devedition");
+
+  writeCompatibilityIni(mydefaultProfile);
+  writeCompatibilityIni(devDefaultProfile);
+
+  writeProfilesIni({
+    profiles: [{
+      name: "mydefault",
+      path: mydefaultProfile.leafName,
+      default: true,
+    }, {
+      name: "default",
+      path: defaultProfile.leafName,
+    }, {
+      name: "dev-edition-default",
+      path: devDefaultProfile.leafName,
+    }],
+  });
+
+  let service = getProfileService();
+  let { profile: selectedProfile, didCreate } = selectStartupProfile();
+  checkStartupReason("firstrun-claimed-default");
+
+  let hash = xreDirProvider.getInstallHash();
+  let profileData = readProfilesIni();
+  let installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 3, "Should have the right number of profiles.");
+
+  let profile = profileData.profiles[0];
+  Assert.equal(profile.name, "default", "Should have the right name.");
+  Assert.equal(profile.path, defaultProfile.leafName, "Should be the original non-default profile.");
+  Assert.ok(!profile.default, "Should not be marked as the old-style default.");
+
+  profile = profileData.profiles[1];
+  Assert.equal(profile.name, "dev-edition-default", "Should have the right name.");
+  Assert.equal(profile.path, devDefaultProfile.leafName, "Should be the original dev default profile.");
+  Assert.ok(!profile.default, "Should not be marked as the old-style default.");
+
+  profile = profileData.profiles[2];
+  Assert.equal(profile.name, "mydefault", "Should have the right name.");
+  Assert.equal(profile.path, mydefaultProfile.leafName, "Should be the original default profile.");
+  Assert.ok(profile.default, "Should be marked as the old-style default.");
+
+  Assert.equal(Object.keys(installData.installs).length, 1, "Should be only one known install.");
+  if (AppConstants.MOZ_DEV_EDITION) {
+    Assert.equal(installData.installs[hash].default, devDefaultProfile.leafName, "Should have marked the original dev default profile as the default for this install.");
+  } else {
+    Assert.equal(installData.installs[hash].default, mydefaultProfile.leafName, "Should have marked the original default profile as the default for this install.");
+  }
+
+  Assert.ok(!installData.installs[hash].locked, "Should not be locked as we're not the default app.");
+
+  checkProfileService(profileData, installData);
+
+  Assert.ok(!didCreate, "Should not have created a new profile.");
+  if (AppConstants.MOZ_DEV_EDITION) {
+    Assert.ok(selectedProfile.rootDir.equals(devDefaultProfile), "Should be using the right directory.");
+    Assert.equal(selectedProfile.name, "dev-edition-default");
+  } else {
+    Assert.ok(selectedProfile.rootDir.equals(mydefaultProfile), "Should be using the right directory.");
+    Assert.equal(selectedProfile.name, "mydefault");
+  }
+  Assert.ok(!service.createdAlternateProfile, "Should not have created an alternate profile.");
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/xpcshell/test_previous_dedicated.js
@@ -0,0 +1,48 @@
+/**
+ * If install.ini lists a default profile for this build but that profile no
+ * longer exists don't try to steal the old-style default even if it was used
+ * by this build. It means this install has previously used dedicated profiles.
+ */
+
+add_task(async () => {
+  let hash = xreDirProvider.getInstallHash();
+  let defaultProfile = makeRandomProfileDir("default");
+
+  writeCompatibilityIni(defaultProfile);
+
+  writeProfilesIni({
+    profiles: [{
+      name: "default",
+      path: defaultProfile.leafName,
+      default: true,
+    }],
+  });
+
+  writeInstallsIni({
+    installs: {
+      [hash]: {
+        default: "foobar",
+      },
+    },
+  });
+
+  let service = getProfileService();
+  testStartsProfileManager();
+
+  let profileData = readProfilesIni();
+  let installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
+  Assert.ok(!service.createdAlternateProfile, "Should not have created an alternate profile.");
+
+  let profile = profileData.profiles[0];
+  Assert.equal(profile.name, "default", "Should have the right name.");
+  Assert.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
+  Assert.ok(profile.default, "Should be marked as the old-style default.");
+
+  // We keep the data here so we don't steal on the next reboot...
+  Assert.equal(Object.keys(installData.installs).length, 1, "Still list the broken reference.");
+
+  checkProfileService(profileData, installData);
+});
--- a/toolkit/profile/xpcshell/test_profile_reset.js
+++ b/toolkit/profile/xpcshell/test_profile_reset.js
@@ -1,14 +1,17 @@
 /*
  * Tests that from an empty database profile reset doesn't create a new profile.
  */
 
 add_task(async () => {
   let service = getProfileService();
 
   let { profile, didCreate } = selectStartupProfile([], true);
+  // Profile reset will normally end up in a restart.
+  checkStartupReason("unknown");
   checkProfileService();
 
   Assert.ok(!didCreate, "Should not have created a new profile.");
   Assert.ok(!profile, "Should not be a returned profile.");
   Assert.equal(service.profileCount, 0, "Still should be no profiles.");
+  Assert.ok(!service.createdAlternateProfile, "Should not have created an alternate profile.");
 });
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/xpcshell/test_remove_default.js
@@ -0,0 +1,61 @@
+/**
+ * Tests that calling nsIToolkitProfile.remove on the default profile correctly
+ * removes the profile.
+ */
+
+add_task(async () => {
+  let hash = xreDirProvider.getInstallHash();
+  let defaultProfile = makeRandomProfileDir("default");
+
+  let profilesIni = {
+    profiles: [{
+      name: "default",
+      path: defaultProfile.leafName,
+      default: true,
+    }],
+  };
+  writeProfilesIni(profilesIni);
+
+  let installsIni = {
+    installs: {
+      [hash]: {
+        default: defaultProfile.leafName,
+      },
+    },
+  };
+  writeInstallsIni(installsIni);
+
+  let service = getProfileService();
+  checkProfileService(profilesIni, installsIni);
+
+  let { profile, didCreate } = selectStartupProfile();
+  Assert.ok(!didCreate, "Should have not created a new profile.");
+  Assert.equal(profile.name, "default", "Should have selected the default profile.");
+  Assert.equal(profile, service.defaultProfile, "Should have selected the default profile.");
+
+  checkProfileService(profilesIni, installsIni);
+
+  // In an actual run of Firefox we wouldn't be able to delete the profile in
+  // use because it would be locked. But we don't actually lock the profile in
+  // tests.
+  profile.remove(false);
+
+  Assert.ok(!service.defaultProfile, "Should no longer be a default profile.");
+  Assert.equal(profile, service.currentProfile, "Should still be the profile in use.");
+
+  // These are the modifications that should have been made.
+  profilesIni.profiles.pop();
+  installsIni.installs[hash].default = "";
+
+  checkProfileService(profilesIni, installsIni);
+
+  service.flush();
+
+  // And that should have flushed to disk correctly.
+  checkProfileService();
+
+  // checkProfileService doesn't differentiate between a blank default profile
+  // for the install and a missing install.
+  let installs = readInstallsIni();
+  Assert.equal(installs.installs[hash].default, "", "Should be a blank default profile.");
+});
--- a/toolkit/profile/xpcshell/test_select_default.js
+++ b/toolkit/profile/xpcshell/test_select_default.js
@@ -1,25 +1,34 @@
 /*
  * Tests that from a database of profiles the default profile is selected.
  */
 
 add_task(async () => {
+  let hash = xreDirProvider.getInstallHash();
+
   let profileData = {
     options: {
       startWithLastProfile: true,
     },
     profiles: [{
       name: "Profile1",
       path: "Path1",
     }, {
       name: "Profile3",
       path: "Path3",
     }],
   };
+  let installData = {
+    installs: {
+      [hash]: {
+        default: "Path2",
+      },
+    },
+  };
 
   if (AppConstants.MOZ_DEV_EDITION) {
     profileData.profiles.push({
       name: "default",
       path: "Path2",
       default: true,
     }, {
       name: PROFILE_DEFAULT,
@@ -29,18 +38,21 @@ add_task(async () => {
     profileData.profiles.push({
       name: PROFILE_DEFAULT,
       path: "Path2",
       default: true,
     });
   }
 
   writeProfilesIni(profileData);
+  writeInstallsIni(installData);
+
+  let { profile, didCreate } = selectStartupProfile();
+  checkStartupReason("default");
 
   let service = getProfileService();
   checkProfileService(profileData);
 
-  let { profile, didCreate } = selectStartupProfile();
-
   Assert.ok(!didCreate, "Should not have created a new profile.");
-  Assert.equal(profile, service.selectedProfile, "Should have returned the selected profile.");
-  Assert.equal(profile.name, PROFILE_DEFAULT, "Should have selected the right profile");
+  Assert.equal(profile, service.defaultProfile, "Should have returned the default profile.");
+  Assert.equal(profile.name, "default", "Should have selected the right profile");
+  Assert.ok(!service.createdAlternateProfile, "Should not have created an alternate profile.");
 });
--- a/toolkit/profile/xpcshell/test_select_environment.js
+++ b/toolkit/profile/xpcshell/test_select_environment.js
@@ -26,14 +26,15 @@ add_task(async () => {
   checkProfileService(profileData);
 
   let env = Cc["@mozilla.org/process/environment;1"].
             getService(Ci.nsIEnvironment);
   env.set("XRE_PROFILE_PATH", dir.path);
   env.set("XRE_PROFILE_LOCAL_PATH", dir.path);
 
   let { rootDir, localDir, profile, didCreate } = selectStartupProfile();
+  checkStartupReason("restart");
 
   Assert.ok(!didCreate, "Should not have created a new profile.");
   Assert.ok(rootDir.equals(dir), "Should have selected the right root dir.");
   Assert.ok(localDir.equals(dir), "Should have selected the right local dir.");
   Assert.ok(!profile, "No named profile matches this.");
 });
--- a/toolkit/profile/xpcshell/test_select_environment_named.js
+++ b/toolkit/profile/xpcshell/test_select_environment_named.js
@@ -28,15 +28,16 @@ add_task(async () => {
   checkProfileService(profileData);
 
   let env = Cc["@mozilla.org/process/environment;1"].
             getService(Ci.nsIEnvironment);
   env.set("XRE_PROFILE_PATH", root.path);
   env.set("XRE_PROFILE_LOCAL_PATH", local.path);
 
   let { rootDir, localDir, profile, didCreate } = selectStartupProfile(["-P", "Profile3"]);
+  checkStartupReason("restart");
 
   Assert.ok(!didCreate, "Should not have created a new profile.");
   Assert.ok(rootDir.equals(root), "Should have selected the right root dir.");
   Assert.ok(localDir.equals(local), "Should have selected the right local dir.");
   Assert.ok(profile, "A named profile matches this.");
   Assert.equal(profile.name, "Profile1", "The right profile was matched.");
 });
--- a/toolkit/profile/xpcshell/test_select_named.js
+++ b/toolkit/profile/xpcshell/test_select_named.js
@@ -20,12 +20,13 @@ add_task(async () => {
     }],
   };
 
   writeProfilesIni(profileData);
 
   checkProfileService(profileData);
 
   let { profile, didCreate } = selectStartupProfile(["-P", "Profile1"]);
+  checkStartupReason("argument-p");
 
   Assert.ok(!didCreate, "Should not have created a new profile.");
   Assert.equal(profile.name, "Profile1", "Should have chosen the right profile");
 });
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/xpcshell/test_single_profile_selected.js
@@ -0,0 +1,46 @@
+/*
+ * Previous versions of Firefox automatically used a single profile even if it
+ * wasn't marked as the default. So we should try to upgrade that one if it was
+ * last used by this build. This test checks the case where it was.
+ */
+
+add_task(async () => {
+  let defaultProfile = makeRandomProfileDir("default");
+
+  writeCompatibilityIni(defaultProfile);
+
+  writeProfilesIni({
+    profiles: [{
+      name: "default",
+      path: defaultProfile.leafName,
+      default: false,
+    }],
+  });
+
+  let { profile: selectedProfile, didCreate } = selectStartupProfile();
+  checkStartupReason("firstrun-claimed-default");
+
+  let hash = xreDirProvider.getInstallHash();
+  let profileData = readProfilesIni();
+  let installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
+
+  let profile = profileData.profiles[0];
+  Assert.equal(profile.name, "default", "Should have the right name.");
+  Assert.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
+  Assert.ok(profile.default, "Should be marked as the old-style default.");
+
+  Assert.equal(Object.keys(installData.installs).length, 1, "Should be only one known install.");
+  Assert.equal(installData.installs[hash].default, defaultProfile.leafName, "Should have marked the original default profile as the default for this install.");
+  Assert.ok(!installData.installs[hash].locked, "Should not have locked as we're not the default app.");
+
+  checkProfileService(profileData, installData);
+
+  Assert.ok(!didCreate, "Should not have created a new profile.");
+  let service = getProfileService();
+  Assert.ok(!service.createdAlternateProfile, "Should not have created an alternate profile.");
+  Assert.ok(selectedProfile.rootDir.equals(defaultProfile), "Should be using the right directory.");
+  Assert.equal(selectedProfile.name, "default");
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/xpcshell/test_single_profile_unselected.js
@@ -0,0 +1,55 @@
+/*
+ * Previous versions of Firefox automatically used a single profile even if it
+ * wasn't marked as the default. So we should try to upgrade that one if it was
+ * last used by this build. This test checks the case where it wasn't.
+ */
+
+add_task(async () => {
+  let defaultProfile = makeRandomProfileDir("default");
+
+  // Just pretend this profile was last used by something in the profile dir.
+  let greDir = gProfD.clone();
+  greDir.append("app");
+  writeCompatibilityIni(defaultProfile, greDir, greDir);
+
+  writeProfilesIni({
+    profiles: [{
+      name: "default",
+      path: defaultProfile.leafName,
+      default: false,
+    }],
+  });
+
+  let service = getProfileService();
+
+  let profileData = readProfilesIni();
+  let installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
+
+  let profile = profileData.profiles[0];
+  Assert.equal(profile.name, "default", "Should have the right name.");
+  Assert.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
+  Assert.ok(!profile.default, "Should not be marked as the old-style default.");
+
+  Assert.equal(Object.keys(installData.installs).length, 0, "Should be no defaults for installs yet.");
+
+  checkProfileService(profileData, installData);
+
+  let { profile: selectedProfile, didCreate } = selectStartupProfile();
+  checkStartupReason("firstrun-skipped-default");
+  Assert.ok(didCreate, "Should have created a new profile.");
+  Assert.ok(service.createdAlternateProfile, "Should have created an alternate profile.");
+  Assert.ok(!selectedProfile.rootDir.equals(defaultProfile), "Should be using the right directory.");
+  Assert.equal(selectedProfile.name, DEDICATED_NAME);
+
+  profileData = readProfilesIni();
+
+  profile = profileData.profiles[0];
+  Assert.equal(profile.name, "default", "Should have the right name.");
+  Assert.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
+  Assert.ok(profile.default, "Should now be marked as the old-style default.");
+
+  checkProfileService(profileData);
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/xpcshell/test_snap.js
@@ -0,0 +1,45 @@
+/*
+ * Tests that an old-style default profile not previously used by this build gets
+ * used in a snap environment.
+ */
+
+add_task(async () => {
+  let defaultProfile = makeRandomProfileDir("default");
+
+  // Just pretend this profile was last used by something in the profile dir.
+  let greDir = gProfD.clone();
+  greDir.append("app");
+  writeCompatibilityIni(defaultProfile, greDir, greDir);
+
+  writeProfilesIni({
+    profiles: [{
+      name: PROFILE_DEFAULT,
+      path: defaultProfile.leafName,
+      default: true,
+    }],
+  });
+
+  simulateSnapEnvironment();
+
+  let { profile: selectedProfile, didCreate } = selectStartupProfile();
+  checkStartupReason("default");
+
+  let profileData = readProfilesIni();
+  let installsINI = gDataHome.clone();
+  installsINI.append("installs.ini");
+  Assert.ok(!installsINI.exists(), "Installs database should not have been created.");
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
+
+  let profile = profileData.profiles[0];
+  Assert.equal(profile.name, PROFILE_DEFAULT, "Should have the right name.");
+  Assert.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
+  Assert.ok(profile.default, "Should be marked as the old-style default.");
+
+  checkProfileService(profileData);
+
+  Assert.ok(!didCreate, "Should not have created a new profile.");
+  Assert.ok(selectedProfile.rootDir.equals(defaultProfile), "Should be using the right directory.");
+  Assert.equal(selectedProfile.name, PROFILE_DEFAULT);
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/xpcshell/test_snap_empty.js
@@ -0,0 +1,21 @@
+/*
+ * Tests that from a clean slate snap builds create an appropriate profile.
+ */
+
+add_task(async () => {
+  simulateSnapEnvironment();
+
+  let service = getProfileService();
+  let { profile, didCreate } = selectStartupProfile();
+  checkStartupReason("firstrun-created-default");
+
+  Assert.ok(didCreate, "Should have created a new profile.");
+  Assert.equal(profile.name, PROFILE_DEFAULT, "Should have used the normal name.");
+  if (AppConstants.MOZ_DEV_EDITION) {
+    Assert.equal(service.profileCount, 2, "Should be two profiles.");
+  } else {
+    Assert.equal(service.profileCount, 1, "Should be only one profile.");
+  }
+
+  checkProfileService();
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/xpcshell/test_steal_inuse.js
@@ -0,0 +1,52 @@
+/*
+ * Tests that an old-style default profile previously used by this build but
+ * that has already been claimed by a different build gets stolen by this build.
+ */
+
+add_task(async () => {
+  let defaultProfile = makeRandomProfileDir("default");
+
+  writeCompatibilityIni(defaultProfile);
+
+  writeProfilesIni({
+    profiles: [{
+      name: PROFILE_DEFAULT,
+      path: defaultProfile.leafName,
+      default: true,
+    }],
+  });
+  writeInstallsIni({
+    installs: {
+      otherhash: {
+        default: defaultProfile.leafName,
+      },
+    },
+  });
+
+  let service = getProfileService();
+  let { profile: selectedProfile, didCreate } = selectStartupProfile();
+  checkStartupReason("firstrun-claimed-default");
+
+  let hash = xreDirProvider.getInstallHash();
+  let profileData = readProfilesIni();
+  let installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
+
+  let profile = profileData.profiles[0];
+  Assert.equal(profile.name, PROFILE_DEFAULT, "Should have the right name.");
+  Assert.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
+  Assert.ok(profile.default, "Should be marked as the old-style default.");
+
+  Assert.equal(Object.keys(installData.installs).length, 1, "Should only be one known installs.");
+  Assert.equal(installData.installs[hash].default, defaultProfile.leafName, "Should have taken the original default profile as the default for the current install.");
+  Assert.ok(!installData.installs[hash].locked, "Should not have locked as we're not the default app.");
+
+  checkProfileService(profileData, installData);
+
+  Assert.ok(!didCreate, "Should not have created a new profile.");
+  Assert.ok(!service.createdAlternateProfile, "Should not have created an alternate profile.");
+  Assert.ok(selectedProfile.rootDir.equals(defaultProfile), "Should be using the right directory.");
+  Assert.equal(selectedProfile.name, PROFILE_DEFAULT);
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/xpcshell/test_update_selected_dedicated.js
@@ -0,0 +1,45 @@
+/*
+ * Tests that an old-style default profile previously used by this build gets
+ * updated to a dedicated profile for this build.
+ */
+
+add_task(async () => {
+  let defaultProfile = makeRandomProfileDir("default");
+
+  writeCompatibilityIni(defaultProfile);
+
+  writeProfilesIni({
+    profiles: [{
+      name: PROFILE_DEFAULT,
+      path: defaultProfile.leafName,
+      default: true,
+    }],
+  });
+
+  let service = getProfileService();
+  let { profile: selectedProfile, didCreate } = selectStartupProfile();
+  checkStartupReason("firstrun-claimed-default");
+
+  let hash = xreDirProvider.getInstallHash();
+  let profileData = readProfilesIni();
+  let installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
+
+  let profile = profileData.profiles[0];
+  Assert.equal(profile.name, PROFILE_DEFAULT, "Should have the right name.");
+  Assert.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
+  Assert.ok(profile.default, "Should be marked as the old-style default.");
+
+  Assert.equal(Object.keys(installData.installs).length, 1, "Should be only one known install.");
+  Assert.equal(installData.installs[hash].default, defaultProfile.leafName, "Should have marked the original default profile as the default for this install.");
+  Assert.ok(!installData.installs[hash].locked, "Should not have locked as we're not the default app.");
+
+  checkProfileService(profileData, installData);
+
+  Assert.ok(!didCreate, "Should not have created a new profile.");
+  Assert.ok(!service.createdAlternateProfile, "Should not have created an alternate profile.");
+  Assert.ok(selectedProfile.rootDir.equals(defaultProfile), "Should be using the right directory.");
+  Assert.equal(selectedProfile.name, PROFILE_DEFAULT);
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/xpcshell/test_update_unknown_dedicated.js
@@ -0,0 +1,43 @@
+/*
+ * Tests that an old-style default profile not previously used by any build gets
+ * updated to a dedicated profile for this build.
+ */
+
+add_task(async () => {
+  let hash = xreDirProvider.getInstallHash();
+  let defaultProfile = makeRandomProfileDir("default");
+
+  writeProfilesIni({
+    profiles: [{
+      name: PROFILE_DEFAULT,
+      path: defaultProfile.leafName,
+      default: true,
+    }],
+  });
+
+  let service = getProfileService();
+  let { profile: selectedProfile, didCreate } = selectStartupProfile();
+  checkStartupReason("firstrun-claimed-default");
+
+  let profileData = readProfilesIni();
+  let installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
+
+  let profile = profileData.profiles[0];
+  Assert.equal(profile.name, PROFILE_DEFAULT, "Should have the right name.");
+  Assert.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
+  Assert.ok(profile.default, "Should be marked as the old-style default.");
+
+  Assert.equal(Object.keys(installData.installs).length, 1, "Should be a default for installs.");
+  Assert.equal(installData.installs[hash].default, profile.path, "Should have the right default profile.");
+  Assert.ok(!installData.installs[hash].locked, "Should not have locked as we didn't create this profile for this install.");
+
+  checkProfileService(profileData, installData);
+
+  Assert.ok(!didCreate, "Should not have created a new profile.");
+  Assert.ok(!service.createdAlternateProfile, "Should not have created an alternate profile.");
+  Assert.ok(selectedProfile.rootDir.equals(defaultProfile), "Should be using the right directory.");
+  Assert.equal(selectedProfile.name, PROFILE_DEFAULT);
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/xpcshell/test_update_unselected_dedicated.js
@@ -0,0 +1,57 @@
+/*
+ * Tests that an old-style default profile not previously used by this build gets
+ * ignored.
+ */
+
+add_task(async () => {
+  let hash = xreDirProvider.getInstallHash();
+  let defaultProfile = makeRandomProfileDir("default");
+
+  // Just pretend this profile was last used by something in the profile dir.
+  let greDir = gProfD.clone();
+  greDir.append("app");
+  writeCompatibilityIni(defaultProfile, greDir, greDir);
+
+  writeProfilesIni({
+    profiles: [{
+      name: PROFILE_DEFAULT,
+      path: defaultProfile.leafName,
+      default: true,
+    }],
+  });
+
+  let service = getProfileService();
+  let { profile: selectedProfile, didCreate } = selectStartupProfile();
+  checkStartupReason("firstrun-skipped-default");
+
+  let profileData = readProfilesIni();
+  let installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 2, "Should have the right number of profiles.");
+
+  // The name ordering is different for dev edition.
+  if (AppConstants.MOZ_DEV_EDITION) {
+    profileData.profiles.reverse();
+  }
+
+  let profile = profileData.profiles[0];
+  Assert.equal(profile.name, PROFILE_DEFAULT, "Should have the right name.");
+  Assert.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
+  Assert.ok(profile.default, "Should be marked as the old-style default.");
+  profile = profileData.profiles[1];
+  Assert.equal(profile.name, DEDICATED_NAME, "Should have the right name.");
+  Assert.notEqual(profile.path, defaultProfile.leafName, "Should not be the original default profile.");
+  Assert.ok(!profile.default, "Should not be marked as the old-style default.");
+
+  Assert.equal(Object.keys(installData.installs).length, 1, "Should be a default for this install.");
+  Assert.equal(installData.installs[hash].default, profile.path, "Should have marked the new profile as the default for this install.");
+  Assert.ok(installData.installs[hash].locked, "Should have locked as we created this profile for this install.");
+
+  checkProfileService(profileData, installData);
+
+  Assert.ok(didCreate, "Should have created a new profile.");
+  Assert.ok(service.createdAlternateProfile, "Should have created an alternate profile.");
+  Assert.ok(!selectedProfile.rootDir.equals(defaultProfile), "Should be using the right directory.");
+  Assert.equal(selectedProfile.name, DEDICATED_NAME);
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/xpcshell/test_use_dedicated.js
@@ -0,0 +1,67 @@
+/**
+ * Tests that if installs.ini lists a profile we use it as the default.
+ */
+
+add_task(async () => {
+  let hash = xreDirProvider.getInstallHash();
+  let defaultProfile = makeRandomProfileDir("default");
+  let dedicatedProfile = makeRandomProfileDir("dedicated");
+  let devProfile = makeRandomProfileDir("devedition");
+
+  // Make sure we don't steal the old-style default.
+  writeCompatibilityIni(defaultProfile);
+
+  writeProfilesIni({
+    profiles: [{
+      name: "default",
+      path: defaultProfile.leafName,
+      default: true,
+    }, {
+      name: "dedicated",
+      path: dedicatedProfile.leafName,
+    }, {
+      name: "dev-edition-default",
+      path: devProfile.leafName,
+    }],
+  });
+
+  writeInstallsIni({
+    installs: {
+      [hash]: {
+        default: dedicatedProfile.leafName,
+      },
+      "otherhash": {
+        default: "foobar",
+      },
+    },
+  });
+
+  let { profile: selectedProfile, didCreate } = selectStartupProfile();
+  checkStartupReason("default");
+
+  let profileData = readProfilesIni();
+  let installData = readInstallsIni();
+
+  Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
+  Assert.equal(profileData.profiles.length, 3, "Should have the right number of profiles.");
+
+  let profile = profileData.profiles[0];
+  Assert.equal(profile.name, `dedicated`, "Should have the right name.");
+  Assert.equal(profile.path, dedicatedProfile.leafName, "Should be the expected dedicated profile.");
+  Assert.ok(!profile.default, "Should not be marked as the old-style default.");
+
+  profile = profileData.profiles[1];
+  Assert.equal(profile.name, "default", "Should have the right name.");
+  Assert.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
+  Assert.ok(profile.default, "Should be marked as the old-style default.");
+
+  Assert.equal(Object.keys(installData.installs).length, 2, "Should be two known installs.");
+  Assert.equal(installData.installs[hash].default, dedicatedProfile.leafName, "Should have kept the default for this install.");
+  Assert.equal(installData.installs.otherhash.default, "foobar", "Should have kept the default for the other install.");
+
+  checkProfileService(profileData, installData);
+
+  Assert.ok(!didCreate, "Should not have created a new profile.");
+  Assert.ok(selectedProfile.rootDir.equals(dedicatedProfile), "Should be using the right directory.");
+  Assert.equal(selectedProfile.name, "dedicated");
+});
--- a/toolkit/profile/xpcshell/xpcshell.ini
+++ b/toolkit/profile/xpcshell/xpcshell.ini
@@ -6,8 +6,25 @@ skip-if = toolkit == 'android'
 [test_select_profilemanager.js]
 [test_select_named.js]
 [test_select_missing.js]
 [test_select_noname.js]
 [test_create_default.js]
 [test_select_environment.js]
 [test_select_environment_named.js]
 [test_profile_reset.js]
+[test_clean.js]
+[test_previous_dedicated.js]
+[test_single_profile_selected.js]
+skip-if = devedition
+[test_single_profile_unselected.js]
+skip-if = devedition
+[test_update_selected_dedicated.js]
+[test_update_unknown_dedicated.js]
+[test_update_unselected_dedicated.js]
+[test_use_dedicated.js]
+[test_new_default.js]
+[test_steal_inuse.js]
+[test_snap.js]
+[test_snap_empty.js]
+[test_remove_default.js]
+[test_claim_locked.js]
+[test_lock.js]
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -55,16 +55,17 @@
   --blue-40-a10: rgb(69, 161, 255, 0.1);
   --blue-50: #0a84ff;
   --blue-50-a30: rgba(10, 132, 255, 0.3);
   --blue-60: #0060df;
   --blue-70: #003eaa;
   --blue-80: #002275;
   --grey-20: #ededf0;
   --grey-30: #d7d7db;
+  --grey-50: #737373;
   --grey-60: #4a4a4f;
   --grey-90: #0c0c0d;
   --grey-90-a10: rgba(12, 12, 13, 0.1);
   --grey-90-a20: rgba(12, 12, 13, 0.2);
   --grey-90-a30: rgba(12, 12, 13, 0.3);
   --grey-90-a40: rgba(12, 12, 13, 0.4);
   --grey-90-a50: rgba(12, 12, 13, 0.5);
   --purple-70: #6200a4;
--- a/toolkit/themes/shared/mozapps.inc.mn
+++ b/toolkit/themes/shared/mozapps.inc.mn
@@ -23,16 +23,18 @@
   skin/classic/mozapps/extensions/alerticon-info-positive.svg (../../shared/extensions/alerticon-info-positive.svg)
   skin/classic/mozapps/extensions/alerticon-info-negative.svg (../../shared/extensions/alerticon-info-negative.svg)
   skin/classic/mozapps/extensions/category-legacy.svg        (../../shared/extensions/category-legacy.svg)
   skin/classic/mozapps/aboutNetworking.css                   (../../shared/aboutNetworking.css)
 #ifndef ANDROID
   skin/classic/mozapps/aboutProfiles.css                     (../../shared/aboutProfiles.css)
 #endif
   skin/classic/mozapps/aboutServiceWorkers.css               (../../shared/aboutServiceWorkers.css)
+  skin/classic/mozapps/profile/profileDowngrade.css          (../../shared/profile/profileDowngrade.css)
+  skin/classic/mozapps/profile/information.svg               (../../shared/profile/information.svg)
 
 % override chrome://mozapps/skin/extensions/category-plugins.svg          chrome://global/skin/plugins/pluginGeneric.svg
 % override chrome://mozapps/skin/extensions/category-extensions.svg       chrome://mozapps/skin/extensions/extensionGeneric.svg
 % override chrome://mozapps/skin/extensions/category-languages.svg        chrome://mozapps/skin/extensions/localeGeneric.svg
 % override chrome://mozapps/skin/extensions/category-themes.svg           chrome://mozapps/skin/extensions/themeGeneric.svg
 % override chrome://mozapps/skin/extensions/category-dictionaries.svg chrome://mozapps/skin/extensions/dictionaryGeneric.svg
 % override chrome://mozapps/skin/extensions/localeGeneric.svg         chrome://mozapps/skin/extensions/dictionaryGeneric.svg
 % override chrome://mozapps/skin/extensions/category-experiments.svg  chrome://mozapps/skin/extensions/experimentGeneric.svg
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/profile/information.svg
@@ -0,0 +1,4 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="context-fill" d="M8 16a8 8 0 1 1 8-8 8.009 8.009 0 0 1-8 8zM8 2a6 6 0 1 0 6 6 6.006 6.006 0 0 0-6-6z"></path><path fill="context-fill" d="M8 7a1 1 0 0 0-1 1v3a1 1 0 0 0 2 0V8a1 1 0 0 0-1-1z"></path><circle cx="8" cy="5" r="1.188"></circle></svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/profile/profileDowngrade.css
@@ -0,0 +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/. */
+
+spacer[anonid="spacer"] {
+  display: none;
+}
+
+#info {
+  list-style-image: url("chrome://mozapps/skin/profile/information.svg");
+  width: 32px;
+  height: 32px;
+}
--- a/toolkit/xre/ProfileReset.cpp
+++ b/toolkit/xre/ProfileReset.cpp
@@ -9,17 +9,16 @@
 #include "nsIToolkitProfile.h"
 #include "nsIWindowWatcher.h"
 
 #include "ProfileReset.h"
 
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsPIDOMWindow.h"
-#include "nsPrintfCString.h"
 #include "nsString.h"
 #include "nsXPCOMCIDInternal.h"
 #include "mozilla/Components.h"
 #include "mozilla/XREAppData.h"
 
 #include "mozilla/Services.h"
 #include "mozilla/Unused.h"
 #include "prtime.h"
@@ -27,55 +26,22 @@
 using namespace mozilla;
 
 extern const XREAppData* gAppData;
 
 static const char kProfileProperties[] =
     "chrome://mozapps/locale/profile/profileSelection.properties";
 
 /**
- * Creates a new profile with a timestamp in the name to use for profile reset.
+ * Spin up a thread to backup the old profile's main directory and delete the
+ * profile's local directory. Once complete have the profile service remove the
+ * old profile and if necessary make the new profile the default.
  */
-nsresult CreateResetProfile(nsIToolkitProfileService* aProfileSvc,
-                            nsIToolkitProfile* aOldProfile,
-                            nsIToolkitProfile** aNewProfile) {
-  MOZ_ASSERT(aProfileSvc, "NULL profile service");
-
-  nsAutoCString oldProfileName;
-  aOldProfile->GetName(oldProfileName);
-
-  nsCOMPtr<nsIToolkitProfile> newProfile;
-  // Make the new profile the old profile (or "default-") + the time in seconds
-  // since epoch for uniqueness.
-  nsAutoCString newProfileName;
-  if (!oldProfileName.IsEmpty()) {
-    newProfileName.Assign(oldProfileName);
-    newProfileName.Append("-");
-  } else {
-    newProfileName.AssignLiteral("default-");
-  }
-  newProfileName.Append(nsPrintfCString("%" PRId64, PR_Now() / 1000));
-  nsresult rv =
-      aProfileSvc->CreateProfile(nullptr,  // choose a default dir for us
-                                 newProfileName, getter_AddRefs(newProfile));
-  if (NS_FAILED(rv)) return rv;
-
-  rv = aProfileSvc->Flush();
-  if (NS_FAILED(rv)) return rv;
-
-  newProfile.swap(*aNewProfile);
-
-  return NS_OK;
-}
-
-/**
- * Delete the profile directory being reset after a backup and delete the local
- * profile directory.
- */
-nsresult ProfileResetCleanup(nsIToolkitProfile* aOldProfile) {
+nsresult ProfileResetCleanup(nsToolkitProfileService* aService,
+                             nsIToolkitProfile* aOldProfile) {
   nsresult rv;
   nsCOMPtr<nsIFile> profileDir;
   rv = aOldProfile->GetRootDir(getter_AddRefs(profileDir));
   if (NS_FAILED(rv)) return rv;
 
   nsCOMPtr<nsIFile> profileLocalDir;
   rv = aOldProfile->GetLocalDir(getter_AddRefs(profileLocalDir));
   if (NS_FAILED(rv)) return rv;
@@ -173,15 +139,10 @@ nsresult ProfileResetCleanup(nsIToolkitP
     gProfileResetCleanupCompleted = true;
     NS_WARNING("Cleanup thread creation failed");
     return rv;
   }
   // Close the progress window now that the cleanup thread is done.
   auto* piWindow = nsPIDOMWindowOuter::From(progressWindow);
   piWindow->Close();
 
-  // Delete the old profile from profiles.ini. The folder was already deleted by
-  // the thread above.
-  rv = aOldProfile->Remove(false);
-  if (NS_FAILED(rv)) NS_WARNING("Could not remove the profile");
-
-  return rv;
+  return aService->ApplyResetProfile(aOldProfile);
 }
--- a/toolkit/xre/ProfileReset.h
+++ b/toolkit/xre/ProfileReset.h
@@ -1,26 +1,23 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsIToolkitProfileService.h"
+#include "nsToolkitProfileService.h"
 #include "nsIFile.h"
 #include "nsThreadUtils.h"
 
 static bool gProfileResetCleanupCompleted = false;
 static const char kResetProgressURL[] =
     "chrome://global/content/resetProfileProgress.xul";
 
-nsresult CreateResetProfile(nsIToolkitProfileService* aProfileSvc,
-                            nsIToolkitProfile* aOldProfile,
-                            nsIToolkitProfile** aNewProfile);
-
-nsresult ProfileResetCleanup(nsIToolkitProfile* aOldProfile);
+nsresult ProfileResetCleanup(nsToolkitProfileService* aService,
+                             nsIToolkitProfile* aOldProfile);
 
 class ProfileResetCleanupResultTask : public mozilla::Runnable {
  public:
   ProfileResetCleanupResultTask()
       : mozilla::Runnable("ProfileResetCleanupResultTask"),
         mWorkerThread(do_GetCurrentThread()) {
     MOZ_ASSERT(!NS_IsMainThread());
   }
--- a/toolkit/xre/moz.build
+++ b/toolkit/xre/moz.build
@@ -1,14 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+include('../components/telemetry/telemetry-constants.mozbuild')
+
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'Startup and Profile System')
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     TEST_DIRS += ['test/win']
 
 MOCHITEST_MANIFESTS += ['test/mochitest.ini']
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
@@ -182,16 +184,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'andr
 if CONFIG['TARGET_XPCOM_ABI']:
     DEFINES['TARGET_OS_ABI'] = '"%s_%s"' % (CONFIG['OS_TARGET'],
                                             CONFIG['TARGET_XPCOM_ABI'])
 
 if CONFIG['OS_ARCH'] == 'Linux' and 'lib64' in CONFIG['libdir']:
     DEFINES['HAVE_USR_LIB64_DIR'] = True
 
 DEFINES['GRE_MILESTONE'] = CONFIG['GRE_MILESTONE']
+DEFINES['MOZ_APP_VERSION_DISPLAY'] = CONFIG['MOZ_APP_VERSION_DISPLAY']
 
 for var in ('APP_VERSION', 'APP_ID'):
     DEFINES[var] = CONFIG['MOZ_%s' % var]
 
 if CONFIG['MOZ_BUILD_APP'] == 'browser':
     DEFINES['MOZ_BUILD_APP_IS_BROWSER'] = True
 
 LOCAL_INCLUDES += [
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -21,16 +21,17 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/Printf.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/intl/LocaleService.h"
 #include "mozilla/recordreplay/ParentIPC.h"
+#include "mozilla/JSONWriter.h"
 
 #include "nsAppRunner.h"
 #include "mozilla/XREAppData.h"
 #include "mozilla/Bootstrap.h"
 #if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
 #  include "nsUpdateDriver.h"
 #endif
 #include "ProfileReset.h"
@@ -71,16 +72,17 @@
 #include "mozilla/ModuleUtils.h"
 #include "nsIIOService.h"
 #include "nsIObserverService.h"
 #include "nsINativeAppSupport.h"
 #include "nsIPlatformInfo.h"
 #include "nsIProcess.h"
 #include "nsIProfileUnlocker.h"
 #include "nsIPromptService.h"
+#include "nsIPropertyBag2.h"
 #include "nsIServiceManager.h"
 #include "nsIStringBundle.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIToolkitChromeRegistry.h"
 #include "nsIToolkitProfile.h"
 #include "nsToolkitProfileService.h"
 #include "nsIURI.h"
 #include "nsIURL.h"
@@ -1489,16 +1491,19 @@ static void DumpHelp() {
       "  --migration        Start with migration wizard.\n"
       "  --ProfileManager   Start with ProfileManager.\n"
       "  --no-remote        Do not accept or send remote commands; implies\n"
       "                     --new-instance.\n"
       "  --new-instance     Open new instance, not a new window in running "
       "instance.\n"
       "  --UILocale <locale> Start with <locale> resources as UI Locale.\n"
       "  --safe-mode        Disables extensions and themes for this session.\n"
+#ifdef MOZ_BLOCK_PROFILE_DOWNGRADE
+      "  --allow-downgrade  Allows downgrading a profile.\n"
+#endif
       "  -MOZ_LOG=<modules> Treated as MOZ_LOG=<modules> environment variable, "
       "overrides it.\n"
       "  -MOZ_LOG_FILE=<file> Treated as MOZ_LOG_FILE=<file> environment "
       "variable, overrides it.\n"
       "                     If MOZ_LOG_FILE is not specified as an argument or "
       "as an environment variable,\n"
       "                     logging will be written to stdout.\n",
       (const char*)gAppData->name);
@@ -1953,18 +1958,16 @@ static nsresult ProfileLockedDialog(nsIT
 static const char kProfileManagerURL[] =
     "chrome://mozapps/content/profile/profileSelection.xul";
 
 static ReturnAbortOnError ShowProfileManager(
     nsIToolkitProfileService* aProfileSvc, nsINativeAppSupport* aNative) {
   nsresult rv;
 
   nsCOMPtr<nsIFile> profD, profLD;
-  char16_t* profileNamePtr;
-  nsAutoCString profileName;
   bool offline = false;
 
   {
     ScopedXPCOMStartup xpcom;
     rv = xpcom.Initialize();
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Initialize the graphics prefs, some of the paths need them before
@@ -2024,28 +2027,23 @@ static ReturnAbortOnError ShowProfileMan
       NS_ENSURE_SUCCESS_LOG(rv, rv);
 
       rv = lock->GetDirectory(getter_AddRefs(profD));
       NS_ENSURE_SUCCESS(rv, rv);
 
       rv = lock->GetLocalDirectory(getter_AddRefs(profLD));
       NS_ENSURE_SUCCESS(rv, rv);
 
-      rv = ioParamBlock->GetString(0, &profileNamePtr);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      CopyUTF16toUTF8(MakeStringSpan(profileNamePtr), profileName);
-      free(profileNamePtr);
-
       lock->Unlock();
     }
   }
 
   SaveFileToEnv("XRE_PROFILE_PATH", profD);
   SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", profLD);
+  SaveToEnv("XRE_RESTARTED_BY_PROFILE_MANAGER=1");
 
   if (offline) {
     SaveToEnv("XRE_START_OFFLINE=1");
   }
   if (gRestartedByOS) {
     // Re-add this argument when actually starting the application.
     char** newArgv =
         (char**)realloc(gRestartArgv, sizeof(char*) * (gRestartArgc + 2));
@@ -2053,51 +2051,16 @@ static ReturnAbortOnError ShowProfileMan
     gRestartArgv = newArgv;
     gRestartArgv[gRestartArgc++] = const_cast<char*>("-os-restarted");
     gRestartArgv[gRestartArgc] = nullptr;
   }
 
   return LaunchChild(aNative);
 }
 
-/**
- * Get the currently running profile using its root directory.
- *
- * @param aProfileSvc         The profile service
- * @param aCurrentProfileRoot The root directory of the current profile.
- * @param aProfile            Out-param that returns the profile object.
- * @return an error if aCurrentProfileRoot is not found
- */
-static nsresult GetCurrentProfile(nsIToolkitProfileService* aProfileSvc,
-                                  nsIFile* aCurrentProfileRoot,
-                                  nsIToolkitProfile** aProfile) {
-  NS_ENSURE_ARG_POINTER(aProfileSvc);
-  NS_ENSURE_ARG_POINTER(aProfile);
-
-  nsCOMPtr<nsISimpleEnumerator> profiles;
-  nsresult rv = aProfileSvc->GetProfiles(getter_AddRefs(profiles));
-  if (NS_FAILED(rv)) return rv;
-
-  bool foundMatchingProfile = false;
-  nsCOMPtr<nsISupports> supports;
-  rv = profiles->GetNext(getter_AddRefs(supports));
-  while (NS_SUCCEEDED(rv)) {
-    nsCOMPtr<nsIToolkitProfile> profile = do_QueryInterface(supports);
-    nsCOMPtr<nsIFile> profileRoot;
-    profile->GetRootDir(getter_AddRefs(profileRoot));
-    profileRoot->Equals(aCurrentProfileRoot, &foundMatchingProfile);
-    if (foundMatchingProfile) {
-      profile.forget(aProfile);
-      return NS_OK;
-    }
-    rv = profiles->GetNext(getter_AddRefs(supports));
-  }
-  return rv;
-}
-
 static bool gDoMigration = false;
 static bool gDoProfileReset = false;
 static nsCOMPtr<nsIToolkitProfile> gResetOldProfile;
 
 // Pick a profile. We need to end up with a profile lock.
 //
 // 1) check for --profile <path>
 // 2) check for -P <name>
@@ -2205,18 +2168,17 @@ static nsresult SelectProfile(nsIProfile
       if (NS_FAILED(rv)) {
         return ProfileLockedDialog(profile, unlocker, aNative,
                                    &tempProfileLock);
       }
     }
 
     // If we're resetting a profile, create a new one and use it to startup.
     gResetOldProfile = profile;
-    rv = CreateResetProfile(aProfileSvc, gResetOldProfile,
-                            getter_AddRefs(profile));
+    rv = service->CreateResetProfile(getter_AddRefs(profile));
     if (NS_SUCCEEDED(rv)) {
       rv = profile->GetRootDir(getter_AddRefs(rootDir));
       NS_ENSURE_SUCCESS(rv, rv);
       SaveFileToEnv("XRE_PROFILE_PATH", rootDir);
 
       rv = profile->GetLocalDir(getter_AddRefs(localDir));
       NS_ENSURE_SUCCESS(rv, rv);
       SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", localDir);
@@ -2268,41 +2230,401 @@ static nsresult SelectProfile(nsIProfile
     }
     PR_Sleep(kLockRetrySleepMS);
   } while (TimeStamp::Now() - start <
            TimeDuration::FromSeconds(kLockRetrySeconds));
 
   return ProfileLockedDialog(rootDir, localDir, unlocker, aNative, aResult);
 }
 
+#ifdef MOZ_BLOCK_PROFILE_DOWNGRADE
+struct FileWriteFunc : public JSONWriteFunc {
+  FILE* mFile;
+  explicit FileWriteFunc(FILE* aFile) : mFile(aFile) {}
+
+  void Write(const char* aStr) override { fprintf(mFile, "%s", aStr); }
+};
+
+static void SubmitDowngradeTelemetry(const nsCString& aLastVersion,
+                                     bool aHasSync, int32_t aButton) {
+  nsCOMPtr<nsIPrefService> prefSvc =
+      do_GetService("@mozilla.org/preferences-service;1");
+  NS_ENSURE_TRUE_VOID(prefSvc);
+
+  nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(prefSvc);
+  NS_ENSURE_TRUE_VOID(prefBranch);
+
+  bool enabled;
+  nsresult rv = prefBranch->GetBoolPref(
+      "datareporting.healthreport.uploadEnabled", &enabled);
+  NS_ENSURE_SUCCESS_VOID(rv);
+  if (!enabled) {
+    return;
+  }
+
+  nsCString server;
+  rv = prefBranch->GetCharPref("toolkit.telemetry.server", server);
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  nsCString clientId;
+  rv = prefBranch->GetCharPref("toolkit.telemetry.cachedClientID", clientId);
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  rv = prefSvc->GetDefaultBranch(nullptr, getter_AddRefs(prefBranch));
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  nsCString channel("default");
+  rv = prefBranch->GetCharPref("app.update.channel", channel);
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  nsID uuid;
+  nsCOMPtr<nsIUUIDGenerator> uuidGen =
+      do_GetService("@mozilla.org/uuid-generator;1");
+  NS_ENSURE_TRUE_VOID(uuidGen);
+  rv = uuidGen->GenerateUUIDInPlace(&uuid);
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  char strid[NSID_LENGTH];
+  uuid.ToProvidedString(strid);
+
+  nsCString arch("null");
+  nsCOMPtr<nsIPropertyBag2> sysInfo =
+      do_GetService("@mozilla.org/system-info;1");
+  NS_ENSURE_TRUE_VOID(sysInfo);
+  sysInfo->GetPropertyAsACString(NS_LITERAL_STRING("arch"), arch);
+
+  time_t now;
+  time(&now);
+  char date[sizeof "YYYY-MM-DDThh:mm:ss.000Z"];
+  strftime(date, sizeof date, "%FT%T.000Z", gmtime(&now));
+
+  // NSID_LENGTH includes the trailing \0 and we also want to strip off the
+  // surrounding braces so the length becomes NSID_LENGTH - 3.
+  nsDependentCSubstring pingId(strid + 1, NSID_LENGTH - 3);
+  NS_NAMED_LITERAL_CSTRING(pingType, "downgrade");
+
+  int32_t pos = aLastVersion.Find("_");
+  if (pos == kNotFound) {
+    return;
+  }
+
+  const nsDependentCSubstring lastVersion = Substring(aLastVersion, 0, pos);
+  const nsDependentCSubstring lastBuildId =
+      Substring(aLastVersion, pos + 1, 14);
+
+  nsPrintfCString url("%s/submit/telemetry/%s/%s/%s/%s/%s/%s?v=%d",
+                      server.get(), PromiseFlatCString(pingId).get(),
+                      pingType.get(), (const char*)gAppData->name,
+                      (const char*)gAppData->version, channel.get(),
+                      (const char*)gAppData->buildID,
+                      TELEMETRY_PING_FORMAT_VERSION);
+
+  nsCOMPtr<nsIFile> pingFile;
+  rv = NS_GetSpecialDirectory(XRE_USER_APP_DATA_DIR, getter_AddRefs(pingFile));
+  NS_ENSURE_SUCCESS_VOID(rv);
+  rv = pingFile->Append(NS_LITERAL_STRING("Pending Pings"));
+  NS_ENSURE_SUCCESS_VOID(rv);
+  rv = pingFile->Create(nsIFile::DIRECTORY_TYPE, 0755);
+  if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
+    return;
+  }
+  rv = pingFile->Append(NS_ConvertUTF8toUTF16(pingId));
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  nsCOMPtr<nsIFile> pingSender;
+  rv = NS_GetSpecialDirectory(NS_GRE_BIN_DIR, getter_AddRefs(pingSender));
+  NS_ENSURE_SUCCESS_VOID(rv);
+#  ifdef XP_WIN
+  pingSender->Append(NS_LITERAL_STRING("pingsender.exe"));
+#  else
+  pingSender->Append(NS_LITERAL_STRING("pingsender"));
+#  endif
+
+  bool exists;
+  rv = pingSender->Exists(&exists);
+  NS_ENSURE_SUCCESS_VOID(rv);
+  if (!exists) {
+    return;
+  }
+
+  FILE* file;
+  rv = pingFile->OpenANSIFileDesc("w", &file);
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  JSONWriter w(MakeUnique<FileWriteFunc>(file));
+  w.Start();
+  {
+    w.StringProperty("type", pingType.get());
+    w.StringProperty("id", PromiseFlatCString(pingId).get());
+    w.StringProperty("creationDate", date);
+    w.IntProperty("version", TELEMETRY_PING_FORMAT_VERSION);
+    w.StringProperty("clientId", clientId.get());
+    w.StartObjectProperty("application");
+    {
+      w.StringProperty("architecture", arch.get());
+      w.StringProperty("buildId", gAppData->buildID);
+      w.StringProperty("name", gAppData->name);
+      w.StringProperty("version", gAppData->version);
+      w.StringProperty("displayVersion", NS_STRINGIFY(MOZ_APP_VERSION_DISPLAY));
+      w.StringProperty("vendor", gAppData->vendor);
+      w.StringProperty("platformVersion", gToolkitVersion);
+#  ifdef TARGET_XPCOM_ABI
+      w.StringProperty("xpcomAbi", TARGET_XPCOM_ABI);
+#  else
+      w.StringProperty("xpcomAbi", "unknown");
+#  endif
+      w.StringProperty("channel", channel.get());
+    }
+    w.EndObject();
+    w.StartObjectProperty("payload");
+    {
+      w.StringProperty("lastVersion", PromiseFlatCString(lastVersion).get());
+      w.StringProperty("lastBuildId", PromiseFlatCString(lastBuildId).get());
+      w.BoolProperty("hasSync", aHasSync);
+      w.IntProperty("button", aButton);
+    }
+    w.EndObject();
+  }
+  w.End();
+
+  fclose(file);
+
+  PathString filePath = pingFile->NativePath();
+  const filesystem::Path::value_type* args[2];
+#  ifdef XP_WIN
+  nsString urlw = NS_ConvertUTF8toUTF16(url);
+  args[0] = urlw.get();
+#  else
+  args[0] = url.get();
+#  endif
+  args[1] = filePath.get();
+
+  nsCOMPtr<nsIProcess> process =
+      do_CreateInstance("@mozilla.org/process/util;1");
+  NS_ENSURE_TRUE_VOID(process);
+  process->Init(pingSender);
+  process->SetStartHidden(true);
+  process->SetNoShell(true);
+
+#  ifdef XP_WIN
+  process->Runw(false, args, 2);
+#  else
+  process->Run(false, args, 2);
+#  endif
+}
+
+static const char kProfileDowngradeURL[] =
+    "chrome://mozapps/content/profile/profileDowngrade.xul";
+
+static ReturnAbortOnError CheckDowngrade(nsIFile* aProfileDir,
+                                         nsIFile* aProfileLocalDir,
+                                         nsACString& aProfileName,
+                                         nsINativeAppSupport* aNative,
+                                         nsIToolkitProfileService* aProfileSvc,
+                                         const nsCString& aLastVersion) {
+  int32_t result = 0;
+  nsresult rv;
+
+  {
+    if (gfxPlatform::IsHeadless()) {
+      // TODO: make a way to turn off all dialogs when headless.
+      Output(true,
+             "This profile was last used with a newer version of this "
+             "application. Please create a new profile.\n");
+      return NS_ERROR_ABORT;
+    }
+
+    ScopedXPCOMStartup xpcom;
+    rv = xpcom.Initialize();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = xpcom.SetWindowCreator(aNative);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    {  // extra scoping is needed so we release these components before xpcom
+       // shutdown
+      bool hasSync = false;
+      nsCOMPtr<nsIPrefService> prefSvc =
+          do_GetService("@mozilla.org/preferences-service;1");
+      NS_ENSURE_TRUE(prefSvc, rv);
+
+      nsCOMPtr<nsIFile> prefsFile;
+      rv = aProfileDir->Clone(getter_AddRefs(prefsFile));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = prefsFile->Append(NS_LITERAL_STRING("prefs.js"));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = prefSvc->ReadUserPrefsFromFile(prefsFile);
+      if (NS_SUCCEEDED(rv)) {
+        nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(prefSvc);
+
+        rv = prefBranch->PrefHasUserValue("services.sync.username", &hasSync);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+
+      nsCOMPtr<nsIWindowWatcher> windowWatcher =
+          do_GetService(NS_WINDOWWATCHER_CONTRACTID);
+      NS_ENSURE_TRUE(windowWatcher, NS_ERROR_ABORT);
+
+      nsCOMPtr<nsIAppStartup> appStartup(components::AppStartup::Service());
+      NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE);
+
+      nsCOMPtr<nsIDialogParamBlock> paramBlock =
+          do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID);
+      NS_ENSURE_TRUE(paramBlock, NS_ERROR_ABORT);
+
+      uint8_t flags = 0;
+      if (hasSync) {
+        flags |= nsIToolkitProfileService::hasSync;
+      }
+
+      paramBlock->SetInt(0, flags);
+
+      nsCOMPtr<mozIDOMWindowProxy> newWindow;
+      rv = windowWatcher->OpenWindow(nullptr, kProfileDowngradeURL, "_blank",
+                                     "centerscreen,chrome,modal,titlebar",
+                                     paramBlock, getter_AddRefs(newWindow));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      paramBlock->GetInt(1, &result);
+
+      SubmitDowngradeTelemetry(aLastVersion, hasSync, result);
+    }
+  }
+
+  if (result == nsIToolkitProfileService::createNewProfile) {
+    // Create a new profile and start it.
+    nsCString profileName;
+    profileName.AssignLiteral("default");
+#  ifdef MOZ_DEDICATED_PROFILES
+    profileName.Append("-" NS_STRINGIFY(MOZ_UPDATE_CHANNEL));
+#  endif
+    nsCOMPtr<nsIToolkitProfile> newProfile;
+    rv = aProfileSvc->CreateUniqueProfile(nullptr, profileName,
+                                          getter_AddRefs(newProfile));
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = aProfileSvc->SetDefaultProfile(newProfile);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = aProfileSvc->Flush();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIFile> profD, profLD;
+    rv = newProfile->GetRootDir(getter_AddRefs(profD));
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = newProfile->GetLocalDir(getter_AddRefs(profLD));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    SaveFileToEnv("XRE_PROFILE_PATH", profD);
+    SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", profLD);
+
+    return LaunchChild(aNative);
+  }
+
+  // Cancel
+  return NS_ERROR_ABORT;
+}
+#endif
+
+/**
+ * The version string used in compatibility.ini is in the form
+ * <appversion>_<appbuildid>/<aplatformbuildidid>. The build IDs are in the form
+ * <year><month><day><hour><minute><second>. We need to be able to turn this
+ * into something that can be compared by the version comparator. Build IDs are
+ * numeric so normally we could just use those as part of the version but they
+ * are larger than 32-bit integers so we must split them into two parts.
+ * So we generate a version string of the format:
+ * <appversion>.<appbuilddate>.<appbuildtime>.<platformbuilddate>.<platformbuildtime>
+ * This doesn't compare correctly so
+ * we have to make the build ids separate version parts instead. We also have
+ * to split up the build ids as they are too large for the version comparator's
+ * brain.
+ */
+
+// Build ID dates are 8 digits, build ID times are 6 digits.
+#define BUILDID_DATE_LENGTH 8
+#define BUILDID_TIME_LENGTH 6
+#define BUILDID_LENGTH BUILDID_DATE_LENGTH + BUILDID_TIME_LENGTH
+
+static void GetBuildIDVersions(const nsACString& aFullVersion, int32_t aPos,
+                               nsACString& aBuildVersions) {
+  // Extract the date part then the time part.
+  aBuildVersions.Assign(Substring(aFullVersion, aPos, BUILDID_DATE_LENGTH) +
+      NS_LITERAL_CSTRING(".") +
+      Substring(aFullVersion, aPos + BUILDID_DATE_LENGTH, BUILDID_TIME_LENGTH));
+}
+
+static Version GetComparableVersion(const nsCString& aVersionStr) {
+  // We'll find the position of the '_' and '/' characters. The length from the
+  // '_' character to the '/' character will be the length of a build ID plus
+  // one for the '_' character. Similarly the length from the '/' character to
+  // the end of the string will be the same.
+  const uint32_t kExpectedLength = BUILDID_LENGTH + 1;
+
+  int32_t underscorePos = aVersionStr.FindChar('_');
+  int32_t slashPos = aVersionStr.FindChar('/');
+  if (underscorePos == kNotFound || slashPos == kNotFound ||
+      (slashPos - underscorePos) != kExpectedLength ||
+      (aVersionStr.Length() - slashPos) != kExpectedLength) {
+    NS_WARNING("compatibility.ini Version string does not match the expected format.");
+    return Version(aVersionStr.get());
+  }
+
+  nsCString appBuild, platBuild;
+  NS_NAMED_LITERAL_CSTRING(dot, ".");
+
+  const nsACString& version = Substring(aVersionStr, 0, underscorePos);
+
+  GetBuildIDVersions(aVersionStr, underscorePos + 1, /* outparam */ appBuild);
+  GetBuildIDVersions(aVersionStr, slashPos + 1, /* outparam */ platBuild);
+
+  const nsACString& fullVersion = version + dot + appBuild + dot + platBuild;
+
+  return Version(PromiseFlatCString(fullVersion).get());
+}
+
 /**
  * Checks the compatibility.ini file to see if we have updated our application
  * or otherwise invalidated our caches. If the application has been updated,
  * we return false; otherwise, we return true. We also write the status
  * of the caches (valid/invalid) into the return param aCachesOK. The aCachesOK
- * is always invalid if the application has been updated.
+ * is always invalid if the application has been updated. aIsDowngrade is set to
+ * true if the current application is older than that previously used by the
+ * profile.
  */
 static bool CheckCompatibility(nsIFile* aProfileDir, const nsCString& aVersion,
                                const nsCString& aOSABI, nsIFile* aXULRunnerDir,
                                nsIFile* aAppDir, nsIFile* aFlagFile,
-                               bool* aCachesOK) {
+                               bool* aCachesOK, bool* aIsDowngrade,
+                               nsCString& aLastVersion) {
   *aCachesOK = false;
+  *aIsDowngrade = false;
+
   nsCOMPtr<nsIFile> file;
   aProfileDir->Clone(getter_AddRefs(file));
   if (!file) return false;
   file->AppendNative(FILE_COMPATIBILITY_INFO);
 
   nsINIParser parser;
   nsresult rv = parser.Init(file);
   if (NS_FAILED(rv)) return false;
 
+  rv = parser.GetString("Compatibility", "LastVersion", aLastVersion);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  if (!aVersion.Equals(aLastVersion)) {
+    Version current = GetComparableVersion(aVersion);
+    Version last = GetComparableVersion(aLastVersion);
+    *aIsDowngrade = last > current;
+    return false;
+  }
+
   nsAutoCString buf;
-  rv = parser.GetString("Compatibility", "LastVersion", buf);
-  if (NS_FAILED(rv) || !aVersion.Equals(buf)) return false;
-
   rv = parser.GetString("Compatibility", "LastOSABI", buf);
   if (NS_FAILED(rv) || !aOSABI.Equals(buf)) return false;
 
   rv = parser.GetString("Compatibility", "LastPlatformDir", buf);
   if (NS_FAILED(rv)) return false;
 
   nsCOMPtr<nsIFile> lf;
   rv = NS_NewNativeLocalFile(EmptyCString(), false, getter_AddRefs(lf));
@@ -3878,40 +4200,16 @@ int XREMain::XRE_mainStartup(bool* aExit
   gProfileLock = mProfileLock;
 
   rv = mProfileLock->GetDirectory(getter_AddRefs(mProfD));
   NS_ENSURE_SUCCESS(rv, 1);
 
   rv = mProfileLock->GetLocalDirectory(getter_AddRefs(mProfLD));
   NS_ENSURE_SUCCESS(rv, 1);
 
-  rv = mDirProvider.SetProfile(mProfD, mProfLD);
-  NS_ENSURE_SUCCESS(rv, 1);
-
-  //////////////////////// NOW WE HAVE A PROFILE ////////////////////////
-
-  mozilla::Telemetry::SetProfileDir(mProfD);
-
-  if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER)
-    MakeOrSetMinidumpPath(mProfD);
-
-  CrashReporter::SetProfileDirectory(mProfD);
-
-#ifdef MOZ_ASAN_REPORTER
-  // In ASan reporter builds, we need to set ASan's log_path as early as
-  // possible, so it dumps its errors into files there instead of using
-  // the default stderr location. Since this is crucial for ASan reporter
-  // to work at all (and we don't want people to use a non-functional
-  // ASan reporter build), all failures while setting log_path are fatal.
-  setASanReporterPath(mProfD);
-
-  // Export to env for child processes
-  SaveFileToEnv("ASAN_REPORTER_PATH", mProfD);
-#endif
-
   nsAutoCString version;
   BuildVersion(version);
 
 #ifdef TARGET_OS_ABI
   NS_NAMED_LITERAL_CSTRING(osABI, TARGET_OS_ABI);
 #else
   // No TARGET_XPCOM_ABI, but at least the OS is known
   NS_NAMED_LITERAL_CSTRING(osABI, OS_TARGET "_UNKNOWN");
@@ -3929,19 +4227,57 @@ int XREMain::XRE_mainStartup(bool* aExit
   if (mAppData->directory) {
     Unused << mAppData->directory->Clone(getter_AddRefs(flagFile));
   }
   if (flagFile) {
     flagFile->AppendNative(FILE_INVALIDATE_CACHES);
   }
 
   bool cachesOK;
-  bool versionOK =
-      CheckCompatibility(mProfD, version, osABI, mDirProvider.GetGREDir(),
-                         mAppData->directory, flagFile, &cachesOK);
+  bool isDowngrade;
+  nsCString lastVersion;
+  bool versionOK = CheckCompatibility(
+      mProfD, version, osABI, mDirProvider.GetGREDir(), mAppData->directory,
+      flagFile, &cachesOK, &isDowngrade, lastVersion);
+
+#ifdef MOZ_BLOCK_PROFILE_DOWNGRADE
+  if (isDowngrade && !CheckArg("allow-downgrade")) {
+    rv = CheckDowngrade(mProfD, mProfLD, mProfileName, mNativeApp, mProfileSvc,
+                        lastVersion);
+    if (rv == NS_ERROR_LAUNCHED_CHILD_PROCESS || rv == NS_ERROR_ABORT) {
+      *aExitFlag = true;
+      return 0;
+    }
+  }
+#endif
+
+  rv = mDirProvider.SetProfile(mProfD, mProfLD);
+  NS_ENSURE_SUCCESS(rv, 1);
+
+  //////////////////////// NOW WE HAVE A PROFILE ////////////////////////
+
+  mozilla::Telemetry::SetProfileDir(mProfD);
+
+  if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) {
+    MakeOrSetMinidumpPath(mProfD);
+  }
+
+  CrashReporter::SetProfileDirectory(mProfD);
+
+#ifdef MOZ_ASAN_REPORTER
+  // In ASan reporter builds, we need to set ASan's log_path as early as
+  // possible, so it dumps its errors into files there instead of using
+  // the default stderr location. Since this is crucial for ASan reporter
+  // to work at all (and we don't want people to use a non-functional
+  // ASan reporter build), all failures while setting log_path are fatal.
+  setASanReporterPath(mProfD);
+
+  // Export to env for child processes
+  SaveFileToEnv("ASAN_REPORTER_PATH", mProfD);
+#endif
 
   bool lastStartupWasCrash = CheckLastStartupWasCrash().unwrapOr(false);
 
   if (CheckArg("purgecaches") || PR_GetEnv("MOZ_PURGE_CACHES") ||
       lastStartupWasCrash) {
     cachesOK = false;
   }
 
@@ -4134,28 +4470,16 @@ nsresult XREMain::XRE_mainRun() {
         if (buf[0] == '0' || buf[0] == 'f' || buf[0] == 'F') {
           gDoMigration = false;
         }
       }
     }
   }
 
   {
-    bool profileWasSelected = false;
-    if (gDoProfileReset) {
-      nsCOMPtr<nsIToolkitProfile> defaultProfile;
-      // This can fail if there is no default profile.
-      // That shouldn't stop reset from proceeding.
-      nsresult gotSelected =
-          mProfileSvc->GetSelectedProfile(getter_AddRefs(defaultProfile));
-      if (NS_SUCCEEDED(gotSelected)) {
-        profileWasSelected = defaultProfile == gResetOldProfile;
-      }
-    }
-
     // Profile Migration
     if (mAppData->flags & NS_XRE_ENABLE_PROFILE_MIGRATOR && gDoMigration) {
       gDoMigration = false;
       nsCOMPtr<nsIProfileMigrator> pm(
           do_CreateInstance(NS_PROFILEMIGRATOR_CONTRACTID));
       if (pm) {
         nsAutoCString aKey;
         nsAutoCString aName;
@@ -4165,43 +4489,21 @@ nsresult XREMain::XRE_mainRun() {
           aKey = MOZ_APP_NAME;
           gResetOldProfile->GetName(aName);
         }
         pm->Migrate(&mDirProvider, aKey, aName);
       }
     }
 
     if (gDoProfileReset) {
-      nsresult backupCreated = ProfileResetCleanup(gResetOldProfile);
+      nsresult backupCreated = ProfileResetCleanup(
+          static_cast<nsToolkitProfileService*>(mProfileSvc.get()),
+          gResetOldProfile);
       if (NS_FAILED(backupCreated))
         NS_WARNING("Could not cleanup the profile that was reset");
-
-      nsCOMPtr<nsIToolkitProfile> newProfile;
-      rv = GetCurrentProfile(mProfileSvc, mProfD, getter_AddRefs(newProfile));
-      if (NS_SUCCEEDED(rv)) {
-        nsAutoCString name;
-        gResetOldProfile->GetName(name);
-        newProfile->SetName(name);
-        mProfileName.Assign(name);
-        // Set the new profile as the default after we're done cleaning up the
-        // old profile, iff that profile was already the default
-        if (profileWasSelected) {
-          rv = mProfileSvc->SetDefaultProfile(newProfile);
-          if (NS_FAILED(rv))
-            NS_WARNING("Could not set current profile as the default");
-        }
-      } else {
-        NS_WARNING(
-            "Could not find current profile to set as default / change name.");
-      }
-
-      // Need to write out the fact that the profile has been removed, the new
-      // profile renamed, and potentially that the selected/default profile
-      // changed.
-      mProfileSvc->Flush();
     }
   }
 
 #ifndef XP_WIN
   nsCOMPtr<nsIFile> profileDir;
   nsAutoCString path;
   rv = mDirProvider.GetProfileStartupDir(getter_AddRefs(profileDir));
   if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(profileDir->GetNativePath(path)) &&
@@ -4277,16 +4579,17 @@ nsresult XREMain::XRE_mainRun() {
 
   // clear out any environment variables which may have been set
   // during the relaunch process now that we know we won't be relaunching.
   SaveToEnv("XRE_PROFILE_PATH=");
   SaveToEnv("XRE_PROFILE_LOCAL_PATH=");
   SaveToEnv("XRE_START_OFFLINE=");
   SaveToEnv("XUL_APP_FILE=");
   SaveToEnv("XRE_BINARY_PATH=");
+  SaveToEnv("XRE_RESTARTED_BY_PROFILE_MANAGER=");
 
   if (!mShuttingDown) {
     rv = appStartup->CreateHiddenWindow();
     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
 #ifdef XP_WIN
     Preferences::RegisterCallbackAndCall(RegisterApplicationRestartChanged,
                                          PREF_WIN_REGISTER_APPLICATION_RESTART);
@@ -4385,16 +4688,18 @@ nsresult XREMain::XRE_mainRun() {
   CrashReporter::AnnotateCrashReport(
       CrashReporter::Annotation::ContentSandboxCapabilities, flagsString);
 #endif /* MOZ_SANDBOX && XP_LINUX */
 
 #if defined(MOZ_CONTENT_SANDBOX)
   AddSandboxAnnotations();
 #endif /* MOZ_CONTENT_SANDBOX */
 
+  static_cast<nsToolkitProfileService*>(mProfileSvc.get())->RecordStartupTelemetry();
+
   {
     rv = appStartup->Run();
     if (NS_FAILED(rv)) {
       NS_ERROR("failed to run appstartup");
       gLogConsoleErrors = true;
     }
   }
 
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -1,15 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsAppRunner.h"
 #include "nsXREDirProvider.h"
+#ifndef ANDROID
+#  include "commonupdatedir.h"
+#endif
 
 #include "jsapi.h"
 #include "xpcpublic.h"
 
 #include "nsIAppStartup.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsIFile.h"
 #include "nsIObserver.h"
@@ -1616,17 +1619,17 @@ nsresult nsXREDirProvider::AppendProfile
   nsAutoCString vendor;
   if (gAppData->profile) {
     profile = gAppData->profile;
   } else {
     appName = gAppData->name;
     vendor = gAppData->vendor;
   }
 
-  nsresult rv;
+  nsresult rv = NS_OK;
 
 #if defined(XP_MACOSX)
   if (!profile.IsEmpty()) {
     rv = AppendProfileString(aFile, profile.get());
   } else {
     // Note that MacOS ignores the vendor when creating the profile hierarchy -
     // all application preferences directories live alongside one another in
     // ~/Library/Application Support/
@@ -1678,20 +1681,23 @@ nsresult nsXREDirProvider::AppendProfile
       ToLowerCase(folder);
 
       rv = aFile->AppendNative(folder);
       NS_ENSURE_SUCCESS(rv, rv);
 
       folder.Truncate();
     }
 
-    folder.Append(appName);
-    ToLowerCase(folder);
+    // This can be the case in tests.
+    if (!appName.IsEmpty()) {
+      folder.Append(appName);
+      ToLowerCase(folder);
 
-    rv = aFile->AppendNative(folder);
+      rv = aFile->AppendNative(folder);
+    }
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
 #else
 #  error "Don't know how to get profile path on your platform"
 #endif
   return NS_OK;
 }
--- a/uriloader/base/nsIWebProgressListener.idl
+++ b/uriloader/base/nsIWebProgressListener.idl
@@ -265,40 +265,56 @@ interface nsIWebProgressListener : nsISu
    * These flags describe the reason of cookie jar rejection.
    *
    * STATE_BLOCKED_TRACKING_CONTENT
    *   Tracking content has been blocked from loading.
    *
    * STATE_LOADED_TRACKING_CONTENT
    *   Tracking content has been loaded.
    *
+   * STATE_BLOCKED_FINGERPRINTING_CONTENT
+   *   Fingerprinting content has been blocked from loading.
+   *
+   * STATE_LOADED_FINGERPRINTING_CONTENT
+   *   Fingerprinting content has been loaded.
+   *
+   * STATE_BLOCKED_CRYPTOMINING_CONTENT
+   *   Cryptomining content has been blocked from loading.
+   *
+   * STATE_LOADED_CRYPTOMINING_CONTENT
+   *   Cryptomining content has been loaded.
+   *
    * STATE_BLOCKED_UNSAFE_CONTENT
    *   Content which againts SafeBrowsing list has been blocked from loading.
    *
    * STATE_COOKIES_BLOCKED_BY_PERMISSION
    *   Rejected for custom site permission.
    *
    * STATE_COOKIES_BLOCKED_TRACKER
    *   Rejected because the resource is a tracker and cookie policy doesn't
    *   allow its loading.
    *
    * STATE_COOKIES_BLOCKED_ALL
    *   Rejected because cookie policy blocks all cookies.
    *
    * STATE_COOKIES_BLOCKED_FOREIGN
    *   Rejected because cookie policy blocks 3rd party cookies.
    */
-  const unsigned long STATE_BLOCKED_TRACKING_CONTENT      = 0x00001000;
-  const unsigned long STATE_LOADED_TRACKING_CONTENT       = 0x00002000;
-  const unsigned long STATE_BLOCKED_UNSAFE_CONTENT        = 0x00004000;
-  const unsigned long STATE_COOKIES_LOADED                = 0x00008000;
-  const unsigned long STATE_COOKIES_BLOCKED_BY_PERMISSION = 0x10000000;
-  const unsigned long STATE_COOKIES_BLOCKED_TRACKER       = 0x20000000;
-  const unsigned long STATE_COOKIES_BLOCKED_ALL           = 0x40000000;
-  const unsigned long STATE_COOKIES_BLOCKED_FOREIGN       = 0x00000080;
+  const unsigned long STATE_BLOCKED_TRACKING_CONTENT       = 0x00001000;
+  const unsigned long STATE_LOADED_TRACKING_CONTENT        = 0x00002000;
+  const unsigned long STATE_BLOCKED_FINGERPRINTING_CONTENT = 0x00000040;
+  const unsigned long STATE_LOADED_FINGERPRINTING_CONTENT  = 0x00000400;
+  const unsigned long STATE_BLOCKED_CRYPTOMINING_CONTENT   = 0x00000800;
+  const unsigned long STATE_LOADED_CRYPTOMINING_CONTENT    = 0x00200000;
+  const unsigned long STATE_BLOCKED_UNSAFE_CONTENT         = 0x00004000;
+  const unsigned long STATE_COOKIES_LOADED                 = 0x00008000;
+  const unsigned long STATE_COOKIES_BLOCKED_BY_PERMISSION  = 0x10000000;
+  const unsigned long STATE_COOKIES_BLOCKED_TRACKER        = 0x20000000;
+  const unsigned long STATE_COOKIES_BLOCKED_ALL            = 0x40000000;
+  const unsigned long STATE_COOKIES_BLOCKED_FOREIGN        = 0x00000080;
 
   /**
    * Notification indicating the state has changed for one of the requests
    * associated with aWebProgress.
    *
    * @param aWebProgress
    *        The nsIWebProgress instance that fired the notification
    * @param aRequest
--- a/uriloader/exthandler/nsExternalProtocolHandler.cpp
+++ b/uriloader/exthandler/nsExternalProtocolHandler.cpp
@@ -383,27 +383,28 @@ NS_IMETHODIMP nsExtProtocolChannel::SetP
   // ContentParent::RecvExtProtocolChannelConnectParent.  Setting
   // this flag tells this channel to not proceed and makes AsyncOpen
   // just no-op.  Actual operation will happen from the child process
   // via CompleteRedirectSetup call on the child channel.
   mConnectedParent = true;
   return NS_OK;
 }
 
-NS_IMETHODIMP nsExtProtocolChannel::NotifyTrackingProtectionDisabled() {
+NS_IMETHODIMP nsExtProtocolChannel::NotifyChannelClassifierProtectionDisabled(
+    uint32_t aAcceptedReason) {
   // nothing to do
   return NS_OK;
 }
 
 NS_IMETHODIMP nsExtProtocolChannel::NotifyCookieAllowed() {
   // nothing to do
   return NS_OK;
 }
 
-NS_IMETHODIMP nsExtProtocolChannel::NotifyTrackingCookieBlocked(
+NS_IMETHODIMP nsExtProtocolChannel::NotifyCookieBlocked(
     uint32_t aRejectedReason) {
   // nothing to do
   return NS_OK;
 }
 
 NS_IMETHODIMP nsExtProtocolChannel::SetClassifierMatchedInfo(
     const nsACString &aList, const nsACString &aProvider,
     const nsACString &aFullHash) {
--- a/xpcom/base/ErrorList.py
+++ b/xpcom/base/ErrorList.py
@@ -802,16 +802,18 @@ with modules["URILOADER"]:
     errors["NS_ERROR_MALWARE_URI"] = FAILURE(30)
     errors["NS_ERROR_PHISHING_URI"] = FAILURE(31)
     errors["NS_ERROR_TRACKING_URI"] = FAILURE(34)
     errors["NS_ERROR_UNWANTED_URI"] = FAILURE(35)
     errors["NS_ERROR_BLOCKED_URI"] = FAILURE(37)
     errors["NS_ERROR_HARMFUL_URI"] = FAILURE(38)
     errors["NS_ERROR_MAYBE_TRACKING_URI"] = FAILURE(39)
     errors["NS_ERROR_TRACKING_ANNOTATION_URI"] = FAILURE(40)
+    errors["NS_ERROR_FINGERPRINTING_URI"] = FAILURE(41)
+    errors["NS_ERROR_CRYPTOMINING_URI"] = FAILURE(42)
     # Used when "Save Link As..." doesn't see the headers quickly enough to
     # choose a filename.  See nsContextMenu.js.
     errors["NS_ERROR_SAVE_LINK_AS_TIMEOUT"] = FAILURE(32)
     # Used when the data from a channel has already been parsed and cached so it
     # doesn't need to be reparsed from the original source.
     errors["NS_ERROR_PARSED_DATA_CACHED"] = FAILURE(33)
 
     # This success code indicates that a refresh header was found and
--- a/xpcom/ds/nsTArray.h
+++ b/xpcom/ds/nsTArray.h
@@ -1229,17 +1229,17 @@ class nsTArray_Impl
   // @return       The index of the found element or NoIndex if not found.
   template <class Item, class Comparator>
   index_type BinaryIndexOf(const Item& aItem, const Comparator& aComp) const {
     using mozilla::BinarySearchIf;
     ::detail::CompareWrapper<Comparator, Item> comp(aComp);
 
     size_t index;
     bool found = BinarySearchIf(
-        *this, 0, Length(),
+        Elements(), 0, Length(),
         // Note: We pass the Compare() args here in reverse order and negate the
         // results for compatibility reasons. Some existing callers use Equals()
         // functions with first arguments which match aElement but not aItem, or
         // second arguments that match aItem but not aElement. To accommodate
         // those callers, we preserve the argument order of the older version of
         // this API. These callers, however, should be fixed, and this special
         // case removed.
         [&](const elem_type& aElement) {
@@ -1504,17 +1504,17 @@ class nsTArray_Impl
   // @precondition The array is sorted
   template <class Item, class Comparator>
   index_type IndexOfFirstElementGt(const Item& aItem,
                                    const Comparator& aComp) const {
     using mozilla::BinarySearchIf;
     ::detail::CompareWrapper<Comparator, Item> comp(aComp);
 
     size_t index;
-    BinarySearchIf(*this, 0, Length(),
+    BinarySearchIf(Elements(), 0, Length(),
                    [&](const elem_type& aElement) {
                      return comp.Compare(aElement, aItem) <= 0 ? 1 : -1;
                    },
                    &index);
     return index;
   }
 
   // A variation on the IndexOfFirstElementGt method defined above.
--- a/xpcom/threads/BlockingResourceBase.cpp
+++ b/xpcom/threads/BlockingResourceBase.cpp
@@ -39,17 +39,17 @@ namespace mozilla {
 // static members
 const char* const BlockingResourceBase::kResourceTypeName[] = {
     // needs to be kept in sync with BlockingResourceType
     "Mutex", "ReentrantMonitor", "CondVar", "RecursiveMutex"};
 
 #ifdef DEBUG
 
 PRCallOnceType BlockingResourceBase::sCallOnce;
-unsigned BlockingResourceBase::sResourceAcqnChainFrontTPI = (unsigned)-1;
+MOZ_THREAD_LOCAL(BlockingResourceBase*) BlockingResourceBase::sResourceAcqnChainFront;
 BlockingResourceBase::DDT* BlockingResourceBase::sDeadlockDetector;
 
 void BlockingResourceBase::StackWalkCallback(uint32_t aFrameNumber, void* aPc,
                                              void* aSp, void* aClosure) {
 #  ifndef MOZ_CALLSTACK_DISABLED
   AcquisitionState* state = (AcquisitionState*)aClosure;
   state->AppendElement(aPc);
 #  endif
@@ -225,17 +225,17 @@ BlockingResourceBase::~BlockingResourceB
 size_t BlockingResourceBase::SizeOfDeadlockDetector(
     MallocSizeOf aMallocSizeOf) {
   return sDeadlockDetector
              ? sDeadlockDetector->SizeOfIncludingThis(aMallocSizeOf)
              : 0;
 }
 
 PRStatus BlockingResourceBase::InitStatics() {
-  PR_NewThreadPrivateIndex(&sResourceAcqnChainFrontTPI, 0);
+  MOZ_ASSERT(sResourceAcqnChainFront.init());
   sDeadlockDetector = new DDT();
   if (!sDeadlockDetector) {
     MOZ_CRASH("can't allocate deadlock detector");
   }
   return PR_SUCCESS;
 }
 
 void BlockingResourceBase::Shutdown() {
--- a/xpcom/threads/BlockingResourceBase.h
+++ b/xpcom/threads/BlockingResourceBase.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_BlockingResourceBase_h
 #define mozilla_BlockingResourceBase_h
 
 #include "mozilla/Logging.h"
+#include "mozilla/ThreadLocal.h"
 
 #include "nscore.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsISupportsImpl.h"
 
 #ifdef DEBUG
 
@@ -147,18 +148,17 @@ class BlockingResourceBase {
    * ResourceChainFront
    *
    * Thread safe.
    *
    * @return the front of the resource acquisition chain, i.e., the last
    *         resource acquired.
    */
   static BlockingResourceBase* ResourceChainFront() {
-    return (BlockingResourceBase*)PR_GetThreadPrivate(
-        sResourceAcqnChainFrontTPI);
+    return sResourceAcqnChainFront.get();
   }
 
   /**
    * ResourceChainPrev
    *
    * *NOT* thread safe.  Requires ownership of underlying resource.
    */
   static BlockingResourceBase* ResourceChainPrev(
@@ -170,28 +170,28 @@ class BlockingResourceBase {
    * ResourceChainAppend
    * Set |this| to the front of the resource acquisition chain, and link
    * |this| to |aPrev|.
    *
    * *NOT* thread safe.  Requires ownership of underlying resource.
    */
   void ResourceChainAppend(BlockingResourceBase* aPrev) {
     mChainPrev = aPrev;
-    PR_SetThreadPrivate(sResourceAcqnChainFrontTPI, this);
+    sResourceAcqnChainFront.set(this);
   }  // NS_NEEDS_RESOURCE(this)
 
   /**
    * ResourceChainRemove
    * Remove |this| from the front of the resource acquisition chain.
    *
    * *NOT* thread safe.  Requires ownership of underlying resource.
    */
   void ResourceChainRemove() {
     NS_ASSERTION(this == ResourceChainFront(), "not at chain front");
-    PR_SetThreadPrivate(sResourceAcqnChainFrontTPI, mChainPrev);
+    sResourceAcqnChainFront.set(mChainPrev);
   }  // NS_NEEDS_RESOURCE(this)
 
   /**
    * GetAcquisitionState
    * Return whether or not this resource was acquired.
    *
    * *NOT* thread safe.  Requires ownership of underlying resource.
    */
@@ -275,21 +275,20 @@ class BlockingResourceBase {
   /**
    * sCallOnce
    * Ensures static members are initialized only once, and in a
    * thread-safe way.
    */
   static PRCallOnceType sCallOnce;
 
   /**
-   * sResourceAcqnChainFrontTPI
-   * Thread-private index to the front of each thread's resource
+   * Thread-private pointer to the front of each thread's resource
    * acquisition chain.
    */
-  static unsigned sResourceAcqnChainFrontTPI;
+  static MOZ_THREAD_LOCAL(BlockingResourceBase*) sResourceAcqnChainFront;
 
   /**
    * sDeadlockDetector
    * Does as named.
    */
   static DDT* sDeadlockDetector;
 
   /**