Merge autoland to mozilla-central. a=merge
authorButkovits Atila <abutkovits@mozilla.com>
Fri, 26 Feb 2021 23:43:17 +0200
changeset 568962 cb97abb159426df201455523307964287956f8e0
parent 568888 96cb6ca6f536f5fd18c33994f891cabe01b4ccf8 (current diff)
parent 568961 e2fa99639bbfcbd58d60f6c3212765201792eb08 (diff)
child 568963 50a18a517f904f3c3fe1af471d7e2d9bd11bd367
child 568977 55f1a82bb300006c6ed3302f35c29ec897d36f02
push id38244
push userabutkovits@mozilla.com
push dateFri, 26 Feb 2021 21:45:01 +0000
treeherdermozilla-central@cb97abb15942 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone88.0a1
first release with
nightly linux32
cb97abb15942 / 88.0a1 / 20210226214501 / files
nightly linux64
cb97abb15942 / 88.0a1 / 20210226214501 / files
nightly mac
cb97abb15942 / 88.0a1 / 20210226214501 / files
nightly win32
cb97abb15942 / 88.0a1 / 20210226214501 / files
nightly win64
cb97abb15942 / 88.0a1 / 20210226214501 / 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 autoland to mozilla-central. a=merge
--- a/browser/actors/DecoderDoctorParent.jsm
+++ b/browser/actors/DecoderDoctorParent.jsm
@@ -175,32 +175,25 @@ class DecoderDoctorParent extends JSWind
         return;
       }
 
       let buttons = [];
       let sumo = this.getSumoForLearnHowButton(type);
       if (sumo) {
         buttons.push({
           label: gNavigatorBundle.GetStringFromName("decoder.noCodecs.button"),
-          accessKey: gNavigatorBundle.GetStringFromName(
-            "decoder.noCodecs.accesskey"
-          ),
+          supportPage: sumo,
           callback() {
             let clickedInPref = Services.prefs.getBoolPref(
               buttonClickedPref,
               false
             );
             if (!clickedInPref) {
               Services.prefs.setBoolPref(buttonClickedPref, true);
             }
-
-            let baseURL = Services.urlFormatter.formatURLPref(
-              "app.support.baseURL"
-            );
-            window.openTrustedLinkIn(baseURL + sumo, "tab");
           },
         });
       }
       let endpoint = this.getEndpointForReportIssueButton(type);
       if (endpoint) {
         buttons.push({
           label: gNavigatorBundle.GetStringFromName(
             "decoder.decodeError.button"
--- a/browser/actors/EncryptedMediaParent.jsm
+++ b/browser/actors/EncryptedMediaParent.jsm
@@ -7,22 +7,16 @@
 
 var EXPORTED_SYMBOLS = ["EncryptedMediaParent"];
 
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
 
-ChromeUtils.defineModuleGetter(
-  this,
-  "BrowserUIUtils",
-  "resource:///modules/BrowserUIUtils.jsm"
-);
-
 XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
   return Services.strings.createBundle(
     "chrome://branding/locale/brand.properties"
   );
 });
 
 XPCOMUtils.defineLazyGetter(this, "gNavigatorBundle", function() {
   return Services.strings.createBundle(
@@ -56,31 +50,16 @@ class EncryptedMediaParent extends JSWin
       aKeySystem == "com.widevine.alpha" &&
       Services.prefs.getPrefType("media.gmp-widevinecdm.visible")
     ) {
       return Services.prefs.getBoolPref("media.gmp-widevinecdm.visible");
     }
     return true;
   }
 
-  getEMEDisabledFragment(aBrowser) {
-    let mainMessage = gNavigatorBundle.GetStringFromName(
-      "emeNotifications.drmContentDisabled.message"
-    );
-    let text = gNavigatorBundle.GetStringFromName(
-      "emeNotifications.drmContentDisabled.learnMoreLabel"
-    );
-    let document = aBrowser.ownerDocument;
-    let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
-    let link = document.createXULElement("label", { is: "text-link" });
-    link.setAttribute("href", baseURL + "drm-content");
-    link.textContent = text;
-    return BrowserUIUtils.getLocalizedFragment(document, mainMessage, link);
-  }
-
   getMessageWithBrandName(aNotificationId) {
     let msgId = "emeNotifications." + aNotificationId + ".message";
     return gNavigatorBundle.formatStringFromName(msgId, [
       gBrandBundle.GetStringFromName("brandShortName"),
     ]);
   }
 
   receiveMessage(aMessage) {
@@ -112,16 +91,17 @@ class EncryptedMediaParent extends JSWin
 
     // Don't need to show UI if disabled.
     if (!this.isUiEnabled()) {
       return;
     }
 
     let notificationId;
     let buttonCallback;
+    let supportPage;
     // Notification message can be either a string or a DOM fragment.
     let notificationMessage;
     switch (status) {
       case "available":
       case "cdm-created":
         // Only show the chain icon for proprietary CDMs. Clearkey is not one.
         if (keySystem != "org.w3.clearkey") {
           this.showPopupNotificationForSuccess(browser, keySystem);
@@ -130,17 +110,20 @@ class EncryptedMediaParent extends JSWin
         return;
 
       case "api-disabled":
       case "cdm-disabled":
         notificationId = "drmContentDisabled";
         buttonCallback = () => {
           this.ensureEMEEnabled(browser, keySystem);
         };
-        notificationMessage = this.getEMEDisabledFragment(browser);
+        notificationMessage = gNavigatorBundle.GetStringFromName(
+          "emeNotifications.drmContentDisabled.message2"
+        );
+        supportPage = "drm-content";
         break;
 
       case "cdm-not-installed":
         notificationId = "drmContentCDMInstalling";
         notificationMessage = this.getMessageWithBrandName(notificationId);
         break;
 
       case "cdm-not-supported":
@@ -162,16 +145,19 @@ class EncryptedMediaParent extends JSWin
     // Now actually create the notification
 
     let notificationBox = browser.getTabBrowser().getNotificationBox(browser);
     if (notificationBox.getNotificationWithValue(notificationId)) {
       return;
     }
 
     let buttons = [];
+    if (supportPage) {
+      buttons.push({ supportPage });
+    }
     if (buttonCallback) {
       let msgPrefix = "emeNotifications." + notificationId + ".";
       let btnLabelId = msgPrefix + "button.label";
       let btnAccessKeyId = msgPrefix + "button.accesskey";
       buttons.push({
         label: gNavigatorBundle.GetStringFromName(btnLabelId),
         accessKey: gNavigatorBundle.GetStringFromName(btnAccessKeyId),
         callback: buttonCallback,
--- a/browser/actors/PromptParent.jsm
+++ b/browser/actors/PromptParent.jsm
@@ -286,18 +286,19 @@ class PromptParent extends JSWindowActor
 
       args.promptAborted = false;
       args.openedWithTabDialog = true;
 
       // Convert args object to a prop bag for the dialog to consume.
       let bag;
 
       if (
-        args.modalType === Services.prompt.MODAL_TYPE_TAB ||
-        args.modalType === Services.prompt.MODAL_TYPE_CONTENT
+        (args.modalType === Services.prompt.MODAL_TYPE_TAB ||
+          args.modalType === Services.prompt.MODAL_TYPE_CONTENT) &&
+        win?.gBrowser?.getTabDialogBox
       ) {
         if (!browser) {
           let modal_type =
             args.modalType === Services.prompt.MODAL_TYPE_TAB
               ? "tab"
               : "content";
           throw new Error(`Cannot ${modal_type}-prompt without a browser!`);
         }
@@ -314,16 +315,19 @@ class PromptParent extends JSWindowActor
           {
             features: "resizable=no",
             modalType: args.modalType,
             allowFocusCheckbox: args.allowFocusCheckbox,
           },
           bag
         );
       } else {
+        // Ensure we set the correct modal type at this point.
+        // If we use window prompts as a fallback it may not be set.
+        args.modalType = Services.prompt.MODAL_TYPE_WINDOW;
         // Window prompt
         bag = PromptUtils.objectToPropBag(args);
         Services.ww.openWindow(
           win,
           uri,
           "_blank",
           "centerscreen,chrome,modal,titlebar",
           bag
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -286,19 +286,21 @@
       <menuitem id="context-selectall"
                 data-l10n-id="text-action-select-all"
                 command="cmd_selectAll"/>
       <menuseparator id="context-sep-selectall"/>
       <menuitem id="context-keywordfield"
                 data-l10n-id="main-context-menu-keyword"
                 oncommand="AddKeywordForSearchField();"/>
       <menuitem id="context-searchselect"
-                oncommand="BrowserSearch.loadSearchFromContext(this.searchTerms, this.usePrivate, this.principal, this.csp);"/>
+                oncommand="BrowserSearch.loadSearchFromContext(this.searchTerms, this.usePrivate, this.principal, this.csp, event);"
+                onclick="checkForMiddleClick(this, event);"/>
       <menuitem id="context-searchselect-private"
-                oncommand="BrowserSearch.loadSearchFromContext(this.searchTerms, true, this.principal, this.csp);"/>
+                oncommand="BrowserSearch.loadSearchFromContext(this.searchTerms, true, this.principal, this.csp, event);"
+                onclick="checkForMiddleClick(this, event);"/>
       <menuseparator id="context-sep-sendlinktodevice" class="sync-ui-item"
                      hidden="true"/>
       <menu id="context-sendlinktodevice"
             class="sync-ui-item"
             data-l10n-id="main-context-menu-link-send-to-device"
             hidden="true">
         <menupopup id="context-sendlinktodevice-popup"
                    onpopupshowing="gSync.populateSendTabToDevicesMenu(event.target, gContextMenu.linkURL, gContextMenu.linkTextStr);"/>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -963,28 +963,18 @@ const gStoragePressureObserver = {
 
     const BYTES_IN_GIGABYTE = 1073741824;
     const USAGE_THRESHOLD_BYTES =
       BYTES_IN_GIGABYTE *
       Services.prefs.getIntPref(
         "browser.storageManager.pressureNotification.usageThresholdGB"
       );
     let msg = "";
-    let buttons = [];
+    let buttons = [{ supportPage: "storage-permissions" }];
     let usage = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
-    buttons.push({
-      "l10n-id": "space-alert-learn-more-button",
-      callback(notificationBar, button) {
-        let learnMoreURL =
-          Services.urlFormatter.formatURLPref("app.support.baseURL") +
-          "storage-permissions";
-        // This is a content URL, loaded from trusted UX.
-        openTrustedLinkIn(learnMoreURL, "tab");
-      },
-    });
     if (usage < USAGE_THRESHOLD_BYTES) {
       // The firefox-used space < 5GB, then warn user to free some disk space.
       // This is because this usage is small and not the main cause for space issue.
       // In order to avoid the bad and wrong impression among users that
       // firefox eats disk space a lot, indicate users to clean up other disk space.
       [msg] = await document.l10n.formatValues([
         { id: "space-alert-under-5gb-message" },
       ]);
@@ -4254,31 +4244,35 @@ const BrowserSearch = {
    * @param purpose [optional]
    *        A string meant to indicate the context of the search request. This
    *        allows the search service to provide a different nsISearchSubmission
    *        depending on e.g. where the search is triggered in the UI.
    * @param triggeringPrincipal
    *        The principal to use for a new window or tab.
    * @param csp
    *        The content security policy to use for a new window or tab.
+   * @param flipLoadInBackground [optional]
+   *        If a modifier/middle mouse button is used:
+   *        Flip preference to load search in background tab.
    * @param engine [optional]
    *        The search engine to use for the search.
    * @param tab [optional]
    *        The tab to show the search result.
    *
    * @return engine The search engine used to perform a search, or null if no
    *                search was performed.
    */
   async _loadSearch(
     searchText,
     where,
     usePrivate,
     purpose,
     triggeringPrincipal,
     csp,
+    flipLoadInBackground = false,
     engine = null,
     tab = null
   ) {
     if (!triggeringPrincipal) {
       throw new Error(
         "Required argument triggeringPrincipal missing within _loadSearch"
       );
     }
@@ -4301,45 +4295,62 @@ const BrowserSearch = {
 
     let inBackground = Services.prefs.getBoolPref(
       "browser.search.context.loadInBackground"
     );
 
     openLinkIn(submission.uri.spec, where || "current", {
       private: usePrivate && !PrivateBrowsingUtils.isWindowPrivate(window),
       postData: submission.postData,
-      inBackground,
+      inBackground: flipLoadInBackground ? !inBackground : inBackground,
       relatedToCurrent: true,
       triggeringPrincipal,
       csp,
       targetBrowser: tab?.linkedBrowser,
     });
 
     return { engine, url: submission.uri };
   },
 
   /**
    * Perform a search initiated from the context menu.
    *
    * This should only be called from the context menu. See
    * BrowserSearch.loadSearch for the preferred API.
    */
-  async loadSearchFromContext(terms, usePrivate, triggeringPrincipal, csp) {
+  async loadSearchFromContext(
+    terms,
+    usePrivate,
+    triggeringPrincipal,
+    csp,
+    event
+  ) {
+    event = getRootEvent(event);
+    let where = whereToOpenLink(event);
+    if (where == "current") {
+      // override: historically search opens in new tab
+      where = "tab";
+    }
+    if (usePrivate && !PrivateBrowsingUtils.isWindowPrivate(window)) {
+      where = "window";
+    }
+    let flipLoadInBackground = event.button == 1 || event.ctrlKey;
+
     let { engine, url } = await BrowserSearch._loadSearch(
       terms,
-      usePrivate && !PrivateBrowsingUtils.isWindowPrivate(window)
-        ? "window"
-        : "tab",
+      where,
       usePrivate,
       "contextmenu",
       Services.scriptSecurityManager.createNullPrincipal(
         triggeringPrincipal.originAttributes
       ),
-      csp
+      csp,
+      flipLoadInBackground
     );
+
     if (engine) {
       BrowserSearchTelemetry.recordSearch(
         gBrowser.selectedBrowser,
         engine,
         "contextmenu",
         { url }
       );
     }
@@ -4373,16 +4384,17 @@ const BrowserSearch = {
   async loadSearchFromExtension(terms, engine, tab, triggeringPrincipal) {
     const result = await BrowserSearch._loadSearch(
       terms,
       tab ? "current" : "tab",
       PrivateBrowsingUtils.isWindowPrivate(window),
       "webextension",
       triggeringPrincipal,
       null,
+      false,
       engine,
       tab
     );
 
     BrowserSearchTelemetry.recordSearch(
       gBrowser.selectedBrowser,
       result.engine,
       "webextension",
--- a/browser/base/content/test/general/browser_storagePressure_notification.js
+++ b/browser/base/content/test/general/browser_storagePressure_notification.js
@@ -110,17 +110,17 @@ add_task(async function() {
   let notification = gHighPriorityNotificationBox.getNotificationWithValue(
     "storage-pressure-notification"
   );
   ok(
     notification instanceof XULElement,
     "Should display storage pressure notification"
   );
 
-  let prefBtn = notification.getElementsByTagName("button")[1];
+  let prefBtn = notification.getElementsByTagName("button")[0];
   let aboutPrefPromise = openAboutPrefPromise();
   prefBtn.doCommand();
   await aboutPrefPromise;
   let aboutPrefTab = gBrowser.selectedTab;
   let prefDoc = gBrowser.selectedBrowser.contentDocument;
   let siteDataGroup = prefDoc.getElementById("siteDataGroup");
   is_element_visible(
     siteDataGroup,
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -3090,39 +3090,19 @@ BrowserGlue.prototype = {
     var applicationName = gBrandBundle.GetStringFromName("brandShortName");
     var placesBundle = Services.strings.createBundle(
       "chrome://browser/locale/places/places.properties"
     );
     var title = placesBundle.GetStringFromName("lockPrompt.title");
     var text = placesBundle.formatStringFromName("lockPrompt.text", [
       applicationName,
     ]);
-    var buttonText = placesBundle.GetStringFromName(
-      "lockPromptInfoButton.label"
-    );
-    var accessKey = placesBundle.GetStringFromName(
-      "lockPromptInfoButton.accessKey"
-    );
-
-    var helpTopic = "places-locked";
-    var url = Services.urlFormatter.formatURLPref("app.support.baseURL");
-    url += helpTopic;
 
     var win = BrowserWindowTracker.getTopWindow();
-
-    var buttons = [
-      {
-        label: buttonText,
-        accessKey,
-        popup: null,
-        callback(aNotificationBar, aButton) {
-          win.openTrustedLinkIn(url, "tab");
-        },
-      },
-    ];
+    var buttons = [{ supportPage: "places-locked" }];
 
     var notifyBox = win.gBrowser.getNotificationBox();
     var notification = notifyBox.appendNotification(
       text,
       title,
       null,
       notifyBox.PRIORITY_CRITICAL_MEDIUM,
       buttons
@@ -4064,25 +4044,22 @@ BrowserGlue.prototype = {
       productName,
     ]);
     let buttons = [
       {
         label: win.gNavigatorBundle.getString("flashHang.helpButton.label"),
         accessKey: win.gNavigatorBundle.getString(
           "flashHang.helpButton.accesskey"
         ),
-        callback() {
-          win.openTrustedLinkIn(
-            "https://support.mozilla.org/kb/flash-protected-mode-autodisabled",
-            "tab"
-          );
-        },
+        link:
+          "https://support.mozilla.org/kb/flash-protected-mode-autodisabled",
       },
     ];
 
+    // XXXndeakin is this notification still relevant?
     win.gNotificationBox.appendNotification(
       message,
       "flash-hang",
       null,
       win.gNotificationBox.PRIORITY_INFO_MEDIUM,
       buttons
     );
   },
copy from toolkit/components/passwordmgr/test/browser/authenticate.sjs
copy to browser/components/extensions/test/browser/authenticate.sjs
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -193,16 +193,19 @@ skip-if = !e10s || !crashreporter
 [browser_ext_sessions_window_tab_value.js]
 skip-if = debug # Bug 1394984 disable debug builds on all platforms
 [browser_ext_settings_overrides_default_search.js]
 [browser_ext_sidebarAction.js]
 [browser_ext_sidebarAction_browser_style.js]
 [browser_ext_sidebarAction_click.js]
 [browser_ext_sidebarAction_context.js]
 [browser_ext_sidebarAction_contextMenu.js]
+[browser_ext_sidebarAction_httpAuth.js]
+support-files =
+  authenticate.sjs
 [browser_ext_sidebarAction_incognito.js]
 skip-if = true # Bug 1575369
 [browser_ext_sidebarAction_runtime.js]
 [browser_ext_sidebarAction_tabs.js]
 [browser_ext_sidebarAction_windows.js]
 [browser_ext_simple.js]
 [browser_ext_slow_script.js]
 skip-if = !e10s || debug || asan || (os == "win" && processor == "aarch64") # windows10-aarch64 due to 1534857
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction_httpAuth.js
@@ -0,0 +1,72 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { PromptTestUtils } = ChromeUtils.import(
+  "resource://testing-common/PromptTestUtils.jsm"
+);
+
+add_task(async function sidebar_httpAuthPrompt() {
+  let data = {
+    manifest: {
+      permissions: ["https://example.com/*"],
+      sidebar_action: {
+        default_panel: "sidebar.html",
+      },
+    },
+    useAddonManager: "temporary",
+    files: {
+      "sidebar.html": `
+        <!DOCTYPE html>
+        <html>
+        <head><meta charset="utf-8"/>
+        <script src="sidebar.js"></script>
+        </head>
+        <body>
+        A Test Sidebar
+        </body></html>
+      `,
+      "sidebar.js": function() {
+        fetch(
+          "https://example.com/browser/browser/components/extensions/test/browser/authenticate.sjs?user=user&pass=pass",
+          { credentials: "include" }
+        ).then(response => {
+          browser.test.sendMessage("fetchResult", response.ok);
+        });
+      },
+    },
+  };
+
+  // Wait for the http auth prompt and close it with accept button.
+  let promptPromise = PromptTestUtils.handleNextPrompt(
+    SidebarUI.browser.contentWindow,
+    {
+      modalType: Services.prompt.MODAL_TYPE_WINDOW,
+      promptType: "promptUserAndPass",
+    },
+    { buttonNumClick: 0, loginInput: "user", passwordInput: "pass" }
+  );
+
+  let extension = ExtensionTestUtils.loadExtension(data);
+  await extension.startup();
+  let fetchResultPromise = extension.awaitMessage("fetchResult");
+
+  await promptPromise;
+  ok(true, "Extension fetch should trigger auth prompt.");
+
+  let responseOk = await fetchResultPromise;
+  ok(responseOk, "Login should succeed.");
+
+  await extension.unload();
+
+  // Cleanup
+  await new Promise(resolve =>
+    Services.clearData.deleteData(
+      Ci.nsIClearDataService.CLEAR_AUTH_CACHE,
+      resolve
+    )
+  );
+});
--- a/browser/components/newtab/.eslintrc.js
+++ b/browser/components/newtab/.eslintrc.js
@@ -33,17 +33,16 @@ module.exports = {
     "plugin:mozilla/xpcshell-test",
     "plugin:prettier/recommended", // require("eslint-plugin-prettier")
     "prettier/react", // require("eslint-config-prettier")
   ],
   overrides: [
     {
       // These files use fluent-dom to insert content
       files: [
-        "content-src/aboutwelcome/components/HeroText.jsx",
         "content-src/aboutwelcome/components/Zap.jsx",
         "content-src/aboutwelcome/components/MultiStageAboutWelcome.jsx",
         "content-src/aboutwelcome/components/ReturnToAMO.jsx",
         "content-src/asrouter/templates/OnboardingMessage/**",
         "content-src/asrouter/templates/FirstRun/**",
         "content-src/components/TopSites/**",
         "content-src/components/MoreRecommendations/MoreRecommendations.jsx",
         "content-src/components/CollapsibleSection/CollapsibleSection.jsx",
--- a/browser/components/newtab/aboutwelcome/content/aboutwelcome.bundle.js
+++ b/browser/components/newtab/aboutwelcome/content/aboutwelcome.bundle.js
@@ -96,39 +96,36 @@
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
 /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
-/* harmony import */ var _components_SimpleAboutWelcome__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8);
-/* harmony import */ var _components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(12);
-/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(6);
+/* harmony import */ var _components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8);
+/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6);
 function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
 
 /* 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/. */
 
 
 
 
 
 
-
 class AboutWelcome extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
   constructor(props) {
     super(props);
     this.state = {
       metricsFlowUri: null
     };
     this.fetchFxAFlowUri = this.fetchFxAFlowUri.bind(this);
-    this.handleStartBtnClick = this.handleStartBtnClick.bind(this);
   }
 
   async fetchFxAFlowUri() {
     this.setState({
       metricsFlowUri: await window.AWGetFxAMetricsFlowURI()
     });
   }
 
@@ -165,48 +162,23 @@ class AboutWelcome extends react__WEBPAC
       });
     } // Captures user has seen about:welcome by setting
     // firstrun.didSeeAboutWelcome pref to true and capturing welcome UI unique messageId
 
 
     window.AWSendToParent("SET_WELCOME_MESSAGE_SEEN", this.props.messageId);
   }
 
-  handleStartBtnClick() {
-    _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_5__["AboutWelcomeUtils"].handleUserAction(this.props.startButton.action);
-    const ping = {
-      event: "CLICK_BUTTON",
-      event_context: {
-        source: this.props.startButton.message_id,
-        page: "about:welcome"
-      },
-      message_id: this.props.messageId,
-      id: "ABOUT_WELCOME"
-    };
-    window.AWSendEventTelemetry(ping);
-  }
-
   render() {
     const {
       props
     } = this;
 
-    if (props.template === "simplified") {
-      return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_SimpleAboutWelcome__WEBPACK_IMPORTED_MODULE_3__["SimpleAboutWelcome"], {
-        metricsFlowUri: this.state.metricsFlowUri,
-        message_id: props.messageId,
-        utm_term: props.UTMTerm,
-        title: props.title,
-        subtitle: props.subtitle,
-        cards: props.cards,
-        startButton: props.startButton,
-        handleStartBtnClick: this.handleStartBtnClick
-      });
-    } else if (props.template === "return_to_amo") {
-      return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_4__["ReturnToAMO"], {
+    if (props.template === "return_to_amo") {
+      return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_3__["ReturnToAMO"], {
         message_id: props.messageId,
         name: props.name,
         url: props.url,
         iconURL: props.iconURL
       });
     }
 
     return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_2__["MultiStageAboutWelcome"], {
@@ -214,17 +186,17 @@ class AboutWelcome extends react__WEBPAC
       metricsFlowUri: this.state.metricsFlowUri,
       message_id: props.messageId,
       utm_term: props.UTMTerm
     });
   }
 
 }
 
-AboutWelcome.defaultProps = _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_5__["DEFAULT_WELCOME_CONTENT"]; // Computes messageId and UTMTerm info used in telemetry
+AboutWelcome.defaultProps = _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_4__["DEFAULT_WELCOME_CONTENT"]; // Computes messageId and UTMTerm info used in telemetry
 
 function ComputeTelemetryInfo(welcomeContent, experimentId, branchId) {
   let messageId = welcomeContent.template === "return_to_amo" ? "RTAMO_DEFAULT_WELCOME" : "DEFAULT_ABOUTWELCOME";
   let UTMTerm = "default";
 
   if (welcomeContent.id) {
     messageId = welcomeContent.id.toUpperCase();
   }
@@ -1182,250 +1154,16 @@ function addUtmParams(url, utmTerm) {
 }
 
 /***/ }),
 /* 8 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SimpleAboutWelcome", function() { return SimpleAboutWelcome; });
-/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
-/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
-/* harmony import */ var _HeroText__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
-/* harmony import */ var _FxCards__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10);
-/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(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/. */
-
-
-
-
-class SimpleAboutWelcome extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
-  render() {
-    const {
-      props
-    } = this;
-    return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
-      className: "outer-wrapper welcomeContainer"
-    }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
-      className: "welcomeContainerInner"
-    }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("main", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_HeroText__WEBPACK_IMPORTED_MODULE_1__["HeroText"], {
-      title: props.title,
-      subtitle: props.subtitle
-    }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_FxCards__WEBPACK_IMPORTED_MODULE_2__["FxCards"], {
-      cards: props.cards,
-      metricsFlowUri: this.props.metricsFlowUri,
-      sendTelemetry: window.AWSendEventTelemetry,
-      utm_term: this.props.UTMTerm
-    }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_3__["Localized"], {
-      text: props.startButton.label
-    }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("button", {
-      className: "start-button",
-      onClick: this.props.handleStartBtnClick
-    })))));
-  }
-
-}
-
-/***/ }),
-/* 9 */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HeroText", function() { return HeroText; });
-/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
-/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
-/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(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/. */
-
-
-const HeroText = props => {
-  return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react__WEBPACK_IMPORTED_MODULE_0___default.a.Fragment, null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], {
-    text: props.title
-  }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h1", {
-    className: "welcome-title"
-  })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], {
-    text: props.subtitle
-  }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h2", {
-    className: "welcome-subtitle"
-  })));
-};
-
-/***/ }),
-/* 10 */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FxCards", function() { return FxCards; });
-/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
-/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
-/* harmony import */ var _asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
-/* harmony import */ var _asrouter_templates_OnboardingMessage_OnboardingMessage__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(11);
-/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6);
-function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
-
-/* 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/. */
-
-
-
-
-class FxCards extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
-  constructor(props) {
-    super(props);
-    this.state = {
-      flowParams: null
-    };
-    this.fetchFxAFlowParams = this.fetchFxAFlowParams.bind(this);
-    this.onCardAction = this.onCardAction.bind(this);
-  }
-
-  componentDidUpdate() {
-    this.fetchFxAFlowParams();
-  }
-
-  componentDidMount() {
-    this.fetchFxAFlowParams();
-  }
-
-  async fetchFxAFlowParams() {
-    if (this.state.flowParams || !this.props.metricsFlowUri) {
-      return;
-    }
-
-    const flowParams = await _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_3__["AboutWelcomeUtils"].fetchFlowParams(this.props.metricsFlowUri);
-    this.setState({
-      flowParams
-    });
-  }
-
-  onCardAction(action) {
-    let {
-      type,
-      data
-    } = action;
-    let UTMTerm = `aboutwelcome-${this.props.utm_term}-card`;
-
-    if (action.type === "OPEN_URL") {
-      let url = new URL(action.data.args);
-      Object(_asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_1__["addUtmParams"])(url, UTMTerm);
-
-      if (action.addFlowParams && this.state.flowParams) {
-        url.searchParams.append("device_id", this.state.flowParams.deviceId);
-        url.searchParams.append("flow_id", this.state.flowParams.flowId);
-        url.searchParams.append("flow_begin_time", this.state.flowParams.flowBeginTime);
-      }
-
-      data = { ...data,
-        args: url.toString()
-      };
-    }
-
-    _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_3__["AboutWelcomeUtils"].handleUserAction({
-      type,
-      data
-    });
-  }
-
-  render() {
-    const {
-      props
-    } = this;
-    return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react__WEBPACK_IMPORTED_MODULE_0___default.a.Fragment, null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
-      className: `welcomeCardGrid show`
-    }, props.cards.map(card => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_asrouter_templates_OnboardingMessage_OnboardingMessage__WEBPACK_IMPORTED_MODULE_2__["OnboardingCard"], _extends({
-      key: card.id,
-      message: card,
-      className: "welcomeCard",
-      sendUserActionTelemetry: props.sendTelemetry,
-      onAction: this.onCardAction,
-      UISurface: "ABOUT_WELCOME"
-    }, card)))));
-  }
-
-}
-
-/***/ }),
-/* 11 */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "OnboardingCard", function() { return OnboardingCard; });
-/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
-/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
-/* harmony import */ var _aboutwelcome_components_MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(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/. */
-
-
-class OnboardingCard extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
-  constructor(props) {
-    super(props);
-    this.onClick = this.onClick.bind(this);
-  }
-
-  onClick() {
-    const {
-      props
-    } = this;
-    const ping = {
-      event: "CLICK_BUTTON",
-      message_id: props.id,
-      id: props.UISurface
-    };
-    props.sendUserActionTelemetry(ping);
-    props.onAction(props.content.primary_button.action, props.message);
-  }
-
-  render() {
-    const {
-      content
-    } = this.props;
-    const className = this.props.className || "onboardingMessage";
-    return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
-      className: className
-    }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
-      className: `onboardingMessageImage ${content.icon}`
-    }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
-      className: "onboardingContent"
-    }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("span", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_aboutwelcome_components_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], {
-      text: content.title
-    }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h2", {
-      className: "onboardingTitle"
-    })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_aboutwelcome_components_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], {
-      text: content.text
-    }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("p", {
-      className: "onboardingText"
-    }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("span", {
-      className: "onboardingButtonContainer"
-    }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_aboutwelcome_components_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], {
-      text: content.primary_button.label
-    }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("button", {
-      className: "button onboardingButton",
-      onClick: this.onClick
-    })))));
-  }
-
-}
-
-/***/ }),
-/* 12 */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ReturnToAMO", function() { return ReturnToAMO; });
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6);
 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(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/. */
--- a/browser/components/newtab/aboutwelcome/content/aboutwelcome.css
+++ b/browser/components/newtab/aboutwelcome/content/aboutwelcome.css
@@ -20,89 +20,32 @@ input {
   background-color: inherit;
   color: inherit;
   font-family: inherit;
   font-size: inherit; }
 
 [hidden] {
   display: none !important; }
 
-.onboardingMessageImage.addons {
-  background-image: url("chrome://activity-stream/content/data/content/assets/illustration-addons@2x.png"); }
-
-.onboardingMessageImage.privatebrowsing {
-  background-image: url("chrome://activity-stream/content/data/content/assets/illustration-privatebrowsing@2x.png"); }
-
-.onboardingMessageImage.screenshots {
-  background-image: url("chrome://activity-stream/content/data/content/assets/illustration-screenshots@2x.png"); }
-
-.onboardingMessageImage.gift {
-  background-image: url("chrome://activity-stream/content/data/content/assets/illustration-gift@2x.png"); }
-
-.onboardingMessageImage.sync {
-  background-image: url("chrome://activity-stream/content/data/content/assets/illustration-sync@2x.png"); }
-
-.onboardingMessageImage.devices {
-  background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-devices.svg"); }
-
-.onboardingMessageImage.fbcont {
-  background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-fbcont.svg"); }
-
-.onboardingMessageImage.import {
-  background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-import.svg"); }
-
-.onboardingMessageImage.ffmonitor {
-  background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-ffmonitor.svg"); }
-
-.onboardingMessageImage.ffsend {
-  background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-ffsend.svg"); }
-
-.onboardingMessageImage.lockwise {
-  background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-lockwise.svg"); }
-
-.onboardingMessageImage.mobile {
-  background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-mobile.svg"); }
-
-.onboardingMessageImage.pledge {
-  background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-pledge.svg"); }
-
-.onboardingMessageImage.pocket {
-  background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-pocket.svg"); }
-
-.onboardingMessageImage.private {
-  background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-private.svg"); }
-
-.onboardingMessageImage.sendtab {
-  background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-sendtab.svg"); }
-
-.onboardingMessageImage.tracking {
-  background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-tracking.svg"); }
-
 html {
   height: 100%; }
 
 body {
-  --grey-subtitle: #4A4A4F;
   --grey-subtitle-1: #696977;
   --newtab-background-color: #EDEDF0;
   --newtab-background-color-1: #F9F9FA;
   --newtab-text-primary-color: #0C0C0D;
   --newtab-text-conditional-color: #4A4A4F;
   --newtab-button-primary-color: #0060DF;
   --newtab-button-secondary-color: #0060DF;
   --newtab-card-background-color: #FFF;
   --newtab-card-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.4);
   --tiles-theme-section-border-width: 1px;
-  --welcome-header-text-color: #2B2156;
   --welcome-header-text-color-1: #20133A;
-  --welcome-card-button-background-color: rgba(12, 12, 13, 0.1);
-  --welcome-card-button-background-hover-color: rgba(12, 12, 13, 0.2);
-  --welcome-card-button-background-active-color: rgba(12, 12, 13, 0.3);
   --welcome-button-box-shadow-color: #0A84FF;
-  --welcome-button-box-shadow-inset-color: rgba(10, 132, 255, 0.3);
   --welcome-button-text-color: #FFF;
   --welcome-button-background-hover-color: #003EAA;
   --welcome-button-background-active-color: #002275;
   --about-welcome-media-fade: linear-gradient(transparent, transparent 35%, #F9F9FA, #F9F9FA);
   font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Ubuntu', 'Helvetica Neue', sans-serif;
   font-size: 16px;
   position: relative;
   /* these two rules fix test failures in
@@ -114,160 +57,20 @@ body {
     --newtab-background-color-1: #1D1133;
     --newtab-text-primary-color: #F9F9FA;
     --newtab-text-conditional-color: #F9F9FA;
     --grey-subtitle-1: #FFF;
     --newtab-button-primary-color: #0060DF;
     --newtab-button-secondary-color: #FFF;
     --newtab-card-background-color: #38383D;
     --newtab-card-shadow: 0 1px 8px 0 rgba(12, 12, 13, 0.4);
-    --welcome-header-text-color: rgba(255, 255, 255, 0.6);
     --welcome-header-text-color-1: #7542E5;
-    --welcome-card-button-background-color: rgba(12, 12, 13, 0.3);
-    --welcome-card-button-background-hover-color: rgba(12, 12, 13, 0.5);
-    --welcome-card-button-background-active-color: rgba(12, 12, 13, 0.7);
     --welcome-button-box-shadow-color: #0A84FF;
     --about-welcome-media-fade: linear-gradient(transparent, transparent 35%, #1D1133, #1D1133); }
 
-.welcomeCardGrid {
-  margin: 0;
-  margin-top: 32px;
-  display: grid;
-  grid-gap: 32px;
-  transition: opacity 0.4s;
-  transition-delay: 0.1s;
-  grid-auto-rows: 1fr; }
-  @media (min-width: 610px) {
-    .welcomeCardGrid {
-      grid-template-columns: repeat(auto-fit, 224px); } }
-  @media (min-width: 1122px) {
-    .welcomeCardGrid {
-      grid-template-columns: repeat(auto-fit, 309px); } }
-
-.welcomeContainer {
-  text-align: center; }
-  @media (min-width: 610px) {
-    .welcomeContainer {
-      max-height: 1000px; } }
-  .welcomeContainer h1 {
-    font-size: 36px;
-    font-weight: 200;
-    margin: 0 0 40px;
-    color: var(--welcome-header-text-color); }
-  .welcomeContainer .welcome-title {
-    margin-bottom: 5px;
-    line-height: 52px; }
-  .welcomeContainer .welcome-subtitle {
-    font-size: 28px;
-    font-weight: 200;
-    margin: 6px 0 0;
-    color: var(--grey-subtitle);
-    line-height: 42px; }
-
-.welcomeContainerInner {
-  margin: auto;
-  padding: 40px 25px; }
-  @media (min-width: 610px) {
-    .welcomeContainerInner {
-      width: 530px; } }
-  @media (min-width: 866px) {
-    .welcomeContainerInner {
-      width: 786px; } }
-  @media (min-width: 1122px) {
-    .welcomeContainerInner {
-      width: 1042px; } }
-
-.welcomeCard {
-  position: relative;
-  background: var(--newtab-card-background-color);
-  border-radius: 4px;
-  box-shadow: var(--newtab-card-shadow);
-  font-size: 13px;
-  padding: 20px 20px 60px; }
-  @media (max-width: 866px) {
-    .welcomeCard {
-      padding: 20px; } }
-  @media (min-width: 1122px) {
-    .welcomeCard {
-      font-size: 15px; } }
-
-.welcomeCard .onboardingTitle {
-  font-weight: normal;
-  color: var(--newtab-text-primary-color);
-  margin: 10px 0 4px;
-  font-size: 15px; }
-  @media (min-width: 1122px) {
-    .welcomeCard .onboardingTitle {
-      font-size: 18px; } }
-
-.welcomeCard .onboardingText {
-  margin: 0 0 60px;
-  color: var(--newtab-text-conditional-color);
-  line-height: 1.5;
-  font-weight: 200; }
-
-.welcomeCard .onboardingButton {
-  color: var(--newtab-text-conditional-color);
-  background: var(--welcome-card-button-background-color);
-  border: 0;
-  border-radius: 4px;
-  margin: 14px;
-  min-width: 70%;
-  padding: 6px 14px;
-  white-space: pre-wrap;
-  cursor: pointer; }
-  .welcomeCard .onboardingButton:focus, .welcomeCard .onboardingButton:hover {
-    box-shadow: none;
-    background: var(--welcome-card-button-background-hover-color); }
-  .welcomeCard .onboardingButton:focus {
-    outline: dotted 1px; }
-  .welcomeCard .onboardingButton:active {
-    background: var(--welcome-card-button-background-active-color); }
-
-.welcomeCard .onboardingButtonContainer {
-  position: absolute;
-  bottom: 16px;
-  left: 0;
-  width: 100%;
-  text-align: center; }
-
-.onboardingMessageImage {
-  height: 112px;
-  width: 180px;
-  background-size: auto 140px;
-  background-position: center center;
-  background-repeat: no-repeat;
-  display: inline-block; }
-  @media (max-width: 866px) {
-    .onboardingMessageImage {
-      height: 75px;
-      min-width: 80px;
-      background-size: 140px; } }
-
-.start-button {
-  border: 0;
-  font-size: 15px;
-  font-family: inherit;
-  font-weight: 200;
-  margin-inline-start: 12px;
-  margin: 30px 0 25px;
-  padding: 8px 16px;
-  white-space: nowrap;
-  background-color: var(--newtab-button-primary-color);
-  color: var(--welcome-button-text-color);
-  cursor: pointer;
-  border-radius: 2px; }
-  .start-button:focus {
-    background: var(--welcome-button-background-hover-color);
-    box-shadow: 0 0 0 1px var(--welcome-button-box-shadow-inset-color) inset, 0 0 0 1px var(--welcome-button-box-shadow-inset-color), 0 0 0 4px var(--welcome-button-box-shadow-color); }
-  .start-button:hover {
-    background: var(--welcome-button-background-hover-color); }
-  .start-button:active {
-    background: var(--welcome-button-background-active-color); }
-
 .onboardingContainer {
   text-align: center;
   overflow-x: auto;
   height: 100vh;
   background-color: var(--newtab-background-color-1); }
   .onboardingContainer .screen {
     display: flex;
     flex-flow: column nowrap;
--- a/browser/components/newtab/content-src/aboutwelcome/aboutwelcome.jsx
+++ b/browser/components/newtab/content-src/aboutwelcome/aboutwelcome.jsx
@@ -1,29 +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/. */
 
 import React from "react";
 import ReactDOM from "react-dom";
 import { MultiStageAboutWelcome } from "./components/MultiStageAboutWelcome";
-import { SimpleAboutWelcome } from "./components/SimpleAboutWelcome";
 import { ReturnToAMO } from "./components/ReturnToAMO";
 
-import {
-  AboutWelcomeUtils,
-  DEFAULT_WELCOME_CONTENT,
-} from "../lib/aboutwelcome-utils";
+import { DEFAULT_WELCOME_CONTENT } from "../lib/aboutwelcome-utils";
 
 class AboutWelcome extends React.PureComponent {
   constructor(props) {
     super(props);
     this.state = { metricsFlowUri: null };
     this.fetchFxAFlowUri = this.fetchFxAFlowUri.bind(this);
-    this.handleStartBtnClick = this.handleStartBtnClick.bind(this);
   }
 
   async fetchFxAFlowUri() {
     this.setState({ metricsFlowUri: await window.AWGetFxAMetricsFlowURI() });
   }
 
   componentDidMount() {
     this.fetchFxAFlowUri();
@@ -57,46 +52,19 @@ class AboutWelcome extends React.PureCom
       });
     }
 
     // Captures user has seen about:welcome by setting
     // firstrun.didSeeAboutWelcome pref to true and capturing welcome UI unique messageId
     window.AWSendToParent("SET_WELCOME_MESSAGE_SEEN", this.props.messageId);
   }
 
-  handleStartBtnClick() {
-    AboutWelcomeUtils.handleUserAction(this.props.startButton.action);
-    const ping = {
-      event: "CLICK_BUTTON",
-      event_context: {
-        source: this.props.startButton.message_id,
-        page: "about:welcome",
-      },
-      message_id: this.props.messageId,
-      id: "ABOUT_WELCOME",
-    };
-    window.AWSendEventTelemetry(ping);
-  }
-
   render() {
     const { props } = this;
-    if (props.template === "simplified") {
-      return (
-        <SimpleAboutWelcome
-          metricsFlowUri={this.state.metricsFlowUri}
-          message_id={props.messageId}
-          utm_term={props.UTMTerm}
-          title={props.title}
-          subtitle={props.subtitle}
-          cards={props.cards}
-          startButton={props.startButton}
-          handleStartBtnClick={this.handleStartBtnClick}
-        />
-      );
-    } else if (props.template === "return_to_amo") {
+    if (props.template === "return_to_amo") {
       return (
         <ReturnToAMO
           message_id={props.messageId}
           name={props.name}
           url={props.url}
           iconURL={props.iconURL}
         />
       );
--- a/browser/components/newtab/content-src/aboutwelcome/aboutwelcome.scss
+++ b/browser/components/newtab/content-src/aboutwelcome/aboutwelcome.scss
@@ -1,44 +1,37 @@
 // sass-lint:disable no-css-comments
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 @import '../styles/normalize';
-@import '../styles/OnboardingImages';
 
 $break-point-medium: 610px;
 $break-point-large: 866px;
 $break-point-widest: 1122px;
 $logo-size: 112px;
 
 html {
   height: 100%;
 }
 
 body {
   // sass-lint:disable no-color-literals
-  --grey-subtitle: #4A4A4F;
   --grey-subtitle-1: #696977;
   --newtab-background-color: #EDEDF0;
   --newtab-background-color-1: #F9F9FA;
   --newtab-text-primary-color: #0C0C0D;
   --newtab-text-conditional-color: #4A4A4F;
   --newtab-button-primary-color: #0060DF;
   --newtab-button-secondary-color: #0060DF;
   --newtab-card-background-color: #FFF;
   --newtab-card-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.4);
   --tiles-theme-section-border-width: 1px;
-  --welcome-header-text-color: #2B2156;
   --welcome-header-text-color-1: #20133A;
-  --welcome-card-button-background-color: rgba(12, 12, 13, 0.1);
-  --welcome-card-button-background-hover-color: rgba(12, 12, 13, 0.2);
-  --welcome-card-button-background-active-color: rgba(12, 12, 13, 0.3);
   --welcome-button-box-shadow-color: #0A84FF;
-  --welcome-button-box-shadow-inset-color: rgba(10, 132, 255, 0.3);
   --welcome-button-text-color: #FFF;
   --welcome-button-background-hover-color: #003EAA;
   --welcome-button-background-active-color: #002275;
   --about-welcome-media-fade: linear-gradient(transparent, transparent 35%, #F9F9FA, #F9F9FA);
 
   font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Ubuntu',
   'Helvetica Neue', sans-serif;
   font-size: 16px;
@@ -53,204 +46,22 @@ body {
     --newtab-background-color-1: #1D1133;
     --newtab-text-primary-color: #F9F9FA;
     --newtab-text-conditional-color: #F9F9FA;
     --grey-subtitle-1: #FFF;
     --newtab-button-primary-color: #0060DF;
     --newtab-button-secondary-color: #FFF;
     --newtab-card-background-color: #38383D;
     --newtab-card-shadow: 0 1px 8px 0 rgba(12, 12, 13, 0.4);
-    --welcome-header-text-color: rgba(255, 255, 255, 0.6);
     --welcome-header-text-color-1: #7542E5;
-    --welcome-card-button-background-color: rgba(12, 12, 13, 0.3);
-    --welcome-card-button-background-hover-color: rgba(12, 12, 13, 0.5);
-    --welcome-card-button-background-active-color: rgba(12, 12, 13, 0.7);
     --welcome-button-box-shadow-color: #0A84FF;
     --about-welcome-media-fade: linear-gradient(transparent, transparent 35%, #1D1133, #1D1133);
   }
 }
 
-.welcomeCardGrid {
-  margin: 0;
-  margin-top: 32px;
-  display: grid;
-  grid-gap: 32px;
-  transition: opacity 0.4s;
-  transition-delay: 0.1s;
-  grid-auto-rows: 1fr;
-
-  @media (min-width: $break-point-medium) {
-    grid-template-columns: repeat(auto-fit, 224px);
-  }
-
-  @media (min-width: $break-point-widest) {
-    grid-template-columns: repeat(auto-fit, 309px);
-  }
-}
-
-.welcomeContainer {
-  text-align: center;
-
-  @media (min-width: $break-point-medium) {
-    max-height: 1000px;
-  }
-
-  h1 {
-    font-size: 36px;
-    font-weight: 200;
-    margin: 0 0 40px;
-    color: var(--welcome-header-text-color);
-  }
-
-  .welcome-title {
-    margin-bottom: 5px;
-    line-height: 52px;
-  }
-
-  .welcome-subtitle {
-    font-size: 28px;
-    font-weight: 200;
-    margin: 6px 0 0;
-    color: var(--grey-subtitle);
-    line-height: 42px;
-  }
-}
-
-.welcomeContainerInner {
-  margin: auto;
-  padding: 40px 25px;
-
-  @media (min-width: $break-point-medium) {
-    width: 530px;
-  }
-
-  @media (min-width: $break-point-large) {
-    width: 786px;
-  }
-
-  @media (min-width: $break-point-widest) {
-    width: 1042px;
-  }
-}
-
-.welcomeCard {
-  position: relative;
-  background: var(--newtab-card-background-color);
-  border-radius: 4px;
-  box-shadow: var(--newtab-card-shadow);
-  font-size: 13px;
-  padding: 20px 20px 60px;
-
-  @media (max-width: $break-point-large) {
-    padding: 20px;
-  }
-
-  @media (min-width: $break-point-widest) {
-    font-size: 15px;
-  }
-}
-
-.welcomeCard .onboardingTitle {
-  font-weight: normal;
-  color: var(--newtab-text-primary-color);
-  margin: 10px 0 4px;
-  font-size: 15px;
-
-  @media (min-width: $break-point-widest) {
-    font-size: 18px;
-  }
-}
-
-.welcomeCard .onboardingText {
-  margin: 0 0 60px;
-  color: var(--newtab-text-conditional-color);
-  line-height: 1.5;
-  font-weight: 200;
-}
-
-.welcomeCard .onboardingButton {
-  color: var(--newtab-text-conditional-color);
-  background: var(--welcome-card-button-background-color);
-  border: 0;
-  border-radius: 4px;
-  margin: 14px;
-  min-width: 70%;
-  padding: 6px 14px;
-  white-space: pre-wrap;
-  cursor: pointer;
-
-  &:focus,
-  &:hover {
-    box-shadow: none;
-    background: var(--welcome-card-button-background-hover-color);
-  }
-
-  &:focus {
-    outline: dotted 1px;
-  }
-
-  &:active {
-    background: var(--welcome-card-button-background-active-color);
-  }
-}
-
-.welcomeCard .onboardingButtonContainer {
-  position: absolute;
-  bottom: 16px;
-  left: 0;
-  width: 100%;
-  text-align: center;
-}
-
-.onboardingMessageImage {
-  height: 112px;
-  width: 180px;
-  background-size: auto 140px;
-  background-position: center center;
-  background-repeat: no-repeat;
-  display: inline-block;
-
-  @media (max-width: $break-point-large) {
-    height: 75px;
-    min-width: 80px;
-    background-size: 140px;
-  }
-}
-
-.start-button {
-  border: 0;
-  font-size: 15px;
-  font-family: inherit;
-  font-weight: 200;
-  margin-inline-start: 12px;
-  margin: 30px 0 25px;
-  padding: 8px 16px;
-  white-space: nowrap;
-  background-color: var(--newtab-button-primary-color);
-  color: var(--welcome-button-text-color);
-  cursor: pointer;
-  border-radius: 2px;
-
-  &:focus {
-    background: var(--welcome-button-background-hover-color);
-    box-shadow: 0 0 0 1px var(--welcome-button-box-shadow-inset-color) inset,
-    0 0 0 1px var(--welcome-button-box-shadow-inset-color),
-    0 0 0 4px var(--welcome-button-box-shadow-color);
-  }
-
-  &:hover {
-    background: var(--welcome-button-background-hover-color);
-  }
-
-  &:active {
-    background: var(--welcome-button-background-active-color);
-  }
-}
-
-
 .onboardingContainer {
   text-align: center;
   overflow-x: auto;
   height: 100vh;
   background-color: var(--newtab-background-color-1);
 
   .screen {
     display: flex;
deleted file mode 100644
--- a/browser/components/newtab/content-src/aboutwelcome/components/FxCards.jsx
+++ /dev/null
@@ -1,83 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-import React from "react";
-import { addUtmParams } from "../../asrouter/templates/FirstRun/addUtmParams";
-import { OnboardingCard } from "../../asrouter/templates/OnboardingMessage/OnboardingMessage";
-import { AboutWelcomeUtils } from "../../lib/aboutwelcome-utils";
-
-export class FxCards extends React.PureComponent {
-  constructor(props) {
-    super(props);
-
-    this.state = { flowParams: null };
-
-    this.fetchFxAFlowParams = this.fetchFxAFlowParams.bind(this);
-    this.onCardAction = this.onCardAction.bind(this);
-  }
-
-  componentDidUpdate() {
-    this.fetchFxAFlowParams();
-  }
-
-  componentDidMount() {
-    this.fetchFxAFlowParams();
-  }
-
-  async fetchFxAFlowParams() {
-    if (this.state.flowParams || !this.props.metricsFlowUri) {
-      return;
-    }
-
-    const flowParams = await AboutWelcomeUtils.fetchFlowParams(
-      this.props.metricsFlowUri
-    );
-
-    this.setState({ flowParams });
-  }
-
-  onCardAction(action) {
-    let { type, data } = action;
-    let UTMTerm = `aboutwelcome-${this.props.utm_term}-card`;
-
-    if (action.type === "OPEN_URL") {
-      let url = new URL(action.data.args);
-      addUtmParams(url, UTMTerm);
-
-      if (action.addFlowParams && this.state.flowParams) {
-        url.searchParams.append("device_id", this.state.flowParams.deviceId);
-        url.searchParams.append("flow_id", this.state.flowParams.flowId);
-        url.searchParams.append(
-          "flow_begin_time",
-          this.state.flowParams.flowBeginTime
-        );
-      }
-
-      data = { ...data, args: url.toString() };
-    }
-
-    AboutWelcomeUtils.handleUserAction({ type, data });
-  }
-
-  render() {
-    const { props } = this;
-    return (
-      <React.Fragment>
-        <div className={`welcomeCardGrid show`}>
-          {props.cards.map(card => (
-            <OnboardingCard
-              key={card.id}
-              message={card}
-              className="welcomeCard"
-              sendUserActionTelemetry={props.sendTelemetry}
-              onAction={this.onCardAction}
-              UISurface="ABOUT_WELCOME"
-              {...card}
-            />
-          ))}
-        </div>
-      </React.Fragment>
-    );
-  }
-}
deleted file mode 100644
--- a/browser/components/newtab/content-src/aboutwelcome/components/HeroText.jsx
+++ /dev/null
@@ -1,19 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-import React from "react";
-import { Localized } from "./MSLocalized";
-
-export const HeroText = props => {
-  return (
-    <React.Fragment>
-      <Localized text={props.title}>
-        <h1 className="welcome-title" />
-      </Localized>
-      <Localized text={props.subtitle}>
-        <h2 className="welcome-subtitle" />
-      </Localized>
-    </React.Fragment>
-  );
-};
deleted file mode 100644
--- a/browser/components/newtab/content-src/aboutwelcome/components/SimpleAboutWelcome.jsx
+++ /dev/null
@@ -1,35 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-import React from "react";
-import { HeroText } from "./HeroText";
-import { FxCards } from "./FxCards";
-import { Localized } from "./MSLocalized";
-
-export class SimpleAboutWelcome extends React.PureComponent {
-  render() {
-    const { props } = this;
-    return (
-      <div className="outer-wrapper welcomeContainer">
-        <div className="welcomeContainerInner">
-          <main>
-            <HeroText title={props.title} subtitle={props.subtitle} />
-            <FxCards
-              cards={props.cards}
-              metricsFlowUri={this.props.metricsFlowUri}
-              sendTelemetry={window.AWSendEventTelemetry}
-              utm_term={this.props.UTMTerm}
-            />
-            <Localized text={props.startButton.label}>
-              <button
-                className="start-button"
-                onClick={this.props.handleStartBtnClick}
-              />
-            </Localized>
-          </main>
-        </div>
-      </div>
-    );
-  }
-}
deleted file mode 100644
--- a/browser/components/newtab/content-src/asrouter/templates/OnboardingMessage/OnboardingMessage.jsx
+++ /dev/null
@@ -1,52 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-import React from "react";
-import { Localized } from "../../../aboutwelcome/components/MSLocalized";
-
-export class OnboardingCard extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.onClick = this.onClick.bind(this);
-  }
-
-  onClick() {
-    const { props } = this;
-    const ping = {
-      event: "CLICK_BUTTON",
-      message_id: props.id,
-      id: props.UISurface,
-    };
-    props.sendUserActionTelemetry(ping);
-    props.onAction(props.content.primary_button.action, props.message);
-  }
-
-  render() {
-    const { content } = this.props;
-    const className = this.props.className || "onboardingMessage";
-    return (
-      <div className={className}>
-        <div className={`onboardingMessageImage ${content.icon}`} />
-        <div className="onboardingContent">
-          <span>
-            <Localized text={content.title}>
-              <h2 className="onboardingTitle" />
-            </Localized>
-            <Localized text={content.text}>
-              <p className="onboardingText" />
-            </Localized>
-          </span>
-          <span className="onboardingButtonContainer">
-            <Localized text={content.primary_button.label}>
-              <button
-                className="button onboardingButton"
-                onClick={this.onClick}
-              />
-            </Localized>
-          </span>
-        </div>
-      </div>
-    );
-  }
-}
deleted file mode 100644
--- a/browser/components/newtab/content-src/asrouter/templates/OnboardingMessage/OnboardingMessage.schema.json
+++ /dev/null
@@ -1,142 +0,0 @@
-{
-  "title": "OnboardingMessage",
-  "description": "A template with a title, icon, button and description. No markup allowed.",
-  "version": "1.0.0",
-  "type": "object",
-  "properties": {
-    "title": {
-      "oneOf": [
-        {
-          "type": "string",
-          "description": "The message displayed in the title of the onboarding card"
-        },
-        {
-          "type": "object",
-          "properties": {
-            "string_id": {
-              "type": "string"
-            }
-          },
-          "required": ["string_id"],
-          "description": "Id of localized string for onboarding card title"
-        }
-      ],
-      "description": "Id of localized string or message override."
-    },
-    "text": {
-      "oneOf": [
-        {
-          "type": "string",
-          "description": "The message displayed in the description of the onboarding card"
-        },
-        {
-          "type": "object",
-          "properties": {
-            "string_id": {
-              "type": "string"
-            },
-            "args": {
-              "type": "object",
-              "description": "An optional argument to pass to the localization module"
-            }
-          },
-          "required": ["string_id"],
-          "description": "Id of localized string for onboarding card description"
-        }
-      ],
-      "description": "Id of localized string or message override."
-    },
-    "icon": {
-      "allOf": [
-        {
-          "type": "string",
-          "description": "Image associated with the onboarding card"
-        }
-      ]
-    },
-    "primary_button": {
-      "type": "object",
-      "properties": {
-        "label": {
-          "oneOf": [
-            {
-              "type": "string",
-              "description": "The label of the onboarding messages' action button"
-            },
-            {
-              "type": "object",
-              "properties": {
-                "string_id": {
-                  "type": "string"
-                }
-              },
-              "required": ["string_id"],
-              "description": "Id of localized string for onboarding messages' button"
-            }
-          ],
-          "description": "Id of localized string or message override."
-        },
-        "action": {
-          "type": "object",
-          "properties": {
-            "type": {
-              "type": "string",
-              "description": "Action dispatched by the button."
-            },
-            "data": {
-              "properties": {
-                "args": {
-                  "type": "string",
-                  "description": "Additional parameters for button action, for example which link the button should open."
-                }
-              }
-            }
-          }
-        }
-      }
-    },
-    "secondary_buttons": {
-      "type": "object",
-      "properties": {
-        "label": {
-          "oneOf": [
-            {
-              "type": "string",
-              "description": "The label of the onboarding messages' (optional) secondary action button"
-            },
-            {
-              "type": "object",
-              "properties": {
-                "string_id": {
-                  "type": "string"
-                }
-              },
-              "required": ["string_id"],
-              "description": "Id of localized string for onboarding messages' button"
-            }
-          ],
-          "description": "Id of localized string or message override."
-        },
-        "action": {
-          "type": "object",
-          "properties": {
-            "type": {
-              "type": "string",
-              "description": "Action dispatched by the button."
-            },
-            "data": {
-              "properties": {
-                "args": {
-                  "type": "string",
-                  "description": "Additional parameters for button action, for example which link the button should open."
-                }
-              }
-            }
-          }
-        }
-      }
-    }
-  },
-  "additionalProperties": true,
-  "required": ["title", "text", "icon", "primary_button"]
-}
deleted file mode 100644
--- a/browser/components/newtab/content-src/asrouter/templates/OnboardingMessage/_OnboardingMessage.scss
+++ /dev/null
@@ -1,131 +0,0 @@
-@import '../../../styles/OnboardingImages';
-
-.onboardingMessage {
-  height: 340px;
-  text-align: center;
-  padding: 13px;
-  font-weight: 200;
-
-  // at 850px, img floats left, content floats right next to it
-  @media(max-width: 850px) {
-    height: 170px;
-    text-align: left;
-    padding: 10px;
-    border-bottom: 1px solid $grey-30;
-    display: flex;
-    margin-bottom: 11px;
-
-    &:last-child {
-      border: 0;
-    }
-
-    .onboardingContent {
-      padding-left: 10px;
-      height: 100%;
-
-      > span > h3 {
-        margin-top: 0;
-        margin-bottom: 4px;
-        font-weight: 400;
-      }
-
-      > span > p {
-        margin-top: 0;
-        line-height: 22px;
-        font-size: 15px;
-      }
-    }
-  }
-
-  @media(max-width: 650px) {
-    height: 250px;
-  }
-
-  .onboardingContent {
-    height: 175px;
-
-    > span > h3 {
-      color: $grey-90;
-      margin-bottom: 8px;
-      font-weight: 400;
-    }
-
-    > span > p {
-      color: $grey-60;
-      margin-top: 0;
-      height: 180px;
-      margin-bottom: 12px;
-      font-size: 15px;
-      line-height: 22px;
-
-      @media(max-width: 650px) {
-        margin-bottom: 0;
-        height: 160px;
-      }
-    }
-  }
-
-  .onboardingButton {
-    background-color: $grey-90-10;
-    border: 0;
-    width: 150px;
-    height: 30px;
-    margin-bottom: 23px;
-    padding: 4px 0 6px;
-    font-size: 15px;
-
-    // at 850px, the button shimmies down and to the right
-    @media(max-width: 850px) {
-      float: right;
-      margin-top: -105px;
-      margin-inline-end: -10px;
-    }
-
-    @media(max-width: 650px) {
-      float: none;
-    }
-
-    &:focus,
-    &.active,
-    &:hover {
-      box-shadow: 0 0 0 5px $grey-30;
-      transition: box-shadow 150ms;
-    }
-  }
-
-
-  &::before {
-    content: '';
-    height: 230px;
-    width: 1px;
-    position: absolute;
-    background-color: $grey-30;
-    margin-top: 40px;
-    margin-inline-start: 215px;
-
-    // at 850px, the line goes from vertical to horizontal
-    @media(max-width: 850px) {
-      content: none;
-    }
-  }
-
-  &:last-child::before {
-    content: none;
-  }
-}
-
-.onboardingMessageImage {
-  height: 112px;
-  width: 180px;
-  background-size: auto 140px;
-  background-position: center center;
-  background-repeat: no-repeat;
-  display: inline-block;
-
-  // Cards will wrap into the next line after this breakpoint
-  @media(max-width: 865px) {
-    height: 75px;
-    min-width: 80px;
-    background-size: 140px;
-  }
-}
deleted file mode 100644
--- a/browser/components/newtab/content-src/styles/_OnboardingImages.scss
+++ /dev/null
@@ -1,71 +0,0 @@
-// Used for Trailhead and about:welcome
-
-.onboardingMessageImage {
-  &.addons {
-    background-image: url('chrome://activity-stream/content/data/content/assets/illustration-addons@2x.png');
-  }
-
-  &.privatebrowsing {
-    background-image: url('chrome://activity-stream/content/data/content/assets/illustration-privatebrowsing@2x.png');
-  }
-
-  &.screenshots {
-    background-image: url('chrome://activity-stream/content/data/content/assets/illustration-screenshots@2x.png');
-  }
-
-  &.gift {
-    background-image: url('chrome://activity-stream/content/data/content/assets/illustration-gift@2x.png');
-  }
-
-  &.sync {
-    background-image: url('chrome://activity-stream/content/data/content/assets/illustration-sync@2x.png');
-  }
-
-  &.devices {
-    background-image: url('chrome://activity-stream/content/data/content/assets/trailhead/card-illo-devices.svg');
-  }
-
-  &.fbcont {
-    background-image: url('chrome://activity-stream/content/data/content/assets/trailhead/card-illo-fbcont.svg');
-  }
-
-  &.import {
-    background-image: url('chrome://activity-stream/content/data/content/assets/trailhead/card-illo-import.svg');
-  }
-
-  &.ffmonitor {
-    background-image: url('chrome://activity-stream/content/data/content/assets/trailhead/card-illo-ffmonitor.svg');
-  }
-
-  &.ffsend {
-    background-image: url('chrome://activity-stream/content/data/content/assets/trailhead/card-illo-ffsend.svg');
-  }
-
-  &.lockwise {
-    background-image: url('chrome://activity-stream/content/data/content/assets/trailhead/card-illo-lockwise.svg');
-  }
-
-  &.mobile {
-    background-image: url('chrome://activity-stream/content/data/content/assets/trailhead/card-illo-mobile.svg');
-  }
-
-  &.pledge {
-    background-image: url('chrome://activity-stream/content/data/content/assets/trailhead/card-illo-pledge.svg');
-  }
-
-  &.pocket {
-    background-image: url('chrome://activity-stream/content/data/content/assets/trailhead/card-illo-pocket.svg');
-  }
-
-  &.private {
-    background-image: url('chrome://activity-stream/content/data/content/assets/trailhead/card-illo-private.svg');
-  }
-
-  &.sendtab {
-    background-image: url('chrome://activity-stream/content/data/content/assets/trailhead/card-illo-sendtab.svg');
-  }
-
-  &.tracking {
-    background-image: url('chrome://activity-stream/content/data/content/assets/trailhead/card-illo-tracking.svg');
-  }
-}
deleted file mode 100644
index 67f94d0a1bbbecf7ea7d45b769ed9f61585f11e4..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 665bfc9ee05c4354d2959393c6c0f492dbd177b1..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 11dd0b895ab63b2b84c18a17921bdd6b9a05b158..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 046b46c49d6a853f80f38afe520c44efb78dd812..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 90875f2fe19d6e8924f17069bc2b4f0cac48213f..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/browser/components/newtab/data/content/assets/trailhead/card-illo-devices.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-   
-<svg viewBox="0 0 288 180" xmlns="http://www.w3.org/2000/svg"><circle cx="144" cy="90" fill="#bfbfc9" opacity=".15" r="72"/><path d="m179.18 109.25a5.26 5.26 0 0 0 1.21-3.34v-63.77a5.33 5.33 0 0 0 -5.31-5.31h-90.33a5.33 5.33 0 0 0 -5.32 5.31v63.77a5.26 5.26 0 0 0 1.22 3.34l-11.84 2v5.31h122.19v-5.31z" fill="#9f9fad"/><path d="m139.66 47.46-54.91 54.9a1.78 1.78 0 0 0 1.77 1.78h86.79a1.78 1.78 0 0 0 1.77-1.78v-54.9z" fill="#592acb"/><path d="m84.75 47.46v54.9l54.91-54.9z" fill="#6736d8"/><path d="m173.31 42.14h-86.79a1.78 1.78 0 0 0 -1.77 1.77v3.55h90.33v-3.55a1.78 1.78 0 0 0 -1.77-1.77z" fill="#592acb"/><path d="m173.47 110.54h-86.79a.89.89 0 1 1 0-1.77h86.79a.89.89 0 1 1 0 1.77z" fill="#8f8f9d"/><path d="m123.38 112.85h12.4" fill="none" stroke="#80808e" stroke-linecap="round" stroke-linejoin="round"/><path d="m204.41 68.93h-37.58a5.38 5.38 0 0 0 -5.36 5.37v68a5.38 5.38 0 0 0 5.36 5.37h37.58a5.38 5.38 0 0 0 5.37-5.37v-68a5.38 5.38 0 0 0 -5.37-5.37z" fill="#bfbfc9"/><path d="m166.83 117.25v21.47a1.8 1.8 0 0 0 1.79 1.79h34a1.79 1.79 0 0 0 1.79-1.79v-59z" fill="#f11f89"/><path d="m166.83 79.67v37.58l37.58-37.58z" fill="#ff298a"/><path d="m202.62 74.3h-34a1.79 1.79 0 0 0 -1.79 1.79v3.58h37.58v-3.58a1.79 1.79 0 0 0 -1.79-1.79z" fill="#f11f89"/><circle cx="176.68" cy="132.46" fill="#ffd567" r="2.68"/><circle cx="185.62" cy="132.46" fill="#ff8a50" r="2.68"/><circle cx="194.57" cy="132.46" fill="#7542e5" r="2.68"/><path d="m183.26 144.09h5.05" fill="none" stroke="#9f9fad" stroke-linecap="round" stroke-linejoin="round"/><g fill="#fff"><path d="m141.32 74.4a1.93 1.93 0 0 0 -.83.06 1.81 1.81 0 0 0 -1.64 1.36 9.15 9.15 0 0 1 -8.86 7 11.89 11.89 0 0 1 -1.2-.08 9.06 9.06 0 0 1 -6-3.58h3.61a1.83 1.83 0 0 0 0-3.66h-7.4a1.83 1.83 0 0 0 -1.83 1.83v7.27a1.83 1.83 0 0 0 3.66 0v-1.68a13.44 13.44 0 0 0 7.49 3.38 11.24 11.24 0 0 0 1.65.13c6.35 0 11.73-4.16 12.8-9.89a1.85 1.85 0 0 0 -1.45-2.14z"/><path d="m142.82 62.61a1.84 1.84 0 0 0 -3.67 0v1.68a13.44 13.44 0 0 0 -7.48-3.38 11.66 11.66 0 0 0 -1.68-.13h-.19c-6.26.08-11.55 4.2-12.6 9.87a1.84 1.84 0 0 0 1.46 2.14h.34a2.14 2.14 0 0 0 .35-.06 1.84 1.84 0 0 0 1.78-1.39 9.13 9.13 0 0 1 8.76-6.94h.11a10.19 10.19 0 0 1 1.24.09 9 9 0 0 1 6 3.57h-3.6a1.83 1.83 0 1 0 0 3.66h7.36a1.83 1.83 0 0 0 1.84-1.83v-7.28z"/><path d="m193.26 105.26a1.6 1.6 0 0 0 -1.6.92 5.93 5.93 0 0 1 -5.75 4.51 6.65 6.65 0 0 1 -.78-.06 5.83 5.83 0 0 1 -3.92-2.32h2.34a1.19 1.19 0 0 0 0-2.38h-4.76a1.2 1.2 0 0 0 -1.19 1.19v4.76a1.19 1.19 0 1 0 2.37 0v-1.09a8.78 8.78 0 0 0 4.87 2.2 7.48 7.48 0 0 0 1.07.08 8.24 8.24 0 0 0 8.31-6.42 1.2 1.2 0 0 0 -.96-1.39z"/><path d="m194.24 97.61a1.19 1.19 0 0 0 -2.38 0v1.09a8.79 8.79 0 0 0 -4.86-2.2 7.88 7.88 0 0 0 -1.09-.08h-.12a8.2 8.2 0 0 0 -8.18 6.41 1.18 1.18 0 0 0 .95 1.39h.22a1.7 1.7 0 0 0 .22 0 1.18 1.18 0 0 0 1.15-.9 5.93 5.93 0 0 1 5.69-4.51h.06a7 7 0 0 1 .8.06 5.87 5.87 0 0 1 3.92 2.32h-2.34a1.19 1.19 0 1 0 0 2.38h4.76a1.2 1.2 0 0 0 1.19-1.19v-4.77z"/></g></svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/components/newtab/data/content/assets/trailhead/card-illo-fbcont.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<svg viewBox="0 0 288 180" xmlns="http://www.w3.org/2000/svg"><circle cx="144" cy="90" fill="#bfbfc9" opacity=".15" r="72"/><path d="m68.3 106.33h-.5v14h.5 4 .5v-14h-.5z" fill="none"/><path d="m92.3 106.33h-.5v14h.5 4 .5v-14h-.5z" fill="none"/><path d="m80.3 106.33h-.5v14h.5 4 .5v-14h-.5z" fill="none"/><path d="m92.3 103.33h4v3h-4z" fill="#ffbd4f"/><path d="m68.3 103.33h4v3h-4z" fill="#ffbd4f"/><path d="m80.3 103.33h4v3h-4z" fill="#ffbd4f"/><path d="m92.3 120.33h4v3h-4z" fill="#ffbd4f"/><path d="m80.3 120.33h4v3h-4z" fill="#ffbd4f"/><path d="m68.3 120.33h4v3h-4z" fill="#ffbd4f"/><g fill="#ffa436"><path d="m67.8 103.33h.5v3h-.5z"/><path d="m67.8 120.33h.5v3h-.5z"/><path d="m72.3 103.33h.5v3h-.5z"/><path d="m72.3 120.33h.5v3h-.5z"/><path d="m79.8 103.33h.5v3h-.5z"/><path d="m79.8 120.33h.5v3h-.5z"/><path d="m84.3 103.33h.5v3h-.5z"/><path d="m84.3 120.33h.5v3h-.5z"/><path d="m91.8 103.33h.5v3h-.5z"/><path d="m91.8 120.33h.5v3h-.5z"/><path d="m96.3 103.33h.5v3h-.5z"/><path d="m96.3 120.33h.5v3h-.5z"/></g><path d="m67.8 106.33v-9a.5.5 0 0 0 -.14-.35l-3-3a.5.5 0 0 0 -.66.02l-3 3a.47.47 0 0 0 -.15.35v32a.5.5 0 0 0 .5.5h6a.5.5 0 0 0 .5-.5v-23zm-2.5 15a1 1 0 1 1 -1-1 1 1 0 0 1 1 1zm-1-18a1 1 0 1 1 -1 1 1 1 0 0 1 1-1z" fill="#ffd567"/><path d="m79.8 106.33v-9a.5.5 0 0 0 -.14-.35l-3-3a.5.5 0 0 0 -.66.02l-3 3a.47.47 0 0 0 -.15.35v32a.5.5 0 0 0 .5.5h6a.5.5 0 0 0 .5-.5v-23zm-2.5 15a1 1 0 1 1 -1-1 1 1 0 0 1 1 1zm-1-18a1 1 0 1 1 -1 1 1 1 0 0 1 1-1z" fill="#ffd567"/><path d="m91.8 106.33v-9a.5.5 0 0 0 -.14-.35l-3-3a.5.5 0 0 0 -.66.02l-3 3a.47.47 0 0 0 -.15.35v32a.5.5 0 0 0 .5.5h6a.5.5 0 0 0 .5-.5v-23zm-2.5 15a1 1 0 1 1 -1-1 1 1 0 0 1 1 1zm-1-18a1 1 0 1 1 -1 1 1 1 0 0 1 1-1z" fill="#ffd567"/><path d="m103.66 97-3-3a.5.5 0 0 0 -.71 0l-3 3a.47.47 0 0 0 -.15.35v32a.5.5 0 0 0 .5.5h6a.5.5 0 0 0 .5-.5v-32a.5.5 0 0 0 -.14-.35zm-2.36 7.35a1 1 0 1 1 -1-1 1 1 0 0 1 1 .98zm0 17a1 1 0 1 1 -1-1 1 1 0 0 1 1 .98z" fill="#ffd567"/><circle cx="88.3" cy="104.33" fill="#afafbb" r="1"/><circle cx="100.3" cy="104.33" fill="#afafbb" r="1"/><circle cx="76.3" cy="104.33" fill="#afafbb" r="1"/><circle cx="64.3" cy="104.33" fill="#afafbb" r="1"/><circle cx="88.3" cy="121.33" fill="#afafbb" r="1"/><circle cx="100.3" cy="121.33" fill="#afafbb" r="1"/><circle cx="76.3" cy="121.33" fill="#afafbb" r="1"/><circle cx="64.3" cy="121.33" fill="#afafbb" r="1"/><path d="m191.7 106.33h-.5v14h.5 4 .5v-14h-.5z" fill="none"/><path d="m215.7 106.33h-.5v14h.5 4 .5v-14h-.5z" fill="none"/><path d="m203.7 106.33h-.5v14h.5 4 .5v-14h-.5z" fill="none"/><path d="m215.7 103.33h4v3h-4z" fill="#ffbd4f"/><path d="m191.7 103.33h4v3h-4z" fill="#ffbd4f"/><path d="m203.7 103.33h4v3h-4z" fill="#ffbd4f"/><path d="m215.7 120.33h4v3h-4z" fill="#ffbd4f"/><path d="m203.7 120.33h4v3h-4z" fill="#ffbd4f"/><path d="m191.7 120.33h4v3h-4z" fill="#ffbd4f"/><path d="m191.2 103.33h.5v3h-.5z" fill="#ffa436"/><path d="m191.2 120.33h.5v3h-.5z" fill="#ffa436"/><path d="m195.7 103.33h.5v3h-.5z" fill="#ffa436"/><path d="m195.7 120.33h.5v3h-.5z" fill="#ffa436"/><path d="m203.2 103.33h.5v3h-.5z" fill="#ffa436"/><path d="m203.2 120.33h.5v3h-.5z" fill="#ffa436"/><path d="m207.7 103.33h.5v3h-.5z" fill="#ffa436"/><path d="m207.7 120.33h.5v3h-.5z" fill="#ffa436"/><path d="m215.2 103.33h.5v3h-.5z" fill="#ffa436"/><path d="m215.2 120.33h.5v3h-.5z" fill="#ffa436"/><path d="m219.7 103.33h.5v3h-.5z" fill="#ffa436"/><path d="m219.7 120.33h.5v3h-.5z" fill="#ffa436"/><path d="m191.2 106.33v-9a.47.47 0 0 0 -.15-.35l-3-3a.5.5 0 0 0 -.71 0l-3 3a.5.5 0 0 0 -.14.35v32a.5.5 0 0 0 .5.5h6a.5.5 0 0 0 .5-.5zm-2.5 15a1 1 0 1 1 -1-1 1 1 0 0 1 1 1zm-1-18a1 1 0 1 1 -1 1 1 1 0 0 1 1-1z" fill="#ffd567"/><path d="m203.2 106.33v-9a.47.47 0 0 0 -.15-.35l-3-3a.5.5 0 0 0 -.71 0l-3 3a.5.5 0 0 0 -.14.35v32a.5.5 0 0 0 .5.5h6a.5.5 0 0 0 .5-.5zm-2.5 15a1 1 0 1 1 -1-1 1 1 0 0 1 1 1zm-1-18a1 1 0 1 1 -1 1 1 1 0 0 1 1-1z" fill="#ffd567"/><path d="m215.2 106.33v-9a.47.47 0 0 0 -.15-.35l-3-3a.5.5 0 0 0 -.71 0l-3 3a.5.5 0 0 0 -.14.35v32a.5.5 0 0 0 .5.5h6a.5.5 0 0 0 .5-.5zm-2.5 15a1 1 0 1 1 -1-1 1 1 0 0 1 1 1zm-1-18a1 1 0 1 1 -1 1 1 1 0 0 1 1-1z" fill="#ffd567"/><path d="m227.05 97-3-3a.5.5 0 0 0 -.71 0l-3 3a.5.5 0 0 0 -.14.35v32a.5.5 0 0 0 .5.5h6a.5.5 0 0 0 .5-.5v-32a.47.47 0 0 0 -.15-.35zm-2.35 7.35a1 1 0 1 1 -1-1 1 1 0 0 1 1 .98zm0 17a1 1 0 1 1 -1-1 1 1 0 0 1 1 .98z" fill="#ffd567"/><circle cx="211.7" cy="104.33" fill="#afafbb" r="1"/><circle cx="223.7" cy="104.33" fill="#afafbb" r="1"/><circle cx="199.7" cy="104.33" fill="#afafbb" r="1"/><circle cx="187.7" cy="104.33" fill="#afafbb" r="1"/><circle cx="211.7" cy="121.33" fill="#afafbb" r="1"/><circle cx="223.7" cy="121.33" fill="#afafbb" r="1"/><circle cx="199.7" cy="121.33" fill="#afafbb" r="1"/><circle cx="187.7" cy="121.33" fill="#afafbb" r="1"/><path d="m193.26 122.56a5.24 5.24 0 0 0 1.22-3.33v-63.77a5.33 5.33 0 0 0 -5.31-5.31h-90.34a5.33 5.33 0 0 0 -5.31 5.31v63.77a5.24 5.24 0 0 0 1.22 3.33l-11.85 2v5.31h122.22v-5.31z" fill="#9f9fad"/><path d="m153.74 60.77-54.91 54.91a1.77 1.77 0 0 0 1.77 1.77h86.8a1.77 1.77 0 0 0 1.77-1.77v-54.91z" fill="#0090ed"/><path d="m98.83 60.77v54.91l54.91-54.91z" fill="#00a2f1"/><path d="m187.4 55.46h-86.8a1.78 1.78 0 0 0 -1.77 1.77v3.54h90.34v-3.54a1.78 1.78 0 0 0 -1.77-1.77z" fill="#0090ed"/><path d="m187.56 123.86h-86.79a.89.89 0 1 1 0-1.77h86.79a.89.89 0 0 1 0 1.77z" fill="#8f8f9d"/><path d="m137.47 126.17h12.4" fill="none" stroke="#80808e" stroke-linecap="round" stroke-linejoin="round"/><path d="m156.54 74.46-2.09 2.09v4.18h-4.18v-4.18l-2.09-2.09-2.09 2.09v4.18h-4.18v-4.18l-2.09-2.09-2.09 2.09v4.18h-4.18v-4.18l-2.09-2.09-2.09 2.09v23h4.18v-4.19h4.18v4.18h4.18v-4.18h4.18v4.18h4.18v-4.18h4.18v4.18h4.18v-23zm-18.81 18.81h-4.18v-10.45h4.18zm8.56 0h-3.69v-9.56h3.69zm8.16 0h-4.18v-10.45h4.18z" fill="#fff" fill-rule="evenodd"/></svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/components/newtab/data/content/assets/trailhead/card-illo-ffmonitor.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-   
-<svg viewBox="0 0 288 180" xmlns="http://www.w3.org/2000/svg"><circle cx="144" cy="90" fill="#bfbfc9" opacity=".15" r="72"/><rect fill="#0df" height="15.49" rx=".5" width="48.16" x="62.2" y="69.68"/><path d="m107.27 66.63h-42.13a7.22 7.22 0 0 0 -7.2 7.2v6.53a7.22 7.22 0 0 0 7.2 7.2h42.13a7.22 7.22 0 0 0 7.2-7.2v-6.53a7.22 7.22 0 0 0 -7.2-7.2zm-29.91 11.37a.39.39 0 0 1 .23.24.4.4 0 0 1 0 .34l-.89 1.51a.4.4 0 0 1 -.28.19.41.41 0 0 1 -.32-.08l-1.91-1.48.3 2.45a.41.41 0 0 1 -.4.46h-1.78a.41.41 0 0 1 -.31-.14.46.46 0 0 1 -.1-.32l.31-2.46-2 1.49a.42.42 0 0 1 -.33.08.45.45 0 0 1 -.27-.2l-.84-1.51a.38.38 0 0 1 0-.33.38.38 0 0 1 .23-.24l2.28-1-2.28-.9a.43.43 0 0 1 -.23-.25.42.42 0 0 1 0-.33l.88-1.52a.4.4 0 0 1 .27-.19.39.39 0 0 1 .33.07l2 1.49-.35-2.37a.46.46 0 0 1 .1-.32.41.41 0 0 1 .31-.14h1.74a.41.41 0 0 1 .4.46l-.3 2.44 2-1.48a.43.43 0 0 1 .33-.07.45.45 0 0 1 .27.2l.85 1.51a.47.47 0 0 1 0 .33.39.39 0 0 1 -.23.24l-2.28.94zm13 0a.39.39 0 0 1 .23.24.44.44 0 0 1 0 .34l-.89 1.51a.4.4 0 0 1 -.28.19.41.41 0 0 1 -.32-.08l-1.91-1.48.3 2.45a.41.41 0 0 1 -.1.32.41.41 0 0 1 -.3.14h-1.75a.41.41 0 0 1 -.3-.14.41.41 0 0 1 -.1-.32l.31-2.46-2 1.49a.42.42 0 0 1 -.33.08.45.45 0 0 1 -.27-.2l-.85-1.51a.47.47 0 0 1 0-.33.39.39 0 0 1 .2-.24l2.27-1-2.27-.9a.43.43 0 0 1 -.23-.25.42.42 0 0 1 0-.33l.92-1.52a.4.4 0 0 1 .27-.19.37.37 0 0 1 .32.07l2 1.49-.34-2.37a.41.41 0 0 1 .1-.32.41.41 0 0 1 .3-.14h1.75a.41.41 0 0 1 .3.14.41.41 0 0 1 .1.32l-.31 2.44 2-1.48a.42.42 0 0 1 .33-.07.45.45 0 0 1 .27.2l.84 1.51a.38.38 0 0 1 0 .33.38.38 0 0 1 -.22.24l-2.28.94zm13 0a.37.37 0 0 1 .23.24.4.4 0 0 1 0 .34l-.9 1.51a.4.4 0 0 1 -.27.19.41.41 0 0 1 -.32-.08l-1.92-1.48.31 2.45a.41.41 0 0 1 -.41.46h-1.7a.41.41 0 0 1 -.3-.14.41.41 0 0 1 -.1-.32l.31-2.46-2 1.49a.44.44 0 0 1 -.33.08.43.43 0 0 1 -.27-.2l-.85-1.51a.38.38 0 0 1 0-.33.39.39 0 0 1 .23-.24l2.27-1-2.34-.9a.42.42 0 0 1 -.19-.58l.92-1.52a.36.36 0 0 1 .27-.19.37.37 0 0 1 .32.07l2 1.49-.32-2.37a.41.41 0 0 1 .1-.32.41.41 0 0 1 .3-.14h1.74a.41.41 0 0 1 .41.46l-.31 2.44 2-1.48a.4.4 0 0 1 .59.13l.85 1.51a.38.38 0 0 1 0 .33.39.39 0 0 1 -.23.24l-2.27.94z" fill="#0090ed"/><path d="m193.26 122.56a5.24 5.24 0 0 0 1.22-3.33v-63.77a5.33 5.33 0 0 0 -5.31-5.31h-90.34a5.33 5.33 0 0 0 -5.31 5.31v63.77a5.24 5.24 0 0 0 1.22 3.33l-11.85 2v5.31h122.22v-5.31z" fill="#9f9fad"/><path d="m153.74 60.77-54.91 54.91a1.77 1.77 0 0 0 1.77 1.77h86.8a1.77 1.77 0 0 0 1.77-1.77v-54.91z" fill="#592acb"/><path d="m98.83 60.77v54.91l54.91-54.91z" fill="#6736d8"/><path d="m187.4 55.46h-86.8a1.78 1.78 0 0 0 -1.77 1.77v3.54h90.34v-3.54a1.78 1.78 0 0 0 -1.77-1.77z" fill="#592acb"/><path d="m187.56 123.86h-86.79a.89.89 0 1 1 0-1.77h86.79a.89.89 0 0 1 0 1.77z" fill="#8f8f9d"/><path d="m137.47 126.17h12.4" fill="none" stroke="#80808e" stroke-linecap="round" stroke-linejoin="round"/><g fill="#fff"><path d="m156.45 76-3.87-2.21-8.83-5.06-.28-.16a4.71 4.71 0 0 0 -4.68 0l-.28.16-12.42 7.11-.28.16a4.66 4.66 0 0 0 -2.34 4v14.84a4.63 4.63 0 0 0 2.34 4l12.7 7.28a2.05 2.05 0 0 0 1 .26 2 2 0 0 0 1.69-1 1.94 1.94 0 0 0 -.72-2.64l-12.48-7.07a1.34 1.34 0 0 1 -.67-1.15v-14.23a1.34 1.34 0 0 1 .67-1.15l2.2-1.26 10.27-5.88a1.34 1.34 0 0 1 1.33 0l12.42 7.11a1.34 1.34 0 0 1 .67 1.15v14.26a1.34 1.34 0 0 1 -.67 1.15l-4.2 2.4-2.12-3.19a10.15 10.15 0 1 0 -6.77 2.57 10.2 10.2 0 0 0 3.42-.59l3.22 4.86a1.2 1.2 0 0 0 .17.21 2.09 2.09 0 0 0 .22.2h.06a1.91 1.91 0 0 0 .27.16h.07l.26.1h.06a1.77 1.77 0 0 0 .32 0h.43a1.94 1.94 0 0 0 .33-.07h.08l.25-.11h.05l6.08-3.48a4.63 4.63 0 0 0 2.34-4v-14.73a4.66 4.66 0 0 0 -2.31-4zm-21.55 11.4a6.24 6.24 0 1 1 6.23 6.19 6.21 6.21 0 0 1 -6.23-6.19z"/><path d="m156.45 76-3.87-2.21-8.83-5.06-.28-.16a4.71 4.71 0 0 0 -4.68 0l-.28.16-12.42 7.11-.28.16a4.66 4.66 0 0 0 -2.34 4v14.84a4.63 4.63 0 0 0 2.34 4l12.7 7.28a2.05 2.05 0 0 0 1 .26 2 2 0 0 0 1.69-1 1.94 1.94 0 0 0 -.72-2.64l-12.48-7.07a1.34 1.34 0 0 1 -.67-1.15v-14.23a1.34 1.34 0 0 1 .67-1.15l2.2-1.26 10.27-5.88a1.34 1.34 0 0 1 1.33 0l12.42 7.11a1.34 1.34 0 0 1 .67 1.15v14.26a1.34 1.34 0 0 1 -.67 1.15l-4.2 2.4-2.12-3.19a10.15 10.15 0 1 0 -6.77 2.57 10.2 10.2 0 0 0 3.42-.59l3.22 4.86a1.2 1.2 0 0 0 .17.21 2.09 2.09 0 0 0 .22.2h.06a1.91 1.91 0 0 0 .27.16h.07l.26.1h.06a1.77 1.77 0 0 0 .32 0h.43a1.94 1.94 0 0 0 .33-.07h.08l.25-.11h.05l6.08-3.48a4.63 4.63 0 0 0 2.34-4v-14.73a4.66 4.66 0 0 0 -2.31-4zm-21.55 11.4a6.24 6.24 0 1 1 6.23 6.19 6.21 6.21 0 0 1 -6.23-6.19z"/><path d="m156.45 76-3.87-2.21-5.4-3.09a4.14 4.14 0 0 0 -4-.06l-2.71 1.36a1.34 1.34 0 0 1 1.33 0l12.42 7.11a1.34 1.34 0 0 1 .67 1.15v14.26a1.32 1.32 0 0 1 -.67 1.15l-4.2 2.4.65 1.05a2.75 2.75 0 0 0 3.7.92l2.08-1.19a4.61 4.61 0 0 0 2.34-4v-14.85a4.63 4.63 0 0 0 -2.34-4z"/><path d="m154.89 80.29v6.9h3.9v-7.19a4.63 4.63 0 0 0 -2.34-4l-3.87-2.21-5.4-3.09a4.14 4.14 0 0 0 -4-.06l-2.71 1.36a1.34 1.34 0 0 1 1.33 0l12.42 7.11a1.34 1.34 0 0 1 .67 1.18z"/><path d="m154.89 88.31v6.21a1.32 1.32 0 0 1 -.67 1.15l-4.2 2.4.65 1.05a2.75 2.75 0 0 0 3.7.92l2.08-1.19a4.61 4.61 0 0 0 2.34-4v-6.54z" opacity=".9"/><path d="m149.72 97.62-1.82-2.74a10.11 10.11 0 0 1 -3.35 2l1.94 2.93c1.09-.72 2.17-1.44 3.23-2.19z" opacity=".9"/></g><rect fill="#ffbdc5" height="13.79" rx="5.4" width="44.43" x="178.92" y="85.86"/><path d="m222.52 81.78h-42.13a7.22 7.22 0 0 0 -7.2 7.2v6.53a7.22 7.22 0 0 0 7.2 7.2h42.13a7.22 7.22 0 0 0 7.2-7.2v-6.51a7.22 7.22 0 0 0 -7.2-7.22zm-29.91 11.41a.39.39 0 0 1 .23.24.44.44 0 0 1 0 .34l-.89 1.51a.4.4 0 0 1 -.28.19.41.41 0 0 1 -.32-.08l-1.91-1.48.3 2.45a.41.41 0 0 1 -.1.32.41.41 0 0 1 -.3.14h-1.75a.41.41 0 0 1 -.3-.14.41.41 0 0 1 -.1-.32l.31-2.46-2 1.49a.42.42 0 0 1 -.33.08.45.45 0 0 1 -.27-.2l-.9-1.51a.47.47 0 0 1 0-.33.39.39 0 0 1 .23-.24l2.27-1-2.27-1a.43.43 0 0 1 -.23-.19.42.42 0 0 1 0-.33l.87-1.48a.4.4 0 0 1 .27-.19.37.37 0 0 1 .32.07l2 1.49-.31-2.43a.41.41 0 0 1 .1-.32.41.41 0 0 1 .3-.14h1.75a.41.41 0 0 1 .3.14.41.41 0 0 1 .1.32l-.31 2.44 2-1.48a.42.42 0 0 1 .33-.07.45.45 0 0 1 .27.2l.84 1.51a.38.38 0 0 1 0 .33.38.38 0 0 1 -.22.24l-2.28.94zm13 0a.37.37 0 0 1 .23.24.4.4 0 0 1 0 .34l-.9 1.51a.4.4 0 0 1 -.27.19.41.41 0 0 1 -.32-.08l-1.92-1.48.31 2.45a.41.41 0 0 1 -.41.46h-1.74a.41.41 0 0 1 -.3-.14.41.41 0 0 1 -.1-.32l.31-2.46-2 1.49a.43.43 0 0 1 -.33.08.43.43 0 0 1 -.27-.2l-.85-1.51a.38.38 0 0 1 0-.33.39.39 0 0 1 .23-.24l2.27-1-2.28-1a.45.45 0 0 1 -.27-.19.42.42 0 0 1 0-.33l.88-1.48a.36.36 0 0 1 .27-.19.37.37 0 0 1 .32.07l2 1.49-.3-2.43a.41.41 0 0 1 .1-.32.41.41 0 0 1 .3-.14h1.74a.41.41 0 0 1 .41.46l-.31 2.44 2-1.48a.4.4 0 0 1 .59.13l.85 1.51a.38.38 0 0 1 0 .33.39.39 0 0 1 -.23.24l-2.27.94zm13 0a.39.39 0 0 1 .23.24.4.4 0 0 1 0 .34l-.9 1.51a.4.4 0 0 1 -.27.19.43.43 0 0 1 -.33-.08l-1.91-1.48.3 2.45a.4.4 0 0 1 -.4.46h-1.74a.41.41 0 0 1 -.4-.46l.3-2.46-2 1.49a.41.41 0 0 1 -.32.08.4.4 0 0 1 -.27-.2l-.85-1.51a.38.38 0 0 1 0-.33.39.39 0 0 1 .23-.24l2.27-1-2.28-1a.39.39 0 0 1 -.22-.25.38.38 0 0 1 0-.33l.87-1.48a.4.4 0 0 1 .27-.19.38.38 0 0 1 .33.07l2 1.49-.3-2.43a.41.41 0 0 1 .4-.46h1.74a.4.4 0 0 1 .4.46l-.3 2.44 2-1.48a.4.4 0 0 1 .59.13l.85 1.51a.38.38 0 0 1 0 .33.39.39 0 0 1 -.23.24l-2.27.94z" fill="#ff298a"/><rect fill="#ff298a" height="16.85" rx="5.4" width="49.79" x="77.09" y="118.86"/><path d="m123.41 117.07h-42.13a7.22 7.22 0 0 0 -7.2 7.2v6.54a7.22 7.22 0 0 0 7.2 7.2h42.13a7.22 7.22 0 0 0 7.2-7.2v-6.54a7.22 7.22 0 0 0 -7.2-7.2zm-29.91 11.42a.39.39 0 0 1 .23.24.45.45 0 0 1 0 .34l-.89 1.5a.45.45 0 0 1 -.28.2.41.41 0 0 1 -.32-.08l-1.91-1.48.3 2.45a.42.42 0 0 1 -.1.32.43.43 0 0 1 -.3.13h-1.78a.44.44 0 0 1 -.31-.13.42.42 0 0 1 -.1-.32l.31-2.47-2 1.5a.38.38 0 0 1 -.33.07.4.4 0 0 1 -.27-.19l-.85-1.51a.47.47 0 0 1 0-.33.39.39 0 0 1 .23-.24l2.28-1-2.28-1a.43.43 0 0 1 -.23-.25.42.42 0 0 1 0-.33l.87-1.48a.4.4 0 0 1 .27-.19.37.37 0 0 1 .32.07l2 1.49-.36-2.37a.4.4 0 0 1 .1-.32.41.41 0 0 1 .31-.14h1.74a.41.41 0 0 1 .4.46l-.31 2.44 2-1.48a.41.41 0 0 1 .6.13l.84 1.5a.4.4 0 0 1 -.19.58l-2.28.94zm13 0a.37.37 0 0 1 .23.24.41.41 0 0 1 0 .34l-.9 1.5a.45.45 0 0 1 -.27.2.41.41 0 0 1 -.32-.08l-1.92-1.48.31 2.45a.42.42 0 0 1 -.1.32.44.44 0 0 1 -.31.13h-1.74a.43.43 0 0 1 -.3-.13.42.42 0 0 1 -.1-.32l.31-2.47-2 1.5a.39.39 0 0 1 -.33.07.38.38 0 0 1 -.27-.19l-.79-1.51a.38.38 0 0 1 0-.33.39.39 0 0 1 .23-.24l2.27-1-2.28-1a.45.45 0 0 1 -.22-.25.39.39 0 0 1 0-.33l.87-1.48a.36.36 0 0 1 .27-.19.37.37 0 0 1 .32.07l2 1.49-.3-2.43a.41.41 0 0 1 .4-.46h1.74a.41.41 0 0 1 .31.14.4.4 0 0 1 .1.32l-.31 2.44 2-1.48a.42.42 0 0 1 .33-.07.38.38 0 0 1 .26.2l.85 1.5a.41.41 0 0 1 0 .34.42.42 0 0 1 -.23.24l-2.27.94zm13 0a.39.39 0 0 1 .23.24.41.41 0 0 1 0 .34l-.9 1.5a.45.45 0 0 1 -.27.2.43.43 0 0 1 -.33-.08l-1.91-1.48.3 2.45a.42.42 0 0 1 -.09.32.44.44 0 0 1 -.31.13h-1.74a.4.4 0 0 1 -.3-.13.39.39 0 0 1 -.1-.32l.3-2.47-2 1.5a.37.37 0 0 1 -.32.07.36.36 0 0 1 -.27-.19l-.85-1.51a.38.38 0 0 1 0-.33.39.39 0 0 1 .23-.24l2.27-1-2.28-1a.39.39 0 0 1 -.22-.25.38.38 0 0 1 0-.33l.87-1.48a.4.4 0 0 1 .27-.19.38.38 0 0 1 .33.07l2 1.49-.3-2.43a.37.37 0 0 1 .1-.32.37.37 0 0 1 .3-.14h1.74a.41.41 0 0 1 .31.14.4.4 0 0 1 .09.32l-.3 2.44 2-1.48a.41.41 0 0 1 .32-.07.37.37 0 0 1 .27.2l.85 1.5a.41.41 0 0 1 0 .34.42.42 0 0 1 -.23.24l-2.27.94z" fill="#ffd567"/></svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/components/newtab/data/content/assets/trailhead/card-illo-ffsend.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<svg viewBox="0 0 288 180" xmlns="http://www.w3.org/2000/svg"><circle cx="144" cy="90" fill="#bfbfc9" opacity=".15" r="72"/><path d="m121.16 76.34-22-8.46a1 1 0 0 0 -1.27.57l-14.09 36.65a1 1 0 0 0 .57 1.27l26.57 10.22a1 1 0 0 0 1.27-.57l12.33-32.02z" fill="#00b3f4"/><path d="m119.4 80.92a1 1 0 0 0 .56 1.27l4.58 1.81-3.38-7.61z" fill="#0090ed"/><path d="m114.9 92.42a2.28 2.28 0 0 0 -1.74.06l-7.16 3.24 5.68-15.07-3.59-3.38-6.35 16.84-3.22-7.11a2.27 2.27 0 0 0 -3-1.13 2.28 2.28 0 0 0 -1.14 3l5.49 12.13a2.28 2.28 0 0 0 3 1.14l12.13-5.52a2.25 2.25 0 0 0 1.19-1.27 2.28 2.28 0 0 0 -1.33-2.93z" fill="#0090ed"/><path d="m116.54 67.77a2.28 2.28 0 1 0 -4.26-1.6l-4.18 11.1 3.59 3.38z" fill="#0060df"/><path d="m140.21 54.66a27.84 27.84 0 0 0 -10.21-11.78 20.44 20.44 0 0 1 5.45 9.63 35.22 35.22 0 0 1 -1.38 20.94 2.07 2.07 0 0 1 -.76 1 1.82 1.82 0 0 1 -.27.15l5.96 2.26a1.68 1.68 0 0 0 1.3 0 1.65 1.65 0 0 0 .88-1 27.83 27.83 0 0 0 -.97-21.2z" fill="#7542e5"/><path d="m95.77 59a35.16 35.16 0 0 1 12.79-16.64 20.2 20.2 0 0 1 10.69-3.64 28 28 0 0 0 -30.53 17.38 1.72 1.72 0 0 0 0 1.3 1.66 1.66 0 0 0 .94.9l6.08 2.29a1.42 1.42 0 0 1 -.1-.29 2.19 2.19 0 0 1 .13-1.3z" fill="#ffd567"/><path d="m134.07 73.45a35.22 35.22 0 0 0 1.38-20.94 20.44 20.44 0 0 0 -5.45-9.63 27.33 27.33 0 0 0 -4.42-2.32c1.38 1.45 2 4.58 1.72 9.06a71.37 71.37 0 0 1 -4.57 19.43 4.26 4.26 0 0 1 -.57 1.09 1.23 1.23 0 0 1 -.28.28l11.12 4.21a1.82 1.82 0 0 0 .27-.15 2.07 2.07 0 0 0 .8-1.03z" fill="#b833e1"/><path d="m106.24 64.14a4.42 4.42 0 0 1 .29-1.2 71.15 71.15 0 0 1 9.39-17.61c2.73-3.51 5.23-5.46 7.22-5.67a27.8 27.8 0 0 0 -3.89-.92 20.2 20.2 0 0 0 -10.69 3.64 35.16 35.16 0 0 0 -12.79 16.62 2.19 2.19 0 0 0 -.1 1.28 1.42 1.42 0 0 0 .1.29l10.49 4a1.61 1.61 0 0 1 -.02-.43z" fill="#ff8a50"/><path d="m123.14 39.66c-2 .21-4.49 2.16-7.22 5.67a71.15 71.15 0 0 0 -9.39 17.61 4.42 4.42 0 0 0 -.29 1.2 1.61 1.61 0 0 0 0 .4l15.62 5.88a1.23 1.23 0 0 0 .28-.28 4.26 4.26 0 0 0 .57-1.09 71.37 71.37 0 0 0 4.57-19.43c.27-4.48-.34-7.61-1.72-9.06l-.93-.38c-.48-.18-.98-.36-1.49-.52z" fill="#ff298a"/><path d="m173.56 92.43-31.2-12a1.4 1.4 0 0 0 -1.8.8l-20 52a1.4 1.4 0 0 0 .8 1.8l37.7 14.51a1.4 1.4 0 0 0 1.8-.8l17.51-45.51z" fill="#00b3f4"/><path d="m171.06 98.93a1.39 1.39 0 0 0 .8 1.8l6.5 2.5-4.8-10.8z" fill="#0090ed"/><path d="m164.68 115.25a3.25 3.25 0 0 0 -2.47.08l-10.15 4.6 8.06-21.39-5.12-4.79-9 23.9-4.59-10.15a3.22 3.22 0 0 0 -4.27-1.61 3.21 3.21 0 0 0 -1.69 1.8 3.16 3.16 0 0 0 .08 2.47l7.8 17.25a3.2 3.2 0 0 0 1.81 1.69 3.25 3.25 0 0 0 2.47-.08l17.24-7.81a3.22 3.22 0 0 0 1.61-4.27 3.21 3.21 0 0 0 -1.78-1.69z" fill="#0090ed"/><path d="m167 80.27a3.23 3.23 0 1 0 -6-2.27l-6 15.75 5.09 4.79z" fill="#0060df"/><path d="m200.58 61.66a39.46 39.46 0 0 0 -14.48-16.66 29 29 0 0 1 7.73 13.67c2.43 9 1.74 19.57-2 29.71a3.1 3.1 0 0 1 -1.09 1.47 2 2 0 0 1 -.38.2l8.41 3.17a2.4 2.4 0 0 0 3.09-1.43 39.52 39.52 0 0 0 -1.28-30.13z" fill="#7542e5"/><path d="m137.54 67.85c3.91-10 10.36-18.44 18.14-23.6a28.66 28.66 0 0 1 15.17-5.17 39.75 39.75 0 0 0 -43.32 24.64 2.39 2.39 0 0 0 1.38 3.11l8.63 3.25a1.34 1.34 0 0 1 -.15-.41 3 3 0 0 1 .15-1.82z" fill="#ffd567"/><path d="m191.88 88.33c3.69-10.14 4.38-20.69 2-29.71a29 29 0 0 0 -7.78-13.62 40.63 40.63 0 0 0 -6.27-3.29c2 2.06 2.82 6.51 2.44 12.85-.48 7.81-2.78 17.6-6.48 27.57a6.38 6.38 0 0 1 -.81 1.55 2 2 0 0 1 -.4.41l15.83 6a2 2 0 0 0 .38-.2 3.1 3.1 0 0 0 1.09-1.56z" fill="#b833e1"/><path d="m152.38 75.12a6.66 6.66 0 0 1 .42-1.7c3.8-9.94 8.53-18.81 13.33-25 3.86-5 7.42-7.75 10.24-8a39.5 39.5 0 0 0 -5.52-1.31 28.66 28.66 0 0 0 -15.17 5.17c-7.78 5.16-14.23 13.55-18.14 23.6a3 3 0 0 0 -.15 1.82 1.34 1.34 0 0 0 .15.41l14.88 5.61a2 2 0 0 1 -.04-.6z" fill="#ff8a50"/><path d="m176.37 40.39c-2.82.29-6.38 3.06-10.24 8-4.8 6.18-9.53 15-13.33 25a6.66 6.66 0 0 0 -.42 1.7 2 2 0 0 0 0 .57l22.2 8.34a2 2 0 0 0 .4-.41 6.38 6.38 0 0 0 .81-1.55c3.7-10 6-19.76 6.48-27.57.38-6.34-.48-10.79-2.44-12.85-.44-.18-.87-.37-1.32-.53-.71-.23-1.42-.47-2.14-.7z" fill="#ff298a"/></svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/components/newtab/data/content/assets/trailhead/card-illo-import.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg viewBox="0 0 288 180" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="a"><path d="m187.39 55.55h-86.79a1.77 1.77 0 0 0 -1.77 1.77v58.45a1.78 1.78 0 0 0 1.77 1.77h86.79a1.77 1.77 0 0 0 1.77-1.77v-58.45a1.77 1.77 0 0 0 -1.77-1.77z"/></clipPath><circle cx="144" cy="90" fill="#bfbfc9" opacity=".15" r="72"/><path d="m193.26 122.56a5.24 5.24 0 0 0 1.22-3.33v-63.77a5.33 5.33 0 0 0 -5.31-5.31h-90.34a5.33 5.33 0 0 0 -5.31 5.31v63.77a5.24 5.24 0 0 0 1.22 3.33l-11.85 2v5.31h122.22v-5.31z" fill="#9f9fad"/><path d="m153.74 60.77-54.91 54.91a1.77 1.77 0 0 0 1.77 1.77h86.8a1.77 1.77 0 0 0 1.77-1.77v-54.91z" fill="#592acb"/><path d="m98.83 60.77v54.91l54.91-54.91z" fill="#6736d8"/><path d="m187.4 55.46h-86.8a1.78 1.78 0 0 0 -1.77 1.77v3.54h90.34v-3.54a1.78 1.78 0 0 0 -1.77-1.77z" fill="#592acb"/><path d="m187.56 123.86h-86.79a.89.89 0 1 1 0-1.77h86.79a.89.89 0 0 1 0 1.77z" fill="#8f8f9d"/><path d="m137.47 126.17h12.4" fill="none" stroke="#80808e" stroke-linecap="round" stroke-linejoin="round"/><path d="m142.65 49.58a1.48 1.48 0 0 0 -1.16-1l-5.57-1-2.63-5.27a1.5 1.5 0 0 0 -2.69 0l-2.6 5.27-5.57 1a1.5 1.5 0 0 0 -.84 2.5l4 4.3-.84 5.95a1.52 1.52 0 0 0 1.46 1.67 1.57 1.57 0 0 0 .7-.17l5-2.63 5 2.62a1.47 1.47 0 0 0 1.56-.1 1.51 1.51 0 0 0 .63-1.43l-.84-5.95 4-4.3a1.5 1.5 0 0 0 .39-1.46z" fill="#00b3f4"/><path d="m166.76 34.46a1.47 1.47 0 0 0 -1.16-1l-5.57-1-2.63-5.28a1.5 1.5 0 0 0 -2.69 0l-2.63 5.28-5.57 1a1.51 1.51 0 0 0 -.84 2.51l4 4.29-.84 6a1.53 1.53 0 0 0 .63 1.44 1.51 1.51 0 0 0 .86.27 1.38 1.38 0 0 0 .69-.17l5-2.62 5 2.62a1.51 1.51 0 0 0 1.56-.1 1.53 1.53 0 0 0 .63-1.44l-.84-6 4-4.29a1.5 1.5 0 0 0 .4-1.51z" fill="#0090ed"/><path d="m147 15.61a1.48 1.48 0 0 0 -1.16-1l-5.58-1-2.63-5.28a1.5 1.5 0 0 0 -2.68 0l-2.63 5.28-5.57 1a1.48 1.48 0 0 0 -1.17 1 1.53 1.53 0 0 0 .32 1.5l4 4.29-.83 6a1.49 1.49 0 0 0 1.49 1.71 1.44 1.44 0 0 0 .69-.17l5-2.62 5 2.62a1.5 1.5 0 0 0 2.18-1.54l-.83-6 4-4.29a1.51 1.51 0 0 0 .4-1.5z" fill="#0df"/><g clip-path="url(#a)"><path d="m124.24 109.74a1.48 1.48 0 0 0 -1.16-1l-5.57-1-2.63-5.28a1.5 1.5 0 0 0 -2.69 0l-2.63 5.28-5.57 1a1.5 1.5 0 0 0 -.84 2.5l4 4.29-.84 6a1.5 1.5 0 0 0 1.49 1.71 1.38 1.38 0 0 0 .69-.17l5-2.62 5 2.62a1.5 1.5 0 0 0 2.18-1.54l-.83-6 4-4.29a1.5 1.5 0 0 0 .4-1.5z" fill="#00b3f4"/><path d="m148.82 109.39a1.5 1.5 0 0 0 -1.16-1l-5.58-1-2.62-5.28a1.5 1.5 0 0 0 -2.69 0l-2.63 5.28-5.57 1a1.5 1.5 0 0 0 -.84 2.5l4 4.29-.83 6a1.48 1.48 0 0 0 .62 1.43 1.53 1.53 0 0 0 .87.28 1.45 1.45 0 0 0 .69-.18l5-2.62 5 2.62a1.5 1.5 0 0 0 2.18-1.54l-.83-5.95 4-4.29a1.5 1.5 0 0 0 .39-1.54z" fill="#00b3f4"/><path d="m161.75 107.31a1.47 1.47 0 0 0 -1.16-1l-5.57-1-2.63-5.31a1.5 1.5 0 0 0 -2.69 0l-2.63 5.28-5.57 1a1.53 1.53 0 0 0 -1.17 1 1.51 1.51 0 0 0 .33 1.5l4 4.29-.83 6a1.5 1.5 0 0 0 1.49 1.71 1.41 1.41 0 0 0 .69-.17l5-2.62 5 2.62a1.5 1.5 0 0 0 2.18-1.54l-.83-6 4-4.29a1.5 1.5 0 0 0 .39-1.47z" fill="#0df"/><path d="m139.18 105.88a1.53 1.53 0 0 0 -1.16-1l-5.58-1-2.63-5.28a1.5 1.5 0 0 0 -2.69 0l-2.63 5.28-5.57 1a1.5 1.5 0 0 0 -.84 2.5l4 4.3-.83 5.95a1.48 1.48 0 0 0 .62 1.43 1.45 1.45 0 0 0 .86.28 1.57 1.57 0 0 0 .7-.17l5-2.63 5 2.63a1.51 1.51 0 0 0 1.57-.11 1.48 1.48 0 0 0 .62-1.43l-.83-5.95 4-4.3a1.5 1.5 0 0 0 .39-1.5z" fill="#0090ed"/><path d="m154.3 116.29a1.52 1.52 0 0 0 -1.16-1l-5.58-1-2.62-5.29a1.51 1.51 0 0 0 -1.35-.83 1.49 1.49 0 0 0 -1.34.83l-2.63 5.28-5.57 1a1.52 1.52 0 0 0 -1.17 1 1.5 1.5 0 0 0 .33 1.5l4 4.3-.83 5.95a1.49 1.49 0 0 0 1.49 1.71 1.56 1.56 0 0 0 .69-.17l5-2.63 5 2.63a1.5 1.5 0 0 0 2.18-1.54l-.83-5.95 4-4.29a1.52 1.52 0 0 0 .39-1.5z" fill="#00b3f4"/><path d="m173.15 113a1.52 1.52 0 0 0 -1.16-1l-5.58-1-2.63-5.28a1.5 1.5 0 0 0 -2.68 0l-2.63 5.28-5.57 1a1.52 1.52 0 0 0 -1.17 1 1.5 1.5 0 0 0 .33 1.5l4 4.3-.83 6a1.49 1.49 0 0 0 1.49 1.71 1.56 1.56 0 0 0 .69-.17l5-2.63 5 2.63a1.5 1.5 0 0 0 2.18-1.54l-.83-6 4-4.29a1.52 1.52 0 0 0 .39-1.51z" fill="#0090ed"/><path d="m113.09 104.46a1.47 1.47 0 0 0 -1.16-1l-5.57-1-2.63-5.28a1.5 1.5 0 0 0 -2.69 0l-2.63 5.28-5.57 1a1.47 1.47 0 0 0 -1.16 1 1.5 1.5 0 0 0 .32 1.54l4 4.29-.83 6a1.49 1.49 0 0 0 1.48 1.71 1.45 1.45 0 0 0 .7-.17l5-2.62 5 2.62a1.51 1.51 0 0 0 2.19-1.54l-.84-6 4-4.29a1.5 1.5 0 0 0 .39-1.54z" fill="#0df"/><path d="m107 115.2a1.5 1.5 0 0 0 -1.16-1l-5.58-1-2.63-5.28a1.5 1.5 0 0 0 -2.69 0l-2.62 5.28-5.58 1a1.5 1.5 0 0 0 -.84 2.5l4 4.29-.83 6a1.49 1.49 0 0 0 1.49 1.71 1.44 1.44 0 0 0 .69-.17l5-2.62 5 2.62a1.5 1.5 0 0 0 2.18-1.54l-.83-6 4-4.29a1.5 1.5 0 0 0 .4-1.5z" fill="#00b3f4"/><path d="m125.37 115.42a1.48 1.48 0 0 0 -1.16-1l-5.58-1-2.63-5.28a1.5 1.5 0 0 0 -2.68 0l-2.63 5.28-5.57 1a1.5 1.5 0 0 0 -.84 2.51l4 4.29-.83 6a1.49 1.49 0 0 0 1.49 1.71 1.44 1.44 0 0 0 .69-.17l5-2.62 5 2.62a1.5 1.5 0 0 0 2.18-1.54l-.83-6 4-4.29a1.51 1.51 0 0 0 .39-1.51z" fill="#0090ed"/><path d="m138.3 113.33a1.52 1.52 0 0 0 -1.16-1l-5.58-1-2.62-5.28a1.51 1.51 0 0 0 -1.35-.83 1.49 1.49 0 0 0 -1.34.83l-2.63 5.28-5.57 1a1.52 1.52 0 0 0 -1.17 1 1.5 1.5 0 0 0 .33 1.5l4 4.3-.83 6a1.49 1.49 0 0 0 1.49 1.71 1.56 1.56 0 0 0 .69-.17l5-2.63 5 2.63a1.5 1.5 0 0 0 2.18-1.54l-.83-6 4-4.29a1.52 1.52 0 0 0 .39-1.51z" fill="#0df"/><path d="m180.52 110.12a1.51 1.51 0 0 0 -1.17-1l-5.57-1-2.65-5.27a1.49 1.49 0 0 0 -1.34-.83 1.47 1.47 0 0 0 -1.34.84l-2.62 5.28-5.57 1a1.51 1.51 0 0 0 -.83 2.51l4 4.28-.81 6a1.51 1.51 0 0 0 .62 1.44 1.51 1.51 0 0 0 1.56.1l5-2.64 5 2.61a1.51 1.51 0 0 0 2.18-1.55l-.85-5.94 4-4.31a1.5 1.5 0 0 0 .39-1.52z" fill="#00b3f4"/><path d="m205.09 109.71a1.47 1.47 0 0 0 -1.16-1l-5.58-1-2.64-5.27a1.5 1.5 0 0 0 -2.69 0l-2.61 5.29-5.57 1a1.5 1.5 0 0 0 -.84 2.5l4 4.29-.82 6a1.51 1.51 0 0 0 .63 1.43 1.53 1.53 0 0 0 .87.28 1.45 1.45 0 0 0 .69-.18l5-2.64 5.05 2.62a1.51 1.51 0 0 0 2.18-1.55l-.85-5.95 4-4.3a1.48 1.48 0 0 0 .34-1.52z" fill="#00b3f4"/><path d="m218 107.59a1.51 1.51 0 0 0 -1.16-1l-5.58-1-2.64-5.27a1.51 1.51 0 0 0 -1.35-.83 1.48 1.48 0 0 0 -1.34.84l-2.62 5.28-5.57 1a1.51 1.51 0 0 0 -1.16 1 1.48 1.48 0 0 0 .33 1.49l4 4.29-.82 5.95a1.49 1.49 0 0 0 .63 1.44 1.5 1.5 0 0 0 1.55.1l5-2.64 5 2.61a1.51 1.51 0 0 0 2.18-1.55l-.85-5.94 4-4.31a1.5 1.5 0 0 0 .4-1.46z" fill="#0df"/><path d="m195.44 106.22a1.49 1.49 0 0 0 -1.16-1l-5.58-1-2.64-5.22a1.51 1.51 0 0 0 -2.69 0l-2.62 5.29-5.56 1a1.5 1.5 0 0 0 -1.17 1 1.53 1.53 0 0 0 .33 1.5l4 4.28-.82 6a1.51 1.51 0 0 0 .63 1.43 1.51 1.51 0 0 0 1.56.1l5-2.64 5 2.61a1.5 1.5 0 0 0 2.28-1.57l-.84-6 4-4.3a1.52 1.52 0 0 0 .28-1.48z" fill="#0090ed"/><path d="m210.59 116.6a1.48 1.48 0 0 0 -1.16-1l-5.58-1-2.64-5.27a1.5 1.5 0 0 0 -2.69 0l-2.62 5.29-5.56 1a1.5 1.5 0 0 0 -1.17 1 1.52 1.52 0 0 0 .33 1.5l4 4.29-.82 5.95a1.51 1.51 0 0 0 .63 1.43 1.46 1.46 0 0 0 1.56.1l5-2.64 5 2.62a1.51 1.51 0 0 0 2.18-1.55l-.84-5.95 4-4.3a1.5 1.5 0 0 0 .38-1.47z" fill="#00b3f4"/><path d="m229.43 113.26a1.5 1.5 0 0 0 -1.16-1l-5.58-1-2.64-5.26a1.5 1.5 0 0 0 -2.69 0l-2.62 5.29-5.56 1a1.5 1.5 0 0 0 -.84 2.5l4 4.29-.82 6a1.53 1.53 0 0 0 .63 1.44 1.51 1.51 0 0 0 .86.27 1.57 1.57 0 0 0 .7-.17l5-2.64 5 2.61a1.51 1.51 0 0 0 2.29-1.59l-.84-6 4-4.3a1.5 1.5 0 0 0 .27-1.44z" fill="#0090ed"/><path d="m163.24 115.62a1.47 1.47 0 0 0 -1.16-1l-5.58-1-2.64-5.28a1.46 1.46 0 0 0 -1.34-.82 1.5 1.5 0 0 0 -1.35.83l-2.61 5.29-5.57 1a1.5 1.5 0 0 0 -.83 2.5l4 4.28-.81 6a1.49 1.49 0 0 0 1.49 1.7 1.41 1.41 0 0 0 .69-.17l5-2.64 5.05 2.61a1.5 1.5 0 0 0 2.18-1.54l-.85-5.95 4-4.3a1.49 1.49 0 0 0 .33-1.51z" fill="#00b3f4"/><path d="m181.66 115.79a1.5 1.5 0 0 0 -1.17-1l-5.57-1-2.65-5.28a1.49 1.49 0 0 0 -1.34-.83 1.5 1.5 0 0 0 -1.34.84l-2.59 5.32-5.57 1a1.48 1.48 0 0 0 -1.16 1 1.5 1.5 0 0 0 .33 1.5l4 4.28-.81 6a1.48 1.48 0 0 0 .62 1.43 1.54 1.54 0 0 0 .87.27 1.41 1.41 0 0 0 .69-.17l5-2.64 5 2.61a1.5 1.5 0 0 0 2.18-1.54l-.85-6 4-4.3a1.52 1.52 0 0 0 .36-1.49z" fill="#0090ed"/><path d="m194.58 113.68a1.48 1.48 0 0 0 -1.16-1l-5.58-1-2.64-5.27a1.5 1.5 0 0 0 -2.69 0l-2.61 5.29-5.57 1a1.5 1.5 0 0 0 -.84 2.5l4 4.29-.82 6a1.53 1.53 0 0 0 .63 1.44 1.52 1.52 0 0 0 .87.27 1.56 1.56 0 0 0 .69-.17l5-2.64 5 2.61a1.49 1.49 0 0 0 1.56-.11 1.53 1.53 0 0 0 .62-1.44l-.85-5.94 4-4.31a1.48 1.48 0 0 0 .39-1.52z" fill="#0df"/></g><path d="m159.69 81.08a2.18 2.18 0 0 0 -1.7-1.47l-8.17-1.46-3.82-7.73a2.2 2.2 0 0 0 -3.94 0l-3.85 7.73-8.21 1.46a2.21 2.21 0 0 0 -1.23 3.67l5.86 6.29-1.22 8.72a2.2 2.2 0 0 0 2.18 2.51 2.16 2.16 0 0 0 1-.25l7.41-3.85 7.38 3.85a2.22 2.22 0 0 0 2.29-.15 2.18 2.18 0 0 0 .91-2.11l-1.22-8.72 5.86-6.29a2.19 2.19 0 0 0 .47-2.2z" fill="#fff"/></svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/components/newtab/data/content/assets/trailhead/card-illo-lockwise.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<svg viewBox="0 0 288 180" xmlns="http://www.w3.org/2000/svg"><circle cx="144" cy="90" fill="#bfbfc9" opacity=".15" r="72"/><rect fill="#0df" height="15.49" rx=".5" width="48.16" x="43.04" y="82.03"/><path d="m88.11 79h-42.11a7.22 7.22 0 0 0 -7.2 7.2v6.53a7.22 7.22 0 0 0 7.2 7.2h42.11a7.22 7.22 0 0 0 7.2-7.2v-6.55a7.22 7.22 0 0 0 -7.2-7.18zm-29.91 11.39a.4.4 0 0 1 .19.58l-.89 1.51a.4.4 0 0 1 -.27.19.42.42 0 0 1 -.33-.08l-1.9-1.47.3 2.44a.41.41 0 0 1 -.4.46h-1.75a.41.41 0 0 1 -.31-.14.46.46 0 0 1 -.1-.32l.31-2.46-2 1.5a.42.42 0 0 1 -.33.07.45.45 0 0 1 -.27-.2l-.83-1.47a.38.38 0 0 1 0-.33.38.38 0 0 1 .22-.24l2.28-1-2.28-1a.39.39 0 0 1 -.23-.24.45.45 0 0 1 0-.34l.87-1.48a.4.4 0 0 1 .27-.19.48.48 0 0 1 .33.07l2 1.49-.31-2.43a.46.46 0 0 1 .1-.32.41.41 0 0 1 .31-.14h1.74a.41.41 0 0 1 .4.46l-.32 2.46 2-1.47a.44.44 0 0 1 .33-.08.45.45 0 0 1 .27.2l.85 1.51a.42.42 0 0 1 0 .33.39.39 0 0 1 -.23.24l-2.28 1zm13 0a.38.38 0 0 1 .23.25.42.42 0 0 1 0 .33l-.89 1.51a.4.4 0 0 1 -.28.19.41.41 0 0 1 -.32-.08l-1.94-1.47.3 2.44a.41.41 0 0 1 -.1.32.41.41 0 0 1 -.3.14h-1.71a.41.41 0 0 1 -.41-.46l.31-2.46-2 1.5a.42.42 0 0 1 -.33.07.45.45 0 0 1 -.27-.2l-.84-1.47a.47.47 0 0 1 0-.33.39.39 0 0 1 .23-.24l2.28-1-2.28-1a.39.39 0 0 1 -.23-.24.45.45 0 0 1 0-.34l.87-1.48a.4.4 0 0 1 .27-.19.45.45 0 0 1 .32.07l2 1.49-.31-2.43a.41.41 0 0 1 .41-.46h1.74a.41.41 0 0 1 .3.14.41.41 0 0 1 .1.32l-.35 2.46 2-1.47a.42.42 0 0 1 .33-.08.45.45 0 0 1 .27.2l.85 1.51a.47.47 0 0 1 0 .33.42.42 0 0 1 -.23.24l-2.28 1zm13 0a.36.36 0 0 1 .23.25.38.38 0 0 1 0 .33l-.9 1.51a.4.4 0 0 1 -.27.19.41.41 0 0 1 -.32-.08l-1.92-1.47.31 2.44a.41.41 0 0 1 -.33.44h-1.78a.41.41 0 0 1 -.3-.14.41.41 0 0 1 -.1-.32l.31-2.46-2 1.5a.41.41 0 0 1 -.6-.13l-.84-1.45a.38.38 0 0 1 0-.33.39.39 0 0 1 .23-.24l2.27-1-2.28-1a.4.4 0 0 1 -.22-.24.41.41 0 0 1 0-.34l.87-1.48a.36.36 0 0 1 .27-.19.45.45 0 0 1 .32.07l2 1.49-.3-2.43a.41.41 0 0 1 .1-.32.41.41 0 0 1 .3-.14h1.75a.41.41 0 0 1 .41.46l-.31 2.44 2-1.47a.42.42 0 0 1 .33-.08.45.45 0 0 1 .27.2l.84 1.51a.38.38 0 0 1 0 .33.39.39 0 0 1 -.23.24l-2.27 1z" fill="#0090ed"/><path d="m161.09 121.72a5.24 5.24 0 0 0 1.22-3.34v-63.76a5.33 5.33 0 0 0 -5.31-5.32h-90.33a5.34 5.34 0 0 0 -5.32 5.32v63.76a5.24 5.24 0 0 0 1.22 3.34l-11.84 2v5.28h122.21v-5.3z" fill="#9f9fad"/><path d="m121.58 59.93-54.91 54.91a1.77 1.77 0 0 0 1.77 1.77h86.79a1.78 1.78 0 0 0 1.77-1.77v-54.91z" fill="#592acb"/><path d="m66.67 59.93v54.91l54.91-54.91z" fill="#6736d8"/><path d="m155.23 54.62h-86.79a1.77 1.77 0 0 0 -1.77 1.77v3.54h90.33v-3.54a1.78 1.78 0 0 0 -1.77-1.77z" fill="#592acb"/><path d="m155.39 123h-86.79a.89.89 0 1 1 0-1.77h86.79a.89.89 0 0 1 0 1.77z" fill="#8f8f9d"/><path d="m105.3 125.33h12.4" fill="none" stroke="#80808e" stroke-linecap="round" stroke-linejoin="round"/><path d="m129.28 81.25a148.43 148.43 0 0 0 -12.79-12.79 7.43 7.43 0 0 0 -9.46 0 148.32 148.32 0 0 0 -12.78 12.8 7.43 7.43 0 0 0 0 9.45 151.34 151.34 0 0 0 12.75 12.79 7.43 7.43 0 0 0 9.47 0c2.35-2.13 4.5-4.18 6.57-6.28a2.15 2.15 0 0 0 -.16-2.94l-6.33-5.92a7.3 7.3 0 0 0 2.45-5.76 7.4 7.4 0 0 0 -6.94-7 7.3 7.3 0 0 0 -5.46 2 7.41 7.41 0 0 0 -2.29 5.34 7.33 7.33 0 0 0 2.44 5.46l-2.3 2.08a1.85 1.85 0 0 0 2.55 2.7l2.54-2.31.07-.06a3.49 3.49 0 0 0 -.24-5.14 3.64 3.64 0 1 1 6-2.93 3.59 3.59 0 0 1 -1.37 2.92 3.54 3.54 0 0 0 -1.26 2.55 3.49 3.49 0 0 0 1 2.6l5.35 5c-1.64 1.62-3.33 3.22-5.15 4.86a3.78 3.78 0 0 1 -4.54 0 146.74 146.74 0 0 1 -12.4-12.41 3.78 3.78 0 0 1 0-4.55 146.74 146.74 0 0 1 12.47-12.47 3.8 3.8 0 0 1 4.54 0 147 147 0 0 1 12.48 12.48 3.78 3.78 0 0 1 0 4.54c-.64.72-1.28 1.42-1.9 2.1a1.85 1.85 0 0 0 2.73 2.51c.63-.69 1.29-1.41 1.95-2.15a7.43 7.43 0 0 0 .01-9.47z" fill="#fff"/><rect fill="#ff298a" height="16.85" rx="5.4" width="49.79" x="147.77" y="72.61"/><path d="m194.09 70.83h-42.09a7.22 7.22 0 0 0 -7.2 7.2v6.53a7.22 7.22 0 0 0 7.2 7.2h42.13a7.22 7.22 0 0 0 7.2-7.2v-6.56a7.22 7.22 0 0 0 -7.24-7.17zm-29.91 11.41a.39.39 0 0 1 .23.24.37.37 0 0 1 0 .34l-.89 1.5a.45.45 0 0 1 -.27.2.43.43 0 0 1 -.33-.08l-1.92-1.44.3 2.45a.42.42 0 0 1 -.09.32.44.44 0 0 1 -.31.13h-1.74a.4.4 0 0 1 -.3-.13.39.39 0 0 1 -.1-.32l.3-2.47-2 1.5a.37.37 0 0 1 -.32.07.36.36 0 0 1 -.27-.19l-.85-1.51a.38.38 0 0 1 0-.33.39.39 0 0 1 .23-.24l2.27-.95-2.28-1a.39.39 0 0 1 -.22-.25.38.38 0 0 1 0-.33l.87-1.48a.4.4 0 0 1 .27-.19.38.38 0 0 1 .33.07l2 1.49-.3-2.43a.37.37 0 0 1 .1-.32.37.37 0 0 1 .3-.14h1.74a.41.41 0 0 1 .31.14.4.4 0 0 1 .09.32l-.3 2.44 2-1.48a.41.41 0 0 1 .32-.07.4.4 0 0 1 .27.2l.85 1.5a.41.41 0 0 1 0 .34.42.42 0 0 1 -.23.24l-2.27.94zm13 0a.39.39 0 0 1 .23.24.41.41 0 0 1 0 .34l-.89 1.5a.45.45 0 0 1 -.27.2.42.42 0 0 1 -.33-.08l-1.92-1.44.3 2.45a.39.39 0 0 1 -.1.32.4.4 0 0 1 -.3.13h-1.74a.44.44 0 0 1 -.31-.13.47.47 0 0 1 -.1-.32l.31-2.47-2 1.5a.38.38 0 0 1 -.33.07.4.4 0 0 1 -.27-.19l-.84-1.51a.38.38 0 0 1 0-.33.38.38 0 0 1 .22-.24l2.28-.95-2.28-1a.43.43 0 0 1 -.23-.25.42.42 0 0 1 0-.33l.87-1.48a.4.4 0 0 1 .27-.19.39.39 0 0 1 .33.07l2 1.49-.31-2.43a.44.44 0 0 1 .1-.32.41.41 0 0 1 .31-.14h1.74a.37.37 0 0 1 .3.14.37.37 0 0 1 .1.32l-.3 2.44 2-1.48a.44.44 0 0 1 .33-.07.42.42 0 0 1 .27.2l.85 1.5a.5.5 0 0 1 0 .34.42.42 0 0 1 -.23.24l-2.28.94zm13 0a.39.39 0 0 1 .23.24.45.45 0 0 1 0 .34l-.89 1.5a.45.45 0 0 1 -.28.2.41.41 0 0 1 -.32-.08l-1.87-1.44.3 2.45a.42.42 0 0 1 -.1.32.43.43 0 0 1 -.3.13h-1.74a.44.44 0 0 1 -.31-.13.42.42 0 0 1 -.1-.32l.31-2.47-2 1.5a.38.38 0 0 1 -.33.07.4.4 0 0 1 -.27-.19l-.85-1.51a.47.47 0 0 1 0-.33.39.39 0 0 1 .23-.24l2.27-.95-2.27-1a.43.43 0 0 1 -.23-.25.42.42 0 0 1 0-.33l.87-1.48a.4.4 0 0 1 .27-.19.37.37 0 0 1 .32.07l2 1.49-.31-2.43a.4.4 0 0 1 .1-.32.41.41 0 0 1 .31-.14h1.78a.41.41 0 0 1 .4.46l-.4 2.41 2-1.48a.41.41 0 0 1 .6.13l.84 1.5a.4.4 0 0 1 -.19.58l-2.28.94z" fill="#ffd567"/><rect fill="#ffbdc5" height="15.78" rx="7.89" width="47.56" x="121.1" y="117.5"/><path d="m166.65 115h-42.13a7.22 7.22 0 0 0 -7.2 7.2v6.53a7.22 7.22 0 0 0 7.2 7.2h42.13a7.22 7.22 0 0 0 7.2-7.2v-6.53a7.22 7.22 0 0 0 -7.2-7.2zm-29.91 11.4a.39.39 0 0 1 .23.24.45.45 0 0 1 0 .34l-.89 1.5a.45.45 0 0 1 -.28.2.41.41 0 0 1 -.32-.08l-1.91-1.48.3 2.45a.42.42 0 0 1 -.1.32.42.42 0 0 1 -.3.13h-1.74a.44.44 0 0 1 -.31-.13.47.47 0 0 1 -.1-.32l.31-2.47-2 1.5a.38.38 0 0 1 -.33.07.4.4 0 0 1 -.27-.19l-.85-1.51a.47.47 0 0 1 0-.33.39.39 0 0 1 .23-.24l2.28-1-2.28-1a.43.43 0 0 1 -.23-.25.42.42 0 0 1 0-.33l.87-1.48a.4.4 0 0 1 .27-.19.37.37 0 0 1 .32.07l2 1.49-.31-2.43a.46.46 0 0 1 .1-.32.41.41 0 0 1 .31-.14h1.74a.39.39 0 0 1 .3.14.37.37 0 0 1 .1.32l-.3 2.44 2-1.48a.43.43 0 0 1 .33-.07.42.42 0 0 1 .27.2l.85 1.51a.47.47 0 0 1 0 .33.42.42 0 0 1 -.23.24l-2.28.94zm13 0a.42.42 0 0 1 .23.24.45.45 0 0 1 0 .34l-.9 1.5a.4.4 0 0 1 -.27.2.41.41 0 0 1 -.32-.08l-1.92-1.48.31 2.45a.42.42 0 0 1 -.1.32.43.43 0 0 1 -.3.13h-1.75a.43.43 0 0 1 -.3-.13.42.42 0 0 1 -.1-.32l.31-2.47-2 1.5a.39.39 0 0 1 -.33.07.4.4 0 0 1 -.27-.19l-.85-1.51a.42.42 0 0 1 0-.33.39.39 0 0 1 .23-.24l2.27-1-2.28-1a.45.45 0 0 1 -.22-.25.39.39 0 0 1 0-.33l.87-1.48a.38.38 0 0 1 .27-.19.37.37 0 0 1 .32.07l2 1.49-.31-2.43a.41.41 0 0 1 .4-.46h1.75a.41.41 0 0 1 .4.46l-.31 2.44 2-1.48a.41.41 0 0 1 .6.13l.84 1.51a.38.38 0 0 1 0 .33.4.4 0 0 1 -.22.24l-2.28.94zm13 0a.37.37 0 0 1 .23.24.41.41 0 0 1 0 .34l-.9 1.5a.45.45 0 0 1 -.27.2.41.41 0 0 1 -.32-.08l-1.92-1.48.31 2.45a.47.47 0 0 1 -.1.32.44.44 0 0 1 -.31.13h-1.74a.42.42 0 0 1 -.3-.13.39.39 0 0 1 -.1-.32l.31-2.47-2 1.5a.4.4 0 0 1 -.33.07.36.36 0 0 1 -.27-.19l-.85-1.51a.38.38 0 0 1 0-.33.39.39 0 0 1 .23-.24l2.27-1-2.28-1a.42.42 0 0 1 -.22-.25.38.38 0 0 1 0-.33l.87-1.48a.4.4 0 0 1 .28-.19.37.37 0 0 1 .32.07l2 1.49-.3-2.43a.38.38 0 0 1 .1-.32.39.39 0 0 1 .3-.14h1.74a.41.41 0 0 1 .31.14.44.44 0 0 1 .1.32l-.31 2.44 2-1.48a.41.41 0 0 1 .32-.07.37.37 0 0 1 .27.2l.85 1.51a.38.38 0 0 1 0 .33.42.42 0 0 1 -.23.24l-2.27.94z" fill="#ff298a"/><path d="m222.84 50.27h-37.58a5.38 5.38 0 0 0 -5.37 5.37v68a5.38 5.38 0 0 0 5.37 5.37h37.58a5.39 5.39 0 0 0 5.37-5.37v-68a5.39 5.39 0 0 0 -5.37-5.37z" fill="#bfbfc9"/><path d="m185.26 98.59v21.47a1.79 1.79 0 0 0 1.79 1.79h34a1.79 1.79 0 0 0 1.79-1.79v-59.06z" fill="#f11f89"/><path d="m185.26 61.01v37.58l37.58-37.58z" fill="#ff298a"/><path d="m221.05 55.64h-34a1.79 1.79 0 0 0 -1.79 1.79v3.57h37.58v-3.57a1.79 1.79 0 0 0 -1.79-1.79z" fill="#f11f89"/><circle cx="195.1" cy="113.8" fill="#ffd567" r="2.68"/><circle cx="204.05" cy="113.8" fill="#ff8a50" r="2.68"/><circle cx="212.99" cy="113.8" fill="#7542e5" r="2.68"/><path d="m201.69 125.43h5.04" fill="none" stroke="#9f9fad" stroke-linecap="round" stroke-linejoin="round"/><path d="m215.38 83.72a96.45 96.45 0 0 0 -8.28-8.27 4.8 4.8 0 0 0 -6.12 0 97.91 97.91 0 0 0 -8.27 8.28 4.82 4.82 0 0 0 0 6.12 99.41 99.41 0 0 0 8.29 8.27 4.8 4.8 0 0 0 6.13 0c1.52-1.37 2.91-2.7 4.25-4.06a1.38 1.38 0 0 0 -.11-1.9l-4.09-3.83a4.7 4.7 0 0 0 1.57-3.72 4.79 4.79 0 0 0 -4.49-4.61 4.76 4.76 0 0 0 -3.44 8.29l-1.49 1.34a1.2 1.2 0 0 0 1.67 1.81l1.59-1.44a2.24 2.24 0 0 0 .66-1.69 2.28 2.28 0 0 0 -.81-1.64 2.35 2.35 0 1 1 3 0 2.25 2.25 0 0 0 -.16 3.33l3.46 3.24c-1.06 1-2.16 2.08-3.33 3.14a2.45 2.45 0 0 1 -2.94 0 94.55 94.55 0 0 1 -8.07-8.07 2.45 2.45 0 0 1 0-2.94 93.12 93.12 0 0 1 8.07-8.07 2.45 2.45 0 0 1 2.94 0 94.67 94.67 0 0 1 8.08 8.07 2.46 2.46 0 0 1 0 2.94l-1.23 1.36a1.19 1.19 0 0 0 .07 1.69 1.2 1.2 0 0 0 1.7-.07c.4-.44.83-.91 1.25-1.39a4.8 4.8 0 0 0 .1-6.18z" fill="#fff"/></svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/components/newtab/data/content/assets/trailhead/card-illo-mobile.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<svg viewBox="0 0 288 180" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><radialGradient id="a" cx="134.01" cy="80.95" gradientUnits="userSpaceOnUse" r="46.58"><stop offset=".11" stop-color="#fff44f"/><stop offset=".46" stop-color="#ff980e"/><stop offset=".62" stop-color="#ff5634"/><stop offset=".72" stop-color="#ff3647"/><stop offset=".9" stop-color="#e31587"/></radialGradient><radialGradient id="b" cx="134.01" cy="80.93" r="46.58" xlink:href="#a"/><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="154.94" x2="132.41" y1="77.01" y2="98.75"><stop offset=".05" stop-color="#fff44f"/><stop offset=".11" stop-color="#ffe847"/><stop offset=".22" stop-color="#ffc830"/><stop offset=".37" stop-color="#ff980e"/><stop offset=".4" stop-color="#ff8b16"/><stop offset=".46" stop-color="#ff672a"/><stop offset=".53" stop-color="#ff3647"/><stop offset=".7" stop-color="#e31587"/></linearGradient><radialGradient id="d" cx="153.9" cy="75.78" gradientUnits="userSpaceOnUse" r="28.29"><stop offset=".13" stop-color="#ffbd4f"/><stop offset=".19" stop-color="#ffac31"/><stop offset=".25" stop-color="#ff9d17"/><stop offset=".28" stop-color="#ff980e"/><stop offset=".4" stop-color="#ff563b"/><stop offset=".47" stop-color="#ff3750"/><stop offset=".71" stop-color="#f5156c"/><stop offset=".78" stop-color="#eb0878"/><stop offset=".86" stop-color="#e50080"/></radialGradient><radialGradient id="e" cx="143.56" cy="87.35" gradientUnits="userSpaceOnUse" r="28.29"><stop offset=".3" stop-color="#960e18"/><stop offset=".35" stop-color="#b11927" stop-opacity=".74"/><stop offset=".43" stop-color="#db293d" stop-opacity=".34"/><stop offset=".5" stop-color="#f5334b" stop-opacity=".09"/><stop offset=".53" stop-color="#ff3750" stop-opacity="0"/></radialGradient><radialGradient id="f" cx="146.97" cy="69.36" gradientUnits="userSpaceOnUse" r="20.49"><stop offset=".13" stop-color="#fff44f"/><stop offset=".25" stop-color="#ffdc3e"/><stop offset=".51" stop-color="#ff9d12"/><stop offset=".53" stop-color="#ff980e"/></radialGradient><radialGradient id="g" cx="140.43" cy="94.74" gradientUnits="userSpaceOnUse" r="13.47"><stop offset=".35" stop-color="#3a8ee6"/><stop offset=".47" stop-color="#5c79f0"/><stop offset=".67" stop-color="#9059ff"/><stop offset="1" stop-color="#c139e6"/></radialGradient><radialGradient id="h" cx="143.94" cy="84.04" gradientTransform="matrix(.97 -.24 .28 1.14 -19.11 22.21)" gradientUnits="userSpaceOnUse" r="7.14"><stop offset=".21" stop-color="#9059ff" stop-opacity="0"/><stop offset=".28" stop-color="#8c4ff3" stop-opacity=".06"/><stop offset=".75" stop-color="#7716a8" stop-opacity=".45"/><stop offset=".97" stop-color="#6e008b" stop-opacity=".6"/></radialGradient><radialGradient id="i" cx="141.13" cy="92.8" r=".22" xlink:href="#g"/><radialGradient id="j" cx="141.19" cy="92.72" gradientTransform="matrix(.97 -.24 .28 1.14 -21.58 20.36)" r=".08" xlink:href="#h"/><radialGradient id="k" cx="143.2" cy="74.63" gradientUnits="userSpaceOnUse" r="9.69"><stop offset="0" stop-color="#ffe226"/><stop offset=".12" stop-color="#ffdb27"/><stop offset=".3" stop-color="#ffc82a"/><stop offset=".5" stop-color="#ffa930"/><stop offset=".73" stop-color="#ff7e37"/><stop offset=".79" stop-color="#ff7139"/></radialGradient><radialGradient id="l" cx="150.82" cy="68.48" r="41.34" xlink:href="#a"/><radialGradient id="m" cx="146.9" cy="73.07" gradientTransform="matrix(.1 .99 -.65 .07 179.21 -78.04)" gradientUnits="userSpaceOnUse" r="30.28"><stop offset="0" stop-color="#fff44f"/><stop offset=".06" stop-color="#ffe847"/><stop offset=".17" stop-color="#ffc830"/><stop offset=".3" stop-color="#ff980e"/><stop offset=".36" stop-color="#ff8b16"/><stop offset=".45" stop-color="#ff672a"/><stop offset=".57" stop-color="#ff3647"/><stop offset=".74" stop-color="#e31587"/></radialGradient><radialGradient id="n" cx="142.98" cy="78.21" gradientUnits="userSpaceOnUse" r="25.81"><stop offset=".14" stop-color="#fff44f"/><stop offset=".48" stop-color="#ff980e"/><stop offset=".59" stop-color="#ff5634"/><stop offset=".66" stop-color="#ff3647"/><stop offset=".9" stop-color="#e31587"/></radialGradient><radialGradient id="o" cx="149.79" cy="79.73" gradientUnits="userSpaceOnUse" r="28.25"><stop offset=".09" stop-color="#fff44f"/><stop offset=".23" stop-color="#ffe141"/><stop offset=".51" stop-color="#ffaf1e"/><stop offset=".63" stop-color="#ff980e"/></radialGradient><linearGradient id="p" gradientUnits="userSpaceOnUse" x1="154.67" x2="135.5" y1="76.9" y2="96.06"><stop offset=".17" stop-color="#fff44f" stop-opacity=".8"/><stop offset=".27" stop-color="#fff44f" stop-opacity=".63"/><stop offset=".49" stop-color="#fff44f" stop-opacity=".22"/><stop offset=".6" stop-color="#fff44f" stop-opacity="0"/></linearGradient><circle cx="144" cy="90" fill="#bfbfc9" opacity=".15" r="72"/><path d="m162.79 50.63h-37.58a5.39 5.39 0 0 0 -5.37 5.37v68a5.39 5.39 0 0 0 5.37 5.37h37.58a5.39 5.39 0 0 0 5.37-5.37v-68a5.39 5.39 0 0 0 -5.37-5.37z" fill="#bfbfc9"/><path d="m125.21 99v21.47a1.79 1.79 0 0 0 1.79 1.79h34a1.79 1.79 0 0 0 1.79-1.79v-59z" fill="#592acb"/><path d="m125.21 61.37v37.58l37.58-37.58z" fill="#6736d8"/><path d="m161 56h-34a1.79 1.79 0 0 0 -1.79 1.79v3.58h37.58v-3.58a1.79 1.79 0 0 0 -1.79-1.79z" fill="#592acb"/><circle cx="135.05" cy="114.16" fill="#ffd567" r="2.68"/><circle cx="144" cy="114.16" fill="#ff7139" r="2.68"/><circle cx="152.95" cy="114.16" fill="#ff298a" r="2.68"/><path d="m141.64 125.79h5.04" fill="none" stroke="#9f9fad" stroke-linecap="round" stroke-linejoin="round"/><path d="m138.06 99.33c.01.04.01.02 0 0z" fill="url(#b)"/><path d="m156.73 82.07a7.63 7.63 0 0 0 -2.73-3.44 14 14 0 0 1 1.38 4.12c-1.54-3.82-4.14-5.36-6.26-8.72-.11-.17-.22-.34-.32-.52a2.6 2.6 0 0 1 -.15-.28 2.43 2.43 0 0 1 -.2-.54s0 0 0 0a9.87 9.87 0 0 0 -4.67 7.54 6.77 6.77 0 0 0 -3.78 1.42 3.54 3.54 0 0 0 -.35-.26 6.32 6.32 0 0 1 0-3.32 10.11 10.11 0 0 0 -3.26 2.53c-.54-.68-.5-2.93-.47-3.4a2.25 2.25 0 0 0 -.45.24 9.08 9.08 0 0 0 -1.33 1.14 12 12 0 0 0 -1.26 1.51 11.43 11.43 0 0 0 -1.88 4.12.29.29 0 0 0 0 .09c0 .11-.12.71-.14.84a12.9 12.9 0 0 0 -.22 1.87v.07a13.57 13.57 0 0 0 26.94 2.3c0-.17 0-.35.06-.52a13.94 13.94 0 0 0 -.91-6.79zm-15.63 10.62.18.09z" fill="url(#c)"/><path d="m156.73 82.07a7.63 7.63 0 0 0 -2.73-3.44 14 14 0 0 1 1.38 4.12 12.32 12.32 0 0 1 -.42 9.16 11.69 11.69 0 0 1 -11.21 6.59 13.66 13.66 0 0 1 -13-11.09 6 6 0 0 1 .09-2.27 10 10 0 0 0 -.22 1.87v.07a13.57 13.57 0 0 0 26.94 2.3c0-.17 0-.35.06-.52a13.94 13.94 0 0 0 -.89-6.79z" fill="url(#d)"/><path d="m156.73 82.07a7.63 7.63 0 0 0 -2.73-3.44 14 14 0 0 1 1.38 4.12 12.32 12.32 0 0 1 -.42 9.16 11.69 11.69 0 0 1 -11.21 6.59 13.66 13.66 0 0 1 -13-11.09 6 6 0 0 1 .09-2.27 10 10 0 0 0 -.22 1.87v.07a13.57 13.57 0 0 0 26.94 2.3c0-.17 0-.35.06-.52a13.94 13.94 0 0 0 -.89-6.79z" fill="url(#e)"/><path d="m150.14 83.66a.27.27 0 0 1 .08.07 7.19 7.19 0 0 0 -1.22-1.65 6.85 6.85 0 0 1 -.59-9.39 9.87 9.87 0 0 0 -4.67 7.54 3.75 3.75 0 0 1 .48 0 6.85 6.85 0 0 1 5.92 3.43z" fill="url(#f)"/><path d="m144.2 84.51a3.28 3.28 0 0 1 -1.63 1.5c-3.86 0-4.48 2.33-4.48 2.33a5.73 5.73 0 0 0 3.19 4.44l.23.11.4.16a5.78 5.78 0 0 0 1.77.34 5.61 5.61 0 0 0 3.19-10.52 4.65 4.65 0 0 1 3.27.79 6.85 6.85 0 0 0 -5.94-3.47 3.75 3.75 0 0 0 -.48 0 6.77 6.77 0 0 0 -3.72 1.46c.21.18.44.41.94.9.9.91 3.26 1.84 3.26 1.96z" fill="url(#g)"/><path d="m144.2 84.51a3.28 3.28 0 0 1 -1.63 1.5c-3.86 0-4.48 2.33-4.48 2.33a5.73 5.73 0 0 0 3.19 4.44l.23.11.4.16a5.78 5.78 0 0 0 1.77.34 5.61 5.61 0 0 0 3.19-10.52 4.65 4.65 0 0 1 3.27.79 6.85 6.85 0 0 0 -5.94-3.47 3.75 3.75 0 0 0 -.48 0 6.77 6.77 0 0 0 -3.72 1.46c.21.18.44.41.94.9.9.91 3.26 1.84 3.26 1.96z" fill="url(#h)"/><path d="m141.1 92.69.19.1z" fill="url(#i)"/><path d="m141.1 92.69.19.1z" fill="url(#j)"/><path d="m139.35 81.2.28.19a6.32 6.32 0 0 1 0-3.32 10.11 10.11 0 0 0 -3.26 2.53 6.72 6.72 0 0 1 2.98.6z" fill="url(#k)"/><path d="m130.74 87.44a13.66 13.66 0 0 0 13 11.09 11.69 11.69 0 0 0 11.26-6.59 12.32 12.32 0 0 0 .42-9.16 9.14 9.14 0 0 1 -3.65 8.22c-4.87 4-9.53 2.39-10.48 1.75l-.19-.1a6.15 6.15 0 0 1 -3.76-6.16 3.49 3.49 0 0 1 -3.22-2 5.1 5.1 0 0 1 5-.2 6.78 6.78 0 0 0 5.09.2c0-.12-2.36-1.05-3.28-2-.5-.49-.73-.72-.94-.9a3.54 3.54 0 0 0 -.35-.26l-.28-.19a6.72 6.72 0 0 0 -3-.6c-.54-.68-.5-2.93-.47-3.4a2.25 2.25 0 0 0 -.45.24 9.08 9.08 0 0 0 -1.33 1.14 12 12 0 0 0 -1.26 1.51 11.43 11.43 0 0 0 -1.85 4.18 10 10 0 0 0 -.26 3.23z" fill="url(#l)"/><path d="m149 82.08a7.19 7.19 0 0 1 1.25 1.65 1.92 1.92 0 0 1 .21.16c3.07 2.84 1.46 6.85 1.34 7.13a9.14 9.14 0 0 0 3.62-8.25c-1.54-3.82-4.14-5.36-6.26-8.72-.11-.17-.22-.34-.32-.52a2.6 2.6 0 0 1 -.15-.28 2.43 2.43 0 0 1 -.2-.54s0 0 0 0a6.85 6.85 0 0 0 .51 9.37z" fill="url(#m)"/><path d="m150.43 83.89a1.92 1.92 0 0 0 -.21-.16.27.27 0 0 0 -.08-.07 4.65 4.65 0 0 0 -3.27-.79 5.61 5.61 0 0 1 -3.19 10.52 5.78 5.78 0 0 1 -1.77-.34l-.4-.16-.23-.11c1 .64 5.61 2.21 10.48-1.75.13-.29 1.74-4.3-1.33-7.14z" fill="url(#n)"/><path d="m138.09 88.34s.62-2.33 4.48-2.33a3.28 3.28 0 0 0 1.63-1.5 6.78 6.78 0 0 1 -5.09-.2 5.1 5.1 0 0 0 -5 .2 3.49 3.49 0 0 0 3.22 2 6.15 6.15 0 0 0 3.76 6.16l.18.09a5.73 5.73 0 0 1 -3.18-4.42z" fill="url(#o)"/><path d="m156.73 82.07a7.63 7.63 0 0 0 -2.73-3.44 14 14 0 0 1 1.38 4.12c-1.54-3.82-4.14-5.36-6.26-8.72-.11-.17-.22-.34-.32-.52a2.6 2.6 0 0 1 -.15-.28 2.43 2.43 0 0 1 -.2-.54s0 0 0 0a9.87 9.87 0 0 0 -4.67 7.54 3.75 3.75 0 0 1 .48 0 6.85 6.85 0 0 1 5.94 3.47 4.65 4.65 0 0 0 -3.27-.79 5.61 5.61 0 0 1 -3.19 10.52 5.78 5.78 0 0 1 -1.77-.34l-.4-.16-.23-.11-.19-.1.18.09a5.73 5.73 0 0 1 -3.19-4.44s.62-2.33 4.48-2.33a3.28 3.28 0 0 0 1.63-1.5c0-.12-2.36-1.05-3.28-2-.5-.49-.73-.72-.94-.9a3.54 3.54 0 0 0 -.35-.26 6.32 6.32 0 0 1 0-3.32 10.11 10.11 0 0 0 -3.26 2.53c-.54-.68-.5-2.93-.47-3.4a2.25 2.25 0 0 0 -.45.24 9.08 9.08 0 0 0 -1.33 1.14 12 12 0 0 0 -1.26 1.51 11.43 11.43 0 0 0 -1.91 4.13.29.29 0 0 0 0 .09c0 .11-.14.72-.16.85a16.32 16.32 0 0 0 -.2 1.89v.07a13.57 13.57 0 0 0 26.94 2.3c0-.17 0-.35.06-.52a13.94 13.94 0 0 0 -.91-6.82z" fill="url(#p)"/></svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/components/newtab/data/content/assets/trailhead/card-illo-pledge.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<svg viewBox="0 0 288 180" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(1 0 0 -1 0 182)" gradientUnits="userSpaceOnUse" x1="161.49" x2="161.49" y1="48.36" y2="138.65"><stop offset="0" stop-color="#3a8ee6"/><stop offset=".24" stop-color="#5c79f0"/><stop offset=".63" stop-color="#9059ff"/><stop offset="1" stop-color="#c139e6"/></linearGradient><linearGradient id="b" gradientTransform="matrix(1 0 0 -1 0 182)" gradientUnits="userSpaceOnUse" x1="192.18" x2="138.57" y1="70.63" y2="70.63"><stop offset=".14" stop-color="#6a2bea" stop-opacity="0"/><stop offset=".34" stop-color="#642de4" stop-opacity=".03"/><stop offset=".55" stop-color="#5131d3" stop-opacity=".12"/><stop offset=".76" stop-color="#3139b7" stop-opacity=".27"/><stop offset=".98" stop-color="#054490" stop-opacity=".48"/><stop offset="1" stop-color="#00458b" stop-opacity=".5"/></linearGradient><linearGradient id="c" gradientTransform="matrix(1 0 0 -1 0 182)" gradientUnits="userSpaceOnUse" x1="146.69" x2="109.67" y1="126.28" y2="62.15"><stop offset="0" stop-color="#ff980e"/><stop offset=".27" stop-color="#ff851b"/><stop offset=".56" stop-color="#ff7f1f"/><stop offset=".77" stop-color="#ff3750"/><stop offset=".9" stop-color="#f92261"/><stop offset="1" stop-color="#f5156c"/></linearGradient><linearGradient id="d" gradientTransform="matrix(1 0 0 -1 0 182)" gradientUnits="userSpaceOnUse" x1="146.69" x2="109.67" y1="126.28" y2="62.15"><stop offset="0" stop-color="#fff261" stop-opacity=".8"/><stop offset=".06" stop-color="#fff261" stop-opacity=".68"/><stop offset=".19" stop-color="#fff261" stop-opacity=".48"/><stop offset=".31" stop-color="#fff261" stop-opacity=".31"/><stop offset=".42" stop-color="#fff261" stop-opacity=".17"/><stop offset=".53" stop-color="#fff261" stop-opacity=".08"/><stop offset=".63" stop-color="#fff261" stop-opacity=".02"/><stop offset=".72" stop-color="#fff261" stop-opacity="0"/></linearGradient><linearGradient id="e" gradientTransform="matrix(1 0 0 -1 11.03 186.57)" gradientUnits="userSpaceOnUse" x1="128.01" x2="128.01" y1="86.9" y2="134.16"><stop offset="0" stop-color="#6e008b" stop-opacity=".5"/><stop offset=".5" stop-color="#c846cb" stop-opacity="0"/></linearGradient><circle cx="144" cy="90" fill="#bfbfc9" opacity=".15" r="72"/><path d="m193.18 52.82a30.66 30.66 0 0 0 -21.93-9.47h-.25a30.62 30.62 0 0 0 -21.8 9l-24.7 24.73a10.53 10.53 0 0 0 0 14.9l4.58 4.57a10.58 10.58 0 0 0 14.93 0l-12.01-11.89 24.67-24.73a20.07 20.07 0 0 1 14.33-5.93h.19a20.07 20.07 0 0 1 14.37 6.19c7.59 7.87 7.2 20.83-.86 28.9l-35.15 35.25a7.75 7.75 0 0 1 -11 0l5.65 5.66.91.92a9.23 9.23 0 0 0 12.31.63l34.7-34.94c12.23-12.13 12.64-31.77 1.06-43.79z" fill="url(#a)"/><path d="m184.66 89.1-35.15 35.25a7.75 7.75 0 0 1 -11 0l5.69 5.65.91.92a9.23 9.23 0 0 0 12.31.63l34.7-34.94z" fill="url(#b)"/><path d="m144.05 96.52h-.05a10.57 10.57 0 0 1 -14.92 0l7.47 7.46a10.55 10.55 0 0 0 14.92 0l12-12a10.58 10.58 0 0 0 0-14.93l-24.63-24.64a30.65 30.65 0 0 0 -21.8-9h-.29a30.68 30.68 0 0 0 -21.93 9.41c-11.58 12-11.13 31.66 1 43.8l34.18 34.15a20 20 0 0 0 27.5.79 9.23 9.23 0 0 1 -12.31-.63l-7.66-7.67-34.18-34.16c-8.07-8.07-8.46-21-.87-28.9a20.05 20.05 0 0 1 14.37-6.2h.15a20.07 20.07 0 0 1 14.31 5.92l24.69 24.57-11.93 12z" fill="url(#c)"/><path d="m144.05 96.52h-.05a10.57 10.57 0 0 1 -14.92 0l7.47 7.46a10.55 10.55 0 0 0 14.92 0l12-12a10.58 10.58 0 0 0 0-14.93l-24.63-24.64a30.65 30.65 0 0 0 -21.8-9h-.29a30.68 30.68 0 0 0 -21.93 9.41c-11.58 12-11.13 31.66 1 43.8l34.18 34.15a20 20 0 0 0 27.5.79 9.23 9.23 0 0 1 -12.31-.63l-7.66-7.67-34.18-34.16c-8.07-8.07-8.46-21-.87-28.9a20.05 20.05 0 0 1 14.37-6.2h.15a20.07 20.07 0 0 1 14.31 5.92l24.69 24.57-11.93 12z" fill="url(#d)"/><path d="m149.16 52.41-24.66 24.67a10.53 10.53 0 0 0 0 14.9l4.58 4.57a10.58 10.58 0 0 0 14.93 0l-12.01-11.89 24.67-24.73z" fill="url(#e)"/></svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/components/newtab/data/content/assets/trailhead/card-illo-pocket.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<svg viewBox="0 0 288 180" xmlns="http://www.w3.org/2000/svg"><circle cx="144" cy="90" fill="#bfbfc9" opacity=".15" r="72"/><path d="m165.68 121.55a5.26 5.26 0 0 0 1.22-3.34v-63.77a5.33 5.33 0 0 0 -5.31-5.31h-90.34a5.33 5.33 0 0 0 -5.31 5.31v63.77a5.26 5.26 0 0 0 1.22 3.34l-11.85 2v5.32h122.22v-5.32z" fill="#9f9fad"/><path d="m126.16 59.76-54.91 54.91a1.78 1.78 0 0 0 1.75 1.77h86.79a1.78 1.78 0 0 0 1.77-1.77v-54.91z" fill="#592acb"/><path d="m71.25 59.76v54.91l54.91-54.91z" fill="#6736d8"/><path d="m159.82 54.44h-86.82a1.78 1.78 0 0 0 -1.78 1.77v3.55h90.34v-3.55a1.78 1.78 0 0 0 -1.74-1.77z" fill="#592acb"/><path d="m160 122.84h-86.81a.89.89 0 1 1 0-1.77h86.81a.89.89 0 0 1 0 1.77z" fill="#8f8f9d"/><path d="m109.89 125.15h12.4" fill="none" stroke="#80808e" stroke-linecap="round" stroke-linejoin="round"/><path d="m227.42 50.1h-37.58a5.37 5.37 0 0 0 -5.36 5.36v68a5.38 5.38 0 0 0 5.36 5.37h37.58a5.38 5.38 0 0 0 5.37-5.37v-68a5.38 5.38 0 0 0 -5.37-5.36z" fill="#bfbfc9"/><path d="m189.84 98.41v21.48a1.8 1.8 0 0 0 1.79 1.79h34a1.79 1.79 0 0 0 1.79-1.79v-59.06z" fill="#f11f89"/><path d="m189.84 60.83v37.58l37.58-37.58z" fill="#ff298a"/><path d="m225.63 55.46h-34a1.8 1.8 0 0 0 -1.79 1.79v3.58h37.58v-3.58a1.79 1.79 0 0 0 -1.79-1.79z" fill="#f11f89"/><circle cx="199.69" cy="113.62" fill="#ffd567" r="2.68"/><circle cx="208.63" cy="113.62" fill="#ff8a50" r="2.68"/><circle cx="217.58" cy="113.62" fill="#7542e5" r="2.68"/><path d="m206.27 125.26h5.05" fill="none" stroke="#9f9fad" stroke-linecap="round" stroke-linejoin="round"/><g fill="#fff"><path d="m116.07 99.86a15.1 15.1 0 0 1 -15.07-15.11v-7.56a3.77 3.77 0 0 1 3.78-3.77h22.67a3.77 3.77 0 0 1 3.77 3.77v7.56a15.1 15.1 0 0 1 -15.15 15.11zm7.53-19a1.87 1.87 0 0 0 -1.37.6l-6.23 6.26-6.09-6.11a1.85 1.85 0 0 0 -1.42-.7 1.89 1.89 0 0 0 -1.89 1.89 1.87 1.87 0 0 0 .54 1.32l6.22 6.25 1.33 1.33a1.88 1.88 0 0 0 2.67 0l1.34-1.33 6.3-6.23a1.89 1.89 0 0 0 0-2.67 2 2 0 0 0 -1.4-.56z"/><path d="m208.73 95.37a10.15 10.15 0 0 1 -10.15-10.15v-5.07a2.54 2.54 0 0 1 2.54-2.54h15.22a2.54 2.54 0 0 1 2.53 2.54v5.07a10.15 10.15 0 0 1 -10.14 10.15zm5.05-12.72a1.24 1.24 0 0 0 -.92.4l-4.16 4.16-4.09-4.1a1.22 1.22 0 0 0 -1-.46 1.25 1.25 0 0 0 -1.26 1.26 1.27 1.27 0 0 0 .36.89l4.18 4.2.89.89a1.26 1.26 0 0 0 1.79 0l.9-.89 4.2-4.2a1.26 1.26 0 0 0 -.91-2.16z"/></g></svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/components/newtab/data/content/assets/trailhead/card-illo-private.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<svg viewBox="0 0 288 180" xmlns="http://www.w3.org/2000/svg"><circle cx="144" cy="90" fill="#bfbfc9" opacity=".15" r="72"/><path d="m193.26 122.56a5.24 5.24 0 0 0 1.22-3.33v-63.77a5.33 5.33 0 0 0 -5.31-5.31h-90.34a5.33 5.33 0 0 0 -5.31 5.31v63.77a5.24 5.24 0 0 0 1.22 3.33l-11.85 2v5.31h122.22v-5.31z" fill="#9f9fad"/><path d="m153.74 60.77-54.91 54.91a1.77 1.77 0 0 0 1.77 1.77h86.8a1.77 1.77 0 0 0 1.77-1.77v-54.91z" fill="#592acb"/><path d="m98.83 60.77v54.91l54.91-54.91z" fill="#6736d8"/><path d="m187.4 55.46h-86.8a1.78 1.78 0 0 0 -1.77 1.77v3.54h90.34v-3.54a1.78 1.78 0 0 0 -1.77-1.77z" fill="#592acb"/><path d="m187.56 123.86h-86.79a.89.89 0 1 1 0-1.77h86.79a.89.89 0 0 1 0 1.77z" fill="#8f8f9d"/><path d="m137.47 126.17h12.4" fill="none" stroke="#80808e" stroke-linecap="round" stroke-linejoin="round"/><circle cx="144" cy="86.4" fill="#b833e1" r="22.54"/><path d="m153.73 95.48c-3.67 0-6.21-4.41-9.73-4.41s-6.28 4.41-9.73 4.41c-4.53 0-7.87-4.29-7.93-11.64 0-4.56 1.33-6 7.21-6s7.6 2.41 10.45 2.41 4.57-2.41 10.45-2.41 7.24 1.46 7.21 6c-.06 7.35-3.4 11.64-7.93 11.64zm-17.3-12c-3.57.16-5 2.28-5 2.81s2.37 2 4.68 2 5.05-.85 5.05-1.61-1.36-3.39-4.73-3.24zm15.14 0c-3.37-.15-4.69 2.37-4.69 3.21s2.73 1.61 5.05 1.61 4.68-1.49 4.68-2-1.47-2.7-5.04-2.86z" fill="#f9f9fa"/></svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/components/newtab/data/content/assets/trailhead/card-illo-sendtab.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<svg viewBox="0 0 288 180" xmlns="http://www.w3.org/2000/svg"><circle cx="144" cy="90" fill="#bfbfc9" opacity=".15" r="72"/><path d="m163.05 84.53h25.83v5.48h-25.83z" fill="#bfbfc9" opacity=".25"/><path d="m165.93 121.47a5.26 5.26 0 0 0 1.22-3.34v-63.77a5.33 5.33 0 0 0 -5.32-5.31h-90.33a5.33 5.33 0 0 0 -5.31 5.31v63.77a5.26 5.26 0 0 0 1.22 3.34l-11.85 2v5.31h122.21v-5.31z" fill="#9f9fad"/><path d="m126.41 59.68-54.91 54.9a1.78 1.78 0 0 0 1.77 1.78h86.79a1.78 1.78 0 0 0 1.77-1.78v-54.9z" fill="#592acb"/><path d="m71.5 59.68v54.9l54.91-54.9z" fill="#6736d8"/><path d="m160.06 54.36h-86.79a1.78 1.78 0 0 0 -1.77 1.77v3.55h90.33v-3.55a1.77 1.77 0 0 0 -1.77-1.77z" fill="#592acb"/><path d="m160.22 122.76h-86.79a.89.89 0 0 1 0-1.77h86.79a.89.89 0 1 1 0 1.77z" fill="#8f8f9d"/><path d="m110.14 125.07h12.39" fill="none" stroke="#80808e" stroke-linecap="round" stroke-linejoin="round"/><path d="m227.67 50h-37.58a5.39 5.39 0 0 0 -5.37 5.37v68a5.38 5.38 0 0 0 5.37 5.36h37.58a5.38 5.38 0 0 0 5.37-5.36v-68a5.39 5.39 0 0 0 -5.37-5.37z" fill="#bfbfc9"/><path d="m190.09 98.33v21.48a1.8 1.8 0 0 0 1.79 1.79h34a1.79 1.79 0 0 0 1.79-1.79v-59.06z" fill="#f11f89"/><path d="m190.09 60.75v37.58l37.58-37.58z" fill="#ff298a"/><path d="m225.88 55.38h-34a1.8 1.8 0 0 0 -1.79 1.79v3.58h37.58v-3.58a1.79 1.79 0 0 0 -1.79-1.79z" fill="#f11f89"/><circle cx="199.93" cy="113.54" fill="#ffd567" r="2.68"/><circle cx="208.88" cy="113.54" fill="#ff8a50" r="2.68"/><circle cx="217.83" cy="113.54" fill="#7542e5" r="2.68"/><path d="m206.52 125.17h5.04" fill="none" stroke="#9f9fad" stroke-linecap="round" stroke-linejoin="round"/><path d="m213.56 85.34-16.45-16.45a2.74 2.74 0 0 0 -3.87 3.88l11.76 11.76h-20.28v5.47h20.28l-11.76 11.76a2.74 2.74 0 0 0 3.81 4l.06-.07 16.45-16.45a2.73 2.73 0 0 0 0-3.9z" fill="#fff"/><path d="m117.57 84.53a2.75 2.75 0 0 0 0 5.49h49.66v-5.49z" fill="#cdcdd4"/></svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/components/newtab/data/content/assets/trailhead/card-illo-tracking.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<svg viewBox="0 0 288 180" xmlns="http://www.w3.org/2000/svg"><circle cx="144" cy="90" fill="#bfbfc9" opacity=".15" r="72"/><path d="m187.73 65.17c-.06-4.45-.12-9.5-.14-15.2a4.87 4.87 0 0 0 -4-4.79l-38.73-6.7a5 5 0 0 0 -1.66 0l-38.73 6.7a4.87 4.87 0 0 0 -4 4.79c0 5.7-.08 10.75-.14 15.2-.1 8-.17 13.84.16 17.46 2.09 23.23 7 33.81 15.31 45.21a45.42 45.42 0 0 0 27.31 17.58 5.2 5.2 0 0 0 1 .09 5 5 0 0 0 1-.09 46 46 0 0 0 27.3-17.58c8.31-11.4 13.22-22 15.32-45.21.17-3.63.1-9.43 0-17.46z" fill="#9f9fad"/><path d="m143.18 38.56-38.73 6.7a4.89 4.89 0 0 0 -4 4.79c0 5.7-.07 10.75-.13 15.2-.1 8-.18 13.84.15 17.46 2.1 23.23 7 33.81 15.32 45.21a45.34 45.34 0 0 0 27.31 17.57 4.32 4.32 0 0 0 .95.1v-107.1a4.57 4.57 0 0 0 -.87.07z" fill="#80808e"/><path d="m143.3 45.93-32.48 5.62a4.08 4.08 0 0 0 -3.38 4c0 4.77-.06 9-.11 12.74-.09 6.74-.15 11.61.12 14.64 1.76 19.49 5.88 28.36 12.85 37.92a38 38 0 0 0 22.91 14.74 3.47 3.47 0 0 0 .79.08v-89.8a4.14 4.14 0 0 0 -.7.06z" fill="#592acb"/><path d="m180.67 68.31c0-3.73-.1-8-.11-12.74a4.08 4.08 0 0 0 -3.38-4l-32.48-5.64a4.14 4.14 0 0 0 -.7-.06v89.82a3.62 3.62 0 0 0 .8-.08 38.54 38.54 0 0 0 22.89-14.74c7-9.56 11.09-18.43 12.85-37.92.28-3.03.22-7.9.13-14.64z" fill="#7542e5"/><path d="m156.89 79.5a3.46 3.46 0 0 0 -2.89-3.4l-10-1.65-10 1.65a3.46 3.46 0 0 0 -2.93 3.4c0 2.71 0 6.3.18 8 .48 5.16 1.49 8 4 11.2a13.51 13.51 0 0 0 8.59 5h.17.17a13.51 13.51 0 0 0 8.59-5c2.46-3.22 3.46-6 4-11.2.11-1.8.12-5.78.12-8zm-3.38 7.68c-.44 4.59-1.21 6.82-3.3 9.55a10.39 10.39 0 0 1 -6.21 3.74 10.42 10.42 0 0 1 -6.21-3.74c-2.09-2.74-2.86-5-3.29-9.54-.12-1.22-.17-4-.17-7.68a.26.26 0 0 1 .23-.23l9.44-1.56 9.44 1.56a.25.25 0 0 1 .23.23c0 3.67-.06 6.49-.16 7.67zm-15.95-5.13c0 3.17.1 4.41.14 4.83.41 4.35 1.12 5.89 2.65 7.89a7 7 0 0 0 3.65 2.39v-16.16z" fill="#fff"/></svg>
\ No newline at end of file
--- a/browser/components/newtab/test/browser/browser.ini
+++ b/browser/components/newtab/test/browser/browser.ini
@@ -12,17 +12,16 @@ prefs =
   browser.newtabpage.activity-stream.debug=false
   browser.newtabpage.activity-stream.discoverystream.enabled=true
   browser.newtabpage.activity-stream.discoverystream.endpoints=data:
   browser.newtabpage.activity-stream.feeds.system.topstories=true
   browser.newtabpage.activity-stream.feeds.section.topstories=true
   browser.newtabpage.activity-stream.feeds.section.topstories.options={"provider_name":""}
   messaging-system.log=all
 
-[browser_aboutwelcome_actors.js]
 [browser_aboutwelcome_focus.js]
 [browser_aboutwelcome_simplified.js]
 [browser_aboutwelcome_multistage.js]
 [browser_aboutwelcome_rtamo.js]
 [browser_aboutwelcome_attribution.js]
 skip-if = (os == "linux") # Test setup only implemented for OSX and Windows
 [browser_aboutwelcome_observer.js]
 [browser_as_load_location.js]
deleted file mode 100644
--- a/browser/components/newtab/test/browser/browser_aboutwelcome_actors.js
+++ /dev/null
@@ -1,237 +0,0 @@
-"use strict";
-
-const SEPARATE_ABOUT_WELCOME_PREF = "browser.aboutwelcome.enabled";
-const DID_SEE_ABOUT_WELCOME_PREF = "trailhead.firstrun.didSeeAboutWelcome";
-const ABOUT_WELCOME_OVERRIDE_CONTENT = "browser.aboutwelcome.overrideContent";
-
-const { PrivateBrowsingUtils } = ChromeUtils.import(
-  "resource://gre/modules/PrivateBrowsingUtils.jsm"
-);
-
-const { FxAccounts } = ChromeUtils.import(
-  "resource://gre/modules/FxAccounts.jsm"
-);
-
-const SIMPLIFIED_WELCOME_CONTENT = {
-  template: "simplified",
-  title: {
-    string_id: "onboarding-welcome-header",
-  },
-  startButton: {
-    label: {
-      string_id: "onboarding-start-browsing-button-label",
-    },
-    message_id: "START_BROWSING_BUTTON",
-    action: {
-      type: "OPEN_AWESOME_BAR",
-    },
-  },
-  cards: [
-    {
-      content: {
-        title: {
-          string_id: "onboarding-data-sync-title",
-        },
-        text: {
-          string_id: "onboarding-data-sync-text2",
-        },
-        icon: "devices",
-        primary_button: {
-          label: {
-            string_id: "onboarding-data-sync-button2",
-          },
-          action: {
-            type: "OPEN_URL",
-            addFlowParams: true,
-            data: {
-              args:
-                "https://accounts.firefox.com/?service=sync&action=email&context=fx_desktop_v3&entrypoint=activity-stream-firstrun&style=trailhead",
-              where: "tabshifted",
-            },
-          },
-        },
-      },
-      id: "TRAILHEAD_CARD_2",
-      order: 1,
-      blockOnClick: false,
-    },
-    {
-      content: {
-        title: {
-          string_id: "onboarding-firefox-monitor-title",
-        },
-        text: {
-          string_id: "onboarding-firefox-monitor-text2",
-        },
-        icon: "ffmonitor",
-        primary_button: {
-          label: {
-            string_id: "onboarding-firefox-monitor-button",
-          },
-          action: {
-            type: "OPEN_URL",
-            data: {
-              args: "https://monitor.firefox.com/",
-              where: "tabshifted",
-            },
-          },
-        },
-      },
-      id: "TRAILHEAD_CARD_3",
-      order: 2,
-      blockOnClick: false,
-    },
-    {
-      content: {
-        title: {
-          string_id: "onboarding-browse-privately-title",
-        },
-        text: {
-          string_id: "onboarding-browse-privately-text",
-        },
-        icon: "private",
-        primary_button: {
-          label: {
-            string_id: "onboarding-browse-privately-button",
-          },
-          action: {
-            type: "OPEN_PRIVATE_BROWSER_WINDOW",
-          },
-        },
-      },
-      id: "TRAILHEAD_CARD_4",
-      order: 3,
-      blockOnClick: true,
-    },
-  ],
-};
-
-/**
- * Sets the aboutwelcome pref to enabled simplified welcome UI
- */
-async function setAboutWelcomePref(value) {
-  await pushPrefs([
-    ABOUT_WELCOME_OVERRIDE_CONTENT,
-    JSON.stringify(SIMPLIFIED_WELCOME_CONTENT),
-  ]);
-  return pushPrefs([SEPARATE_ABOUT_WELCOME_PREF, value]);
-}
-
-async function openAboutWelcome() {
-  let tab = await BrowserTestUtils.openNewForegroundTab(
-    gBrowser,
-    "about:welcome",
-    true
-  );
-  registerCleanupFunction(() => {
-    BrowserTestUtils.removeTab(tab);
-  });
-  return tab.linkedBrowser;
-}
-
-// Test events from AboutWelcomeUtils
-async function test_set_message() {
-  Services.prefs.setBoolPref(DID_SEE_ABOUT_WELCOME_PREF, false);
-  await openAboutWelcome();
-  Assert.equal(
-    Services.prefs.getBoolPref(DID_SEE_ABOUT_WELCOME_PREF, false),
-    true,
-    "Pref was set"
-  );
-}
-
-async function test_open_awesome_bar(browser, message) {
-  await ContentTask.spawn(browser, message, async () => {
-    await ContentTaskUtils.waitForCondition(
-      () =>
-        content.document.querySelector(
-          "button[data-l10n-id=onboarding-start-browsing-button-label]"
-        ),
-      "Wait for start browsing button to load"
-    );
-    let button = content.document.querySelector(
-      "button[data-l10n-id=onboarding-start-browsing-button-label]"
-    );
-    button.click();
-  });
-  Assert.ok(gURLBar.focused, "Focus should be on awesome bar");
-}
-
-async function test_open_private_browser(browser, message) {
-  let newWindowPromise = BrowserTestUtils.waitForNewWindow();
-  await ContentTask.spawn(browser, message, async () => {
-    await ContentTaskUtils.waitForCondition(
-      () =>
-        content.document.querySelector(
-          "button[data-l10n-id=onboarding-browse-privately-button]"
-        ),
-      "Wait for private browsing button to load"
-    );
-    let button = content.document.querySelector(
-      "button[data-l10n-id=onboarding-browse-privately-button]"
-    );
-    button.click();
-  });
-  let win = await newWindowPromise;
-  Assert.ok(PrivateBrowsingUtils.isWindowPrivate(win));
-  await BrowserTestUtils.closeWindow(win);
-}
-
-// Test Fxaccounts MetricsFlowURI
-
-add_task(async function setup() {
-  const sandbox = sinon.createSandbox();
-  sandbox.stub(FxAccounts.config, "promiseMetricsFlowURI").resolves("");
-
-  await pushPrefs([
-    ABOUT_WELCOME_OVERRIDE_CONTENT,
-    JSON.stringify(SIMPLIFIED_WELCOME_CONTENT),
-  ]);
-
-  registerCleanupFunction(() => {
-    sandbox.restore();
-  });
-});
-
-test_newtab(
-  {
-    async before({ pushPrefs }) {
-      await pushPrefs(["browser.aboutwelcome.enabled", true]);
-    },
-    test: async function test_startBrowsing() {
-      await ContentTaskUtils.waitForCondition(
-        () =>
-          content.document.querySelector(
-            "button[data-l10n-id=onboarding-start-browsing-button-label]"
-          ),
-        "Wait for start browsing button to load"
-      );
-    },
-    after() {
-      ok(
-        FxAccounts.config.promiseMetricsFlowURI.callCount === 1,
-        "Stub was called"
-      );
-      Assert.equal(
-        FxAccounts.config.promiseMetricsFlowURI.firstCall.args[0],
-        "aboutwelcome",
-        "Called by AboutWelcomeParent"
-      );
-    },
-  },
-  "about:welcome"
-);
-
-add_task(async function test_onContentMessage() {
-  await setAboutWelcomePref(true);
-  let browser = await openAboutWelcome();
-
-  //case "SET_WELCOME_MESSAGE_SEEN"
-  await test_set_message();
-
-  // //case "OPEN_AWESOME_BAR"
-  await test_open_awesome_bar(browser, "Open awesome bar");
-
-  //case "OPEN_PRIVATE_BROWSER_WINDOW"
-  await test_open_private_browser(browser, "Open private window");
-});
--- a/browser/components/newtab/test/browser/browser_aboutwelcome_multistage.js
+++ b/browser/components/newtab/test/browser/browser_aboutwelcome_multistage.js
@@ -1,20 +1,24 @@
 "use strict";
 
 const { ExperimentAPI } = ChromeUtils.import(
   "resource://nimbus/ExperimentAPI.jsm"
 );
 const { ExperimentFakes } = ChromeUtils.import(
   "resource://testing-common/NimbusTestUtils.jsm"
 );
+const { FxAccounts } = ChromeUtils.import(
+  "resource://gre/modules/FxAccounts.jsm"
+);
 
 const SEPARATE_ABOUT_WELCOME_PREF = "browser.aboutwelcome.enabled";
 const ABOUT_WELCOME_OVERRIDE_CONTENT_PREF =
   "browser.aboutwelcome.overrideContent";
+const DID_SEE_ABOUT_WELCOME_PREF = "trailhead.firstrun.didSeeAboutWelcome";
 
 const TEST_MULTISTAGE_CONTENT = {
   id: "multi-stage-welcome",
   template: "multistage",
   screens: [
     {
       id: "AW_STEP1",
       order: 0,
@@ -195,16 +199,27 @@ async function onButtonClick(browser, el
         buttonId
       );
       let button = content.document.querySelector(buttonId);
       button.click();
     }
   );
 }
 
+add_task(async function setup() {
+  const sandbox = sinon.createSandbox();
+  // This needs to happen before any about:welcome page opens
+  sandbox.stub(FxAccounts.config, "promiseMetricsFlowURI").resolves("");
+  await setAboutWelcomeMultiStage("");
+
+  registerCleanupFunction(() => {
+    sandbox.restore();
+  });
+});
+
 /**
  * Test the zero onboarding using ExperimentAPI
  */
 add_task(async function test_multistage_zeroOnboarding_experimentAPI() {
   await setAboutWelcomePref(true);
   let updatePromise = ExperimentFakes.waitForExperimentUpdate(ExperimentAPI, {
     slug: "mochitest-1-aboutwelcome",
   });
@@ -783,8 +798,54 @@ add_task(async function test_AWMultistag
     "Source passed to event handler"
   );
   Assert.equal(
     eventCall.args[0],
     "AWPage:TELEMETRY_EVENT",
     "Got call to handle Telemetry event"
   );
 });
+
+// Test events from AboutWelcomeUtils
+async function test_set_message() {
+  Services.prefs.setBoolPref(DID_SEE_ABOUT_WELCOME_PREF, false);
+  await openAboutWelcome();
+  Assert.equal(
+    Services.prefs.getBoolPref(DID_SEE_ABOUT_WELCOME_PREF, false),
+    true,
+    "Pref was set"
+  );
+}
+
+add_task(async function test_onContentMessage() {
+  await setAboutWelcomePref(true);
+  await openAboutWelcome();
+
+  //case "SET_WELCOME_MESSAGE_SEEN"
+  await test_set_message();
+});
+
+// Test Fxaccounts MetricsFlowURI
+test_newtab(
+  {
+    async before({ pushPrefs }) {
+      await pushPrefs(["browser.aboutwelcome.enabled", true]);
+    },
+    test: async function test_startBrowsing() {
+      await ContentTaskUtils.waitForCondition(
+        () => content.document.querySelector(".indicator.current"),
+        "Wait for about:welcome to load"
+      );
+    },
+    after() {
+      Assert.ok(
+        FxAccounts.config.promiseMetricsFlowURI.called,
+        "Stub was called"
+      );
+      Assert.equal(
+        FxAccounts.config.promiseMetricsFlowURI.firstCall.args[0],
+        "aboutwelcome",
+        "Called by AboutWelcomeParent"
+      );
+    },
+  },
+  "about:welcome"
+);
deleted file mode 100644
--- a/browser/components/newtab/test/unit/asrouter/templates/OnboardingMessage.test.jsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import { GlobalOverrider } from "test/unit/utils";
-import { OnboardingMessageProvider } from "lib/OnboardingMessageProvider.jsm";
-import schema from "content-src/asrouter/templates/OnboardingMessage/OnboardingMessage.schema.json";
-import badgeSchema from "content-src/asrouter/templates/OnboardingMessage/ToolbarBadgeMessage.schema.json";
-import whatsNewSchema from "content-src/asrouter/templates/OnboardingMessage/WhatsNewMessage.schema.json";
-
-const DEFAULT_CONTENT = {
-  title: "A title",
-  text: "A description",
-  icon: "icon",
-  primary_button: {
-    label: "some_button_label",
-    action: {
-      type: "SOME_TYPE",
-      data: { args: "example.com" },
-    },
-  },
-};
-
-const L10N_CONTENT = {
-  title: { string_id: "onboarding-private-browsing-title" },
-  text: { string_id: "onboarding-private-browsing-text" },
-  icon: "icon",
-  primary_button: {
-    label: { string_id: "onboarding-button-label-get-started" },
-    action: { type: "SOME_TYPE" },
-  },
-};
-
-describe("OnboardingMessage", () => {
-  let globals;
-  let sandbox;
-  beforeEach(() => {
-    globals = new GlobalOverrider();
-    sandbox = sinon.createSandbox();
-    globals.set("FxAccountsConfig", {
-      promiseConnectAccountURI: sandbox.stub().resolves("some/url"),
-    });
-    globals.set("AddonRepository", {
-      getAddonsByIDs: ([content]) => [
-        {
-          name: content,
-          sourceURI: { spec: "foo", scheme: "https" },
-          icons: { 64: "icon" },
-        },
-      ],
-    });
-  });
-  afterEach(() => {
-    sandbox.restore();
-    globals.restore();
-  });
-  it("should validate DEFAULT_CONTENT", () => {
-    assert.jsonSchema(DEFAULT_CONTENT, schema);
-  });
-  it("should validate L10N_CONTENT", () => {
-    assert.jsonSchema(L10N_CONTENT, schema);
-  });
-  it("should validate all messages from OnboardingMessageProvider", async () => {
-    const messages = await OnboardingMessageProvider.getUntranslatedMessages();
-    // FXA_1 doesn't have content - so filter it out
-    messages
-      .filter(msg => msg.template in ["onboarding", "return_to_amo_overlay"])
-      .forEach(msg => assert.jsonSchema(msg.content, schema));
-  });
-  it("should validate all badge template messages", async () => {
-    const messages = await OnboardingMessageProvider.getUntranslatedMessages();
-
-    messages
-      .filter(msg => msg.template === "toolbar_badge")
-      .forEach(msg => assert.jsonSchema(msg.content, badgeSchema));
-  });
-  it("should validate all What's New template messages", async () => {
-    const messages = await OnboardingMessageProvider.getUntranslatedMessages();
-
-    messages
-      .filter(msg => msg.template === "whatsnew_panel_message")
-      .forEach(msg => assert.jsonSchema(msg.content, whatsNewSchema));
-  });
-});
--- a/browser/components/search/test/browser/browser.ini
+++ b/browser/components/search/test/browser/browser.ini
@@ -23,17 +23,17 @@ support-files =
 support-files = browser_contentContextMenu.xhtml
 [browser_contentSearchUI.js]
 support-files =
   contentSearchUI.html
   contentSearchUI.js
   searchSuggestionEngine.sjs
 [browser_contentSearchUI_default.js]
 [browser_contextmenu.js]
-skip-if = (os == "win" && processor == "aarch64") # disabled on aarch64 due to 1531590
+[browser_contextmenu_whereToOpenLink.js]
 [browser_contextSearchTabPosition.js]
 [browser_google_behavior.js]
 skip-if = (os == 'linux') && ccov # Bug 1511273
 [browser_healthreport.js]
 skip-if = (verify && debug && (os == 'win' || os == 'linux'))
 [browser_hiddenOneOffs_cleanup.js]
 [browser_hiddenOneOffs_diacritics.js]
 [browser_ime_composition.js]
--- a/browser/components/search/test/browser/browser_contextSearchTabPosition.js
+++ b/browser/components/search/test/browser/browser_contextSearchTabPosition.js
@@ -45,22 +45,26 @@ add_task(async function test() {
 
   let container = gBrowser.tabContainer;
   container.addEventListener("TabOpen", tabAdded);
 
   BrowserTestUtils.addTab(gBrowser, "about:blank");
   BrowserSearch.loadSearchFromContext(
     "mozilla",
     false,
-    Services.scriptSecurityManager.getSystemPrincipal()
+    Services.scriptSecurityManager.getSystemPrincipal(),
+    Services.scriptSecurityManager.getSystemPrincipal().csp,
+    new MouseEvent("click")
   );
   BrowserSearch.loadSearchFromContext(
     "firefox",
     false,
-    Services.scriptSecurityManager.getSystemPrincipal()
+    Services.scriptSecurityManager.getSystemPrincipal(),
+    Services.scriptSecurityManager.getSystemPrincipal().csp,
+    new MouseEvent("click")
   );
 
   // Wait for all the tabs to open.
   await tabsLoadedDeferred.promise;
 
   is(tabs[0], gBrowser.tabs[3], "blank tab has been pushed to the end");
   is(
     tabs[1],
new file mode 100644
--- /dev/null
+++ b/browser/components/search/test/browser/browser_contextmenu_whereToOpenLink.js
@@ -0,0 +1,185 @@
+/* Any copyright is dedicated to the Public Domain.
+ *  * http://creativecommons.org/publicdomain/zero/1.0/ */
+/*
+ * Test searching for the selected text using the context menu
+ */
+
+const { SearchTestUtils } = ChromeUtils.import(
+  "resource://testing-common/SearchTestUtils.jsm"
+);
+
+SearchTestUtils.init(this);
+
+const ENGINE_NAME = "mozSearch";
+const ENGINE_URL =
+  "https://example.com/browser/browser/components/search/test/browser/mozsearch.sjs";
+
+let engine;
+let extension;
+let oldDefaultEngine;
+
+add_task(async function setup() {
+  await Services.search.init();
+
+  extension = await SearchTestUtils.installSearchExtension({
+    name: ENGINE_NAME,
+    search_url: ENGINE_URL,
+    search_url_get_params: "test={searchTerms}",
+  });
+
+  engine = await Services.search.getEngineByName(ENGINE_NAME);
+  ok(engine, "Got a search engine");
+  oldDefaultEngine = await Services.search.getDefault();
+  await Services.search.setDefault(engine);
+});
+
+async function openNewSearchTab(event_args, expect_new_window = false) {
+  // open context menu with right click
+  let contextMenu = document.getElementById("contentAreaContextMenu");
+
+  let popupPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+  BrowserTestUtils.synthesizeMouseAtCenter(
+    "body",
+    { type: "contextmenu", button: 2 },
+    gBrowser.selectedBrowser
+  );
+  await popupPromise;
+
+  let searchItem = contextMenu.getElementsByAttribute(
+    "id",
+    "context-searchselect"
+  )[0];
+
+  // open new search tab with desired modifiers
+  let searchTabPromise;
+  if (expect_new_window) {
+    searchTabPromise = BrowserTestUtils.waitForNewWindow({
+      url: ENGINE_URL + "?test=test%2520search",
+    });
+  } else {
+    searchTabPromise = BrowserTestUtils.waitForNewTab(
+      gBrowser,
+      ENGINE_URL + "?test=test%2520search",
+      true
+    );
+  }
+
+  EventUtils.synthesizeMouseAtCenter(searchItem, event_args);
+
+  if (expect_new_window) {
+    let win = await searchTabPromise;
+    return win.gBrowser.selectedTab;
+  }
+  return searchTabPromise;
+}
+
+add_task(async function test_whereToOpenLink() {
+  // open search test page and select search text
+  let tab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    "https://example.com/browser/browser/components/search/test/browser/test_search.html"
+  );
+
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      // We want select events to be fired.
+      ["dom.select_events.enabled", true],
+    ],
+  });
+  await SpecialPowers.spawn(tab.linkedBrowser, [""], async function() {
+    return new Promise(resolve => {
+      content.document.addEventListener(
+        "selectionchange",
+        function() {
+          resolve();
+        },
+        { once: true }
+      );
+      content.document.getSelection().selectAllChildren(content.document.body);
+    });
+  });
+
+  // check where context search opens for different buttons/modifiers
+  let searchTab = await openNewSearchTab({});
+  is(
+    searchTab,
+    gBrowser.selectedTab,
+    "Search tab is opened in foreground (no modifiers)"
+  );
+  BrowserTestUtils.removeTab(searchTab);
+
+  searchTab = await openNewSearchTab({ button: 1 });
+  isnot(
+    searchTab,
+    gBrowser.selectedTab,
+    "Search tab is opened in background (middle mouse)"
+  );
+  BrowserTestUtils.removeTab(searchTab);
+
+  searchTab = await openNewSearchTab({ ctrlKey: true });
+  isnot(
+    searchTab,
+    gBrowser.selectedTab,
+    "Search tab is opened in background (Ctrl)"
+  );
+  BrowserTestUtils.removeTab(searchTab);
+
+  let current_browser = gBrowser.selectedBrowser;
+  searchTab = await openNewSearchTab({ shiftKey: true }, true);
+  isnot(
+    current_browser,
+    gBrowser.getBrowserForTab(searchTab),
+    "Search tab is opened in new window (Shift)"
+  );
+  BrowserTestUtils.removeTab(searchTab);
+
+  info("flipping browser.search.context.loadInBackground and re-checking");
+  await SpecialPowers.pushPrefEnv({
+    set: [["browser.search.context.loadInBackground", true]],
+  });
+
+  searchTab = await openNewSearchTab({});
+  isnot(
+    searchTab,
+    gBrowser.selectedTab,
+    "Search tab is opened in background (no modifiers)"
+  );
+  BrowserTestUtils.removeTab(searchTab);
+
+  searchTab = await openNewSearchTab({ button: 1 });
+  is(
+    searchTab,
+    gBrowser.selectedTab,
+    "Search tab is opened in foreground (middle mouse)"
+  );
+  BrowserTestUtils.removeTab(searchTab);
+
+  searchTab = await openNewSearchTab({ ctrlKey: true });
+  is(
+    searchTab,
+    gBrowser.selectedTab,
+    "Search tab is opened in foreground (Ctrl)"
+  );
+  BrowserTestUtils.removeTab(searchTab);
+
+  current_browser = gBrowser.selectedBrowser;
+  searchTab = await openNewSearchTab({ shiftKey: true }, true);
+  isnot(
+    current_browser,
+    gBrowser.getBrowserForTab(searchTab),
+    "Search tab is opened in new window (Shift)"
+  );
+  BrowserTestUtils.removeTab(searchTab);
+
+  // cleanup
+  BrowserTestUtils.removeTab(tab);
+});
+
+// We can't do the unload within registerCleanupFunction as that's too late for
+// the test to be happy. Do it into a cleanup "test" here instead.
+add_task(async function cleanup() {
+  await Services.search.setDefault(oldDefaultEngine);
+  await Services.search.removeEngine(engine);
+
+  await extension.unload();
+});
--- a/browser/components/search/test/browser/mozsearch.sjs
+++ b/browser/components/search/test/browser/mozsearch.sjs
@@ -1,11 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function handleRequest(req, resp) {
-  resp.setHeader("Content-Type", "text/html", false);
+  resp.setHeader("Content-Type", "text/html; charset=UTF-8", false);
   if (req.hasHeader("Origin") && req.getHeader("Origin") != "null") {
     resp.write("error");
     return;
   }
   resp.write("hello world");
 }
--- a/browser/locales/en-US/browser/browser.ftl
+++ b/browser/locales/en-US/browser/browser.ftl
@@ -533,19 +533,18 @@ fullscreen-exit-mac-button = Exit Full S
 # Variables
 #  $domain (String): the domain that is using pointer-lock, e.g. "mozilla.org"
 pointerlock-warning-domain = <span data-l10n-name="domain">{ $domain }</span> has control of your pointer. Press Esc to take back control.
 pointerlock-warning-no-domain = This document has control of your pointer. Press Esc to take back control.
 
 ## Subframe crash notification
 
 crashed-subframe-message = <strong>Part of this page crashed.</strong> To let { -brand-product-name } know about this issue and get it fixed faster, please submit a report.
-crashed-subframe-learnmore =
-  .label = Learn More
-  .accesskey = L
+crashed-subframe-learnmore-link =
+  .value = Learn More
 crashed-subframe-submit =
   .label = Submit report
   .accesskey = S
 
 ## Bookmarks panels, menus and toolbar
 
 bookmarks-show-all-bookmarks =
   .label = Show All Bookmarks
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -1325,20 +1325,16 @@ certs-enable-ocsp =
 certs-view =
     .label = View Certificates…
     .accesskey = C
 
 certs-devices =
     .label = Security Devices…
     .accesskey = D
 
-space-alert-learn-more-button =
-    .label = Learn More
-    .accesskey = L
-
 space-alert-over-5gb-pref-button =
     .label =
         { PLATFORM() ->
             [windows] Open Options
            *[other] Open Preferences
         }
     .accesskey =
         { PLATFORM() ->
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -856,21 +856,19 @@ getUserMedia.sharingMenuAudioCaptureBrow
 getUserMedia.sharingMenuUnknownHost = Unknown origin
 
 # LOCALIZATION NOTE(emeNotifications.drmContentPlaying.message2): %S is brandShortName.
 emeNotifications.drmContentPlaying.message2 = Some audio or video on this site uses DRM software, which may limit what %S can let you do with it.
 emeNotifications.drmContentPlaying.button.label = Configure…
 emeNotifications.drmContentPlaying.button.accesskey = C
 
 # LOCALIZATION NOTE(emeNotifications.drmContentDisabled.message): NB: inserted via innerHTML, so please don't use <, > or & in this string. %S will be the 'learn more' link
-emeNotifications.drmContentDisabled.message = You must enable DRM to play some audio or video on this page. %S
+emeNotifications.drmContentDisabled.message2 = You must enable DRM to play some audio or video on this page.
 emeNotifications.drmContentDisabled.button.label = Enable DRM
 emeNotifications.drmContentDisabled.button.accesskey = E
-# LOCALIZATION NOTE(emeNotifications.drmContentDisabled.learnMoreLabel): NB: inserted via innerHTML, so please don't use <, > or & in this string.
-emeNotifications.drmContentDisabled.learnMoreLabel = Learn More
 
 # LOCALIZATION NOTE(emeNotifications.drmContentCDMInstalling.message): NB: inserted via innerHTML, so please don't use <, > or & in this string. %S is brandShortName
 emeNotifications.drmContentCDMInstalling.message = %S is installing components needed to play the audio or video on this page. Please try again later.
 
 emeNotifications.unknownDRMSoftware = Unknown
 
 # LOCALIZATION NOTE  - %S is brandShortName
 flashHang.message = %S changed some Adobe Flash settings to improve performance.
--- a/browser/locales/en-US/chrome/browser/places/places.properties
+++ b/browser/locales/en-US/chrome/browser/places/places.properties
@@ -49,18 +49,16 @@ detailsPane.noItems=No items
 # #1 number of items
 # example: 111 items
 detailsPane.itemsCountLabel=One item;#1 items
 
 # LOCALIZATION NOTE (lockPrompt.text)
 # %S will be replaced with the application name.
 lockPrompt.title=Browser Startup Error
 lockPrompt.text=The bookmarks and history system will not be functional because one of %S’s files is in use by another application. Some security software can cause this problem.
-lockPromptInfoButton.label=Learn More
-lockPromptInfoButton.accessKey=L
 
 # LOCALIZATION NOTE (cmd.deleteSinglePage.accesskey,
 # cmd.deleteMultiplePages.accesskey): these accesskeys can use the same
 # character, since they're never displayed at the same time
 cmd.deleteSinglePage.label=Delete Page
 cmd.deleteSinglePage.accesskey=D
 cmd.deleteMultiplePages.label=Delete Pages
 cmd.deleteMultiplePages.accesskey=D
--- a/browser/modules/ContentCrashHandlers.jsm
+++ b/browser/modules/ContentCrashHandlers.jsm
@@ -404,21 +404,19 @@ var TabCrashHandler = {
     let doc = browser.ownerDocument;
     let messageFragment = doc.createDocumentFragment();
     let message = doc.createElement("span");
     message.setAttribute("data-l10n-id", "crashed-subframe-message");
     messageFragment.appendChild(message);
 
     let buttons = [
       {
-        "l10n-id": "crashed-subframe-learnmore",
+        "l10n-id": "crashed-subframe-learnmore-link",
         popup: null,
-        callback: async () => {
-          doc.defaultView.openTrustedLinkIn(SUBFRAMECRASH_LEARNMORE_URI, "tab");
-        },
+        link: SUBFRAMECRASH_LEARNMORE_URI,
       },
       {
         "l10n-id": "crashed-subframe-submit",
         popup: null,
         callback: async () => {
           if (dumpID) {
             UnsubmittedCrashHandler.submitReports([dumpID]);
           }
--- a/browser/modules/ProcessHangMonitor.jsm
+++ b/browser/modules/ProcessHangMonitor.jsm
@@ -518,16 +518,21 @@ var ProcessHangMonitor = {
     if (notification) {
       return;
     }
 
     let bundle = win.gNavigatorBundle;
 
     let buttons = [
       {
+        label: bundle.getString("processHang.add-on.learn-more.text"),
+        link:
+          "https://support.mozilla.org/kb/warning-unresponsive-script#w_other-causes",
+      },
+      {
         label: bundle.getString("processHang.button_stop.label"),
         accessKey: bundle.getString("processHang.button_stop.accessKey"),
         callback() {
           ProcessHangMonitor.stopIt(win);
         },
       },
       {
         label: bundle.getString("processHang.button_wait.label"),
@@ -544,37 +549,21 @@ var ProcessHangMonitor = {
         Ci.nsIAddonPolicyService
       );
 
       let doc = win.document;
       let brandBundle = doc.getElementById("bundle_brand");
 
       let addonName = aps.getExtensionName(report.addonId);
 
-      let label = bundle.getFormattedString("processHang.add-on.label", [
+      message = bundle.getFormattedString("processHang.add-on.label", [
         addonName,
         brandBundle.getString("brandShortName"),
       ]);
 
-      let linkText = bundle.getString("processHang.add-on.learn-more.text");
-      let linkURL =
-        "https://support.mozilla.org/kb/warning-unresponsive-script#w_other-causes";
-
-      let link = doc.createXULElement("label", { is: "text-link" });
-      link.setAttribute("role", "link");
-      link.setAttribute(
-        "onclick",
-        `openTrustedLinkIn(${JSON.stringify(linkURL)}, "tab")`
-      );
-      link.setAttribute("value", linkText);
-
-      message = doc.createDocumentFragment();
-      message.appendChild(doc.createTextNode(label + " "));
-      message.appendChild(link);
-
       buttons.unshift({
         label: bundle.getString("processHang.button_stop_sandbox.label"),
         accessKey: bundle.getString(
           "processHang.button_stop_sandbox.accessKey"
         ),
         callback() {
           ProcessHangMonitor.stopGlobal(win);
         },
--- a/devtools/client/inspector/test/browser.ini
+++ b/devtools/client/inspector/test/browser.ini
@@ -66,16 +66,17 @@ skip-if = os == "mac" # Full keyboard na
 [browser_inspector_breadcrumbs_mutations.js]
 [browser_inspector_breadcrumbs_namespaced.js]
 [browser_inspector_breadcrumbs_shadowdom.js]
 [browser_inspector_breadcrumbs_visibility.js]
 [browser_inspector_delete-selected-node-01.js]
 [browser_inspector_delete-selected-node-02.js]
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533490
 [browser_inspector_delete-selected-node-03.js]
+[browser_inspector_delete_node_in_frame.js]
 [browser_inspector_destroy-after-navigation.js]
 [browser_inspector_destroy-before-ready.js]
 [browser_inspector_expand-collapse.js]
 [browser_inspector_fission_frame.js]
 [browser_inspector_fission_frame_navigation.js]
 skip-if = fission && (bits == 64 && (os == "win" || os == "linux")) # Bug 1665482
 [browser_inspector_fission_switch_target.js]
 [browser_inspector_highlighter-01.js]
--- a/devtools/client/inspector/test/browser_inspector_delete-selected-node-02.js
+++ b/devtools/client/inspector/test/browser_inspector_delete-selected-node-02.js
@@ -18,17 +18,19 @@ add_task(async function() {
   await testDeleteWithNonElementNode();
 
   async function testManuallyDeleteSelectedNode() {
     info(
       "Selecting a node, deleting it via context menu and checking that " +
         "its parent node is selected and breadcrumbs are updated."
     );
 
-    await deleteNodeWithContextMenu("#deleteManually");
+    await selectNode("#deleteManually", inspector);
+    const nodeToBeDeleted = inspector.selection.nodeFront;
+    await deleteNodeWithContextMenu(nodeToBeDeleted, inspector);
 
     info("Performing checks.");
     await assertNodeSelectedAndPanelsUpdated(
       "#selectedAfterDelete",
       "li#selectedAfterDelete"
     );
   }
 
@@ -78,65 +80,33 @@ add_task(async function() {
 
   async function testDeleteWithNonElementNode() {
     info(
       "Selecting a node, deleting it via context menu and checking that " +
         "its parent node is selected and breadcrumbs are updated " +
         "when the node is followed by a non-element node"
     );
 
-    await deleteNodeWithContextMenu("#deleteWithNonElement");
+    await selectNode("#deleteWithNonElement", inspector);
+    const nodeToBeDeleted = inspector.selection.nodeFront;
+    await deleteNodeWithContextMenu(nodeToBeDeleted, inspector);
 
     let expectedCrumbs = ["html", "body", "div#deleteToMakeSingleTextNode"];
     await assertNodeSelectedAndCrumbsUpdated(expectedCrumbs, Node.TEXT_NODE);
 
     // Delete node with key, as cannot delete text node with
     // context menu at this time.
     inspector.markup._frame.focus();
     EventUtils.synthesizeKey("KEY_Delete");
     await inspector.once("inspector-updated");
 
     expectedCrumbs = ["html", "body", "div#deleteToMakeSingleTextNode"];
     await assertNodeSelectedAndCrumbsUpdated(expectedCrumbs, Node.ELEMENT_NODE);
   }
 
-  async function deleteNodeWithContextMenu(selector) {
-    await selectNode(selector, inspector);
-    const nodeToBeDeleted = inspector.selection.nodeFront;
-
-    info("Getting the node container in the markup view.");
-    const container = await getContainerForSelector(selector, inspector);
-
-    const allMenuItems = openContextMenuAndGetAllItems(inspector, {
-      target: container.tagLine,
-    });
-    const menuItem = allMenuItems.find(item => item.id === "node-menu-delete");
-
-    info("Clicking 'Delete Node' in the context menu.");
-    is(menuItem.disabled, false, "delete menu item is enabled");
-    menuItem.click();
-
-    // close the open context menu
-    EventUtils.synthesizeKey("KEY_Escape");
-
-    info("Waiting for inspector to update.");
-    await inspector.once("inspector-updated");
-
-    // Since the mutations are sent asynchronously from the server, the
-    // inspector-updated event triggered by the deletion might happen before
-    // the mutation is received and the element is removed from the
-    // breadcrumbs. See bug 1284125.
-    if (inspector.breadcrumbs.indexOf(nodeToBeDeleted) > -1) {
-      info("Crumbs haven't seen deletion. Waiting for breadcrumbs-updated.");
-      await inspector.once("breadcrumbs-updated");
-    }
-
-    return menuItem;
-  }
-
   function assertNodeSelectedAndCrumbsUpdated(
     expectedCrumbs,
     expectedNodeType
   ) {
     info("Performing checks");
     const actualNodeType = inspector.selection.nodeFront.nodeType;
     is(actualNodeType, expectedNodeType, "The node has the right type");
 
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/test/browser_inspector_delete_node_in_frame.js
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const TEST_URL_1 = `http://example.org/document-builder.sjs?html=
+<iframe srcdoc="<div>"></iframe><body>`;
+const TEST_URL_2 = `http://example.org/document-builder.sjs?html=<div id=url2>`;
+
+// Test that deleting a node in a same-process iframe and doing a navigation
+// does not freeze the browser or break the toolbox.
+add_task(async function() {
+  const { inspector } = await openInspectorForURL(TEST_URL_1);
+
+  info("Select a node in a same-process iframe");
+  const node = await selectNodeInFrames(["iframe", "div"], inspector);
+  const parentNode = node.parentNode();
+
+  info("Removing selected element with the context menu.");
+  await deleteNodeWithContextMenu(node, inspector);
+
+  is(
+    inspector.selection.nodeFront,
+    parentNode,
+    "The parent node of the deleted node was selected."
+  );
+
+  const onInspectorReloaded = inspector.once("reloaded");
+  await navigateTo(TEST_URL_2);
+  await onInspectorReloaded;
+
+  const url2NodeFront = await getNodeFront("#url2", inspector);
+  ok(url2NodeFront, "Can retrieve a node front after the navigation");
+});
--- a/devtools/client/inspector/test/head.js
+++ b/devtools/client/inspector/test/head.js
@@ -1415,8 +1415,46 @@ async function checkEyeDropperColorAt(
     options: { type: "mousemove" },
   });
 
   const colorValue = await testActorFront.getEyeDropperColorValue(
     inspectorActorID
   );
   is(colorValue, expectedColor, assertionDescription);
 }
+
+/**
+ * Delete the provided node front using the context menu in the markup view.
+ * Will resolve after the inspector UI was fully updated.
+ *
+ * @param {NodeFront} node
+ *        The node front to delete.
+ * @param {Inspector} inspector
+ *        The current inspector panel instance.
+ */
+async function deleteNodeWithContextMenu(node, inspector) {
+  const container = inspector.markup.getContainer(node);
+
+  const allMenuItems = openContextMenuAndGetAllItems(inspector, {
+    target: container.tagLine,
+  });
+  const menuItem = allMenuItems.find(item => item.id === "node-menu-delete");
+  const onInspectorUpdated = inspector.once("inspector-updated");
+
+  info("Clicking 'Delete Node' in the context menu.");
+  is(menuItem.disabled, false, "delete menu item is enabled");
+  menuItem.click();
+
+  // close the open context menu
+  EventUtils.synthesizeKey("KEY_Escape");
+
+  info("Waiting for inspector to update.");
+  await onInspectorUpdated;
+
+  // Since the mutations are sent asynchronously from the server, the
+  // inspector-updated event triggered by the deletion might happen before
+  // the mutation is received and the element is removed from the
+  // breadcrumbs. See bug 1284125.
+  if (inspector.breadcrumbs.indexOf(node) > -1) {
+    info("Crumbs haven't seen deletion. Waiting for breadcrumbs-updated.");
+    await inspector.once("breadcrumbs-updated");
+  }
+}
--- a/devtools/client/locales/en-US/perftools.ftl
+++ b/devtools/client/locales/en-US/perftools.ftl
@@ -106,17 +106,15 @@ perftools-record-all-registered-threads 
 
 perftools-tools-threads-input-label =
   .title = These thread names are a comma separated list that is used to enable profiling of the threads in the profiler. The name needs to be only a partial match of the thread name to be included. It is whitespace sensitive.
 
 ## Onboarding UI labels. These labels are displayed in the new performance panel UI, when
 ## both devtools.performance.new-panel-onboarding & devtools.performance.new-panel-enabled
 ## preferences are true.
 
--profiler-brand-name = Firefox Profiler
-
 perftools-onboarding-message = <b>New</b>: { -profiler-brand-name } is now integrated into Developer Tools. <a>Learn more</a> about this powerful new tool.
 
 # `options-context-advanced-settings` is defined in toolbox-options.ftl
 perftools-onboarding-reenable-old-panel = (For a limited time, you can access the original Performance panel via <a>{ options-context-advanced-settings }</a>)
 
 perftools-onboarding-close-button =
   .aria-label = Close the onboarding message
--- a/devtools/client/performance-new/aboutprofiling/initializer.js
+++ b/devtools/client/performance-new/aboutprofiling/initializer.js
@@ -88,19 +88,26 @@ const {
  * @param {(() => void)} [openRemoteDevTools] Optionally provide a way to go back to
  *                                            the remote devtools page.
  */
 async function gInit(perfFront, pageContext, openRemoteDevTools) {
   const store = createStore(reducers);
   const supportedFeatures = await perfFront.getSupportedFeatures();
 
   const l10n = new FluentL10n();
-  await l10n.init(["devtools/client/perftools.ftl"], {
-    setAttributesOnDocument: true,
-  });
+  await l10n.init(
+    [
+      "devtools/client/perftools.ftl",
+      // Needed for the onboarding UI
+      "browser/branding/brandings.ftl",
+    ],
+    {
+      setAttributesOnDocument: true,
+    }
+  );
 
   // Do some initialization, especially with privileged things that are part of the
   // the browser.
   store.dispatch(
     actions.initializeStore({
       perfFront,
       receiveProfile,
       supportedFeatures,
--- a/devtools/client/performance-new/initializer.js
+++ b/devtools/client/performance-new/initializer.js
@@ -106,16 +106,17 @@ async function gInit(perfFront, pageCont
     panelWindow.gStore = anyStore;
   }
 
   const l10n = new FluentL10n();
   await l10n.init([
     "devtools/client/perftools.ftl",
     // Needed for the onboarding UI
     "devtools/client/toolbox-options.ftl",
+    "browser/branding/brandings.ftl",
   ]);
 
   // Do some initialization, especially with privileged things that are part of the
   // the browser.
   store.dispatch(
     actions.initializeStore({
       perfFront,
       receiveProfile,
--- a/devtools/server/actors/inspector/walker.js
+++ b/devtools/server/actors/inspector/walker.js
@@ -2528,22 +2528,22 @@ var WalkerActor = protocol.ActorClassWit
         const documentActor = this._getOrCreateNodeActor(window.document);
         this.emit("root-available", documentActor);
       }
     }
   },
 
   // Returns true if domNode is in window or a subframe.
   _childOfWindow: function(window, domNode) {
-    let win = nodeDocument(domNode).defaultView;
-    while (win) {
+    while (domNode) {
+      const win = nodeDocument(domNode).defaultView;
       if (win === window) {
         return true;
       }
-      win = getFrameElement(win);
+      domNode = getFrameElement(win);
     }
     return false;
   },
 
   onFrameUnload: function({ window }) {
     // Any retained orphans that belong to this document
     // or its children need to be released, and a mutation sent
     // to notify of that.
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -807,32 +807,37 @@ Document* ExternalResourceMap::RequestRe
   }
 
   ExternalResource* resource;
   mMap.Get(clone, &resource);
   if (resource) {
     return resource->mDocument;
   }
 
-  RefPtr<PendingLoad>& loadEntry = mPendingLoads.LookupOrInsert(clone);
-  if (loadEntry) {
-    RefPtr<PendingLoad> load(loadEntry);
-    load.forget(aPendingLoad);
-    return nullptr;
-  }
-
-  RefPtr<PendingLoad> load(new PendingLoad(aDisplayDocument));
-  loadEntry = load;
-
-  if (NS_FAILED(load->StartLoad(clone, aReferrerInfo, aRequestingNode))) {
+  bool loadStartSucceeded =
+      mPendingLoads.WithEntryHandle(clone, [&](auto&& loadEntry) {
+        if (!loadEntry) {
+          loadEntry.Insert(MakeRefPtr<PendingLoad>(aDisplayDocument));
+
+          if (NS_FAILED(loadEntry.Data()->StartLoad(clone, aReferrerInfo,
+                                                    aRequestingNode))) {
+            return false;
+          }
+        }
+
+        RefPtr<PendingLoad> load(loadEntry.Data());
+        load.forget(aPendingLoad);
+        return true;
+      });
+  if (!loadStartSucceeded) {
     // Make sure we don't thrash things by trying this load again, since
     // chances are it failed for good reasons (security check, etc).
+    // This must be done outside the WithEntryHandle functor, as it accesses
+    // mPendingLoads.
     AddExternalResource(clone, nullptr, nullptr, aDisplayDocument);
-  } else {
-    load.forget(aPendingLoad);
   }
 
   return nullptr;
 }
 
 void ExternalResourceMap::EnumerateResources(SubDocEnumFunc aCallback) {
   nsTArray<RefPtr<Document>> docs(mMap.Count());
   for (const auto& entry : mMap) {
--- a/dom/base/MimeType.cpp
+++ b/dom/base/MimeType.cpp
@@ -230,20 +230,22 @@ TMimeType<char_type>::Parse(const nsTSub
           paramValue.mRequiresQuoting = true;
         }
         paramValue.Append(*c);
       }
     }
 
     // Step 11.10
     if (!paramName.IsEmpty() && !paramNameHadInvalidChars &&
-        !paramValueHadInvalidChars &&
-        !mimeType->mParameters.Get(paramName, &paramValue)) {
-      mimeType->mParameters.InsertOrUpdate(paramName, paramValue);
-      mimeType->mParameterNames.AppendElement(paramName);
+        !paramValueHadInvalidChars) {
+      // XXX Is the assigned value used anywhere?
+      paramValue = mimeType->mParameters.LookupOrInsertWith(paramName, [&] {
+        mimeType->mParameterNames.AppendElement(paramName);
+        return paramValue;
+      });
     }
   }
 
   // Step 12
   return mimeType;
 }
 
 template <typename char_type>
@@ -304,22 +306,24 @@ bool TMimeType<char_type>::GetParameterV
 
   return true;
 }
 
 template <typename char_type>
 void TMimeType<char_type>::SetParameterValue(
     const nsTSubstring<char_type>& aName,
     const nsTSubstring<char_type>& aValue) {
-  if (!mParameters.Get(aName, nullptr)) {
-    mParameterNames.AppendElement(aName);
-  }
-  ParameterValue value;
-  value.Append(aValue);
-  mParameters.InsertOrUpdate(aName, value);
+  mParameters.WithEntryHandle(aName, [&](auto&& entry) {
+    if (!entry) {
+      mParameterNames.AppendElement(aName);
+    }
+    ParameterValue value;
+    value.Append(aValue);
+    entry.InsertOrUpdate(std::move(value));
+  });
 }
 
 template mozilla::UniquePtr<TMimeType<char16_t>> TMimeType<char16_t>::Parse(
     const nsTSubstring<char16_t>& aMimeType);
 template mozilla::UniquePtr<TMimeType<char>> TMimeType<char>::Parse(
     const nsTSubstring<char>& aMimeType);
 template void TMimeType<char16_t>::Serialize(
     nsTSubstring<char16_t>& aOutput) const;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -959,17 +959,17 @@ bool nsContentUtils::InitializeEventTabl
       new nsDataHashtable<nsRefPtrHashKey<nsAtom>, EventNameMapping>(
           ArrayLength(eventArray));
   sStringEventTable = new nsDataHashtable<nsStringHashKey, EventNameMapping>(
       ArrayLength(eventArray));
   sUserDefinedEvents = new nsTArray<RefPtr<nsAtom>>(64);
 
   // Subtract one from the length because of the trailing null
   for (uint32_t i = 0; i < ArrayLength(eventArray) - 1; ++i) {
-    MOZ_ASSERT(!sAtomEventTable->Lookup(eventArray[i].mAtom),
+    MOZ_ASSERT(!sAtomEventTable->Contains(eventArray[i].mAtom),
                "Double-defining event name; fix your EventNameList.h");
     sAtomEventTable->InsertOrUpdate(eventArray[i].mAtom, eventArray[i]);
     if (ShouldAddEventToStringEventTable(eventArray[i])) {
       sStringEventTable->InsertOrUpdate(
           Substring(nsDependentAtomString(eventArray[i].mAtom), 2),
           eventArray[i]);
     }
   }
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1000,19 +1000,19 @@ void MessageManagerReporter::CountRefere
   for (auto it = aMessageManager->mListeners.Iter(); !it.Done(); it.Next()) {
     nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = it.UserData();
     uint32_t listenerCount = listeners->Length();
     if (listenerCount == 0) {
       continue;
     }
 
     nsString key(it.Key());
-    const uint32_t oldCount = aReferentCount->mMessageCounter.Get(key);
-    uint32_t currentCount = oldCount + listenerCount;
-    aReferentCount->mMessageCounter.InsertOrUpdate(key, currentCount);
+    const uint32_t currentCount =
+        (aReferentCount->mMessageCounter.LookupOrInsert(key, 0) +=
+         listenerCount);
 
     // Keep track of messages that have a suspiciously large
     // number of referents (symptom of leak).
     if (currentCount >= MessageManagerReporter::kSuspectReferentCount) {
       aReferentCount->mSuspectMessages.AppendElement(key);
     }
 
     for (uint32_t i = 0; i < listenerCount; ++i) {
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -1002,17 +1002,17 @@ nsGlobalWindowInner::nsGlobalWindowInner
            static_cast<void*>(ToCanonicalSupports(aOuterWindow))));
 #endif
 
   MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
           ("DOMWINDOW %p created outer=%p", this, aOuterWindow));
 
   // Add ourselves to the inner windows list.
   MOZ_ASSERT(sInnerWindowsById, "Inner Windows hash table must be created!");
-  MOZ_ASSERT(!sInnerWindowsById->Get(mWindowID),
+  MOZ_ASSERT(!sInnerWindowsById->Contains(mWindowID),
              "This window shouldn't be in the hash table yet!");
   // We seem to see crashes in release builds because of null
   // |sInnerWindowsById|.
   if (sInnerWindowsById) {
     sInnerWindowsById->InsertOrUpdate(mWindowID, this);
   }
 }
 
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -1366,17 +1366,17 @@ nsGlobalWindowOuter::nsGlobalWindowOuter
 
   MOZ_LOG(gDOMLeakPRLogOuter, LogLevel::Debug,
           ("DOMWINDOW %p created outer=nullptr", this));
 
   // Add ourselves to the outer windows list.
   MOZ_ASSERT(sOuterWindowsById, "Outer Windows hash table must be created!");
 
   // |this| is an outer window, add to the outer windows list.
-  MOZ_ASSERT(!sOuterWindowsById->Get(mWindowID),
+  MOZ_ASSERT(!sOuterWindowsById->Contains(mWindowID),
              "This window shouldn't be in the hash table yet!");
   // We seem to see crashes in release builds because of null
   // |sOuterWindowsById|.
   if (sOuterWindowsById) {
     sOuterWindowsById->InsertOrUpdate(mWindowID, this);
   }
 }
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -21635,18 +21635,24 @@ class CGMaplikeOrSetlikeHelperFunctionGe
             code += dedent(
                 """
                 AutoJSAPI jsapi;
                 jsapi.Init();
                 JSContext* cx = jsapi.cx();
                 // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here because
                 // all we want is to wrap into _some_ scope and then unwrap to find
                 // the reflector, and wrapping has no side-effects.
-                JSAutoRealm tempRealm(cx, UnprivilegedJunkScopeOrWorkerGlobal());
-                """
+                JSObject* scope = UnprivilegedJunkScopeOrWorkerGlobal(fallible);
+                if (!scope) {
+                  aRv.Throw(NS_ERROR_UNEXPECTED);
+                  return%s;
+                }
+                JSAutoRealm tempRealm(cx, scope);
+                """
+                % self.getDefaultRetval()
             )
 
         code += dedent(
             """
             JS::Rooted<JS::Value> v(cx);
             if(!ToJSValue(cx, self, &v)) {
               aRv.Throw(NS_ERROR_UNEXPECTED);
               return%s;
--- a/dom/ipc/tests/browser_crash_oopiframe.js
+++ b/dom/ipc/tests/browser_crash_oopiframe.js
@@ -155,18 +155,24 @@ async function testFrameCrash(numTabs) {
       notification.getAttribute("value"),
       "subframe-crashed",
       "Should be showing the right notification" + count
     );
 
     let buttons = notification.querySelectorAll(".notification-button");
     is(
       buttons.length,
-      2,
-      "Notification " + count + " should have only two buttons."
+      1,
+      "Notification " + count + " should have only one button."
+    );
+    let links = notification.querySelectorAll(".text-link");
+    is(
+      links.length,
+      1,
+      "Notification " + count + " should have only one link."
     );
   }
 
   // Press the ignore button on the visible notification.
   let notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
   let notification = notificationBox.currentNotification;
   notification.dismiss();
 
--- a/dom/media/doctor/test/browser/browser_decoderDoctor.js
+++ b/dom/media/doctor/test/browser/browser_decoderDoctor.js
@@ -13,16 +13,17 @@
 // 'accessKey': Expected access key for the button.
 // 'tabChecker': function(openedTab) called with the opened tab that resulted
 //   from clicking the button.
 async function test_decoder_doctor_notification(
   data,
   notificationMessage,
   label,
   accessKey,
+  isLink,
   tabChecker
 ) {
   const TEST_URL = "https://example.org";
   // A helper closure to test notifications in same or different origins.
   // 'test_cross_origin' is used to determine if the observers used in the test
   // are notified in the same frame (when false) or in a cross origin iframe
   // (when true).
   async function create_tab_and_test(test_cross_origin) {
@@ -95,38 +96,58 @@ async function test_decoder_doctor_notif
 
         is(
           notification.messageText.textContent,
           notificationMessage,
           "notification message should match expectation"
         );
 
         let button = notification.querySelector("button");
+        let link = notification.querySelector(".text-link");
         if (!label) {
-          ok(!button, "There should not be button");
+          ok(!button, "There should not be a button");
+          ok(!link, "There should not be a link");
           return;
         }
 
-        is(
-          button.getAttribute("label"),
-          label,
-          `notification button should be '${label}'`
-        );
-        is(
-          button.getAttribute("accesskey"),
-          accessKey,
-          "notification button should have accesskey"
-        );
+        if (isLink) {
+          ok(!button, "There should not be a button");
+          is(
+            link.getAttribute("value"),
+            label,
+            `notification link should be '${label}'`
+          );
+          ok(
+            !link.hasAttribute("accesskey"),
+            "notification link should not have accesskey"
+          );
+        } else {
+          ok(!link, "There should not be a link");
+          is(
+            button.getAttribute("label"),
+            label,
+            `notification button should be '${label}'`
+          );
+          is(
+            button.getAttribute("accesskey"),
+            accessKey,
+            "notification button should have accesskey"
+          );
+        }
 
         if (!tabChecker) {
           ok(false, "Test implementation error: Missing tabChecker");
           return;
         }
         let awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser);
-        button.click();
+        if (button) {
+          button.click();
+        } else {
+          link.click();
+        }
         let openedTab = await awaitNewTab;
         tabChecker(openedTab);
         BrowserTestUtils.removeTab(openedTab);
       }
     );
   }
 
   if (typeof data.type === "undefined") {
@@ -191,32 +212,34 @@ add_task(async function test_platform_de
     message = gNavigatorBundle.getString("decoder.noHWAcceleration.message");
   }
 
   await test_decoder_doctor_notification(
     { type: "platform-decoder-not-found", formats: "testFormat" },
     message,
     isLinux ? "" : gNavigatorBundle.getString("decoder.noCodecs.button"),
     isLinux ? "" : gNavigatorBundle.getString("decoder.noCodecs.accesskey"),
+    true,
     tab_checker_for_sumo("fix-video-audio-problems-firefox-windows")
   );
 });
 
 add_task(async function test_cannot_initialize_pulseaudio() {
   let message = "";
   // This is only sent on Linux.
   if (AppConstants.platform == "linux") {
     message = gNavigatorBundle.getString("decoder.noPulseAudio.message");
   }
 
   await test_decoder_doctor_notification(
     { type: "cannot-initialize-pulseaudio", formats: "testFormat" },
     message,
     gNavigatorBundle.getString("decoder.noCodecs.button"),
     gNavigatorBundle.getString("decoder.noCodecs.accesskey"),
+    true,
     tab_checker_for_sumo("fix-common-audio-and-video-issues")
   );
 });
 
 add_task(async function test_unsupported_libavcodec() {
   let message = "";
   // This is only sent on Linux.
   if (AppConstants.platform == "linux") {
@@ -247,16 +270,17 @@ add_task(async function test_decode_erro
       type: "decode-error",
       decodeIssue: "DecodeIssue",
       docURL: "DocURL",
       resourceURL: "ResURL",
     },
     message,
     gNavigatorBundle.getString("decoder.decodeError.button"),
     gNavigatorBundle.getString("decoder.decodeError.accesskey"),
+    false,
     tab_checker_for_webcompat({
       url: "DocURL",
       label: "type-media",
       problem_type: "video_bug",
       details: JSON.stringify({
         "Technical Information:": "DecodeIssue",
         "Resource:": "ResURL",
       }),
@@ -279,16 +303,17 @@ add_task(async function test_decode_warn
       type: "decode-warning",
       decodeIssue: "DecodeIssue",
       docURL: "DocURL",
       resourceURL: "ResURL",
     },
     message,
     gNavigatorBundle.getString("decoder.decodeError.button"),
     gNavigatorBundle.getString("decoder.decodeError.accesskey"),
+    false,
     tab_checker_for_webcompat({
       url: "DocURL",
       label: "type-media",
       problem_type: "video_bug",
       details: JSON.stringify({
         "Technical Information:": "DecodeIssue",
         "Resource:": "ResURL",
       }),
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -1870,24 +1870,24 @@ void PluginInstanceParent::NPP_URLNotify
   PStreamNotifyParent* streamNotify =
       static_cast<PStreamNotifyParent*>(notifyData);
   Unused << PStreamNotifyParent::Send__delete__(streamNotify, reason);
 }
 
 bool PluginInstanceParent::RegisterNPObjectForActor(
     NPObject* aObject, PluginScriptableObjectParent* aActor) {
   NS_ASSERTION(aObject && aActor, "Null pointers!");
-  NS_ASSERTION(!mScriptableObjects.Get(aObject, nullptr), "Duplicate entry!");
+  NS_ASSERTION(!mScriptableObjects.Contains(aObject), "Duplicate entry!");
   mScriptableObjects.InsertOrUpdate(aObject, aActor);
   return true;
 }
 
 void PluginInstanceParent::UnregisterNPObject(NPObject* aObject) {
   NS_ASSERTION(aObject, "Null pointer!");
-  NS_ASSERTION(mScriptableObjects.Get(aObject, nullptr), "Unknown entry!");
+  NS_ASSERTION(mScriptableObjects.Contains(aObject), "Unknown entry!");
   mScriptableObjects.Remove(aObject);
 }
 
 PluginScriptableObjectParent* PluginInstanceParent::GetActorForNPObject(
     NPObject* aObject) {
   NS_ASSERTION(aObject, "Null pointer!");
 
   if (aObject->_class == PluginScriptableObjectParent::GetClass()) {
--- a/dom/plugins/ipc/PluginScriptableObjectChild.cpp
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.cpp
@@ -36,29 +36,23 @@ using namespace mozilla::plugins;
  * reason to retain identifiers there.
  */
 
 PluginScriptableObjectChild::IdentifierTable
     PluginScriptableObjectChild::sIdentifiers;
 
 /* static */ PluginScriptableObjectChild::StoredIdentifier*
 PluginScriptableObjectChild::HashIdentifier(const nsCString& aIdentifier) {
-  StoredIdentifier* stored = sIdentifiers.Get(aIdentifier).get();
-  if (stored) {
-    return stored;
-  }
-
-  stored = new StoredIdentifier(aIdentifier);
-  sIdentifiers.InsertOrUpdate(aIdentifier, stored);
-  return stored;
+  return sIdentifiers.LookupOrInsertWith(
+      aIdentifier, [&] { return MakeRefPtr<StoredIdentifier>(aIdentifier); });
 }
 
 /* static */
 void PluginScriptableObjectChild::UnhashIdentifier(StoredIdentifier* aStored) {
-  MOZ_ASSERT(sIdentifiers.Get(aStored->mIdentifier));
+  MOZ_ASSERT(sIdentifiers.Contains(aStored->mIdentifier));
   sIdentifiers.Remove(aStored->mIdentifier);
 }
 
 /* static */
 void PluginScriptableObjectChild::ClearIdentifiers() { sIdentifiers.Clear(); }
 
 PluginScriptableObjectChild::StackIdentifier::StackIdentifier(
     const PluginIdentifier& aIdentifier)
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -4665,20 +4665,37 @@ nsresult QuotaManager::InitializeReposit
                           }
 
                           // XXXtt: Try to restore the unknown cases base on the
                           // content for their metadata files. Note that if the
                           // restore fails, QM should maintain a list and ensure
                           // they won't be accessed after initialization.
                         }
 
-                        QM_TRY(InitializeOrigin(
-                            aPersistenceType, metadata.mOriginMetadata,
-                            metadata.mTimestamp, metadata.mPersisted,
-                            childDirectory));
+                        QM_TRY(
+                            ToResult(InitializeOrigin(aPersistenceType,
+                                                      metadata.mOriginMetadata,
+                                                      metadata.mTimestamp,
+                                                      metadata.mPersisted,
+                                                      childDirectory))
+                                .orElse([&childDirectory](const nsresult rv)
+                                            -> Result<Ok, nsresult> {
+                                  if (IsDatabaseCorruptionError(rv)) {
+                                    // If the origin can't be initialized due to
+                                    // corruption, this is a permanent
+                                    // condition, and we need to remove all data
+                                    // for the origin on disk.
+
+                                    QM_TRY(childDirectory->Remove(true));
+
+                                    return Ok{};
+                                  }
+
+                                  return Err(rv);
+                                }));
 
                         break;
                       }
 
                       case nsIFileKind::ExistsAsFile:
                         if (IsOSMetadata(leafName) || IsDotFile(leafName)) {
                           break;
                         }
--- a/dom/storage/LocalStorageCache.cpp
+++ b/dom/storage/LocalStorageCache.cpp
@@ -534,22 +534,20 @@ uint32_t LocalStorageCache::LoadedCount(
 bool LocalStorageCache::LoadItem(const nsAString& aKey,
                                  const nsString& aValue) {
   MonitorAutoLock monitor(mMonitor);
   if (mLoaded) {
     return false;
   }
 
   Data& data = mData[kDefaultSet];
-  if (data.mKeys.Get(aKey, nullptr)) {
-    return true;  // don't stop, just don't override
-  }
-
-  data.mKeys.InsertOrUpdate(aKey, aValue);
-  data.mOriginQuotaUsage += aKey.Length() + aValue.Length();
+  data.mKeys.LookupOrInsertWith(aKey, [&] {
+    data.mOriginQuotaUsage += aKey.Length() + aValue.Length();
+    return aValue;
+  });
   return true;
 }
 
 void LocalStorageCache::LoadDone(nsresult aRv) {
   MonitorAutoLock monitor(mMonitor);
   mLoadResult = aRv;
   mLoaded = true;
   monitor.Notify();
--- a/dom/storage/LocalStorageManager.cpp
+++ b/dom/storage/LocalStorageManager.cpp
@@ -105,30 +105,27 @@ LocalStorageCache* LocalStorageManager::
     return nullptr;
   }
 
   return entry->cache();
 }
 
 already_AddRefed<StorageUsage> LocalStorageManager::GetOriginUsage(
     const nsACString& aOriginNoSuffix, const uint32_t aPrivateBrowsingId) {
-  RefPtr<StorageUsage> usage;
-  if (mUsages.Get(aOriginNoSuffix, &usage)) {
-    return usage.forget();
-  }
-
-  usage = new StorageUsage(aOriginNoSuffix);
+  RefPtr<StorageUsage> usage = mUsages.LookupOrInsertWith(aOriginNoSuffix, [&] {
+    auto usage = MakeRefPtr<StorageUsage>(aOriginNoSuffix);
 
-  StorageDBChild* storageChild =
-      StorageDBChild::GetOrCreate(aPrivateBrowsingId);
-  if (storageChild) {
-    storageChild->AsyncGetUsage(usage);
-  }
+    StorageDBChild* storageChild =
+        StorageDBChild::GetOrCreate(aPrivateBrowsingId);
+    if (storageChild) {
+      storageChild->AsyncGetUsage(usage);
+    }
 
-  mUsages.InsertOrUpdate(aOriginNoSuffix, usage);
+    return usage;
+  });
 
   return usage.forget();
 }
 
 already_AddRefed<LocalStorageCache> LocalStorageManager::PutCache(
     const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix,
     const nsACString& aQuotaKey, nsIPrincipal* aPrincipal) {
   CacheOriginHashtable* table = mCaches.GetOrInsertNew(aOriginSuffix);
--- a/dom/storage/SessionStorageManager.cpp
+++ b/dom/storage/SessionStorageManager.cpp
@@ -45,17 +45,17 @@ void RecvPropagateBackgroundSessionStora
     uint64_t aCurrentTopContextId, uint64_t aTargetTopContextId) {
   ::mozilla::ipc::AssertIsOnBackgroundThread();
 
   if (sManagers) {
     if (RefPtr<BackgroundSessionStorageManager> mgr =
             sManagers->Get(aCurrentTopContextId)) {
       // Assuming the target top browsing context should haven't been
       // registered yet.
-      MOZ_DIAGNOSTIC_ASSERT(!sManagers->GetWeak(aTargetTopContextId));
+      MOZ_DIAGNOSTIC_ASSERT(!sManagers->Contains(aTargetTopContextId));
       sManagers->InsertOrUpdate(aTargetTopContextId, std::move(mgr));
     }
   }
 }
 
 bool RecvRemoveBackgroundSessionStorageManager(uint64_t aTopContextId) {
   ::mozilla::ipc::AssertIsOnBackgroundThread();
 
@@ -65,45 +65,39 @@ bool RecvRemoveBackgroundSessionStorageM
 
   return true;
 }
 
 SessionStorageManagerBase::OriginRecord*
 SessionStorageManagerBase::GetOriginRecord(
     const nsACString& aOriginAttrs, const nsACString& aOriginKey,
     const bool aMakeIfNeeded, SessionStorageCache* const aCloneFrom) {
-  OriginKeyHashTable* table;
-  if (!mOATable.Get(aOriginAttrs, &table)) {
-    if (aMakeIfNeeded) {
-      table =
-          mOATable
-              .InsertOrUpdate(aOriginAttrs, MakeUnique<OriginKeyHashTable>())
-              .get();
-    } else {
-      return nullptr;
-    }
+  // XXX It seems aMakeIfNeeded is always known at compile-time, so this could
+  // be split into two functions.
+
+  if (aMakeIfNeeded) {
+    return mOATable.GetOrInsertNew(aOriginAttrs)
+        ->LookupOrInsertWith(
+            aOriginKey,
+            [&] {
+              auto newOriginRecord = MakeUnique<OriginRecord>();
+              if (aCloneFrom) {
+                newOriginRecord->mCache = aCloneFrom->Clone();
+              } else {
+                newOriginRecord->mCache = new SessionStorageCache();
+              }
+              return newOriginRecord;
+            })
+        .get();
   }
 
-  OriginRecord* originRecord;
-  if (!table->Get(aOriginKey, &originRecord)) {
-    if (aMakeIfNeeded) {
-      auto newOriginRecord = MakeUnique<OriginRecord>();
-      if (aCloneFrom) {
-        newOriginRecord->mCache = aCloneFrom->Clone();
-      } else {
-        newOriginRecord->mCache = new SessionStorageCache();
-      }
-      originRecord =
-          table->InsertOrUpdate(aOriginKey, std::move(newOriginRecord)).get();
-    } else {
-      return nullptr;
-    }
-  }
+  auto* const table = mOATable.Get(aOriginAttrs);
+  if (!table) return nullptr;
 
-  return originRecord;
+  return table->Get(aOriginKey);
 }
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SessionStorageManager)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
   NS_INTERFACE_MAP_ENTRY(nsIDOMSessionStorageManager)
 NS_INTERFACE_MAP_END
 
--- a/gfx/qcms/src/chain.rs
+++ b/gfx/qcms/src/chain.rs
@@ -54,18 +54,17 @@ pub struct ModularTransform {
     output_gamma_lut_g: Option<Vec<u16>>,
     output_gamma_lut_b: Option<Vec<u16>>,
     output_gamma_lut_r_length: usize,
     output_gamma_lut_g_length: usize,
     output_gamma_lut_b_length: usize,
     transform_module_fn: TransformModuleFn,
     next_transform: Option<Box<ModularTransform>>,
 }
-pub type TransformModuleFn =
-    Option<fn(_: &ModularTransform, _: &[f32], _: &mut [f32]) -> ()>;
+pub type TransformModuleFn = Option<fn(_: &ModularTransform, _: &[f32], _: &mut [f32]) -> ()>;
 
 #[inline]
 fn lerp(a: f32, b: f32, t: f32) -> f32 {
     a * (1.0 - t) + b * t
 }
 
 fn build_lut_matrix(lut: Option<&lutType>) -> Matrix {
     let mut result: Matrix = Matrix {
@@ -450,21 +449,17 @@ fn transform_module_gamma_lut(transform:
         out_r = lut_interp_linear(in_r as f64, &transform.output_gamma_lut_r.as_ref().unwrap());
         out_g = lut_interp_linear(in_g as f64, &transform.output_gamma_lut_g.as_ref().unwrap());
         out_b = lut_interp_linear(in_b as f64, &transform.output_gamma_lut_b.as_ref().unwrap());
         dest[0] = clamp_float(out_r);
         dest[1] = clamp_float(out_g);
         dest[2] = clamp_float(out_b);
     }
 }
-fn transform_module_matrix_translate(
-    transform: &ModularTransform,
-    src: &[f32],
-    dest: &mut [f32],
-) {
+fn transform_module_matrix_translate(transform: &ModularTransform, src: &[f32], dest: &mut [f32]) {
     let mut mat: Matrix = Matrix {
         m: [[0.; 3]; 3],
         invalid: false,
     };
     /* store the results in column major mode
      * this makes doing the multiplication with sse easier */
     mat.m[0][0] = transform.matrix.m[0][0];
     mat.m[1][0] = transform.matrix.m[0][1];
@@ -514,18 +509,18 @@ fn transform_module_matrix(transform: &M
         let out_r: f32 = mat.m[0][0] * in_r + mat.m[1][0] * in_g + mat.m[2][0] * in_b;
         let out_g: f32 = mat.m[0][1] * in_r + mat.m[1][1] * in_g + mat.m[2][1] * in_b;
         let out_b: f32 = mat.m[0][2] * in_r + mat.m[1][2] * in_g + mat.m[2][2] * in_b;
         dest[0] = clamp_float(out_r);
         dest[1] = clamp_float(out_g);
         dest[2] = clamp_float(out_b);
     }
 }
-fn modular_transform_alloc() -> Option<Box<ModularTransform>> {
-    Some(Box::new(Default::default()))
+fn modular_transform_alloc() -> Box<ModularTransform> {
+    Box::new(Default::default())
 }
 fn modular_transform_release(mut t: Option<Box<ModularTransform>>) {
     // destroy a list of transforms non-recursively
     let mut next_transform;
     while let Some(mut transform) = t {
         next_transform = std::mem::replace(&mut transform.next_transform, None);
         t = next_transform
     }
@@ -563,86 +558,72 @@ fn modular_transform_create_mAB(lut: &lu
     if lut.a_curves[0].is_some() {
         let clut_length: usize;
         // If the A curve is present this also implies the
         // presence of a CLUT.
         lut.clut_table.as_ref()?;
 
         // Prepare A curve.
         transform = modular_transform_alloc();
-        transform.as_ref()?;
-        transform.as_mut().unwrap().input_clut_table_r =
-            build_input_gamma_table(lut.a_curves[0].as_deref());
-        transform.as_mut().unwrap().input_clut_table_g =
-            build_input_gamma_table(lut.a_curves[1].as_deref());
-        transform.as_mut().unwrap().input_clut_table_b =
-            build_input_gamma_table(lut.a_curves[2].as_deref());
-        transform.as_mut().unwrap().transform_module_fn = Some(transform_module_gamma_table);
-        next_transform = append_transform(transform, next_transform);
+        transform.input_clut_table_r = build_input_gamma_table(lut.a_curves[0].as_deref());
+        transform.input_clut_table_g = build_input_gamma_table(lut.a_curves[1].as_deref());
+        transform.input_clut_table_b = build_input_gamma_table(lut.a_curves[2].as_deref());
+        transform.transform_module_fn = Some(transform_module_gamma_table);
+        next_transform = append_transform(Some(transform), next_transform);
 
         if lut.num_grid_points[0] as i32 != lut.num_grid_points[1] as i32
             || lut.num_grid_points[1] as i32 != lut.num_grid_points[2] as i32
         {
             //XXX: We don't currently support clut that are not squared!
             return None;
         }
 
         // Prepare CLUT
         transform = modular_transform_alloc();
-        transform.as_ref()?;
 
         clut_length = (lut.num_grid_points[0] as usize).pow(3) * 3;
         assert_eq!(clut_length, lut.clut_table.as_ref().unwrap().len());
-        transform.as_mut().unwrap().clut = lut.clut_table.clone();
-        transform.as_mut().unwrap().grid_size = lut.num_grid_points[0] as u16;
-        transform.as_mut().unwrap().transform_module_fn = Some(transform_module_clut_only);
-        next_transform = append_transform(transform, next_transform);
+        transform.clut = lut.clut_table.clone();
+        transform.grid_size = lut.num_grid_points[0] as u16;
+        transform.transform_module_fn = Some(transform_module_clut_only);
+        next_transform = append_transform(Some(transform), next_transform);
     }
 
     if lut.m_curves[0].is_some() {
         // M curve imples the presence of a Matrix
 
         // Prepare M curve
         transform = modular_transform_alloc();
-        transform.as_ref()?;
-        transform.as_mut().unwrap().input_clut_table_r =
-            build_input_gamma_table(lut.m_curves[0].as_deref());
-        transform.as_mut().unwrap().input_clut_table_g =
-            build_input_gamma_table(lut.m_curves[1].as_deref());
-        transform.as_mut().unwrap().input_clut_table_b =
-            build_input_gamma_table(lut.m_curves[2].as_deref());
-        transform.as_mut().unwrap().transform_module_fn = Some(transform_module_gamma_table);
-        next_transform = append_transform(transform, next_transform);
+        transform.input_clut_table_r = build_input_gamma_table(lut.m_curves[0].as_deref());
+        transform.input_clut_table_g = build_input_gamma_table(lut.m_curves[1].as_deref());
+        transform.input_clut_table_b = build_input_gamma_table(lut.m_curves[2].as_deref());
+        transform.transform_module_fn = Some(transform_module_gamma_table);
+        next_transform = append_transform(Some(transform), next_transform);
 
         // Prepare Matrix
         transform = modular_transform_alloc();
-        transform.as_ref()?;
-        transform.as_mut().unwrap().matrix = build_mAB_matrix(lut);
-        if transform.as_mut().unwrap().matrix.invalid {
+        transform.matrix = build_mAB_matrix(lut);
+        if transform.matrix.invalid {
             return None;
         }
-        transform.as_mut().unwrap().tx = s15Fixed16Number_to_float(lut.e03);
-        transform.as_mut().unwrap().ty = s15Fixed16Number_to_float(lut.e13);
-        transform.as_mut().unwrap().tz = s15Fixed16Number_to_float(lut.e23);
-        transform.as_mut().unwrap().transform_module_fn = Some(transform_module_matrix_translate);
-        next_transform = append_transform(transform, next_transform);
+        transform.tx = s15Fixed16Number_to_float(lut.e03);
+        transform.ty = s15Fixed16Number_to_float(lut.e13);
+        transform.tz = s15Fixed16Number_to_float(lut.e23);
+        transform.transform_module_fn = Some(transform_module_matrix_translate);
+        next_transform = append_transform(Some(transform), next_transform);
     }
 
     if lut.b_curves[0].is_some() {
         // Prepare B curve
         transform = modular_transform_alloc();
-        transform.as_ref()?;
-        transform.as_mut().unwrap().input_clut_table_r =
-            build_input_gamma_table(lut.b_curves[0].as_deref());
-        transform.as_mut().unwrap().input_clut_table_g =
-            build_input_gamma_table(lut.b_curves[1].as_deref());
-        transform.as_mut().unwrap().input_clut_table_b =
-            build_input_gamma_table(lut.b_curves[2].as_deref());
-        transform.as_mut().unwrap().transform_module_fn = Some(transform_module_gamma_table);
-        append_transform(transform, next_transform);
+        transform.input_clut_table_r = build_input_gamma_table(lut.b_curves[0].as_deref());
+        transform.input_clut_table_g = build_input_gamma_table(lut.b_curves[1].as_deref());
+        transform.input_clut_table_b = build_input_gamma_table(lut.b_curves[2].as_deref());
+        transform.transform_module_fn = Some(transform_module_gamma_table);
+        append_transform(Some(transform), next_transform);
     } else {
         // B curve is mandatory
         return None;
     }
 
     if lut.reversed {
         // mBA are identical to mAB except that the transformation order
         // is reversed
@@ -656,62 +637,59 @@ fn modular_transform_create_lut(lut: &lu
     let mut next_transform = &mut first_transform;
 
     let _in_curve_len: usize;
     let clut_length: usize;
     let _out_curve_len: usize;
     let _in_curves: *mut f32;
     let _out_curves: *mut f32;
     let mut transform = modular_transform_alloc();
-    if transform.is_some() {
-        transform.as_mut().unwrap().matrix = build_lut_matrix(Some(lut));
-        if !transform.as_mut().unwrap().matrix.invalid {
-            transform.as_mut().unwrap().transform_module_fn = Some(transform_module_matrix);
-            next_transform = append_transform(transform, next_transform);
-            // Prepare input curves
-            transform = modular_transform_alloc();
-            if transform.is_some() {
-                transform.as_mut().unwrap().input_clut_table_r =
-                    Some(lut.input_table[0..lut.num_input_table_entries as usize].to_vec());
-                transform.as_mut().unwrap().input_clut_table_g = Some(
-                    lut.input_table[lut.num_input_table_entries as usize
-                        ..lut.num_input_table_entries as usize * 2]
-                        .to_vec(),
-                );
-                transform.as_mut().unwrap().input_clut_table_b = Some(
-                    lut.input_table[lut.num_input_table_entries as usize * 2
-                        ..lut.num_input_table_entries as usize * 3]
-                        .to_vec(),
-                );
-                transform.as_mut().unwrap().input_clut_table_length = lut.num_input_table_entries;
-                // Prepare table
-                clut_length = (lut.num_clut_grid_points as usize).pow(3) * 3;
-                assert_eq!(clut_length, lut.clut_table.len());
-                transform.as_mut().unwrap().clut = Some(lut.clut_table.clone());
 
-                transform.as_mut().unwrap().grid_size = lut.num_clut_grid_points as u16;
-                // Prepare output curves
-                transform.as_mut().unwrap().output_clut_table_r =
-                    Some(lut.output_table[0..lut.num_output_table_entries as usize].to_vec());
-                transform.as_mut().unwrap().output_clut_table_g = Some(
-                    lut.output_table[lut.num_output_table_entries as usize
-                        ..lut.num_output_table_entries as usize * 2]
-                        .to_vec(),
-                );
-                transform.as_mut().unwrap().output_clut_table_b = Some(
-                    lut.output_table[lut.num_output_table_entries as usize * 2
-                        ..lut.num_output_table_entries as usize * 3]
-                        .to_vec(),
-                );
-                transform.as_mut().unwrap().output_clut_table_length = lut.num_output_table_entries;
-                transform.as_mut().unwrap().transform_module_fn = Some(transform_module_clut);
-                append_transform(transform, next_transform);
-                return first_transform;
-            }
-        }
+    transform.matrix = build_lut_matrix(Some(lut));
+    if !transform.matrix.invalid {
+        transform.transform_module_fn = Some(transform_module_matrix);
+        next_transform = append_transform(Some(transform), next_transform);
+        // Prepare input curves
+        transform = modular_transform_alloc();
+        transform.input_clut_table_r =
+            Some(lut.input_table[0..lut.num_input_table_entries as usize].to_vec());
+        transform.input_clut_table_g = Some(
+            lut.input_table
+                [lut.num_input_table_entries as usize..lut.num_input_table_entries as usize * 2]
+                .to_vec(),
+        );
+        transform.input_clut_table_b = Some(
+            lut.input_table[lut.num_input_table_entries as usize * 2
+                ..lut.num_input_table_entries as usize * 3]
+                .to_vec(),
+        );
+        transform.input_clut_table_length = lut.num_input_table_entries;
+        // Prepare table
+        clut_length = (lut.num_clut_grid_points as usize).pow(3) * 3;
+        assert_eq!(clut_length, lut.clut_table.len());
+        transform.clut = Some(lut.clut_table.clone());
+
+        transform.grid_size = lut.num_clut_grid_points as u16;
+        // Prepare output curves
+        transform.output_clut_table_r =
+            Some(lut.output_table[0..lut.num_output_table_entries as usize].to_vec());
+        transform.output_clut_table_g = Some(
+            lut.output_table
+                [lut.num_output_table_entries as usize..lut.num_output_table_entries as usize * 2]
+                .to_vec(),
+        );
+        transform.output_clut_table_b = Some(
+            lut.output_table[lut.num_output_table_entries as usize * 2
+                ..lut.num_output_table_entries as usize * 3]
+                .to_vec(),
+        );
+        transform.output_clut_table_length = lut.num_output_table_entries;
+        transform.transform_module_fn = Some(transform_module_clut);
+        append_transform(Some(transform), next_transform);
+        return first_transform;
     }
     modular_transform_release(first_transform);
     None
 }
 
 fn modular_transform_create_input(input: &Profile) -> Option<Box<ModularTransform>> {
     let mut first_transform = None;
     let mut next_transform = &mut first_transform;
@@ -729,61 +707,47 @@ fn modular_transform_create_input(input:
         let mAB_transform = modular_transform_create_mAB(input.mAB.as_deref().unwrap());
         if mAB_transform.is_none() {
             return None;
         } else {
             append_transform(mAB_transform, next_transform);
         }
     } else {
         let mut transform = modular_transform_alloc();
-        if transform.is_none() {
+        transform.input_clut_table_r = build_input_gamma_table(input.redTRC.as_deref());
+        transform.input_clut_table_g = build_input_gamma_table(input.greenTRC.as_deref());
+        transform.input_clut_table_b = build_input_gamma_table(input.blueTRC.as_deref());
+        transform.transform_module_fn = Some(transform_module_gamma_table);
+        if transform.input_clut_table_r.is_none()
+            || transform.input_clut_table_g.is_none()
+            || transform.input_clut_table_b.is_none()
+        {
+            append_transform(Some(transform), next_transform);
             return None;
         } else {
-            transform.as_mut().unwrap().input_clut_table_r =
-                build_input_gamma_table(input.redTRC.as_deref());
-            transform.as_mut().unwrap().input_clut_table_g =
-                build_input_gamma_table(input.greenTRC.as_deref());
-            transform.as_mut().unwrap().input_clut_table_b =
-                build_input_gamma_table(input.blueTRC.as_deref());
-            transform.as_mut().unwrap().transform_module_fn = Some(transform_module_gamma_table);
-            if transform.as_mut().unwrap().input_clut_table_r.is_none()
-                || transform.as_mut().unwrap().input_clut_table_g.is_none()
-                || transform.as_mut().unwrap().input_clut_table_b.is_none()
-            {
-                append_transform(transform, next_transform);
-                return None;
-            } else {
-                next_transform = append_transform(transform, next_transform);
-                transform = modular_transform_alloc();
-                if transform.is_none() {
-                    return None;
-                } else {
-                    transform.as_mut().unwrap().matrix.m[0][0] = 1. / 1.999_969_5;
-                    transform.as_mut().unwrap().matrix.m[0][1] = 0.0;
-                    transform.as_mut().unwrap().matrix.m[0][2] = 0.0;
-                    transform.as_mut().unwrap().matrix.m[1][0] = 0.0;
-                    transform.as_mut().unwrap().matrix.m[1][1] = 1. / 1.999_969_5;
-                    transform.as_mut().unwrap().matrix.m[1][2] = 0.0;
-                    transform.as_mut().unwrap().matrix.m[2][0] = 0.0;
-                    transform.as_mut().unwrap().matrix.m[2][1] = 0.0;
-                    transform.as_mut().unwrap().matrix.m[2][2] = 1. / 1.999_969_5;
-                    transform.as_mut().unwrap().matrix.invalid = false;
-                    transform.as_mut().unwrap().transform_module_fn = Some(transform_module_matrix);
-                    next_transform = append_transform(transform, next_transform);
-                    transform = modular_transform_alloc();
-                    if transform.is_none() {
-                        return None;
-                    } else {
-                        transform.as_mut().unwrap().matrix = build_colorant_matrix(input);
-                        transform.as_mut().unwrap().transform_module_fn =
-                            Some(transform_module_matrix);
-                        append_transform(transform, next_transform);
-                    }
-                }
-            }
+            next_transform = append_transform(Some(transform), next_transform);
+            transform = modular_transform_alloc();
+
+            transform.matrix.m[0][0] = 1. / 1.999_969_5;
+            transform.matrix.m[0][1] = 0.0;
+            transform.matrix.m[0][2] = 0.0;
+            transform.matrix.m[1][0] = 0.0;
+            transform.matrix.m[1][1] = 1. / 1.999_969_5;
+            transform.matrix.m[1][2] = 0.0;
+            transform.matrix.m[2][0] = 0.0;
+            transform.matrix.m[2][1] = 0.0;
+            transform.matrix.m[2][2] = 1. / 1.999_969_5;
+            transform.matrix.invalid = false;
+            transform.transform_module_fn = Some(transform_module_matrix);
+            next_transform = append_transform(Some(transform), next_transform);
+
+            transform = modular_transform_alloc();
+            transform.matrix = build_colorant_matrix(input);
+            transform.transform_module_fn = Some(transform_module_matrix);
+            append_transform(Some(transform), next_transform);
         }
     }
     first_transform
 }
 fn modular_transform_create_output(out: &Profile) -> Option<Box<ModularTransform>> {
     let mut first_transform = None;
     let mut next_transform = &mut first_transform;
     if out.B2A0.is_some() {
@@ -800,60 +764,48 @@ fn modular_transform_create_output(out: 
         let lut_transform_0 = modular_transform_create_mAB(out.mBA.as_deref().unwrap());
         if lut_transform_0.is_none() {
             return None;
         } else {
             append_transform(lut_transform_0, next_transform);
         }
     } else if out.redTRC.is_some() && out.greenTRC.is_some() && out.blueTRC.is_some() {
         let mut transform = modular_transform_alloc();
-        if transform.is_none() {
+
+        transform.matrix = build_colorant_matrix(out).invert();
+        transform.transform_module_fn = Some(transform_module_matrix);
+        next_transform = append_transform(Some(transform), next_transform);
+        transform = modular_transform_alloc();
+
+        transform.matrix.m[0][0] = 1.999_969_5;
+        transform.matrix.m[0][1] = 0.0;
+        transform.matrix.m[0][2] = 0.0;
+        transform.matrix.m[1][0] = 0.0;
+        transform.matrix.m[1][1] = 1.999_969_5;
+        transform.matrix.m[1][2] = 0.0;
+        transform.matrix.m[2][0] = 0.0;
+        transform.matrix.m[2][1] = 0.0;
+        transform.matrix.m[2][2] = 1.999_969_5;
+        transform.matrix.invalid = false;
+        transform.transform_module_fn = Some(transform_module_matrix);
+        next_transform = append_transform(Some(transform), next_transform);
+
+        transform = modular_transform_alloc();
+
+        transform.output_gamma_lut_r = Some(build_output_lut(out.redTRC.as_deref().unwrap()));
+        transform.output_gamma_lut_g = Some(build_output_lut(out.greenTRC.as_deref().unwrap()));
+        transform.output_gamma_lut_b = Some(build_output_lut(out.blueTRC.as_deref().unwrap()));
+        transform.transform_module_fn = Some(transform_module_gamma_lut);
+        if transform.output_gamma_lut_r.is_none()
+            || transform.output_gamma_lut_g.is_none()
+            || transform.output_gamma_lut_b.is_none()
+        {
             return None;
         } else {
-            transform.as_mut().unwrap().matrix = build_colorant_matrix(out).invert();
-            transform.as_mut().unwrap().transform_module_fn = Some(transform_module_matrix);
-            next_transform = append_transform(transform, next_transform);
-            transform = modular_transform_alloc();
-            if transform.is_none() {
-                return None;
-            } else {
-                transform.as_mut().unwrap().matrix.m[0][0] = 1.999_969_5;
-                transform.as_mut().unwrap().matrix.m[0][1] = 0.0;
-                transform.as_mut().unwrap().matrix.m[0][2] = 0.0;
-                transform.as_mut().unwrap().matrix.m[1][0] = 0.0;
-                transform.as_mut().unwrap().matrix.m[1][1] = 1.999_969_5;
-                transform.as_mut().unwrap().matrix.m[1][2] = 0.0;
-                transform.as_mut().unwrap().matrix.m[2][0] = 0.0;
-                transform.as_mut().unwrap().matrix.m[2][1] = 0.0;
-                transform.as_mut().unwrap().matrix.m[2][2] = 1.999_969_5;
-                transform.as_mut().unwrap().matrix.invalid = false;
-                transform.as_mut().unwrap().transform_module_fn = Some(transform_module_matrix);
-                next_transform = append_transform(transform, next_transform);
-                transform = modular_transform_alloc();
-                if transform.is_none() {
-                    return None;
-                } else {
-                    transform.as_mut().unwrap().output_gamma_lut_r =
-                        Some(build_output_lut(out.redTRC.as_deref().unwrap()));
-                    transform.as_mut().unwrap().output_gamma_lut_g =
-                        Some(build_output_lut(out.greenTRC.as_deref().unwrap()));
-                    transform.as_mut().unwrap().output_gamma_lut_b =
-                        Some(build_output_lut(out.blueTRC.as_deref().unwrap()));
-                    transform.as_mut().unwrap().transform_module_fn =
-                        Some(transform_module_gamma_lut);
-                    if transform.as_mut().unwrap().output_gamma_lut_r.is_none()
-                        || transform.as_mut().unwrap().output_gamma_lut_g.is_none()
-                        || transform.as_mut().unwrap().output_gamma_lut_b.is_none()
-                    {
-                        return None;
-                    } else {
-                        append_transform(transform, next_transform);
-                    }
-                }
-            }
+            append_transform(Some(transform), next_transform);
         }
     } else {
         debug_assert!(false, "Unsupported output profile workflow.");
         return None;
     }
     first_transform
 }
 /* Not Completed
@@ -902,54 +854,49 @@ remove_next:
         curr_trans->next_transform = next_trans->next_transform;
         next_trans->next_transform = NULL;
         qcms_modular_transform_release(next_trans);
         continue;
     }
     return transform;
 }
 */
-fn modular_transform_create(
-    input: &Profile,
-    output: &Profile,
-) -> Option<Box<ModularTransform>> {
+fn modular_transform_create(input: &Profile, output: &Profile) -> Option<Box<ModularTransform>> {
     let mut first_transform = None;
     let mut next_transform = &mut first_transform;
     if input.color_space == RGB_SIGNATURE {
         let rgb_to_pcs = modular_transform_create_input(input);
         rgb_to_pcs.as_ref()?;
         next_transform = append_transform(rgb_to_pcs, next_transform);
     } else {
         debug_assert!(false, "input color space not supported");
         return None;
     }
 
     if input.pcs == LAB_SIGNATURE && output.pcs == XYZ_SIGNATURE {
         let mut lab_to_pcs = modular_transform_alloc();
-        lab_to_pcs.as_ref()?;
-        lab_to_pcs.as_mut().unwrap().transform_module_fn = Some(transform_module_LAB_to_XYZ);
-        next_transform = append_transform(lab_to_pcs, next_transform);
+        lab_to_pcs.transform_module_fn = Some(transform_module_LAB_to_XYZ);
+        next_transform = append_transform(Some(lab_to_pcs), next_transform);
     }
 
     // This does not improve accuracy in practice, something is wrong here.
     //if (in->chromaticAdaption.invalid == false) {
     //	struct qcms_modular_transform* chromaticAdaption;
     //	chromaticAdaption = qcms_modular_transform_alloc();
     //	if (!chromaticAdaption)
     //		goto fail;
     //	append_transform(chromaticAdaption, &next_transform);
     //	chromaticAdaption->matrix = matrix_invert(in->chromaticAdaption);
     //	chromaticAdaption->transform_module_fn = qcms_transform_module_matrix;
     //}
 
     if input.pcs == XYZ_SIGNATURE && output.pcs == LAB_SIGNATURE {
         let mut pcs_to_lab = modular_transform_alloc();
-        pcs_to_lab.as_ref()?;
-        pcs_to_lab.as_mut().unwrap().transform_module_fn = Some(transform_module_XYZ_to_LAB);
-        next_transform = append_transform(pcs_to_lab, next_transform);
+        pcs_to_lab.transform_module_fn = Some(transform_module_XYZ_to_LAB);
+        next_transform = append_transform(Some(pcs_to_lab), next_transform);
     }
 
     if output.color_space == RGB_SIGNATURE {
         let pcs_to_rgb = modular_transform_create_output(output);
         pcs_to_rgb.as_ref()?;
         append_transform(pcs_to_rgb, next_transform);
     } else {
         debug_assert!(false, "output color space not supported");
--- a/gfx/wr/webrender/res/brush_blend.glsl
+++ b/gfx/wr/webrender/res/brush_blend.glsl
@@ -58,17 +58,17 @@ void brush_vs(
     RectWithSize segment_rect,
     ivec4 prim_user_data,
     int specific_resource_address,
     mat4 transform,
     PictureTask pic_task,
     int brush_flags,
     vec4 unused
 ) {
-    ImageResource res = fetch_image_resource(prim_user_data.x);
+    ImageSource res = fetch_image_source(prim_user_data.x);
     vec2 uv0 = res.uv_rect.p0;
     vec2 uv1 = res.uv_rect.p1;
 
     vec2 inv_texture_size = vec2(1.0) / vec2(TEX_SIZE(sColor0).xy);
     vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
     f = get_image_quad_uv(prim_user_data.x, f);
     vec2 uv = mix(uv0, uv1, f);
     float perspective_interpolate = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0;
--- a/gfx/wr/webrender/res/brush_image.glsl
+++ b/gfx/wr/webrender/res/brush_image.glsl
@@ -17,17 +17,17 @@ flat varying vec2 v_tile_repeat;
 
 // Normalized bounds of the source image in the texture.
 flat varying vec4 v_uv_bounds;
 // Normalized bounds of the source image in the texture, adjusted to avoid
 // sampling artifacts.
 flat varying vec4 v_uv_sample_bounds;
 // x: Layer index to sample.
 // y: Flag to allow perspective interpolation of UV.
-flat varying vec2 v_layer_and_perspective;
+flat varying float v_perspective;
 
 #ifdef WR_VERTEX_SHADER
 
 // Must match the AlphaType enum.
 #define BLEND_MODE_ALPHA            0
 #define BLEND_MODE_PREMUL_ALPHA     1
 
 struct ImageBrushData {
@@ -63,17 +63,17 @@ void brush_vs(
     // If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use
     // non-normalized texture coordinates.
 #ifdef WR_FEATURE_TEXTURE_RECT
     vec2 texture_size = vec2(1, 1);
 #else
     vec2 texture_size = vec2(TEX_SIZE(sColor0));
 #endif
 
-    ImageResource res = fetch_image_resource(specific_resource_address);
+    ImageSource res = fetch_image_source(specific_resource_address);
     vec2 uv0 = res.uv_rect.p0;
     vec2 uv1 = res.uv_rect.p1;
 
     RectWithSize local_rect = prim_rect;
     vec2 stretch_size = image_data.stretch_size;
     if (stretch_size.x < 0.0) {
         stretch_size = local_rect.size;
     }
@@ -158,17 +158,17 @@ void brush_vs(
             if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y_ROUND) != 0) {
                 float ny = max(1.0, round(segment_rect.size.y / stretch_size.y));
                 stretch_size.y = segment_rect.size.y / ny;
             }
         #endif
     }
 
     float perspective_interpolate = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0;
-    v_layer_and_perspective = vec2(res.layer, perspective_interpolate);
+    v_perspective = perspective_interpolate;
 
     // Handle case where the UV coords are inverted (e.g. from an
     // external image).
     vec2 min_uv = min(uv0, uv1);
     vec2 max_uv = max(uv0, uv1);
 
     v_uv_sample_bounds = vec4(
         min_uv + vec2(0.5),
@@ -306,23 +306,23 @@ vec2 compute_repeated_uvs(float perspect
 
     return repeated_uv;
 #else
     return v_uv * perspective_divisor + v_uv_bounds.xy;
 #endif
 }
 
 Fragment brush_fs() {
-    float perspective_divisor = mix(gl_FragCoord.w, 1.0, v_layer_and_perspective.y);
+    float perspective_divisor = mix(gl_FragCoord.w, 1.0, v_perspective);
     vec2 repeated_uv = compute_repeated_uvs(perspective_divisor);
 
     // Clamp the uvs to avoid sampling artifacts.
     vec2 uv = clamp(repeated_uv, v_uv_sample_bounds.xy, v_uv_sample_bounds.zw);
 
-    vec4 texel = TEX_SAMPLE(sColor0, vec3(uv, v_layer_and_perspective.x));
+    vec4 texel = TEX_SAMPLE(sColor0, uv);
 
     Fragment frag;
 
 #ifdef WR_FEATURE_ALPHA_PASS
     #ifdef WR_FEATURE_ANTIALIASING
         float alpha = antialias_brush();
     #else
         float alpha = 1.0;
@@ -351,38 +351,38 @@ void swgl_drawSpanRGBA8() {
     }
 
     #ifdef WR_FEATURE_ALPHA_PASS
         if (v_mask_swizzle != vec2(1.0, 0.0)) {
             return;
         }
     #endif
 
-    float perspective_divisor = mix(swgl_forceScalar(gl_FragCoord.w), 1.0, v_layer_and_perspective.y);
+    float perspective_divisor = mix(swgl_forceScalar(gl_FragCoord.w), 1.0, v_perspective);
 
     #ifdef WR_FEATURE_REPETITION
         // Get the UVs before any repetition, scaling, or offsetting has occurred...
         vec2 uv = v_uv * perspective_divisor;
     #else
         vec2 uv = compute_repeated_uvs(perspective_divisor);
     #endif
 
     #ifdef WR_FEATURE_ALPHA_PASS
     if (v_color != vec4(1.0)) {
         #ifdef WR_FEATURE_REPETITION
-            swgl_commitTextureRepeatColorRGBA8(sColor0, uv, v_uv_bounds, v_uv_sample_bounds, v_color, v_layer_and_perspective.x);
+            swgl_commitTextureRepeatColorRGBA8(sColor0, uv, v_uv_bounds, v_uv_sample_bounds, v_color, 0.0);
         #else
-            swgl_commitTextureColorRGBA8(sColor0, uv, v_uv_sample_bounds, v_color, v_layer_and_perspective.x);
+            swgl_commitTextureColorRGBA8(sColor0, uv, v_uv_sample_bounds, v_color, 0.0);
         #endif
         return;
     }
     // No color scaling required, so just fall through to a normal textured span...
     #endif
 
     #ifdef WR_FEATURE_REPETITION
-        swgl_commitTextureRepeatRGBA8(sColor0, uv, v_uv_bounds, v_uv_sample_bounds, v_layer_and_perspective.x);
+        swgl_commitTextureRepeatRGBA8(sColor0, uv, v_uv_bounds, v_uv_sample_bounds, 0.0);
     #else
-        swgl_commitTextureRGBA8(sColor0, uv, v_uv_sample_bounds, v_layer_and_perspective.x);
+        swgl_commitTextureRGBA8(sColor0, uv, v_uv_sample_bounds, 0.0);
     #endif
 }
 #endif
 
 #endif
--- a/gfx/wr/webrender/res/brush_mix_blend.glsl
+++ b/gfx/wr/webrender/res/brush_mix_blend.glsl
@@ -24,17 +24,17 @@ flat varying int v_op;
 void get_uv(
     int res_address,
     vec2 f,
     ivec2 texture_size,
     float perspective_f,
     out vec2 out_uv,
     out vec4 out_uv_sample_bounds
 ) {
-    ImageResource res = fetch_image_resource(res_address);
+    ImageSource res = fetch_image_source(res_address);
     vec2 uv0 = res.uv_rect.p0;
     vec2 uv1 = res.uv_rect.p1;
 
     vec2 inv_texture_size = vec2(1.0) / vec2(texture_size);
     f = get_image_quad_uv(res_address, f);
     vec2 uv = mix(uv0, uv1, f);
 
     out_uv = uv * inv_texture_size * perspective_f;
--- a/gfx/wr/webrender/res/brush_opacity.glsl
+++ b/gfx/wr/webrender/res/brush_opacity.glsl
@@ -9,17 +9,16 @@
 
 // Interpolated UV coordinates to sample.
 varying vec2 v_uv;
 
 // Normalized bounds of the source image in the texture, adjusted to avoid
 // sampling artifacts.
 flat varying vec4 v_uv_sample_bounds;
 
-// Layer index to sample.
 // Flag to allow perspective interpolation of UV.
 flat varying float v_perspective;
 
 flat varying float v_opacity;
 
 #ifdef WR_VERTEX_SHADER
 void brush_vs(
     VertexInfo vi,
@@ -28,17 +27,17 @@ void brush_vs(
     RectWithSize segment_rect,
     ivec4 prim_user_data,
     int specific_resource_address,
     mat4 transform,
     PictureTask pic_task,
     int brush_flags,
     vec4 unused
 ) {
-    ImageResource res = fetch_image_resource(prim_user_data.x);
+    ImageSource res = fetch_image_source(prim_user_data.x);
     vec2 uv0 = res.uv_rect.p0;
     vec2 uv1 = res.uv_rect.p1;
 
     vec2 texture_size = vec2(TEX_SIZE(sColor0).xy);
     vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
     f = get_image_quad_uv(prim_user_data.x, f);
     vec2 uv = mix(uv0, uv1, f);
     float perspective_interpolate = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0;
--- a/gfx/wr/webrender/res/brush_yuv_image.glsl
+++ b/gfx/wr/webrender/res/brush_yuv_image.glsl
@@ -1,18 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #define VECS_PER_SPECIFIC_BRUSH 1
 
 #include shared,prim_shared,brush,yuv
 
-flat varying vec3 vYuvLayers;
-
 varying vec2 vUv_Y;
 flat varying vec4 vUvBounds_Y;
 
 varying vec2 vUv_U;
 flat varying vec4 vUvBounds_U;
 
 varying vec2 vUv_V;
 flat varying vec4 vUvBounds_V;
@@ -65,46 +63,42 @@ void brush_vs(
     // swgl_commitTextureLinearYUV needs to know the color space specifier and
     // also needs to know how many bits of scaling are required to normalize
     // HDR textures.
     vYuvColorSpace = prim.color_space;
     vRescaleFactor = int(log2(prim.coefficient));
 #endif
 
     if (vFormat == YUV_FORMAT_PLANAR) {
-        ImageResource res_y = fetch_image_resource(prim_user_data.x);
-        ImageResource res_u = fetch_image_resource(prim_user_data.y);
-        ImageResource res_v = fetch_image_resource(prim_user_data.z);
+        ImageSource res_y = fetch_image_source(prim_user_data.x);
+        ImageSource res_u = fetch_image_source(prim_user_data.y);
+        ImageSource res_v = fetch_image_source(prim_user_data.z);
         write_uv_rect(res_y.uv_rect.p0, res_y.uv_rect.p1, f, TEX_SIZE_YUV(sColor0), vUv_Y, vUvBounds_Y);
         write_uv_rect(res_u.uv_rect.p0, res_u.uv_rect.p1, f, TEX_SIZE_YUV(sColor1), vUv_U, vUvBounds_U);
         write_uv_rect(res_v.uv_rect.p0, res_v.uv_rect.p1, f, TEX_SIZE_YUV(sColor2), vUv_V, vUvBounds_V);
-        vYuvLayers = vec3(res_y.layer, res_u.layer, res_v.layer);
     } else if (vFormat == YUV_FORMAT_NV12) {
-        ImageResource res_y = fetch_image_resource(prim_user_data.x);
-        ImageResource res_u = fetch_image_resource(prim_user_data.y);
+        ImageSource res_y = fetch_image_source(prim_user_data.x);
+        ImageSource res_u = fetch_image_source(prim_user_data.y);
         write_uv_rect(res_y.uv_rect.p0, res_y.uv_rect.p1, f, TEX_SIZE_YUV(sColor0), vUv_Y, vUvBounds_Y);
         write_uv_rect(res_u.uv_rect.p0, res_u.uv_rect.p1, f, TEX_SIZE_YUV(sColor1), vUv_U, vUvBounds_U);
-        vYuvLayers = vec3(res_y.layer, res_u.layer, 0.0);
     } else if (vFormat == YUV_FORMAT_INTERLEAVED) {
-        ImageResource res_y = fetch_image_resource(prim_user_data.x);
+        ImageSource res_y = fetch_image_source(prim_user_data.x);
         write_uv_rect(res_y.uv_rect.p0, res_y.uv_rect.p1, f, TEX_SIZE_YUV(sColor0), vUv_Y, vUvBounds_Y);
-        vYuvLayers = vec3(res_y.layer, 0.0, 0.0);
     }
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 
 Fragment brush_fs() {
     vec4 color = sample_yuv(
         vFormat,
         vYuvColorMatrix,
         vYuvOffsetVector,
         vCoefficient,
-        vYuvLayers,
         vUv_Y,
         vUv_U,
         vUv_V,
         vUvBounds_Y,
         vUvBounds_U,
         vUvBounds_V
     );
 
@@ -113,24 +107,24 @@ Fragment brush_fs() {
 #endif
 
     return Fragment(color);
 }
 
 #ifdef SWGL_DRAW_SPAN
 void swgl_drawSpanRGBA8() {
     if (vFormat == YUV_FORMAT_PLANAR) {
-        swgl_commitTextureLinearYUV(sColor0, vUv_Y, vUvBounds_Y, vYuvLayers.x,
-                                    sColor1, vUv_U, vUvBounds_U, vYuvLayers.y,
-                                    sColor2, vUv_V, vUvBounds_V, vYuvLayers.z,
+        swgl_commitTextureLinearYUV(sColor0, vUv_Y, vUvBounds_Y, 0.0,
+                                    sColor1, vUv_U, vUvBounds_U, 0.0,
+                                    sColor2, vUv_V, vUvBounds_V, 0.0,
                                     vYuvColorSpace, vRescaleFactor);
     } else if (vFormat == YUV_FORMAT_NV12) {
-        swgl_commitTextureLinearYUV(sColor0, vUv_Y, vUvBounds_Y, vYuvLayers.x,
-                                    sColor1, vUv_U, vUvBounds_U, vYuvLayers.y,
+        swgl_commitTextureLinearYUV(sColor0, vUv_Y, vUvBounds_Y, 0.0,
+                                    sColor1, vUv_U, vUvBounds_U, 0.0,
                                     vYuvColorSpace, vRescaleFactor);
     } else if (vFormat == YUV_FORMAT_INTERLEAVED) {
-        swgl_commitTextureLinearYUV(sColor0, vUv_Y, vUvBounds_Y, vYuvLayers.x,
+        swgl_commitTextureLinearYUV(sColor0, vUv_Y, vUvBounds_Y, 0.0,
                                     vYuvColorSpace, vRescaleFactor);
     }
 }
 #endif
 
 #endif
--- a/gfx/wr/webrender/res/composite.glsl
+++ b/gfx/wr/webrender/res/composite.glsl
@@ -6,43 +6,40 @@
 
 #include shared,yuv
 
 #ifdef WR_FEATURE_YUV
 flat varying mat3 vYuvColorMatrix;
 flat varying vec3 vYuvOffsetVector;
 flat varying float vYuvCoefficient;
 flat varying int vYuvFormat;
-flat varying vec3 vYuvLayers;
 #ifdef SWGL_DRAW_SPAN
 flat varying int vYuvColorSpace;
 flat varying int vRescaleFactor;
 #endif
 varying vec2 vUV_y;
 varying vec2 vUV_u;
 varying vec2 vUV_v;
 flat varying vec4 vUVBounds_y;
 flat varying vec4 vUVBounds_u;
 flat varying vec4 vUVBounds_v;
 #else
 flat varying vec4 vColor;
-flat varying float vLayer;
 varying vec2 vUv;
 flat varying vec4 vUVBounds;
 #endif
 
 #ifdef WR_VERTEX_SHADER
 // CPU side data is in CompositeInstance (gpu_types.rs) and is
 // converted to GPU data using desc::COMPOSITE (renderer.rs) by
 // filling vaos.composite_vao with VertexArrayKind::Composite.
 PER_INSTANCE in vec4 aDeviceRect;
 PER_INSTANCE in vec4 aDeviceClipRect;
 PER_INSTANCE in vec4 aColor;
 PER_INSTANCE in vec4 aParams;
-PER_INSTANCE in vec3 aTextureLayers;
 
 #ifdef WR_FEATURE_YUV
 // YUV treats these as a UV clip rect (clamp)
 PER_INSTANCE in vec4 aUvRect0;
 PER_INSTANCE in vec4 aUvRect1;
 PER_INSTANCE in vec4 aUvRect2;
 #else
 PER_INSTANCE in vec4 aUvRect0;
@@ -62,17 +59,16 @@ void main(void) {
     int yuv_color_space = int(aParams.y);
     int yuv_format = int(aParams.z);
     float yuv_coefficient = aParams.w;
 
     vYuvColorMatrix = get_yuv_color_matrix(yuv_color_space);
     vYuvOffsetVector = get_yuv_offset_vector(yuv_color_space);
     vYuvCoefficient = yuv_coefficient;
     vYuvFormat = yuv_format;
-    vYuvLayers = aTextureLayers.xyz;
 
 #ifdef SWGL_DRAW_SPAN
     // swgl_commitTextureLinearYUV needs to know the color space specifier and
     // also needs to know how many bits of scaling are required to normalize
     // HDR textures.
     vYuvColorSpace = yuv_color_space;
     vRescaleFactor = int(log2(yuv_coefficient));
 #endif
@@ -114,73 +110,71 @@ void main(void) {
         // normalized and clamped.
         vec2 texture_size = TEX_SIZE_YUV(sColor0);
         vUVBounds += vec4(0.5, 0.5, -0.5, -0.5);
     #ifndef WR_FEATURE_TEXTURE_RECT
         vUv /= texture_size;
         vUVBounds /= texture_size.xyxy;
     #endif
     }
-    // Pass through color and texture array layer
+    // Pass through color
     vColor = aColor;
-    vLayer = aTextureLayers.x;
 #endif
 
     gl_Position = uTransform * vec4(clipped_world_pos, aParams.x /* z_id */, 1.0);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
 #ifdef WR_FEATURE_YUV
     vec4 color = sample_yuv(
         vYuvFormat,
         vYuvColorMatrix,
         vYuvOffsetVector,
         vYuvCoefficient,
-        vYuvLayers,
         vUV_y,
         vUV_u,
         vUV_v,
         vUVBounds_y,
         vUVBounds_u,
         vUVBounds_v
     );
 #else
     // The color is just the texture sample modulated by a supplied color
     vec2 uv = clamp(vUv.xy, vUVBounds.xy, vUVBounds.zw);
 #   if defined(WR_FEATURE_TEXTURE_EXTERNAL) || defined(WR_FEATURE_TEXTURE_2D) || defined(WR_FEATURE_TEXTURE_RECT)
-    vec4 texel = TEX_SAMPLE(sColor0, vec3(uv, vLayer));
+    vec4 texel = TEX_SAMPLE(sColor0, uv);
 #   else
-    vec4 texel = textureLod(sColor0, vec3(uv, vLayer), 0.0);
+    vec4 texel = textureLod(sColor0, vec3(uv, 0.0), 0.0);
 #   endif
     vec4 color = vColor * texel;
 #endif
     write_output(color);
 }
 
 #ifdef SWGL_DRAW_SPAN
 void swgl_drawSpanRGBA8() {
 #ifdef WR_FEATURE_YUV
     if (vYuvFormat == YUV_FORMAT_PLANAR) {
-        swgl_commitTextureLinearYUV(sColor0, vUV_y, vUVBounds_y, vYuvLayers.x,
-                                    sColor1, vUV_u, vUVBounds_u, vYuvLayers.y,
-                                    sColor2, vUV_v, vUVBounds_v, vYuvLayers.z,
+        swgl_commitTextureLinearYUV(sColor0, vUV_y, vUVBounds_y, 0.0,
+                                    sColor1, vUV_u, vUVBounds_u, 0.0,
+                                    sColor2, vUV_v, vUVBounds_v, 0.0,
                                     vYuvColorSpace, vRescaleFactor);
     } else if (vYuvFormat == YUV_FORMAT_NV12) {
-        swgl_commitTextureLinearYUV(sColor0, vUV_y, vUVBounds_y, vYuvLayers.x,
-                                    sColor1, vUV_u, vUVBounds_u, vYuvLayers.y,
+        swgl_commitTextureLinearYUV(sColor0, vUV_y, vUVBounds_y, 0.0,
+                                    sColor1, vUV_u, vUVBounds_u, 0.0,
                                     vYuvColorSpace, vRescaleFactor);
     } else if (vYuvFormat == YUV_FORMAT_INTERLEAVED) {
-        swgl_commitTextureLinearYUV(sColor0, vUV_y, vUVBounds_y, vYuvLayers.x,
+        swgl_commitTextureLinearYUV(sColor0, vUV_y, vUVBounds_y, 0.0,
                                     vYuvColorSpace, vRescaleFactor);
     }
 #else
     if (vColor != vec4(1.0)) {
-        swgl_commitTextureColorRGBA8(sColor0, vUv, vUVBounds, vColor, vLayer);
+        swgl_commitTextureColorRGBA8(sColor0, vUv, vUVBounds, vColor, 0.0);
     } else {
-        swgl_commitTextureRGBA8(sColor0, vUv, vUVBounds, vLayer);
+        swgl_commitTextureRGBA8(sColor0, vUv, vUVBounds, 0.0);
     }
 #endif
 }
 #endif
 
 #endif
--- a/gfx/wr/webrender/res/cs_blur.glsl
+++ b/gfx/wr/webrender/res/cs_blur.glsl
@@ -20,26 +20,26 @@ flat varying vec2 vGaussCoefficients;
 #define DIR_HORIZONTAL  0
 #define DIR_VERTICAL    1
 
 PER_INSTANCE in int aBlurRenderTaskAddress;
 PER_INSTANCE in int aBlurSourceTaskAddress;
 PER_INSTANCE in int aBlurDirection;
 
 struct BlurTask {
-    RenderTaskCommonData common_data;
+    RectWithSize task_rect;
     float blur_radius;
     vec2 blur_region;
 };
 
 BlurTask fetch_blur_task(int address) {
     RenderTaskData task_data = fetch_render_task_data(address);
 
     BlurTask task = BlurTask(
-        task_data.common_data,
+        task_data.task_rect,
         task_data.user_data.x,
         task_data.user_data.yz
     );
 
     return task;
 }
 
 void calculate_gauss_coefficients(float sigma) {
@@ -63,20 +63,19 @@ void calculate_gauss_coefficients(float 
 
     // Scale initial coefficient by total to avoid passing the total separately
     // to the fragment shader.
     vGaussCoefficients.x /= gauss_coefficient_total;
 }
 
 void main(void) {
     BlurTask blur_task = fetch_blur_task(aBlurRenderTaskAddress);
-    RenderTaskCommonData src_task = fetch_render_task_common_data(aBlurSourceTaskAddress);
+    RectWithSize src_rect = fetch_render_task_rect(aBlurSourceTaskAddress);
 
-    RectWithSize src_rect = src_task.task_rect;
-    RectWithSize target_rect = blur_task.common_data.task_rect;
+    RectWithSize target_rect = blur_task.task_rect;
 
     vec2 texture_size = vec2(TEX_SIZE(sColor0).xy);
 
     // Ensure that the support is an even number of pixels to simplify the
     // fragment shader logic.
     //
     // TODO(pcwalton): Actually make use of this fact and use the texture
     // hardware for linear filtering.
--- a/gfx/wr/webrender/res/cs_clip_box_shadow.glsl
+++ b/gfx/wr/webrender/res/cs_clip_box_shadow.glsl
@@ -2,17 +2,16 @@
  * 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 shared,clip_shared
 
 varying vec4 vLocalPos;
 varying vec2 vUv;
 flat varying vec4 vUvBounds;
-flat varying float vLayer;
 flat varying vec4 vEdge;
 flat varying vec4 vUvBounds_NoClamp;
 flat varying float vClipMode;
 
 #define MODE_STRETCH        0
 #define MODE_SIMPLE         1
 
 #ifdef WR_VERTEX_SHADER
@@ -56,30 +55,29 @@ BoxShadowData fetch_data() {
     return bs_data;
 }
 
 void main(void) {
     ClipMaskInstanceBoxShadow cmi = fetch_clip_item();
     Transform clip_transform = fetch_transform(cmi.base.clip_transform_id);
     Transform prim_transform = fetch_transform(cmi.base.prim_transform_id);
     BoxShadowData bs_data = fetch_data();
-    ImageResource res = fetch_image_resource_direct(cmi.resource_address);
+    ImageSource res = fetch_image_source_direct(cmi.resource_address);
 
     RectWithSize dest_rect = bs_data.dest_rect;
 
     ClipVertexInfo vi = write_clip_tile_vertex(
         dest_rect,
         prim_transform,
         clip_transform,
         cmi.base.sub_rect,
         cmi.base.task_origin,
         cmi.base.screen_origin,
         cmi.base.device_pixel_scale
     );
-    vLayer = res.layer;
     vClipMode = float(bs_data.clip_mode);
 
     vec2 texture_size = vec2(TEX_SIZE(sColor0));
     vec2 local_pos = vi.local_pos.xy / vi.local_pos.w;
     vLocalPos = vi.local_pos;
 
     switch (bs_data.stretch_mode_x) {
         case MODE_STRETCH: {
@@ -124,16 +122,16 @@ void main(void) {
     vec2 uv_linear = vUv / vLocalPos.w;
     vec2 uv = clamp(uv_linear, vec2(0.0), vEdge.xy);
     uv += max(vec2(0.0), uv_linear - vEdge.zw);
     uv = mix(vUvBounds_NoClamp.xy, vUvBounds_NoClamp.zw, uv);
     uv = clamp(uv, vUvBounds.xy, vUvBounds.zw);
 
     float in_shadow_rect = init_transform_rough_fs(vLocalPos.xy / vLocalPos.w);
 
-    float texel = TEX_SAMPLE(sColor0, vec3(uv, vLayer)).r;
+    float texel = TEX_SAMPLE(sColor0, uv).r;
 
     float alpha = mix(texel, 1.0 - texel, vClipMode);
     float result = vLocalPos.w > 0.0 ? mix(vClipMode, alpha, in_shadow_rect) : 0.0;
 
     oFragColor = vec4(result);
 }
 #endif
--- a/gfx/wr/webrender/res/cs_clip_image.glsl
+++ b/gfx/wr/webrender/res/cs_clip_image.glsl
@@ -34,17 +34,17 @@ ClipMaskInstanceImage fetch_clip_item() 
 
     return cmi;
 }
 
 void main(void) {
     ClipMaskInstanceImage cmi = fetch_clip_item();
     Transform clip_transform = fetch_transform(cmi.base.clip_transform_id);
     Transform prim_transform = fetch_transform(cmi.base.prim_transform_id);
-    ImageResource res = fetch_image_resource_direct(cmi.resource_address);
+    ImageSource res = fetch_image_source_direct(cmi.resource_address);
 
     ClipVertexInfo vi = write_clip_tile_vertex(
         cmi.local_rect,
         prim_transform,
         clip_transform,
         cmi.base.sub_rect,
         cmi.base.task_origin,
         cmi.base.screen_origin,
--- a/gfx/wr/webrender/res/cs_scale.glsl
+++ b/gfx/wr/webrender/res/cs_scale.glsl
@@ -1,54 +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/. */
 
 #include shared,prim_shared
 
 varying vec2 vUv;
-flat varying float vUvLayer;
 flat varying vec4 vUvRect;
 
 #ifdef WR_VERTEX_SHADER
 
 PER_INSTANCE in vec4 aScaleTargetRect;
 PER_INSTANCE in ivec4 aScaleSourceRect;
-PER_INSTANCE in int aScaleSourceLayer;
 
 void main(void) {
     RectWithSize src_rect = RectWithSize(vec2(aScaleSourceRect.xy), vec2(aScaleSourceRect.zw));
 
     // If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use
     // non-normalized texture coordinates.
 #ifdef WR_FEATURE_TEXTURE_RECT
     vec2 texture_size = vec2(1, 1);
 #else
     vec2 texture_size = vec2(TEX_SIZE(sColor0));
 #endif
 
-    vUvLayer = float(aScaleSourceLayer);
-
     vUvRect = vec4(src_rect.p0 + vec2(0.5),
                    src_rect.p0 + src_rect.size - vec2(0.5)) / texture_size.xyxy;
 
     vec2 pos = aScaleTargetRect.xy + aScaleTargetRect.zw * aPosition.xy;
     vUv = (src_rect.p0 + src_rect.size * aPosition.xy) / texture_size;
 
     gl_Position = uTransform * vec4(pos, 0.0, 1.0);
 }
 
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 
 void main(void) {
     vec2 st = clamp(vUv, vUvRect.xy, vUvRect.zw);
-    oFragColor = TEX_SAMPLE(sColor0, vec3(st, vUvLayer));
+    oFragColor = TEX_SAMPLE(sColor0, st);
 }
 
 #ifdef SWGL_DRAW_SPAN
 void swgl_drawSpanRGBA8() {
-    swgl_commitTextureLinearRGBA8(sColor0, vUv, vUvRect, vUvLayer);
+    swgl_commitTextureLinearRGBA8(sColor0, vUv, vUvRect, 0.0);
 }
 #endif
 
 #endif
--- a/gfx/wr/webrender/res/cs_svg_filter.glsl
+++ b/gfx/wr/webrender/res/cs_svg_filter.glsl
@@ -45,66 +45,62 @@ PER_INSTANCE in int aFilterRenderTaskAdd
 PER_INSTANCE in int aFilterInput1TaskAddress;
 PER_INSTANCE in int aFilterInput2TaskAddress;
 PER_INSTANCE in int aFilterKind;
 PER_INSTANCE in int aFilterInputCount;
 PER_INSTANCE in int aFilterGenericInt;
 PER_INSTANCE in ivec2 aFilterExtraDataAddress;
 
 struct FilterTask {
-    RenderTaskCommonData common_data;
+    RectWithSize task_rect;
     vec3 user_data;
 };
 
 FilterTask fetch_filter_task(int address) {
     RenderTaskData task_data = fetch_render_task_data(address);
 
     FilterTask task = FilterTask(
-        task_data.common_data,
+        task_data.task_rect,
         task_data.user_data.xyz
     );
 
     return task;
 }
 
-vec4 compute_uv_rect(RenderTaskCommonData task, vec2 texture_size) {
-    RectWithSize task_rect = task.task_rect;
-
+vec4 compute_uv_rect(RectWithSize task_rect, vec2 texture_size) {
     vec4 uvRect = vec4(task_rect.p0 + vec2(0.5),
                        task_rect.p0 + task_rect.size - vec2(0.5));
     uvRect /= texture_size.xyxy;
     return uvRect;
 }
 
-vec2 compute_uv(RenderTaskCommonData task, vec2 texture_size) {
-    RectWithSize task_rect = task.task_rect;
-
+vec2 compute_uv(RectWithSize task_rect, vec2 texture_size) {
     vec2 uv0 = task_rect.p0 / texture_size;
     vec2 uv1 = floor(task_rect.p0 + task_rect.size) / texture_size;
     return mix(uv0, uv1, aPosition.xy);
 }
 
 void main(void) {
     FilterTask filter_task = fetch_filter_task(aFilterRenderTaskAddress);
-    RectWithSize target_rect = filter_task.common_data.task_rect;
+    RectWithSize target_rect = filter_task.task_rect;
 
     vec2 pos = target_rect.p0 + target_rect.size * aPosition.xy;
 
-    RenderTaskCommonData input_1_task;
+    RectWithSize input_1_task;
     if (aFilterInputCount > 0) {
         vec2 texture_size = vec2(TEX_SIZE(sColor0).xy);
-        input_1_task = fetch_render_task_common_data(aFilterInput1TaskAddress);
+        input_1_task = fetch_render_task_rect(aFilterInput1TaskAddress);
         vInput1UvRect = compute_uv_rect(input_1_task, texture_size);
         vInput1Uv = compute_uv(input_1_task, texture_size);
     }
 
-    RenderTaskCommonData input_2_task;
+    RectWithSize input_2_task;
     if (aFilterInputCount > 1) {
         vec2 texture_size = vec2(TEX_SIZE(sColor1).xy);
-        input_2_task = fetch_render_task_common_data(aFilterInput2TaskAddress);
+        input_2_task = fetch_render_task_rect(aFilterInput2TaskAddress);
         vInput2UvRect = compute_uv_rect(input_2_task, texture_size);
         vInput2Uv = compute_uv(input_2_task, texture_size);
     }
 
     vFilterInputCount = aFilterInputCount;
     vFilterKind = aFilterKind;
 
     // This assignment is only used for component transfer filters but this
@@ -136,17 +132,17 @@ void main(void) {
             break;
         case FILTER_DROP_SHADOW:
             vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress);
             break;
         case FILTER_OFFSET:
             vec2 texture_size = vec2(TEX_SIZE(sColor0).xy);
             vFilterData0 = vec4(-filter_task.user_data.xy / texture_size, vec2(0.0));
 
-            RectWithSize task_rect = input_1_task.task_rect;
+            RectWithSize task_rect = input_1_task;
             vec4 clipRect = vec4(task_rect.p0, task_rect.p0 + task_rect.size);
             clipRect /= texture_size.xyxy;
             vFilterData1 = clipRect;
             break;
         case FILTER_COMPONENT_TRANSFER:
             vData = ivec4(aFilterExtraDataAddress, 0, 0);
             break;
         case FILTER_COMPOSITE:
--- a/gfx/wr/webrender/res/gpu_cache.glsl
+++ b/gfx/wr/webrender/res/gpu_cache.glsl
@@ -91,48 +91,47 @@ vec4[4] fetch_from_gpu_cache_4(int addre
         TEXEL_FETCH(sGpuCache, uv, 0, ivec2(1, 0)),
         TEXEL_FETCH(sGpuCache, uv, 0, ivec2(2, 0)),
         TEXEL_FETCH(sGpuCache, uv, 0, ivec2(3, 0))
     );
 }
 
 //TODO: image resource is too specific for this module
 
-struct ImageResource {
+struct ImageSource {
     RectWithEndpoint uv_rect;
-    float layer;
-    vec3 user_data;
+    vec4 user_data;
 };
 
-ImageResource fetch_image_resource(int address) {
+ImageSource fetch_image_source(int address) {
     //Note: number of blocks has to match `renderer::BLOCKS_PER_UV_RECT`
     vec4 data[2] = fetch_from_gpu_cache_2(address);
     RectWithEndpoint uv_rect = RectWithEndpoint(data[0].xy, data[0].zw);
-    return ImageResource(uv_rect, data[1].x, data[1].yzw);
+    return ImageSource(uv_rect, data[1]);
 }
 
-ImageResource fetch_image_resource_direct(ivec2 address) {
+ImageSource fetch_image_source_direct(ivec2 address) {
     vec4 data[2] = fetch_from_gpu_cache_2_direct(address);
     RectWithEndpoint uv_rect = RectWithEndpoint(data[0].xy, data[0].zw);
-    return ImageResource(uv_rect, data[1].x, data[1].yzw);
+    return ImageSource(uv_rect, data[1]);
 }
 
 // Fetch optional extra data for a texture cache resource. This can contain
 // a polygon defining a UV rect within the texture cache resource.
 // Note: the polygon coordinates are in homogeneous space.
-struct ImageResourceExtra {
+struct ImageSourceExtra {
     vec4 st_tl;
     vec4 st_tr;
     vec4 st_bl;
     vec4 st_br;
 };
 
-ImageResourceExtra fetch_image_resource_extra(int address) {
+ImageSourceExtra fetch_image_source_extra(int address) {
     vec4 data[4] = fetch_from_gpu_cache_4(address + VECS_PER_IMAGE_RESOURCE);
-    return ImageResourceExtra(
+    return ImageSourceExtra(
         data[0],
         data[1],
         data[2],
         data[3]
     );
 }
 
 #endif //WR_VERTEX_SHADER
--- a/gfx/wr/webrender/res/prim_shared.glsl
+++ b/gfx/wr/webrender/res/prim_shared.glsl
@@ -19,18 +19,17 @@ uniform sampler2D sClipMask;
 
 vec2 clamp_rect(vec2 pt, RectWithSize rect) {
     return clamp(pt, rect.p0, rect.p0 + rect.size);
 }
 
 #ifndef SWGL_CLIP_MASK
 // TODO: convert back to RectWithEndPoint if driver issues are resolved, if ever.
 flat varying vec4 vClipMaskUvBounds;
-// XY and W are homogeneous coordinates, Z is the layer index
-varying vec4 vClipMaskUv;
+varying vec2 vClipMaskUv;
 #endif
 
 #ifdef WR_VERTEX_SHADER
 
 #define COLOR_MODE_FROM_PASS            0
 #define COLOR_MODE_ALPHA                1
 #define COLOR_MODE_SUBPX_CONST_COLOR    2
 #define COLOR_MODE_SUBPX_BG_PASS0       3
@@ -120,17 +119,17 @@ VertexInfo write_vertex(vec2 local_pos,
 
     // Transform the current vertex to world space.
     vec4 world_pos = transform.m * vec4(clamped_local_pos, 0.0, 1.0);
 
     // Convert the world positions to device pixel space.
     vec2 device_pos = world_pos.xy * task.device_pixel_scale;
 
     // Apply offsets for the render task to get correct screen location.
-    vec2 final_offset = -task.content_origin + task.common_data.task_rect.p0;
+    vec2 final_offset = -task.content_origin + task.task_rect.p0;
 
     gl_Position = uTransform * vec4(device_pos + final_offset * world_pos.w, z * world_pos.w, world_pos.w);
 
     VertexInfo vi = VertexInfo(
         clamped_local_pos,
         world_pos
     );
 
@@ -210,17 +209,17 @@ VertexInfo write_transform_vertex(RectWi
     local_segment_rect.p0 -= extrude_distance.xy;
     local_segment_rect.size += extrude_distance.xy + extrude_distance.zw;
 #endif
 
     // Select the corner of the local rect that we are processing.
     vec2 local_pos = local_segment_rect.p0 + local_segment_rect.size * aPosition.xy;
 
     // Convert the world positions to device pixel space.
-    vec2 task_offset = task.common_data.task_rect.p0 - task.content_origin;
+    vec2 task_offset = task.task_rect.p0 - task.content_origin;
 
     // Transform the current vertex to world space.
     vec4 world_pos = transform.m * vec4(local_pos, 0.0, 1.0);
     vec4 final_pos = vec4(
         world_pos.xy * task.device_pixel_scale + task_offset * world_pos.w,
         z * world_pos.w,
         world_pos.w
     );
@@ -234,35 +233,35 @@ VertexInfo write_transform_vertex(RectWi
 
     return vi;
 }
 
 void write_clip(vec4 world_pos, ClipArea area, PictureTask task) {
 #ifdef SWGL_CLIP_MASK
     swgl_clipMask(
         sClipMask,
-        (task.common_data.task_rect.p0 - task.content_origin) - (area.common_data.task_rect.p0 - area.screen_origin),
-        area.common_data.task_rect.p0,
-        area.common_data.task_rect.size
+        (task.task_rect.p0 - task.content_origin) - (area.task_rect.p0 - area.screen_origin),
+        area.task_rect.p0,
+        area.task_rect.size
     );
 #else
     vec2 uv = world_pos.xy * area.device_pixel_scale +
-        world_pos.w * (area.common_data.task_rect.p0 - area.screen_origin);
+        world_pos.w * (area.task_rect.p0 - area.screen_origin);
     vClipMaskUvBounds = vec4(
-        area.common_data.task_rect.p0,
-        area.common_data.task_rect.p0 + area.common_data.task_rect.size
+        area.task_rect.p0,
+        area.task_rect.p0 + area.task_rect.size
     );
-    vClipMaskUv = vec4(uv, area.common_data.texture_layer_index, world_pos.w);
+    vClipMaskUv = uv;
 #endif
 }
 
 // Read the exta image data containing the homogeneous screen space coordinates
 // of the corners, interpolate between them, and return real screen space UV.
 vec2 get_image_quad_uv(int address, vec2 f) {
-    ImageResourceExtra extra_data = fetch_image_resource_extra(address);
+    ImageSourceExtra extra_data = fetch_image_source_extra(address);
     vec4 x = mix(extra_data.st_tl, extra_data.st_tr, f.x);
     vec4 y = mix(extra_data.st_bl, extra_data.st_br, f.x);
     vec4 z = mix(x, y, f.y);
     return z.xy / z.w;
 }
 #endif //WR_VERTEX_SHADER
 
 #ifdef WR_FRAGMENT_SHADER
@@ -281,17 +280,17 @@ float do_clip() {
     return 1.0;
 #else
     // check for the dummy bounds, which are given to the opaque objects
     if (vClipMaskUvBounds.xy == vClipMaskUvBounds.zw) {
         return 1.0;
     }
     // anything outside of the mask is considered transparent
     //Note: we assume gl_FragCoord.w == interpolated(1 / vClipMaskUv.w)
-    vec2 mask_uv = vClipMaskUv.xy * gl_FragCoord.w;
+    vec2 mask_uv = vClipMaskUv * gl_FragCoord.w;
     bvec2 left = lessThanEqual(vClipMaskUvBounds.xy, mask_uv); // inclusive
     bvec2 right = greaterThan(vClipMaskUvBounds.zw, mask_uv); // non-inclusive
     // bail out if the pixel is outside the valid bounds
     if (!all(bvec4(left, right))) {
         return 0.0;
     }
     // finally, the slow path - fetch the mask value from an image
     return texelFetch(sClipMask, ivec2(mask_uv), 0).r;
--- a/gfx/wr/webrender/res/ps_split_composite.glsl
+++ b/gfx/wr/webrender/res/ps_split_composite.glsl
@@ -59,20 +59,20 @@ SplitCompositeInstance fetch_composite_i
 }
 
 void main(void) {
     SplitCompositeInstance ci = fetch_composite_instance();
     SplitGeometry geometry = fetch_split_geometry(ci.polygons_address);
     PrimitiveHeader ph = fetch_prim_header(ci.prim_header_index);
     PictureTask dest_task = fetch_picture_task(ci.render_task_index);
     Transform transform = fetch_transform(ph.transform_id);
-    ImageResource res = fetch_image_resource(ph.user_data.x);
+    ImageSource res = fetch_image_source(ph.user_data.x);
     ClipArea clip_area = fetch_clip_area(ph.user_data.w);
 
-    vec2 dest_origin = dest_task.common_data.task_rect.p0 -
+    vec2 dest_origin = dest_task.task_rect.p0 -
                        dest_task.content_origin;
 
     vec2 local_pos = bilerp(geometry.local[0], geometry.local[1],
                             geometry.local[3], geometry.local[2],
                             aPosition.y, aPosition.x);
     vec4 world_pos = transform.m * vec4(local_pos, 0.0, 1.0);
 
     vec4 final_pos = vec4(
--- a/gfx/wr/webrender/res/ps_text_run.glsl
+++ b/gfx/wr/webrender/res/ps_text_run.glsl
@@ -50,24 +50,23 @@ Glyph fetch_glyph(int specific_prim_addr
     vec2 glyph = mix(data.xy, data.zw,
                      bvec2(uint(glyph_index) % GLYPHS_PER_GPU_BLOCK == 1U));
 
     return Glyph(glyph);
 }
 
 struct GlyphResource {
     vec4 uv_rect;
-    float layer;
     vec2 offset;
     float scale;
 };
 
 GlyphResource fetch_glyph_resource(int address) {
     vec4 data[2] = fetch_from_gpu_cache_2(address);
-    return GlyphResource(data[0], data[1].x, data[1].yz, data[1].w);
+    return GlyphResource(data[0], data[1].xy, data[1].z);
 }
 
 struct TextRun {
     vec4 color;
     vec4 bg_color;
 };
 
 TextRun fetch_text_run(int address) {
--- a/gfx/wr/webrender/res/render_task.glsl
+++ b/gfx/wr/webrender/res/render_task.glsl
@@ -3,116 +3,99 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #ifdef WR_VERTEX_SHADER
 #define VECS_PER_RENDER_TASK        2U
 
 uniform HIGHP_SAMPLER_FLOAT sampler2D sRenderTasks;
 
-struct RenderTaskCommonData {
+struct RenderTaskData {
     RectWithSize task_rect;
-    float texture_layer_index;
-};
-
-struct RenderTaskData {
-    RenderTaskCommonData common_data;
-    vec3 user_data;
+    vec4 user_data;
 };
 
 RenderTaskData fetch_render_task_data(int index) {
     ivec2 uv = get_fetch_uv(index, VECS_PER_RENDER_TASK);
 
     vec4 texel0 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(0, 0));
     vec4 texel1 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(1, 0));
 
     RectWithSize task_rect = RectWithSize(
         texel0.xy,
         texel0.zw
     );
 
-    RenderTaskCommonData common_data = RenderTaskCommonData(
+    RenderTaskData data = RenderTaskData(
         task_rect,
-        texel1.x
-    );
-
-    RenderTaskData data = RenderTaskData(
-        common_data,
-        texel1.yzw
+        texel1
     );
 
     return data;
 }
 
-RenderTaskCommonData fetch_render_task_common_data(int index) {
+RectWithSize fetch_render_task_rect(int index) {
     ivec2 uv = get_fetch_uv(index, VECS_PER_RENDER_TASK);
 
     vec4 texel0 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(0, 0));
     vec4 texel1 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(1, 0));
 
     RectWithSize task_rect = RectWithSize(
         texel0.xy,
         texel0.zw
     );
 
-    RenderTaskCommonData data = RenderTaskCommonData(
-        task_rect,
-        texel1.x
-    );
-
-    return data;
+    return task_rect;
 }
 
 #define PIC_TYPE_IMAGE          1
 #define PIC_TYPE_TEXT_SHADOW    2
 
 /*
  The dynamic picture that this brush exists on. Right now, it
  contains minimal information. In the future, it will describe
  the transform mode of primitives on this picture, among other things.
  */
 struct PictureTask {
-    RenderTaskCommonData common_data;
+    RectWithSize task_rect;
     float device_pixel_scale;
     vec2 content_origin;
 };
 
 PictureTask fetch_picture_task(int address) {
     RenderTaskData task_data = fetch_render_task_data(address);
 
     PictureTask task = PictureTask(
-        task_data.common_data,
+        task_data.task_rect,
         task_data.user_data.x,
         task_data.user_data.yz
     );
 
     return task;
 }
 
 #define CLIP_TASK_EMPTY 0x7FFF
 
 struct ClipArea {
-    RenderTaskCommonData common_data;
+    RectWithSize task_rect;
     float device_pixel_scale;
     vec2 screen_origin;
 };
 
 ClipArea fetch_clip_area(int index) {
     ClipArea area;
 
     if (index >= CLIP_TASK_EMPTY) {
-        RectWithSize rect = RectWithSize(vec2(0.0), vec2(0.0));
-
-        area.common_data = RenderTaskCommonData(rect, 0.0);
+        area.task_rect = RectWithSize(vec2(0.0), vec2(0.0));
         area.device_pixel_scale = 0.0;
         area.screen_origin = vec2(0.0);
     } else {
         RenderTaskData task_data = fetch_render_task_data(index);
 
-        area.common_data = task_data.common_data;
+        area.task_rect = task_data.task_rect;
         area.device_pixel_scale = task_data.user_data.x;
         area.screen_origin = task_data.user_data.yz;
     }
 
     return area;
 }
 
 #endif //WR_VERTEX_SHADER
--- a/gfx/wr/webrender/res/shared.glsl
+++ b/gfx/wr/webrender/res/shared.glsl
@@ -17,21 +17,17 @@
 #extension GL_EXT_blend_func_extended : require
 #else
 #extension GL_ARB_explicit_attrib_location : require
 #endif
 #endif
 
 #include base
 
-#if defined(WR_FEATURE_TEXTURE_EXTERNAL) || defined(WR_FEATURE_TEXTURE_RECT) || defined(WR_FEATURE_TEXTURE_2D)
 #define TEX_SAMPLE(sampler, tex_coord) texture(sampler, tex_coord.xy)
-#else
-#define TEX_SAMPLE(sampler, tex_coord) texture(sampler, tex_coord)
-#endif
 
 #if defined(WR_FEATURE_TEXTURE_EXTERNAL) && defined(PLATFORM_ANDROID)
 // On some Mali GPUs we have encountered crashes in glDrawElements when using
 // textureSize(samplerExternalOES) in a vertex shader without potentially
 // sampling from the texture. This tricks the driver in to thinking the texture
 // may be sampled from, avoiding the crash. See bug 1692848.
 uniform float u_mali_workaround_dummy;
 highp ivec2 textureSizeMaliWorkaround(samplerExternalOES sampler, int lod) {
@@ -182,20 +178,16 @@ uniform sampler2D sColor2;
 #elif defined WR_FEATURE_TEXTURE_RECT
 uniform sampler2DRect sColor0;
 uniform sampler2DRect sColor1;
 uniform sampler2DRect sColor2;
 #elif defined WR_FEATURE_TEXTURE_EXTERNAL
 uniform samplerExternalOES sColor0;
 uniform samplerExternalOES sColor1;
 uniform samplerExternalOES sColor2;
-#elif defined WR_FEATURE_TEXTURE_2D_ARRAY
-uniform sampler2DArray sColor0;
-uniform sampler2DArray sColor1;
-uniform sampler2DArray sColor2;
 #endif
 
 #ifdef WR_FEATURE_DITHERING
 uniform sampler2D sDither;
 #endif
 
 //======================================================================================
 // Interpolator definitions
--- a/gfx/wr/webrender/res/yuv.glsl
+++ b/gfx/wr/webrender/res/yuv.glsl
@@ -114,55 +114,54 @@ void write_uv_rect(
 
 #ifdef WR_FRAGMENT_SHADER
 
 vec4 sample_yuv(
     int format,
     mat3 yuv_color_matrix,
     vec3 yuv_offset_vector,
     float coefficient,
-    vec3 yuv_layers,
     vec2 in_uv_y,
     vec2 in_uv_u,
     vec2 in_uv_v,
     vec4 uv_bounds_y,
     vec4 uv_bounds_u,
     vec4 uv_bounds_v
 ) {
     vec3 yuv_value;
 
     switch (format) {
         case YUV_FORMAT_PLANAR:
             {
                 // The yuv_planar format should have this third texture coordinate.
                 vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw);
                 vec2 uv_u = clamp(in_uv_u, uv_bounds_u.xy, uv_bounds_u.zw);
                 vec2 uv_v = clamp(in_uv_v, uv_bounds_v.xy, uv_bounds_v.zw);
-                yuv_value.x = TEX_SAMPLE(sColor0, vec3(uv_y, yuv_layers.x)).r;
-                yuv_value.y = TEX_SAMPLE(sColor1, vec3(uv_u, yuv_layers.y)).r;
-                yuv_value.z = TEX_SAMPLE(sColor2, vec3(uv_v, yuv_layers.z)).r;
+                yuv_value.x = TEX_SAMPLE(sColor0, uv_y).r;
+                yuv_value.y = TEX_SAMPLE(sColor1, uv_u).r;
+                yuv_value.z = TEX_SAMPLE(sColor2, uv_v).r;
             }
             break;
 
         case YUV_FORMAT_NV12:
             {
                 vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw);
                 vec2 uv_uv = clamp(in_uv_u, uv_bounds_u.xy, uv_bounds_u.zw);
-                yuv_value.x = TEX_SAMPLE(sColor0, vec3(uv_y, yuv_layers.x)).r;
-                yuv_value.yz = TEX_SAMPLE(sColor1, vec3(uv_uv, yuv_layers.y)).rg;
+                yuv_value.x = TEX_SAMPLE(sColor0, uv_y).r;
+                yuv_value.yz = TEX_SAMPLE(sColor1, uv_uv).rg;
             }
             break;
 
         case YUV_FORMAT_INTERLEAVED:
             {
                 // "The Y, Cb and Cr color channels within the 422 data are mapped into
                 // the existing green, blue and red color channels."
                 // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt
                 vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw);
-                yuv_value = TEX_SAMPLE(sColor0, vec3(uv_y, yuv_layers.x)).gbr;
+                yuv_value = TEX_SAMPLE(sColor0, uv_y).gbr;
             }
             break;
 
         default:
             yuv_value = vec3(0.0);
             break;
     }
 
--- a/gfx/wr/webrender/src/composite.rs
+++ b/gfx/wr/webrender/src/composite.rs
@@ -137,25 +137,23 @@ pub struct ExternalSurfaceDescriptor {
 }
 
 /// Information about a plane in a YUV or RGB surface.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Copy, Clone)]
 pub struct ExternalPlaneDescriptor {
     pub texture: TextureSource,
-    pub texture_layer: i32,
     pub uv_rect: TexelRect,
 }
 
 impl ExternalPlaneDescriptor {
     fn invalid() -> Self {
         ExternalPlaneDescriptor {
             texture: TextureSource::Invalid,
-            texture_layer: 0,
             uv_rect: TexelRect::invalid(),
         }
     }
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Copy, Clone, PartialEq)]
@@ -762,17 +760,16 @@ impl CompositeState {
                 deferred_resolves,
             );
 
             if cache_item.texture_id != TextureSource::Invalid {
                 valid_plane_count += 1;
                 let plane = &mut planes[i];
                 *plane = ExternalPlaneDescriptor {
                     texture: cache_item.texture_id,
-                    texture_layer: cache_item.texture_layer,
                     uv_rect: cache_item.uv_rect.into(),
                 };
             }
         }
 
         // Check if there are valid images added for each YUV plane
         if valid_plane_count < required_plane_count {
             warn!("Warnings: skip a YUV/RGB compositor surface, found {}/{} valid images",
--- a/gfx/wr/webrender/src/device/gl.rs
+++ b/gfx/wr/webrender/src/device/gl.rs
@@ -149,17 +149,16 @@ fn depth_target_size_in_bytes(dimensions
     // for stencil, so we measure them as 32 bits.
     let pixels = dimensions.width * dimensions.height;
     (pixels as usize) * 4
 }
 
 pub fn get_gl_target(target: ImageBufferKind) -> gl::GLuint {
     match target {
         ImageBufferKind::Texture2D => gl::TEXTURE_2D,
-        ImageBufferKind::Texture2DArray => gl::TEXTURE_2D_ARRAY,
         ImageBufferKind::TextureRect => gl::TEXTURE_RECTANGLE,
         ImageBufferKind::TextureExternal => gl::TEXTURE_EXTERNAL_OES,
     }
 }
 
 fn supports_extension(extensions: &[String], extension: &str) -> bool {
     extensions.iter().any(|s| s == extension)
 }
--- a/gfx/wr/webrender/src/frame_builder.rs
+++ b/gfx/wr/webrender/src/frame_builder.rs
@@ -4,32 +4,31 @@
 
 use api::{ColorF, DebugFlags, FontRenderMode, PremultipliedColorF};
 use api::units::*;
 use crate::batch::{BatchBuilder, AlphaBatchBuilder, AlphaBatchContainer};
 use crate::clip::{ClipStore, ClipChainStack};
 use crate::spatial_tree::{SpatialTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
 use crate::composite::{CompositorKind, CompositeState, CompositeStatePreallocator};
 use crate::debug_item::DebugItem;
-use crate::frame_graph::{Pass, SubPassSurface};
 use crate::gpu_cache::{GpuCache, GpuCacheHandle};
 use crate::gpu_types::{PrimitiveHeaders, TransformPalette, ZBufferIdGenerator};
 use crate::gpu_types::TransformData;
 use crate::internal_types::{FastHashMap, PlaneSplitter};
 use crate::picture::{DirtyRegion, PictureUpdateState, SliceId, TileCacheInstance};
 use crate::picture::{SurfaceInfo, SurfaceIndex, ROOT_SURFACE_INDEX, SurfaceRenderTasks};
 use crate::picture::{BackdropKind, SubpixelMode, TileCacheLogger, RasterConfig, PictureCompositeMode};
 use crate::prepare::prepare_primitives;
 use crate::prim_store::{PictureIndex, PrimitiveDebugId};
 use crate::prim_store::{DeferredResolve};
 use crate::profiler::{self, TransactionProfile};
 use crate::render_backend::{DataStores, FrameStamp, FrameId, ScratchBuffer};
 use crate::render_target::{RenderTarget, PictureCacheTarget, TextureCacheRenderTarget};
 use crate::render_target::{RenderTargetContext, RenderTargetKind, AlphaRenderTarget, ColorRenderTarget};
-use crate::render_task_graph::{RenderTaskId, RenderTaskGraph};
+use crate::render_task_graph::{RenderTaskId, RenderTaskGraph, Pass, SubPassSurface};
 use crate::render_task_graph::{RenderPass, RenderTaskGraphBuilder};
 use crate::render_task::{RenderTaskLocation, RenderTaskKind, StaticRenderTaskSurface};
 use crate::resource_cache::{ResourceCache};
 use crate::scene::{BuiltScene, SceneProperties};
 use crate::space::SpaceMapper;
 use crate::segment::SegmentBuilder;
 use std::{f32, mem};
 use crate::util::{VecHelper, Recycler, Preallocator};
@@ -844,19 +843,19 @@ pub fn build_render_pass(
                     }
                 };
 
                 picture_cache_tasks
                     .entry(pic_index)
                     .or_insert_with(Vec::new)
                     .push(task_id);
             }
-            SubPassSurface::Persistent { surface: StaticRenderTaskSurface::TextureCache { target_kind, texture, layer, .. } } => {
+            SubPassSurface::Persistent { surface: StaticRenderTaskSurface::TextureCache { target_kind, texture, .. } } => {
                 let texture = pass.texture_cache
-                    .entry((texture, layer))
+                    .entry(texture)
                     .or_insert_with(||
                         TextureCacheRenderTarget::new(target_kind)
                     );
                 for task_id in &sub_pass.task_ids {
                     texture.add_task(*task_id, render_tasks);
                 }
             }
             SubPassSurface::Persistent { surface: StaticRenderTaskSurface::ReadOnly { .. } } => {
@@ -951,17 +950,17 @@ pub fn build_render_pass(
         }
 
         // Create picture cache targets, one per render task, and assign
         // the correct batcher to them.
         let batchers = batch_builder.finalize();
         for (task_id, batcher) in task_ids.into_iter().zip(batchers.into_iter()) {
             profile_scope!("task");
             let task = &render_tasks[task_id];
-            let (target_rect, _) = task.get_target_rect();
+            let target_rect = task.get_target_rect();
 
             match task.location {
                 RenderTaskLocation::Static { surface: StaticRenderTaskSurface::PictureCache { ref surface, .. }, .. } => {
                     // TODO(gw): The interface here is a bit untidy since it's
                     //           designed to support batch merging, which isn't
                     //           relevant for picture cache targets. We
                     //           can restructure / tidy this up a bit.
                     let (scissor_rect, valid_rect)  = match render_tasks[task_id].kind {
deleted file mode 100644
--- a/gfx/wr/webrender/src/frame_graph.rs
+++ /dev/null
@@ -1,1035 +0,0 @@
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-use api::units::*;
-use api::ImageFormat;
-use crate::gpu_cache::{GpuCache, GpuCacheAddress};
-use crate::internal_types::{TextureSource, CacheTextureId, FastHashMap, FastHashSet};
-use crate::render_backend::FrameId;
-use crate::render_task_graph::{RenderTaskId};
-use crate::render_task::{StaticRenderTaskSurface, RenderTaskLocation, RenderTask};
-use crate::render_target::RenderTargetKind;
-use crate::render_task::{RenderTaskData, RenderTaskKind};
-use crate::render_task_graph::RenderTaskAllocation;
-use crate::resource_cache::ResourceCache;
-use crate::texture_pack::GuillotineAllocator;
-use crate::prim_store::DeferredResolve;
-use crate::image_source::{resolve_image, resolve_cached_render_task};
-use crate::util::VecHelper;
-use smallvec::SmallVec;
-use std::mem;
-
-/// According to apitrace, textures larger than 2048 break fast clear
-/// optimizations on some intel drivers. We sometimes need to go larger, but
-/// we try to avoid it.
-const MAX_SHARED_SURFACE_SIZE: i32 = 2048;
-
-/// If we ever need a larger texture than the ideal, we better round it up to a
-/// reasonable number in order to have a bit of leeway in case the size of this
-/// this target is changing each frame.
-const TEXTURE_DIMENSION_MASK: i32 = 0xFF;
-
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)]
-pub struct PassId(usize);
-
-impl PassId {
-    pub const MIN: PassId = PassId(0);
-    pub const MAX: PassId = PassId(!0);
-}
-
-/// An internal representation of a dynamic surface that tasks can be
-/// allocated into. Maintains some extra metadata about each surface
-/// during the graph build.
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-struct Surface {
-    /// Whether this is a color or alpha render target
-    kind: RenderTargetKind,
-    /// Allocator for this surface texture
-    allocator: GuillotineAllocator,
-    /// We can only allocate into this for reuse if it's a shared surface
-    is_shared: bool,
-}
-
-impl Surface {
-    /// Allocate a rect within a shared surfce. Returns None if the
-    /// format doesn't match, or allocation fails.
-    fn alloc_rect(
-        &mut self,
-        size: DeviceIntSize,
-        kind: RenderTargetKind,
-        is_shared: bool,
-    ) -> Option<DeviceIntPoint> {
-        if self.kind == kind && self.is_shared == is_shared {
-            self.allocator
-                .allocate(&size)
-                .map(|(_slice, origin)| origin)
-        } else {
-            None
-        }
-    }
-}
-
-/// A sub-pass can draw to either a dynamic (temporary render target) surface,
-/// or a persistent surface (texture or picture cache).
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-#[derive(Debug)]
-pub enum SubPassSurface {
-    /// A temporary (intermediate) surface.
-    Dynamic {
-        /// The renderer texture id
-        texture_id: CacheTextureId,
-        /// Color / alpha render target
-        target_kind: RenderTargetKind,
-        /// The rectangle occupied by tasks in this surface. Used as a clear
-        /// optimization on some GPUs.
-        used_rect: DeviceIntRect,
-    },
-    Persistent {
-        /// Reference to the texture or picture cache surface being drawn to.
-        surface: StaticRenderTaskSurface,
-    },
-}
-
-/// A subpass is a specific render target, and a list of tasks to draw to it.
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct SubPass {
-    /// The surface this subpass draws to
-    pub surface: SubPassSurface,
-    /// The tasks assigned to this subpass.
-    pub task_ids: Vec<RenderTaskId>,
-}
-
-/// A pass expresses dependencies between tasks. Each pass consists of a number
-/// of subpasses.
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct Pass {
-    /// The tasks assigned to this render pass
-    pub task_ids: Vec<RenderTaskId>,
-    /// The subpasses that make up this dependency pass
-    pub sub_passes: Vec<SubPass>,
-    /// A list of intermediate surfaces that can be invalidated after
-    /// this pass completes.
-    pub textures_to_invalidate: Vec<CacheTextureId>,
-}
-
-/// The FrameGraph is the immutable representation of the render task graph. It is
-/// built by the FrameGraphBuilder, and is constructed once per frame.
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct FrameGraph {
-    /// List of tasks added to the graph
-    pub tasks: Vec<RenderTask>,
-
-    /// The passes that were created, based on dependencies between tasks
-    pub passes: Vec<Pass>,
-
-    /// Current frame id, used for debug validation
-    frame_id: FrameId,
-
-    /// GPU specific data for each task that is made available to shaders
-    pub task_data: Vec<RenderTaskData>,
-
-    /// Total number of intermediate surfaces that will be drawn to, used for test validation.
-    #[cfg(test)]
-    surface_count: usize,
-
-    /// Total number of real allocated textures that will be drawn to, used for test validation.
-    #[cfg(test)]
-    unique_surfaces: FastHashSet<CacheTextureId>,
-}
-
-/// The persistent interface that is used during frame building to construct the
-/// frame graph.
-pub struct FrameGraphBuilder {
-    /// List of tasks added to the builder
-    tasks: Vec<RenderTask>,
-
-    /// List of task roots
-    roots: FastHashSet<RenderTaskId>,
-
-    /// Input dependencies where the input is a persistent target,
-    /// rather than a specific render task id. Useful for expressing
-    /// when a task relies on a readback of a surface that is partially
-    /// drawn to.
-    target_inputs: Vec<(RenderTaskId, StaticRenderTaskSurface)>,
-
-    /// Current frame id, used for debug validation
-    frame_id: FrameId,
-
-    /// A list of texture surfaces that can be freed at the end of a pass. Retained
-    /// here to reduce heap allocations.
-    textures_to_free: FastHashSet<CacheTextureId>,
-
-    // Keep a map of `texture_id` to metadata about surfaces that are currently
-    // borrowed from the render target pool.
-    active_surfaces: FastHashMap<CacheTextureId, Surface>,
-
-    /// A temporary buffer used by assign_free_pass. Kept here to avoid heap reallocs
-    child_task_buffer: Vec<RenderTaskId>,
-}
-
-impl FrameGraphBuilder {
-    /// Construct a new graph builder. Typically constructed once and maintained
-    /// over many frames, to avoid extra heap allocations where possible.
-    pub fn new() -> Self {
-        FrameGraphBuilder {
-            tasks: Vec::new(),
-            roots: FastHashSet::default(),
-            target_inputs: Vec::new(),
-            frame_id: FrameId::INVALID,
-            textures_to_free: FastHashSet::default(),
-            active_surfaces: FastHashMap::default(),
-            child_task_buffer: Vec::new(),
-        }
-    }
-
-    pub fn frame_id(&self) -> FrameId {
-        self.frame_id
-    }
-
-    /// Begin a new frame
-    pub fn begin_frame(&mut self, frame_id: FrameId) {
-        self.frame_id = frame_id;
-        self.roots.clear();
-    }
-
-    /// Get immutable access to a task
-    // TODO(gw): There's only a couple of places that existing code needs to access
-    //           a task during the building step. Perhaps we can remove this?
-    pub fn get_task(
-        &self,
-        task_id: RenderTaskId,
-    ) -> &RenderTask {
-        &self.tasks[task_id.index as usize]
-    }
-
-    /// Get mutable access to a task
-    // TODO(gw): There's only a couple of places that existing code needs to access
-    //           a task during the building step. Perhaps we can remove this?
-    pub fn get_task_mut(
-        &mut self,
-        task_id: RenderTaskId,
-    ) -> &mut RenderTask {
-        &mut self.tasks[task_id.index as usize]
-    }
-
-    /// Add a new task to the graph.
-    pub fn add(&mut self) -> RenderTaskAllocation {
-        // Assume every task is a root to start with
-        self.roots.insert(
-            RenderTaskId { index: self.tasks.len() as u16 }
-        );
-
-        RenderTaskAllocation {
-            alloc: self.tasks.alloc(),
-        }
-    }
-
-    /// Express a dependency, such that `task_id` depends on `input` as a texture source.
-    pub fn add_dependency(
-        &mut self,
-        task_id: RenderTaskId,
-        input: RenderTaskId,
-    ) {
-        self.tasks[task_id.index as usize].children.push(input);
-
-        // Once a task is an input, it's no longer a root
-        self.roots.remove(&input);
-    }
-
-    /// Register a persistent surface as an input dependency of a task (readback).
-    pub fn add_target_input(
-        &mut self,
-        task_id: RenderTaskId,
-        target: StaticRenderTaskSurface,
-    ) {
-        self.target_inputs.push((task_id, target));
-    }
-
-    /// End the graph building phase and produce the immutable task graph for this frame
-    pub fn end_frame(
-        &mut self,
-        resource_cache: &mut ResourceCache,
-        gpu_cache: &mut GpuCache,
-        deferred_resolves: &mut Vec<DeferredResolve>,
-    ) -> FrameGraph {
-        // Copy the render tasks over to the immutable graph output
-        let task_count = self.tasks.len();
-        let tasks = mem::replace(
-            &mut self.tasks,
-            Vec::with_capacity(task_count),
-        );
-
-        let mut graph = FrameGraph {
-            tasks,
-            passes: Vec::new(),
-            task_data: Vec::with_capacity(task_count),
-            frame_id: self.frame_id,
-            #[cfg(test)]
-            surface_count: 0,
-            #[cfg(test)]
-            unique_surfaces: FastHashSet::default(),
-        };
-
-        // Handle late mapping of dependencies on a specific persistent target.
-        // NOTE: This functionality isn't used by current callers of the frame graph, but
-        //       will be used in future (for example, to express readbacks of partially
-        //       rendered picture tiles for mix-blend-mode etc).
-        if !self.target_inputs.is_empty() {
-            // Create a mapping from persistent surface id -> render task root (used below):
-            let mut roots = FastHashMap::default();
-            roots.reserve(self.roots.len());
-            for root_id in &self.roots {
-                let task = &graph.tasks[root_id.index as usize];
-                match task.location {
-                    RenderTaskLocation::Static { ref surface, .. } => {
-                        // We should never encounter a graph where the same surface is a
-                        // render root more than one.
-                        assert!(!roots.contains_key(surface));
-                        roots.insert(surface.clone(), *root_id);
-                    }
-                    RenderTaskLocation::Dynamic { .. }
-                    | RenderTaskLocation::CacheRequest { .. }
-                    | RenderTaskLocation::Unallocated { .. } => {
-                        // Intermediate surfaces can't be render roots, they should always
-                        // be a dependency of a render root.
-                        panic!("bug: invalid root");
-                    }
-                }
-            }
-            assert_eq!(roots.len(), self.roots.len());
-
-            // Now resolve those dependencies on persistent targets and add them
-            // as a render task dependency.
-            for (task_id, target_id) in self.target_inputs.drain(..) {
-                match roots.get(&target_id) {
-                    Some(root_task_id) => {
-                        graph.tasks[task_id.index as usize].children.push(*root_task_id);
-                        self.roots.remove(root_task_id);
-                    }
-                    None => {
-                        println!("WARN: {:?} depends on root {:?} but it has no tasks!",
-                            task_id,
-                            target_id,
-                        );
-                    }
-                }
-            }
-        }
-
-        // Two traversals of the graph are required. The first pass determines how many passes
-        // are required, and assigns render tasks a pass to be drawn on. The second pass determines
-        // when the last time a render task is used as an input, and assigns what pass the surface
-        // backing that render task can be freed (the surface is then returned to the render target
-        // pool and may be aliased / reused during subsequent passes).
-
-        let mut pass_count = 0;
-
-        // Traverse each root, and assign `render_on` for each task and count number of required passes
-        for root_id in &self.roots {
-            assign_render_pass(
-                *root_id,
-                PassId(0),
-                &mut graph,
-                &mut pass_count,
-            );
-        }
-
-        // Determine which pass each task can be freed on, which depends on which is
-        // the last task that has this as an input.
-        for i in 0 .. graph.tasks.len() {
-            let task_id = RenderTaskId { index: i as u16 };
-            assign_free_pass(
-                task_id,
-                &mut self.child_task_buffer,
-                &mut graph,
-            );
-        }
-
-        // Construct passes array for tasks to be assigned to below
-        for _ in 0 .. pass_count+1 {
-            graph.passes.push(Pass {
-                task_ids: Vec::new(),
-                sub_passes: Vec::new(),
-                textures_to_invalidate: Vec::new(),
-            });
-        }
-
-        // Assign tasks to each pass based on their `render_on` attribute
-        for (index, task) in graph.tasks.iter().enumerate() {
-            if task.kind.is_a_rendering_operation() {
-                let id = RenderTaskId { index: index as u16 };
-                graph.passes[task.render_on.0].task_ids.push(id);
-            }
-        }
-
-        // At this point, tasks are assigned to each dependency pass. Now we
-        // can go through each pass and create sub-passes, assigning each task
-        // to a target and destination rect.
-        assert!(self.active_surfaces.is_empty());
-
-        for (pass_id, pass) in graph.passes.iter_mut().enumerate().rev() {
-            assert!(self.textures_to_free.is_empty());
-
-            for task_id in &pass.task_ids {
-                let task = &mut graph.tasks[task_id.index as usize];
-
-                match task.location {
-                    RenderTaskLocation::Unallocated { size } => {
-                        let mut location = None;
-                        let kind = task.kind.target_kind();
-
-                        // Allow this render task to use a shared surface target if it
-                        // is freed straight after this pass. Tasks that must remain
-                        // allocated for inputs on subsequent passes are always assigned
-                        // to a standalone surface, to simplify lifetime management of
-                        // render targets.
-
-                        let can_use_shared_surface =
-                            task.render_on == PassId(task.free_after.0 + 1);
-
-                        if can_use_shared_surface {
-                            // If we can use a shared surface, step through the existing shared
-                            // surfaces for this subpass, and see if we can allocate the task
-                            // to one of these targets.
-                            for sub_pass in &mut pass.sub_passes {
-                                if let SubPassSurface::Dynamic { texture_id, ref mut used_rect, .. } = sub_pass.surface {
-                                    let surface = self.active_surfaces.get_mut(&texture_id).unwrap();
-                                    if let Some(p) = surface.alloc_rect(size, kind, true) {
-                                        location = Some((texture_id, p));
-                                        *used_rect = used_rect.union(&DeviceIntRect::new(p, size));
-                                        sub_pass.task_ids.push(*task_id);
-                                        break;
-                                    }
-                                }
-                            }
-                        }
-
-                        if location.is_none() {
-                            // If it wasn't possible to allocate the task to a shared surface, get a new
-                            // render target from the resource cache pool/
-
-                            // If this is a really large task, don't bother allocating it as a potential
-                            // shared surface for other tasks.
-
-                            let can_use_shared_surface = can_use_shared_surface &&
-                                size.width <= MAX_SHARED_SURFACE_SIZE &&
-                                size.height <= MAX_SHARED_SURFACE_SIZE;
-
-                            let surface_size = if can_use_shared_surface {
-                                DeviceIntSize::new(
-                                    MAX_SHARED_SURFACE_SIZE,
-                                    MAX_SHARED_SURFACE_SIZE,
-                                )
-                            } else {
-                                // Round up size here to avoid constant re-allocs during resizing
-                                DeviceIntSize::new(
-                                    (size.width + TEXTURE_DIMENSION_MASK) & !TEXTURE_DIMENSION_MASK,
-                                    (size.height + TEXTURE_DIMENSION_MASK) & !TEXTURE_DIMENSION_MASK,
-                                )
-                            };
-
-                            let format = match kind {
-                                RenderTargetKind::Color => ImageFormat::RGBA8,
-                                RenderTargetKind::Alpha => ImageFormat::R8,
-                            };
-
-                            // Get render target of appropriate size and format from resource cache
-                            let texture_id = resource_cache.get_or_create_render_target_from_pool(
-                                surface_size,
-                                format,
-                            );
-
-                            // Allocate metadata we need about this surface while it's active
-                            let mut surface = Surface {
-                                kind,
-                                allocator: GuillotineAllocator::new(Some(surface_size)),
-                                is_shared: can_use_shared_surface,
-                            };
-
-                            // Allocation of the task must fit in this new surface!
-                            let p = surface.alloc_rect(
-                                size,
-                                kind,
-                                can_use_shared_surface,
-                            ).expect("bug: alloc must succeed!");
-
-                            location = Some((texture_id, p));
-
-                            // Store the metadata about this newly active surface. We should never
-                            // get a target surface with the same texture_id as a currently active surface.
-                            let _prev_surface = self.active_surfaces.insert(texture_id, surface);
-                            assert!(_prev_surface.is_none());
-
-                            // Store some information about surface allocations if in test mode
-                            #[cfg(test)]
-                            {
-                                graph.surface_count += 1;
-                                graph.unique_surfaces.insert(texture_id);
-                            }
-
-                            // Add the target as a new subpass for this render pass.
-                            pass.sub_passes.push(SubPass {
-                                surface: SubPassSurface::Dynamic {
-                                    texture_id,
-                                    target_kind: kind,
-                                    used_rect: DeviceIntRect::new(p, size),
-                                },
-                                task_ids: vec![*task_id],
-                            });
-                        }
-
-                        // By now, we must have allocated a surface and rect for this task, so assign it!
-                        assert!(location.is_some());
-                        task.location = RenderTaskLocation::Dynamic {
-                            texture_id: location.unwrap().0,
-                            rect: DeviceIntRect::new(location.unwrap().1, size),
-                        };
-                    }
-                    RenderTaskLocation::Static { ref surface, .. } => {
-                        // No need to allocate for this surface, since it's a persistent
-                        // target. Instead, just create a new sub-pass for it.
-                        pass.sub_passes.push(SubPass {
-                            surface: SubPassSurface::Persistent {
-                                surface: surface.clone(),
-                            },
-                            task_ids: vec![*task_id],
-                        });
-                    }
-                    RenderTaskLocation::CacheRequest { .. } => {
-                        // No need to allocate nor to create a sub-path for read-only locations.
-                    }
-                    RenderTaskLocation::Dynamic { .. } => {
-                        // Dynamic tasks shouldn't be allocated by this point
-                        panic!("bug: encountered an already allocated task");
-                    }
-                }
-
-                // Return the shared surfaces from this pass
-                let task = &graph.tasks[task_id.index as usize];
-                for child_id in &task.children {
-                    let child_task = &graph.tasks[child_id.index as usize];
-                    match child_task.location {
-                        RenderTaskLocation::Unallocated { .. } => panic!("bug: must be allocated"),
-                        RenderTaskLocation::Dynamic { texture_id, .. } => {
-                            // If this task can be freed after this pass, include it in the
-                            // unique set of textures to be returned to the render target pool below.
-                            if child_task.free_after == PassId(pass_id) {
-                                self.textures_to_free.insert(texture_id);
-                            }
-                        }
-                        RenderTaskLocation::Static { .. } => {}
-                        RenderTaskLocation::CacheRequest { .. } => {}
-                    }
-                }
-            }
-
-            // Return no longer used textures to the pool, so that they can be reused / aliased
-            // by later passes.
-            for texture_id in self.textures_to_free.drain() {
-                resource_cache.return_render_target_to_pool(texture_id);
-                self.active_surfaces.remove(&texture_id).unwrap();
-                pass.textures_to_invalidate.push(texture_id);
-            }
-        }
-
-        // By now, all surfaces that were borrowed from the render target pool must
-        // be returned to the resource cache, or we are leaking intermediate surfaces!
-        assert!(self.active_surfaces.is_empty());
-
-        // Each task is now allocated to a surface and target rect. Write that to the
-        // GPU blocks and task_data. After this point, the graph is returned and is
-        // considered to be immutable for the rest of the frame building process.
-
-        for task in &mut graph.tasks {
-            // First check whether the render task texture and uv rects are managed
-            // externally. This is the case for image tasks and cached tasks. In both
-            // cases it results in a finding the information in the texture cache.
-            let cache_item = if let Some(ref cache_handle) = task.cache_handle {
-                Some(resolve_cached_render_task(
-                    cache_handle,
-                    resource_cache,
-                ))
-            } else if let RenderTaskKind::Image(request) = &task.kind {
-                Some(resolve_image(
-                    *request,
-                    resource_cache,
-                    gpu_cache,
-                    deferred_resolves,
-                ))
-            } else {
-                // General case (non-cached non-image tasks).
-                None
-            };
-
-            if let Some(cache_item) = cache_item {
-                // Update the render task even if the item is invalid.
-                // We'll handle it later and it's easier to not have to
-                // deal with unexpected location variants like
-                // RenderTaskLocation::CacheRequest when we do.
-                let source = cache_item.texture_id;
-                task.uv_rect_handle = cache_item.uv_rect_handle;
-                task.location = RenderTaskLocation::Static {
-                    surface: StaticRenderTaskSurface::ReadOnly { source },
-                    rect: cache_item.uv_rect,
-                };
-            }
-            // Give the render task an opportunity to add any
-            // information to the GPU cache, if appropriate.
-            let (target_rect, target_index) = task.get_target_rect();
-
-            task.write_gpu_blocks(
-                target_rect,
-                target_index,
-                gpu_cache,
-            );
-
-            graph.task_data.push(
-                task.kind.write_task_data(
-                    target_rect,
-                    target_index,
-                )
-            );
-        }
-
-        graph
-    }
-}
-
-impl FrameGraph {
-    /// Print the render task graph to console
-    #[allow(dead_code)]
-    pub fn print(
-        &self,
-    ) {
-        println!("-- FrameGraph --");
-
-        for (i, task) in self.tasks.iter().enumerate() {
-            println!("Task {}: render_on={} free_after={} {:?}",
-                i,
-                task.render_on.0,
-                task.free_after.0,
-                task.kind.as_str(),
-            );
-        }
-
-        for (p, pass) in self.passes.iter().enumerate() {
-            println!("Pass {}:", p);
-
-            for (s, sub_pass) in pass.sub_passes.iter().enumerate() {
-                println!("\tSubPass {}: {:?}",
-                    s,
-                    sub_pass.surface,
-                );
-
-                for task_id in &sub_pass.task_ids {
-                    println!("\t\tTask {:?}", task_id.index);
-                }
-            }
-        }
-    }
-
-    pub fn resolve_location(
-        &self,
-        task_id: impl Into<Option<RenderTaskId>>,
-        gpu_cache: &GpuCache,
-    ) -> Option<(GpuCacheAddress, TextureSource)> {
-        self.resolve_impl(task_id.into()?, gpu_cache)
-    }
-
-    fn resolve_impl(
-        &self,
-        task_id: RenderTaskId,
-        gpu_cache: &GpuCache,
-    ) -> Option<(GpuCacheAddress, TextureSource)> {
-        let task = &self[task_id];
-        let texture_source = task.get_texture_source();
-
-        if let TextureSource::Invalid = texture_source {
-            return None;
-        }
-
-        let uv_address = task.get_texture_address(gpu_cache);
-
-        Some((uv_address, texture_source))
-    }
-
-
-    /// Return the surface and texture counts, used for testing
-    #[cfg(test)]
-    pub fn surface_counts(&self) -> (usize, usize) {
-        (self.surface_count, self.unique_surfaces.len())
-    }
-
-    /// Return current frame id, used for validation
-    #[cfg(debug_assertions)]
-    pub fn frame_id(&self) -> FrameId {
-        self.frame_id
-    }
-}
-
-/// Batching uses index access to read information about tasks
-impl std::ops::Index<RenderTaskId> for FrameGraph {
-    type Output = RenderTask;
-    fn index(&self, id: RenderTaskId) -> &RenderTask {
-        &self.tasks[id.index as usize]
-    }
-}
-
-/// Recursive helper to assign pass that a task should render on
-fn assign_render_pass(
-    id: RenderTaskId,
-    pass: PassId,
-    graph: &mut FrameGraph,
-    pass_count: &mut usize,
-) {
-    let task = &mut graph.tasks[id.index as usize];
-
-    // No point in recursing into paths in the graph if this task already
-    // has been set to draw after this pass.
-    if task.render_on > pass {
-        return;
-    }
-
-    let next_pass = if task.kind.is_a_rendering_operation() {
-        // Keep count of number of passes needed
-        *pass_count = pass.0.max(*pass_count);
-        PassId(pass.0 + 1)
-    } else {
-        // If the node is not a rendering operation, it doesn't create a
-        // render pass, so we don't increment the pass count. 
-        // For now we expect non-rendering nodes to be leafs of the graph.
-        // We don't strictly depend on it but it simplifies the mental model.
-        debug_assert!(task.children.is_empty());
-        pass
-    };
-
-    // A task should be rendered on the earliest pass in the dependency
-    // graph that it's required. Using max here ensures the correct value
-    // in the presence of multiple paths to this task from the root(s).
-    task.render_on = task.render_on.max(pass);
-
-    // TODO(gw): Work around the borrowck - maybe we could structure the dependencies
-    //           storage better, to avoid this?
-    let mut child_task_ids: SmallVec<[RenderTaskId; 8]> = SmallVec::new();
-    child_task_ids.extend_from_slice(&task.children);
-
-    for child_id in child_task_ids {
-        assign_render_pass(
-            child_id,
-            next_pass,
-            graph,
-            pass_count,
-        );
-    }
-}
-
-fn assign_free_pass(
-    id: RenderTaskId,
-    child_task_buffer: &mut Vec<RenderTaskId>,
-    graph: &mut FrameGraph,
-) {
-    let task = &graph.tasks[id.index as usize];
-    let render_on = task.render_on;
-    debug_assert!(child_task_buffer.is_empty());
-
-    // TODO(gw): Work around the borrowck - maybe we could structure the dependencies
-    //           storage better, to avoid this?
-    child_task_buffer.extend_from_slice(&task.children);
-
-    for child_id in child_task_buffer.drain(..) {
-        let child_task = &mut graph.tasks[child_id.index as usize];
-
-        // Each dynamic child task can free its backing surface after the last
-        // task that references it as an input. Using min here ensures the
-        // safe time to free this surface in the presence of multiple paths
-        // to this task from the root(s).
-        match child_task.location {
-            RenderTaskLocation::CacheRequest { .. } => {}
-            RenderTaskLocation::Static { .. } => {
-                // never get freed anyway, so can leave untouched
-                // (could validate that they remain at PassId::MIN)
-            }
-            RenderTaskLocation::Unallocated { .. } => {
-                child_task.free_after = child_task.free_after.min(render_on);
-            }
-            RenderTaskLocation::Dynamic { .. } => {
-                panic!("bug: should not be allocated yet");
-            }
-        }
-    }
-}
-
-
-/// Construct a picture cache render task location for testing
-#[cfg(test)]
-fn pc_target(
-    surface_id: u64,
-    tile_x: i32,
-    tile_y: i32,
-) -> RenderTaskLocation {
-    use crate::{
-        composite::{NativeSurfaceId, NativeTileId},
-        picture::ResolvedSurfaceTexture,
-    };
-
-    let width = 512;
-    let height = 512;
-
-    RenderTaskLocation::Static {
-        surface: StaticRenderTaskSurface::PictureCache {
-            surface: ResolvedSurfaceTexture::Native {
-                id: NativeTileId {
-                    surface_id: NativeSurfaceId(surface_id),
-                    x: tile_x,
-                    y: tile_y,
-                },
-                size: DeviceIntSize::new(width, height),
-            },
-        },
-        rect: DeviceIntSize::new(width, height).into(),
-    }
-}
-
-#[cfg(test)]
-impl FrameGraphBuilder {
-    fn test_expect(
-        mut self,
-        pass_count: usize,
-        total_surface_count: usize,
-        unique_surfaces: &[(i32, i32, ImageFormat)],
-    ) {
-        use crate::render_backend::FrameStamp;
-        use api::{DocumentId, IdNamespace};
-
-        let mut rc = ResourceCache::new_for_testing();
-        let mut gc =  GpuCache::new();
-
-        let mut frame_stamp = FrameStamp::first(DocumentId::new(IdNamespace(1), 1));
-        frame_stamp.advance();
-        gc.prepare_for_frames();
-        gc.begin_frame(frame_stamp);
-
-        let g = self.end_frame(&mut rc, &mut gc, &mut Vec::new());
-        g.print();
-
-        assert_eq!(g.passes.len(), pass_count);
-        assert_eq!(g.surface_counts(), (total_surface_count, unique_surfaces.len()));
-
-        rc.validate_surfaces(unique_surfaces);
-    }
-}
-
-/// Construct a testing render task with given location
-#[cfg(test)]
-fn task_location(location: RenderTaskLocation) -> RenderTask {
-    RenderTask::new_test(
-        location,
-        RenderTargetKind::Color,
-    )
-}
-
-/// Construct a dynamic render task location for testing
-#[cfg(test)]
-fn task_dynamic(size: i32) -> RenderTask {
-    RenderTask::new_test(
-        RenderTaskLocation::Unallocated { size: DeviceIntSize::new(size, size) },
-        RenderTargetKind::Color,
-    )
-}
-
-#[test]
-fn fg_test_1() {
-    // Test that a root target can be used as an input for readbacks
-    // This functionality isn't currently used, but will be in future.
-
-    let mut gb = FrameGraphBuilder::new();
-
-    let root_target = pc_target(0, 0, 0);
-
-    let root = gb.add().init(task_location(root_target.clone()));
-
-    let readback = gb.add().init(task_dynamic(100));
-    gb.add_dependency(readback, root);
-
-    let mix_blend_content = gb.add().init(task_dynamic(50));
-
-    let content = gb.add().init(task_location(root_target));
-    gb.add_dependency(content, readback);
-    gb.add_dependency(content, mix_blend_content);
-
-    gb.test_expect(3, 1, &[
-        (2048, 2048, ImageFormat::RGBA8),
-    ]);
-}
-
-#[test]
-fn fg_test_2() {
-    // Test that texture cache tasks can be added and scheduled correctly as inputs
-    // to picture cache tasks. Ensure that no dynamic surfaces are allocated from the
-    // target pool in this case.
-
-    let mut gb = FrameGraphBuilder::new();
-
-    let pc_root = gb.add().init(task_location(pc_target(0, 0, 0)));
-
-    let tc_0 = StaticRenderTaskSurface::TextureCache {
-        texture: CacheTextureId(0),
-        layer: 0,
-        target_kind: RenderTargetKind::Color,
-    };
-
-    let tc_1 = StaticRenderTaskSurface::TextureCache {
-        texture: CacheTextureId(1),
-        layer: 0,
-        target_kind: RenderTargetKind::Color,
-    };
-
-    gb.add_target_input(
-        pc_root,
-        tc_0.clone(),
-    );
-
-    gb.add_target_input(
-        pc_root,
-        tc_1.clone(),
-    );
-
-    gb.add().init(
-        task_location(RenderTaskLocation::Static { surface: tc_0.clone(), rect: DeviceIntSize::new(128, 128).into() }),
-    );
-
-    gb.add().init(
-        task_location(RenderTaskLocation::Static { surface: tc_1.clone(), rect: DeviceIntSize::new(128, 128).into() }),
-    );
-
-    gb.test_expect(2, 0, &[]);
-}
-
-#[test]
-fn fg_test_3() {
-    // Test that small targets are allocated in a shared surface, and that large
-    // tasks are allocated in a rounded up texture size.
-
-    let mut gb = FrameGraphBuilder::new();
-
-    let pc_root = gb.add().init(task_location(pc_target(0, 0, 0)));
-
-    let child_pic_0 = gb.add().init(task_dynamic(128));
-    let child_pic_1 = gb.add().init(task_dynamic(3000));
-
-    gb.add_dependency(pc_root, child_pic_0);
-    gb.add_dependency(pc_root, child_pic_1);
-
-    gb.test_expect(2, 2, &[
-        (2048, 2048, ImageFormat::RGBA8),
-        (3072, 3072, ImageFormat::RGBA8),
-    ]);
-}
-
-#[test]
-fn fg_test_4() {
-    // Test that for a simple dependency chain of tasks, that render
-    // target surfaces are aliased and reused between passes where possible.
-
-    let mut gb = FrameGraphBuilder::new();
-
-    let pc_root = gb.add().init(task_location(pc_target(0, 0, 0)));
-
-    let child_pic_0 = gb.add().init(task_dynamic(128));
-    let child_pic_1 = gb.add().init(task_dynamic(128));
-    let child_pic_2 = gb.add().init(task_dynamic(128));
-
-    gb.add_dependency(pc_root, child_pic_0);
-    gb.add_dependency(child_pic_0, child_pic_1);
-    gb.add_dependency(child_pic_1, child_pic_2);
-
-    gb.test_expect(4, 3, &[
-        (2048, 2048, ImageFormat::RGBA8),
-        (2048, 2048, ImageFormat::RGBA8),
-    ]);
-}
-
-#[test]
-fn fg_test_5() {
-    // Test that a task that is used as an input by direct parent and also
-    // distance ancestor are scheduled correctly, and allocates the correct
-    // number of passes, taking advantage of surface reuse / aliasing where feasible.
-
-    let mut gb = FrameGraphBuilder::new();
-
-    let pc_root = gb.add().init(task_location(pc_target(0, 0, 0)));
-
-    let child_pic_0 = gb.add().init(task_dynamic(128));
-    let child_pic_1 = gb.add().init(task_dynamic(64));
-    let child_pic_2 = gb.add().init(task_dynamic(32));
-    let child_pic_3 = gb.add().init(task_dynamic(16));
-
-    gb.add_dependency(pc_root, child_pic_0);
-    gb.add_dependency(child_pic_0, child_pic_1);
-    gb.add_dependency(child_pic_1, child_pic_2);
-    gb.add_dependency(child_pic_2, child_pic_3);
-    gb.add_dependency(pc_root, child_pic_3);
-
-    gb.test_expect(5, 4, &[
-        (256, 256, ImageFormat::RGBA8),
-        (2048, 2048, ImageFormat::RGBA8),
-        (2048, 2048, ImageFormat::RGBA8),
-    ]);
-}
-
-#[test]
-fn fg_test_6() {
-    // Test that a task that is used as an input dependency by two parent
-    // tasks is correctly allocated and freed.
-
-    let mut gb = FrameGraphBuilder::new();
-
-    let pc_root_1 = gb.add().init(task_location(pc_target(0, 0, 0)));
-    let pc_root_2 = gb.add().init(task_location(pc_target(0, 1, 0)));
-
-    let child_pic = gb.add().init(task_dynamic(128));
-
-    gb.add_dependency(pc_root_1, child_pic);
-    gb.add_dependency(pc_root_2, child_pic);
-
-    gb.test_expect(2, 1, &[
-        (2048, 2048, ImageFormat::RGBA8),
-    ]);
-}
-
-#[test]
-fn fg_test_7() {
-    // Test that a standalone surface is not incorrectly used to
-    // allocate subsequent shared task rects.
-
-    let mut gb = FrameGraphBuilder::new();
-
-    let pc_root = gb.add().init(task_location(pc_target(0, 0, 0)));
-
-    let child0 = gb.add().init(task_dynamic(16));
-    let child1 = gb.add().init(task_dynamic(16));
-
-    let child2 = gb.add().init(task_dynamic(16));
-    let child3 = gb.add().init(task_dynamic(16));
-
-    gb.add_dependency(pc_root, child0);
-    gb.add_dependency(child0, child1);
-    gb.add_dependency(pc_root, child1);
-
-    gb.add_dependency(pc_root, child2);
-    gb.add_dependency(child2, child3);
-
-    gb.test_expect(3, 3, &[
-        (256, 256, ImageFormat::RGBA8),
-        (2048, 2048, ImageFormat::RGBA8),
-        (2048, 2048, ImageFormat::RGBA8),
-    ]);
-}
--- a/gfx/wr/webrender/src/glyph_rasterizer/mod.rs
+++ b/gfx/wr/webrender/src/glyph_rasterizer/mod.rs
@@ -296,17 +296,17 @@ impl GlyphRasterizer {
                                 size: size2(glyph.width, glyph.height),
                                 stride: None,
                                 format: glyph.format.image_format(self.can_use_r8_format),
                                 flags: ImageDescriptorFlags::empty(),
                                 offset: 0,
                             },
                             TextureFilter::Linear,
                             Some(CachedImageData::Raw(Arc::new(glyph.bytes))),
-                            [glyph.left, -glyph.top, glyph.scale],
+                            [glyph.left, -glyph.top, glyph.scale, 0.0],
                             DirtyRect::All,
                             gpu_cache,
                             Some(glyph_key_cache.eviction_notice()),
                             UvRectKind::Rect,
                             Eviction::Auto,
                             TargetShader::Text,
                         );
                         GlyphCacheEntry::Cached(CachedGlyphInfo {
--- a/gfx/wr/webrender/src/gpu_types.rs
+++ b/gfx/wr/webrender/src/gpu_types.rs
@@ -95,17 +95,16 @@ pub struct BlurInstance {
 
 #[derive(Clone, Debug)]
 #[repr(C)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct ScalingInstance {
     pub target_rect: DeviceRect,
     pub source_rect: DeviceIntRect,
-    pub source_layer: i32,
 }
 
 #[derive(Clone, Debug)]
 #[repr(C)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct SvgFilterInstance {
     pub task_address: RenderTaskAddress,
@@ -248,83 +247,74 @@ pub struct CompositeInstance {
     z_id: f32,
     color_space_or_uv_type: f32, // YuvColorSpace for YUV;
                                  // UV coordinate space for RGB
     yuv_format: f32,            // YuvFormat
     yuv_rescale: f32,
 
     // UV rectangles (pixel space) for color / yuv texture planes
     uv_rects: [TexelRect; 3],
-
-    // Texture array layers for color / yuv texture planes
-    texture_layers: [f32; 3],
 }
 
 impl CompositeInstance {
     pub fn new(
         rect: DeviceRect,
         clip_rect: DeviceRect,
         color: PremultipliedColorF,
-        layer: f32,
         z_id: ZBufferId,
     ) -> Self {
         let uv = TexelRect::new(0.0, 0.0, 1.0, 1.0);
         CompositeInstance {
             rect,
             clip_rect,
             color,
             z_id: z_id.0 as f32,
             color_space_or_uv_type: pack_as_float(0u32),
             yuv_format: 0.0,
             yuv_rescale: 0.0,
-            texture_layers: [layer, 0.0, 0.0],
             uv_rects: [uv, uv, uv],
         }
     }
 
     pub fn new_rgb(
         rect: DeviceRect,
         clip_rect: DeviceRect,
         color: PremultipliedColorF,
-        layer: f32,
         z_id: ZBufferId,
         uv_rect: TexelRect,
     ) -> Self {
         CompositeInstance {
             rect,
             clip_rect,
             color,
             z_id: z_id.0 as f32,
             color_space_or_uv_type: pack_as_float(1u32),
             yuv_format: 0.0,
             yuv_rescale: 0.0,
-            texture_layers: [layer, 0.0, 0.0],
             uv_rects: [uv_rect, uv_rect, uv_rect],
         }
     }
 
     pub fn new_yuv(
         rect: DeviceRect,
         clip_rect: DeviceRect,
         z_id: ZBufferId,
         yuv_color_space: YuvColorSpace,
         yuv_format: YuvFormat,
         yuv_rescale: f32,
-        texture_layers: [f32; 3],
         uv_rects: [TexelRect; 3],
     ) -> Self {
         CompositeInstance {
             rect,
             clip_rect,
             color: PremultipliedColorF::WHITE,
             z_id: z_id.0 as f32,
             color_space_or_uv_type: pack_as_float(yuv_color_space as u32),
             yuv_format: pack_as_float(yuv_format as u32),
             yuv_rescale,
-            texture_layers,
             uv_rects,
         }
     }
 }
 
 /// Vertex format for issuing colored quads.
 #[derive(Debug, Clone)]
 #[repr(C)]
@@ -745,37 +735,35 @@ pub enum UvRectKind {
 }
 
 #[derive(Debug, Copy, Clone)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct ImageSource {
     pub p0: DevicePoint,
     pub p1: DevicePoint,
-    pub texture_layer: f32,
-    pub user_data: [f32; 3],
+    // TODO: It appears that only glyphs make use of user_data (to store glyph offset
+    // and scale).
+    // Perhaps we should separate the two so we don't have to push an empty unused vec4
+    // for all image sources.
+    pub user_data: [f32; 4],
     pub uv_rect_kind: UvRectKind,
 }
 
 impl ImageSource {
     pub fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
         // see fetch_image_resource in GLSL
         // has to be VECS_PER_IMAGE_RESOURCE vectors
         request.push([
             self.p0.x,
             self.p0.y,
             self.p1.x,
             self.p1.y,
         ]);
-        request.push([
-            self.texture_layer,
-            self.user_data[0],
-            self.user_data[1],
-            self.user_data[2],
-        ]);
+        request.push(self.user_data);
 
         // If this is a polygon uv kind, then upload the four vertices.
         if let UvRectKind::Quad { top_left, top_right, bottom_left, bottom_right } = self.uv_rect_kind {
             // see fetch_image_resource_extra in GLSL
             //Note: we really need only 3 components per point here: X, Y, and W
             request.push(top_left);
             request.push(top_right);
             request.push(bottom_left);
--- a/gfx/wr/webrender/src/image_source.rs
+++ b/gfx/wr/webrender/src/image_source.rs
@@ -51,18 +51,17 @@ pub fn resolve_image(
 
                     let cache_item = CacheItem {
                         texture_id: TextureSource::External(deferred_resolve_index, image_buffer_kind),
                         uv_rect_handle: cache_handle,
                         uv_rect: DeviceIntRect::new(
                             DeviceIntPoint::zero(),
                             image_properties.descriptor.size,
                         ),
-                        texture_layer: 0,
-                        user_data: [0.0, 0.0, 0.0],
+                        user_data: [0.0; 4],
                     };
 
                     deferred_resolves.push(DeferredResolve {
                         image_properties,
                         address: gpu_cache.get_address(&cache_handle),
                         rendering: request.rendering,
                     });
 
--- a/gfx/wr/webrender/src/internal_types.rs
+++ b/gfx/wr/webrender/src/internal_types.rs
@@ -326,17 +326,16 @@ pub struct TextureCacheAllocation {
     pub kind: TextureCacheAllocationKind,
 }
 
 /// Information used when allocating / reallocating.
 #[derive(Debug)]
 pub struct TextureCacheAllocInfo {
     pub width: i32,
     pub height: i32,
-    pub layer_count: i32,
     pub format: ImageFormat,
     pub filter: TextureFilter,
     pub target: ImageBufferKind,
     /// Indicates whether this corresponds to one of the shared texture caches.
     pub is_shared_cache: bool,
     /// If true, this texture requires a depth target.
     pub has_depth: bool,
 }
@@ -353,17 +352,16 @@ pub enum TextureCacheAllocationKind {
 }
 
 /// Command to update the contents of the texture cache.
 #[derive(Debug)]
 pub struct TextureCacheUpdate {
     pub rect: DeviceIntRect,
     pub stride: Option<i32>,
     pub offset: i32,
-    pub layer_index: i32,
     pub format_override: Option<ImageFormat>,
     pub source: TextureUpdateSource,
 }
 
 /// Atomic set of commands to manipulate the texture cache, generated on the
 /// RenderBackend thread and executed on the Renderer thread.
 ///
 /// The list of allocation operations is processed before the updates. This is
@@ -413,25 +411,23 @@ impl TextureUpdateList {
     /// we just freed. Used when the texture cache debugger is enabled.
     #[cold]
     pub fn push_debug_clear(
         &mut self,
         id: CacheTextureId,
         origin: DeviceIntPoint,
         width: i32,
         height: i32,
-        layer_index: usize
     ) {
         let size = DeviceIntSize::new(width, height);
         let rect = DeviceIntRect::new(origin, size);
         self.push_update(id, TextureCacheUpdate {
             rect,
             stride: None,
             offset: 0,
-            layer_index: layer_index as i32,
             format_override: None,
             source: TextureUpdateSource::DebugClear,
         });
     }
 
 
     /// Pushes an allocation operation onto the list.
     pub fn push_alloc(&mut self, id: CacheTextureId, info: TextureCacheAllocInfo) {
--- a/gfx/wr/webrender/src/lib.rs
+++ b/gfx/wr/webrender/src/lib.rs
@@ -93,17 +93,16 @@ mod debug_colors;
 mod debug_font_data;
 mod debug_item;
 #[cfg(feature = "debugger")]
 mod debug_server;
 mod device;
 mod ellipse;
 mod filterdata;
 mod frame_builder;
-mod frame_graph;
 mod freelist;
 #[cfg(any(target_os = "macos", target_os = "windows"))]
 mod gamma_lut;
 mod glyph_cache;
 mod glyph_rasterizer;
 mod gpu_cache;
 mod gpu_types;
 mod hit_test;
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -633,18 +633,16 @@ pub enum SurfaceTextureDescriptor {
 /// batching and compositing code in the renderer.
 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum ResolvedSurfaceTexture {
     TextureCache {
         /// The texture ID to draw to.
         texture: TextureSource,
-        /// Slice index in the texture array to draw to.
-        layer: i32,
     },
     Native {
         /// The arbitrary id of this tile.
         id: NativeTileId,
         /// The size of the tile in device pixels.
         size: DeviceIntSize,
     }
 }
@@ -657,17 +655,16 @@ impl SurfaceTextureDescriptor {
         size: DeviceIntSize,
     ) -> ResolvedSurfaceTexture {
         match self {
             SurfaceTextureDescriptor::TextureCache { handle } => {
                 let cache_item = resource_cache.texture_cache.get(handle);
 
                 ResolvedSurfaceTexture::TextureCache {
                     texture: cache_item.texture_id,
-                    layer: cache_item.texture_layer,
                 }
             }
             SurfaceTextureDescriptor::Native { id } => {
                 ResolvedSurfaceTexture::Native {
                     id: id.expect("bug: native surface not allocated"),
                     size,
                 }
             }
--- a/gfx/wr/webrender/src/render_target.rs
+++ b/gfx/wr/webrender/src/render_target.rs
@@ -269,17 +269,17 @@ impl RenderTarget for ColorRenderTarget 
                             surface.raster_spatial_node_index
                         }
                         None => {
                             // This must be the main framebuffer
                             ROOT_SPATIAL_NODE_INDEX
                         }
                     };
 
-                    let (target_rect, _) = task.get_target_rect();
+                    let target_rect = task.get_target_rect();
 
                     let scissor_rect = if pic_task.can_merge {
                         None
                     } else {
                         Some(target_rect)
                     };
 
                     // Typical workloads have a single or a few batch builders with a
@@ -413,18 +413,17 @@ impl RenderTarget for ColorRenderTarget 
                     info,
                     &mut self.scalings,
                     task,
                     task.children.first().map(|&child| &render_tasks[child]),
                 );
             }
             RenderTaskKind::Blit(ref task_info) => {
                 let target_rect = task
-                    .get_target_rect()
-                    .0;
+                    .get_target_rect();
                 self.blits.push(BlitJob {
                     source: task_info.source,
                     target_rect,
                 });
             }
             #[cfg(test)]
             RenderTaskKind::Test(..) => {}
         }
@@ -482,17 +481,17 @@ impl RenderTarget for AlphaRenderTarget 
         ctx: &RenderTargetContext,
         gpu_cache: &mut GpuCache,
         render_tasks: &RenderTaskGraph,
         clip_store: &ClipStore,
         transforms: &mut TransformPalette,
     ) {
         profile_scope!("add_task");
         let task = &render_tasks[task_id];
-        let (target_rect, _) = task.get_target_rect();
+        let target_rect = task.get_target_rect();
 
         match task.kind {
             RenderTaskKind::Image(..) |
             RenderTaskKind::Cached(..) |
             RenderTaskKind::Readback(..) |
             RenderTaskKind::Picture(..) |
             RenderTaskKind::Blit(..) |
             RenderTaskKind::Border(..) |
@@ -622,20 +621,20 @@ impl TextureCacheRenderTarget {
         profile_scope!("add_task");
         let task_address = task_id.into();
 
         let task = &render_tasks[task_id];
         let target_rect = task.get_target_rect();
 
         match task.kind {
             RenderTaskKind::LineDecoration(ref info) => {
-                self.clears.push(target_rect.0);
+                self.clears.push(target_rect);
 
                 self.line_decorations.push(LineDecorationJob {
-                    task_rect: target_rect.0.to_f32(),
+                    task_rect: target_rect.to_f32(),
                     local_size: info.local_size,
                     style: info.style as i32,
                     axis_select: match info.orientation {
                         LineOrientation::Horizontal => 0.0,
                         LineOrientation::Vertical => 1.0,
                     },
                     wavy_line_thickness: info.wavy_line_thickness,
                 });
@@ -649,23 +648,23 @@ impl TextureCacheRenderTarget {
                     render_tasks,
                 );
             }
             RenderTaskKind::Blit(ref task_info) => {
                 // Add a blit job to copy from an existing render
                 // task to this target.
                 self.blits.push(BlitJob {
                     source: task_info.source,
-                    target_rect: target_rect.0,
+                    target_rect,
                 });
             }
             RenderTaskKind::Border(ref task_info) => {
-                self.clears.push(target_rect.0);
+                self.clears.push(target_rect);
 
-                let task_origin = target_rect.0.origin.to_f32();
+                let task_origin = target_rect.origin.to_f32();
                 // TODO(gw): Clone here instead of a move of this vec, since the frame
                 //           graph is immutable by this point. It's rare that borders
                 //           are drawn since they are persisted in the texture cache,
                 //           but perhaps this could be improved in future.
                 let instances = task_info.instances.clone();
                 for mut instance in instances {
                     // TODO(gw): It may be better to store the task origin in
                     //           the render task data instead of per instance.
@@ -687,17 +686,17 @@ impl TextureCacheRenderTarget {
                 };
 
                 for (stop, (offset, color)) in task_info.stops.iter().zip(stops.iter_mut().zip(colors.iter_mut())) {
                     *offset = stop.offset;
                     *color = ColorF::from(stop.color).premultiplied();
                 }
 
                 self.gradients.push(GradientJob {
-                    task_rect: target_rect.0.to_f32(),
+                    task_rect: target_rect.to_f32(),
                     axis_select,
                     stops,
                     colors,
                     start_stop: [task_info.start_point, task_info.end_point],
                 });
             }
             RenderTaskKind::Image(..) |
             RenderTaskKind::Cached(..) |
@@ -740,31 +739,29 @@ fn add_blur_instances(
 fn add_scaling_instances(
     task: &ScalingTask,
     instances: &mut FastHashMap<TextureSource, Vec<ScalingInstance>>,
     target_task: &RenderTask,
     source_task: Option<&RenderTask>,
 ) {
     let target_rect = target_task
         .get_target_rect()
-        .0
         .inner_rect(task.padding)
         .to_f32();
 
     let source = source_task.unwrap().get_texture_source();
 
-    let (source_rect, source_layer) = source_task.unwrap().location.to_source_rect();
+    let source_rect = source_task.unwrap().get_target_rect();
 
     instances
         .entry(source)
         .or_insert(Vec::new())
         .push(ScalingInstance {
             target_rect,
             source_rect,
-            source_layer: source_layer as i32,
         });
 }
 
 fn add_svg_filter_instances(
     instances: &mut Vec<(BatchTextures, Vec<SvgFilterInstance>)>,
     render_tasks: &RenderTaskGraph,
     filter: &SvgFilterInfo,
     task_id: RenderTaskId,
--- a/gfx/wr/webrender/src/render_task.rs
+++ b/gfx/wr/webrender/src/render_task.rs
@@ -4,29 +4,28 @@
 
 use api::{CompositeOperator, FilterPrimitive, FilterPrimitiveInput, FilterPrimitiveKind};
 use api::{LineStyle, LineOrientation, ClipMode, MixBlendMode, ColorF, ColorSpace};
 use api::units::*;
 use crate::clip::{ClipDataStore, ClipItemKind, ClipStore, ClipNodeRange, ClipNodeFlags};
 use crate::spatial_tree::SpatialNodeIndex;
 use crate::filterdata::SFilterData;
 use crate::frame_builder::FrameBuilderConfig;
-use crate::frame_graph::PassId;
 use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
 use crate::gpu_types::{BorderInstance, ImageSource, UvRectKind};
-use crate::internal_types::{CacheTextureId, FastHashMap, LayerIndex, TextureSource, Swizzle};
+use crate::internal_types::{CacheTextureId, FastHashMap, TextureSource, Swizzle};
 use crate::picture::{ResolvedSurfaceTexture, SurfaceInfo};
 use crate::prim_store::{ClipData, PictureIndex};
 use crate::prim_store::gradient::{GRADIENT_FP_STOPS, GradientStopKey};
 #[cfg(feature = "debugger")]
 use crate::print_tree::{PrintTreePrinter};
 use crate::resource_cache::{ResourceCache, ImageRequest};
 use std::{usize, f32, i32, u32};
-use crate::render_target::{RenderTargetIndex, RenderTargetKind};
-use crate::render_task_graph::{RenderTaskId, RenderTaskGraphBuilder};
+use crate::render_target::RenderTargetKind;
+use crate::render_task_graph::{PassId, RenderTaskId, RenderTaskGraphBuilder};
 #[cfg(feature = "debugger")]
 use crate::render_task_graph::RenderTaskGraph;
 use crate::render_task_cache::{RenderTaskCacheEntryHandle, RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskParent};
 use crate::visibility::PrimitiveVisibilityMask;
 use smallvec::SmallVec;
 
 const FLOATS_PER_RENDER_TASK_INFO: usize = 8;
 pub const MAX_RENDER_TASK_SIZE: i32 = 16384;
@@ -59,18 +58,16 @@ impl Into<RenderTaskAddress> for RenderT
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum StaticRenderTaskSurface {
     /// The output of the `RenderTask` will be persisted beyond this frame, and
     /// thus should be drawn into the `TextureCache`.
     TextureCache {
         /// Which texture in the texture cache should be drawn into.
         texture: CacheTextureId,
-        /// The target layer in the above texture.
-        layer: LayerIndex,
         /// What format this texture cache surface is
         target_kind: RenderTargetKind,
     },
     /// Only used as a source for render tasks, can be any texture including an
     /// external one.
     ReadOnly {
         source: TextureSource,
     },
@@ -132,35 +129,16 @@ impl RenderTaskLocation {
     pub fn size(&self) -> DeviceIntSize {
         match self {
             RenderTaskLocation::Unallocated { size } => *size,
             RenderTaskLocation::Dynamic { rect, .. } => rect.size,
             RenderTaskLocation::Static { rect, .. } => rect.size,
             RenderTaskLocation::CacheRequest { size } => *size,
         }
     }
-
-    pub fn to_source_rect(&self) -> (DeviceIntRect, LayerIndex) {
-        match *self {
-            RenderTaskLocation::Unallocated { .. } => panic!("Expected position to be set for the task!"),
-            RenderTaskLocation::Dynamic { rect, .. } => (rect, 0),
-            RenderTaskLocation::Static { surface: StaticRenderTaskSurface::PictureCache { .. }, .. } => {
-                panic!("bug: picture cache tasks should never be a source!");
-            }
-            RenderTaskLocation::Static { rect, surface: StaticRenderTaskSurface::TextureCache { layer, .. } } => {
-                (rect, layer)
-            }
-            RenderTaskLocation::Static { rect, surface: StaticRenderTaskSurface::ReadOnly { .. } } => {
-                (rect, 0)
-            }
-            RenderTaskLocation::CacheRequest { .. } => {
-                panic!("should not be called");
-            }
-        }
-    }
 }
 
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct CachedTask {
     pub target_kind: RenderTargetKind,
 }
@@ -620,93 +598,96 @@ impl RenderTaskKind {
     }
 
     // Write (up to) 8 floats of data specific to the type
     // of render task that is provided to the GPU shaders
     // via a vertex texture.
     pub fn write_task_data(
         &self,
         target_rect: DeviceIntRect,
-        target_index: RenderTargetIndex,
     ) -> RenderTaskData {
         // NOTE: The ordering and layout of these structures are
         //       required to match both the GPU structures declared
         //       in prim_shared.glsl, and also the uses in submit_batch()
         //       in renderer.rs.
         // TODO(gw): Maybe there's a way to make this stuff a bit
         //           more type-safe. Although, it will always need
         //           to be kept in sync with the GLSL code anyway.
 
         let data = match self {
             RenderTaskKind::Picture(ref task) => {
                 // Note: has to match `PICTURE_TYPE_*` in shaders
                 [
                     task.device_pixel_scale.0,
                     task.content_origin.x,
                     task.content_origin.y,
+                    0.0,
                 ]
             }
             RenderTaskKind::CacheMask(ref task) => {
                 [
                     task.device_pixel_scale.0,
                     task.actual_rect.origin.x,
                     task.actual_rect.origin.y,
+                    0.0,
                 ]
             }
             RenderTaskKind::ClipRegion(ref task) => {
                 [
                     task.device_pixel_scale.0,
                     0.0,
                     0.0,
+                    0.0,
                 ]
             }
             RenderTaskKind::VerticalBlur(ref task) |
             RenderTaskKind::HorizontalBlur(ref task) => {
                 [
                     task.blur_std_deviation,
                     task.blur_region.width as f32,
                     task.blur_region.height as f32,
+                    0.0,
                 ]
             }
             RenderTaskKind::Image(..) |
             RenderTaskKind::Cached(..) |
             RenderTaskKind::Readback(..) |
             RenderTaskKind::Scaling(..) |
             RenderTaskKind::Border(..) |
             RenderTaskKind::LineDecoration(..) |
             RenderTaskKind::Gradient(..) |
             RenderTaskKind::Blit(..) => {
-                [0.0; 3]
+                [0.0; 4]
             }
 
 
             RenderTaskKind::SvgFilter(ref task) => {
                 match task.info {
-                    SvgFilterInfo::Opacity(opacity) => [opacity, 0.0, 0.0],
-                    SvgFilterInfo::Offset(offset) => [offset.x, offset.y, 0.0],
-                    _ => [0.0; 3]
+                    SvgFilterInfo::Opacity(opacity) => [opacity, 0.0, 0.0, 0.0],
+                    SvgFilterInfo::Offset(offset) => [offset.x, offset.y, 0.0, 0.0],
+                    _ => [0.0; 4]
                 }
             }
 
             #[cfg(test)]
             RenderTaskKind::Test(..) => {
-                [0.0; 3]
+                [0.0; 4]
             }
         };
 
         RenderTaskData {
             data: [
                 target_rect.origin.x as f32,
                 target_rect.origin.y as f32,
                 target_rect.size.width as f32,
                 target_rect.size.height as f32,
-                target_index.0 as f32,
                 data[0],
                 data[1],
                 data[2],
+                data[3],
             ]
         }
     }
 
     pub fn write_gpu_blocks(
         &mut self,
         gpu_cache: &mut GpuCache,
     ) {
@@ -1416,58 +1397,38 @@ impl RenderTask {
             RenderTaskLocation::Static { .. } |
             RenderTaskLocation::CacheRequest { .. } |
             RenderTaskLocation::Unallocated { .. } => {
                 unreachable!();
             }
         }
     }
 
-    pub fn get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex) {
+    pub fn get_target_rect(&self) -> DeviceIntRect {
         match self.location {
             // Previously, we only added render tasks after the entire
             // primitive chain was determined visible. This meant that
             // we could assert any render task in the list was also
             // allocated (assigned to passes). Now, we add render
             // tasks earlier, and the picture they belong to may be
             // culled out later, so we can't assert that the task
             // has been allocated.
             // Render tasks that are created but not assigned to
             // passes consume a row in the render task texture, but
             // don't allocate any space in render targets nor
             // draw any pixels.
             // TODO(gw): Consider some kind of tag or other method
             //           to mark a task as unused explicitly. This
             //           would allow us to restore this debug check.
-            RenderTaskLocation::Dynamic { rect, .. } => {
-                (rect, RenderTargetIndex(0))
-            }
-            RenderTaskLocation::Unallocated { .. } => {
+            RenderTaskLocation::Dynamic { rect, .. } => rect,
+            RenderTaskLocation::Static { rect, .. } => rect,
+            RenderTaskLocation::CacheRequest { .. }
+            | RenderTaskLocation::Unallocated { .. } => {
                 panic!("bug: get_target_rect called before allocating");
             }
-            RenderTaskLocation::Static { rect, surface: StaticRenderTaskSurface::PictureCache { ref surface, .. } } => {
-                let layer = match surface {
-                    ResolvedSurfaceTexture::TextureCache { layer, .. } => *layer,
-                    ResolvedSurfaceTexture::Native { .. } => 0,
-                };
-
-                (
-                    rect,
-                    RenderTargetIndex(layer as usize),
-                )
-            }
-            RenderTaskLocation::Static { rect, surface: StaticRenderTaskSurface::TextureCache { layer, .. } } => {
-                (rect, RenderTargetIndex(layer as usize))
-            }
-            RenderTaskLocation::Static { rect, surface: StaticRenderTaskSurface::ReadOnly { .. } } => {
-                (rect, RenderTargetIndex(0))
-            }
-            RenderTaskLocation::CacheRequest { .. }  => {
-                panic!();
-            }
         }
     }
 
     pub fn target_kind(&self) -> RenderTargetKind {
         self.kind.target_kind()
     }
 
     #[cfg(feature = "debugger")]
@@ -1537,17 +1498,16 @@ impl RenderTask {
 
         pt.end_level();
         true
     }
 
     pub fn write_gpu_blocks(
         &mut self,
         target_rect: DeviceIntRect,
-        target_index: RenderTargetIndex,
         gpu_cache: &mut GpuCache,
     ) {
         profile_scope!("write_gpu_blocks");
 
         self.kind.write_gpu_blocks(gpu_cache);
 
         if self.cache_handle.is_some() {
             // The uv rect handle of cached render tasks is requested and set by the
@@ -1556,18 +1516,17 @@ impl RenderTask {
         }
 
         if let Some(mut request) = gpu_cache.request(&mut self.uv_rect_handle) {
             let p0 = target_rect.min().to_f32();
             let p1 = target_rect.max().to_f32();
             let image_source = ImageSource {
                 p0,
                 p1,
-                texture_layer: target_index.0 as f32,
-                user_data: [0.0; 3],
+                user_data: [0.0; 4],
                 uv_rect_kind: self.uv_rect_kind,
             };
             image_source.write_gpu_blocks(&mut request);
         }
     }
 
     /// Called by the render task cache.
     ///
--- a/gfx/wr/webrender/src/render_task_cache.rs
+++ b/gfx/wr/webrender/src/render_task_cache.rs
@@ -55,17 +55,17 @@ pub struct RenderTaskCacheKey {
     pub size: DeviceIntSize,
     pub kind: RenderTaskCacheKeyKind,
 }
 
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct RenderTaskCacheEntry {
-    user_data: Option<[f32; 3]>,
+    user_data: Option<[f32; 4]>,
     target_kind: RenderTargetKind,
     is_opaque: bool,
     pub handle: TextureCacheHandle,
     /// If a render task was generated for this cache entry on _this_ frame,
     /// we need to track the task id here. This allows us to hook it up as
     /// a dependency of any parent tasks that make a reqiest from the render
     /// task cache.
     pub render_task_id: Option<RenderTaskId>,
@@ -171,51 +171,49 @@ impl RenderTaskCache {
 
         // Allocate space in the texture cache, but don't supply
         // and CPU-side data to be uploaded.
         texture_cache.update(
             &mut entry.handle,
             descriptor,
             TextureFilter::Linear,
             None,
-            entry.user_data.unwrap_or([0.0; 3]),
+            entry.user_data.unwrap_or([0.0; 4]),
             DirtyRect::All,
             gpu_cache,
             None,
             render_task.uv_rect_kind(),
             Eviction::Auto,
             TargetShader::Default,
         );
 
         // Get the allocation details in the texture cache, and store
-        // this in the render task. The renderer will draw this
-        // task into the appropriate layer and rect of the texture
-        // cache on this frame.
-        let (texture_id, texture_layer, uv_rect, _, _, _) =
+        // this in the render task. The renderer will draw this task
+        // into the appropriate rect of the texture cache on this frame.
+        let (texture_id, uv_rect, _, _, _) =
             texture_cache.get_cache_location(&entry.handle);
 
         let surface = StaticRenderTaskSurface::TextureCache {
             texture: texture_id,
-            layer: texture_layer,
             target_kind,
         };
 
         render_task.location = RenderTaskLocation::Static {
             surface,
             rect: uv_rect.to_i32(),
         };
     }
 
     pub fn request_render_task<F>(
         &mut self,
         key: RenderTaskCacheKey,
         texture_cache: &mut TextureCache,
         gpu_cache: &mut GpuCache,
         rg_builder: &mut RenderTaskGraphBuilder,
-        user_data: Option<[f32; 3]>,
+        user_data: Option<[f32; 4]>,
         is_opaque: bool,
         parent: RenderTaskParent,
         surfaces: &[SurfaceInfo],
         f: F,
     ) -> Result<RenderTaskId, ()>
     where
         F: FnOnce(&mut RenderTaskGraphBuilder) -> Result<RenderTaskId, ()>,
     {
--- a/gfx/wr/webrender/src/render_task_graph.rs
+++ b/gfx/wr/webrender/src/render_task_graph.rs
@@ -1,33 +1,52 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
 //! This module contains the render task graph.
 //!
 //! Code associated with creating specific render tasks is in the render_task
 //! module.
 
+use api::units::*;
 use api::ImageFormat;
-use crate::frame_graph::{FrameGraph, FrameGraphBuilder, Pass};
-use crate::internal_types::{CacheTextureId, FastHashMap};
+use crate::gpu_cache::{GpuCache, GpuCacheAddress};
+use crate::internal_types::{TextureSource, CacheTextureId, FastHashMap, FastHashSet};
+use crate::render_backend::FrameId;
+use crate::render_task::{StaticRenderTaskSurface, RenderTaskLocation, RenderTask};
+use crate::render_target::RenderTargetKind;
+use crate::render_task::{RenderTaskData, RenderTaskKind};
+use crate::resource_cache::ResourceCache;
+use crate::texture_pack::GuillotineAllocator;
+use crate::prim_store::DeferredResolve;
+use crate::image_source::{resolve_image, resolve_cached_render_task};
+use crate::util::VecHelper;
+use smallvec::SmallVec;
+use std::mem;
+
 use crate::render_target::{RenderTargetList, ColorRenderTarget};
 use crate::render_target::{PictureCacheTarget, TextureCacheRenderTarget, AlphaRenderTarget};
-use crate::render_task::RenderTask;
 use crate::util::Allocation;
 use std::{usize, f32};
 
-// TODO(gw): To reduce the size of the initial inegration patch, typedef the
-//           old task graph and builder to the new frame graph / builder, which
-//           are API compatible. In future, use the new types directly and adjust
-//           the API.
-pub type RenderTaskGraphBuilder = FrameGraphBuilder;
-pub type RenderTaskGraph = FrameGraph;
+/// According to apitrace, textures larger than 2048 break fast clear
+/// optimizations on some intel drivers. We sometimes need to go larger, but
+/// we try to avoid it.
+const MAX_SHARED_SURFACE_SIZE: i32 = 2048;
+
+/// If we ever need a larger texture than the ideal, we better round it up to a
+/// reasonable number in order to have a bit of leeway in case the size of this
+/// this target is changing each frame.
+const TEXTURE_DIMENSION_MASK: i32 = 0xFF;
 
 /// Allows initializing a render task directly into the render task buffer.
 ///
 /// See utils::VecHelpers. RenderTask is fairly large so avoiding the move when
-/// pushing into the vector can save a lot of exensive memcpys on pages with many
+/// pushing into the vector can save a lot of expensive memcpys on pages with many
 /// render tasks.
 pub struct RenderTaskAllocation<'a> {
     pub alloc: Allocation<'a, RenderTask>,
 }
 
 impl<'l> RenderTaskAllocation<'l> {
     #[inline(always)]
     pub fn init(self, value: RenderTask) -> RenderTaskId {
@@ -46,28 +65,761 @@ pub struct RenderTaskId {
 }
 
 impl RenderTaskId {
     pub const INVALID: RenderTaskId = RenderTaskId {
         index: u16::MAX,
     };
 }
 
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)]
+pub struct PassId(usize);
+
+impl PassId {
+    pub const MIN: PassId = PassId(0);
+    pub const MAX: PassId = PassId(!0);
+}
+
+/// An internal representation of a dynamic surface that tasks can be
+/// allocated into. Maintains some extra metadata about each surface
+/// during the graph build.
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+struct Surface {
+    /// Whether this is a color or alpha render target
+    kind: RenderTargetKind,
+    /// Allocator for this surface texture
+    allocator: GuillotineAllocator,
+    /// We can only allocate into this for reuse if it's a shared surface
+    is_shared: bool,
+}
+
+impl Surface {
+    /// Allocate a rect within a shared surfce. Returns None if the
+    /// format doesn't match, or allocation fails.
+    fn alloc_rect(
+        &mut self,
+        size: DeviceIntSize,
+        kind: RenderTargetKind,
+        is_shared: bool,
+    ) -> Option<DeviceIntPoint> {
+        if self.kind == kind && self.is_shared == is_shared {
+            self.allocator
+                .allocate(&size)
+                .map(|(_slice, origin)| origin)
+        } else {
+            None
+        }
+    }
+}
+
+/// A sub-pass can draw to either a dynamic (temporary render target) surface,
+/// or a persistent surface (texture or picture cache).
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+#[derive(Debug)]
+pub enum SubPassSurface {
+    /// A temporary (intermediate) surface.
+    Dynamic {
+        /// The renderer texture id
+        texture_id: CacheTextureId,
+        /// Color / alpha render target
+        target_kind: RenderTargetKind,
+        /// The rectangle occupied by tasks in this surface. Used as a clear
+        /// optimization on some GPUs.
+        used_rect: DeviceIntRect,
+    },
+    Persistent {
+        /// Reference to the texture or picture cache surface being drawn to.
+        surface: StaticRenderTaskSurface,
+    },
+}
+
+/// A subpass is a specific render target, and a list of tasks to draw to it.
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct SubPass {
+    /// The surface this subpass draws to
+    pub surface: SubPassSurface,
+    /// The tasks assigned to this subpass.
+    pub task_ids: Vec<RenderTaskId>,
+}
+
+/// A pass expresses dependencies between tasks. Each pass consists of a number
+/// of subpasses.
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct Pass {
+    /// The tasks assigned to this render pass
+    pub task_ids: Vec<RenderTaskId>,
+    /// The subpasses that make up this dependency pass
+    pub sub_passes: Vec<SubPass>,
+    /// A list of intermediate surfaces that can be invalidated after
+    /// this pass completes.
+    pub textures_to_invalidate: Vec<CacheTextureId>,
+}
+
+/// The RenderTaskGraph is the immutable representation of the render task graph. It is
+/// built by the RenderTaskGraphBuilder, and is constructed once per frame.
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct RenderTaskGraph {
+    /// List of tasks added to the graph
+    pub tasks: Vec<RenderTask>,
+
+    /// The passes that were created, based on dependencies between tasks
+    pub passes: Vec<Pass>,
+
+    /// Current frame id, used for debug validation
+    frame_id: FrameId,
+
+    /// GPU specific data for each task that is made available to shaders
+    pub task_data: Vec<RenderTaskData>,
+
+    /// Total number of intermediate surfaces that will be drawn to, used for test validation.
+    #[cfg(test)]
+    surface_count: usize,
+
+    /// Total number of real allocated textures that will be drawn to, used for test validation.
+    #[cfg(test)]
+    unique_surfaces: FastHashSet<CacheTextureId>,
+}
+
+/// The persistent interface that is used during frame building to construct the
+/// frame graph.
+pub struct RenderTaskGraphBuilder {
+    /// List of tasks added to the builder
+    tasks: Vec<RenderTask>,
+
+    /// List of task roots
+    roots: FastHashSet<RenderTaskId>,
+
+    /// Input dependencies where the input is a persistent target,
+    /// rather than a specific render task id. Useful for expressing
+    /// when a task relies on a readback of a surface that is partially
+    /// drawn to.
+    target_inputs: Vec<(RenderTaskId, StaticRenderTaskSurface)>,
+
+    /// Current frame id, used for debug validation
+    frame_id: FrameId,
+
+    /// A list of texture surfaces that can be freed at the end of a pass. Retained
+    /// here to reduce heap allocations.
+    textures_to_free: FastHashSet<CacheTextureId>,
+
+    // Keep a map of `texture_id` to metadata about surfaces that are currently
+    // borrowed from the render target pool.
+    active_surfaces: FastHashMap<CacheTextureId, Surface>,
+
+    /// A temporary buffer used by assign_free_pass. Kept here to avoid heap reallocs
+    child_task_buffer: Vec<RenderTaskId>,
+}
+
+impl RenderTaskGraphBuilder {
+    /// Construct a new graph builder. Typically constructed once and maintained
+    /// over many frames, to avoid extra heap allocations where possible.
+    pub fn new() -> Self {
+        RenderTaskGraphBuilder {
+            tasks: Vec::new(),
+            roots: FastHashSet::default(),
+            target_inputs: Vec::new(),
+            frame_id: FrameId::INVALID,
+            textures_to_free: FastHashSet::default(),
+            active_surfaces: FastHashMap::default(),
+            child_task_buffer: Vec::new(),
+        }
+    }
+
+    pub fn frame_id(&self) -> FrameId {
+        self.frame_id
+    }
+
+    /// Begin a new frame
+    pub fn begin_frame(&mut self, frame_id: FrameId) {
+        self.frame_id = frame_id;
+        self.roots.clear();
+    }
+
+    /// Get immutable access to a task
+    // TODO(gw): There's only a couple of places that existing code needs to access
+    //           a task during the building step. Perhaps we can remove this?
+    pub fn get_task(
+        &self,
+        task_id: RenderTaskId,
+    ) -> &RenderTask {
+        &self.tasks[task_id.index as usize]
+    }
+
+    /// Get mutable access to a task
+    // TODO(gw): There's only a couple of places that existing code needs to access
+    //           a task during the building step. Perhaps we can remove this?
+    pub fn get_task_mut(
+        &mut self,
+        task_id: RenderTaskId,
+    ) -> &mut RenderTask {
+        &mut self.tasks[task_id.index as usize]
+    }
+
+    /// Add a new task to the graph.
+    pub fn add(&mut self) -> RenderTaskAllocation {
+        // Assume every task is a root to start with
+        self.roots.insert(
+            RenderTaskId { index: self.tasks.len() as u16 }
+        );
+
+        RenderTaskAllocation {
+            alloc: self.tasks.alloc(),
+        }
+    }
+
+    /// Express a dependency, such that `task_id` depends on `input` as a texture source.
+    pub fn add_dependency(
+        &mut self,
+        task_id: RenderTaskId,
+        input: RenderTaskId,
+    ) {
+        self.tasks[task_id.index as usize].children.push(input);
+
+        // Once a task is an input, it's no longer a root
+        self.roots.remove(&input);
+    }
+
+    /// Register a persistent surface as an input dependency of a task (readback).
+    pub fn add_target_input(
+        &mut self,
+        task_id: RenderTaskId,
+        target: StaticRenderTaskSurface,
+    ) {
+        self.target_inputs.push((task_id, target));
+    }
+
+    /// End the graph building phase and produce the immutable task graph for this frame
+    pub fn end_frame(
+        &mut self,
+        resource_cache: &mut ResourceCache,
+        gpu_cache: &mut GpuCache,
+        deferred_resolves: &mut Vec<DeferredResolve>,
+    ) -> RenderTaskGraph {
+        // Copy the render tasks over to the immutable graph output
+        let task_count = self.tasks.len();
+        let tasks = mem::replace(
+            &mut self.tasks,
+            Vec::with_capacity(task_count),
+        );
+
+        let mut graph = RenderTaskGraph {
+            tasks,
+            passes: Vec::new(),
+            task_data: Vec::with_capacity(task_count),
+            frame_id: self.frame_id,
+            #[cfg(test)]
+            surface_count: 0,
+            #[cfg(test)]
+            unique_surfaces: FastHashSet::default(),
+        };
+
+        // Handle late mapping of dependencies on a specific persistent target.
+        // NOTE: This functionality isn't used by current callers of the frame graph, but
+        //       will be used in future (for example, to express readbacks of partially
+        //       rendered picture tiles for mix-blend-mode etc).
+        if !self.target_inputs.is_empty() {
+            // Create a mapping from persistent surface id -> render task root (used below):
+            let mut roots = FastHashMap::default();
+            roots.reserve(self.roots.len());
+            for root_id in &self.roots {
+                let task = &graph.tasks[root_id.index as usize];
+                match task.location {
+                    RenderTaskLocation::Static { ref surface, .. } => {
+                        // We should never encounter a graph where the same surface is a
+                        // render root more than one.
+                        assert!(!roots.contains_key(surface));
+                        roots.insert(surface.clone(), *root_id);
+                    }
+                    RenderTaskLocation::Dynamic { .. }
+                    | RenderTaskLocation::CacheRequest { .. }
+                    | RenderTaskLocation::Unallocated { .. } => {
+                        // Intermediate surfaces can't be render roots, they should always
+                        // be a dependency of a render root.
+                        panic!("bug: invalid root");
+                    }
+                }
+            }
+            assert_eq!(roots.len(), self.roots.len());
+
+            // Now resolve those dependencies on persistent targets and add them
+            // as a render task dependency.
+            for (task_id, target_id) in self.target_inputs.drain(..) {
+                match roots.get(&target_id) {
+                    Some(root_task_id) => {
+                        graph.tasks[task_id.index as usize].children.push(*root_task_id);
+                        self.roots.remove(root_task_id);
+                    }
+                    None => {
+                        println!("WARN: {:?} depends on root {:?} but it has no tasks!",
+                            task_id,
+                            target_id,
+                        );
+                    }
+                }
+            }
+        }
+
+        // Two traversals of the graph are required. The first pass determines how many passes
+        // are required, and assigns render tasks a pass to be drawn on. The second pass determines
+        // when the last time a render task is used as an input, and assigns what pass the surface
+        // backing that render task can be freed (the surface is then returned to the render target
+        // pool and may be aliased / reused during subsequent passes).
+
+        let mut pass_count = 0;
+
+        // Traverse each root, and assign `render_on` for each task and count number of required passes
+        for root_id in &self.roots {
+            assign_render_pass(
+                *root_id,
+                PassId(0),
+                &mut graph,
+                &mut pass_count,
+            );
+        }
+
+        // Determine which pass each task can be freed on, which depends on which is
+        // the last task that has this as an input.
+        for i in 0 .. graph.tasks.len() {
+            let task_id = RenderTaskId { index: i as u16 };
+            assign_free_pass(
+                task_id,
+                &mut self.child_task_buffer,
+                &mut graph,
+            );
+        }
+
+        // Construct passes array for tasks to be assigned to below
+        for _ in 0 .. pass_count+1 {
+            graph.passes.push(Pass {
+                task_ids: Vec::new(),
+                sub_passes: Vec::new(),
+                textures_to_invalidate: Vec::new(),
+            });
+        }
+
+        // Assign tasks to each pass based on their `render_on` attribute
+        for (index, task) in graph.tasks.iter().enumerate() {
+            if task.kind.is_a_rendering_operation() {
+                let id = RenderTaskId { index: index as u16 };
+                graph.passes[task.render_on.0].task_ids.push(id);
+            }
+        }
+
+        // At this point, tasks are assigned to each dependency pass. Now we
+        // can go through each pass and create sub-passes, assigning each task
+        // to a target and destination rect.
+        assert!(self.active_surfaces.is_empty());
+
+        for (pass_id, pass) in graph.passes.iter_mut().enumerate().rev() {
+            assert!(self.textures_to_free.is_empty());
+
+            for task_id in &pass.task_ids {
+                let task = &mut graph.tasks[task_id.index as usize];
+
+                match task.location {
+                    RenderTaskLocation::Unallocated { size } => {
+                        let mut location = None;
+                        let kind = task.kind.target_kind();
+
+                        // Allow this render task to use a shared surface target if it
+                        // is freed straight after this pass. Tasks that must remain
+                        // allocated for inputs on subsequent passes are always assigned
+                        // to a standalone surface, to simplify lifetime management of
+                        // render targets.
+
+                        let can_use_shared_surface =
+                            task.render_on == PassId(task.free_after.0 + 1);
+
+                        if can_use_shared_surface {
+                            // If we can use a shared surface, step through the existing shared
+                            // surfaces for this subpass, and see if we can allocate the task
+                            // to one of these targets.
+                            for sub_pass in &mut pass.sub_passes {
+                                if let SubPassSurface::Dynamic { texture_id, ref mut used_rect, .. } = sub_pass.surface {
+                                    let surface = self.active_surfaces.get_mut(&texture_id).unwrap();
+                                    if let Some(p) = surface.alloc_rect(size, kind, true) {
+                                        location = Some((texture_id, p));
+                                        *used_rect = used_rect.union(&DeviceIntRect::new(p, size));
+                                        sub_pass.task_ids.push(*task_id);
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+
+                        if location.is_none() {
+                            // If it wasn't possible to allocate the task to a shared surface, get a new
+                            // render target from the resource cache pool/
+
+                            // If this is a really large task, don't bother allocating it as a potential
+                            // shared surface for other tasks.
+
+                            let can_use_shared_surface = can_use_shared_surface &&
+                                size.width <= MAX_SHARED_SURFACE_SIZE &&
+                                size.height <= MAX_SHARED_SURFACE_SIZE;
+
+                            let surface_size = if can_use_shared_surface {
+                                DeviceIntSize::new(
+                                    MAX_SHARED_SURFACE_SIZE,
+                                    MAX_SHARED_SURFACE_SIZE,
+                                )
+                            } else {
+                                // Round up size here to avoid constant re-allocs during resizing
+                                DeviceIntSize::new(
+                                    (size.width + TEXTURE_DIMENSION_MASK) & !TEXTURE_DIMENSION_MASK,
+                                    (size.height + TEXTURE_DIMENSION_MASK) & !TEXTURE_DIMENSION_MASK,
+                                )
+                            };
+
+                            let format = match kind {
+                                RenderTargetKind::Color => ImageFormat::RGBA8,
+                                RenderTargetKind::Alpha => ImageFormat::R8,
+                            };
+
+                            // Get render target of appropriate size and format from resource cache
+                            let texture_id = resource_cache.get_or_create_render_target_from_pool(
+                                surface_size,
+                                format,
+                            );
+
+                            // Allocate metadata we need about this surface while it's active
+                            let mut surface = Surface {
+                                kind,
+                                allocator: GuillotineAllocator::new(Some(surface_size)),
+                                is_shared: can_use_shared_surface,
+                            };
+
+                            // Allocation of the task must fit in this new surface!
+                            let p = surface.alloc_rect(
+                                size,
+                                kind,
+                                can_use_shared_surface,
+                            ).expect("bug: alloc must succeed!");
+
+                            location = Some((texture_id, p));
+
+                            // Store the metadata about this newly active surface. We should never
+                            // get a target surface with the same texture_id as a currently active surface.
+                            let _prev_surface = self.active_surfaces.insert(texture_id, surface);
+                            assert!(_prev_surface.is_none());
+
+                            // Store some information about surface allocations if in test mode
+                            #[cfg(test)]
+                            {
+                                graph.surface_count += 1;
+                                graph.unique_surfaces.insert(texture_id);
+                            }
+
+                            // Add the target as a new subpass for this render pass.
+                            pass.sub_passes.push(SubPass {
+                                surface: SubPassSurface::Dynamic {
+                                    texture_id,
+                                    target_kind: kind,
+                                    used_rect: DeviceIntRect::new(p, size),
+                                },
+                                task_ids: vec![*task_id],
+                            });
+                        }
+
+                        // By now, we must have allocated a surface and rect for this task, so assign it!
+                        assert!(location.is_some());
+                        task.location = RenderTaskLocation::Dynamic {
+                            texture_id: location.unwrap().0,
+                            rect: DeviceIntRect::new(location.unwrap().1, size),
+                        };
+                    }
+                    RenderTaskLocation::Static { ref surface, .. } => {
+                        // No need to allocate for this surface, since it's a persistent
+                        // target. Instead, just create a new sub-pass for it.
+                        pass.sub_passes.push(SubPass {
+                            surface: SubPassSurface::Persistent {
+                                surface: surface.clone(),
+                            },
+                            task_ids: vec![*task_id],
+                        });
+                    }
+                    RenderTaskLocation::CacheRequest { .. } => {
+                        // No need to allocate nor to create a sub-path for read-only locations.
+                    }
+                    RenderTaskLocation::Dynamic { .. } => {
+                        // Dynamic tasks shouldn't be allocated by this point
+                        panic!("bug: encountered an already allocated task");
+                    }
+                }
+
+                // Return the shared surfaces from this pass
+                let task = &graph.tasks[task_id.index as usize];
+                for child_id in &task.children {
+                    let child_task = &graph.tasks[child_id.index as usize];
+                    match child_task.location {
+                        RenderTaskLocation::Unallocated { .. } => panic!("bug: must be allocated"),
+                        RenderTaskLocation::Dynamic { texture_id, .. } => {
+                            // If this task can be freed after this pass, include it in the
+                            // unique set of textures to be returned to the render target pool below.
+                            if child_task.free_after == PassId(pass_id) {
+                                self.textures_to_free.insert(texture_id);
+                            }
+                        }
+                        RenderTaskLocation::Static { .. } => {}
+                        RenderTaskLocation::CacheRequest { .. } => {}
+                    }
+                }
+            }
+
+            // Return no longer used textures to the pool, so that they can be reused / aliased
+            // by later passes.
+            for texture_id in self.textures_to_free.drain() {
+                resource_cache.return_render_target_to_pool(texture_id);
+                self.active_surfaces.remove(&texture_id).unwrap();
+                pass.textures_to_invalidate.push(texture_id);
+            }
+        }
+
+        // By now, all surfaces that were borrowed from the render target pool must
+        // be returned to the resource cache, or we are leaking intermediate surfaces!
+        assert!(self.active_surfaces.is_empty());
+
+        // Each task is now allocated to a surface and target rect. Write that to the
+        // GPU blocks and task_data. After this point, the graph is returned and is
+        // considered to be immutable for the rest of the frame building process.
+
+        for task in &mut graph.tasks {
+            // First check whether the render task texture and uv rects are managed
+            // externally. This is the case for image tasks and cached tasks. In both
+            // cases it results in a finding the information in the texture cache.
+            let cache_item = if let Some(ref cache_handle) = task.cache_handle {
+                Some(resolve_cached_render_task(
+                    cache_handle,
+                    resource_cache,
+                ))
+            } else if let RenderTaskKind::Image(request) = &task.kind {
+                Some(resolve_image(
+                    *request,
+                    resource_cache,
+                    gpu_cache,
+                    deferred_resolves,
+                ))
+            } else {
+                // General case (non-cached non-image tasks).
+                None
+            };
+
+            if let Some(cache_item) = cache_item {
+                // Update the render task even if the item is invalid.
+                // We'll handle it later and it's easier to not have to
+                // deal with unexpected location variants like
+                // RenderTaskLocation::CacheRequest when we do.
+                let source = cache_item.texture_id;
+                task.uv_rect_handle = cache_item.uv_rect_handle;
+                task.location = RenderTaskLocation::Static {
+                    surface: StaticRenderTaskSurface::ReadOnly { source },
+                    rect: cache_item.uv_rect,
+                };
+            }
+            // Give the render task an opportunity to add any
+            // information to the GPU cache, if appropriate.
+            let target_rect = task.get_target_rect();
+
+            task.write_gpu_blocks(
+                target_rect,
+                gpu_cache,
+            );
+
+            graph.task_data.push(
+                task.kind.write_task_data(target_rect)
+            );
+        }
+
+        graph
+    }
+}
+
+impl RenderTaskGraph {
+    /// Print the render task graph to console
+    #[allow(dead_code)]
+    pub fn print(
+        &self,
+    ) {
+        println!("-- RenderTaskGraph --");
+
+        for (i, task) in self.tasks.iter().enumerate() {
+            println!("Task {}: render_on={} free_after={} {:?}",
+                i,
+                task.render_on.0,
+                task.free_after.0,
+                task.kind.as_str(),
+            );
+        }
+
+        for (p, pass) in self.passes.iter().enumerate() {
+            println!("Pass {}:", p);
+
+            for (s, sub_pass) in pass.sub_passes.iter().enumerate() {
+                println!("\tSubPass {}: {:?}",
+                    s,
+                    sub_pass.surface,
+                );
+
+                for task_id in &sub_pass.task_ids {
+                    println!("\t\tTask {:?}", task_id.index);
+                }
+            }
+        }
+    }
+
+    pub fn resolve_location(
+        &self,
+        task_id: impl Into<Option<RenderTaskId>>,
+        gpu_cache: &GpuCache,
+    ) -> Option<(GpuCacheAddress, TextureSource)> {
+        self.resolve_impl(task_id.into()?, gpu_cache)
+    }
+
+    fn resolve_impl(
+        &self,
+        task_id: RenderTaskId,
+        gpu_cache: &GpuCache,
+    ) -> Option<(GpuCacheAddress, TextureSource)> {
+        let task = &self[task_id];
+        let texture_source = task.get_texture_source();
+
+        if let TextureSource::Invalid = texture_source {
+            return None;
+        }
+
+        let uv_address = task.get_texture_address(gpu_cache);
+
+        Some((uv_address, texture_source))
+    }
+
+
+    /// Return the surface and texture counts, used for testing
+    #[cfg(test)]
+    pub fn surface_counts(&self) -> (usize, usize) {
+        (self.surface_count, self.unique_surfaces.len())
+    }
+
+    /// Return current frame id, used for validation
+    #[cfg(debug_assertions)]
+    pub fn frame_id(&self) -> FrameId {
+        self.frame_id
+    }
+}
+
+/// Batching uses index access to read information about tasks
+impl std::ops::Index<RenderTaskId> for RenderTaskGraph {
+    type Output = RenderTask;
+    fn index(&self, id: RenderTaskId) -> &RenderTask {
+        &self.tasks[id.index as usize]
+    }
+}
+
+/// Recursive helper to assign pass that a task should render on
+fn assign_render_pass(
+    id: RenderTaskId,
+    pass: PassId,
+    graph: &mut RenderTaskGraph,
+    pass_count: &mut usize,
+) {
+    let task = &mut graph.tasks[id.index as usize];
+
+    // No point in recursing into paths in the graph if this task already
+    // has been set to draw after this pass.
+    if task.render_on > pass {
+        return;
+    }
+
+    let next_pass = if task.kind.is_a_rendering_operation() {
+        // Keep count of number of passes needed
+        *pass_count = pass.0.max(*pass_count);
+        PassId(pass.0 + 1)
+    } else {
+        // If the node is not a rendering operation, it doesn't create a
+        // render pass, so we don't increment the pass count. 
+        // For now we expect non-rendering nodes to be leafs of the graph.
+        // We don't strictly depend on it but it simplifies the mental model.
+        debug_assert!(task.children.is_empty());
+        pass
+    };
+
+    // A task should be rendered on the earliest pass in the dependency
+    // graph that it's required. Using max here ensures the correct value
+    // in the presence of multiple paths to this task from the root(s).
+    task.render_on = task.render_on.max(pass);
+
+    // TODO(gw): Work around the borrowck - maybe we could structure the dependencies
+    //           storage better, to avoid this?
+    let mut child_task_ids: SmallVec<[RenderTaskId; 8]> = SmallVec::new();
+    child_task_ids.extend_from_slice(&task.children);
+
+    for child_id in child_task_ids {
+        assign_render_pass(
+            child_id,
+            next_pass,
+            graph,
+            pass_count,
+        );
+    }
+}
+
+fn assign_free_pass(
+    id: RenderTaskId,
+    child_task_buffer: &mut Vec<RenderTaskId>,
+    graph: &mut RenderTaskGraph,
+) {
+    let task = &graph.tasks[id.index as usize];
+    let render_on = task.render_on;
+    debug_assert!(child_task_buffer.is_empty());
+
+    // TODO(gw): Work around the borrowck - maybe we could structure the dependencies
+    //           storage better, to avoid this?
+    child_task_buffer.extend_from_slice(&task.children);
+
+    for child_id in child_task_buffer.drain(..) {
+        let child_task = &mut graph.tasks[child_id.index as usize];
+
+        // Each dynamic child task can free its backing surface after the last
+        // task that references it as an input. Using min here ensures the
+        // safe time to free this surface in the presence of multiple paths
+        // to this task from the root(s).
+        match child_task.location {
+            RenderTaskLocation::CacheRequest { .. } => {}
+            RenderTaskLocation::Static { .. } => {
+                // never get freed anyway, so can leave untouched
+                // (could validate that they remain at PassId::MIN)
+            }
+            RenderTaskLocation::Unallocated { .. } => {
+                child_task.free_after = child_task.free_after.min(render_on);
+            }
+            RenderTaskLocation::Dynamic { .. } => {
+                panic!("bug: should not be allocated yet");
+            }
+        }
+    }
+}
+
 /// A render pass represents a set of rendering operations that don't depend on one
 /// another.
 ///
 /// A render pass can have several render targets if there wasn't enough space in one
 /// target to do all of the rendering for that pass. See `RenderTargetList`.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct RenderPass {
     /// The subpasses that describe targets being rendered to in this pass
     pub alpha: RenderTargetList<AlphaRenderTarget>,
     pub color: RenderTargetList<ColorRenderTarget>,
-    pub texture_cache: FastHashMap<(CacheTextureId, usize), TextureCacheRenderTarget>,
+    pub texture_cache: FastHashMap<CacheTextureId, TextureCacheRenderTarget>,
     pub picture_cache: Vec<PictureCacheTarget>,
     /// The set of tasks to be performed in this pass, as indices into the
     /// `RenderTaskGraph`.
     pub tasks: Vec<RenderTaskId>,
     pub textures_to_invalidate: Vec<CacheTextureId>,
 }
 
 impl RenderPass {
@@ -264,8 +1016,272 @@ fn dump_task_dependency_link(
             path().move_to(x1, y1)
                 .cubic_bezier_to(ctrl1_x, y1, ctrl1_x, ctrl_y, mid_x, ctrl_y)
                 .cubic_bezier_to(ctrl2_x, ctrl_y, ctrl2_x, y2, x2, y2)
                 .fill(Fill::None)
                 .stroke(Stroke::Color(rgb(100, 100, 100), 3.0))
         ).unwrap();
     }
 }
+
+/// Construct a picture cache render task location for testing
+#[cfg(test)]
+fn pc_target(
+    surface_id: u64,
+    tile_x: i32,
+    tile_y: i32,
+) -> RenderTaskLocation {
+    use crate::{
+        composite::{NativeSurfaceId, NativeTileId},
+        picture::ResolvedSurfaceTexture,
+    };
+
+    let width = 512;
+    let height = 512;
+
+    RenderTaskLocation::Static {
+        surface: StaticRenderTaskSurface::PictureCache {
+            surface: ResolvedSurfaceTexture::Native {
+                id: NativeTileId {
+                    surface_id: NativeSurfaceId(surface_id),
+                    x: tile_x,
+                    y: tile_y,
+                },
+                size: DeviceIntSize::new(width, height),
+            },
+        },
+        rect: DeviceIntSize::new(width, height).into(),
+    }
+}
+
+#[cfg(test)]
+impl RenderTaskGraphBuilder {
+    fn test_expect(
+        mut self,
+        pass_count: usize,
+        total_surface_count: usize,
+        unique_surfaces: &[(i32, i32, ImageFormat)],
+    ) {
+        use crate::render_backend::FrameStamp;
+        use api::{DocumentId, IdNamespace};
+
+        let mut rc = ResourceCache::new_for_testing();
+        let mut gc =  GpuCache::new();
+
+        let mut frame_stamp = FrameStamp::first(DocumentId::new(IdNamespace(1), 1));
+        frame_stamp.advance();
+        gc.prepare_for_frames();
+        gc.begin_frame(frame_stamp);
+
+        let g = self.end_frame(&mut rc, &mut gc, &mut Vec::new());
+        g.print();
+
+        assert_eq!(g.passes.len(), pass_count);
+        assert_eq!(g.surface_counts(), (total_surface_count, unique_surfaces.len()));
+
+        rc.validate_surfaces(unique_surfaces);
+    }
+}
+
+/// Construct a testing render task with given location
+#[cfg(test)]
+fn task_location(location: RenderTaskLocation) -> RenderTask {
+    RenderTask::new_test(
+        location,
+        RenderTargetKind::Color,
+    )
+}
+
+/// Construct a dynamic render task location for testing
+#[cfg(test)]
+fn task_dynamic(size: i32) -> RenderTask {
+    RenderTask::new_test(
+        RenderTaskLocation::Unallocated { size: DeviceIntSize::new(size, size) },
+        RenderTargetKind::Color,
+    )
+}
+
+#[test]
+fn fg_test_1() {
+    // Test that a root target can be used as an input for readbacks
+    // This functionality isn't currently used, but will be in future.
+
+    let mut gb = RenderTaskGraphBuilder::new();
+
+    let root_target = pc_target(0, 0, 0);
+
+    let root = gb.add().init(task_location(root_target.clone()));
+
+    let readback = gb.add().init(task_dynamic(100));
+    gb.add_dependency(readback, root);
+
+    let mix_blend_content = gb.add().init(task_dynamic(50));
+
+    let content = gb.add().init(task_location(root_target));
+    gb.add_dependency(content, readback);
+    gb.add_dependency(content, mix_blend_content);
+
+    gb.test_expect(3, 1, &[
+        (2048, 2048, ImageFormat::RGBA8),
+    ]);
+}
+
+#[test]
+fn fg_test_2() {
+    // Test that texture cache tasks can be added and scheduled correctly as inputs
+    // to picture cache tasks. Ensure that no dynamic surfaces are allocated from the
+    // target pool in this case.
+
+    let mut gb = RenderTaskGraphBuilder::new();
+
+    let pc_root = gb.add().init(task_location(pc_target(0, 0, 0)));
+
+    let tc_0 = StaticRenderTaskSurface::TextureCache {
+        texture: CacheTextureId(0),
+        target_kind: RenderTargetKind::Color,
+    };
+
+    let tc_1 = StaticRenderTaskSurface::TextureCache {
+        texture: CacheTextureId(1),
+        target_kind: RenderTargetKind::Color,
+    };
+
+    gb.add_target_input(
+        pc_root,
+        tc_0.clone(),
+    );
+
+    gb.add_target_input(
+        pc_root,
+        tc_1.clone(),
+    );
+
+    gb.add().init(
+        task_location(RenderTaskLocation::Static { surface: tc_0.clone(), rect: DeviceIntSize::new(128, 128).into() }),
+    );
+
+    gb.add().init(
+        task_location(RenderTaskLocation::Static { surface: tc_1.clone(), rect: DeviceIntSize::new(128, 128).into() }),
+    );
+
+    gb.test_expect(2, 0, &[]);
+}
+
+#[test]
+fn fg_test_3() {
+    // Test that small targets are allocated in a shared surface, and that large
+    // tasks are allocated in a rounded up texture size.
+
+    let mut gb = RenderTaskGraphBuilder::new();
+
+    let pc_root = gb.add().init(task_location(pc_target(0, 0, 0)));
+
+    let child_pic_0 = gb.add().init(task_dynamic(128));
+    let child_pic_1 = gb.add().init(task_dynamic(3000));
+
+    gb.add_dependency(pc_root, child_pic_0);
+    gb.add_dependency(pc_root, child_pic_1);
+
+    gb.test_expect(2, 2, &[
+        (2048, 2048, ImageFormat::RGBA8),
+        (3072, 3072, ImageFormat::RGBA8),
+    ]);
+}
+
+#[test]
+fn fg_test_4() {
+    // Test that for a simple dependency chain of tasks, that render
+    // target surfaces are aliased and reused between passes where possible.
+
+    let mut gb = RenderTaskGraphBuilder::new();
+
+    let pc_root = gb.add().init(task_location(pc_target(0, 0, 0)));
+
+    let child_pic_0 = gb.add().init(task_dynamic(128));
+    let child_pic_1 = gb.add().init(task_dynamic(128));
+    let child_pic_2 = gb.add().init(task_dynamic(128));
+
+    gb.add_dependency(pc_root, child_pic_0);
+    gb.add_dependency(child_pic_0, child_pic_1);
+    gb.add_dependency(child_pic_1, child_pic_2);
+
+    gb.test_expect(4, 3, &[
+        (2048, 2048, ImageFormat::RGBA8),
+        (2048, 2048, ImageFormat::RGBA8),
+    ]);
+}
+
+#[test]
+fn fg_test_5() {
+    // Test that a task that is used as an input by direct parent and also
+    // distance ancestor are scheduled correctly, and allocates the correct
+    // number of passes, taking advantage of surface reuse / aliasing where feasible.
+
+    let mut gb = RenderTaskGraphBuilder::new();
+
+    let pc_root = gb.add().init(task_location(pc_target(0, 0, 0)));
+
+    let child_pic_0 = gb.add().init(task_dynamic(128));
+    let child_pic_1 = gb.add().init(task_dynamic(64));
+    let child_pic_2 = gb.add().init(task_dynamic(32));
+    let child_pic_3 = gb.add().init(task_dynamic(16));
+
+    gb.add_dependency(pc_root, child_pic_0);
+    gb.add_dependency(child_pic_0, child_pic_1);
+    gb.add_dependency(child_pic_1, child_pic_2);
+    gb.add_dependency(child_pic_2, child_pic_3);
+    gb.add_dependency(pc_root, child_pic_3);
+
+    gb.test_expect(5, 4, &[
+        (256, 256, ImageFormat::RGBA8),
+        (2048, 2048, ImageFormat::RGBA8),
+        (2048, 2048, ImageFormat::RGBA8),
+    ]);
+}
+
+#[test]
+fn fg_test_6() {
+    // Test that a task that is used as an input dependency by two parent
+    // tasks is correctly allocated and freed.
+
+    let mut gb = RenderTaskGraphBuilder::new();
+
+    let pc_root_1 = gb.add().init(task_location(pc_target(0, 0, 0)));
+    let pc_root_2 = gb.add().init(task_location(pc_target(0, 1, 0)));
+
+    let child_pic = gb.add().init(task_dynamic(128));
+
+    gb.add_dependency(pc_root_1, child_pic);
+    gb.add_dependency(pc_root_2, child_pic);
+
+    gb.test_expect(2, 1, &[
+        (2048, 2048, ImageFormat::RGBA8),
+    ]);
+}
+
+#[test]
+fn fg_test_7() {
+    // Test that a standalone surface is not incorrectly used to
+    // allocate subsequent shared task rects.
+
+    let mut gb = RenderTaskGraphBuilder::new();
+
+    let pc_root = gb.add().init(task_location(pc_target(0, 0, 0)));
+
+    let child0 = gb.add().init(task_dynamic(16));
+    let child1 = gb.add().init(task_dynamic(16));
+
+    let child2 = gb.add().init(task_dynamic(16));
+    let child3 = gb.add().init(task_dynamic(16));
+
+    gb.add_dependency(pc_root, child0);
+    gb.add_dependency(child0, child1);
+    gb.add_dependency(pc_root, child1);
+
+    gb.add_dependency(pc_root, child2);
+    gb.add_dependency(child2, child3);
+
+    gb.test_expect(3, 3, &[
+        (256, 256, ImageFormat::RGBA8),
+        (2048, 2048, ImageFormat::RGBA8),
+        (2048, 2048, ImageFormat::RGBA8),
+    ]);
+}
--- a/gfx/wr/webrender/src/renderer/mod.rs
+++ b/gfx/wr/webrender/src/renderer/mod.rs
@@ -69,17 +69,17 @@ use crate::debug_item::DebugItem;
 use crate::frame_builder::{Frame, ChasePrimitive, FrameBuilderConfig};
 use crate::glyph_cache::GlyphCache;
 use crate::glyph_rasterizer::{GlyphFormat, GlyphRasterizer};
 use crate::gpu_cache::{GpuCacheUpdate, GpuCacheUpdateList};
 use crate::gpu_cache::{GpuCacheDebugChunk, GpuCacheDebugCmd};
 use crate::gpu_types::{PrimitiveInstanceData, ScalingInstance, SvgFilterInstance};
 use crate::gpu_types::{BlurInstance, ClearInstance, CompositeInstance, ZBufferId};
 use crate::internal_types::{TextureSource, ResourceCacheError};
-use crate::internal_types::{CacheTextureId, DebugOutput, FastHashMap, FastHashSet, LayerIndex, RenderedDocument, ResultMsg};
+use crate::internal_types::{CacheTextureId, DebugOutput, FastHashMap, FastHashSet, RenderedDocument, ResultMsg};
 use crate::internal_types::{TextureCacheAllocationKind, TextureUpdateList};
 use crate::internal_types::{RenderTargetInfo, Swizzle, DeferredResolveIndex};
 use crate::picture::{self, ResolvedSurfaceTexture};
 use crate::prim_store::DeferredResolve;
 use crate::profiler::{self, GpuProfileTag, TransactionProfile};
 use crate::profiler::{Profiler, add_event_marker, add_text_marker, thread_is_being_profiled};
 use crate::device::query::{GpuProfiler, GpuDebugMethod};
 use crate::render_backend::{FrameId, RenderBackend};
@@ -2370,17 +2370,17 @@ impl Renderer {
                             info.target,
                             info.format,
                             info.width,
                             info.height,
                             info.filter,
                             // This needs to be a render target because some render
                             // tasks get rendered into the texture cache.
                             Some(RenderTargetInfo { has_depth: info.has_depth }),
-                            info.layer_count,
+                            1,
                         );
 
                         if info.is_shared_cache {
                             texture.flags_mut()
                                 .insert(TextureFlags::IS_SHARED_TEXTURE_CACHE);
 
                             // On Mali-Gxx devices we use batched texture uploads as it performs much better.
                             // However, due to another driver bug we must ensure the textures are fully cleared,
@@ -2546,30 +2546,30 @@ impl Renderer {
             Swizzle::default(),
         );
         let (cache_texture, _) = self.texture_resolver
             .resolve(&texture_source).expect("bug: no source texture");
 
         // Before submitting the composite batch, do the
         // framebuffer readbacks that are needed for each
         // composite operation in this batch.
-        let (readback_rect, readback_layer) = readback.get_target_rect();
-        let (backdrop_rect, _) = backdrop.get_target_rect();
+        let readback_rect = readback.get_target_rect();
+        let backdrop_rect = backdrop.get_target_rect();
         let (backdrop_screen_origin, _) = match backdrop.kind {
             RenderTaskKind::Picture(ref task_info) => (task_info.content_origin, task_info.device_pixel_scale),
             _ => panic!("bug: composite on non-picture?"),
         };
 
         // Bind the FBO to blit the backdrop to.
         // Called per-instance in case the layer (and therefore FBO)
         // changes. The device will skip the GL call if the requested
         // target is already bound.
         let cache_draw_target = DrawTarget::from_texture(
             cache_texture,
-            readback_layer.0 as usize,
+            0,
             false,
         );
 
         // Get the rect that we ideally want, in space of the parent surface
         let wanted_rect = DeviceRect::new(
             readback_origin,
             readback_rect.size.to_f32(),
         );
@@ -2638,39 +2638,39 @@ impl Renderer {
             return;
         }
 
         let _timer = self.gpu_profiler.start_timer(GPU_TAG_BLIT);
 
         // TODO(gw): For now, we don't bother batching these by source texture.
         //           If if ever shows up as an issue, we can easily batch them.
         for blit in blits {
-            let (source, layer, source_rect) = {
+            let (source, source_rect) = {
                 // A blit from the child render task into this target.
                 // TODO(gw): Support R8 format here once we start
                 //           creating mips for alpha masks.
                 let task = &render_tasks[blit.source];
-                let (source_rect, layer) = task.get_target_rect();
+                let source_rect = task.get_target_rect();
                 let source_texture = task.get_texture_source();
 
-                (source_texture, layer.0, source_rect)
+                (source_texture, source_rect)
             };
 
             debug_assert_eq!(source_rect.size, blit.target_rect.size);
             let (texture, swizzle) = self.texture_resolver
                 .resolve(&source)
                 .expect("BUG: invalid source texture");
 
             if swizzle != Swizzle::default() {
                 error!("Swizzle {:?} can't be handled by a blit", swizzle);
             }
 
             let read_target = DrawTarget::from_texture(
                 texture,
-                layer,
+                0,
                 false,
             );
 
             self.device.blit_render_target(
                 read_target.into(),
                 read_target.to_framebuffer_rect(source_rect),
                 draw_target,
                 draw_target.to_framebuffer_rect(blit.target_rect),
@@ -3130,21 +3130,16 @@ impl Renderer {
                         surface_rect.to_f32(),
                         surface_rect.to_f32(),
                         // z-id is not relevant when updating a native compositor surface.
                         // TODO(gw): Support compositor surfaces without z-buffer, for memory / perf win here.
                         ZBufferId(0),
                         color_space,
                         format,
                         rescale,
-                        [
-                            planes[0].texture_layer as f32,
-                            planes[1].texture_layer as f32,
-                            planes[2].texture_layer as f32,
-                        ],
                         uv_rects,
                     );
 
                     ( textures, instance )
                 },
                 ResolvedExternalSurfaceColorData::Rgb{ ref plane, flip_y, .. } => {
                     self.shaders
                         .borrow_mut()
@@ -3163,17 +3158,16 @@ impl Renderer {
                         let y = uv_rect.uv0.y;
                         uv_rect.uv0.y = uv_rect.uv1.y;
                         uv_rect.uv1.y = y;
                     }
                     let instance = CompositeInstance::new_rgb(
                         surface_rect.to_f32(),
                         surface_rect.to_f32(),
                         PremultipliedColorF::WHITE,
-                        plane.texture_layer as f32,
                         ZBufferId(0),
                         uv_rect,
                     );
 
                     ( textures, instance )
                 },
             };
 
@@ -3246,45 +3240,42 @@ impl Renderer {
                 CompositeTileSurface::Color { color } => {
                     let dummy = TextureSource::Dummy;
                     let image_buffer_kind = dummy.image_buffer_kind();
                     (
                         CompositeInstance::new(
                             tile.rect,
                             clip_rect,
                             color.premultiplied(),
-                            0.0,
                             tile.z_id,
                         ),
                         BatchTextures::composite_rgb(dummy),
                         (CompositeSurfaceFormat::Rgba, image_buffer_kind),
                     )
                 }
                 CompositeTileSurface::Clear => {
                     let dummy = TextureSource::Dummy;
                     let image_buffer_kind = dummy.image_buffer_kind();
                     (
                         CompositeInstance::new(
                             tile.rect,
                             clip_rect,
                             PremultipliedColorF::BLACK,
-                            0.0,
                             tile.z_id,
                         ),
                         BatchTextures::composite_rgb(dummy),
                         (CompositeSurfaceFormat::Rgba, image_buffer_kind),
                     )
                 }
-                CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::TextureCache { texture, layer } } => {
+                CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::TextureCache { texture } } => {
                     (
                         CompositeInstance::new(
                             tile.rect,
                             clip_rect,
                             PremultipliedColorF::WHITE,
-                            layer as f32,
                             tile.z_id,
                         ),
                         BatchTextures::composite_rgb(texture),
                         (CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2D),
                     )
                 }
                 CompositeTileSurface::ExternalSurface { external_surface_index } => {
                     let surface = &external_surfaces[external_surface_index.0];
@@ -3311,21 +3302,16 @@ impl Renderer {
                             (
                                 CompositeInstance::new_yuv(
                                     tile.rect,
                                     clip_rect,
                                     tile.z_id,
                                     color_space,
                                     format,
                                     rescale,
-                                    [
-                                        planes[0].texture_layer as f32,
-                                        planes[1].texture_layer as f32,
-                                        planes[2].texture_layer as f32,
-                                    ],
                                     uv_rects,
                                 ),
                                 textures,
                                 (CompositeSurfaceFormat::Yuv, surface.image_buffer_kind),
                             )
                         },
                         ResolvedExternalSurfaceColorData::Rgb{ ref plane, flip_y, .. } => {
 
@@ -3336,17 +3322,16 @@ impl Renderer {
                                 uv_rect.uv1.y = y;
                             }
 
                             (
                                 CompositeInstance::new_rgb(
                                     tile.rect,
                                     clip_rect,
                                     PremultipliedColorF::WHITE,
-                                    plane.texture_layer as f32,
                                     tile.z_id,
                                     uv_rect,
                                 ),
                                 BatchTextures::composite_rgb(plane.texture),
                                 (CompositeSurfaceFormat::Rgba, surface.image_buffer_kind),
                             )
                         },
                     }
@@ -3759,27 +3744,27 @@ impl Renderer {
             // TODO(gw): Applying a scissor rect and minimal clear here
             // is a very large performance win on the Intel and nVidia
             // GPUs that I have tested with. It's possible it may be a
             // performance penalty on other GPU types - we should test this
             // and consider different code paths.
 
             let zero_color = [0.0, 0.0, 0.0, 0.0];
             for &task_id in &target.zero_clears {
-                let (rect, _) = render_tasks[task_id].get_target_rect();
+                let rect = render_tasks[task_id].get_target_rect();
                 self.device.clear_target(
                     Some(zero_color),
                     None,
                     Some(draw_target.to_framebuffer_rect(rect)),
                 );
             }
 
             let one_color = [1.0, 1.0, 1.0, 1.0];
             for &task_id in &target.one_clears {
-                let (rect, _) = render_tasks[task_id].get_target_rect();
+                let rect = render_tasks[task_id].get_target_rect();
                 self.device.clear_target(
                     Some(one_color),
                     None,
                     Some(draw_target.to_framebuffer_rect(rect)),
                 );
             }
         }
 
@@ -3845,17 +3830,16 @@ impl Renderer {
         }
 
         self.gpu_profiler.finish_sampler(alpha_sampler);
     }
 
     fn draw_texture_cache_target(
         &mut self,
         texture: &CacheTextureId,
-        layer: LayerIndex,
         target: &TextureCacheRenderTarget,
         render_tasks: &RenderTaskGraph,
         stats: &mut RendererStats,
     ) {
         profile_scope!("draw_texture_cache_target");
 
         self.device.disable_depth();
         self.device.disable_depth_write();
@@ -3871,17 +3855,17 @@ impl Renderer {
             0.0,
             target_size.height as f32,
             self.device.ortho_near_plane(),
             self.device.ortho_far_plane(),
         );
 
         let draw_target = DrawTarget::from_texture(
             texture,
-            layer,
+            0,
             false,
         );
         self.device.bind_draw_target(draw_target);
 
         {
             let _timer = self.gpu_profiler.start_timer(GPU_TAG_CLEAR);
 
             self.device.disable_depth();
@@ -4394,43 +4378,42 @@ impl Renderer {
             let _gm = self.gpu_profiler.start_marker(&format!("pass {}", _pass_index));
 
             profile_scope!("offscreen target");
 
             // If this frame has already been drawn, then any texture
             // cache targets have already been updated and can be
             // skipped this time.
             if !frame.has_been_rendered {
-                for (&(texture_id, target_index), target) in &pass.texture_cache {
+                for (&texture_id, target) in &pass.texture_cache {
                     self.draw_texture_cache_target(
                         &texture_id,
-                        target_index,
                         target,
                         &frame.render_tasks,
                         &mut results.stats,
                     );
                 }
 
                 if !pass.picture_cache.is_empty() {
                     self.profile.inc(profiler::COLOR_PASSES);
                 }
 
                 // Draw picture caching tiles for this pass.
                 for picture_target in &pass.picture_cache {
                     results.stats.color_target_count += 1;
 
                     let draw_target = match picture_target.surface {
-                        ResolvedSurfaceTexture::TextureCache { ref texture, layer } => {
+                        ResolvedSurfaceTexture::TextureCache { ref texture } => {
                             let (texture, _) = self.texture_resolver
                                 .resolve(texture)
                                 .expect("bug");
 
                             DrawTarget::from_texture(
                                 texture,
-                                layer as usize,
+                                0,
                                 true,
                             )
                         }
                         ResolvedSurfaceTexture::Native { id, size } => {
                             let surface_info = match self.current_compositor_kind {
                                 CompositorKind::Native { .. } => {
                                     let compositor = self.compositor_config.compositor().unwrap();
                                     compositor.bind(
@@ -5879,17 +5862,17 @@ impl Renderer {
             self.device_size = renderer.device_size;
 
             for (_id, texture) in self.texture_resolver.texture_cache_map.drain() {
                 self.device.delete_texture(texture);
             }
             for (id, texture) in renderer.textures {
                 info!("\t{}", texture.data);
                 let target = if texture.is_array {
-                    ImageBufferKind::Texture2DArray
+                    panic!("Texture arrays aren't supported");
                 } else {
                     ImageBufferKind::Texture2D
                 };
                 let t = Self::load_texture(
                     target,
                     &texture,
                     Some(RenderTargetInfo { has_depth: texture.has_depth }),
                     &root,
--- a/gfx/wr/webrender/src/renderer/shade.rs
+++ b/gfx/wr/webrender/src/renderer/shade.rs
@@ -20,38 +20,35 @@ use time::precise_time_ns;
 use std::cell::RefCell;
 use std::rc::Rc;
 
 use webrender_build::shader::{ShaderFeatures, ShaderFeatureFlags, get_shader_features};
 
 pub(crate) fn get_feature_string(kind: ImageBufferKind) -> &'static str {
     match kind {
         ImageBufferKind::Texture2D => "TEXTURE_2D",
-        ImageBufferKind::Texture2DArray => "TEXTURE_2D_ARRAY",
         ImageBufferKind::TextureRect => "TEXTURE_RECT",
         ImageBufferKind::TextureExternal => "TEXTURE_EXTERNAL",
     }
 }
 
 fn has_platform_support(kind: ImageBufferKind, gl_type: &GlType) -> bool {
     match (kind, gl_type) {
         (ImageBufferKind::Texture2D, _) => true,
-        (ImageBufferKind::Texture2DArray, _) => true,
         (ImageBufferKind::TextureRect, &GlType::Gles) => false,
         (ImageBufferKind::TextureRect, &GlType::Gl) => true,
         (ImageBufferKind::TextureExternal, &GlType::Gles) => true,
         (ImageBufferKind::TextureExternal, &GlType::Gl) => false,
     }
 }
 
-pub const IMAGE_BUFFER_KINDS: [ImageBufferKind; 4] = [
+pub const IMAGE_BUFFER_KINDS: [ImageBufferKind; 3] = [
     ImageBufferKind::Texture2D,
     ImageBufferKind::TextureRect,
     ImageBufferKind::TextureExternal,
-    ImageBufferKind::Texture2DArray,
 ];
 
 const ADVANCED_BLEND_FEATURE: &str = "ADVANCED_BLEND";
 const ALPHA_FEATURE: &str = "ALPHA_PASS";
 const DEBUG_OVERDRAW_FEATURE: &str = "DEBUG_OVERDRAW";
 const DITHERING_FEATURE: &str = "DITHERING";
 const DUAL_SOURCE_FEATURE: &str = "DUAL_SOURCE_BLENDING";
 const FAST_PATH_FEATURE: &str = "FAST_PATH";
--- a/gfx/wr/webrender/src/renderer/upload.rs
+++ b/gfx/wr/webrender/src/renderer/upload.rs
@@ -79,19 +79,17 @@ pub fn upload_to_texture_cache(
         &mut renderer.texture_upload_pbo_pool,
     );
 
     let num_updates = update_list.len();
 
     for (texture_id, updates) in update_list {
         let texture = &renderer.texture_resolver.texture_cache_map[&texture_id];
         for update in updates {
-            let TextureCacheUpdate { rect, stride, offset, layer_index, format_override, source } = update;
-
-            assert_eq!(layer_index, 0);
+            let TextureCacheUpdate { rect, stride, offset, format_override, source } = update;
 
             let dummy_data;
             let data = match source {
                 TextureUpdateSource::Bytes { ref data } => {
                     &data[offset as usize ..]
                 }
                 TextureUpdateSource::External { id, channel_index } => {
                     let handler = renderer.external_image_handler
@@ -552,17 +550,16 @@ fn copy_from_staging_to_cache_using_draw
             (copy.src_offset.x + copy.size.width) as f32,
             (copy.src_offset.y + copy.size.height) as f32,
         );
 
         copy_instances.push(CompositeInstance::new_rgb(
             dest_rect,
             dest_rect,
             PremultipliedColorF::WHITE,
-            0.0,
             ZBufferId(0),
             src_rect,
         ));
     }
 
     if !copy_instances.is_empty() {
         renderer.draw_instanced_batch(
             &copy_instances,
--- a/gfx/wr/webrender/src/renderer/vertex.rs
+++ b/gfx/wr/webrender/src/renderer/vertex.rs
@@ -220,21 +220,16 @@ pub mod desc {
                 count: 4,
                 kind: VertexAttributeKind::F32,
             },
             VertexAttribute {
                 name: "aScaleSourceRect",
                 count: 4,
                 kind: VertexAttributeKind::I32,
             },
-            VertexAttribute {
-                name: "aScaleSourceLayer",
-                count: 1,
-                kind: VertexAttributeKind::I32,
-            },
         ],
     };
 
     pub const CLIP_RECT: VertexDescriptor = VertexDescriptor {
         vertex_attributes: &[VertexAttribute {
             name: "aPosition",
             count: 2,
             kind: VertexAttributeKind::U8Norm,
@@ -615,21 +610,16 @@ pub mod desc {
                 count: 4,
                 kind: VertexAttributeKind::F32,
             },
             VertexAttribute {
                 name: "aUvRect2",
                 count: 4,
                 kind: VertexAttributeKind::F32,
             },
-            VertexAttribute {
-                name: "aTextureLayers",
-                count: 3,
-                kind: VertexAttributeKind::F32,
-            },
         ],
     };
 
     pub const CLEAR: VertexDescriptor = VertexDescriptor {
         vertex_attributes: &[VertexAttribute {
             name: "aPosition",
             count: 2,
             kind: VertexAttributeKind::U8Norm,