Backed out 1 changesets (bug 1565293) did not have approval/request a=backout
authorAndreea Pavel <apavel@mozilla.com>
Fri, 02 Aug 2019 07:30:47 +0300
changeset 541708 03b028ae464bb042d8141ba0f45f5bd99ab81833
parent 541707 27c6f94fdb170b8713aca8227e9f0f572faee5b0
child 541709 07dbf2e071264668aca8b001076b67eeda2eece4
push id11688
push userapavel@mozilla.com
push dateFri, 02 Aug 2019 04:48:59 +0000
treeherdermozilla-beta@07dbf2e07126 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1565293
milestone69.0
backs outa547b44c0ec3629502767bb2480c4b67a3b75cba
Backed out 1 changesets (bug 1565293) did not have approval/request a=backout Backed out changeset a547b44c0ec3 (bug 1565293)
browser/components/newtab/bin/bootstrap
browser/components/newtab/bin/render-activity-stream-html.js
browser/components/newtab/content-src/asrouter/asrouter-content.jsx
browser/components/newtab/content-src/asrouter/docs/targeting-attributes.md
browser/components/newtab/content-src/asrouter/templates/OnboardingMessage/ToolbarBadgeMessage.schema.json
browser/components/newtab/content-src/asrouter/templates/OnboardingMessage/WhatsNewMessage.schema.json
browser/components/newtab/content-src/asrouter/templates/OnboardingMessage/_OnboardingMessage.scss
browser/components/newtab/content-src/asrouter/templates/Trailhead/_Trailhead.scss
browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx
browser/components/newtab/css/activity-stream-linux.css
browser/components/newtab/css/activity-stream-mac.css
browser/components/newtab/css/activity-stream-windows.css
browser/components/newtab/data/content/activity-stream.bundle.js
browser/components/newtab/data/content/assets/whatsnew-send-icon.png
browser/components/newtab/docs/v2-system-addon/1.GETTING_STARTED.md
browser/components/newtab/hooks/post-commit
browser/components/newtab/hooks/pre-commit
browser/components/newtab/hooks/pre-push
browser/components/newtab/lib/ASRouter.jsm
browser/components/newtab/lib/ASRouterTargeting.jsm
browser/components/newtab/lib/AboutPreferences.jsm
browser/components/newtab/lib/BookmarkPanelHub.jsm
browser/components/newtab/lib/LinksCache.jsm
browser/components/newtab/lib/PanelTestProvider.jsm
browser/components/newtab/lib/PersistentCache.jsm
browser/components/newtab/lib/Tokenize.jsm
browser/components/newtab/lib/ToolbarPanelHub.jsm
browser/components/newtab/test/browser/browser_asrouter_targeting.js
browser/components/newtab/test/unit/asrouter/ASRouter.test.js
browser/components/newtab/test/unit/asrouter/ASRouterFeed.test.js
browser/components/newtab/test/unit/asrouter/ASRouterTargeting.test.js
browser/components/newtab/test/unit/asrouter/PanelTestProvider.test.js
browser/components/newtab/test/unit/asrouter/templates/OnboardingMessage.test.jsx
browser/components/newtab/test/unit/lib/AboutPreferences.test.js
browser/components/newtab/test/unit/lib/BookmarkPanelHub.test.js
browser/components/newtab/test/unit/lib/LinksCache.test.js
browser/components/newtab/test/unit/lib/PersistentCache.test.js
browser/components/newtab/test/unit/lib/ToolbarBadgeHub.test.js
browser/components/newtab/test/unit/lib/ToolbarPanelHub.test.js
browser/components/newtab/test/unit/unit-entry.js
deleted file mode 100755
--- a/browser/components/newtab/bin/bootstrap
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/sh -x
-
-# bootstrap an activity-stream repo
-ln -s ../../hooks/pre-commit .git/hooks/pre-commit
-ln -s ../../hooks/post-commit .git/hooks/post-commit
--- a/browser/components/newtab/bin/render-activity-stream-html.js
+++ b/browser/components/newtab/bin/render-activity-stream-html.js
@@ -33,22 +33,17 @@ function templateHTML(options) {
     `${options.baseUrl}data/content/activity-stream.bundle.js`,
   ];
 
   // Add spacing and script tags
   const scriptRender = `\n${scripts
     .map(script => `    <script src="${script}"></script>`)
     .join("\n")}`;
 
-  return `
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this file,
-   - You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<!doctype html>
+  return `<!doctype html>
 <html>
   <head>
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';">
     <title data-l10n-id="newtab-page-title"></title>
     <link rel="icon" type="image/png" href="chrome://branding/content/icon32.png"/>
     <link rel="localization" href="browser/branding/brandings.ftl" />
     <link rel="localization" href="browser/newtab/newtab.ftl" />
@@ -58,17 +53,17 @@ function templateHTML(options) {
   <body class="activity-stream">
     <div id="header-asrouter-container" role="presentation"></div>
     <div id="root"></div>
     <div id="footer-asrouter-container" role="presentation"></div>${
       options.noscripts ? "" : scriptRender
     }
   </body>
 </html>
-`.trimStart();
+`;
 }
 
 /**
  * writeFiles - Writes to the desired files the result of a template given
  * various prerendered data and options.
  *
  * @param {string} destPath      Path to write the files to
  * @param {Map}    filesMap      Mapping of a string file name to templater
--- a/browser/components/newtab/content-src/asrouter/asrouter-content.jsx
+++ b/browser/components/newtab/content-src/asrouter/asrouter-content.jsx
@@ -264,22 +264,19 @@ export class ASRouterUISurface extends R
 
   componentWillUnmount() {
     ASRouterUtils.removeListener(this.onMessageFromParent);
   }
 
   renderSnippets() {
     if (
       this.state.bundle.template === "onboarding" ||
-      [
-        "fxa_overlay",
-        "return_to_amo_overlay",
-        "trailhead",
-        "whatsnew_panel_message",
-      ].includes(this.state.message.template)
+      this.state.message.template === "fxa_overlay" ||
+      this.state.message.template === "return_to_amo_overlay" ||
+      this.state.message.template === "trailhead"
     ) {
       return null;
     }
     const SnippetComponent = SnippetsTemplates[this.state.message.template];
     const { content } = this.state.message;
 
     return (
       <ImpressionsWrapper
--- a/browser/components/newtab/content-src/asrouter/docs/targeting-attributes.md
+++ b/browser/components/newtab/content-src/asrouter/docs/targeting-attributes.md
@@ -29,19 +29,16 @@ Please note that some targeting attribut
 * [topFrecentSites](#topfrecentsites)
 * [totalBookmarksCount](#totalbookmarkscount)
 * [trailheadInterrupt](#trailheadinterrupt)
 * [trailheadTriplet](#trailheadtriplet)
 * [usesFirefoxSync](#usesfirefoxsync)
 * [isFxAEnabled](#isFxAEnabled)
 * [xpinstallEnabled](#xpinstallEnabled)
 * [hasPinnedTabs](#haspinnedtabs)
-* [hasAccessedFxAPanel](#hasaccessedfxapanel)
-* [isWhatsNewPanelEnabled](#iswhatsnewpanelenabled)
-* [earliestFirefoxVersion](#earliestfirefoxversion)
 
 ## Detailed usage
 
 ### `addonsInfo`
 Provides information about the add-ons the user has installed.
 
 Note that the `name`, `userDisabled`, and `installDate` is only available if `isFullData` is `true` (this is usually not the case right at start-up).
 
@@ -472,38 +469,8 @@ declare const xpinstallEnabled: boolean;
 
 Does the user have any pinned tabs in any windows.
 
 #### Definition
 
 ```ts
 declare const hasPinnedTabs: boolean;
 ```
-
-### `hasAccessedFxAPanel`
-
-Boolean pref that gets set the first time the user opens the FxA toolbar panel
-
-#### Definition
-
-```ts
-declare const hasAccessedFxAPanel: boolean;
-```
-
-### `isWhatsNewPanelEnabled`
-
-Boolean pref that controls if the What's New panel feature is enabled
-
-#### Definition
-
-```ts
-declare const isWhatsNewPanelEnabled: boolean;
-```
-
-### `earliestFirefoxVersion`
-
-Integer value of the first Firefox version the profile ran on
-
-#### Definition
-
-```ts
-declare const earliestFirefoxVersion: boolean;
-```
deleted file mode 100644
--- a/browser/components/newtab/content-src/asrouter/templates/OnboardingMessage/ToolbarBadgeMessage.schema.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-  "title": "ToolbarBadgeMessage",
-  "description": "A template that specifies to which element in the browser toolbar to add a notification.",
-  "version": "1.0.0",
-  "type": "object",
-  "properties": {
-    "target": {
-      "type": "string"
-    },
-    "action": {
-      "type": "object",
-      "properties": {
-        "id": {
-          "type": "string"
-        }
-      },
-      "additionalProperties": false,
-      "required": ["id"],
-      "description": "Optional action to take in addition to showing the notification"
-    },
-    "delay": {
-      "type": "number",
-      "description": "Optional delay in ms after which to show the notification"
-    }
-  },
-  "additionalProperties": false,
-  "required": ["target"]
-}
deleted file mode 100644
--- a/browser/components/newtab/content-src/asrouter/templates/OnboardingMessage/WhatsNewMessage.schema.json
+++ /dev/null
@@ -1,62 +0,0 @@
-{
-  "title": "WhatsNewMessage",
-  "description": "A template for the messages that appear in the What's New panel.",
-  "version": "1.0.0",
-  "type": "object",
-  "definitions": {
-    "localizableText": {
-      "oneOf": [
-        {
-          "type": "string",
-          "description": "The string to be rendered."
-        },
-        {
-          "type": "object",
-          "properties": {
-            "string_id": {
-              "type": "string"
-            }
-          },
-          "required": ["string_id"],
-          "description": "Id of localized string to be rendered."
-        }
-      ]
-    }
-  },
-  "properties": {
-    "published_date": {
-      "type": "integer",
-      "description": "The date/time (number of milliseconds elapsed since January 1, 1970 00:00:00 UTC) the message was published."
-    },
-    "title": {
-      "allOf": [
-        {"$ref": "#/definitions/localizableText"},
-        {"description": "Id of localized string or message override of What's New message title"}
-      ]
-    },
-    "body": {
-      "allOf": [
-        {"$ref": "#/definitions/localizableText"},
-        {"description": "Id of localized string or message override of What's New message body"}
-      ]
-    },
-    "link_text": {
-      "allOf": [
-        {"$ref": "#/definitions/localizableText"},
-        {"description": "(optional) Id of localized string or message override of What's New message link text"}
-      ]
-    },
-    "cta_url": {
-      "description": "Target URL for the What's New message.",
-      "type": "string",
-      "format": "uri"
-    },
-    "icon_url": {
-      "description": "(optional) URL for the What's New message icon.",
-      "type": "string",
-      "format": "uri"
-    }
-  },
-  "additionalProperties": false,
-  "required": ["published_date", "title", "body", "cta_url"]
-}
--- a/browser/components/newtab/content-src/asrouter/templates/OnboardingMessage/_OnboardingMessage.scss
+++ b/browser/components/newtab/content-src/asrouter/templates/OnboardingMessage/_OnboardingMessage.scss
@@ -134,18 +134,17 @@
   height: 100px;
   width: 120px;
   background-size: 120px;
   background-position: center center;
   background-repeat: no-repeat;
   display: inline-block;
   vertical-align: middle;
 
-  // Cards will wrap into the next line after this breakpoint
-  @media(max-width: 865px) {
+  @media(max-width: 850px) {
     height: 75px;
     min-width: 80px;
     background-size: 80px;
   }
 
   &.addons {
     background-image: url('#{$image-path}illustration-addons@2x.png');
   }
--- a/browser/components/newtab/content-src/asrouter/templates/Trailhead/_Trailhead.scss
+++ b/browser/components/newtab/content-src/asrouter/templates/Trailhead/_Trailhead.scss
@@ -314,17 +314,16 @@
 
 .trailheadCardGrid {
   display: grid;
   grid-gap: $base-gutter;
   margin: 0;
   opacity: 0;
   transition: opacity 0.4s;
   transition-delay: 0.1s;
-  grid-auto-rows: 1fr;
 
   &.show {
     opacity: 1;
   }
 
   @media (min-width: $break-point-medium) {
     grid-template-columns: repeat(auto-fit, $card-width);
   }
--- a/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx
+++ b/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx
@@ -67,23 +67,30 @@ export class ContextMenu extends React.P
   }
 }
 
 export class ContextMenuItem extends React.PureComponent {
   constructor(props) {
     super(props);
     this.onClick = this.onClick.bind(this);
     this.onKeyDown = this.onKeyDown.bind(this);
+    this.focusFirst = this.focusFirst.bind(this);
   }
 
   onClick() {
     this.props.hideContext();
     this.props.option.onClick();
   }
 
+  focusFirst(button) {
+    if (button) {
+      button.focus();
+    }
+  }
+
   // This selects the correct node based on the key pressed
   focusSibling(target, key) {
     const parent = target.parentNode;
     const closestSiblingSelector =
       key === "ArrowUp" ? "previousSibling" : "nextSibling";
     if (!parent[closestSiblingSelector]) {
       return;
     }
@@ -129,16 +136,17 @@ export class ContextMenuItem extends Rea
     const { option } = this.props;
     return (
       <li role="menuitem" className="context-menu-item">
         <button
           className={option.disabled ? "disabled" : ""}
           tabIndex="0"
           onClick={this.onClick}
           onKeyDown={this.onKeyDown}
+          ref={option.first ? this.focusFirst : null}
         >
           {option.icon && (
             <span className={`icon icon-spacer icon-${option.icon}`} />
           )}
           <span data-l10n-id={option.string_id || option.id} />
         </button>
       </li>
     );
--- a/browser/components/newtab/css/activity-stream-linux.css
+++ b/browser/components/newtab/css/activity-stream-linux.css
@@ -3508,17 +3508,17 @@ body[lwt-newtab-brighttext] .scene2Icon 
 .onboardingMessageImage {
   height: 100px;
   width: 120px;
   background-size: 120px;
   background-position: center center;
   background-repeat: no-repeat;
   display: inline-block;
   vertical-align: middle; }
-  @media (max-width: 865px) {
+  @media (max-width: 850px) {
     .onboardingMessageImage {
       height: 75px;
       min-width: 80px;
       background-size: 80px; } }
   .onboardingMessageImage.addons {
     background-image: url("../data/content/assets/illustration-addons@2x.png"); }
   .onboardingMessageImage.privatebrowsing {
     background-image: url("../data/content/assets/illustration-privatebrowsing@2x.png"); }
@@ -4045,18 +4045,17 @@ a.firstrun-link {
       background-color: var(--newtab-element-hover-color); }
 
 .trailheadCardGrid {
   display: grid;
   grid-gap: 32px;
   margin: 0;
   opacity: 0;
   transition: opacity 0.4s;
-  transition-delay: 0.1s;
-  grid-auto-rows: 1fr; }
+  transition-delay: 0.1s; }
   .trailheadCardGrid.show {
     opacity: 1; }
   @media (min-width: 610px) {
     .trailheadCardGrid {
       grid-template-columns: repeat(auto-fit, 224px); } }
   @media (min-width: 1122px) {
     .trailheadCardGrid {
       grid-template-columns: repeat(auto-fit, 309px); } }
--- a/browser/components/newtab/css/activity-stream-mac.css
+++ b/browser/components/newtab/css/activity-stream-mac.css
@@ -3511,17 +3511,17 @@ body[lwt-newtab-brighttext] .scene2Icon 
 .onboardingMessageImage {
   height: 100px;
   width: 120px;
   background-size: 120px;
   background-position: center center;
   background-repeat: no-repeat;
   display: inline-block;
   vertical-align: middle; }
-  @media (max-width: 865px) {
+  @media (max-width: 850px) {
     .onboardingMessageImage {
       height: 75px;
       min-width: 80px;
       background-size: 80px; } }
   .onboardingMessageImage.addons {
     background-image: url("../data/content/assets/illustration-addons@2x.png"); }
   .onboardingMessageImage.privatebrowsing {
     background-image: url("../data/content/assets/illustration-privatebrowsing@2x.png"); }
@@ -4048,18 +4048,17 @@ a.firstrun-link {
       background-color: var(--newtab-element-hover-color); }
 
 .trailheadCardGrid {
   display: grid;
   grid-gap: 32px;
   margin: 0;
   opacity: 0;
   transition: opacity 0.4s;
-  transition-delay: 0.1s;
-  grid-auto-rows: 1fr; }
+  transition-delay: 0.1s; }
   .trailheadCardGrid.show {
     opacity: 1; }
   @media (min-width: 610px) {
     .trailheadCardGrid {
       grid-template-columns: repeat(auto-fit, 224px); } }
   @media (min-width: 1122px) {
     .trailheadCardGrid {
       grid-template-columns: repeat(auto-fit, 309px); } }
--- a/browser/components/newtab/css/activity-stream-windows.css
+++ b/browser/components/newtab/css/activity-stream-windows.css
@@ -3508,17 +3508,17 @@ body[lwt-newtab-brighttext] .scene2Icon 
 .onboardingMessageImage {
   height: 100px;
   width: 120px;
   background-size: 120px;
   background-position: center center;
   background-repeat: no-repeat;
   display: inline-block;
   vertical-align: middle; }
-  @media (max-width: 865px) {
+  @media (max-width: 850px) {
     .onboardingMessageImage {
       height: 75px;
       min-width: 80px;
       background-size: 80px; } }
   .onboardingMessageImage.addons {
     background-image: url("../data/content/assets/illustration-addons@2x.png"); }
   .onboardingMessageImage.privatebrowsing {
     background-image: url("../data/content/assets/illustration-privatebrowsing@2x.png"); }
@@ -4045,18 +4045,17 @@ a.firstrun-link {
       background-color: var(--newtab-element-hover-color); }
 
 .trailheadCardGrid {
   display: grid;
   grid-gap: 32px;
   margin: 0;
   opacity: 0;
   transition: opacity 0.4s;
-  transition-delay: 0.1s;
-  grid-auto-rows: 1fr; }
+  transition-delay: 0.1s; }
   .trailheadCardGrid.show {
     opacity: 1; }
   @media (min-width: 610px) {
     .trailheadCardGrid {
       grid-template-columns: repeat(auto-fit, 224px); } }
   @media (min-width: 1122px) {
     .trailheadCardGrid {
       grid-template-columns: repeat(auto-fit, 309px); } }
--- a/browser/components/newtab/data/content/activity-stream.bundle.js
+++ b/browser/components/newtab/data/content/activity-stream.bundle.js
@@ -96,19 +96,16 @@
 /* harmony import */ var content_src_lib_init_store__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(24);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_4__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_5__);
 /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(14);
 /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_6__);
 /* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(53);
-/* 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/. */
 
 
 
 
 
 
 
 
@@ -567,19 +564,16 @@ var actionUtils = {
 /* harmony import */ var content_src_components_DiscoveryStreamBase_DiscoveryStreamBase__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(49);
 /* harmony import */ var content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(31);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_7__);
 /* harmony import */ var content_src_components_Search_Search__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(47);
 /* harmony import */ var content_src_components_Sections_Sections__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(36);
 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/. */
 
 
 
 
 
 
 
 
@@ -760,19 +754,16 @@ const Base = Object(react_redux__WEBPACK
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(24);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_2__);
 /* harmony import */ var _asrouter_components_ModalOverlay_ModalOverlay__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(13);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_4__);
 /* harmony import */ var _SimpleHashRouter__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(26);
 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/. */
 
 
 
 
 
 
 
 const Row = props => react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("tr", _extends({
@@ -1728,19 +1719,16 @@ const ASRouterAdmin = Object(react_redux
 /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(14);
 /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_8__);
 /* harmony import */ var _templates_ReturnToAMO_ReturnToAMO__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(15);
 /* harmony import */ var _templates_template_manifest__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(51);
 /* harmony import */ var _templates_StartupOverlay_StartupOverlay__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(23);
 /* harmony import */ var _templates_Trailhead_Trailhead__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(25);
 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/. */
 
 
 
 
 
 
 
 
@@ -2079,17 +2067,17 @@ class ASRouterUISurface extends react__W
     }
   }
 
   componentWillUnmount() {
     ASRouterUtils.removeListener(this.onMessageFromParent);
   }
 
   renderSnippets() {
-    if (this.state.bundle.template === "onboarding" || ["fxa_overlay", "return_to_amo_overlay", "trailhead", "whatsnew_panel_message"].includes(this.state.message.template)) {
+    if (this.state.bundle.template === "onboarding" || this.state.message.template === "fxa_overlay" || this.state.message.template === "return_to_amo_overlay" || this.state.message.template === "trailhead") {
       return null;
     }
 
     const SnippetComponent = _templates_template_manifest__WEBPACK_IMPORTED_MODULE_10__["SnippetsTemplates"][this.state.message.template];
     const {
       content
     } = this.state.message;
     return react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement(_components_ImpressionsWrapper_ImpressionsWrapper__WEBPACK_IMPORTED_MODULE_3__["ImpressionsWrapper"], {
@@ -2223,20 +2211,16 @@ ASRouterUISurface.defaultProps = {
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "INCOMING_MESSAGE_NAME", function() { return INCOMING_MESSAGE_NAME; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "EARLY_QUEUED_ACTIONS", function() { return EARLY_QUEUED_ACTIONS; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rehydrationMiddleware", function() { return rehydrationMiddleware; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "queueEarlyMessageMiddleware", function() { return queueEarlyMessageMiddleware; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "initStore", function() { return initStore; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var redux__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
 /* harmony import */ var redux__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(redux__WEBPACK_IMPORTED_MODULE_1__);
-/* 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 mozilla/frame-script */
 
 
 const MERGE_STORE_ACTION = "NEW_TAB_INITIAL_STATE";
 const OUTGOING_MESSAGE_NAME = "ActivityStream:ContentToMain";
 const INCOMING_MESSAGE_NAME = "ActivityStream:MainToContent";
 const EARLY_QUEUED_ACTIONS = [common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].SAVE_SESSION_PERF_DATA];
 /**
@@ -2388,19 +2372,16 @@ module.exports = Redux;
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VISIBLE", function() { return VISIBLE; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VISIBILITY_CHANGE_EVENT", function() { return VISIBILITY_CHANGE_EVENT; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ImpressionsWrapper", function() { return ImpressionsWrapper; });
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const VISIBLE = "visible";
 const VISIBILITY_CHANGE_EVENT = "visibilitychange";
 /**
  * Component wrapper used to send telemetry pings on every impression.
  */
 
 class ImpressionsWrapper extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
@@ -2477,19 +2458,16 @@ module.exports = PropTypes;
 /***/ }),
 /* 11 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "IS_NEWTAB", function() { return IS_NEWTAB; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "NEWTAB_DARK_THEME", function() { return NEWTAB_DARK_THEME; });
-/* 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 IS_NEWTAB = global.document && global.document.documentURI === "about:newtab";
 const NEWTAB_DARK_THEME = {
   ntp_background: {
     r: 42,
     g: 42,
     b: 46,
     a: 1
   },
@@ -2522,19 +2500,16 @@ const NEWTAB_DARK_THEME = {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "OnboardingCard", function() { return OnboardingCard; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "OnboardingMessage", function() { return OnboardingMessage; });
 /* harmony import */ var _components_ModalOverlay_ModalOverlay__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
 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/. */
 
 
 const FLUENT_FILES = ["branding/brand.ftl", "browser/branding/sync-brand.ftl", "browser/newtab/onboarding.ftl"];
 class OnboardingCard extends react__WEBPACK_IMPORTED_MODULE_1___default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onClick = this.onClick.bind(this);
   }
@@ -2616,19 +2591,16 @@ class OnboardingMessage extends react__W
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ModalOverlayWrapper", function() { return ModalOverlayWrapper; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ModalOverlay", function() { return ModalOverlay; });
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_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/. */
 
 class ModalOverlayWrapper extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onKeyDown = this.onKeyDown.bind(this);
   }
 
   onKeyDown(event) {
@@ -2704,19 +2676,16 @@ module.exports = ReactDOM;
 /***/ (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__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _components_RichText_RichText__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(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/. */
 
  // Alt text if available; in the future this should come from the server. See bug 1551711
 
 const ICON_ALT_TEXT = "";
 class ReturnToAMO extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onClickAddExtension = this.onClickAddExtension.bind(this);
@@ -2801,19 +2770,16 @@ class ReturnToAMO extends react__WEBPACK
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RichText", function() { return RichText; });
 /* harmony import */ var fluent_react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(50);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _rich_text_strings__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(52);
 /* harmony import */ var _template_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(17);
 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/. */
 
 
 
  // Elements allowed in snippet content
 
 const ALLOWED_TAGS = {
   b: react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("b", null),
   i: react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("i", null),
@@ -2869,19 +2835,16 @@ function RichText(props) {
 
 /***/ }),
 /* 17 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "safeURI", function() { return safeURI; });
-/* 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/. */
 function safeURI(url) {
   if (!url) {
     return "";
   }
 
   const {
     protocol
   } = new URL(url);
@@ -2932,19 +2895,16 @@ module.exports = {"title":"SendToDeviceS
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_StartupOverlay", function() { return _StartupOverlay; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StartupOverlay", function() { return StartupOverlay; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(24);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_2__);
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 
 const FLUENT_FILES = ["branding/brand.ftl", "browser/branding/sync-brand.ftl", "browser/newtab/onboarding.ftl"];
 class _StartupOverlay extends react__WEBPACK_IMPORTED_MODULE_2___default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onInputChange = this.onInputChange.bind(this);
@@ -3239,19 +3199,16 @@ module.exports = ReactRedux;
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Trailhead", function() { return Trailhead; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var _components_ModalOverlay_ModalOverlay__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(13);
 /* harmony import */ var _OnboardingMessage_OnboardingMessage__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(12);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_3__);
 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/. */
 
 
 
 
 const FLUENT_FILES = ["branding/brand.ftl", "browser/branding/brandings.ftl", "browser/branding/sync-brand.ftl", "browser/newtab/onboarding.ftl"]; // From resource://devtools/client/shared/focus.js
 
 const FOCUSABLE_SELECTOR = ["a[href]:not([tabindex='-1'])", "button:not([disabled]):not([tabindex='-1'])", "iframe:not([tabindex='-1'])", "input:not([disabled]):not([tabindex='-1'])", "select:not([disabled]):not([tabindex='-1'])", "textarea:not([disabled]):not([tabindex='-1'])", "[tabindex]:not([tabindex='-1'])"].join(", ");
 class Trailhead extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureComponent {
@@ -3658,19 +3615,16 @@ class Trailhead extends react__WEBPACK_I
 /* 26 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SimpleHashRouter", function() { return SimpleHashRouter; });
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_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/. */
 
 class SimpleHashRouter extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onHashChange = this.onHashChange.bind(this);
     this.state = {
       hash: global.location.hash
     };
@@ -3711,19 +3665,16 @@ class SimpleHashRouter extends react__WE
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_ConfirmDialog", function() { return _ConfirmDialog; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ConfirmDialog", function() { return ConfirmDialog; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(24);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_2__);
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 
 /**
  * ConfirmDialog component.
  * One primary action button, one cancel button.
  *
  * Content displayed is controlled by `data` prop the component receives.
@@ -3813,19 +3764,16 @@ const ConfirmDialog = Object(react_redux
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ContextMenu", function() { return ContextMenu; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ContextMenuItem", function() { return ContextMenuItem; });
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_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/. */
 
 class ContextMenu extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
   constructor(props) {
     super(props);
     this.hideContext = this.hideContext.bind(this);
     this.onShow = this.onShow.bind(this);
     this.onClick = this.onClick.bind(this);
   }
@@ -3881,21 +3829,28 @@ class ContextMenu extends react__WEBPACK
   }
 
 }
 class ContextMenuItem extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onClick = this.onClick.bind(this);
     this.onKeyDown = this.onKeyDown.bind(this);
+    this.focusFirst = this.focusFirst.bind(this);
   }
 
   onClick() {
     this.props.hideContext();
     this.props.option.onClick();
+  }
+
+  focusFirst(button) {
+    if (button) {
+      button.focus();
+    }
   } // This selects the correct node based on the key pressed
 
 
   focusSibling(target, key) {
     const parent = target.parentNode;
     const closestSiblingSelector = key === "ArrowUp" ? "previousSibling" : "nextSibling";
 
     if (!parent[closestSiblingSelector]) {
@@ -3948,17 +3903,18 @@ class ContextMenuItem extends react__WEB
     } = this.props;
     return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("li", {
       role: "menuitem",
       className: "context-menu-item"
     }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("button", {
       className: option.disabled ? "disabled" : "",
       tabIndex: "0",
       onClick: this.onClick,
-      onKeyDown: this.onKeyDown
+      onKeyDown: this.onKeyDown,
+      ref: option.first ? this.focusFirst : null
     }, option.icon && react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("span", {
       className: `icon icon-spacer icon-${option.icon}`
     }), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("span", {
       "data-l10n-id": option.string_id || option.id
     })));
   }
 
 }
@@ -3970,19 +3926,16 @@ class ContextMenuItem extends react__WEB
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "INTERSECTION_RATIO", function() { return INTERSECTION_RATIO; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ImpressionStats", function() { return ImpressionStats; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
-/* 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 VISIBLE = "visible";
 const VISIBILITY_CHANGE_EVENT = "visibilitychange"; // Per analytical requirement, we set the minimal intersection ratio to
 // 0.5, and an impression is identified when the wrapped item has at least
 // 50% visibility.
 //
 // This constant is exported for unit test
@@ -4200,19 +4153,16 @@ ImpressionStats.defaultProps = {
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CollapsibleSection", function() { return CollapsibleSection; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(31);
 /* harmony import */ var content_src_components_FluentOrText_FluentOrText__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(33);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_3__);
 /* harmony import */ var content_src_components_SectionMenu_SectionMenu__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(34);
 /* harmony import */ var content_src_lib_section_menu_options__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(35);
-/* 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 VISIBLE = "visible";
 const VISIBILITY_CHANGE_EVENT = "visibilitychange";
@@ -4499,19 +4449,16 @@ CollapsibleSection.defaultProps = {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ErrorBoundaryFallback", function() { return ErrorBoundaryFallback; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ErrorBoundary", function() { return ErrorBoundary; });
 /* harmony import */ var content_src_components_A11yLinkButton_A11yLinkButton__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(32);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
-/* 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 ErrorBoundaryFallback extends react__WEBPACK_IMPORTED_MODULE_1___default.a.PureComponent {
   constructor(props) {
     super(props);
     this.windowObj = this.props.windowObj || window;
     this.onClick = this.onClick.bind(this);
   }
@@ -4586,19 +4533,16 @@ ErrorBoundary.defaultProps = {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "A11yLinkButton", function() { return A11yLinkButton; });
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
 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/. */
 
 function A11yLinkButton(props) {
   // function for merging classes, if necessary
   let className = "a11y-link-button";
 
   if (props.className) {
     className += ` ${props.className}`;
   }
@@ -4614,19 +4558,16 @@ function A11yLinkButton(props) {
 /* 33 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FluentOrText", function() { return FluentOrText; });
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_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/. */
 
 /**
  * Set text on a child element/component depending on if the message is already
  * translated plain text or a fluent id with optional args.
  */
 
 class FluentOrText extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
   render() {
@@ -4664,19 +4605,16 @@ class FluentOrText extends react__WEBPAC
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_SectionMenu", function() { return _SectionMenu; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SectionMenu", function() { return SectionMenu; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var content_src_components_ContextMenu_ContextMenu__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_2__);
 /* harmony import */ var content_src_lib_section_menu_options__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(35);
-/* 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 DEFAULT_SECTION_MENU_OPTIONS = ["MoveUp", "MoveDown", "Separator", "RemoveSection", "CheckCollapsed", "Separator", "ManageSection"];
 const WEBEXT_SECTION_MENU_OPTIONS = ["MoveUp", "MoveDown", "Separator", "CheckCollapsed", "Separator", "ManageWebExtension"];
 class _SectionMenu extends react__WEBPACK_IMPORTED_MODULE_2___default.a.PureComponent {
   getOptions() {
@@ -4743,19 +4681,16 @@ const SectionMenu = _SectionMenu;
 /***/ }),
 /* 35 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SectionMenuOptions", function() { return SectionMenuOptions; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /**
  * List of functions that return items that can be included as menu options in a
  * SectionMenu. All functions take the section as the only parameter.
  */
 
 const SectionMenuOptions = {
   Separator: () => ({
@@ -4890,19 +4825,16 @@ const SectionMenuOptions = {
 /* harmony import */ var content_src_components_MoreRecommendations_MoreRecommendations__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(40);
 /* harmony import */ var content_src_components_PocketLoggedInCta_PocketLoggedInCta__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(41);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_8__);
 /* harmony import */ var content_src_components_Topics_Topics__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(42);
 /* harmony import */ var content_src_components_TopSites_TopSites__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(43);
 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/. */
 
 
 
 
 
 
 
 
@@ -5243,20 +5175,16 @@ const Sections = Object(react_redux__WEB
 
 /***/ }),
 /* 37 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ScreenshotUtils", function() { return ScreenshotUtils; });
-/* 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/. */
-
 /**
  * List of helper functions for screenshot-based images.
  *
  * There are two kinds of images:
  * 1. Remote Image: This is the image from the main process and it refers to
  *    the image in the React props. This can either be an object with the `data`
  *    and `path` properties, if it is a blob, or a string, if it is a normal image.
  * 2. Local Image: This is the image object in the content process and it refers
@@ -5316,19 +5244,16 @@ const ScreenshotUtils = {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ComponentPerfTimer", function() { return ComponentPerfTimer; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var common_PerfService_jsm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(39);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_2__);
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
  // Currently record only a fixed set of sections. This will prevent data
 // from custom sections from showing up or from topstories.
 
 const RECORDED_SECTIONS = ["highlights", "topsites"];
 class ComponentPerfTimer extends react__WEBPACK_IMPORTED_MODULE_2___default.a.Component {
   constructor(props) {
@@ -5493,19 +5418,16 @@ class ComponentPerfTimer extends react__
 /***/ }),
 /* 39 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_PerfService", function() { return _PerfService; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "perfService", function() { return perfService; });
-/* 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 (typeof ChromeUtils !== "undefined") {
   // Use a var here instead of let outside to avoid creating a locally scoped
   // variable that hides the global, which we modify for testing.
   // eslint-disable-next-line no-var, vars-on-top
   var {
     Services
@@ -5626,19 +5548,16 @@ var perfService = new _PerfService();
 /* 40 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MoreRecommendations", function() { return MoreRecommendations; });
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_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/. */
 
 class MoreRecommendations extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
   render() {
     const {
       read_more_endpoint
     } = this.props;
 
     if (read_more_endpoint) {
@@ -5661,19 +5580,16 @@ class MoreRecommendations extends react_
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_PocketLoggedInCta", function() { return _PocketLoggedInCta; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PocketLoggedInCta", function() { return PocketLoggedInCta; });
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(24);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
-/* 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 _PocketLoggedInCta extends react__WEBPACK_IMPORTED_MODULE_1___default.a.PureComponent {
   render() {
     const {
       pocketCta
     } = this.props.Pocket;
     return react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("span", {
@@ -5702,19 +5618,16 @@ const PocketLoggedInCta = Object(react_r
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Topic", function() { return Topic; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Topics", function() { return Topics; });
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_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/. */
 
 class Topic extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
   render() {
     const {
       url,
       name
     } = this.props;
     return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("li", null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("a", {
@@ -5760,19 +5673,16 @@ class Topics extends react__WEBPACK_IMPO
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_6__);
 /* harmony import */ var _SearchShortcutsForm__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(45);
 /* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(53);
 /* harmony import */ var _TopSiteForm__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(55);
 /* harmony import */ var _TopSite__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(46);
 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/. */
 
 
 
 
 
 
 
 
@@ -5974,19 +5884,16 @@ const TopSites = Object(react_redux__WEB
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TOP_SITES_SOURCE", function() { return TOP_SITES_SOURCE; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TOP_SITES_CONTEXT_MENU_OPTIONS", function() { return TOP_SITES_CONTEXT_MENU_OPTIONS; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS", function() { return TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MIN_RICH_FAVICON_SIZE", function() { return MIN_RICH_FAVICON_SIZE; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MIN_CORNER_FAVICON_SIZE", function() { return MIN_CORNER_FAVICON_SIZE; });
-/* 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 TOP_SITES_SOURCE = "TOP_SITES";
 const TOP_SITES_CONTEXT_MENU_OPTIONS = ["CheckPinTopSite", "EditTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl", "DeleteUrl"]; // the special top site for search shortcut experiment can only have the option to unpin (which removes) the topsite
 
 const TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS = ["CheckPinTopSite", "Separator", "BlockUrl"]; // minimum size necessary to show a rich icon instead of a screenshot
 
 const MIN_RICH_FAVICON_SIZE = 96; // minimum size necessary to show any icon in the top left corner with a screenshot
 
 const MIN_CORNER_FAVICON_SIZE = 16;
@@ -5998,19 +5905,16 @@ const MIN_CORNER_FAVICON_SIZE = 16;
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SelectableSearchShortcut", function() { return SelectableSearchShortcut; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SearchShortcutsForm", function() { return SearchShortcutsForm; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(44);
-/* 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 SelectableSearchShortcut extends react__WEBPACK_IMPORTED_MODULE_1___default.a.PureComponent {
   render() {
     const {
       shortcut,
       selected
@@ -6196,19 +6100,16 @@ class SearchShortcutsForm extends react_
 /* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(44);
 /* harmony import */ var content_src_components_LinkMenu_LinkMenu__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(56);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_3__);
 /* harmony import */ var content_src_lib_screenshot_utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(37);
 /* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(53);
 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 TopSiteLink extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureComponent {
   constructor(props) {
@@ -6841,20 +6742,16 @@ class TopSiteList extends react__WEBPACK
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_Search", function() { return _Search; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Search", function() { return Search; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(24);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var content_src_lib_constants__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(11);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_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/. */
-
 /* globals ContentSearchUIController */
 
 
 
 
 
 
 class _Search extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureComponent {
@@ -7024,19 +6921,16 @@ const Search = Object(react_redux__WEBPA
 /* 48 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DetectUserSessionStart", function() { return DetectUserSessionStart; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var common_PerfService_jsm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(39);
-/* 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 VISIBLE = "visible";
 const VISIBILITY_CHANGE_EVENT = "visibilitychange";
 class DetectUserSessionStart {
   constructor(store, options = {}) {
     this._store = store; // Overrides for testing
 
@@ -7116,19 +7010,16 @@ var Actions = __webpack_require__(2);
 var external_React_ = __webpack_require__(9);
 var external_React_default = /*#__PURE__*/__webpack_require__.n(external_React_);
 
 // EXTERNAL MODULE: external "ReactDOM"
 var external_ReactDOM_ = __webpack_require__(14);
 var external_ReactDOM_default = /*#__PURE__*/__webpack_require__.n(external_ReactDOM_);
 
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/DSImage/DSImage.jsx
-/* 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 DSImage_DSImage extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onOptimizedImageError = this.onOptimizedImageError.bind(this);
     this.onNonOptimizedImageError = this.onNonOptimizedImageError.bind(this);
     this.state = {
@@ -7244,19 +7135,16 @@ DSImage_DSImage.defaultProps = {
   // Additional classnames to append to component
   optimize: true // Measure parent container to request exact sizes
 
 };
 // EXTERNAL MODULE: ./content-src/components/LinkMenu/LinkMenu.jsx + 1 modules
 var LinkMenu = __webpack_require__(56);
 
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx
-/* 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 DSLinkMenu_DSLinkMenu extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
     this.state = {
       activeCard: null,
       showContextMenu: false
@@ -7343,19 +7231,16 @@ class DSLinkMenu_DSLinkMenu extends exte
     }));
   }
 
 }
 // EXTERNAL MODULE: ./content-src/components/DiscoveryStreamImpressionStats/ImpressionStats.jsx
 var ImpressionStats = __webpack_require__(29);
 
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/SafeAnchor/SafeAnchor.jsx
-/* 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 SafeAnchor_SafeAnchor extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onClick = this.onClick.bind(this);
   }
 
@@ -7422,19 +7307,16 @@ class SafeAnchor_SafeAnchor extends exte
       href: this.safeURI(url),
       className: className,
       onClick: this.onClick
     }, this.props.children);
   }
 
 }
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
-/* 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 DSCard_DSCard extends external_React_default.a.PureComponent {
   constructor(props) {
@@ -7514,19 +7396,16 @@ class DSCard_DSCard extends external_Rea
     }));
   }
 
 }
 const PlaceholderDSCard = props => external_React_default.a.createElement(DSCard_DSCard, {
   placeholder: true
 });
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/DSEmptyState/DSEmptyState.jsx
-/* 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 DSEmptyState_DSEmptyState extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onReset = this.onReset.bind(this);
     this.state = {};
   }
@@ -7600,19 +7479,16 @@ class DSEmptyState_DSEmptyState extends 
       className: "section-empty-state"
     }, external_React_default.a.createElement("div", {
       className: "empty-state-message"
     }, this.renderState()));
   }
 
 }
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx
-/* 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 CardGrid_CardGrid extends external_React_default.a.PureComponent {
   renderCards() {
     const recs = this.props.data.recommendations.slice(0, this.props.items);
     const cards = [];
 
@@ -7683,19 +7559,16 @@ CardGrid_CardGrid.defaultProps = {
 };
 // EXTERNAL MODULE: ./content-src/components/CollapsibleSection/CollapsibleSection.jsx
 var CollapsibleSection = __webpack_require__(30);
 
 // EXTERNAL MODULE: external "ReactRedux"
 var external_ReactRedux_ = __webpack_require__(24);
 
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/DSMessage/DSMessage.jsx
-/* 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 DSMessage_DSMessage extends external_React_default.a.PureComponent {
   render() {
     return external_React_default.a.createElement("div", {
       className: "ds-message"
     }, external_React_default.a.createElement("header", {
       className: "title"
@@ -7709,19 +7582,16 @@ class DSMessage_DSMessage extends extern
     }, this.props.title), this.props.link_text && this.props.link_url && external_React_default.a.createElement(SafeAnchor_SafeAnchor, {
       className: "link",
       url: this.props.link_url
     }, this.props.link_text)));
   }
 
 }
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/List/List.jsx
-/* 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/. */
 
 
 
 
 
 
 
 
@@ -7881,19 +7751,16 @@ function _List(props) {
   // Display numbers for each item
   items: 6 // Number of stories to display.  TODO: get from endpoint
 
 };
 const List = Object(external_ReactRedux_["connect"])(state => ({
   DiscoveryStream: state.DiscoveryStream
 }))(_List);
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/Hero/Hero.jsx
-/* 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/. */
 
 
 
 
 
 
 
 
@@ -8055,19 +7922,16 @@ Hero_Hero.defaultProps = {
 
 };
 // EXTERNAL MODULE: ./content-src/components/Sections/Sections.jsx
 var Sections = __webpack_require__(36);
 
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/Highlights/Highlights.jsx
 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 Highlights_Highlights extends external_React_default.a.PureComponent {
   render() {
     const section = this.props.Sections.find(s => s.id === "highlights");
 
     if (!section || !section.enabled) {
@@ -8081,32 +7945,26 @@ class Highlights_Highlights extends exte
     })));
   }
 
 }
 const Highlights = Object(external_ReactRedux_["connect"])(state => ({
   Sections: state.Sections
 }))(Highlights_Highlights);
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/HorizontalRule/HorizontalRule.jsx
-/* 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 HorizontalRule_HorizontalRule extends external_React_default.a.PureComponent {
   render() {
     return external_React_default.a.createElement("hr", {
       className: "ds-hr"
     });
   }
 
 }
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/Navigation/Navigation.jsx
-/* 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 Navigation_Topic extends external_React_default.a.PureComponent {
   render() {
     const {
       url,
       name
     } = this.props;
@@ -8134,19 +7992,16 @@ class Navigation_Navigation extends exte
       key: t.name,
       url: t.url,
       name: t.name
     })))));
   }
 
 }
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/SectionTitle/SectionTitle.jsx
-/* 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 SectionTitle_SectionTitle extends external_React_default.a.PureComponent {
   render() {
     const {
       header: {
         title,
         subtitle
       }
@@ -8157,19 +8012,16 @@ class SectionTitle_SectionTitle extends 
       className: "title"
     }, title), subtitle ? external_React_default.a.createElement("div", {
       className: "subtitle"
     }, subtitle) : null);
   }
 
 }
 // CONCATENATED MODULE: ./content-src/lib/selectLayoutRender.js
-/* 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 selectLayoutRender = (state, prefs, rickRollCache) => {
   const {
     layout,
     feeds,
     spocs
   } = state;
   let spocIndex = 0;
   let bufferRollCache = []; // Records the chosen and unchosen spocs by the probability selection.
@@ -8364,19 +8216,16 @@ const selectLayoutRender = (state, prefs
     spocsFill,
     layoutRender
   };
 };
 // EXTERNAL MODULE: ./content-src/components/TopSites/TopSites.jsx
 var TopSites = __webpack_require__(43);
 
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/TopSites/TopSites.jsx
-/* 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 TopSites_TopSites extends external_React_default.a.PureComponent {
   render() {
     const header = this.props.header || {};
     return external_React_default.a.createElement("div", {
       className: "ds-top-sites"
@@ -8389,19 +8238,16 @@ class TopSites_TopSites extends external
 }
 const TopSites_TopSites_TopSites = Object(external_ReactRedux_["connect"])(state => ({
   TopSites: state.TopSites
 }))(TopSites_TopSites);
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isAllowedCSS", function() { return isAllowedCSS; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_DiscoveryStreamBase", function() { return DiscoveryStreamBase_DiscoveryStreamBase; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DiscoveryStreamBase", function() { return DiscoveryStreamBase; });
-/* 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/. */
 
 
 
 
 
 
 
 
@@ -9501,19 +9347,16 @@ localized_Localized.propTypes = {
 // EXTERNAL MODULE: external "React"
 var external_React_ = __webpack_require__(9);
 var external_React_default = /*#__PURE__*/__webpack_require__.n(external_React_);
 
 // EXTERNAL MODULE: ./content-src/asrouter/templates/EOYSnippet/EOYSnippet.schema.json
 var EOYSnippet_schema = __webpack_require__(18);
 
 // CONCATENATED MODULE: ./content-src/asrouter/components/Button/Button.jsx
-/* 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 ALLOWED_STYLE_TAGS = ["color", "backgroundColor"];
 const Button = props => {
   const style = {}; // Add allowed style tags from props, e.g. props.color becomes style={color: props.color}
 
   for (const tag of ALLOWED_STYLE_TAGS) {
     if (typeof props[tag] !== "undefined") {
       style[tag] = props[tag];
@@ -9527,38 +9370,32 @@ const Button = props => {
 
   return external_React_default.a.createElement("button", {
     onClick: props.onClick,
     className: props.className || "ASRouterButton secondary",
     style: style
   }, props.children);
 };
 // CONCATENATED MODULE: ./content-src/asrouter/components/ConditionalWrapper/ConditionalWrapper.jsx
-/* 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/. */
 // lifted from https://gist.github.com/kitze/23d82bb9eb0baabfd03a6a720b1d637f
 const ConditionalWrapper = ({
   condition,
   wrap,
   children
 }) => condition ? wrap(children) : children;
 // EXTERNAL MODULE: ./content-src/asrouter/components/RichText/RichText.jsx
 var RichText = __webpack_require__(16);
 
 // EXTERNAL MODULE: ./content-src/asrouter/template-utils.js
 var template_utils = __webpack_require__(17);
 
 // EXTERNAL MODULE: ./content-src/asrouter/templates/SimpleSnippet/SimpleSnippet.schema.json
 var SimpleSnippet_schema = __webpack_require__(19);
 
 // CONCATENATED MODULE: ./content-src/asrouter/components/SnippetBase/SnippetBase.jsx
-/* 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 SnippetBase_SnippetBase extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onBlockClicked = this.onBlockClicked.bind(this);
     this.onDismissClicked = this.onDismissClicked.bind(this);
     this.setBlockButtonRef = this.setBlockButtonRef.bind(this);
@@ -9658,19 +9495,16 @@ class SnippetBase_SnippetBase extends ex
       className: "innerWrapper"
     }, props.children), this.renderDismissButton());
   }
 
 }
 // CONCATENATED MODULE: ./content-src/asrouter/templates/SimpleSnippet/SimpleSnippet.jsx
 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/. */
 
 
 
 
 
 
 const DEFAULT_ICON_PATH = "chrome://branding/content/icon64.png"; // Alt text placeholder in case the prop from the server isn't available
 
@@ -9854,19 +9688,16 @@ class SimpleSnippet_SimpleSnippet extend
       className: "body"
     }, this.renderText()), this.props.extraContent), external_React_default.a.createElement("div", null, this.renderButton())));
   }
 
 }
 // CONCATENATED MODULE: ./content-src/asrouter/templates/EOYSnippet/EOYSnippet.jsx
 function EOYSnippet_extends() { EOYSnippet_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 EOYSnippet_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 EOYSnippet_EOYSnippetBase extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
     this.handleSubmit = this.handleSubmit.bind(this);
@@ -10005,19 +9836,16 @@ const EOYSnippet = props => {
   }));
 };
 // EXTERNAL MODULE: ./content-src/asrouter/templates/FXASignupSnippet/FXASignupSnippet.schema.json
 var FXASignupSnippet_schema = __webpack_require__(20);
 
 // CONCATENATED MODULE: ./content-src/asrouter/templates/SubmitFormSnippet/SubmitFormSnippet.jsx
 function SubmitFormSnippet_extends() { SubmitFormSnippet_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 SubmitFormSnippet_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/. */
 
 
 
 
 
  // Alt text placeholder in case the prop from the server isn't available
 
 const SubmitFormSnippet_ICON_ALT_TEXT = "";
@@ -10314,19 +10142,16 @@ class SubmitFormSnippet_SubmitFormSnippe
       onButtonClick: this.expandSnippet
     }));
   }
 
 }
 // CONCATENATED MODULE: ./content-src/asrouter/templates/FXASignupSnippet/FXASignupSnippet.jsx
 function FXASignupSnippet_extends() { FXASignupSnippet_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 FXASignupSnippet_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/. */
 
 
 
 const FXASignupSnippet = props => {
   const userAgent = window.navigator.userAgent.match(/Firefox\/([0-9]+)\./);
   const firefox_version = userAgent ? parseInt(userAgent[1], 10) : 0;
   const extendedContent = {
     scene1_button_label: FXASignupSnippet_schema.properties.scene1_button_label.default,
@@ -10353,19 +10178,16 @@ const FXASignupSnippet = props => {
   }));
 };
 // EXTERNAL MODULE: ./content-src/asrouter/templates/NewsletterSnippet/NewsletterSnippet.schema.json
 var NewsletterSnippet_schema = __webpack_require__(21);
 
 // CONCATENATED MODULE: ./content-src/asrouter/templates/NewsletterSnippet/NewsletterSnippet.jsx
 function NewsletterSnippet_extends() { NewsletterSnippet_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 NewsletterSnippet_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/. */
 
 
 
 const NewsletterSnippet = props => {
   const extendedContent = {
     scene1_button_label: NewsletterSnippet_schema.properties.scene1_button_label.default,
     scene2_email_placeholder_text: NewsletterSnippet_schema.properties.scene2_email_placeholder_text.default,
     scene2_button_label: NewsletterSnippet_schema.properties.scene2_button_label.default,
@@ -10382,20 +10204,16 @@ const NewsletterSnippet = props => {
   };
   return external_React_default.a.createElement(SubmitFormSnippet_SubmitFormSnippet, NewsletterSnippet_extends({}, props, {
     content: extendedContent,
     form_action: "https://basket.mozilla.org/subscribe.json",
     form_method: "POST"
   }));
 };
 // CONCATENATED MODULE: ./content-src/asrouter/templates/SendToDeviceSnippet/isEmailOrPhoneNumber.js
-/* 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/. */
-
 /**
  * Checks if a given string is an email or phone number or neither
  * @param {string} val The user input
  * @param {ASRMessageContent} content .content property on ASR message
  * @returns {"email"|"phone"|""} The type of the input
  */
 function isEmailOrPhoneNumber(val, content) {
   const {
@@ -10433,19 +10251,16 @@ function isEmailOrPhoneNumber(val, conte
   return "";
 }
 // EXTERNAL MODULE: ./content-src/asrouter/templates/SendToDeviceSnippet/SendToDeviceSnippet.schema.json
 var SendToDeviceSnippet_schema = __webpack_require__(22);
 
 // CONCATENATED MODULE: ./content-src/asrouter/templates/SendToDeviceSnippet/SendToDeviceSnippet.jsx
 function SendToDeviceSnippet_extends() { SendToDeviceSnippet_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 SendToDeviceSnippet_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/. */
 
 
 
 
 
 function validateInput(value, content) {
   const type = isEmailOrPhoneNumber(value, content);
   return type ? "" : "Must be an email or a phone number.";
@@ -10502,19 +10317,16 @@ const SendToDeviceSnippet = props => {
     inputType: propsWithDefaults.content.include_sms ? "text" : "email",
     validateInput: propsWithDefaults.content.include_sms ? validateInput : null,
     processFormData: processFormData
   }));
 };
 // CONCATENATED MODULE: ./content-src/asrouter/templates/SimpleBelowSearchSnippet/SimpleBelowSearchSnippet.jsx
 function SimpleBelowSearchSnippet_extends() { SimpleBelowSearchSnippet_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 SimpleBelowSearchSnippet_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/. */
 
 
 
 
 const SimpleBelowSearchSnippet_DEFAULT_ICON_PATH = "chrome://branding/content/icon64.png"; // Alt text placeholder in case the prop from the server isn't available
 
 const SimpleBelowSearchSnippet_ICON_ALT_TEXT = "";
 class SimpleBelowSearchSnippet_SimpleBelowSearchSnippet extends external_React_default.a.PureComponent {
@@ -10555,19 +10367,16 @@ class SimpleBelowSearchSnippet_SimpleBel
     }), external_React_default.a.createElement("div", null, external_React_default.a.createElement("p", {
       className: "body"
     }, this.renderText()), this.props.extraContent));
   }
 
 }
 // CONCATENATED MODULE: ./content-src/asrouter/templates/template-manifest.jsx
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SnippetsTemplates", function() { return SnippetsTemplates; });
-/* 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/. */
 
 
 
 
 
  // Key names matching schema name of templates
 
 const SnippetsTemplates = {
@@ -11950,19 +11759,16 @@ function ftl(strings) {
 
 
 
 
 
 // CONCATENATED MODULE: ./content-src/asrouter/rich-text-strings.js
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RICH_TEXT_KEYS", function() { return RICH_TEXT_KEYS; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "generateBundles", function() { return generateBundles; });
-/* 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/. */
 
 /**
  * Properties that allow rich text MUST be added to this list.
  *   key: the localization_id that should be used
  *   value: a property or array of properties on the message.content object
  */
 
 const RICH_TEXT_CONFIG = {
@@ -12005,19 +11811,16 @@ function generateBundles(content) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 
 // EXTERNAL MODULE: ./common/Actions.jsm
 var Actions = __webpack_require__(2);
 
 // CONCATENATED MODULE: ./common/Dedupe.jsm
-/* 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 Dedupe {
   constructor(createKey) {
     this.createKey = createKey || this.defaultCreateKey;
   }
 
   defaultCreateKey(item) {
     return item;
   }
@@ -12864,19 +12667,16 @@ var reducers = {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 
 // EXTERNAL MODULE: ./common/Actions.jsm
 var Actions = __webpack_require__(2);
 
 // CONCATENATED MODULE: ./content-src/components/Card/types.js
-/* 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 cardContextTypes = {
   history: {
     fluentID: "newtab-label-visited",
     icon: "history-item"
   },
   bookmark: {
     fluentID: "newtab-label-bookmarked",
     icon: "bookmark-added"
@@ -12906,19 +12706,16 @@ var external_React_default = /*#__PURE__
 
 // EXTERNAL MODULE: ./content-src/lib/screenshot-utils.js
 var screenshot_utils = __webpack_require__(37);
 
 // CONCATENATED MODULE: ./content-src/components/Card/Card.jsx
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_Card", function() { return Card_Card; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Card", function() { return Card; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PlaceholderCard", function() { return PlaceholderCard; });
-/* 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/. */
 
 
 
 
 
  // Keep track of pending image loads to only request once
 
 const gImageLoading = new Map();
@@ -13260,19 +13057,16 @@ var A11yLinkButton = __webpack_require__
 // EXTERNAL MODULE: external "React"
 var external_React_ = __webpack_require__(9);
 var external_React_default = /*#__PURE__*/__webpack_require__.n(external_React_);
 
 // EXTERNAL MODULE: ./content-src/components/TopSites/TopSitesConstants.js
 var TopSitesConstants = __webpack_require__(44);
 
 // CONCATENATED MODULE: ./content-src/components/TopSites/TopSiteFormInput.jsx
-/* 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 TopSiteFormInput_TopSiteFormInput extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
     this.state = {
       validationError: this.props.validationError
     };
     this.onChange = this.onChange.bind(this);
@@ -13376,19 +13170,16 @@ TopSiteFormInput_TopSiteFormInput.defaul
   value: "",
   validationError: false
 };
 // EXTERNAL MODULE: ./content-src/components/TopSites/TopSite.jsx
 var TopSite = __webpack_require__(46);
 
 // CONCATENATED MODULE: ./content-src/components/TopSites/TopSiteForm.jsx
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TopSiteForm", function() { return TopSiteForm_TopSiteForm; });
-/* 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 TopSiteForm_TopSiteForm extends external_React_default.a.PureComponent {
   constructor(props) {
@@ -13685,19 +13476,16 @@ var Actions = __webpack_require__(2);
 
 // EXTERNAL MODULE: external "ReactRedux"
 var external_ReactRedux_ = __webpack_require__(24);
 
 // EXTERNAL MODULE: ./content-src/components/ContextMenu/ContextMenu.jsx
 var ContextMenu = __webpack_require__(28);
 
 // CONCATENATED MODULE: ./content-src/lib/link-menu-options.js
-/* 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 _OpenInPrivateWindow = site => ({
   id: "newtab-menu-open-new-private-window",
   icon: "new-window-private",
   action: Actions["actionCreators"].OnlyToMain({
     type: Actions["actionTypes"].OPEN_PRIVATE_WINDOW,
     data: {
@@ -13969,19 +13757,16 @@ const LinkMenuOptions = {
 };
 // EXTERNAL MODULE: external "React"
 var external_React_ = __webpack_require__(9);
 var external_React_default = /*#__PURE__*/__webpack_require__.n(external_React_);
 
 // CONCATENATED MODULE: ./content-src/components/LinkMenu/LinkMenu.jsx
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_LinkMenu", function() { return LinkMenu_LinkMenu; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LinkMenu", function() { return LinkMenu; });
-/* 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 DEFAULT_SITE_MENU_OPTIONS = ["CheckPinTopSite", "EditTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"];
 class LinkMenu_LinkMenu extends external_React_default.a.PureComponent {
   getOptions() {
deleted file mode 100644
index 09e371bd817085ca3b0b3fff4ffc7f2d020fd2ee..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/components/newtab/docs/v2-system-addon/1.GETTING_STARTED.md
+++ b/browser/components/newtab/docs/v2-system-addon/1.GETTING_STARTED.md
@@ -42,24 +42,16 @@ You will also need to install:
 
 You will need to to clone Activity Stream to a local directory from the `master`
 branch of our Github repository: https://github.com/mozilla/activity-stream
 
 ```
 git clone https://github.com/mozilla/activity-stream.git
 ```
 
-Also be sure to install the hooks for this repository so that (at least)
-eslint and prettier fixing happens at commit time.
-
-```bash
-cd activity-stream
-./bin/bootstrap
-```
-
 ### Mozilla Central
 You will need a local copy of Mozilla Central in a directory named `mozilla-central`. Check the detail of how to get and build Mozilla Central in [Building Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Build_Instructions/Simple_Firefox_build).
 That directory should be a sibling of your local `activity-stream` directory (like so):
 
 ```
 /
   mozilla-central/
   activity-stream/
deleted file mode 100755
--- a/browser/components/newtab/hooks/post-commit
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh
-#
-# Clean up any weirdness left around by prettier execution from pre-commit
-# hook.  Can happen for some workflows (eg `git commit .`).
-#
-# Install by executing
-#
-#   ln -s ../../hooks/post-commit .git/hooks/post-commit
-#
-# at the top-level of the activity-stream github repo.
-git update-index -g
deleted file mode 100755
--- a/browser/components/newtab/hooks/pre-commit
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/sh
-#
-# Recommended pre-commit git hook for activity-stream github repo
-#
-# Install by executing
-#
-#   ln -s ../../hooks/pre-commit .git/hooks/pre-commit
-#
-# at the top-level of the activity-stream github repo.
-#
-# Runs `eslint --fix` on all selected files, which, given our current
-# prettier configuration, means prettifying these files as well.  The
-# commit will be aborted if eslint exits with a failure code.
-#
-# Based on the example script in the prettier docs at
-# https://prettier.io/docs/en/precommit.html
-
-FILES=$(git diff --cached --name-only --diff-filter=ACM "*.js" "*.jsx" "*.jsm" | sed 's| |\\ |g')
-[ -z "$FILES" ] && exit 0
-
-echo "$FILES" | xargs ./node_modules/.bin/eslint --cache --fix
-if [ $? -ne 0 ]
-then
-  echo "eslint found errors but was unable to fix them all with --fix."
-  echo "Please check the output, resolve any issues, and retry."
-  echo "If you want to commit anyway, pass the --no-verify flag to git commit."
-  exit -1
-fi
-
-# Add back the modified/prettified files to staging
-echo "$FILES" | xargs git add
-
-exit 0
new file mode 100644
--- /dev/null
+++ b/browser/components/newtab/hooks/pre-push
@@ -0,0 +1,11 @@
+#!/bin/sh
+# Recommended pre-push hook for activity-stream
+
+hookName=`basename "$0"`
+
+if ! command -v node >/dev/null 2>&1; then
+  echo "Can't find node in PATH, trying to find a node binary on your system"
+fi
+
+echo "Running $hookName hook..."
+npm run lint
--- a/browser/components/newtab/lib/ASRouter.jsm
+++ b/browser/components/newtab/lib/ASRouter.jsm
@@ -13,18 +13,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
   UITour: "resource:///modules/UITour.jsm",
   FxAccounts: "resource://gre/modules/FxAccounts.jsm",
   AppConstants: "resource://gre/modules/AppConstants.jsm",
   OS: "resource://gre/modules/osfile.jsm",
   BookmarkPanelHub: "resource://activity-stream/lib/BookmarkPanelHub.jsm",
   SnippetsTestMessageProvider:
     "resource://activity-stream/lib/SnippetsTestMessageProvider.jsm",
   PanelTestProvider: "resource://activity-stream/lib/PanelTestProvider.jsm",
-  ToolbarBadgeHub: "resource://activity-stream/lib/ToolbarBadgeHub.jsm",
-  ToolbarPanelHub: "resource://activity-stream/lib/ToolbarPanelHub.jsm",
 });
 const {
   ASRouterActions: ra,
   actionTypes: at,
   actionCreators: ac,
 } = ChromeUtils.import("resource://activity-stream/common/Actions.jsm");
 const { CFRMessageProvider } = ChromeUtils.import(
   "resource://activity-stream/lib/CFRMessageProvider.jsm"
@@ -488,23 +486,21 @@ class _ASRouter {
       trailheadInitialized: false,
       trailheadInterrupt: "",
       trailheadTriplet: "",
       messages: [],
       errors: [],
     };
     this._triggerHandler = this._triggerHandler.bind(this);
     this._localProviders = localProviders;
-    this.blockMessageById = this.blockMessageById.bind(this);
     this.onMessage = this.onMessage.bind(this);
     this.handleMessageRequest = this.handleMessageRequest.bind(this);
     this.addImpression = this.addImpression.bind(this);
     this._handleTargetingError = this._handleTargetingError.bind(this);
     this.onPrefChange = this.onPrefChange.bind(this);
-    this.dispatch = this.dispatch.bind(this);
   }
 
   async onPrefChange(prefName) {
     if (TARGETING_PREFERENCES.includes(prefName)) {
       // Notify all tabs of messages that have become invalid after pref change
       const invalidMessages = [];
       for (const msg of this._getUnblockedMessages()) {
         if (!msg.targeting) {
@@ -711,32 +707,25 @@ class _ASRouter {
     this.messageChannel = channel;
     this.messageChannel.addMessageListener(
       INCOMING_MESSAGE_NAME,
       this.onMessage
     );
     this._storage = storage;
     this.WHITELIST_HOSTS = this._loadSnippetsWhitelistHosts();
     this.dispatchToAS = dispatchToAS;
+    this.dispatch = this.dispatch.bind(this);
 
     ASRouterPreferences.init();
     ASRouterPreferences.addListener(this.onPrefChange);
     BookmarkPanelHub.init(
       this.handleMessageRequest,
       this.addImpression,
       this.dispatch
     );
-    ToolbarBadgeHub.init(this.waitForInitialized, {
-      handleMessageRequest: this.handleMessageRequest,
-      addImpression: this.addImpression,
-      blockMessageById: this.blockMessageById,
-    });
-    ToolbarPanelHub.init({
-      getMessages: this.handleMessageRequest,
-    });
 
     this._loadLocalProviders();
 
     // We need to check whether to set up telemetry for trailhead
     await this.setupTrailhead();
 
     const messageBlockList =
       (await this._storage.get("messageBlockList")) || [];
@@ -782,18 +771,16 @@ class _ASRouter {
       this.onMessage
     );
     this.messageChannel = null;
     this.dispatchToAS = null;
 
     ASRouterPreferences.removeListener(this.onPrefChange);
     ASRouterPreferences.uninit();
     BookmarkPanelHub.uninit();
-    ToolbarPanelHub.uninit();
-    ToolbarBadgeHub.uninit();
 
     // Uninitialise all trigger listeners
     for (const listener of ASRouterTriggerListeners.values()) {
       listener.uninit();
     }
     // If we added any CFR recommendations, they need to be removed
     CFRPageActions.clearRecommendations();
     this._resetInitialization();
@@ -1029,52 +1016,34 @@ class _ASRouter {
         });
       }
     }
   }
 
   // Return an object containing targeting parameters used to select messages
   _getMessagesContext() {
     const {
-      messageImpressions,
       previousSessionEnd,
       trailheadInterrupt,
       trailheadTriplet,
     } = this.state;
 
     return {
-      get messageImpressions() {
-        return messageImpressions;
-      },
       get previousSessionEnd() {
         return previousSessionEnd;
       },
       get trailheadInterrupt() {
         return trailheadInterrupt;
       },
       get trailheadTriplet() {
         return trailheadTriplet;
       },
     };
   }
 
-  _findAllMessages(candidateMessages, trigger) {
-    const messages = candidateMessages.filter(m =>
-      this.isBelowFrequencyCaps(m)
-    );
-    const context = this._getMessagesContext();
-
-    return ASRouterTargeting.findAllMatchingMessages({
-      messages,
-      trigger,
-      context,
-      onError: this._handleTargetingError,
-    });
-  }
-
   _findMessage(candidateMessages, trigger) {
     const messages = candidateMessages.filter(m =>
       this.isBelowFrequencyCaps(m)
     );
     const context = this._getMessagesContext();
 
     // Find a message that matches the targeting context as well as the trigger context (if one is provided)
     // If no trigger is provided, we should find a message WITHOUT a trigger property defined.
@@ -1279,19 +1248,16 @@ class _ASRouter {
           );
         }
         break;
       case "fxa_bookmark_panel":
         if (force) {
           BookmarkPanelHub._forceShowMessage(target, message);
         }
         break;
-      case "toolbar_badge":
-        ToolbarBadgeHub.registerBadgeNotificationListener(message, { force });
-        break;
       default:
         target.sendAsyncMessage(OUTGOING_MESSAGE_NAME, {
           type: "SET_MESSAGE",
           data: message,
         });
         break;
     }
   }
@@ -1475,33 +1441,22 @@ class _ASRouter {
         messages: state.messages.filter(m => m.id !== message.id),
       }));
     } else {
       await this.setState({ lastMessageId: message ? message.id : null });
     }
     await this._sendMessageToTarget(message, target, trigger);
   }
 
-  handleMessageRequest({ triggerId, template, returnAll = false }) {
-    const msgs = this._getUnblockedMessages().filter(m => {
-      if (template && m.template !== template) {
-        return false;
-      }
-      if (m.trigger && m.trigger.id !== triggerId) {
-        return false;
-      }
-
-      return true;
-    });
-
-    if (returnAll) {
-      return this._findAllMessages(msgs, { id: triggerId });
-    }
-
-    return this._findMessage(msgs, { id: triggerId });
+  handleMessageRequest(trigger) {
+    const msgs = this._getUnblockedMessages();
+    return this._findMessage(
+      msgs.filter(m => m.trigger && m.trigger.id === trigger.id),
+      trigger
+    );
   }
 
   async setMessageById(id, target, force = true, action = {}) {
     await this.setState({ lastMessageId: id });
     const newMessage = this.getMessageById(id);
 
     await this._sendMessageToTarget(newMessage, target, action.data, force);
   }
--- a/browser/components/newtab/lib/ASRouterTargeting.jsm
+++ b/browser/components/newtab/lib/ASRouterTargeting.jsm
@@ -1,15 +1,12 @@
 const { FilterExpressions } = ChromeUtils.import(
   "resource://gre/modules/components-utils/FilterExpressions.jsm"
 );
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-const { XPCOMUtils } = ChromeUtils.import(
-  "resource://gre/modules/XPCOMUtils.jsm"
-);
 
 ChromeUtils.defineModuleGetter(
   this,
   "ASRouterPreferences",
   "resource://activity-stream/lib/ASRouterPreferences.jsm"
 );
 ChromeUtils.defineModuleGetter(
   this,
@@ -41,22 +38,16 @@ ChromeUtils.defineModuleGetter(
   "AppConstants",
   "resource://gre/modules/AppConstants.jsm"
 );
 ChromeUtils.defineModuleGetter(
   this,
   "AttributionCode",
   "resource:///modules/AttributionCode.jsm"
 );
-XPCOMUtils.defineLazyServiceGetter(
-  this,
-  "UpdateManager",
-  "@mozilla.org/updates/update-manager;1",
-  "nsIUpdateManager"
-);
 
 const FXA_USERNAME_PREF = "services.sync.username";
 const FXA_ENABLED_PREF = "identity.fxaccounts.enabled";
 const SEARCH_REGION_PREF = "browser.search.region";
 const MOZ_JEXL_FILEPATH = "mozjexl";
 
 const { activityStreamProvider: asProvider } = NewTabUtils;
 
@@ -79,23 +70,30 @@ function CachedTargetingGetter(
   return {
     _lastUpdated: 0,
     _value: null,
     // For testing
     expire() {
       this._lastUpdated = 0;
       this._value = null;
     },
-    async get() {
-      const now = Date.now();
-      if (now - this._lastUpdated >= updateInterval) {
-        this._value = await asProvider[property](options);
-        this._lastUpdated = now;
-      }
-      return this._value;
+    get() {
+      return new Promise(async (resolve, reject) => {
+        const now = Date.now();
+        if (now - this._lastUpdated >= updateInterval) {
+          try {
+            this._value = await asProvider[property](options);
+            this._lastUpdated = now;
+          } catch (e) {
+            Cu.reportError(e);
+            reject(e);
+          }
+        }
+        resolve(this._value);
+      });
     },
   };
 }
 
 function CheckBrowserNeedsUpdate(
   updateInterval = FRECENT_SITES_UPDATE_INTERVAL
 ) {
   const UpdateChecker = Cc["@mozilla.org/updates/update-checker;1"];
@@ -203,45 +201,16 @@ function sortMessagesByTargeting(message
     if (!a.targeting && b.targeting) {
       return 1;
     }
 
     return 0;
   });
 }
 
-/**
- * Sort messages in descending order based on the value of `priority`
- * Messages with no `priority` are ranked lowest (even after a message with
- * priority 0).
- */
-function sortMessagesByPriority(messages) {
-  return messages.sort((a, b) => {
-    if (isNaN(a.priority) && isNaN(b.priority)) {
-      return 0;
-    }
-    if (!isNaN(a.priority) && isNaN(b.priority)) {
-      return -1;
-    }
-    if (isNaN(a.priority) && !isNaN(b.priority)) {
-      return 1;
-    }
-
-    // Descending order; higher priority comes first
-    if (a.priority > b.priority) {
-      return -1;
-    }
-    if (a.priority < b.priority) {
-      return 1;
-    }
-
-    return 0;
-  });
-}
-
 const TargetingGetters = {
   get locale() {
     return Services.locale.appLocaleAsLangTag;
   },
   get localeLanguageCode() {
     return (
       Services.locale.appLocaleAsLangTag &&
       Services.locale.appLocaleAsLangTag.substr(0, 2)
@@ -385,38 +354,16 @@ const TargetingGetters = {
       }
       if (win.ownerGlobal.gBrowser.visibleTabs.filter(t => t.pinned).length) {
         return true;
       }
     }
 
     return false;
   },
-  get hasAccessedFxAPanel() {
-    return Services.prefs.getBoolPref(
-      "identity.fxaccounts.toolbar.accessed",
-      true
-    );
-  },
-  get isWhatsNewPanelEnabled() {
-    return Services.prefs.getBoolPref(
-      "browser.messaging-system.whatsNewPanel.enabled",
-      false
-    );
-  },
-  get earliestFirefoxVersion() {
-    if (UpdateManager.updateCount) {
-      const earliestFirefoxVersion = UpdateManager.getUpdateAt(
-        UpdateManager.updateCount - 1
-      ).previousAppVersion;
-      return parseInt(earliestFirefoxVersion.match(/\d+/), 10);
-    }
-
-    return null;
-  },
 };
 
 this.ASRouterTargeting = {
   Environment: TargetingGetters,
 
   ERROR_TYPES: {
     MALFORMED_EXPRESSION: "MALFORMED_EXPRESSION",
     OTHER_ERROR: "OTHER_ERROR",
@@ -501,87 +448,48 @@ this.ASRouterTargeting = {
           : this.ERROR_TYPES.OTHER_ERROR;
         onError(type, error, message);
       }
       result = false;
     }
     return result;
   },
 
-  _getSortedMessages(messages) {
-    const weightSortedMessages = sortMessagesByWeightedRank([...messages]);
-    const sortedMessages = sortMessagesByTargeting(weightSortedMessages);
-    return sortMessagesByPriority(sortedMessages);
-  },
-
-  _getCombinedContext(trigger, context) {
-    const triggerContext = trigger ? trigger.context : {};
-    return this.combineContexts(context, triggerContext);
-  },
-
-  _isMessageMatch(message, trigger, context, onError) {
-    return (
-      message &&
-      (trigger
-        ? this.isTriggerMatch(trigger, message.trigger)
-        : !message.trigger) &&
-      // If a trigger expression was passed to this function, the message should match it.
-      // Otherwise, we should choose a message with no trigger property (i.e. a message that can show up at any time)
-      this.checkMessageTargeting(message, context, onError)
-    );
-  },
-
   /**
    * findMatchingMessage - Given an array of messages, returns one message
    *                       whos targeting expression evaluates to true
    *
    * @param {Array} messages An array of AS router messages
+   * @param {obj} impressions An object containing impressions, where keys are message ids
    * @param {trigger} string A trigger expression if a message for that trigger is desired
    * @param {obj|null} context A FilterExpression context. Defaults to TargetingGetters above.
-   * @param {func} onError A function to handle errors (takes two params; error, message)
    * @returns {obj} an AS router message
    */
   async findMatchingMessage({ messages, trigger, context, onError }) {
-    const sortedMessages = this._getSortedMessages(messages);
-    const combinedContext = this._getCombinedContext(trigger, context);
+    const weightSortedMessages = sortMessagesByWeightedRank([...messages]);
+    const sortedMessages = sortMessagesByTargeting(weightSortedMessages);
+    const triggerContext = trigger ? trigger.context : {};
+    const combinedContext = this.combineContexts(context, triggerContext);
 
     for (const candidate of sortedMessages) {
       if (
-        await this._isMessageMatch(candidate, trigger, combinedContext, onError)
+        candidate &&
+        (trigger
+          ? this.isTriggerMatch(trigger, candidate.trigger)
+          : !candidate.trigger) &&
+        // If a trigger expression was passed to this function, the message should match it.
+        // Otherwise, we should choose a message with no trigger property (i.e. a message that can show up at any time)
+        (await this.checkMessageTargeting(candidate, combinedContext, onError))
       ) {
         return candidate;
       }
     }
+
     return null;
   },
-
-  /**
-   * findAllMatchingMessages - Given an array of messages, returns an array of
-   *                           messages that that match the targeting.
-   *
-   * @param {Array} messages An array of AS router messages.
-   * @param {trigger} string A trigger expression if a message for that trigger is desired.
-   * @param {obj|null} context A FilterExpression context. Defaults to TargetingGetters above.
-   * @param {func} onError A function to handle errors (takes two params; error, message)
-   * @returns {Array} An array of AS router messages that match.
-   */
-  async findAllMatchingMessages({ messages, trigger, context, onError }) {
-    const sortedMessages = this._getSortedMessages(messages);
-    const combinedContext = this._getCombinedContext(trigger, context);
-    const matchingMessages = [];
-
-    for (const candidate of sortedMessages) {
-      if (
-        await this._isMessageMatch(candidate, trigger, combinedContext, onError)
-      ) {
-        matchingMessages.push(candidate);
-      }
-    }
-    return matchingMessages;
-  },
 };
 
 // Export for testing
 this.QueryCache = QueryCache;
 this.CachedTargetingGetter = CachedTargetingGetter;
 this.EXPORTED_SYMBOLS = [
   "ASRouterTargeting",
   "QueryCache",
--- a/browser/components/newtab/lib/AboutPreferences.jsm
+++ b/browser/components/newtab/lib/AboutPreferences.jsm
@@ -1,18 +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/. */
 "use strict";
 
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { XPCOMUtils } = ChromeUtils.import(
+  "resource://gre/modules/XPCOMUtils.jsm"
+);
 const { actionTypes: at } = ChromeUtils.import(
   "resource://activity-stream/common/Actions.jsm"
 );
 
+XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const PREFERENCES_LOADED_EVENT = "home-pane-loaded";
 
 // These "section" objects are formatted in a way to be similar to the ones from
 // SectionsManager to construct the preferences view.
 const PREFS_BEFORE_SECTIONS = [
   {
     id: "search",
@@ -103,36 +107,69 @@ this.AboutPreferences = class AboutPrefe
     sectionsCopy.forEach(obj => {
       if (obj.id === "topstories") {
         obj.rowsPref = "";
       }
     });
     return sectionsCopy;
   }
 
-  observe(window) {
+  async observe(window) {
     const discoveryStreamConfig = this.store.getState().DiscoveryStream.config;
     let sections = this.store.getState().Sections;
 
     if (discoveryStreamConfig.enabled) {
       sections = this.handleDiscoverySettings(sections);
     }
 
-    this.renderPreferences(window, [
+    this.renderPreferences(window, await this.strings, [
       ...PREFS_BEFORE_SECTIONS,
       ...sections,
       ...PREFS_AFTER_SECTIONS,
     ]);
   }
 
   /**
+   * Get strings from a js file that the content page would have loaded. The
+   * file should be a single variable assignment of a JSON/JS object of strings.
+   */
+  get strings() {
+    return (
+      this._strings ||
+      (this._strings = new Promise(async resolve => {
+        let data = {};
+        try {
+          const locale = Cc[
+            "@mozilla.org/browser/aboutnewtab-service;1"
+          ].getService(Ci.nsIAboutNewTabService).activityStreamLocale;
+          const request = await fetch(
+            `resource://activity-stream/prerendered/${locale}/activity-stream-strings.js`
+          );
+          const text = await request.text();
+          const [json] = text.match(/{[^]*}/);
+          data = JSON.parse(json);
+        } catch (ex) {
+          Cu.reportError(
+            "Failed to load strings for Activity Stream about:preferences"
+          );
+        }
+        resolve(data);
+      }))
+    );
+  }
+
+  /**
    * Render preferences to an about:preferences content window with the provided
-   * preferences structure.
+   * strings and preferences structure.
    */
-  renderPreferences({ document, Preferences, gHomePane }, prefStructure) {
+  renderPreferences(
+    { document, Preferences, gHomePane },
+    strings,
+    prefStructure
+  ) {
     // Helper to create a new element and append it
     const createAppend = (tag, parent, options) =>
       parent.appendChild(document.createXULElement(tag, options));
 
     // Helper to get fluentIDs sometimes encase in an object
     const getString = message =>
       typeof message !== "object" ? message : message.id;
 
--- a/browser/components/newtab/lib/BookmarkPanelHub.jsm
+++ b/browser/components/newtab/lib/BookmarkPanelHub.jsm
@@ -80,19 +80,17 @@ class _BookmarkPanelHub {
     ) {
       this.showMessage(this._response.content, target, win);
       return true;
     }
 
     // If we didn't match on a previously cached request then make sure
     // the container is empty
     this._removeContainer(target);
-    const response = await this._handleMessageRequest({
-      triggerId: this._trigger.id,
-    });
+    const response = await this._handleMessageRequest(this._trigger);
 
     return this.onResponse(response, target, win);
   }
 
   /**
    * If the response contains a message render it and send an impression.
    * Otherwise we remove the message from the container.
    */
--- a/browser/components/newtab/lib/LinksCache.jsm
+++ b/browser/components/newtab/lib/LinksCache.jsm
@@ -78,59 +78,54 @@ this.LinksCache = class LinksCache {
       // Allow custom rules around refreshing based on options
       this.shouldRefresh(this.lastOptions, options)
     ) {
       // Update request state early so concurrent requests can refer to it
       this.lastOptions = options;
       this.lastUpdate = now;
 
       // Save a promise before awaits, so other requests wait for correct data
-      // eslint-disable-next-line no-async-promise-executor
-      this.cache = new Promise(async (resolve, reject) => {
-        try {
-          // Allow fast lookup of old links by url that might need to migrate
-          const toMigrate = new Map();
-          for (const oldLink of await this.cache) {
-            if (oldLink) {
-              toMigrate.set(oldLink.url, oldLink);
+      this.cache = new Promise(async resolve => {
+        // Allow fast lookup of old links by url that might need to migrate
+        const toMigrate = new Map();
+        for (const oldLink of await this.cache) {
+          if (oldLink) {
+            toMigrate.set(oldLink.url, oldLink);
+          }
+        }
+
+        // Update the cache with migrated links without modifying source objects
+        resolve(
+          (await this.linkGetter(options)).map(link => {
+            // Keep original array hole positions
+            if (!link) {
+              return link;
             }
-          }
-
-          // Update the cache with migrated links without modifying source objects
-          resolve(
-            (await this.linkGetter(options)).map(link => {
-              // Keep original array hole positions
-              if (!link) {
-                return link;
-              }
 
-              // Migrate data to the new link copy if we have an old link
-              const newLink = Object.assign({}, link);
-              const oldLink = toMigrate.get(newLink.url);
-              if (oldLink) {
-                for (const property of this.migrateProperties) {
-                  const oldValue = oldLink[property];
-                  if (oldValue !== undefined) {
-                    newLink[property] = oldValue;
-                  }
+            // Migrate data to the new link copy if we have an old link
+            const newLink = Object.assign({}, link);
+            const oldLink = toMigrate.get(newLink.url);
+            if (oldLink) {
+              for (const property of this.migrateProperties) {
+                const oldValue = oldLink[property];
+                if (oldValue !== undefined) {
+                  newLink[property] = oldValue;
                 }
-              } else {
-                // Share data among link copies and new links from future requests
-                newLink.__sharedCache = {};
               }
-              // Provide a helper to update the cached link
-              newLink.__sharedCache.updateLink = (property, value) => {
-                newLink[property] = value;
-              };
+            } else {
+              // Share data among link copies and new links from future requests
+              newLink.__sharedCache = {};
+            }
+            // Provide a helper to update the cached link
+            newLink.__sharedCache.updateLink = (property, value) => {
+              newLink[property] = value;
+            };
 
-              return newLink;
-            })
-          );
-        } catch (error) {
-          reject(error);
-        }
+            return newLink;
+          })
+        );
       });
     }
 
     // Provide a shallow copy of the cached link objects for callers to modify
     return (await this.cache).map(link => link && Object.assign({}, link));
   }
 };
--- a/browser/components/newtab/lib/PanelTestProvider.jsm
+++ b/browser/components/newtab/lib/PanelTestProvider.jsm
@@ -1,17 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-const FIREFOX_VERSION = parseInt(Services.appinfo.version.match(/\d+/), 10);
-
 const MESSAGES = () => [
   {
     id: "SIMPLE_FXA_BOOKMARK_TEST_FLUENT",
     template: "fxa_bookmark_panel",
     content: {
       title: { string_id: "cfr-doorhanger-bookmark-fxa-header" },
       text: { string_id: "cfr-doorhanger-bookmark-fxa-body" },
       cta: { string_id: "cfr-doorhanger-bookmark-fxa-link-text" },
@@ -45,92 +41,16 @@ const MESSAGES = () => [
         tooltiptext: "Toggle tooltip",
       },
       close_button: {
         tooltiptext: "Close tooltip",
       },
     },
     trigger: { id: "bookmark-panel" },
   },
-  {
-    id: "FXA_ACCOUNTS_BADGE",
-    template: "toolbar_badge",
-    content: {
-      target: "fxa-toolbar-menu-button",
-    },
-    // Never accessed the FxA panel && doesn't use Firefox sync & has FxA enabled
-    targeting: `!hasAccessedFxAPanel && !usesFirefoxSync && isFxAEnabled == true`,
-    trigger: { id: "toolbarBadgeUpdate" },
-  },
-  {
-    id: `WHATS_NEW_BADGE_${FIREFOX_VERSION}`,
-    template: "toolbar_badge",
-    content: {
-      // delay: 5 * 3600 * 1000,
-      delay: 5000,
-      target: "whats-new-menu-button",
-      action: { id: "show-whatsnew-button" },
-    },
-    priority: 1,
-    trigger: { id: "toolbarBadgeUpdate" },
-    frequency: {
-      // Makes it so that we track impressions for this message while at the
-      // same time it can have unlimited impressions
-      lifetime: Infinity,
-    },
-    // Never saw this message or saw it in the past 4 days or more recent
-    targeting: `isWhatsNewPanelEnabled &&
-      (earliestFirefoxVersion && firefoxVersion > earliestFirefoxVersion) &&
-        messageImpressions[.id == 'WHATS_NEW_BADGE_${FIREFOX_VERSION}']|length == 0 ||
-      (messageImpressions[.id == 'WHATS_NEW_BADGE_${FIREFOX_VERSION}']|length >= 1 &&
-        currentDate|date - messageImpressions[.id == 'WHATS_NEW_BADGE_${FIREFOX_VERSION}'][0] <= 4 * 24 * 3600 * 1000)`,
-  },
-  {
-    id: "WHATS_NEW_70_1",
-    template: "whatsnew_panel_message",
-    content: {
-      published_date: 1560969794394,
-      title: "Protection Is Our Focus",
-      icon_url:
-        "resource://activity-stream/data/content/assets/whatsnew-send-icon.png",
-      body:
-        "The New Enhanced Tracking Protection, gives you the best level of protection and performance. Discover how this version is the safest version of firefox ever made.",
-      cta_url: "https://blog.mozilla.org/",
-    },
-    targeting: `firefoxVersion > 69`,
-    trigger: { id: "whatsNewPanelOpened" },
-  },
-  {
-    id: "WHATS_NEW_70_2",
-    template: "whatsnew_panel_message",
-    content: {
-      published_date: 1560969794394,
-      title: "Another thing new in Firefox 70",
-      body:
-        "The New Enhanced Tracking Protection, gives you the best level of protection and performance. Discover how this version is the safest version of firefox ever made.",
-      link_text: "Learn more on our blog",
-      cta_url: "https://blog.mozilla.org/",
-    },
-    targeting: `firefoxVersion > 69`,
-    trigger: { id: "whatsNewPanelOpened" },
-  },
-  {
-    id: "WHATS_NEW_69_1",
-    template: "whatsnew_panel_message",
-    content: {
-      published_date: 1557346235089,
-      title: "Something new in Firefox 69",
-      body:
-        "The New Enhanced Tracking Protection, gives you the best level of protection and performance. Discover how this version is the safest version of firefox ever made.",
-      link_text: "Learn more on our blog",
-      cta_url: "https://blog.mozilla.org/",
-    },
-    targeting: `firefoxVersion > 68`,
-    trigger: { id: "whatsNewPanelOpened" },
-  },
 ];
 
 const PanelTestProvider = {
   getMessages() {
     return MESSAGES().map(message => ({
       ...message,
       targeting: `providerCohorts.panel_local_testing == "SHOW_TEST"`,
     }));
--- a/browser/components/newtab/lib/PersistentCache.jsm
+++ b/browser/components/newtab/lib/PersistentCache.jsm
@@ -52,35 +52,28 @@ this.PersistentCache = class PersistentC
   }
 
   /**
    * Load the cache into memory if it isn't already.
    */
   _load() {
     return (
       this._cache ||
-      // eslint-disable-next-line no-async-promise-executor
-      (this._cache = new Promise(async (resolve, reject) => {
-        let filepath;
-        try {
-          filepath = OS.Path.join(
-            OS.Constants.Path.localProfileDir,
-            this._filename
-          );
-        } catch (error) {
-          reject(error);
-          return;
-        }
+      (this._cache = new Promise(async resolve => {
+        let file;
+        let data = {};
+        const filepath = OS.Path.join(
+          OS.Constants.Path.localProfileDir,
+          this._filename
+        );
 
-        let file;
         try {
           file = await fetch(`file://${filepath}`);
         } catch (error) {} // Cache file doesn't exist yet.
 
-        let data = {};
         if (file) {
           try {
             data = await file.json();
           } catch (error) {
             Cu.reportError(
               `Failed to parse ${this._filename}: ${error.message}`
             );
           }
--- a/browser/components/newtab/lib/Tokenize.jsm
+++ b/browser/components/newtab/lib/Tokenize.jsm
@@ -24,18 +24,16 @@ const UNICODE_NUMBER =
 const UNICODE_MARK =
   "\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F";
 const UNICODE_LETTER =
   "A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC";
 
 const REGEXP_SPLITS = new RegExp(
   `[${UNICODE_SPACE}${UNICODE_SYMBOL}${UNICODE_PUNCT}]+`
 );
-// Match all token characters, so okay for regex to split multiple code points
-// eslint-disable-next-line no-misleading-character-class
 const REGEXP_ALPHANUMS = new RegExp(
   `^[${UNICODE_NUMBER}${UNICODE_MARK}${UNICODE_LETTER}]+$`
 );
 
 /**
  * Downcases the text, and splits it into consecutive alphanumeric characters.
  * This is locale aware, and so will not strip accents. This uses "word
  * breaks", and os is not appropriate for languages without them
deleted file mode 100644
--- a/browser/components/newtab/lib/ToolbarPanelHub.jsm
+++ /dev/null
@@ -1,229 +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 strict";
-
-ChromeUtils.defineModuleGetter(
-  this,
-  "Services",
-  "resource://gre/modules/Services.jsm"
-);
-ChromeUtils.defineModuleGetter(
-  this,
-  "EveryWindow",
-  "resource:///modules/EveryWindow.jsm"
-);
-
-const WHATSNEW_ENABLED_PREF = "browser.messaging-system.whatsNewPanel.enabled";
-
-const TOOLBAR_BUTTON_ID = "whats-new-menu-button";
-const APPMENU_BUTTON_ID = "appMenu-whatsnew-button";
-const PANEL_HEADER_SELECTOR = "#PanelUI-whatsNew-title > label";
-
-const BUTTON_STRING_ID = "cfr-whatsnew-button";
-
-class _ToolbarPanelHub {
-  constructor() {
-    this._showAppmenuButton = this._showAppmenuButton.bind(this);
-    this._hideAppmenuButton = this._hideAppmenuButton.bind(this);
-    this._showToolbarButton = this._showToolbarButton.bind(this);
-    this._hideToolbarButton = this._hideToolbarButton.bind(this);
-  }
-
-  init({ getMessages }) {
-    this._getMessages = getMessages;
-    if (this.whatsNewPanelEnabled) {
-      this.enableAppmenuButton();
-    }
-  }
-
-  uninit() {
-    EveryWindow.unregisterCallback(TOOLBAR_BUTTON_ID);
-    EveryWindow.unregisterCallback(APPMENU_BUTTON_ID);
-  }
-
-  get whatsNewPanelEnabled() {
-    return Services.prefs.getBoolPref(WHATSNEW_ENABLED_PREF, false);
-  }
-
-  maybeInsertFTL(win) {
-    win.MozXULElement.insertFTLIfNeeded("browser/newtab/asrouter.ftl");
-  }
-
-  // Turns on the Appmenu (hamburger menu) button for all open windows and future windows.
-  enableAppmenuButton() {
-    EveryWindow.registerCallback(
-      APPMENU_BUTTON_ID,
-      this._showAppmenuButton,
-      this._hideAppmenuButton
-    );
-  }
-
-  // Turns on the Toolbar button for all open windows and future windows.
-  enableToolbarButton() {
-    EveryWindow.registerCallback(
-      TOOLBAR_BUTTON_ID,
-      this._showToolbarButton,
-      this._hideToolbarButton
-    );
-  }
-
-  // Render what's new messages into the panel.
-  async renderMessages(win, doc, containerId) {
-    const messages = (await this._getMessages({
-      template: "whatsnew_panel_message",
-      triggerId: "whatsNewPanelOpened",
-      returnAll: true,
-    })).sort((m1, m2) => {
-      // Sort by published_date in descending order.
-      if (m1.content.published_date === m2.content.published_date) {
-        return 0;
-      }
-      if (m1.content.published_date > m2.content.published_date) {
-        return -1;
-      }
-      return 1;
-    });
-    const container = doc.getElementById(containerId);
-
-    if (messages && !container.querySelector(".whatsNew-message")) {
-      let previousDate = 0;
-      for (let { content } of messages) {
-        container.appendChild(
-          this._createMessageElements(win, doc, content, previousDate)
-        );
-        previousDate = content.published_date;
-      }
-    }
-
-    // TODO: TELEMETRY
-  }
-
-  _createMessageElements(win, doc, content, previousDate) {
-    const messageEl = this._createElement(doc, "div");
-    messageEl.classList.add("whatsNew-message");
-
-    // Only render date if it is different from the one rendered before.
-    if (content.published_date !== previousDate) {
-      messageEl.appendChild(
-        this._createDateElement(doc, content.published_date)
-      );
-    }
-
-    const wrapperEl = this._createElement(doc, "div");
-    wrapperEl.classList.add("whatsNew-message-body");
-    messageEl.appendChild(wrapperEl);
-    wrapperEl.addEventListener("click", () => {
-      win.ownerGlobal.openLinkIn(content.cta_url, "tabshifted", {
-        private: false,
-        triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
-          {}
-        ),
-        csp: null,
-      });
-
-      // TODO: TELEMETRY
-    });
-
-    if (content.icon_url) {
-      wrapperEl.classList.add("has-icon");
-      const iconEl = this._createElement(doc, "img");
-      iconEl.src = content.icon_url;
-      iconEl.classList.add("whatsNew-message-icon");
-      wrapperEl.appendChild(iconEl);
-    }
-
-    const titleEl = this._createElement(doc, "h2");
-    titleEl.classList.add("whatsNew-message-title");
-    this._setString(doc, titleEl, content.title);
-    wrapperEl.appendChild(titleEl);
-
-    const bodyEl = this._createElement(doc, "p");
-    this._setString(doc, bodyEl, content.body);
-    wrapperEl.appendChild(bodyEl);
-
-    if (content.link_text) {
-      const linkEl = this._createElement(doc, "button");
-      linkEl.classList.add("text-link");
-      this._setString(doc, linkEl, content.link_text);
-      wrapperEl.appendChild(linkEl);
-    }
-
-    return messageEl;
-  }
-
-  _createElement(doc, elem) {
-    return doc.createElementNS("http://www.w3.org/1999/xhtml", elem);
-  }
-
-  _createDateElement(doc, date) {
-    const dateEl = this._createElement(doc, "p");
-    dateEl.classList.add("whatsNew-message-date");
-    dateEl.textContent = new Date(date).toLocaleDateString("default", {
-      month: "long",
-      day: "numeric",
-      year: "numeric",
-    });
-    return dateEl;
-  }
-
-  // If `string_id` is present it means we are relying on fluent for translations.
-  // Otherwise, we have a vanilla string.
-  _setString(doc, el, stringObj) {
-    if (stringObj.string_id) {
-      doc.l10n.setAttributes(el, stringObj.string_id);
-    } else {
-      el.textContent = stringObj;
-    }
-  }
-
-  _showAppmenuButton(win) {
-    this.maybeInsertFTL(win);
-    this._showElement(
-      win.browser.ownerDocument,
-      APPMENU_BUTTON_ID,
-      BUTTON_STRING_ID
-    );
-  }
-
-  _hideAppmenuButton(win) {
-    this._hideElement(win.browser.ownerDocument, APPMENU_BUTTON_ID);
-  }
-
-  _showToolbarButton(win) {
-    const document = win.browser.ownerDocument;
-    this.maybeInsertFTL(win);
-    this._showElement(document, TOOLBAR_BUTTON_ID, BUTTON_STRING_ID);
-    // The toolbar dropdown panel uses this extra header element that is hidden
-    // in the appmenu subview version of the panel. We only need to set it
-    // when showing the toolbar button.
-    document.l10n.setAttributes(
-      document.querySelector(PANEL_HEADER_SELECTOR),
-      "cfr-whatsnew-panel-header"
-    );
-  }
-
-  _hideToolbarButton(win) {
-    this._hideElement(win.browser.ownerDocument, TOOLBAR_BUTTON_ID);
-  }
-
-  _showElement(document, id, string_id) {
-    const el = document.getElementById(id);
-    document.l10n.setAttributes(el, string_id);
-    el.removeAttribute("hidden");
-  }
-
-  _hideElement(document, id) {
-    document.getElementById(id).setAttribute("hidden", true);
-  }
-}
-
-this._ToolbarPanelHub = _ToolbarPanelHub;
-
-/**
- * ToolbarPanelHub - singleton instance of _ToolbarPanelHub that can initiate
- * message requests and render messages.
- */
-this.ToolbarPanelHub = new _ToolbarPanelHub();
-
-const EXPORTED_SYMBOLS = ["ToolbarPanelHub", "_ToolbarPanelHub"];
--- a/browser/components/newtab/test/browser/browser_asrouter_targeting.js
+++ b/browser/components/newtab/test/browser/browser_asrouter_targeting.js
@@ -802,48 +802,16 @@ add_task(async function check_pinned_tab
         "Should detect pinned tab"
       );
 
       gBrowser.unpinTab(tab);
     }
   );
 });
 
-add_task(async function check_hasAccessedFxAPanel() {
-  is(
-    await ASRouterTargeting.Environment.hasAccessedFxAPanel,
-    false,
-    "Not accessed yet"
-  );
-
-  await pushPrefs(["identity.fxaccounts.toolbar.accessed", true]);
-
-  is(
-    await ASRouterTargeting.Environment.hasAccessedFxAPanel,
-    true,
-    "Should detect panel access"
-  );
-});
-
-add_task(async function check_isWhatsNewPanelEnabled() {
-  is(
-    await ASRouterTargeting.Environment.isWhatsNewPanelEnabled,
-    false,
-    "Not enabled yet"
-  );
-
-  await pushPrefs(["browser.messaging-system.whatsNewPanel.enabled", true]);
-
-  is(
-    await ASRouterTargeting.Environment.isWhatsNewPanelEnabled,
-    true,
-    "Should update based on pref"
-  );
-});
-
 add_task(async function checkCFRPinnedTabsTargetting() {
   const now = Date.now();
   const timeMinutesAgo = numMinutes => now - numMinutes * 60 * 1000;
   const messages = CFRMessageProvider.getMessages();
   const trigger = {
     id: "frequentVisits",
     context: {
       recentVisits: [
--- a/browser/components/newtab/test/unit/asrouter/ASRouter.test.js
+++ b/browser/components/newtab/test/unit/asrouter/ASRouter.test.js
@@ -61,18 +61,16 @@ describe("ASRouter", () => {
   let providerImpressions;
   let previousSessionEnd;
   let fetchStub;
   let clock;
   let getStringPrefStub;
   let dispatchStub;
   let fakeAttributionCode;
   let FakeBookmarkPanelHub;
-  let FakeToolbarBadgeHub;
-  let FakeToolbarPanelHub;
 
   function createFakeStorage() {
     const getStub = sandbox.stub();
     getStub.returns(Promise.resolve());
     getStub
       .withArgs("messageBlockList")
       .returns(Promise.resolve(messageBlockList));
     getStub
@@ -136,34 +134,23 @@ describe("ASRouter", () => {
       _clearCache: () => sinon.stub(),
       getAttrDataAsync: () => Promise.resolve({ content: "addonID" }),
     };
     FakeBookmarkPanelHub = {
       init: sandbox.stub(),
       uninit: sandbox.stub(),
       _forceShowMessage: sandbox.stub(),
     };
-    FakeToolbarPanelHub = {
-      init: sandbox.stub(),
-      uninit: sandbox.stub(),
-    };
-    FakeToolbarBadgeHub = {
-      init: sandbox.stub(),
-      uninit: sandbox.stub(),
-      registerBadgeNotificationListener: sandbox.stub(),
-    };
     globals.set({
       AttributionCode: fakeAttributionCode,
       // Testing framework doesn't know how to `defineLazyModuleGetter` so we're
       // importing these modules into the global scope ourselves.
       SnippetsTestMessageProvider,
       PanelTestProvider,
       BookmarkPanelHub: FakeBookmarkPanelHub,
-      ToolbarBadgeHub: FakeToolbarBadgeHub,
-      ToolbarPanelHub: FakeToolbarPanelHub,
     });
     await createRouterAndInit();
   });
   afterEach(() => {
     ASRouterPreferences.uninit();
     sandbox.restore();
     globals.restore();
   });
@@ -187,44 +174,16 @@ describe("ASRouter", () => {
     });
     it("should set state.messageBlockList to the block list in persistent storage", async () => {
       messageBlockList = ["foo"];
       Router = new _ASRouter();
       await Router.init(channel, createFakeStorage(), dispatchStub);
 
       assert.deepEqual(Router.state.messageBlockList, ["foo"]);
     });
-    it("should initialize all the hub providers", async () => {
-      // ASRouter init called in `beforeEach` block above
-
-      assert.calledOnce(FakeToolbarBadgeHub.init);
-      assert.calledOnce(FakeToolbarPanelHub.init);
-      assert.calledOnce(FakeBookmarkPanelHub.init);
-
-      assert.calledWithExactly(
-        FakeToolbarBadgeHub.init,
-        Router.waitForInitialized,
-        {
-          handleMessageRequest: Router.handleMessageRequest,
-          addImpression: Router.addImpression,
-          blockMessageById: Router.blockMessageById,
-        }
-      );
-
-      assert.calledWithExactly(FakeToolbarPanelHub.init, {
-        getMessages: Router.handleMessageRequest,
-      });
-
-      assert.calledWithExactly(
-        FakeBookmarkPanelHub.init,
-        Router.handleMessageRequest,
-        Router.addImpression,
-        Router.dispatch
-      );
-    });
     it("should set state.messageImpressions to the messageImpressions object in persistent storage", async () => {
       // Note that messageImpressions are only kept if a message exists in router and has a .frequency property,
       // otherwise they will be cleaned up by .cleanupImpressions()
       const testMessage = { id: "foo", frequency: { lifetimeCap: 10 } };
       messageImpressions = { foo: [0, 1, 2] };
       setMessageProviderPref([
         { id: "onboarding", type: "local", messages: [testMessage] },
       ]);
@@ -448,85 +407,16 @@ describe("ASRouter", () => {
 
       assert.isFalse(
         targetStub.sendAsyncMessage.firstCall.args[1].data.evaluationStatus
           .success
       );
     });
   });
 
-  describe("#routeMessageToTarget", () => {
-    let target;
-    beforeEach(() => {
-      sandbox.stub(CFRPageActions, "forceRecommendation");
-      sandbox.stub(CFRPageActions, "addRecommendation");
-      target = { sendAsyncMessage: sandbox.stub() };
-    });
-    it("should route toolbar_badge message to the right hub", () => {
-      Router.routeMessageToTarget({ template: "toolbar_badge" }, target);
-
-      assert.calledOnce(FakeToolbarBadgeHub.registerBadgeNotificationListener);
-      assert.notCalled(FakeBookmarkPanelHub._forceShowMessage);
-      assert.notCalled(CFRPageActions.addRecommendation);
-      assert.notCalled(CFRPageActions.forceRecommendation);
-      assert.notCalled(target.sendAsyncMessage);
-    });
-    it("should route fxa_bookmark_panel message to the right hub force = true", () => {
-      Router.routeMessageToTarget(
-        { template: "fxa_bookmark_panel" },
-        target,
-        {},
-        true
-      );
-
-      assert.calledOnce(FakeBookmarkPanelHub._forceShowMessage);
-      assert.notCalled(FakeToolbarBadgeHub.registerBadgeNotificationListener);
-      assert.notCalled(CFRPageActions.addRecommendation);
-      assert.notCalled(CFRPageActions.forceRecommendation);
-      assert.notCalled(target.sendAsyncMessage);
-    });
-    it("should route cfr_doorhanger message to the right hub force = false", () => {
-      Router.routeMessageToTarget(
-        { template: "cfr_doorhanger" },
-        target,
-        { param: {} },
-        false
-      );
-
-      assert.calledOnce(CFRPageActions.addRecommendation);
-      assert.notCalled(FakeBookmarkPanelHub._forceShowMessage);
-      assert.notCalled(FakeToolbarBadgeHub.registerBadgeNotificationListener);
-      assert.notCalled(CFRPageActions.forceRecommendation);
-      assert.notCalled(target.sendAsyncMessage);
-    });
-    it("should route cfr_doorhanger message to the right hub force = true", () => {
-      Router.routeMessageToTarget(
-        { template: "cfr_doorhanger" },
-        target,
-        {},
-        true
-      );
-
-      assert.calledOnce(CFRPageActions.forceRecommendation);
-      assert.notCalled(CFRPageActions.addRecommendation);
-      assert.notCalled(FakeBookmarkPanelHub._forceShowMessage);
-      assert.notCalled(FakeToolbarBadgeHub.registerBadgeNotificationListener);
-      assert.notCalled(target.sendAsyncMessage);
-    });
-    it("should route default to sending to content", () => {
-      Router.routeMessageToTarget({ template: "snippets" }, target, {}, true);
-
-      assert.calledOnce(target.sendAsyncMessage);
-      assert.notCalled(CFRPageActions.forceRecommendation);
-      assert.notCalled(CFRPageActions.addRecommendation);
-      assert.notCalled(FakeBookmarkPanelHub._forceShowMessage);
-      assert.notCalled(FakeToolbarBadgeHub.registerBadgeNotificationListener);
-    });
-  });
-
   describe("#loadMessagesFromAllProviders", () => {
     function assertRouterContainsMessages(messages) {
       const messageIdsInRouter = Router.state.messages.map(m => m.id);
       for (const message of messages) {
         assert.include(messageIdsInRouter, message.id);
       }
     }
 
@@ -796,115 +686,28 @@ describe("ASRouter", () => {
         .returns(false);
       Router._updateMessageProviders();
       assert.equal(Router.state.providers.length, 0);
     });
   });
 
   describe("#handleMessageRequest", () => {
     it("should get unblocked messages that match the trigger", async () => {
-      const message1 = {
+      const message = {
         id: "1",
         campaign: "foocampaign",
         trigger: { id: "foo" },
       };
-      const message2 = {
-        id: "2",
-        campaign: "foocampaign",
-        trigger: { id: "bar" },
-      };
-      await Router.setState({ messages: [message2, message1] });
-      // Just return the first message provided as arg
-      sandbox.stub(Router, "_findMessage").callsFake(messages => messages[0]);
-
-      const result = Router.handleMessageRequest({ triggerId: "foo" });
-
-      assert.deepEqual(result, message1);
-    });
-    it("should get unblocked messages that match trigger and template", async () => {
-      const message1 = {
-        id: "1",
-        campaign: "foocampaign",
-        template: "badge",
-        trigger: { id: "foo" },
-      };
-      const message2 = {
-        id: "2",
-        campaign: "foocampaign",
-        template: "snippet",
-        trigger: { id: "foo" },
-      };
-      await Router.setState({ messages: [message2, message1] });
+      await Router.setState({ messages: [message] });
       // Just return the first message provided as arg
       sandbox.stub(Router, "_findMessage").callsFake(messages => messages[0]);
 
-      const result = Router.handleMessageRequest({
-        triggerId: "foo",
-        template: "badge",
-      });
-
-      assert.deepEqual(result, message1);
-    });
-    it("should get unblocked messages that match trigger and template", async () => {
-      const message1 = {
-        id: "1",
-        campaign: "foocampaign",
-        template: "badge",
-        trigger: { id: "foo" },
-      };
-      const message2 = {
-        id: "2",
-        campaign: "foocampaign",
-        template: "snippet",
-        trigger: { id: "foo" },
-      };
-      await Router.setState({ messages: [message2, message1] });
-      // Just return the first message provided as arg
-      sandbox.stub(Router, "_findMessage").callsFake(messages => messages[0]);
-
-      const result = Router.handleMessageRequest({
-        triggerId: "foo",
-        template: "badge",
-      });
+      const result = Router.handleMessageRequest({ id: "foo" });
 
-      assert.deepEqual(result, message1);
-    });
-    it("should have messageImpressions in the message context", () => {
-      assert.propertyVal(
-        Router._getMessagesContext(),
-        "messageImpressions",
-        Router.state.messageImpressions
-      );
-    });
-    it("should return all unblocked messages that match the template, trigger if returnAll=true", async () => {
-      const message1 = {
-        id: "1",
-        template: "whatsnew_panel_message",
-        trigger: { id: "whatsNewPanelOpened" },
-      };
-      const message2 = {
-        id: "2",
-        template: "whatsnew_panel_message",
-        trigger: { id: "whatsNewPanelOpened" },
-      };
-      const message3 = {
-        id: "3",
-        template: "badge",
-      };
-      sandbox
-        .stub(Router, "_findAllMessages")
-        .callsFake(messages => [message2, message1]);
-      await Router.setState({ messages: [message3, message2, message1] });
-      const result = await Router.handleMessageRequest({
-        template: "whatsnew-panel",
-        triggerId: "whatsNewPanelOpened",
-        returnAll: true,
-      });
-
-      assert.deepEqual(result, [message2, message1]);
+      assert.deepEqual(result, message);
     });
   });
 
   describe("blocking", () => {
     it("should not return a blocked message", async () => {
       // Block all messages except the first
       await Router.setState(() => ({
         messageBlockList: ALL_MESSAGE_IDS.slice(1),
@@ -1573,22 +1376,16 @@ describe("ASRouter", () => {
         ];
 
         const { data } = fakeAsyncMessage({
           type: "TRIGGER",
           data: { trigger: { id: "foo" } },
         });
         const message = await Router._findMessage(messages, data.data.trigger);
         assert.equal(message, messages[0]);
-
-        const matches = await Router._findAllMessages(
-          messages,
-          data.data.trigger
-        );
-        assert.deepEqual(matches, messages);
       });
       it("should pick a message with the right targeting and trigger", async () => {
         let messages = [
           {
             id: "foo1",
             template: "simple_template",
             bundled: 2,
             trigger: { id: "foo" },
--- a/browser/components/newtab/test/unit/asrouter/ASRouterFeed.test.js
+++ b/browser/components/newtab/test/unit/asrouter/ASRouterFeed.test.js
@@ -7,42 +7,26 @@ import { GlobalOverrider } from "test/un
 describe("ASRouterFeed", () => {
   let Router;
   let feed;
   let channel;
   let sandbox;
   let storage;
   let globals;
   let FakeBookmarkPanelHub;
-  let FakeToolbarBadgeHub;
-  let FakeToolbarPanelHub;
   beforeEach(() => {
     sandbox = sinon.createSandbox();
     globals = new GlobalOverrider();
     FakeBookmarkPanelHub = {
       init: sandbox.stub(),
       uninit: sandbox.stub(),
     };
-    FakeToolbarBadgeHub = {
-      init: sandbox.stub(),
-    };
-    FakeToolbarPanelHub = {
-      init: sandbox.stub(),
-      uninit: sandbox.stub(),
-    };
     globals.set("BookmarkPanelHub", FakeBookmarkPanelHub);
-    globals.set("ToolbarBadgeHub", FakeToolbarBadgeHub);
-    globals.set("ToolbarPanelHub", FakeToolbarPanelHub);
 
     Router = new _ASRouter({ providers: [FAKE_LOCAL_PROVIDER] });
-    FakeToolbarPanelHub = {
-      init: sandbox.stub(),
-      uninit: sandbox.stub(),
-    };
-
     storage = {
       get: sandbox.stub().returns(Promise.resolve([])),
       set: sandbox.stub().returns(Promise.resolve()),
     };
     feed = new ASRouterFeed({ router: Router }, storage);
     channel = new FakeRemotePageManager();
     feed.store = {
       _messageChannel: { channel },
--- a/browser/components/newtab/test/unit/asrouter/ASRouterTargeting.test.js
+++ b/browser/components/newtab/test/unit/asrouter/ASRouterTargeting.test.js
@@ -43,30 +43,28 @@ describe("#CachedTargetingGetter", () =>
     clock.tick(sixHours);
 
     await topsitesCache.get();
 
     assert.calledTwice(
       global.NewTabUtils.activityStreamProvider.getTopFrecentSites
     );
   });
-  it("throws when failing getter", async () => {
+  it("should report errors", async () => {
     frecentStub.rejects(new Error("fake error"));
     clock.tick(sixHours);
 
     // assert.throws expect a function as the first parameter, try/catch is a
     // workaround
-    let rejected = false;
     try {
       await topsitesCache.get();
+      assert.isTrue(false);
     } catch (e) {
-      rejected = true;
+      assert.calledOnce(global.Cu.reportError);
     }
-
-    assert(rejected);
   });
   it("should check targeted message before message without targeting", async () => {
     const messages = await OnboardingMessageProvider.getUntranslatedMessages();
     const stub = sandbox
       .stub(ASRouterTargeting, "checkMessageTargeting")
       .resolves();
     const context = {
       attributionData: {
@@ -99,111 +97,16 @@ describe("#CachedTargetingGetter", () =>
       messages,
       trigger: { id: "firstRun" },
       context,
     });
 
     assert.isDefined(result);
     assert.equal(result.id, "FXA_1");
   });
-  describe("sortMessagesByPriority", () => {
-    it("should sort messages in descending priority order", async () => {
-      const [
-        m1,
-        m2,
-        m3,
-      ] = await OnboardingMessageProvider.getUntranslatedMessages();
-      const checkMessageTargetingStub = sandbox
-        .stub(ASRouterTargeting, "checkMessageTargeting")
-        .resolves(false);
-      sandbox.stub(ASRouterTargeting, "isTriggerMatch").resolves(true);
-
-      await ASRouterTargeting.findMatchingMessage({
-        messages: [
-          { ...m1, priority: 0 },
-          { ...m2, priority: 1 },
-          { ...m3, priority: 2 },
-        ],
-        trigger: "testing",
-      });
-
-      assert.equal(checkMessageTargetingStub.callCount, 3);
-
-      const [arg_m1] = checkMessageTargetingStub.firstCall.args;
-      assert.equal(arg_m1.id, m3.id);
-
-      const [arg_m2] = checkMessageTargetingStub.secondCall.args;
-      assert.equal(arg_m2.id, m2.id);
-
-      const [arg_m3] = checkMessageTargetingStub.thirdCall.args;
-      assert.equal(arg_m3.id, m1.id);
-    });
-    it("should sort messages with no priority last", async () => {
-      const [
-        m1,
-        m2,
-        m3,
-      ] = await OnboardingMessageProvider.getUntranslatedMessages();
-      const checkMessageTargetingStub = sandbox
-        .stub(ASRouterTargeting, "checkMessageTargeting")
-        .resolves(false);
-      sandbox.stub(ASRouterTargeting, "isTriggerMatch").resolves(true);
-
-      await ASRouterTargeting.findMatchingMessage({
-        messages: [
-          { ...m1, priority: 0 },
-          { ...m2, priority: undefined },
-          { ...m3, priority: 2 },
-        ],
-        trigger: "testing",
-      });
-
-      assert.equal(checkMessageTargetingStub.callCount, 3);
-
-      const [arg_m1] = checkMessageTargetingStub.firstCall.args;
-      assert.equal(arg_m1.id, m3.id);
-
-      const [arg_m2] = checkMessageTargetingStub.secondCall.args;
-      assert.equal(arg_m2.id, m1.id);
-
-      const [arg_m3] = checkMessageTargetingStub.thirdCall.args;
-      assert.equal(arg_m3.id, m2.id);
-    });
-    it("should keep the order of messages with same priority unchanged", async () => {
-      const [
-        m1,
-        m2,
-        m3,
-      ] = await OnboardingMessageProvider.getUntranslatedMessages();
-      const checkMessageTargetingStub = sandbox
-        .stub(ASRouterTargeting, "checkMessageTargeting")
-        .resolves(false);
-      sandbox.stub(ASRouterTargeting, "isTriggerMatch").resolves(true);
-
-      await ASRouterTargeting.findMatchingMessage({
-        messages: [
-          { ...m1, priority: 2, targeting: undefined, rank: 1 },
-          { ...m2, priority: undefined, targeting: undefined, rank: 1 },
-          { ...m3, priority: 2, targeting: undefined, rank: 1 },
-        ],
-        trigger: "testing",
-      });
-
-      assert.equal(checkMessageTargetingStub.callCount, 3);
-
-      const [arg_m1] = checkMessageTargetingStub.firstCall.args;
-      assert.equal(arg_m1.id, m1.id);
-
-      const [arg_m2] = checkMessageTargetingStub.secondCall.args;
-      assert.equal(arg_m2.id, m3.id);
-
-      const [arg_m3] = checkMessageTargetingStub.thirdCall.args;
-      assert.equal(arg_m3.id, m2.id);
-    });
-  });
   describe("combineContexts", () => {
     it("should combine the properties of the two objects", () => {
       const joined = ASRouterTargeting.combineContexts(
         {
           get foo() {
             return "foo";
           },
         },
--- a/browser/components/newtab/test/unit/asrouter/PanelTestProvider.test.js
+++ b/browser/components/newtab/test/unit/asrouter/PanelTestProvider.test.js
@@ -1,12 +1,12 @@
 import { PanelTestProvider } from "lib/PanelTestProvider.jsm";
 import schema from "content-src/asrouter/schemas/panel/cfr-fxa-bookmark.schema.json";
 const messages = PanelTestProvider.getMessages();
 
 describe("PanelTestProvider", () => {
   it("should have a message", () => {
-    assert.lengthOf(messages, 7);
+    assert.lengthOf(messages, 2);
   });
   it("should be a valid message", () => {
     assert.jsonSchema(messages[0].content, schema);
   });
 });
--- a/browser/components/newtab/test/unit/asrouter/templates/OnboardingMessage.test.jsx
+++ b/browser/components/newtab/test/unit/asrouter/templates/OnboardingMessage.test.jsx
@@ -1,13 +1,11 @@
 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: {
@@ -58,30 +56,16 @@ describe("OnboardingMessage", () => {
   });
   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));
-  });
   it("should decode the content field (double decoding)", async () => {
     const fakeContent = "foo%2540bar.org";
     globals.set("AttributionCode", {
       getAttrDataAsync: sandbox
         .stub()
         .resolves({ content: fakeContent, source: "addons.mozilla.org" }),
     });
 
--- a/browser/components/newtab/test/unit/lib/AboutPreferences.test.js
+++ b/browser/components/newtab/test/unit/lib/AboutPreferences.test.js
@@ -88,48 +88,98 @@ describe("AboutPreferences Feed", () => 
       assert.calledWith(
         Services.obs.removeObserver,
         instance,
         PREFERENCES_LOADED_EVENT
       );
     });
     it("should try to render on event", async () => {
       const stub = sandbox.stub(instance, "renderPreferences");
+      instance._strings = {};
       Sections.push({});
 
       await instance.observe(window, PREFERENCES_LOADED_EVENT);
 
       assert.calledOnce(stub);
       assert.equal(stub.firstCall.args[0], window);
-      assert.include(stub.firstCall.args[1], Sections[0]);
+      assert.deepEqual(stub.firstCall.args[1], instance._strings);
+      assert.include(stub.firstCall.args[2], Sections[0]);
     });
     it("Hide topstories rows select in sections if discovery stream is enabled", async () => {
       const stub = sandbox.stub(instance, "renderPreferences");
+      instance._strings = {};
 
       Sections.push({
         rowsPref: "row_pref",
         maxRows: 3,
         pref: { descString: "foo" },
         learnMore: { link: "https://foo.com" },
         id: "topstories",
       });
       DiscoveryStream = { config: { enabled: true } };
 
       await instance.observe(window, PREFERENCES_LOADED_EVENT);
 
       assert.calledOnce(stub);
-      const [, structure] = stub.firstCall.args;
-      assert.equal(structure[0].id, "search");
-      assert.equal(structure[1].id, "topsites");
-      assert.equal(structure[2].id, "topstories");
-      assert.isEmpty(structure[2].rowsPref);
+      assert.equal(stub.firstCall.args[2][0].id, "search");
+      assert.equal(stub.firstCall.args[2][1].id, "topsites");
+      assert.equal(stub.firstCall.args[2][2].id, "topstories");
+      assert.isEmpty(stub.firstCall.args[2][2].rowsPref);
+    });
+  });
+  describe("#strings", () => {
+    let activityStreamLocale;
+    let fetchStub;
+    let fetchText;
+    beforeEach(() => {
+      global.Cc["@mozilla.org/browser/aboutnewtab-service;1"] = {
+        getService() {
+          return { activityStreamLocale };
+        },
+      };
+      fetchStub = sandbox
+        .stub()
+        .resolves({ text: () => Promise.resolve(fetchText) });
+      globals.set("fetch", fetchStub);
+    });
+    it("should use existing strings if they exist", async () => {
+      instance._strings = {};
+
+      const strings = await instance.strings;
+
+      assert.equal(strings, instance._strings);
+    });
+    it("should report failure if missing", async () => {
+      sandbox.stub(Cu, "reportError");
+
+      const strings = await instance.strings;
+
+      assert.calledOnce(Cu.reportError);
+      assert.deepEqual(strings, {});
+    });
+    it("should fetch with the appropriate locale", async () => {
+      activityStreamLocale = "en-TEST";
+
+      await instance.strings;
+
+      assert.calledOnce(fetchStub);
+      assert.include(fetchStub.firstCall.args[0], activityStreamLocale);
+    });
+    it("should extract strings from js text", async () => {
+      const testStrings = { hello: "world" };
+      fetchText = `var strings = ${JSON.stringify(testStrings)};`;
+
+      const strings = await instance.strings;
+
+      assert.deepEqual(strings, testStrings);
     });
   });
   describe("#renderPreferences", () => {
     let node;
+    let strings;
     let prefStructure;
     let Preferences;
     let gHomePane;
     const testRender = () =>
       instance.renderPreferences(
         {
           document: {
             createXULElement: sandbox.stub().returns(node),
@@ -145,30 +195,32 @@ describe("AboutPreferences Feed", () => 
             insertBefore: sandbox.stub().returnsArg(0),
             querySelector: sandbox
               .stub()
               .returns({ appendChild: sandbox.stub() }),
           },
           Preferences,
           gHomePane,
         },
+        strings,
         prefStructure,
         DiscoveryStream.config
       );
     beforeEach(() => {
       node = {
         appendChild: sandbox.stub().returnsArg(0),
         addEventListener: sandbox.stub(),
         classList: { add: sandbox.stub(), remove: sandbox.stub() },
         cloneNode: sandbox.stub().returnsThis(),
         insertAdjacentElement: sandbox.stub().returnsArg(1),
         setAttribute: sandbox.stub(),
         remove: sandbox.stub(),
         style: {},
       };
+      strings = {};
       prefStructure = [];
       Preferences = {
         add: sandbox.stub(),
         get: sandbox.stub().returns({
           on: sandbox.stub(),
         }),
       };
       gHomePane = { toggleRestoreDefaultsBtn: sandbox.stub() };
--- a/browser/components/newtab/test/unit/lib/BookmarkPanelHub.test.js
+++ b/browser/components/newtab/test/unit/lib/BookmarkPanelHub.test.js
@@ -133,19 +133,17 @@ describe("BookmarkPanelHub", () => {
       assert.calledOnce(instance.showMessage);
     });
     it("should call handleMessageRequest", async () => {
       fakeHandleMessageRequest.resolves(fakeMessage);
 
       await instance.messageRequest(fakeTarget, {});
 
       assert.calledOnce(fakeHandleMessageRequest);
-      assert.calledWithExactly(fakeHandleMessageRequest, {
-        triggerId: instance._trigger.id,
-      });
+      assert.calledWithExactly(fakeHandleMessageRequest, instance._trigger);
     });
     it("should call onResponse", async () => {
       fakeHandleMessageRequest.resolves(fakeMessage);
 
       await instance.messageRequest(fakeTarget, {});
 
       assert.calledOnce(instance.onResponse);
       assert.calledWithExactly(
deleted file mode 100644
--- a/browser/components/newtab/test/unit/lib/LinksCache.test.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { LinksCache } from "lib/LinksCache.jsm";
-
-describe("LinksCache", () => {
-  it("throws when failing request", async () => {
-    const cache = new LinksCache();
-
-    let rejected = false;
-    try {
-      await cache.request();
-    } catch (error) {
-      rejected = true;
-    }
-
-    assert(rejected);
-  });
-});
--- a/browser/components/newtab/test/unit/lib/PersistentCache.test.js
+++ b/browser/components/newtab/test/unit/lib/PersistentCache.test.js
@@ -110,22 +110,10 @@ describe("PersistentCache", () => {
       assert.calledOnce(fakeOS.File.writeAtomic);
       assert.calledWith(
         fakeOS.File.writeAtomic,
         filename,
         `{"testkey":{"x":1,"y":2,"z":3}}`,
         { tmpPath: `${filename}.tmp` }
       );
     });
-    it("throws when failing the file", async () => {
-      sandbox.stub(OS.Path, "join").throws("bad file");
-
-      let rejected = false;
-      try {
-        await cache.set("key", "val");
-      } catch (error) {
-        rejected = true;
-      }
-
-      assert(rejected);
-    });
   });
 });
deleted file mode 100644
--- a/browser/components/newtab/test/unit/lib/ToolbarBadgeHub.test.js
+++ /dev/null
@@ -1,364 +0,0 @@
-import { _ToolbarBadgeHub } from "lib/ToolbarBadgeHub.jsm";
-import { GlobalOverrider } from "test/unit/utils";
-import { PanelTestProvider } from "lib/PanelTestProvider.jsm";
-import { _ToolbarPanelHub } from "lib/ToolbarPanelHub.jsm";
-
-describe("ToolbarBadgeHub", () => {
-  let sandbox;
-  let instance;
-  let fakeAddImpression;
-  let fxaMessage;
-  let whatsnewMessage;
-  let fakeElement;
-  let globals;
-  let everyWindowStub;
-  let clearTimeoutStub;
-  let setTimeoutStub;
-  beforeEach(async () => {
-    globals = new GlobalOverrider();
-    sandbox = sinon.createSandbox();
-    instance = new _ToolbarBadgeHub();
-    fakeAddImpression = sandbox.stub();
-    const msgs = await PanelTestProvider.getMessages();
-    fxaMessage = msgs.find(({ id }) => id === "FXA_ACCOUNTS_BADGE");
-    whatsnewMessage = msgs.find(({ id }) => id.includes("WHATS_NEW_BADGE_"));
-    fakeElement = {
-      setAttribute: sandbox.stub(),
-      removeAttribute: sandbox.stub(),
-      querySelector: sandbox.stub(),
-      addEventListener: sandbox.stub(),
-    };
-    // Share the same element when selecting child nodes
-    fakeElement.querySelector.returns(fakeElement);
-    everyWindowStub = {
-      registerCallback: sandbox.stub(),
-      unregisterCallback: sandbox.stub(),
-    };
-    clearTimeoutStub = sandbox.stub();
-    setTimeoutStub = sandbox.stub();
-    globals.set("EveryWindow", everyWindowStub);
-    globals.set("setTimeout", setTimeoutStub);
-    globals.set("clearTimeout", clearTimeoutStub);
-  });
-  afterEach(() => {
-    sandbox.restore();
-    globals.restore();
-  });
-  it("should create an instance", () => {
-    assert.ok(instance);
-  });
-  describe("#init", () => {
-    it("should make a messageRequest on init", async () => {
-      sandbox.stub(instance, "messageRequest");
-      const waitForInitialized = sandbox.stub().resolves();
-
-      await instance.init(waitForInitialized, {});
-      assert.calledOnce(instance.messageRequest);
-      assert.calledWithExactly(instance.messageRequest, "toolbarBadgeUpdate");
-    });
-  });
-  describe("#uninit", () => {
-    it("should clear any setTimeout cbs", () => {
-      instance.init(sandbox.stub().resolves(), {});
-
-      instance.state.showBadgeTimeoutId = 2;
-
-      instance.uninit();
-
-      assert.calledOnce(clearTimeoutStub);
-      assert.calledWithExactly(clearTimeoutStub, 2);
-    });
-  });
-  describe("messageRequest", () => {
-    let handleMessageRequestStub;
-    beforeEach(() => {
-      handleMessageRequestStub = sandbox.stub().returns(fxaMessage);
-      sandbox
-        .stub(instance, "_handleMessageRequest")
-        .value(handleMessageRequestStub);
-      sandbox.stub(instance, "registerBadgeNotificationListener");
-    });
-    it("should fetch a message with the provided trigger and template", async () => {
-      await instance.messageRequest("trigger");
-
-      assert.calledOnce(handleMessageRequestStub);
-      assert.calledWithExactly(handleMessageRequestStub, {
-        triggerId: "trigger",
-        template: instance.template,
-      });
-    });
-    it("should call addToolbarNotification with browser window and message", async () => {
-      await instance.messageRequest("trigger");
-
-      assert.calledOnce(instance.registerBadgeNotificationListener);
-      assert.calledWithExactly(
-        instance.registerBadgeNotificationListener,
-        fxaMessage
-      );
-    });
-    it("shouldn't do anything if no message is provided", () => {
-      handleMessageRequestStub.returns(null);
-      instance.messageRequest("trigger");
-
-      assert.notCalled(instance.registerBadgeNotificationListener);
-    });
-  });
-  describe("addToolbarNotification", () => {
-    let target;
-    let fakeDocument;
-    beforeEach(() => {
-      fakeDocument = { getElementById: sandbox.stub().returns(fakeElement) };
-      target = { browser: { ownerDocument: fakeDocument } };
-    });
-    it("shouldn't do anything if target element is not found", () => {
-      fakeDocument.getElementById.returns(null);
-      instance.addToolbarNotification(target, fxaMessage);
-
-      assert.notCalled(fakeElement.setAttribute);
-    });
-    it("should target the element specified in the message", () => {
-      instance.addToolbarNotification(target, fxaMessage);
-
-      assert.calledOnce(fakeDocument.getElementById);
-      assert.calledWithExactly(
-        fakeDocument.getElementById,
-        fxaMessage.content.target
-      );
-    });
-    it("should show a notification", () => {
-      instance.addToolbarNotification(target, fxaMessage);
-
-      assert.calledTwice(fakeElement.setAttribute);
-      assert.calledWithExactly(fakeElement.setAttribute, "badged", true);
-      assert.calledWithExactly(fakeElement.setAttribute, "value", "x");
-    });
-    it("should attach a cb on the notification", () => {
-      instance.addToolbarNotification(target, fxaMessage);
-
-      assert.calledTwice(fakeElement.addEventListener);
-      assert.calledWithExactly(
-        fakeElement.addEventListener,
-        "mousedown",
-        instance.removeAllNotifications
-      );
-      assert.calledWithExactly(
-        fakeElement.addEventListener,
-        "click",
-        instance.removeAllNotifications
-      );
-    });
-    it("should execute actions if they exist", () => {
-      sandbox.stub(instance, "executeAction");
-      instance.addToolbarNotification(target, whatsnewMessage);
-
-      assert.calledOnce(instance.executeAction);
-      assert.calledWithExactly(
-        instance.executeAction,
-        whatsnewMessage.content.action
-      );
-    });
-  });
-  describe("registerBadgeNotificationListener", () => {
-    beforeEach(() => {
-      instance.init(sandbox.stub().resolves(), {
-        addImpression: fakeAddImpression,
-      });
-      sandbox.stub(instance, "addToolbarNotification").returns(fakeElement);
-      sandbox.stub(instance, "removeToolbarNotification");
-    });
-    afterEach(() => {
-      instance.uninit();
-    });
-    it("should add an impression for the message", () => {
-      instance.registerBadgeNotificationListener(fxaMessage);
-
-      assert.calledOnce(instance._addImpression);
-      assert.calledWithExactly(instance._addImpression, fxaMessage);
-    });
-    it("should register a callback that adds/removes the notification", () => {
-      instance.registerBadgeNotificationListener(fxaMessage);
-
-      assert.calledOnce(everyWindowStub.registerCallback);
-      assert.calledWithExactly(
-        everyWindowStub.registerCallback,
-        instance.id,
-        sinon.match.func,
-        sinon.match.func
-      );
-
-      const [
-        ,
-        initFn,
-        uninitFn,
-      ] = everyWindowStub.registerCallback.firstCall.args;
-
-      initFn(window);
-      // Test that it doesn't try to add a second notification
-      initFn(window);
-
-      assert.calledOnce(instance.addToolbarNotification);
-      assert.calledWithExactly(
-        instance.addToolbarNotification,
-        window,
-        fxaMessage
-      );
-
-      uninitFn(window);
-
-      assert.calledOnce(instance.removeToolbarNotification);
-      assert.calledWithExactly(instance.removeToolbarNotification, fakeElement);
-    });
-    it("should unregister notifications when forcing a badge via devtools", () => {
-      instance.registerBadgeNotificationListener(fxaMessage, { force: true });
-
-      assert.calledOnce(everyWindowStub.unregisterCallback);
-      assert.calledWithExactly(everyWindowStub.unregisterCallback, instance.id);
-    });
-  });
-  describe("executeAction", () => {
-    it("should call ToolbarPanelHub.enableToolbarButton", () => {
-      const stub = sandbox.stub(
-        _ToolbarPanelHub.prototype,
-        "enableToolbarButton"
-      );
-
-      instance.executeAction({ id: "show-whatsnew-button" });
-
-      assert.calledOnce(stub);
-    });
-  });
-  describe("removeToolbarNotification", () => {
-    it("should remove the notification", () => {
-      instance.removeToolbarNotification(fakeElement);
-
-      assert.calledTwice(fakeElement.removeAttribute);
-      assert.calledWithExactly(fakeElement.removeAttribute, "badged");
-    });
-  });
-  describe("removeAllNotifications", () => {
-    let blockMessageByIdStub;
-    let fakeEvent;
-    beforeEach(() => {
-      blockMessageByIdStub = sandbox.stub();
-      sandbox.stub(instance, "_blockMessageById").value(blockMessageByIdStub);
-      instance.state = { notification: { id: fxaMessage.id } };
-      fakeEvent = { target: { removeEventListener: sandbox.stub() } };
-    });
-    it("should call to block the message", () => {
-      instance.removeAllNotifications();
-
-      assert.calledOnce(blockMessageByIdStub);
-      assert.calledWithExactly(blockMessageByIdStub, fxaMessage.id);
-    });
-    it("should remove the window listener", () => {
-      instance.removeAllNotifications();
-
-      assert.calledOnce(everyWindowStub.unregisterCallback);
-      assert.calledWithExactly(everyWindowStub.unregisterCallback, instance.id);
-    });
-    it("should ignore right mouse button (mousedown event)", () => {
-      fakeEvent.type = "mousedown";
-      fakeEvent.button = 1; // not left click
-
-      instance.removeAllNotifications(fakeEvent);
-
-      assert.notCalled(fakeEvent.target.removeEventListener);
-      assert.notCalled(everyWindowStub.unregisterCallback);
-    });
-    it("should ignore right mouse button (click event)", () => {
-      fakeEvent.type = "click";
-      fakeEvent.button = 1; // not left click
-
-      instance.removeAllNotifications(fakeEvent);
-
-      assert.notCalled(fakeEvent.target.removeEventListener);
-      assert.notCalled(everyWindowStub.unregisterCallback);
-    });
-    it("should ignore keypresses that are not meant to focus the target", () => {
-      fakeEvent.type = "keypress";
-      fakeEvent.key = "\t"; // not enter
-
-      instance.removeAllNotifications(fakeEvent);
-
-      assert.notCalled(fakeEvent.target.removeEventListener);
-      assert.notCalled(everyWindowStub.unregisterCallback);
-    });
-    it("should remove the event listeners after succesfully focusing the element", () => {
-      fakeEvent.type = "click";
-      fakeEvent.button = 0;
-
-      instance.removeAllNotifications(fakeEvent);
-
-      assert.calledTwice(fakeEvent.target.removeEventListener);
-      assert.calledWithExactly(
-        fakeEvent.target.removeEventListener,
-        "mousedown",
-        instance.removeAllNotifications
-      );
-      assert.calledWithExactly(
-        fakeEvent.target.removeEventListener,
-        "click",
-        instance.removeAllNotifications
-      );
-    });
-    it("should remove the event listeners after succesfully focusing the element", () => {
-      fakeEvent.type = "keypress";
-      fakeEvent.key = "Enter";
-
-      instance.removeAllNotifications(fakeEvent);
-
-      assert.calledTwice(fakeEvent.target.removeEventListener);
-      assert.calledWithExactly(
-        fakeEvent.target.removeEventListener,
-        "mousedown",
-        instance.removeAllNotifications
-      );
-      assert.calledWithExactly(
-        fakeEvent.target.removeEventListener,
-        "click",
-        instance.removeAllNotifications
-      );
-    });
-  });
-  describe("message with delay", () => {
-    let msg_with_delay;
-    beforeEach(() => {
-      instance.init(sandbox.stub().resolves(), {
-        addImpression: fakeAddImpression,
-      });
-      msg_with_delay = {
-        ...fxaMessage,
-        content: {
-          ...fxaMessage.content,
-          delay: 500,
-        },
-      };
-      sandbox.stub(instance, "registerBadgeToAllWindows");
-    });
-    afterEach(() => {
-      instance.uninit();
-    });
-    it("should register a cb to fire after msg.content.delay ms", () => {
-      instance.registerBadgeNotificationListener(msg_with_delay);
-
-      assert.calledOnce(setTimeoutStub);
-      assert.calledWithExactly(
-        setTimeoutStub,
-        sinon.match.func,
-        msg_with_delay.content.delay
-      );
-
-      const [cb] = setTimeoutStub.firstCall.args;
-
-      assert.notCalled(instance.registerBadgeToAllWindows);
-
-      cb();
-
-      assert.calledOnce(instance.registerBadgeToAllWindows);
-      assert.calledWithExactly(
-        instance.registerBadgeToAllWindows,
-        msg_with_delay
-      );
-    });
-  });
-});
deleted file mode 100644
--- a/browser/components/newtab/test/unit/lib/ToolbarPanelHub.test.js
+++ /dev/null
@@ -1,150 +0,0 @@
-import { _ToolbarPanelHub } from "lib/ToolbarPanelHub.jsm";
-import { GlobalOverrider } from "test/unit/utils";
-import { PanelTestProvider } from "lib/PanelTestProvider.jsm";
-
-describe("ToolbarPanelHub", () => {
-  let globals;
-  let sandbox;
-  let instance;
-  let everyWindowStub;
-  let fakeDocument;
-  let fakeWindow;
-  let fakeElementById;
-  let createdElements = [];
-  let eventListeners = {};
-
-  beforeEach(async () => {
-    sandbox = sinon.createSandbox();
-    globals = new GlobalOverrider();
-    instance = new _ToolbarPanelHub();
-    fakeElementById = {
-      setAttribute: sandbox.stub(),
-      removeAttribute: sandbox.stub(),
-      querySelector: sandbox.stub().returns(null),
-      appendChild: sandbox.stub(),
-    };
-    fakeDocument = {
-      l10n: {
-        setAttributes: sandbox.stub(),
-      },
-      getElementById: sandbox.stub().returns(fakeElementById),
-      querySelector: sandbox.stub().returns({}),
-      createElementNS: (ns, tagName) => {
-        const element = {
-          tagName,
-          classList: {
-            add: sandbox.stub(),
-          },
-          addEventListener: (ev, fn) => {
-            eventListeners[ev] = fn;
-          },
-          appendChild: sandbox.stub(),
-        };
-        createdElements.push(element);
-        return element;
-      },
-    };
-    fakeWindow = {
-      browser: {
-        ownerDocument: fakeDocument,
-      },
-      MozXULElement: { insertFTLIfNeeded: sandbox.stub() },
-      ownerGlobal: {
-        openLinkIn: sandbox.stub(),
-      },
-    };
-    everyWindowStub = {
-      registerCallback: sandbox.stub(),
-      unregisterCallback: sandbox.stub(),
-    };
-    globals.set("EveryWindow", everyWindowStub);
-  });
-  afterEach(() => {
-    instance.uninit();
-    sandbox.restore();
-  });
-  it("should create an instance", () => {
-    assert.ok(instance);
-  });
-  it("should not enableAppmenuButton() on init() if pref is not enabled", () => {
-    sandbox.stub(global.Services.prefs, "getBoolPref").returns(false);
-    instance.enableAppmenuButton = sandbox.stub();
-    instance.init({ getMessages: () => {} });
-    assert.notCalled(instance.enableAppmenuButton);
-  });
-  it("should enableAppmenuButton() on init() if pref is enabled", () => {
-    sandbox.stub(global.Services.prefs, "getBoolPref").returns(true);
-    instance.enableAppmenuButton = sandbox.stub();
-    instance.init({ getMessages: () => {} });
-    assert.calledOnce(instance.enableAppmenuButton);
-  });
-  it("should unregisterCallback on uninit()", () => {
-    instance.uninit();
-    assert.calledTwice(everyWindowStub.unregisterCallback);
-  });
-  it("should registerCallback on enableAppmenuButton()", () => {
-    instance.enableAppmenuButton();
-    assert.calledOnce(everyWindowStub.registerCallback);
-  });
-  it("should registerCallback on enableToolbarButton()", () => {
-    instance.enableToolbarButton();
-    assert.calledOnce(everyWindowStub.registerCallback);
-  });
-  it("should unhide appmenu button on _showAppmenuButton()", () => {
-    instance._showAppmenuButton(fakeWindow);
-    assert.calledWith(fakeElementById.removeAttribute, "hidden");
-  });
-  it("should hide appmenu button on _hideAppmenuButton()", () => {
-    instance._hideAppmenuButton(fakeWindow);
-    assert.calledWith(fakeElementById.setAttribute, "hidden", true);
-  });
-  it("should unhide toolbar button on _showToolbarButton()", () => {
-    instance._showToolbarButton(fakeWindow);
-    assert.calledWith(fakeElementById.removeAttribute, "hidden");
-  });
-  it("should hide toolbar button on _hideToolbarButton()", () => {
-    instance._hideToolbarButton(fakeWindow);
-    assert.calledWith(fakeElementById.setAttribute, "hidden", true);
-  });
-  it("should render messages to the panel on renderMessages()", async () => {
-    const messages = (await PanelTestProvider.getMessages()).filter(
-      m => m.template === "whatsnew_panel_message"
-    );
-    messages[0].content.link_text = { string_id: "link_text_id" };
-    instance.init({
-      getMessages: sandbox
-        .stub()
-        .returns([messages[0], messages[2], messages[1]]),
-    });
-    await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
-    for (let message of messages) {
-      assert.ok(
-        createdElements.find(
-          el => el.tagName === "h2" && el.textContent === message.content.title
-        )
-      );
-      assert.ok(
-        createdElements.find(
-          el => el.tagName === "p" && el.textContent === message.content.body
-        )
-      );
-    }
-    // Call the click handler to make coverage happy.
-    eventListeners.click();
-    assert.calledOnce(fakeWindow.ownerGlobal.openLinkIn);
-  });
-  it("should only render unique dates (no duplicates)", async () => {
-    instance._createDateElement = sandbox.stub();
-    const messages = (await PanelTestProvider.getMessages()).filter(
-      m => m.template === "whatsnew_panel_message"
-    );
-    const uniqueDates = [
-      ...new Set(messages.map(m => m.content.published_date)),
-    ];
-    instance.init({
-      getMessages: sandbox.stub().returns(messages),
-    });
-    await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
-    assert.callCount(instance._createDateElement, uniqueDates.length);
-  });
-});
--- a/browser/components/newtab/test/unit/unit-entry.js
+++ b/browser/components/newtab/test/unit/unit-entry.js
@@ -37,17 +37,17 @@ chai.use(chaiJsonSchema);
 
 const overrider = new GlobalOverrider();
 const TEST_GLOBAL = {
   AddonManager: {
     getActiveAddons() {
       return Promise.resolve({ addons: [], fullData: false });
     },
   },
-  AppConstants: { MOZILLA_OFFICIAL: true, MOZ_APP_VERSION: "69.0a1" },
+  AppConstants: { MOZILLA_OFFICIAL: true },
   UpdateUtils: { getUpdateChannel() {} },
   BrowserWindowTracker: { getTopWindow() {} },
   ChromeUtils: {
     defineModuleGetter() {},
     generateQI() {
       return {};
     },
     import() {
@@ -277,23 +277,19 @@ const TEST_GLOBAL = {
         searchForm:
           "https://www.google.com/search?q=&ie=utf-8&oe=utf-8&client=firefox-b",
       },
     },
     scriptSecurityManager: {
       createNullPrincipal() {},
       getSystemPrincipal() {},
     },
-    wm: {
-      getMostRecentWindow: () => window,
-      getMostRecentBrowserWindow: () => window,
-      getEnumerator: () => [],
-    },
+    wm: { getMostRecentWindow: () => window, getEnumerator: () => [] },
     ww: { registerNotification() {}, unregisterNotification() {} },
-    appinfo: { appBuildID: "20180710100040", version: "69.0a1" },
+    appinfo: { appBuildID: "20180710100040" },
   },
   XPCOMUtils: {
     defineLazyGetter(object, name, f) {
       if (object && name) {
         object[name] = f();
       } else {
         f();
       }