Backed out 11 changesets (bug 1878554, bug 1878553, bug 1878556) for causing node failures in aboutwelcome CLOSED TREE
authorCristian Tuns <ctuns@mozilla.com>
Wed, 07 Feb 2024 01:26:39 -0500 (17 months ago)
changeset 694840 33a6d6712188dbb4c5443ccf6ddd8c5050c30d63
parent 694839 eed15114b8083e5a303b600b88a917d2d6befbe6
child 694841 5ff139138582468b66861fcbe67d8237b0c9a3db
push id199554
push userctuns@mozilla.com
push dateWed, 07 Feb 2024 06:31:09 +0000 (17 months ago)
treeherderautoland@33a6d6712188 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1878554, 1878553, 1878556
milestone124.0a1
backs out425f70fa7e5a6e4eb2b5aa64a2a4910f341b8265
2ab3a28f5c046ed54207a48056d33ee475ec514b
fb2e02682c8c42c33ba673b830072d9042b874fa
2fc5e630568e5cf22dd6e510250291b5e66fd63b
78839d5841b463f74a98f8036c36aa737a280990
e0ab31d2de1befa843a6bb49e7fd1190c07f5d0d
4eb59e4ebafacb81f9d1dda4a32a79ef6b2ac74d
ab1e077d3f843fc18cf93db8eacbd249bf409f65
ef999f293e3ea2e7947caf61acbb0b6f6a19af92
cf49920cb1ad4a9da5c0ab4f5cc230e624638b39
95f0fce637263b8bdd782f33a8a5e0c39e45796d
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 11 changesets (bug 1878554, bug 1878553, bug 1878556) for causing node failures in aboutwelcome CLOSED TREE Backed out changeset 425f70fa7e5a (bug 1878556) Backed out changeset 2ab3a28f5c04 (bug 1878556) Backed out changeset fb2e02682c8c (bug 1878556) Backed out changeset 2fc5e630568e (bug 1878554) Backed out changeset 78839d5841b4 (bug 1878554) Backed out changeset e0ab31d2de1b (bug 1878554) Backed out changeset 4eb59e4ebafa (bug 1878554) Backed out changeset ab1e077d3f84 (bug 1878554) Backed out changeset ef999f293e3e (bug 1878553) Backed out changeset cf49920cb1ad (bug 1878553) Backed out changeset 95f0fce63726 (bug 1878553)
.eslintrc.js
browser/components/aboutwelcome/.eslintrc.js
browser/components/aboutwelcome/content-src/aboutwelcome.jsx
browser/components/aboutwelcome/content-src/components/AddonsPicker.jsx
browser/components/aboutwelcome/content-src/components/HelpText.jsx
browser/components/aboutwelcome/content-src/components/HeroImage.jsx
browser/components/aboutwelcome/content-src/components/LanguageSwitcher.jsx
browser/components/aboutwelcome/content-src/components/MobileDownloads.jsx
browser/components/aboutwelcome/content-src/components/MultiStageAboutWelcome.jsx
browser/components/aboutwelcome/content-src/components/MultiStageProtonScreen.jsx
browser/components/aboutwelcome/content-src/components/ReturnToAMO.jsx
browser/components/aboutwelcome/content-src/lib/aboutwelcome-utils.js
browser/components/aboutwelcome/content-src/lib/aboutwelcome-utils.mjs
browser/components/aboutwelcome/content-src/lib/addUtmParams.mjs
browser/components/aboutwelcome/content/aboutwelcome.bundle.js
browser/components/aboutwelcome/karma.mc.config.js
browser/components/aboutwelcome/tests/unit/MultiStageAboutWelcome.test.jsx
browser/components/aboutwelcome/tests/unit/addUtmParams.test.js
browser/components/asrouter/.eslintrc.js
browser/components/asrouter/content-src/components/ASRouterAdmin/ASRouterAdmin.jsx
browser/components/asrouter/content-src/template-utils.js
browser/components/asrouter/content-src/templates/FirstRun/addUtmParams.js
browser/components/asrouter/content/asrouter-admin.bundle.js
browser/components/asrouter/tests/unit/addUtmParams.test.js
browser/components/asrouter/tests/unit/template-utils.test.js
browser/components/newtab/.eslintrc.js
browser/components/newtab/content-src/components/Search/Search.jsx
browser/components/newtab/data/content/activity-stream.bundle.js
browser/components/newtab/test/unit/content-src/components/DiscoveryStreamComponents/ImpressionStats.test.jsx
browser/components/newtab/test/unit/content-src/components/TopSites/TopSiteImpressionWrapper.test.jsx
browser/components/pocket/webpack.config.js
browser/components/storybook/custom-elements-manifest.config.mjs
devtools/client/debugger/babel.config.js
devtools/client/debugger/jest-test.config.js
devtools/client/debugger/jest.config.js
devtools/client/shared/sourceeditor/webpack.config.js
devtools/client/shared/test-helpers/shared-jest.config.js
tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -63,25 +63,20 @@ module.exports = {
   root: true,
   // New rules and configurations should generally be added in
   // tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js to
   // allow external repositories that use the plugin to pick them up as well.
   extends: ["plugin:mozilla/recommended"],
   plugins: ["mozilla", "import", "json"],
   overrides: [
     {
-      files: [
-        // All .eslintrc.js files are in the node environment, so turn that
-        // on here.
-        // https://github.com/eslint/eslint/issues/13008
-        ".eslintrc.js",
-        // *.config.js files are generally assumed to be configuration files
-        // based for node.
-        "*.config.?(m)js",
-      ],
+      // All .eslintrc.js files are in the node environment, so turn that
+      // on here.
+      // https://github.com/eslint/eslint/issues/13008
+      files: [".eslintrc.js"],
       env: {
         node: true,
         browser: false,
       },
     },
     {
       files: [
         "**/*.jsx",
--- a/browser/components/aboutwelcome/.eslintrc.js
+++ b/browser/components/aboutwelcome/.eslintrc.js
@@ -1,26 +1,30 @@
 /* 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/. */
 
 module.exports = {
   // When adding items to this file please check for effects on sub-directories.
+  parserOptions: {
+    ecmaFeatures: {
+      jsx: true,
+    },
+  },
   plugins: ["import", "react", "jsx-a11y"],
   settings: {
     react: {
       version: "16.2.0",
     },
   },
   extends: ["plugin:jsx-a11y/recommended"],
   overrides: [
     {
-      // TODO: Bug 1773467 - Move these to .mjs or figure out a generic way
-      // to identify these as modules.
-      files: ["tests/unit/**/*.js"],
+      // Only mark the files as modules which are actually modules.
+      files: ["content-src/**", "tests/unit/**"],
       parserOptions: {
         sourceType: "module",
       },
     },
     {
       // These files use fluent-dom to insert content
       files: [
         "content-src/components/Zap.jsx",
@@ -33,17 +37,17 @@ module.exports = {
       rules: {
         "jsx-a11y/anchor-has-content": "off",
         "jsx-a11y/heading-has-content": "off",
         "jsx-a11y/label-has-associated-control": "off",
         "jsx-a11y/no-onchange": "off",
       },
     },
     {
-      files: ["content-src/**", "tests/unit/**"],
+      files: ["./*.js", "content-src/**", "tests/unit/**"],
       env: {
         node: true,
       },
     },
     {
       // Use a configuration that's appropriate for modules, workers and
       // non-production files.
       files: ["modules/*.jsm", "tests/**"],
@@ -51,16 +55,18 @@ module.exports = {
         "no-implicit-globals": "off",
       },
     },
     {
       files: ["content-src/**", "tests/unit/**"],
       rules: {
         // Disallow commonjs in these directories.
         "import/no-commonjs": 2,
+        // Allow JSX with arrow functions.
+        "react/jsx-no-bind": 0,
       },
     },
     {
       // These tests simulate the browser environment.
       files: "tests/unit/**",
       env: {
         browser: true,
         mocha: true,
@@ -71,28 +77,26 @@ module.exports = {
         sinon: true,
       },
     },
     {
       files: "tests/**",
       rules: {
         "func-name-matching": 0,
         "lines-between-class-members": 0,
+        "require-await": 0,
       },
     },
   ],
   rules: {
     "fetch-options/no-fetch-credentials": "error",
 
     "react/jsx-boolean-value": ["error", "always"],
     "react/jsx-key": "error",
-    "react/jsx-no-bind": [
-      "error",
-      { allowArrowFunctions: true, allowFunctions: true },
-    ],
+    "react/jsx-no-bind": "error",
     "react/jsx-no-comment-textnodes": "error",
     "react/jsx-no-duplicate-props": "error",
     "react/jsx-no-target-blank": "error",
     "react/jsx-no-undef": "error",
     "react/jsx-pascal-case": "error",
     "react/jsx-uses-react": "error",
     "react/jsx-uses-vars": "error",
     "react/no-access-state-in-setstate": "error",
@@ -118,40 +122,49 @@ module.exports = {
     ],
     "react/require-render-return": "error",
 
     "accessor-pairs": ["error", { setWithoutGet: true, getWithoutSet: false }],
     "array-callback-return": "error",
     "block-scoped-var": "error",
     "consistent-this": ["error", "use-bind"],
     eqeqeq: "error",
+    "for-direction": "error",
     "func-name-matching": "error",
     "getter-return": "error",
     "guard-for-in": "error",
+    "handle-callback-err": "error",
+    "lines-between-class-members": "error",
     "max-depth": ["error", 4],
     "max-nested-callbacks": ["error", 4],
     "max-params": ["error", 6],
     "max-statements": ["error", 50],
+    "max-statements-per-line": ["error", { max: 2 }],
     "new-cap": ["error", { newIsCap: true, capIsNew: false }],
     "no-alert": "error",
+    "no-buffer-constructor": "error",
     "no-console": ["error", { allow: ["error"] }],
     "no-div-regex": "error",
     "no-duplicate-imports": "error",
     "no-eq-null": "error",
     "no-extend-native": "error",
     "no-extra-label": "error",
     "no-implicit-coercion": ["error", { allow: ["!!"] }],
     "no-implicit-globals": "error",
     "no-loop-func": "error",
+    "no-mixed-requires": "error",
     "no-multi-assign": "error",
     "no-multi-str": "error",
     "no-new": "error",
     "no-new-func": "error",
+    "no-new-require": "error",
     "no-octal-escape": "error",
     "no-param-reassign": "error",
+    "no-path-concat": "error",
+    "no-process-exit": "error",
     "no-proto": "error",
     "no-prototype-builtins": "error",
     "no-return-assign": ["error", "except-parens"],
     "no-script-url": "error",
     "no-shadow": "error",
     "no-template-curly-in-string": "error",
     "no-undef-init": "error",
     "no-unmodified-loop-condition": "error",
@@ -172,14 +185,15 @@ module.exports = {
       },
     ],
     "prefer-numeric-literals": "error",
     "prefer-promise-reject-errors": "error",
     "prefer-rest-params": "error",
     "prefer-spread": "error",
     "prefer-template": "error",
     radix: ["error", "always"],
+    "require-await": "error",
     "sort-vars": "error",
     "symbol-description": "error",
     "vars-on-top": "error",
     yoda: ["error", "never"],
   },
 };
--- a/browser/components/aboutwelcome/content-src/aboutwelcome.jsx
+++ b/browser/components/aboutwelcome/content-src/aboutwelcome.jsx
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 import React from "react";
 import ReactDOM from "react-dom";
-import { AboutWelcomeUtils } from "./lib/aboutwelcome-utils.mjs";
+import { AboutWelcomeUtils } from "./lib/aboutwelcome-utils";
 import { MultiStageAboutWelcome } from "./components/MultiStageAboutWelcome";
 import { ReturnToAMO } from "./components/ReturnToAMO";
 
 class AboutWelcome extends React.PureComponent {
   constructor(props) {
     super(props);
     this.state = { metricsFlowUri: null };
     this.fetchFxAFlowUri = this.fetchFxAFlowUri.bind(this);
--- a/browser/components/aboutwelcome/content-src/components/AddonsPicker.jsx
+++ b/browser/components/aboutwelcome/content-src/components/AddonsPicker.jsx
@@ -1,14 +1,14 @@
 /* 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 { AboutWelcomeUtils } from "../lib/aboutwelcome-utils.mjs";
+import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils";
 import { Localized } from "./MSLocalized";
 
 export const AddonsPicker = props => {
   const { content } = props;
 
   if (!content) {
     return null;
   }
--- a/browser/components/aboutwelcome/content-src/components/HelpText.jsx
+++ b/browser/components/aboutwelcome/content-src/components/HelpText.jsx
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 import React from "react";
 import { Localized } from "./MSLocalized";
-import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils.mjs";
+import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils";
 const MS_STRING_PROP = "string_id";
 
 export const HelpText = props => {
   if (!props.text) {
     return null;
   }
 
   if (props.hasImg) {
--- a/browser/components/aboutwelcome/content-src/components/HeroImage.jsx
+++ b/browser/components/aboutwelcome/content-src/components/HeroImage.jsx
@@ -1,14 +1,14 @@
 /* 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 { AboutWelcomeUtils } from "../lib/aboutwelcome-utils.mjs";
+import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils";
 
 export const HeroImage = props => {
   const { height, url, alt } = props;
 
   if (!url) {
     return null;
   }
 
--- a/browser/components/aboutwelcome/content-src/components/LanguageSwitcher.jsx
+++ b/browser/components/aboutwelcome/content-src/components/LanguageSwitcher.jsx
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 import React, { useState, useEffect } from "react";
 import { Localized } from "./MSLocalized";
-import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils.mjs";
+import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils";
 
 /**
  * The language switcher implements a hook that should be placed at a higher level
  * than the actual language switcher component, as it needs to preemptively fetch
  * and install langpacks for the user if there is a language mismatch screen.
  */
 export function useLanguageSwitcher(
   appAndSystemLocaleInfo,
--- a/browser/components/aboutwelcome/content-src/components/MobileDownloads.jsx
+++ b/browser/components/aboutwelcome/content-src/components/MobileDownloads.jsx
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 import React from "react";
 import { Localized } from "./MSLocalized";
-import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils.mjs";
+import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils";
 
 export const MarketplaceButtons = props => {
   return (
     <ul className="mobile-download-buttons">
       {props.buttons.includes("ios") ? (
         <li className="ios">
           <button
             data-l10n-id={"spotlight-ios-marketplace-button"}
--- a/browser/components/aboutwelcome/content-src/components/MultiStageAboutWelcome.jsx
+++ b/browser/components/aboutwelcome/content-src/components/MultiStageAboutWelcome.jsx
@@ -1,19 +1,22 @@
 /* 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, { useState, useEffect, useRef } from "react";
 import { Localized } from "./MSLocalized";
-import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils.mjs";
+import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils";
 import { MultiStageProtonScreen } from "./MultiStageProtonScreen";
 import { useLanguageSwitcher } from "./LanguageSwitcher";
 import { SubmenuButton } from "./SubmenuButton";
-import { BASE_PARAMS, addUtmParams } from "../lib/addUtmParams.mjs";
+import {
+  BASE_PARAMS,
+  addUtmParams,
+} from "asrouter/content-src/templates/FirstRun/addUtmParams";
 
 // Amount of milliseconds for all transitions to complete (including delays).
 const TRANSITION_OUT_TIME = 1000;
 const LANGUAGE_MISMATCH_SCREEN_ID = "AW_LANGUAGE_MISMATCH";
 
 export const MultiStageAboutWelcome = props => {
   let { defaultScreens } = props;
   const didFilter = useRef(false);
--- a/browser/components/aboutwelcome/content-src/components/MultiStageProtonScreen.jsx
+++ b/browser/components/aboutwelcome/content-src/components/MultiStageProtonScreen.jsx
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 import React, { useEffect, useState } from "react";
 import { Localized } from "./MSLocalized";
-import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils.mjs";
+import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils";
 import { MobileDownloads } from "./MobileDownloads";
 import { MultiSelect } from "./MultiSelect";
 import { Themes } from "./Themes";
 import {
   SecondaryCTA,
   StepsIndicator,
   ProgressBar,
 } from "./MultiStageAboutWelcome";
--- a/browser/components/aboutwelcome/content-src/components/ReturnToAMO.jsx
+++ b/browser/components/aboutwelcome/content-src/components/ReturnToAMO.jsx
@@ -1,19 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 import React from "react";
 import {
   AboutWelcomeUtils,
   DEFAULT_RTAMO_CONTENT,
-} from "../lib/aboutwelcome-utils.mjs";
+} from "../lib/aboutwelcome-utils";
 import { MultiStageProtonScreen } from "./MultiStageProtonScreen";
-import { BASE_PARAMS } from "../lib/addUtmParams.mjs";
+import { BASE_PARAMS } from "asrouter/content-src/templates/FirstRun/addUtmParams";
 
 export class ReturnToAMO extends React.PureComponent {
   constructor(props) {
     super(props);
     this.fetchFlowParams = this.fetchFlowParams.bind(this);
     this.handleAction = this.handleAction.bind(this);
   }
 
rename from browser/components/aboutwelcome/content-src/lib/aboutwelcome-utils.mjs
rename to browser/components/aboutwelcome/content-src/lib/aboutwelcome-utils.js
--- a/browser/components/aboutwelcome/content/aboutwelcome.bundle.js
+++ b/browser/components/aboutwelcome/content/aboutwelcome.bundle.js
@@ -16,167 +16,172 @@ module.exports = React;
 /***/ }),
 /* 2 */
 /***/ ((module) => {
 
 module.exports = ReactDOM;
 
 /***/ }),
 /* 3 */
-/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
+/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
 
 __webpack_require__.r(__webpack_exports__);
 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
 /* harmony export */   "AboutWelcomeUtils": () => (/* binding */ AboutWelcomeUtils),
 /* harmony export */   "DEFAULT_RTAMO_CONTENT": () => (/* binding */ DEFAULT_RTAMO_CONTENT)
 /* harmony export */ });
 /* 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/. */
 
 // If the container has a "page" data attribute, then this is
 // a Spotlight modal or Feature Callout. Otherwise, this is
 // about:welcome and we should return the current page.
-const page =
-  document.querySelector(
-    "#multi-stage-message-root.onboardingContainer[data-page]"
-  )?.dataset.page || document.location.href;
-
+const page = document.querySelector("#multi-stage-message-root.onboardingContainer[data-page]")?.dataset.page || document.location.href;
 const AboutWelcomeUtils = {
   handleUserAction(action) {
     return window.AWSendToParent("SPECIAL_ACTION", action);
   },
   sendImpressionTelemetry(messageId, context) {
     window.AWSendEventTelemetry?.({
       event: "IMPRESSION",
       event_context: {
         ...context,
-        page,
+        page
       },
-      message_id: messageId,
+      message_id: messageId
     });
   },
   sendActionTelemetry(messageId, elementId, eventName = "CLICK_BUTTON") {
     const ping = {
       event: eventName,
       event_context: {
         source: elementId,
-        page,
+        page
       },
-      message_id: messageId,
+      message_id: messageId
     };
     window.AWSendEventTelemetry?.(ping);
   },
   sendDismissTelemetry(messageId, elementId) {
     // Don't send DISMISS telemetry in spotlight modals since they already send
     // their own equivalent telemetry.
     if (page !== "spotlight") {
       this.sendActionTelemetry(messageId, elementId, "DISMISS");
     }
   },
   async fetchFlowParams(metricsFlowUri) {
     let flowParams;
     try {
       const response = await fetch(metricsFlowUri, {
-        credentials: "omit",
+        credentials: "omit"
       });
       if (response.status === 200) {
-        const { deviceId, flowId, flowBeginTime } = await response.json();
-        flowParams = { deviceId, flowId, flowBeginTime };
+        const {
+          deviceId,
+          flowId,
+          flowBeginTime
+        } = await response.json();
+        flowParams = {
+          deviceId,
+          flowId,
+          flowBeginTime
+        };
       } else {
         console.error("Non-200 response", response);
       }
     } catch (e) {
       flowParams = null;
     }
     return flowParams;
   },
   sendEvent(type, detail) {
-    document.dispatchEvent(
-      new CustomEvent(`AWPage:${type}`, {
-        bubbles: true,
-        detail,
-      })
-    );
+    document.dispatchEvent(new CustomEvent(`AWPage:${type}`, {
+      bubbles: true,
+      detail
+    }));
   },
   getLoadingStrategyFor(url) {
     return url?.startsWith("http") ? "lazy" : "eager";
-  },
+  }
 };
-
 const DEFAULT_RTAMO_CONTENT = {
   template: "return_to_amo",
   utm_term: "rtamo",
   content: {
     position: "split",
-    title: { string_id: "mr1-return-to-amo-subtitle" },
+    title: {
+      string_id: "mr1-return-to-amo-subtitle"
+    },
     has_noodles: false,
     subtitle: {
-      string_id: "mr1-return-to-amo-addon-title",
+      string_id: "mr1-return-to-amo-addon-title"
     },
-    backdrop:
-      "var(--mr-welcome-background-color) var(--mr-welcome-background-gradient)",
-    background:
-      "url('chrome://activity-stream/content/data/content/assets/mr-rtamo-background-image.svg') no-repeat center",
+    backdrop: "var(--mr-welcome-background-color) var(--mr-welcome-background-gradient)",
+    background: "url('chrome://activity-stream/content/data/content/assets/mr-rtamo-background-image.svg') no-repeat center",
     progress_bar: true,
     primary_button: {
-      label: { string_id: "mr1-return-to-amo-add-extension-label" },
+      label: {
+        string_id: "mr1-return-to-amo-add-extension-label"
+      },
       source_id: "ADD_EXTENSION_BUTTON",
       action: {
         type: "INSTALL_ADDON_FROM_URL",
-        data: { url: null, telemetrySource: "rtamo" },
-      },
+        data: {
+          url: null,
+          telemetrySource: "rtamo"
+        }
+      }
     },
     secondary_button: {
       label: {
-        string_id: "onboarding-not-now-button-label",
+        string_id: "onboarding-not-now-button-label"
       },
       source_id: "RTAMO_START_BROWSING_BUTTON",
       action: {
-        type: "OPEN_AWESOME_BAR",
-      },
+        type: "OPEN_AWESOME_BAR"
+      }
     },
     secondary_button_top: {
       label: {
-        string_id: "mr1-onboarding-sign-in-button-label",
+        string_id: "mr1-onboarding-sign-in-button-label"
       },
       source_id: "RTAMO_FXA_SIGNIN_BUTTON",
       action: {
         data: {
           entrypoint: "activity-stream-firstrun",
-          where: "tab",
+          where: "tab"
         },
         type: "SHOW_FIREFOX_ACCOUNTS",
-        addFlowParams: true,
-      },
-    },
-  },
+        addFlowParams: true
+      }
+    }
+  }
 };
 
-
 /***/ }),
 /* 4 */
 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
 
 __webpack_require__.r(__webpack_exports__);
 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
 /* harmony export */   "MultiStageAboutWelcome": () => (/* binding */ MultiStageAboutWelcome),
 /* harmony export */   "SecondaryCTA": () => (/* binding */ SecondaryCTA),
 /* harmony export */   "StepsIndicator": () => (/* binding */ StepsIndicator),
 /* harmony export */   "ProgressBar": () => (/* binding */ ProgressBar),
 /* harmony export */   "WelcomeScreen": () => (/* binding */ WelcomeScreen)
 /* harmony export */ });
 /* 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__(5);
-/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
+/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
 /* harmony import */ var _MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6);
 /* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(10);
 /* harmony import */ var _SubmenuButton__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(15);
-/* harmony import */ var _lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(19);
+/* harmony import */ var asrouter_content_src_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(19);
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 
 
 
@@ -217,17 +222,17 @@ const MultiStageAboutWelcome = props => 
       didFilter.current = true;
       const screenInitials = filteredScreens.map(({
         id
       }) => id?.split("_")[1]?.[0]).join("");
       // Send impression ping when respective screen first renders
       filteredScreens.forEach((screen, order) => {
         if (index === order) {
           const messageId = `${props.message_id}_${order}_${screen.id}_${screenInitials}`;
-          _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendImpressionTelemetry(messageId, {
+          _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendImpressionTelemetry(messageId, {
             screen_family: props.message_id,
             screen_index: order,
             screen_id: screen.id,
             screen_initials: screenInitials
           });
           window.AWAddScreenImpression?.(screen);
         }
       });
@@ -244,17 +249,17 @@ const MultiStageAboutWelcome = props => 
 
   const [flowParams, setFlowParams] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
   const {
     metricsFlowUri
   } = props;
   (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
     (async () => {
       if (metricsFlowUri) {
-        setFlowParams(await _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.fetchFlowParams(metricsFlowUri));
+        setFlowParams(await _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.fetchFlowParams(metricsFlowUri));
       }
     })();
   }, [metricsFlowUri]);
 
   // Allow "in" style to render to actually transition towards regular state,
   // which also makes using browser back/forward navigation skip transitions.
   const [transition, setTransition] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(props.transitions ? "in" : "");
   (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
@@ -463,43 +468,43 @@ class WelcomeScreen extends (react__WEBP
   }
   handleOpenURL(action, flowParams, UTMTerm) {
     let {
       type,
       data
     } = action;
     if (type === "SHOW_FIREFOX_ACCOUNTS") {
       let params = {
-        ..._lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_6__.BASE_PARAMS,
+        ...asrouter_content_src_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_6__.BASE_PARAMS,
         utm_term: `${UTMTerm}-screen`
       };
       if (action.addFlowParams && flowParams) {
         params = {
           ...params,
           ...flowParams
         };
       }
       data = {
         ...data,
         extraParams: params
       };
     } else if (type === "OPEN_URL") {
       let url = new URL(data.args);
-      (0,_lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_6__.addUtmParams)(url, `${UTMTerm}-screen`);
+      (0,asrouter_content_src_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_6__.addUtmParams)(url, `${UTMTerm}-screen`);
       if (action.addFlowParams && flowParams) {
         url.searchParams.append("device_id", flowParams.deviceId);
         url.searchParams.append("flow_id", flowParams.flowId);
         url.searchParams.append("flow_begin_time", flowParams.flowBeginTime);
       }
       data = {
         ...data,
         args: url.toString()
       };
     }
-    return _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction({
+    return _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction({
       type,
       data
     });
   }
   async handleAction(event) {
     let {
       props
     } = this;
@@ -510,44 +515,44 @@ class WelcomeScreen extends (react__WEBP
       targetContent = {
         action: event.action
       };
     }
     if (!(targetContent && targetContent.action)) {
       return;
     }
     // Send telemetry before waiting on actions
-    _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, source, event.name);
+    _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, source, event.name);
 
     // Send additional telemetry if a messaging surface like feature callout is
     // dismissed via the dismiss button. Other causes of dismissal will be
     // handled separately by the messaging surface's own code.
     if (value === "dismiss_button" && !event.name) {
-      _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendDismissTelemetry(props.messageId, source);
+      _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendDismissTelemetry(props.messageId, source);
     }
     let {
       action
     } = targetContent;
     action = JSON.parse(JSON.stringify(action));
     if (action.collectSelect) {
       this.setMultiSelectActions(action);
     }
     let actionResult;
     if (["OPEN_URL", "SHOW_FIREFOX_ACCOUNTS"].includes(action.type)) {
       actionResult = await this.handleOpenURL(action, props.flowParams, props.UTMTerm);
     } else if (action.type) {
-      actionResult = await _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction(action);
+      actionResult = await _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction(action);
       if (action.type === "FXA_SIGNIN_FLOW") {
-        _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, actionResult ? "sign_in" : "sign_in_cancel", "FXA_SIGNIN_FLOW");
+        _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, actionResult ? "sign_in" : "sign_in_cancel", "FXA_SIGNIN_FLOW");
       }
       // Wait until migration closes to complete the action
       const hasMigrate = a => a.type === "SHOW_MIGRATION_WIZARD" || a.type === "MULTI_ACTION" && a.data?.actions?.some(hasMigrate);
       if (hasMigrate(action)) {
         await window.AWWaitForMigrationClose();
-        _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, "migrate_close");
+        _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, "migrate_close");
       }
     }
 
     // A special tiles.action.theme value indicates we should use the event's value vs provided value.
     if (action.theme) {
       let themeToUse = action.theme === "<event>" ? event.currentTarget.value : this.props.initialTheme || action.theme;
       this.props.setActiveTheme(themeToUse);
       window.AWSelectTheme(themeToUse);
@@ -604,17 +609,17 @@ class WelcomeScreen extends (react__WEBP
       }
       if (checkboxAction) {
         multiSelectActions.push(checkboxAction);
       }
     }
     action.data.actions.unshift(...multiSelectActions);
 
     // Send telemetry with selected checkbox ids
-    _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, props.activeMultiSelect, "SELECT_CHECKBOX");
+    _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, props.activeMultiSelect, "SELECT_CHECKBOX");
   }
   render() {
     return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_3__.MultiStageProtonScreen, {
       content: this.props.content,
       id: this.props.id,
       order: this.props.order,
       previousOrder: this.props.previousOrder,
       activeTheme: this.props.activeTheme,
@@ -758,17 +763,17 @@ const Localized = ({
 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
 /* harmony export */   "MultiStageProtonScreen": () => (/* binding */ MultiStageProtonScreen),
 /* harmony export */   "ProtonScreenActionButtons": () => (/* binding */ ProtonScreenActionButtons),
 /* harmony export */   "ProtonScreen": () => (/* binding */ ProtonScreen)
 /* harmony export */ });
 /* 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__(5);
-/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
+/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
 /* harmony import */ var _MobileDownloads__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(7);
 /* harmony import */ var _MultiSelect__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(8);
 /* harmony import */ var _Themes__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(9);
 /* harmony import */ var _MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(4);
 /* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(10);
 /* harmony import */ var _CTAParagraph__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(11);
 /* harmony import */ var _HeroImage__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(12);
 /* harmony import */ var _OnboardingVideo__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(13);
@@ -953,17 +958,17 @@ class ProtonScreen extends (react__WEBPA
     width,
     height,
     marginBlock,
     marginInline,
     className = "logo-container"
   }) {
     function getLoadingStrategy() {
       for (let url of [imageURL, darkModeImageURL, reducedMotionImageURL, darkModeReducedMotionImageURL]) {
-        if (_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getLoadingStrategyFor(url) === "lazy") {
+        if (_lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getLoadingStrategyFor(url) === "lazy") {
           return "lazy";
         }
       }
       return "eager";
     }
     return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("picture", {
       className: className,
       style: {
@@ -1184,17 +1189,17 @@ class ProtonScreen extends (react__WEBPA
         background: content.background && isCenterPosition ? content.background : null,
         width: content.width && content.position !== "split" ? content.width : null
       }
     }, content.logo ? this.renderPicture(content.logo) : null, isRtamo ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
       className: "rtamo-icon"
     }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
       className: `${isTheme ? "rtamo-theme-icon" : "brand-logo"}`,
       src: this.props.iconURL,
-      loading: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getLoadingStrategyFor(this.props.iconURL),
+      loading: _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getLoadingStrategyFor(this.props.iconURL),
       alt: "",
       role: "presentation"
     })) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
       className: "main-content-inner"
     }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
       className: `welcome-text ${content.title_style || ""}`
     }, content.title ? this.renderTitle(content) : null, content.subtitle ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
       text: content.subtitle
@@ -1230,17 +1235,17 @@ class ProtonScreen extends (react__WEBPA
 __webpack_require__.r(__webpack_exports__);
 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
 /* harmony export */   "MarketplaceButtons": () => (/* binding */ MarketplaceButtons),
 /* harmony export */   "MobileDownloads": () => (/* binding */ MobileDownloads)
 /* harmony export */ });
 /* 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__(5);
-/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
+/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
 /* 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 MarketplaceButtons = props => {
@@ -1267,17 +1272,17 @@ const MobileDownloads = props => {
   const showEmailLink = props.data.email && window.AWSendToDeviceEmailsSupported();
   return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
     className: "mobile-downloads"
   }, QRCode ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
     "data-l10n-id": QRCode.alt_text.string_id ? QRCode.alt_text.string_id : null,
     className: "qr-code-image",
     alt: typeof QRCode.alt_text === "string" ? QRCode.alt_text : "",
     src: QRCode.image_url,
-    loading: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getLoadingStrategyFor(QRCode.image_url)
+    loading: _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getLoadingStrategyFor(QRCode.image_url)
   }) : null, showEmailLink ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
     text: props.data.email.link_text
   }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
     className: "email-link",
     value: "email_link",
     onClick: props.handleAction
   }))) : null, props.data.marketplace_buttons ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(MarketplaceButtons, {
     buttons: props.data.marketplace_buttons,
@@ -1443,17 +1448,17 @@ const Themes = props => {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
 /* harmony export */   "useLanguageSwitcher": () => (/* binding */ useLanguageSwitcher),
 /* harmony export */   "LanguageSwitcher": () => (/* binding */ LanguageSwitcher)
 /* harmony export */ });
 /* 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__(5);
-/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
+/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
 /* 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/. */
 
 
 
 
 
@@ -1667,17 +1672,17 @@ function LanguageSwitcher(props) {
   })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
     style: {
       display: showReadyScreen ? "block" : "none"
     }
   }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
     className: "primary",
     value: "primary_button",
     onClick: () => {
-      _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(messageId, "download_langpack");
+      _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(messageId, "download_langpack");
       setIsAwaitingLangpack(true);
     }
   }, content.languageSwitcher.switch ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
     text: content.languageSwitcher.switch
   }) :
   // This is the localized name from the Intl.DisplayNames API.
   negotiatedLanguage?.langPackDisplayName)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
     type: "button",
@@ -1742,17 +1747,17 @@ const CTAParagraph = props => {
 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
 
 __webpack_require__.r(__webpack_exports__);
 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
 /* harmony export */   "HeroImage": () => (/* binding */ HeroImage)
 /* harmony export */ });
 /* 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_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
+/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
 /* 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 HeroImage = props => {
   const {
@@ -1765,17 +1770,17 @@ const HeroImage = props => {
   }
   return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
     className: "hero-image"
   }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
     style: height ? {
       height
     } : null,
     src: url,
-    loading: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.getLoadingStrategyFor(url),
+    loading: _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.getLoadingStrategyFor(url),
     alt: alt || "",
     role: alt ? null : "presentation"
   }));
 };
 
 /***/ }),
 /* 13 */
 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
@@ -2078,17 +2083,17 @@ const EmbeddedMigrationWizard = ({
 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
 
 __webpack_require__.r(__webpack_exports__);
 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
 /* harmony export */   "AddonsPicker": () => (/* binding */ AddonsPicker)
 /* harmony export */ });
 /* 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_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
+/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5);
 /* 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/. */
 
 
 
 
@@ -2111,21 +2116,21 @@ const AddonsPicker = props => {
       type,
       data
     } = action;
     if (type === "INSTALL_ADDON_FROM_URL") {
       if (!data) {
         return;
       }
     }
-    _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.handleUserAction({
+    _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.handleUserAction({
       type,
       data
     });
-    _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.sendActionTelemetry(message_id, source_id);
+    _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.sendActionTelemetry(message_id, source_id);
   }
   return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
     className: "addons-picker-container"
   }, content.tiles.data.map(({
     id,
     install_label,
     name,
     type,
@@ -2212,34 +2217,34 @@ const LinkParagraph = props => {
     // must pass in tabIndex when no href is provided
     ,
     tabIndex: "0"
   }, " "))));
 };
 
 /***/ }),
 /* 19 */
-/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
+/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
 
 __webpack_require__.r(__webpack_exports__);
 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
 /* harmony export */   "BASE_PARAMS": () => (/* binding */ BASE_PARAMS),
 /* harmony export */   "addUtmParams": () => (/* binding */ addUtmParams)
 /* harmony export */ });
 /* 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/. */
 
 /**
  * BASE_PARAMS keys/values can be modified from outside this file
  */
 const BASE_PARAMS = {
   utm_source: "activity-stream",
   utm_campaign: "firstrun",
-  utm_medium: "referral",
+  utm_medium: "referral"
 };
 
 /**
  * Takes in a url as a string or URL object and returns a URL object with the
  * utm_* parameters added to it. If a URL object is passed in, the paraemeters
  * are added to it (the return value can be ignored in that case as it's the
  * same object).
  */
@@ -2252,30 +2257,29 @@ function addUtmParams(url, utmTerm) {
     if (!returnUrl.searchParams.has(key)) {
       returnUrl.searchParams.append(key, value);
     }
   }
   returnUrl.searchParams.append("utm_term", utmTerm);
   return returnUrl;
 }
 
-
 /***/ }),
 /* 20 */
 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
 
 __webpack_require__.r(__webpack_exports__);
 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
 /* harmony export */   "ReturnToAMO": () => (/* binding */ ReturnToAMO)
 /* harmony export */ });
 /* 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_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
+/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
 /* harmony import */ var _MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
-/* harmony import */ var _lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(19);
+/* harmony import */ var asrouter_content_src_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(19);
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 
 
 
@@ -2283,17 +2287,17 @@ class ReturnToAMO extends (react__WEBPAC
   constructor(props) {
     super(props);
     this.fetchFlowParams = this.fetchFlowParams.bind(this);
     this.handleAction = this.handleAction.bind(this);
   }
   async fetchFlowParams() {
     if (this.props.metricsFlowUri) {
       this.setState({
-        flowParams: await _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.fetchFlowParams(this.props.metricsFlowUri)
+        flowParams: await _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.fetchFlowParams(this.props.metricsFlowUri)
       });
     }
   }
   componentDidUpdate() {
     this.fetchFlowParams();
   }
   handleAction(event) {
     const {
@@ -2316,35 +2320,35 @@ class ReturnToAMO extends (react__WEBPAC
       }
       // Set add-on url in action.data.url property from JSON
       data = {
         ...data,
         url
       };
     } else if (type === "SHOW_FIREFOX_ACCOUNTS") {
       let params = {
-        ..._lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_3__.BASE_PARAMS,
+        ...asrouter_content_src_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_3__.BASE_PARAMS,
         utm_term: `aboutwelcome-${utm_term}-screen`
       };
       if (action.addFlowParams && this.state.flowParams) {
         params = {
           ...params,
           ...this.state.flowParams
         };
       }
       data = {
         ...data,
         extraParams: params
       };
     }
-    _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.handleUserAction({
+    _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.handleUserAction({
       type,
       data
     });
-    _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.sendActionTelemetry(message_id, source_id);
+    _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.sendActionTelemetry(message_id, source_id);
   }
   render() {
     const {
       content,
       type
     } = this.props;
     if (!content) {
       return null;
@@ -2370,17 +2374,17 @@ class ReturnToAMO extends (react__WEBPAC
       isSingleScreen: true,
       autoAdvance: this.props.auto_advance,
       iconURL: type.includes("theme") ? this.props.themeScreenshots[0]?.url : this.props.iconURL,
       addonName: this.props.name,
       handleAction: this.handleAction
     }));
   }
 }
-ReturnToAMO.defaultProps = _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.DEFAULT_RTAMO_CONTENT;
+ReturnToAMO.defaultProps = _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__.DEFAULT_RTAMO_CONTENT;
 
 /***/ })
 /******/ 	]);
 /************************************************************************/
 /******/ 	// The module cache
 /******/ 	var __webpack_module_cache__ = {};
 /******/ 	
 /******/ 	// The require function
@@ -2449,17 +2453,17 @@ ReturnToAMO.defaultProps = _lib_aboutwel
 var __webpack_exports__ = {};
 // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
 (() => {
 __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 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
+/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
 /* harmony import */ var _components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
 /* harmony import */ var _components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(20);
 function _extends() { _extends = Object.assign ? Object.assign.bind() : 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/. */
 
 
@@ -2486,17 +2490,17 @@ class AboutWelcome extends (react__WEBPA
     }
     if (document.location.href === "about:welcome") {
       // Record impression with performance data after allowing the page to load
       const recordImpression = domState => {
         const {
           domComplete,
           domInteractive
         } = performance.getEntriesByType("navigation").pop();
-        _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendImpressionTelemetry(this.props.messageId, {
+        _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendImpressionTelemetry(this.props.messageId, {
           domComplete,
           domInteractive,
           mountStart: performance.getEntriesByName("mount").pop().startTime,
           domState,
           source: this.props.UTMTerm
         });
       };
       if (document.readyState === "complete") {
--- a/browser/components/aboutwelcome/karma.mc.config.js
+++ b/browser/components/aboutwelcome/karma.mc.config.js
@@ -78,17 +78,17 @@ module.exports = function (config) {
           branches: 66,
           overrides: {
             "modules/*.jsm": {
               statements: 0,
               lines: 0,
               functions: 0,
               branches: 0,
             },
-            "content-src/lib/aboutwelcome-utils.mjs": {
+            "content-src/lib/aboutwelcome-utils.js": {
               statements: 50,
               lines: 50,
               functions: 50,
               branches: 0,
             },
             "content-src/components/LanguageSwitcher.jsx": {
               // This file is covered by the mochitest: browser_aboutwelcome_multistage_languageSwitcher.js
               statements: 0,
@@ -247,17 +247,17 @@ module.exports = function (config) {
             },
           },
           {
             test: /\.md$/,
             use: "raw-loader",
           },
           {
             enforce: "post",
-            test: /\.m?js[mx]?$/,
+            test: /\.js[mx]?$/,
             loader: "@jsdevtools/coverage-istanbul-loader",
             options: { esModules: true },
             include: [path.resolve("content-src"), path.resolve("modules")],
             exclude: [path.resolve("tests"), path.resolve("../newtab")],
           },
         ],
       },
     },
--- a/browser/components/aboutwelcome/tests/unit/MultiStageAboutWelcome.test.jsx
+++ b/browser/components/aboutwelcome/tests/unit/MultiStageAboutWelcome.test.jsx
@@ -5,17 +5,17 @@ import {
   StepsIndicator,
   ProgressBar,
   WelcomeScreen,
 } from "content-src/components/MultiStageAboutWelcome";
 import { Themes } from "content-src/components/Themes";
 import React from "react";
 import { shallow, mount } from "enzyme";
 import { AboutWelcomeDefaults } from "modules/AboutWelcomeDefaults.sys.mjs";
-import { AboutWelcomeUtils } from "content-src/lib/aboutwelcome-utils.mjs";
+import { AboutWelcomeUtils } from "content-src/lib/aboutwelcome-utils";
 
 describe("MultiStageAboutWelcome module", () => {
   let globals;
   let sandbox;
 
   const DEFAULT_PROPS = {
     defaultScreens: AboutWelcomeDefaults.getDefaults().screens,
     metricsFlowUri: "http://localhost/",
--- a/browser/components/asrouter/.eslintrc.js
+++ b/browser/components/asrouter/.eslintrc.js
@@ -1,32 +1,36 @@
 /* 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/. */
 
 module.exports = {
   // When adding items to this file please check for effects on sub-directories.
+  parserOptions: {
+    ecmaFeatures: {
+      jsx: true,
+    },
+  },
   plugins: ["import", "react", "jsx-a11y"],
   settings: {
     react: {
       version: "16.2.0",
     },
   },
   extends: ["plugin:jsx-a11y/recommended"],
   overrides: [
     {
-      // TODO: Bug 1773467 - Move these to .mjs or figure out a generic way
-      // to identify these as modules.
-      files: ["content-src/**/*.js", "tests/unit/**/*.js"],
+      // Only mark the files as modules which are actually modules.
+      files: ["content-src/**", "tests/unit/**"],
       parserOptions: {
         sourceType: "module",
       },
     },
     {
-      files: ["bin/**", "content-src/**", "tests/unit/**"],
+      files: ["bin/**", "./*.js", "content-src/**", "tests/unit/**"],
       env: {
         node: true,
       },
     },
     {
       // Use a configuration that's appropriate for modules, workers and
       // non-production files.
       files: ["tests/**", "modules/**"],
@@ -34,16 +38,18 @@ module.exports = {
         "no-implicit-globals": "off",
       },
     },
     {
       files: ["content-src/**", "tests/unit/**"],
       rules: {
         // Disallow commonjs in these directories.
         "import/no-commonjs": 2,
+        // Allow JSX with arrow functions.
+        "react/jsx-no-bind": 0,
       },
     },
     {
       // These tests simulate the browser environment.
       files: "tests/unit/**",
       env: {
         browser: true,
         mocha: true,
@@ -54,28 +60,26 @@ module.exports = {
         sinon: true,
       },
     },
     {
       files: "tests/**",
       rules: {
         "func-name-matching": 0,
         "lines-between-class-members": 0,
+        "require-await": 0,
       },
     },
   ],
   rules: {
     "fetch-options/no-fetch-credentials": "error",
 
     "react/jsx-boolean-value": ["error", "always"],
     "react/jsx-key": "error",
-    "react/jsx-no-bind": [
-      "error",
-      { allowArrowFunctions: true, allowFunctions: true },
-    ],
+    "react/jsx-no-bind": "error",
     "react/jsx-no-comment-textnodes": "error",
     "react/jsx-no-duplicate-props": "error",
     "react/jsx-no-target-blank": "error",
     "react/jsx-no-undef": "error",
     "react/jsx-pascal-case": "error",
     "react/jsx-uses-react": "error",
     "react/jsx-uses-vars": "error",
     "react/no-access-state-in-setstate": "error",
@@ -88,40 +92,49 @@ module.exports = {
     "react/no-unknown-property": "error",
     "react/require-render-return": "error",
 
     "accessor-pairs": ["error", { setWithoutGet: true, getWithoutSet: false }],
     "array-callback-return": "error",
     "block-scoped-var": "error",
     "consistent-this": ["error", "use-bind"],
     eqeqeq: "error",
+    "for-direction": "error",
     "func-name-matching": "error",
     "getter-return": "error",
     "guard-for-in": "error",
+    "handle-callback-err": "error",
+    "lines-between-class-members": "error",
     "max-depth": ["error", 4],
     "max-nested-callbacks": ["error", 4],
     "max-params": ["error", 6],
     "max-statements": ["error", 50],
+    "max-statements-per-line": ["error", { max: 2 }],
     "new-cap": ["error", { newIsCap: true, capIsNew: false }],
     "no-alert": "error",
+    "no-buffer-constructor": "error",
     "no-console": ["error", { allow: ["error"] }],
     "no-div-regex": "error",
     "no-duplicate-imports": "error",
     "no-eq-null": "error",
     "no-extend-native": "error",
     "no-extra-label": "error",
     "no-implicit-coercion": ["error", { allow: ["!!"] }],
     "no-implicit-globals": "error",
     "no-loop-func": "error",
+    "no-mixed-requires": "error",
     "no-multi-assign": "error",
     "no-multi-str": "error",
     "no-new": "error",
     "no-new-func": "error",
+    "no-new-require": "error",
     "no-octal-escape": "error",
     "no-param-reassign": "error",
+    "no-path-concat": "error",
+    "no-process-exit": "error",
     "no-proto": "error",
     "no-prototype-builtins": "error",
     "no-return-assign": ["error", "except-parens"],
     "no-script-url": "error",
     "no-shadow": "error",
     "no-template-curly-in-string": "error",
     "no-undef-init": "error",
     "no-unmodified-loop-condition": "error",
@@ -142,14 +155,15 @@ module.exports = {
       },
     ],
     "prefer-numeric-literals": "error",
     "prefer-promise-reject-errors": "error",
     "prefer-rest-params": "error",
     "prefer-spread": "error",
     "prefer-template": "error",
     radix: ["error", "always"],
+    "require-await": "error",
     "sort-vars": "error",
     "symbol-description": "error",
     "vars-on-top": "error",
     yoda: ["error", "never"],
   },
 };
--- a/browser/components/asrouter/content-src/components/ASRouterAdmin/ASRouterAdmin.jsx
+++ b/browser/components/asrouter/content-src/components/ASRouterAdmin/ASRouterAdmin.jsx
@@ -502,32 +502,34 @@ export class ASRouterAdminInner extends 
           >
             {isBlocked ? "Unblock" : "Block"}
           </button>
           {
             // eslint-disable-next-line no-nested-ternary
             isBlocked ? null : isModified ? (
               <button
                 className="button restore"
+                // eslint-disable-next-line react/jsx-no-bind
                 onClick={e => this.resetJSON(msg)}
               >
                 Reset
               </button>
             ) : (
               <button
                 className="button show"
                 onClick={this.handleOverride(msg.id)}
               >
                 Show
               </button>
             )
           }
           {isBlocked ? null : (
             <button
               className="button modify"
+              // eslint-disable-next-line react/jsx-no-bind
               onClick={e => this.modifyJson(msg)}
             >
               Modify
             </button>
           )}
           {aboutMessagePreviewSupported ? (
             <CopyButton
               transformer={text =>
@@ -552,16 +554,17 @@ export class ASRouterAdminInner extends 
           )}
           <tr>
             <pre className={isCollapsed ? "collapsed" : "expanded"}>
               <textarea
                 id={`${msg.id}-textarea`}
                 name={msg.id}
                 className="general-textarea"
                 disabled={isBlocked}
+                // eslint-disable-next-line react/jsx-no-bind
                 onChange={e => this.handleChange(msg.id)}
               >
                 {JSON.stringify(msg, null, 2)}
               </textarea>
             </pre>
           </tr>
         </td>
       </tr>
@@ -693,16 +696,17 @@ export class ASRouterAdminInner extends 
               message.provider === this.state.messageFilter &&
               message.template !== "pb_newtab"
           );
 
     return (
       <div>
         <button
           className="ASRouterButton slim"
+          // eslint-disable-next-line react/jsx-no-bind
           onClick={e => this.toggleAllMessages(messagesToShow)}
         >
           Collapse/Expand All
         </button>
         <p className="helpLink">
           <span className="icon icon-small-spacer icon-info" />{" "}
           <span>
             To modify a message, change the JSON and click 'Modify' to see your
@@ -1302,16 +1306,17 @@ export class ASRouterAdminInner extends 
             id={`clear radio`}
             name="PB_message_radio"
             value="clearPBMessage"
             style={{ display: "none" }}
           />
           <h2>Messages</h2>
           <button
             className="ASRouterButton slim button"
+            // eslint-disable-next-line react/jsx-no-bind
             onClick={e => this.toggleAllMessages(messagesToShow)}
           >
             Collapse/Expand All
           </button>
           {this.renderPBMessages()}
         </div>
       </div>
     );
new file mode 100644
--- /dev/null
+++ b/browser/components/asrouter/content-src/template-utils.js
@@ -0,0 +1,22 @@
+/* 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/. */
+
+export function safeURI(url) {
+  if (!url) {
+    return "";
+  }
+  const { protocol } = new URL(url);
+  const isAllowed = [
+    "http:",
+    "https:",
+    "data:",
+    "resource:",
+    "chrome:",
+  ].includes(protocol);
+  if (!isAllowed) {
+    // eslint-disable-next-line no-console
+    console.warn(`The protocol ${protocol} is not allowed for template URLs.`);
+  }
+  return isAllowed ? url : "";
+}
rename from browser/components/aboutwelcome/content-src/lib/addUtmParams.mjs
rename to browser/components/asrouter/content-src/templates/FirstRun/addUtmParams.js
--- a/browser/components/asrouter/content/asrouter-admin.bundle.js
+++ b/browser/components/asrouter/content/asrouter-admin.bundle.js
@@ -1424,39 +1424,45 @@ class ASRouterAdminInner extends (react_
     })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", {
       className: "button-column"
     }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", {
       className: `button ${isBlocked ? "" : " primary"}`,
       onClick: isBlocked ? this.handleUnblock(msg) : this.handleBlock(msg)
     }, isBlocked ? "Unblock" : "Block"),
     // eslint-disable-next-line no-nested-ternary
     isBlocked ? null : isModified ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", {
-      className: "button restore",
+      className: "button restore"
+      // eslint-disable-next-line react/jsx-no-bind
+      ,
       onClick: e => this.resetJSON(msg)
     }, "Reset") : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", {
       className: "button show",
       onClick: this.handleOverride(msg.id)
     }, "Show"), isBlocked ? null : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", {
-      className: "button modify",
+      className: "button modify"
+      // eslint-disable-next-line react/jsx-no-bind
+      ,
       onClick: e => this.modifyJson(msg)
     }, "Modify"), aboutMessagePreviewSupported ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(_CopyButton__WEBPACK_IMPORTED_MODULE_4__.CopyButton, {
       transformer: text => `about:messagepreview?json=${encodeURIComponent(btoa(text))}`,
       label: "Share",
       copiedLabel: "Copied!",
       inputSelector: `#${msg.id}-textarea`,
       className: "button share"
     }) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("br", null), "(", impressions, " impressions)"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", {
       className: "message-summary"
     }, isBlocked && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, "Block reason:", isBlockedByGroup && " Blocked by group", isProviderExcluded && " Excluded by provider", isMessageBlocked && " Message blocked"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("pre", {
       className: isCollapsed ? "collapsed" : "expanded"
     }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("textarea", {
       id: `${msg.id}-textarea`,
       name: msg.id,
       className: "general-textarea",
-      disabled: isBlocked,
+      disabled: isBlocked
+      // eslint-disable-next-line react/jsx-no-bind
+      ,
       onChange: e => this.handleChange(msg.id)
     }, JSON.stringify(msg, null, 2))))));
   }
   selectPBMessage(msgId) {
     if (msgId === "clear") {
       this.setState({
         selectedPBMessage: ""
       });
@@ -1536,17 +1542,19 @@ class ASRouterAdminInner extends (react_
     }
   }
   renderMessages() {
     if (!this.state.messages) {
       return null;
     }
     const messagesToShow = this.state.messageFilter === "all" ? this.state.messages : this.state.messages.filter(message => message.provider === this.state.messageFilter && message.template !== "pb_newtab");
     return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", {
-      className: "ASRouterButton slim",
+      className: "ASRouterButton slim"
+      // eslint-disable-next-line react/jsx-no-bind
+      ,
       onClick: e => this.toggleAllMessages(messagesToShow)
     }, "Collapse/Expand All"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("p", {
       className: "helpLink"
     }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", {
       className: "icon icon-small-spacer icon-info"
     }), " ", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", null, "To modify a message, change the JSON and click 'Modify' to see your changes. Click 'Reset' to restore the JSON to the original. Click 'Share' to copy a link to the clipboard that can be used to preview the message by opening the link in Nightly/local builds.")), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("table", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tbody", null, messagesToShow.map(msg => this.renderMessageItem(msg)))));
   }
   renderMessagesByGroup() {
@@ -1838,17 +1846,19 @@ class ASRouterAdminInner extends (react_
       type: "radio",
       id: `clear radio`,
       name: "PB_message_radio",
       value: "clearPBMessage",
       style: {
         display: "none"
       }
     }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("h2", null, "Messages"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", {
-      className: "ASRouterButton slim button",
+      className: "ASRouterButton slim button"
+      // eslint-disable-next-line react/jsx-no-bind
+      ,
       onClick: e => this.toggleAllMessages(messagesToShow)
     }, "Collapse/Expand All"), this.renderPBMessages()));
   }
   getSection() {
     const [section] = this.props.location.routes;
     switch (section) {
       case "private":
         return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement((react__WEBPACK_IMPORTED_MODULE_1___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("h2", null, "Private Browsing Messages"), this.renderPBTab());
rename from browser/components/aboutwelcome/tests/unit/addUtmParams.test.js
rename to browser/components/asrouter/tests/unit/addUtmParams.test.js
--- a/browser/components/aboutwelcome/tests/unit/addUtmParams.test.js
+++ b/browser/components/asrouter/tests/unit/addUtmParams.test.js
@@ -1,9 +1,12 @@
-import { addUtmParams, BASE_PARAMS } from "content-src/lib/addUtmParams.mjs";
+import {
+  addUtmParams,
+  BASE_PARAMS,
+} from "content-src/templates/FirstRun/addUtmParams";
 
 describe("addUtmParams", () => {
   it("should convert a string URL", () => {
     const result = addUtmParams("https://foo.com", "foo");
     assert.equal(result.hostname, "foo.com");
   });
   it("should add all base params", () => {
     assert.match(
new file mode 100644
--- /dev/null
+++ b/browser/components/asrouter/tests/unit/template-utils.test.js
@@ -0,0 +1,31 @@
+import { safeURI } from "content-src/template-utils";
+
+describe("safeURI", () => {
+  let warnStub;
+  beforeEach(() => {
+    warnStub = sinon.stub(console, "warn");
+  });
+  afterEach(() => {
+    warnStub.restore();
+  });
+  it("should allow http: URIs", () => {
+    assert.equal(safeURI("http://foo.com"), "http://foo.com");
+  });
+  it("should allow https: URIs", () => {
+    assert.equal(safeURI("https://foo.com"), "https://foo.com");
+  });
+  it("should allow data URIs", () => {
+    assert.equal(
+      safeURI("data:image/png;base64,iVBO"),
+      "data:image/png;base64,iVBO"
+    );
+  });
+  it("should not allow javascript: URIs", () => {
+    assert.equal(safeURI("javascript:foo()"), ""); // eslint-disable-line no-script-url
+    assert.calledOnce(warnStub);
+  });
+  it("should not warn if the URL is falsey ", () => {
+    assert.equal(safeURI(), "");
+    assert.notCalled(warnStub);
+  });
+});
--- a/browser/components/newtab/.eslintrc.js
+++ b/browser/components/newtab/.eslintrc.js
@@ -1,30 +1,30 @@
 /* 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/. */
 
 module.exports = {
   // When adding items to this file please check for effects on sub-directories.
+  parserOptions: {
+    ecmaFeatures: {
+      jsx: true,
+    },
+  },
   plugins: ["import", "react", "jsx-a11y"],
   settings: {
     react: {
       version: "16.2.0",
     },
   },
   extends: ["plugin:jsx-a11y/recommended"],
   overrides: [
     {
-      // TODO: Bug 1773467 - Move these to .mjs or figure out a generic way
-      // to identify these as modules.
-      files: [
-        "content-src/**/*.js",
-        "test/schemas/**/*.js",
-        "test/unit/**/*.js",
-      ],
+      // Only mark the files as modules which are actually modules.
+      files: ["content-src/**", "test/schemas/**", "test/unit/**"],
       parserOptions: {
         sourceType: "module",
       },
     },
     {
       // These files use fluent-dom to insert content
       files: [
         "content-src/asrouter/templates/OnboardingMessage/**",
@@ -42,16 +42,17 @@ module.exports = {
         "jsx-a11y/label-has-associated-control": "off",
         "jsx-a11y/no-onchange": "off",
       },
     },
     {
       files: [
         "bin/**",
         "content-src/**",
+        "./*.js",
         "loaders/**",
         "tools/**",
         "test/unit/**",
       ],
       env: {
         node: true,
       },
     },
@@ -63,16 +64,18 @@ module.exports = {
         "no-implicit-globals": "off",
       },
     },
     {
       files: ["content-src/**", "test/unit/**"],
       rules: {
         // Disallow commonjs in these directories.
         "import/no-commonjs": 2,
+        // Allow JSX with arrow functions.
+        "react/jsx-no-bind": 0,
       },
     },
     {
       // These tests simulate the browser environment.
       files: "test/unit/**",
       env: {
         browser: true,
         mocha: true,
@@ -83,28 +86,26 @@ module.exports = {
         sinon: true,
       },
     },
     {
       files: "test/**",
       rules: {
         "func-name-matching": 0,
         "lines-between-class-members": 0,
+        "require-await": 0,
       },
     },
   ],
   rules: {
     "fetch-options/no-fetch-credentials": "error",
 
     "react/jsx-boolean-value": ["error", "always"],
     "react/jsx-key": "error",
-    "react/jsx-no-bind": [
-      "error",
-      { allowArrowFunctions: true, allowFunctions: true },
-    ],
+    "react/jsx-no-bind": "error",
     "react/jsx-no-comment-textnodes": "error",
     "react/jsx-no-duplicate-props": "error",
     "react/jsx-no-target-blank": "error",
     "react/jsx-no-undef": "error",
     "react/jsx-pascal-case": "error",
     "react/jsx-uses-react": "error",
     "react/jsx-uses-vars": "error",
     "react/no-access-state-in-setstate": "error",
@@ -117,40 +118,49 @@ module.exports = {
     "react/no-unknown-property": "error",
     "react/require-render-return": "error",
 
     "accessor-pairs": ["error", { setWithoutGet: true, getWithoutSet: false }],
     "array-callback-return": "error",
     "block-scoped-var": "error",
     "consistent-this": ["error", "use-bind"],
     eqeqeq: "error",
+    "for-direction": "error",
     "func-name-matching": "error",
     "getter-return": "error",
     "guard-for-in": "error",
+    "handle-callback-err": "error",
+    "lines-between-class-members": "error",
     "max-depth": ["error", 4],
     "max-nested-callbacks": ["error", 4],
     "max-params": ["error", 6],
     "max-statements": ["error", 50],
+    "max-statements-per-line": ["error", { max: 2 }],
     "new-cap": ["error", { newIsCap: true, capIsNew: false }],
     "no-alert": "error",
+    "no-buffer-constructor": "error",
     "no-console": ["error", { allow: ["error"] }],
     "no-div-regex": "error",
     "no-duplicate-imports": "error",
     "no-eq-null": "error",
     "no-extend-native": "error",
     "no-extra-label": "error",
     "no-implicit-coercion": ["error", { allow: ["!!"] }],
     "no-implicit-globals": "error",
     "no-loop-func": "error",
+    "no-mixed-requires": "error",
     "no-multi-assign": "error",
     "no-multi-str": "error",
     "no-new": "error",
     "no-new-func": "error",
+    "no-new-require": "error",
     "no-octal-escape": "error",
     "no-param-reassign": "error",
+    "no-path-concat": "error",
+    "no-process-exit": "error",
     "no-proto": "error",
     "no-prototype-builtins": "error",
     "no-return-assign": ["error", "except-parens"],
     "no-script-url": "error",
     "no-shadow": "error",
     "no-template-curly-in-string": "error",
     "no-undef-init": "error",
     "no-unmodified-loop-condition": "error",
@@ -171,14 +181,15 @@ module.exports = {
       },
     ],
     "prefer-numeric-literals": "error",
     "prefer-promise-reject-errors": "error",
     "prefer-rest-params": "error",
     "prefer-spread": "error",
     "prefer-template": "error",
     radix: ["error", "always"],
+    "require-await": "error",
     "sort-vars": "error",
     "symbol-description": "error",
     "vars-on-top": "error",
     yoda: ["error", "never"],
   },
 };
--- a/browser/components/newtab/content-src/components/Search/Search.jsx
+++ b/browser/components/newtab/content-src/components/Search/Search.jsx
@@ -1,13 +1,14 @@
 /* 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/. */
 
 /* globals ContentSearchUIController, ContentSearchHandoffUIController */
+"use strict";
 
 import {
   actionCreators as ac,
   actionTypes as at,
 } from "common/Actions.sys.mjs";
 import { connect } from "react-redux";
 import { IS_NEWTAB } from "content-src/lib/constants";
 import React from "react";
--- a/browser/components/newtab/data/content/activity-stream.bundle.js
+++ b/browser/components/newtab/data/content/activity-stream.bundle.js
@@ -8878,16 +8878,17 @@ const NEWTAB_DARK_THEME = {
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* globals ContentSearchUIController, ContentSearchHandoffUIController */
 
 
 
 
 
+
 class _Search extends (external_React_default()).PureComponent {
   constructor(props) {
     super(props);
     this.onSearchClick = this.onSearchClick.bind(this);
     this.onSearchHandoffClick = this.onSearchHandoffClick.bind(this);
     this.onSearchHandoffPaste = this.onSearchHandoffPaste.bind(this);
     this.onSearchHandoffDrop = this.onSearchHandoffDrop.bind(this);
     this.onInputMount = this.onInputMount.bind(this);
--- a/browser/components/newtab/test/unit/content-src/components/DiscoveryStreamComponents/ImpressionStats.test.jsx
+++ b/browser/components/newtab/test/unit/content-src/components/DiscoveryStreamComponents/ImpressionStats.test.jsx
@@ -1,8 +1,10 @@
+"use strict";
+
 import {
   ImpressionStats,
   INTERSECTION_RATIO,
 } from "content-src/components/DiscoveryStreamImpressionStats/ImpressionStats";
 import { actionTypes as at } from "common/Actions.sys.mjs";
 import React from "react";
 import { shallow } from "enzyme";
 
--- a/browser/components/newtab/test/unit/content-src/components/TopSites/TopSiteImpressionWrapper.test.jsx
+++ b/browser/components/newtab/test/unit/content-src/components/TopSites/TopSiteImpressionWrapper.test.jsx
@@ -1,8 +1,10 @@
+"use strict";
+
 import {
   TopSiteImpressionWrapper,
   INTERSECTION_RATIO,
 } from "content-src/components/TopSites/TopSiteImpressionWrapper";
 import { actionTypes as at } from "common/Actions.sys.mjs";
 import React from "react";
 import { shallow } from "enzyme";
 
--- a/browser/components/pocket/webpack.config.js
+++ b/browser/components/pocket/webpack.config.js
@@ -1,11 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* eslint-env node */
 
 module.exports = {
   mode: "production",
   entry: {
     main: "./content/panels/js/main.js",
   },
   output: {
     filename: "[name].bundle.js",
--- a/browser/components/storybook/custom-elements-manifest.config.mjs
+++ b/browser/components/storybook/custom-elements-manifest.config.mjs
@@ -5,18 +5,18 @@
 /**
  * Custom element manifest analyzer plugin to remove static and private
  * properties from custom-elements.json that we don't want to document in our
  * Storybook props tables.
  */
 function removePrivateAndStaticFields() {
   return {
     packageLinkPhase({ customElementsManifest }) {
-      customElementsManifest?.modules?.forEach(m => {
-        m?.declarations?.forEach(declaration => {
+      customElementsManifest?.modules?.forEach(module => {
+        module?.declarations?.forEach(declaration => {
           if (declaration.members != null) {
             declaration.members = declaration.members.filter(member => {
               return (
                 !member.kind === "field" ||
                 (!member.static && !member.name.startsWith("#"))
               );
             });
           }
--- a/devtools/client/debugger/babel.config.js
+++ b/devtools/client/debugger/babel.config.js
@@ -1,14 +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/>. */
 
 "use strict";
 
+/* global __dirname */
+
 /**
  * NOTE: This file does not apply to builds in MC. This config is used for
  * our Jest tests and for webpack bundle builds.
  */
 module.exports = {
   sourceType: "unambiguous",
   overrides: [
     {
--- a/devtools/client/debugger/jest-test.config.js
+++ b/devtools/client/debugger/jest-test.config.js
@@ -1,14 +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/>. */
 
 "use strict";
 
+/* global __dirname */
+
 const sharedJestConfig = require(`${__dirname}/../shared/test-helpers/shared-jest.config`);
 
 module.exports = {
   testEnvironment: "jsdom",
   testPathIgnorePatterns: [
     "/node_modules/",
     "/helpers/",
     "/fixtures/",
--- a/devtools/client/debugger/jest.config.js
+++ b/devtools/client/debugger/jest.config.js
@@ -1,13 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 "use strict";
 
+/* global __dirname */
+
 const { resolve } = require("path");
 const rootDir = resolve(__dirname);
 module.exports = {
   rootDir,
   reporters: ["default"],
   projects: ["<rootDir>/jest-test.config.js"],
 };
--- a/devtools/client/shared/sourceeditor/webpack.config.js
+++ b/devtools/client/shared/sourceeditor/webpack.config.js
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+/* global __dirname */
 const path = require("path");
 
 module.exports = (env, argv) => {
   return {
     bail: true,
     entry: [
       "./codemirror/addon/dialog/dialog.js",
       "./codemirror/addon/search/searchcursor.js",
--- a/devtools/client/shared/test-helpers/shared-jest.config.js
+++ b/devtools/client/shared/test-helpers/shared-jest.config.js
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+/* global __dirname */
 const fixturesDir = `${__dirname}/jest-fixtures`;
 
 module.exports = {
   verbose: true,
   moduleNameMapper: {
     // Custom name mappers for modules that require m-c specific API.
     "^devtools/shared/generate-uuid": `${fixturesDir}/generate-uuid`,
     "^devtools/shared/DevToolsUtils": `${fixturesDir}/devtools-utils`,
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
@@ -5,21 +5,16 @@
 "use strict";
 
 /**
  * The configuration is based on eslint:recommended config. The details for all
  * the ESLint rules, and which ones are in the recommended configuration can
  * be found here:
  *
  * https://eslint.org/docs/rules/
- *
- * Rules that we've explicitly decided not to enable:
- *
- *   require-await - bug 1381030.
- *   no-prototype-builtins - bug 1551829.
  */
 module.exports = {
   env: {
     browser: true,
     es2022: true,
     "mozilla/privileged": true,
     "mozilla/specific": true,
   },
@@ -53,40 +48,46 @@ module.exports = {
         "mozilla/reject-mixing-eager-and-lazy": "error",
         "mozilla/reject-top-level-await": "error",
         // TODO: Bug 1575506 turn `builtinGlobals` on here.
         // We can enable builtinGlobals for jsms due to their scopes.
         "no-redeclare": ["error", { builtinGlobals: false }],
       },
     },
     {
-      files: ["**/*.mjs", "**/*.jsx", "**/*.jsm", "**/?(*.)worker.?(m)js"],
+      files: ["**/*.mjs", "**/*.jsm", "**/?(*.)worker.?(m)js"],
       rules: {
         // Modules and workers are far easier to check for no-unused-vars on a
         // global scope, than our content files. Hence we turn that on here.
         "no-unused-vars": [
           "error",
           {
             args: "none",
             vars: "all",
           },
         ],
       },
     },
     {
+      files: ["**/*.sys.mjs"],
+      rules: {
+        "mozilla/use-static-import": "error",
+      },
+    },
+    {
       excludedFiles: ["**/*.sys.mjs"],
       files: ["**/*.mjs"],
       rules: {
         "mozilla/reject-import-system-module-from-non-system": "error",
         "mozilla/reject-lazy-imports-into-globals": "error",
         "no-shadow": ["error", { allow: ["event"], builtinGlobals: true }],
       },
     },
     {
-      files: ["**/*.mjs", "**/*.jsx"],
+      files: ["**/*.mjs"],
       parserOptions: {
         sourceType: "module",
       },
       rules: {
         "mozilla/use-static-import": "error",
         // This rule defaults to not allowing "use strict" in module files since
         // they are always loaded in strict mode.
         strict: "error",