Bug 1426203 - Add ES modules, pinned counts and bug fixes to Activity Stream. r?k88hudson draft
authorEd Lee <edilee@mozilla.com>
Wed, 20 Dec 2017 12:10:35 -0800
changeset 713792 7a015d9d33cf0b8ff64795b4d7a764e5a9557ad6
parent 713607 cd50ddedbd7c4b4d976b4c290dcc1b1e20cdf66c
child 744430 c427226c9e7883aebc019bb593377aa7b6ef074b
push id93749
push userbmo:edilee@mozilla.com
push dateWed, 20 Dec 2017 23:45:58 +0000
reviewersk88hudson
bugs1426203
milestone59.0a1
Bug 1426203 - Add ES modules, pinned counts and bug fixes to Activity Stream. r?k88hudson MozReview-Commit-ID: K1Jyn2qFDPr
browser/components/newtab/aboutNewTabService.js
browser/extensions/activity-stream/css/activity-stream-linux.css
browser/extensions/activity-stream/css/activity-stream-mac.css
browser/extensions/activity-stream/css/activity-stream-windows.css
browser/extensions/activity-stream/data/content/activity-stream.bundle.js
browser/extensions/activity-stream/install.rdf.in
browser/extensions/activity-stream/prerendered/locales/ar/activity-stream-strings.js
browser/extensions/activity-stream/prerendered/locales/br/activity-stream-strings.js
browser/extensions/activity-stream/prerendered/locales/el/activity-stream-strings.js
browser/extensions/activity-stream/prerendered/locales/ff/activity-stream-prerendered.html
browser/extensions/activity-stream/prerendered/locales/ff/activity-stream-strings.js
browser/extensions/activity-stream/prerendered/locales/gn/activity-stream-prerendered.html
browser/extensions/activity-stream/prerendered/locales/gn/activity-stream-strings.js
browser/extensions/activity-stream/prerendered/locales/gn/activity-stream.html
browser/extensions/activity-stream/prerendered/locales/ka/activity-stream-strings.js
browser/extensions/activity-stream/prerendered/locales/kab/activity-stream-strings.js
browser/extensions/activity-stream/prerendered/locales/ko/activity-stream-strings.js
browser/extensions/activity-stream/prerendered/locales/lo/activity-stream-strings.js
browser/extensions/activity-stream/prerendered/locales/pt-BR/activity-stream-strings.js
browser/extensions/activity-stream/prerendered/locales/sl/activity-stream-strings.js
browser/extensions/activity-stream/prerendered/locales/sr/activity-stream-prerendered.html
browser/extensions/activity-stream/prerendered/locales/sr/activity-stream-strings.js
browser/extensions/activity-stream/prerendered/locales/ta/activity-stream-strings.js
browser/extensions/activity-stream/prerendered/locales/th/activity-stream-strings.js
browser/extensions/activity-stream/test/.eslintrc.js
browser/extensions/activity-stream/test/schemas/pings.js
browser/extensions/activity-stream/test/unit/activity-stream-prerender.test.jsx
browser/extensions/activity-stream/test/unit/common/Actions.test.js
browser/extensions/activity-stream/test/unit/common/Dedupe.test.js
browser/extensions/activity-stream/test/unit/common/PerfService.test.js
browser/extensions/activity-stream/test/unit/common/PrerenderData.test.js
browser/extensions/activity-stream/test/unit/common/Reducers.test.js
browser/extensions/activity-stream/test/unit/lib/ActivityStream.test.js
browser/extensions/activity-stream/test/unit/lib/ActivityStreamMessageChannel.test.js
browser/extensions/activity-stream/test/unit/lib/ActivityStreamPrefs.test.js
browser/extensions/activity-stream/test/unit/lib/FaviconFeed.test.js
browser/extensions/activity-stream/test/unit/lib/FilterAdult.test.js
browser/extensions/activity-stream/test/unit/lib/HighlightsFeed.test.js
browser/extensions/activity-stream/test/unit/lib/ManualMigration.test.js
browser/extensions/activity-stream/test/unit/lib/NewTabInit.test.js
browser/extensions/activity-stream/test/unit/lib/PersistentCache.test.js
browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js
browser/extensions/activity-stream/test/unit/lib/PrefsFeed.test.js
browser/extensions/activity-stream/test/unit/lib/Screenshots.test.js
browser/extensions/activity-stream/test/unit/lib/SectionsManager.test.js
browser/extensions/activity-stream/test/unit/lib/ShortUrl.test.js
browser/extensions/activity-stream/test/unit/lib/SnippetsFeed.test.js
browser/extensions/activity-stream/test/unit/lib/Store.test.js
browser/extensions/activity-stream/test/unit/lib/SystemTickFeed.test.js
browser/extensions/activity-stream/test/unit/lib/TelemetryFeed.test.js
browser/extensions/activity-stream/test/unit/lib/TippyTopProvider.test.js
browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
browser/extensions/activity-stream/test/unit/lib/TopStoriesFeed.test.js
browser/extensions/activity-stream/test/unit/lib/UserDomainAffinityProvider.test.js
browser/extensions/activity-stream/test/unit/lib/init-store.test.js
browser/extensions/activity-stream/test/unit/unit-entry.js
browser/extensions/activity-stream/test/unit/utils.js
--- a/browser/components/newtab/aboutNewTabService.js
+++ b/browser/components/newtab/aboutNewTabService.js
@@ -16,17 +16,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource:///modules/AboutNewTab.jsm");
 
 const LOCAL_NEWTAB_URL = "chrome://browser/content/newtab/newTab.xhtml";
 const TOPIC_APP_QUIT = "quit-application-granted";
 const TOPIC_LOCALES_CHANGE = "intl:requested-locales-changed";
 
 // Automated tests ensure packaged locales are in this list. Copied output of:
 // https://github.com/mozilla/activity-stream/blob/master/bin/render-activity-stream-html.js
-const ACTIVITY_STREAM_LOCALES = new Set("en-US ach ar ast az be bg bn-BD bn-IN br bs ca cak cs cy da de dsb el en-GB eo es-AR es-CL es-ES es-MX et eu fa ff fi fr fy-NL ga-IE gd gl gu-IN he hi-IN hr hsb hu hy-AM ia id it ja ka kab kk km kn ko lij lo lt ltg lv mk ml mr ms my nb-NO ne-NP nl nn-NO pa-IN pl pt-BR pt-PT rm ro ru si sk sl sq sr sv-SE ta te th tl tr uk ur uz vi zh-CN zh-TW".split(" "));
+const ACTIVITY_STREAM_LOCALES = new Set("en-US ach ar ast az be bg bn-BD bn-IN br bs ca cak cs cy da de dsb el en-GB eo es-AR es-CL es-ES es-MX et eu fa ff fi fr fy-NL ga-IE gd gl gn gu-IN he hi-IN hr hsb hu hy-AM ia id it ja ka kab kk km kn ko lij lo lt ltg lv mk ml mr ms my nb-NO ne-NP nl nn-NO pa-IN pl pt-BR pt-PT rm ro ru si sk sl sq sr sv-SE ta te th tl tr uk ur uz vi zh-CN zh-TW".split(" "));
 
 const ABOUT_URL = "about:newtab";
 
 const IS_MAIN_PROCESS = Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT;
 
 const IS_RELEASE_OR_BETA = AppConstants.RELEASE_OR_BETA;
 
 // Pref that tells if activity stream is enabled
--- a/browser/extensions/activity-stream/css/activity-stream-linux.css
+++ b/browser/extensions/activity-stream/css/activity-stream-linux.css
@@ -1176,18 +1176,18 @@ main {
       position: absolute; }
     .collapsible-section .section-top-bar .section-info-option .info-option::before {
       background-image: url("chrome://global/skin/arrow/panelarrow-vertical.svg");
       background-position: right 6px bottom;
       background-repeat: no-repeat;
       background-size: 20px 10px;
       -moz-context-properties: fill, stroke;
       fill: #FFF;
+      height: 32px;
       stroke: #D7D7DB;
-      height: 32px;
       top: -32px;
       width: 43px; }
     .collapsible-section .section-top-bar .section-info-option .info-option:dir(rtl)::before {
       background-position-x: 6px; }
     .collapsible-section .section-top-bar .section-info-option .info-option::after {
       height: 10px;
       offset-inline-start: 0;
       top: -10px; }
--- a/browser/extensions/activity-stream/css/activity-stream-mac.css
+++ b/browser/extensions/activity-stream/css/activity-stream-mac.css
@@ -1176,18 +1176,18 @@ main {
       position: absolute; }
     .collapsible-section .section-top-bar .section-info-option .info-option::before {
       background-image: url("chrome://global/skin/arrow/panelarrow-vertical.svg");
       background-position: right 7px bottom;
       background-repeat: no-repeat;
       background-size: 18px 10px;
       -moz-context-properties: fill, stroke;
       fill: #FFF;
+      height: 32px;
       stroke: #D7D7DB;
-      height: 32px;
       top: -32px;
       width: 43px; }
     .collapsible-section .section-top-bar .section-info-option .info-option:dir(rtl)::before {
       background-position-x: 7px; }
     .collapsible-section .section-top-bar .section-info-option .info-option::after {
       height: 10px;
       offset-inline-start: 0;
       top: -10px; }
--- a/browser/extensions/activity-stream/css/activity-stream-windows.css
+++ b/browser/extensions/activity-stream/css/activity-stream-windows.css
@@ -1176,18 +1176,18 @@ main {
       position: absolute; }
     .collapsible-section .section-top-bar .section-info-option .info-option::before {
       background-image: url("chrome://global/skin/arrow/panelarrow-vertical.svg");
       background-position: right 6px bottom;
       background-repeat: no-repeat;
       background-size: 20px 10px;
       -moz-context-properties: fill, stroke;
       fill: #FFF;
+      height: 32px;
       stroke: #D7D7DB;
-      height: 32px;
       top: -32px;
       width: 43px; }
     .collapsible-section .section-top-bar .section-info-option .info-option:dir(rtl)::before {
       background-position-x: 6px; }
     .collapsible-section .section-top-bar .section-info-option .info-option::after {
       height: 10px;
       offset-inline-start: 0;
       top: -10px; }
--- a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
+++ b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
@@ -55,17 +55,17 @@
 /******/
 /******/ 	// Object.prototype.hasOwnProperty.call
 /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
 /******/
 /******/ 	// __webpack_public_path__
 /******/ 	__webpack_require__.p = "";
 /******/
 /******/ 	// Load entry module and return exports
-/******/ 	return __webpack_require__(__webpack_require__.s = 12);
+/******/ 	return __webpack_require__(__webpack_require__.s = 10);
 /******/ })
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -330,39 +330,26 @@ try {
 // We return undefined, instead of nothing here, so it's
 // easier to handle this case. if(!global) { ...}
 
 module.exports = g;
 
 
 /***/ }),
 /* 5 */
-/***/ (function(module, exports) {
-
-module.exports = {
-  TOP_SITES_SOURCE: "TOP_SITES",
-  TOP_SITES_CONTEXT_MENU_OPTIONS: ["CheckPinTopSite", "EditTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl", "DeleteUrl"],
-  // minimum size necessary to show a rich icon instead of a screenshot
-  MIN_RICH_FAVICON_SIZE: 96,
-  // minimum size necessary to show any icon in the top left corner with a screenshot
-  MIN_CORNER_FAVICON_SIZE: 16
-};
-
-/***/ }),
-/* 6 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 /* 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 { actionTypes: at } = __webpack_require__(0);
-const { Dedupe } = __webpack_require__(20);
+const { Dedupe } = __webpack_require__(13);
 
 const TOP_SITES_DEFAULT_LENGTH = 6;
 const TOP_SITES_SHOWMORE_LENGTH = 12;
 
 const dedupe = new Dedupe(site => site && site.url);
 
 const INITIAL_STATE = {
   App: {
@@ -687,271 +674,257 @@ module.exports = {
   reducers,
   INITIAL_STATE,
   insertPinned,
   TOP_SITES_DEFAULT_LENGTH,
   TOP_SITES_SHOWMORE_LENGTH
 };
 
 /***/ }),
-/* 7 */
-/***/ (function(module, exports, __webpack_require__) {
-
-var _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; };
-
-const React = __webpack_require__(1);
-const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
-
-const LinkMenu = __webpack_require__(8);
-
-const { TOP_SITES_SOURCE, TOP_SITES_CONTEXT_MENU_OPTIONS, MIN_RICH_FAVICON_SIZE, MIN_CORNER_FAVICON_SIZE } = __webpack_require__(5);
-
-const TopSiteLink = props => {
-  const { link, title } = props;
-  const topSiteOuterClassName = `top-site-outer${props.className ? ` ${props.className}` : ""}`;
-  const { tippyTopIcon, faviconSize } = link;
-  const letterFallback = title[0];
-  let imageClassName;
-  let imageStyle;
-  let showSmallFavicon = false;
-  let smallFaviconStyle;
-  let smallFaviconFallback;
-  if (tippyTopIcon || faviconSize >= MIN_RICH_FAVICON_SIZE) {
-    // styles and class names for top sites with rich icons
-    imageClassName = "top-site-icon rich-icon";
-    imageStyle = {
-      backgroundColor: link.backgroundColor,
-      backgroundImage: `url(${tippyTopIcon || link.favicon})`
-    };
-  } else {
-    // styles and class names for top sites with screenshot + small icon in top left corner
-    imageClassName = `screenshot${link.screenshot ? " active" : ""}`;
-    imageStyle = { backgroundImage: link.screenshot ? `url(${link.screenshot})` : "none" };
-
-    // only show a favicon in top left if it's greater than 16x16
-    if (faviconSize >= MIN_CORNER_FAVICON_SIZE) {
-      showSmallFavicon = true;
-      smallFaviconStyle = { backgroundImage: `url(${link.favicon})` };
-    } else if (link.screenshot) {
-      // Don't show a small favicon if there is no screenshot, because that
-      // would result in two fallback icons
-      showSmallFavicon = true;
-      smallFaviconFallback = true;
-    }
-  }
-  return React.createElement(
-    "li",
-    { className: topSiteOuterClassName, key: link.guid || link.url },
-    React.createElement(
-      "a",
-      { href: link.url, onClick: props.onClick },
-      React.createElement(
-        "div",
-        { className: "tile", "aria-hidden": true, "data-fallback": letterFallback },
-        React.createElement("div", { className: imageClassName, style: imageStyle }),
-        showSmallFavicon && React.createElement("div", {
-          className: "top-site-icon default-icon",
-          "data-fallback": smallFaviconFallback && letterFallback,
-          style: smallFaviconStyle })
-      ),
-      React.createElement(
-        "div",
-        { className: `title ${link.isPinned ? "pinned" : ""}` },
-        link.isPinned && React.createElement("div", { className: "icon icon-pin-small" }),
-        React.createElement(
-          "span",
-          { dir: "auto" },
-          title
-        )
-      )
-    ),
-    props.children
-  );
-};
-TopSiteLink.defaultProps = {
-  title: "",
-  link: {}
-};
-
-class TopSite extends React.PureComponent {
+/* 6 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+
+// EXTERNAL MODULE: ./system-addon/common/Actions.jsm
+var Actions = __webpack_require__(0);
+var Actions_default = /*#__PURE__*/__webpack_require__.n(Actions);
+
+// EXTERNAL MODULE: external "React"
+var external__React_ = __webpack_require__(1);
+var external__React__default = /*#__PURE__*/__webpack_require__.n(external__React_);
+
+// CONCATENATED MODULE: ./system-addon/content-src/components/ContextMenu/ContextMenu.jsx
+
+
+class ContextMenu_ContextMenu extends external__React__default.a.PureComponent {
   constructor(props) {
     super(props);
-    this.state = { showContextMenu: false, activeTile: null };
-    this.onLinkClick = this.onLinkClick.bind(this);
-    this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
-    this.onMenuUpdate = this.onMenuUpdate.bind(this);
-    this.onDismissButtonClick = this.onDismissButtonClick.bind(this);
-    this.onPinButtonClick = this.onPinButtonClick.bind(this);
-    this.onEditButtonClick = this.onEditButtonClick.bind(this);
-  }
-  toggleContextMenu(event, index) {
-    this.setState({
-      activeTile: index,
-      showContextMenu: true
-    });
+    this.hideContext = this.hideContext.bind(this);
   }
-  userEvent(event) {
-    this.props.dispatch(ac.UserEvent({
-      event,
-      source: TOP_SITES_SOURCE,
-      action_position: this.props.index
-    }));
+  hideContext() {
+    this.props.onUpdate(false);
   }
-  onLinkClick(ev) {
-    if (this.props.onEdit) {
-      // Ignore clicks if we are in the edit modal.
-      ev.preventDefault();
-      return;
-    }
-    this.userEvent("CLICK");
+  componentWillMount() {
+    this.hideContext();
   }
-  onMenuButtonClick(event) {
-    event.preventDefault();
-    this.toggleContextMenu(event, this.props.index);
-  }
-  onMenuUpdate(showContextMenu) {
-    this.setState({ showContextMenu });
-  }
-  onDismissButtonClick() {
-    const { link } = this.props;
-    if (link.isPinned) {
-      this.props.dispatch(ac.SendToMain({
-        type: at.TOP_SITES_UNPIN,
-        data: { site: { url: link.url } }
-      }));
+  componentDidUpdate(prevProps) {
+    if (this.props.visible && !prevProps.visible) {
+      setTimeout(() => {
+        window.addEventListener("click", this.hideContext);
+      }, 0);
     }
-    this.props.dispatch(ac.SendToMain({
-      type: at.BLOCK_URL,
-      data: link.url
-    }));
-    this.userEvent("BLOCK");
-  }
-  onPinButtonClick() {
-    const { link, index } = this.props;
-    if (link.isPinned) {
-      this.props.dispatch(ac.SendToMain({
-        type: at.TOP_SITES_UNPIN,
-        data: { site: { url: link.url } }
-      }));
-      this.userEvent("UNPIN");
-    } else {
-      this.props.dispatch(ac.SendToMain({
-        type: at.TOP_SITES_PIN,
-        data: { site: { url: link.url }, index }
-      }));
-      this.userEvent("PIN");
+    if (!this.props.visible && prevProps.visible) {
+      window.removeEventListener("click", this.hideContext);
     }
   }
-  onEditButtonClick() {
-    this.props.onEdit(this.props.index);
+  componentWillUnmount() {
+    window.removeEventListener("click", this.hideContext);
   }
   render() {
-    const { props } = this;
-    const { link } = props;
-    const isContextMenuOpen = this.state.showContextMenu && this.state.activeTile === props.index;
-    const title = link.label || link.hostname;
-    return React.createElement(
-      TopSiteLink,
-      _extends({}, props, { onClick: this.onLinkClick, className: isContextMenuOpen ? "active" : "", title: title }),
-      !props.onEdit && React.createElement(
-        "div",
-        null,
-        React.createElement(
-          "button",
-          { className: "context-menu-button icon", onClick: this.onMenuButtonClick },
-          React.createElement(
-            "span",
-            { className: "sr-only" },
-            `Open context menu for ${title}`
-          )
-        ),
-        React.createElement(LinkMenu, {
-          dispatch: props.dispatch,
-          index: props.index,
-          onUpdate: this.onMenuUpdate,
-          options: TOP_SITES_CONTEXT_MENU_OPTIONS,
-          site: link,
-          source: TOP_SITES_SOURCE,
-          visible: isContextMenuOpen })
-      ),
-      props.onEdit && React.createElement(
-        "div",
-        { className: "edit-menu" },
-        React.createElement("button", {
-          className: `icon icon-${link.isPinned ? "unpin" : "pin"}`,
-          title: this.props.intl.formatMessage({ id: `edit_topsites_${link.isPinned ? "unpin" : "pin"}_button` }),
-          onClick: this.onPinButtonClick }),
-        React.createElement("button", {
-          className: "icon icon-edit",
-          title: this.props.intl.formatMessage({ id: "edit_topsites_edit_button" }),
-          onClick: this.onEditButtonClick }),
-        React.createElement("button", {
-          className: "icon icon-dismiss",
-          title: this.props.intl.formatMessage({ id: "edit_topsites_dismiss_button" }),
-          onClick: this.onDismissButtonClick })
+    return external__React__default.a.createElement(
+      "span",
+      { hidden: !this.props.visible, className: "context-menu" },
+      external__React__default.a.createElement(
+        "ul",
+        { role: "menu", className: "context-menu-list" },
+        this.props.options.map((option, i) => option.type === "separator" ? external__React__default.a.createElement("li", { key: i, className: "separator" }) : external__React__default.a.createElement(ContextMenu_ContextMenuItem, { key: i, option: option, hideContext: this.hideContext }))
+      )
+    );
+  }
+}
+
+class ContextMenu_ContextMenuItem extends external__React__default.a.PureComponent {
+  constructor(props) {
+    super(props);
+    this.onClick = this.onClick.bind(this);
+    this.onKeyDown = this.onKeyDown.bind(this);
+  }
+  onClick() {
+    this.props.hideContext();
+    this.props.option.onClick();
+  }
+  onKeyDown(event) {
+    const { option } = this.props;
+    switch (event.key) {
+      case "Tab":
+        // tab goes down in context menu, shift + tab goes up in context menu
+        // if we're on the last item, one more tab will close the context menu
+        // similarly, if we're on the first item, one more shift + tab will close it
+        if (event.shiftKey && option.first || !event.shiftKey && option.last) {
+          this.props.hideContext();
+        }
+        break;
+      case "Enter":
+        this.props.hideContext();
+        option.onClick();
+        break;
+    }
+  }
+  render() {
+    const { option } = this.props;
+    return external__React__default.a.createElement(
+      "li",
+      { role: "menuitem", className: "context-menu-item" },
+      external__React__default.a.createElement(
+        "a",
+        { onClick: this.onClick, onKeyDown: this.onKeyDown, tabIndex: "0" },
+        option.icon && external__React__default.a.createElement("span", { className: `icon icon-spacer icon-${option.icon}` }),
+        option.label
       )
     );
   }
 }
-TopSite.defaultProps = { link: {} };
-
-const TopSitePlaceholder = () => React.createElement(TopSiteLink, { className: "placeholder" });
-
-const TopSiteList = props => {
-  const topSites = props.TopSites.rows.slice(0, props.TopSitesCount);
-  const topSitesUI = [];
-  for (let i = 0, l = props.TopSitesCount; i < l; i++) {
-    const link = topSites[i];
-    topSitesUI.push(!link ? React.createElement(TopSitePlaceholder, { key: i }) : React.createElement(TopSite, {
-      key: link.guid || link.url,
-      dispatch: props.dispatch,
-      link: link,
-      index: i,
-      intl: props.intl,
-      onEdit: props.onEdit }));
-  }
-  return React.createElement(
-    "ul",
-    { className: "top-sites-list" },
-    topSitesUI
-  );
+// EXTERNAL MODULE: external "ReactIntl"
+var external__ReactIntl_ = __webpack_require__(2);
+var external__ReactIntl__default = /*#__PURE__*/__webpack_require__.n(external__ReactIntl_);
+
+// CONCATENATED MODULE: ./system-addon/content-src/lib/link-menu-options.js
+
+
+/**
+ * List of functions that return items that can be included as menu options in a
+ * LinkMenu. All functions take the site as the first parameter, and optionally
+ * the index of the site.
+ */
+const LinkMenuOptions = {
+  Separator: () => ({ type: "separator" }),
+  RemoveBookmark: site => ({
+    id: "menu_action_remove_bookmark",
+    icon: "bookmark-added",
+    action: Actions["actionCreators"].SendToMain({
+      type: Actions["actionTypes"].DELETE_BOOKMARK_BY_ID,
+      data: site.bookmarkGuid
+    }),
+    userEvent: "BOOKMARK_DELETE"
+  }),
+  AddBookmark: site => ({
+    id: "menu_action_bookmark",
+    icon: "bookmark-hollow",
+    action: Actions["actionCreators"].SendToMain({
+      type: Actions["actionTypes"].BOOKMARK_URL,
+      data: { url: site.url, title: site.title, type: site.type }
+    }),
+    userEvent: "BOOKMARK_ADD"
+  }),
+  OpenInNewWindow: site => ({
+    id: "menu_action_open_new_window",
+    icon: "new-window",
+    action: Actions["actionCreators"].SendToMain({
+      type: Actions["actionTypes"].OPEN_NEW_WINDOW,
+      data: { url: site.url, referrer: site.referrer }
+    }),
+    userEvent: "OPEN_NEW_WINDOW"
+  }),
+  OpenInPrivateWindow: site => ({
+    id: "menu_action_open_private_window",
+    icon: "new-window-private",
+    action: Actions["actionCreators"].SendToMain({
+      type: Actions["actionTypes"].OPEN_PRIVATE_WINDOW,
+      data: { url: site.url, referrer: site.referrer }
+    }),
+    userEvent: "OPEN_PRIVATE_WINDOW"
+  }),
+  BlockUrl: (site, index, eventSource) => ({
+    id: "menu_action_dismiss",
+    icon: "dismiss",
+    action: Actions["actionCreators"].SendToMain({
+      type: Actions["actionTypes"].BLOCK_URL,
+      data: site.url
+    }),
+    impression: Actions["actionCreators"].ImpressionStats({
+      source: eventSource,
+      block: 0,
+      tiles: [{ id: site.guid, pos: index }]
+    }),
+    userEvent: "BLOCK"
+  }),
+  DeleteUrl: site => ({
+    id: "menu_action_delete",
+    icon: "delete",
+    action: {
+      type: Actions["actionTypes"].DIALOG_OPEN,
+      data: {
+        onConfirm: [Actions["actionCreators"].SendToMain({ type: Actions["actionTypes"].DELETE_HISTORY_URL, data: { url: site.url, forceBlock: site.bookmarkGuid } }), Actions["actionCreators"].UserEvent({ event: "DELETE" })],
+        body_string_id: ["confirm_history_delete_p1", "confirm_history_delete_notice_p2"],
+        confirm_button_string_id: "menu_action_delete",
+        cancel_button_string_id: "topsites_form_cancel_button",
+        icon: "modal-delete"
+      }
+    },
+    userEvent: "DIALOG_OPEN"
+  }),
+  PinTopSite: (site, index) => ({
+    id: "menu_action_pin",
+    icon: "pin",
+    action: Actions["actionCreators"].SendToMain({
+      type: Actions["actionTypes"].TOP_SITES_PIN,
+      data: { site: { url: site.url }, index }
+    }),
+    userEvent: "PIN"
+  }),
+  UnpinTopSite: site => ({
+    id: "menu_action_unpin",
+    icon: "unpin",
+    action: Actions["actionCreators"].SendToMain({
+      type: Actions["actionTypes"].TOP_SITES_UNPIN,
+      data: { site: { url: site.url } }
+    }),
+    userEvent: "UNPIN"
+  }),
+  SaveToPocket: (site, index, eventSource) => ({
+    id: "menu_action_save_to_pocket",
+    icon: "pocket",
+    action: Actions["actionCreators"].SendToMain({
+      type: Actions["actionTypes"].SAVE_TO_POCKET,
+      data: { site: { url: site.url, title: site.title } }
+    }),
+    impression: Actions["actionCreators"].ImpressionStats({
+      source: eventSource,
+      pocket: 0,
+      tiles: [{ id: site.guid, pos: index }]
+    }),
+    userEvent: "SAVE_TO_POCKET"
+  }),
+  EditTopSite: site => ({
+    id: "edit_topsites_button_text",
+    icon: "edit",
+    action: {
+      type: Actions["actionTypes"].TOP_SITES_EDIT,
+      data: { url: site.url, label: site.label }
+    }
+  }),
+  CheckBookmark: site => site.bookmarkGuid ? LinkMenuOptions.RemoveBookmark(site) : LinkMenuOptions.AddBookmark(site),
+  CheckPinTopSite: (site, index) => site.isPinned ? LinkMenuOptions.UnpinTopSite(site) : LinkMenuOptions.PinTopSite(site, index)
 };
-
-module.exports.TopSite = TopSite;
-module.exports.TopSiteLink = TopSiteLink;
-module.exports.TopSitePlaceholder = TopSitePlaceholder;
-module.exports.TopSiteList = TopSiteList;
-
-/***/ }),
-/* 8 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const React = __webpack_require__(1);
-const { injectIntl } = __webpack_require__(2);
-const ContextMenu = __webpack_require__(18);
-const { actionCreators: ac } = __webpack_require__(0);
-const linkMenuOptions = __webpack_require__(19);
+// CONCATENATED MODULE: ./system-addon/content-src/components/LinkMenu/LinkMenu.jsx
+
+
+
+
+
+
 const DEFAULT_SITE_MENU_OPTIONS = ["CheckPinTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"];
 
-class LinkMenu extends React.PureComponent {
+class LinkMenu__LinkMenu extends external__React__default.a.PureComponent {
   getOptions() {
     const props = this.props;
     const { site, index, source } = props;
 
     // Handle special case of default site
     const propOptions = !site.isDefault ? props.options : DEFAULT_SITE_MENU_OPTIONS;
 
-    const options = propOptions.map(o => linkMenuOptions[o](site, index, source)).map(option => {
+    const options = propOptions.map(o => LinkMenuOptions[o](site, index, source)).map(option => {
       const { action, impression, id, type, userEvent } = option;
       if (!type && id) {
         option.label = props.intl.formatMessage(option);
         option.onClick = () => {
           props.dispatch(action);
           if (userEvent) {
-            props.dispatch(ac.UserEvent({
+            props.dispatch(Actions["actionCreators"].UserEvent({
               event: userEvent,
               source,
               action_position: index
             }));
           }
           if (impression && props.shouldSendImpressionStats) {
             props.dispatch(impression);
           }
@@ -963,51 +936,61 @@ class LinkMenu extends React.PureCompone
     // This is for accessibility to support making each item tabbable.
     // We want to know which item is the first and which item
     // is the last, so we can close the context menu accordingly.
     options[0].first = true;
     options[options.length - 1].last = true;
     return options;
   }
   render() {
-    return React.createElement(ContextMenu, {
+    return external__React__default.a.createElement(ContextMenu_ContextMenu, {
       visible: this.props.visible,
       onUpdate: this.props.onUpdate,
       options: this.getOptions() });
   }
 }
-
-module.exports = injectIntl(LinkMenu);
-module.exports._unconnected = LinkMenu;
+/* unused harmony export _LinkMenu */
+
+
+const LinkMenu = Object(external__ReactIntl_["injectIntl"])(LinkMenu__LinkMenu);
+/* harmony export (immutable) */ __webpack_exports__["a"] = LinkMenu;
+
 
 /***/ }),
-/* 9 */
-/***/ (function(module, exports, __webpack_require__) {
-
-/* WEBPACK VAR INJECTION */(function(global) {var _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; };
-
-const React = __webpack_require__(1);
-const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
-const { injectIntl, FormattedMessage } = __webpack_require__(2);
+/* 7 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+/* WEBPACK VAR INJECTION */(function(global) {/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__ = __webpack_require__(0);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_react_intl__ = __webpack_require__(2);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_react_intl___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_react_intl__);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_react__ = __webpack_require__(1);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_react___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_react__);
+var _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; };
+
+
+
+
 
 const VISIBLE = "visible";
 const VISIBILITY_CHANGE_EVENT = "visibilitychange";
 
 function getFormattedMessage(message) {
-  return typeof message === "string" ? React.createElement(
+  return typeof message === "string" ? __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
     "span",
     null,
     message
-  ) : React.createElement(FormattedMessage, message);
+  ) : __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_1_react_intl__["FormattedMessage"], message);
 }
 function getCollapsed(props) {
   return props.Prefs.values[props.prefName];
 }
 
-class Info extends React.PureComponent {
+class Info extends __WEBPACK_IMPORTED_MODULE_2_react___default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onInfoEnter = this.onInfoEnter.bind(this);
     this.onInfoLeave = this.onInfoLeave.bind(this);
     this.onManageClick = this.onManageClick.bind(this);
     this.state = { infoActive: false };
   }
 
@@ -1026,111 +1009,119 @@ class Info extends React.PureComponent {
   }
   onInfoLeave(event) {
     // We currently have an active (true) info state, so keep it true only if we
     // have a related event target that is contained "within" the current target
     // (section-info-option) as itself or a descendant. Set to false otherwise.
     this._setInfoState(event && event.relatedTarget && (event.relatedTarget === event.currentTarget || event.relatedTarget.compareDocumentPosition(event.currentTarget) & Node.DOCUMENT_POSITION_CONTAINS));
   }
   onManageClick() {
-    this.props.dispatch({ type: at.SETTINGS_OPEN });
-    this.props.dispatch(ac.UserEvent({ event: "OPEN_NEWTAB_PREFS" }));
+    this.props.dispatch({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SETTINGS_OPEN });
+    this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].UserEvent({ event: "OPEN_NEWTAB_PREFS" }));
   }
   render() {
     const { infoOption, intl } = this.props;
     const infoOptionIconA11yAttrs = {
       "aria-haspopup": "true",
       "aria-controls": "info-option",
       "aria-expanded": this.state.infoActive ? "true" : "false",
       "role": "note",
       "tabIndex": 0
     };
     const sectionInfoTitle = intl.formatMessage({ id: "section_info_option" });
 
-    return React.createElement(
+    return __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
       "span",
       { className: "section-info-option",
         onBlur: this.onInfoLeave,
         onFocus: this.onInfoEnter,
         onMouseOut: this.onInfoLeave,
         onMouseOver: this.onInfoEnter },
-      React.createElement("img", _extends({ className: "info-option-icon", title: sectionInfoTitle
+      __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement("img", _extends({ className: "info-option-icon", title: sectionInfoTitle
       }, infoOptionIconA11yAttrs)),
-      React.createElement(
+      __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
         "div",
         { className: "info-option" },
-        infoOption.header && React.createElement(
+        infoOption.header && __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
           "div",
           { className: "info-option-header", role: "heading" },
           getFormattedMessage(infoOption.header)
         ),
-        React.createElement(
+        __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
           "p",
           { className: "info-option-body" },
           infoOption.body && getFormattedMessage(infoOption.body),
-          infoOption.link && React.createElement(
+          infoOption.link && __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
             "a",
             { href: infoOption.link.href, target: "_blank", rel: "noopener noreferrer", className: "info-option-link" },
             getFormattedMessage(infoOption.link.title || infoOption.link)
           )
         ),
-        React.createElement(
+        __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
           "div",
           { className: "info-option-manage" },
-          React.createElement(
+          __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
             "button",
             { onClick: this.onManageClick },
-            React.createElement(FormattedMessage, { id: "settings_pane_header" })
+            __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_1_react_intl__["FormattedMessage"], { id: "settings_pane_header" })
           )
         )
       )
     );
   }
 }
-
-const InfoIntl = injectIntl(Info);
-
-class Disclaimer extends React.PureComponent {
+/* unused harmony export Info */
+
+
+const InfoIntl = Object(__WEBPACK_IMPORTED_MODULE_1_react_intl__["injectIntl"])(Info);
+/* unused harmony export InfoIntl */
+
+
+class Disclaimer extends __WEBPACK_IMPORTED_MODULE_2_react___default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onAcknowledge = this.onAcknowledge.bind(this);
   }
 
   onAcknowledge() {
-    this.props.dispatch(ac.SetPref(this.props.disclaimerPref, false));
-    this.props.dispatch(ac.UserEvent({ event: "SECTION_DISCLAIMER_ACKNOWLEDGED", source: this.props.eventSource }));
+    this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SetPref(this.props.disclaimerPref, false));
+    this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].UserEvent({ event: "SECTION_DISCLAIMER_ACKNOWLEDGED", source: this.props.eventSource }));
   }
 
   render() {
     const disclaimer = this.props.disclaimer;
-    return React.createElement(
+    return __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
       "div",
       { className: "section-disclaimer" },
-      React.createElement(
+      __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
         "div",
         { className: "section-disclaimer-text" },
         getFormattedMessage(disclaimer.text),
-        disclaimer.link && React.createElement(
+        disclaimer.link && __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
           "a",
           { href: disclaimer.link.href, target: "_blank", rel: "noopener noreferrer" },
           getFormattedMessage(disclaimer.link.title || disclaimer.link)
         )
       ),
-      React.createElement(
+      __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
         "button",
         { onClick: this.onAcknowledge },
         getFormattedMessage(disclaimer.button)
       )
     );
   }
 }
-
-const DisclaimerIntl = injectIntl(Disclaimer);
-
-class CollapsibleSection extends React.PureComponent {
+/* unused harmony export Disclaimer */
+
+
+const DisclaimerIntl = Object(__WEBPACK_IMPORTED_MODULE_1_react_intl__["injectIntl"])(Disclaimer);
+/* unused harmony export DisclaimerIntl */
+
+
+class _CollapsibleSection extends __WEBPACK_IMPORTED_MODULE_2_react___default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onBodyMount = this.onBodyMount.bind(this);
     this.onInfoEnter = this.onInfoEnter.bind(this);
     this.onInfoLeave = this.onInfoLeave.bind(this);
     this.onHeaderClick = this.onHeaderClick.bind(this);
     this.onTransitionEnd = this.onTransitionEnd.bind(this);
     this.enableOrDisableAnimation = this.enableOrDisableAnimation.bind(this);
@@ -1181,105 +1172,111 @@ class CollapsibleSection extends React.P
     this._setInfoState(event && event.relatedTarget && (event.relatedTarget === event.currentTarget || event.relatedTarget.compareDocumentPosition(event.currentTarget) & Node.DOCUMENT_POSITION_CONTAINS));
   }
   onHeaderClick() {
     // Get the current height of the body so max-height transitions can work
     this.setState({
       isAnimating: true,
       maxHeight: `${this.sectionBody.scrollHeight}px`
     });
-    this.props.dispatch(ac.SetPref(this.props.prefName, !getCollapsed(this.props)));
+    this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SetPref(this.props.prefName, !getCollapsed(this.props)));
   }
   onTransitionEnd(event) {
     // Only update the animating state for our own transition (not a child's)
     if (event.target === event.currentTarget) {
       this.setState({ isAnimating: false });
     }
   }
   renderIcon() {
     const icon = this.props.icon;
     if (icon && icon.startsWith("moz-extension://")) {
-      return React.createElement("span", { className: "icon icon-small-spacer", style: { backgroundImage: `url('${icon}')` } });
+      return __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement("span", { className: "icon icon-small-spacer", style: { backgroundImage: `url('${icon}')` } });
     }
-    return React.createElement("span", { className: `icon icon-small-spacer icon-${icon || "webextension"}` });
+    return __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement("span", { className: `icon icon-small-spacer icon-${icon || "webextension"}` });
   }
   render() {
     const isCollapsed = getCollapsed(this.props);
     const { enableAnimation, isAnimating, maxHeight } = this.state;
     const { id, infoOption, eventSource, disclaimer } = this.props;
     const disclaimerPref = `section.${id}.showDisclaimer`;
     const needsDisclaimer = disclaimer && this.props.Prefs.values[disclaimerPref];
 
-    return React.createElement(
+    return __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
       "section",
       { className: `collapsible-section ${this.props.className}${enableAnimation ? " animation-enabled" : ""}${isCollapsed ? " collapsed" : ""}` },
-      React.createElement(
+      __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
         "div",
         { className: "section-top-bar" },
-        React.createElement(
+        __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
           "h3",
           { className: "section-title" },
-          React.createElement(
+          __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
             "span",
             { className: "click-target", onClick: this.onHeaderClick },
             this.renderIcon(),
             this.props.title,
-            React.createElement("span", { className: `icon ${isCollapsed ? "icon-arrowhead-forward" : "icon-arrowhead-down"}` })
+            __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement("span", { className: `icon ${isCollapsed ? "icon-arrowhead-forward" : "icon-arrowhead-down"}` })
           )
         ),
-        infoOption && React.createElement(InfoIntl, { infoOption: infoOption, dispatch: this.props.dispatch })
+        infoOption && __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(InfoIntl, { infoOption: infoOption, dispatch: this.props.dispatch })
       ),
-      React.createElement(
+      __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
         "div",
         {
           className: `section-body${isAnimating ? " animating" : ""}`,
           onTransitionEnd: this.onTransitionEnd,
           ref: this.onBodyMount,
           style: isAnimating && !isCollapsed ? { maxHeight } : null },
-        needsDisclaimer && React.createElement(DisclaimerIntl, { disclaimerPref: disclaimerPref, disclaimer: disclaimer, eventSource: eventSource, dispatch: this.props.dispatch }),
+        needsDisclaimer && __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(DisclaimerIntl, { disclaimerPref: disclaimerPref, disclaimer: disclaimer, eventSource: eventSource, dispatch: this.props.dispatch }),
         this.props.children
       )
     );
   }
 }
-
-CollapsibleSection.defaultProps = {
+/* unused harmony export _CollapsibleSection */
+
+
+_CollapsibleSection.defaultProps = {
   document: global.document || {
     addEventListener: () => {},
     removeEventListener: () => {},
     visibilityState: "hidden"
   },
   Prefs: { values: {} }
 };
 
-module.exports = injectIntl(CollapsibleSection);
-module.exports._unconnected = CollapsibleSection;
-module.exports.Info = Info;
-module.exports.InfoIntl = InfoIntl;
-module.exports.Disclaimer = Disclaimer;
-module.exports.DisclaimerIntl = DisclaimerIntl;
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
+const CollapsibleSection = Object(__WEBPACK_IMPORTED_MODULE_1_react_intl__["injectIntl"])(_CollapsibleSection);
+/* harmony export (immutable) */ __webpack_exports__["a"] = CollapsibleSection;
+
+/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(4)))
 
 /***/ }),
-/* 10 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const React = __webpack_require__(1);
-const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
-const { perfService: perfSvc } = __webpack_require__(11);
+/* 8 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__ = __webpack_require__(0);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm__ = __webpack_require__(9);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm__);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_react__ = __webpack_require__(1);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_react___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_react__);
+
+
+
 
 // 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.Component {
+class ComponentPerfTimer extends __WEBPACK_IMPORTED_MODULE_2_react___default.a.Component {
   constructor(props) {
     super(props);
     // Just for test dependency injection:
-    this.perfSvc = this.props.perfSvc || perfSvc;
+    this.perfSvc = this.props.perfSvc || __WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm__["perfService"];
 
     this._sendBadStateEvent = this._sendBadStateEvent.bind(this);
     this._sendPaintedEvent = this._sendPaintedEvent.bind(this);
     this._reportMissingData = false;
     this._timestampHandled = false;
     this._recordedFirstRender = false;
   }
 
@@ -1375,18 +1372,18 @@ class ComponentPerfTimer extends React.C
     // highlights_data_ready_ts, topsites_data_ready_ts.
     const dataReadyKey = `${this.props.id}_data_ready_ts`;
     this.perfSvc.mark(dataReadyKey);
 
     try {
       const firstRenderKey = `${this.props.id}_first_render_ts`;
       // value has to be Int32.
       const value = parseInt(this.perfSvc.getMostRecentAbsMarkStartByName(dataReadyKey) - this.perfSvc.getMostRecentAbsMarkStartByName(firstRenderKey), 10);
-      this.props.dispatch(ac.SendToMain({
-        type: at.SAVE_SESSION_PERF_DATA,
+      this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({
+        type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SAVE_SESSION_PERF_DATA,
         // highlights_data_late_by_ms, topsites_data_late_by_ms.
         data: { [`${this.props.id}_data_late_by_ms`]: value }
       }));
     } catch (ex) {
       // If this failed, it's likely because the `privacy.resistFingerprinting`
       // pref is true.
     }
   }
@@ -1400,18 +1397,18 @@ class ComponentPerfTimer extends React.C
     // topsites_first_painted_ts.
     const key = `${this.props.id}_first_painted_ts`;
     this.perfSvc.mark(key);
 
     try {
       const data = {};
       data[key] = this.perfSvc.getMostRecentAbsMarkStartByName(key);
 
-      this.props.dispatch(ac.SendToMain({
-        type: at.SAVE_SESSION_PERF_DATA,
+      this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({
+        type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SAVE_SESSION_PERF_DATA,
         data
       }));
     } catch (ex) {
       // If this failed, it's likely because the `privacy.resistFingerprinting`
       // pref is true.  We should at least not blow up, and should continue
       // to set this._timestampHandled to avoid going through this again.
     }
   }
@@ -1419,21 +1416,21 @@ class ComponentPerfTimer extends React.C
   render() {
     if (RECORDED_SECTIONS.includes(this.props.id)) {
       this._ensureFirstRenderTsRecorded();
       this._maybeSendBadStateEvent();
     }
     return this.props.children;
   }
 }
-
-module.exports = ComponentPerfTimer;
+/* harmony export (immutable) */ __webpack_exports__["a"] = ComponentPerfTimer;
+
 
 /***/ }),
-/* 11 */
+/* 9 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 /* globals Services */
 
 
 /* istanbul ignore if */
 // Note: normally we would just feature detect Components.utils here, but
@@ -1447,16 +1444,17 @@ let usablePerfObj;
 
 /* istanbul ignore if */
 /* istanbul ignore else */
 if (typeof Services !== "undefined") {
   // Borrow the high-resolution timer from the hidden window....
   usablePerfObj = Services.appShell.hiddenDOMWindow.performance;
 } else if (typeof performance !== "undefined") {
   // we must be running in content space
+  // eslint-disable-next-line no-undef
   usablePerfObj = performance;
 } else {
   // This is a dummy object so this file doesn't crash in the node prerendering
   // task.
   usablePerfObj = {
     now() {},
     mark() {}
   };
@@ -1559,435 +1557,1173 @@ var _PerfService = function _PerfService
 
 var perfService = new _PerfService();
 module.exports = {
   _PerfService,
   perfService
 };
 
 /***/ }),
-/* 12 */
-/***/ (function(module, exports, __webpack_require__) {
-
-/* WEBPACK VAR INJECTION */(function(global) {const React = __webpack_require__(1);
-const ReactDOM = __webpack_require__(13);
-const Base = __webpack_require__(14);
-const { Provider } = __webpack_require__(3);
-const initStore = __webpack_require__(31);
-const { reducers } = __webpack_require__(6);
-const DetectUserSessionStart = __webpack_require__(33);
-const { addSnippetsSubscriber } = __webpack_require__(34);
-const { actionTypes: at, actionCreators: ac } = __webpack_require__(0);
-
-const store = initStore(reducers, global.gActivityStreamPrerenderedState);
-
-new DetectUserSessionStart(store).sendEventOrAddListener();
+/* 10 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
+/* WEBPACK VAR INJECTION */(function(global) {/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__ = __webpack_require__(0);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_content_src_lib_snippets__ = __webpack_require__(11);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_content_src_components_Base_Base__ = __webpack_require__(12);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3_content_src_lib_detect_user_session_start__ = __webpack_require__(19);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4_content_src_lib_init_store__ = __webpack_require__(20);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5_react_redux__ = __webpack_require__(3);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5_react_redux___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_5_react_redux__);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_react__ = __webpack_require__(1);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_react___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6_react__);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7_react_dom__ = __webpack_require__(22);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7_react_dom___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_7_react_dom__);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8_common_Reducers_jsm__ = __webpack_require__(5);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8_common_Reducers_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_8_common_Reducers_jsm__);
+
+
+
+
+
+
+
+
+
+
+const store = Object(__WEBPACK_IMPORTED_MODULE_4_content_src_lib_init_store__["a" /* initStore */])(__WEBPACK_IMPORTED_MODULE_8_common_Reducers_jsm__["reducers"], global.gActivityStreamPrerenderedState);
+
+new __WEBPACK_IMPORTED_MODULE_3_content_src_lib_detect_user_session_start__["a" /* DetectUserSessionStart */](store).sendEventOrAddListener();
 
 // If we are starting in a prerendered state, we must wait until the first render
 // to request state rehydration (see Base.jsx). If we are NOT in a prerendered state,
 // we can request it immedately.
 if (!global.gActivityStreamPrerenderedState) {
-  store.dispatch(ac.SendToMain({ type: at.NEW_TAB_STATE_REQUEST }));
+  store.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].NEW_TAB_STATE_REQUEST }));
 }
 
-ReactDOM.render(React.createElement(
-  Provider,
+__WEBPACK_IMPORTED_MODULE_7_react_dom___default.a.render(__WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(
+  __WEBPACK_IMPORTED_MODULE_5_react_redux__["Provider"],
   { store: store },
-  React.createElement(Base, {
+  __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_2_content_src_components_Base_Base__["a" /* Base */], {
     isPrerendered: !!global.gActivityStreamPrerenderedState,
     locale: global.document.documentElement.lang,
     strings: global.gActivityStreamStrings })
 ), document.getElementById("root"));
 
-addSnippetsSubscriber(store);
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
-
-/***/ }),
-/* 13 */
-/***/ (function(module, exports) {
-
-module.exports = ReactDOM;
+Object(__WEBPACK_IMPORTED_MODULE_1_content_src_lib_snippets__["a" /* addSnippetsSubscriber */])(store);
+/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(4)))
 
 /***/ }),
-/* 14 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const React = __webpack_require__(1);
-const { connect } = __webpack_require__(3);
-const { addLocaleData, IntlProvider } = __webpack_require__(2);
-const TopSites = __webpack_require__(15);
-const Search = __webpack_require__(21);
-const ConfirmDialog = __webpack_require__(23);
-const ManualMigration = __webpack_require__(24);
-const PreferencesPane = __webpack_require__(25);
-const Sections = __webpack_require__(26);
-const { actionTypes: at, actionCreators: ac } = __webpack_require__(0);
-const { PrerenderData } = __webpack_require__(30);
-
-// Add the locale data for pluralization and relative-time formatting for now,
-// this just uses english locale data. We can make this more sophisticated if
-// more features are needed.
-function addLocaleDataForReactIntl(locale) {
-  addLocaleData([{ locale, parentLocale: "en" }]);
-}
-
-class Base extends React.PureComponent {
-  componentWillMount() {
-    const { App, locale } = this.props;
-    this.sendNewTabRehydrated(App);
-    addLocaleDataForReactIntl(locale);
+/* 11 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+/* WEBPACK VAR INJECTION */(function(global) {/* harmony export (immutable) */ __webpack_exports__["a"] = addSnippetsSubscriber;
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__ = __webpack_require__(0);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__);
+const DATABASE_NAME = "snippets_db";
+const DATABASE_VERSION = 1;
+const SNIPPETS_OBJECTSTORE_NAME = "snippets";
+const SNIPPETS_UPDATE_INTERVAL_MS = 14400000;
+/* unused harmony export SNIPPETS_UPDATE_INTERVAL_MS */
+ // 4 hours.
+
+const SNIPPETS_ENABLED_EVENT = "Snippets:Enabled";
+const SNIPPETS_DISABLED_EVENT = "Snippets:Disabled";
+
+
+
+/**
+ * SnippetsMap - A utility for cacheing values related to the snippet. It has
+ *               the same interface as a Map, but is optionally backed by
+ *               indexedDB for persistent storage.
+ *               Call .connect() to open a database connection and restore any
+ *               previously cached data, if necessary.
+ *
+ */
+class SnippetsMap extends Map {
+  constructor(dispatch) {
+    super();
+    this._db = null;
+    this._dispatch = dispatch;
   }
 
-  componentDidMount() {
-    // Request state AFTER the first render to ensure we don't cause the
-    // prerendered DOM to be unmounted. Otherwise, NEW_TAB_STATE_REQUEST is
-    // dispatched right after the store is ready.
-    if (this.props.isPrerendered) {
-      this.props.dispatch(ac.SendToMain({ type: at.NEW_TAB_STATE_REQUEST }));
-      this.props.dispatch(ac.SendToMain({ type: at.PAGE_PRERENDERED }));
-    }
+  set(key, value) {
+    super.set(key, value);
+    return this._dbTransaction(db => db.put(value, key));
+  }
+
+  delete(key) {
+    super.delete(key);
+    return this._dbTransaction(db => db.delete(key));
+  }
+
+  clear() {
+    super.clear();
+    return this._dbTransaction(db => db.clear());
+  }
+
+  get blockList() {
+    return this.get("blockList") || [];
   }
 
-  componentWillUpdate({ App }) {
-    this.sendNewTabRehydrated(App);
-  }
-
-  // The NEW_TAB_REHYDRATED event is used to inform feeds that their
-  // data has been consumed e.g. for counting the number of tabs that
-  // have rendered that data.
-  sendNewTabRehydrated(App) {
-    if (App && App.initialized && !this.renderNotified) {
-      this.props.dispatch(ac.SendToMain({ type: at.NEW_TAB_REHYDRATED, data: {} }));
-      this.renderNotified = true;
+  /**
+   * blockSnippetById - Blocks a snippet given an id
+   *
+   * @param  {str|int} id   The id of the snippet
+   * @return {Promise}      Resolves when the id has been written to indexedDB,
+   *                        or immediately if the snippetMap is not connected
+   */
+  async blockSnippetById(id) {
+    if (!id) {
+      return;
+    }
+    let blockList = this.blockList;
+    if (!blockList.includes(id)) {
+      blockList.push(id);
+      this._dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SNIPPETS_BLOCKLIST_UPDATED, data: blockList }));
+      await this.set("blockList", blockList);
     }
   }
 
-  render() {
-    const props = this.props;
-    const { App, locale, strings } = props;
-    const { initialized } = App;
-    const prefs = props.Prefs.values;
-
-    const shouldBeFixedToTop = PrerenderData.arePrefsValid(name => prefs[name]);
-
-    const outerClassName = `outer-wrapper${shouldBeFixedToTop ? " fixed-to-top" : ""}`;
-
-    if (!props.isPrerendered && !initialized) {
-      return null;
-    }
-
-    return React.createElement(
-      IntlProvider,
-      { locale: locale, messages: strings },
-      React.createElement(
-        "div",
-        { className: outerClassName },
-        React.createElement(
-          "main",
-          null,
-          prefs.showSearch && React.createElement(Search, null),
-          React.createElement(
-            "div",
-            { className: `body-wrapper${initialized ? " on" : ""}` },
-            !prefs.migrationExpired && React.createElement(ManualMigration, null),
-            prefs.showTopSites && React.createElement(TopSites, null),
-            React.createElement(Sections, null)
-          ),
-          React.createElement(ConfirmDialog, null)
-        ),
-        initialized && React.createElement(PreferencesPane, null)
-      )
-    );
+  disableOnboarding() {
+    this._dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].DISABLE_ONBOARDING }));
+  }
+
+  showFirefoxAccounts() {
+    this._dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SHOW_FIREFOX_ACCOUNTS }));
   }
-}
-
-module.exports = connect(state => ({ App: state.App, Prefs: state.Prefs }))(Base);
-module.exports._unconnected = Base;
-
-/***/ }),
-/* 15 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const React = __webpack_require__(1);
-const { connect } = __webpack_require__(3);
-const { FormattedMessage } = __webpack_require__(2);
-
-const TopSitesEdit = __webpack_require__(16);
-const { TopSiteList } = __webpack_require__(7);
-const CollapsibleSection = __webpack_require__(9);
-const ComponentPerfTimer = __webpack_require__(10);
-const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
-const { MIN_RICH_FAVICON_SIZE, MIN_CORNER_FAVICON_SIZE } = __webpack_require__(5);
-
-/**
- * Iterates through TopSites and counts types of images.
- * @param acc Accumulator for reducer.
- * @param topsite Entry in TopSites.
- */
-function countTopSitesIconsTypes(topSites) {
-  const countTopSitesTypes = (acc, link) => {
-    if (link.tippyTopIcon || link.faviconRef === "tippytop") {
-      acc.tippytop++;
-    } else if (link.faviconSize >= MIN_RICH_FAVICON_SIZE) {
-      acc.rich_icon++;
-    } else if (link.screenshot && link.faviconSize >= MIN_CORNER_FAVICON_SIZE) {
-      acc.screenshot_with_icon++;
-    } else if (link.screenshot) {
-      acc.screenshot++;
-    } else {
-      acc.no_image++;
-    }
-
-    return acc;
-  };
-
-  return topSites.reduce(countTopSitesTypes, {
-    "screenshot_with_icon": 0,
-    "screenshot": 0,
-    "tippytop": 0,
-    "rich_icon": 0,
-    "no_image": 0
-  });
-}
-
-class TopSites extends React.PureComponent {
+
   /**
-   * Dispatch session statistics about the quality of TopSites icons.
+   * connect - Attaches an indexedDB back-end to the Map so that any set values
+   *           are also cached in a store. It also restores any existing values
+   *           that are already stored in the indexedDB store.
+   *
+   * @return {type}  description
    */
-  _dispatchTopSitesIconStats() {
-    const topSitesIconsStats = countTopSitesIconsTypes(this._getTopSites());
-    // Dispatch telemetry event with the count of TopSites images types.
-    this.props.dispatch(ac.SendToMain({
-      type: at.SAVE_SESSION_PERF_DATA,
-      data: { topsites_icon_stats: topSitesIconsStats }
-    }));
+  async connect() {
+    // Open the connection
+    const db = await this._openDB();
+
+    // Restore any existing values
+    await this._restoreFromDb(db);
+
+    // Attach a reference to the db
+    this._db = db;
   }
 
   /**
-   * Return the TopSites to display based on prefs.
+   * _dbTransaction - Returns a db transaction wrapped with the given modifier
+   *                  function as a Promise. If the db has not been connected,
+   *                  it resolves immediately.
+   *
+   * @param  {func} modifier A function to call with the transaction
+   * @return {obj}           A Promise that resolves when the transaction has
+   *                         completed or errored
    */
-  _getTopSites() {
-    return this.props.TopSites.rows.slice(0, this.props.TopSitesCount);
+  _dbTransaction(modifier) {
+    if (!this._db) {
+      return Promise.resolve();
+    }
+    return new Promise((resolve, reject) => {
+      const transaction = modifier(this._db.transaction(SNIPPETS_OBJECTSTORE_NAME, "readwrite").objectStore(SNIPPETS_OBJECTSTORE_NAME));
+      transaction.onsuccess = event => resolve();
+
+      /* istanbul ignore next */
+      transaction.onerror = event => reject(transaction.error);
+    });
+  }
+
+  _openDB() {
+    return new Promise((resolve, reject) => {
+      const openRequest = indexedDB.open(DATABASE_NAME, DATABASE_VERSION);
+
+      /* istanbul ignore next */
+      openRequest.onerror = event => {
+        // Try to delete the old database so that we can start this process over
+        // next time.
+        indexedDB.deleteDatabase(DATABASE_NAME);
+        reject(event);
+      };
+
+      openRequest.onupgradeneeded = event => {
+        const db = event.target.result;
+        if (!db.objectStoreNames.contains(SNIPPETS_OBJECTSTORE_NAME)) {
+          db.createObjectStore(SNIPPETS_OBJECTSTORE_NAME);
+        }
+      };
+
+      openRequest.onsuccess = event => {
+        let db = event.target.result;
+
+        /* istanbul ignore next */
+        db.onerror = err => console.error(err); // eslint-disable-line no-console
+        /* istanbul ignore next */
+        db.onversionchange = versionChangeEvent => versionChangeEvent.target.close();
+
+        resolve(db);
+      };
+    });
+  }
+
+  _restoreFromDb(db) {
+    return new Promise((resolve, reject) => {
+      let cursorRequest;
+      try {
+        cursorRequest = db.transaction(SNIPPETS_OBJECTSTORE_NAME).objectStore(SNIPPETS_OBJECTSTORE_NAME).openCursor();
+      } catch (err) {
+        // istanbul ignore next
+        reject(err);
+        // istanbul ignore next
+        return;
+      }
+
+      /* istanbul ignore next */
+      cursorRequest.onerror = event => reject(event);
+
+      cursorRequest.onsuccess = event => {
+        let cursor = event.target.result;
+        // Populate the cache from the persistent storage.
+        if (cursor) {
+          this.set(cursor.key, cursor.value);
+          cursor.continue();
+        } else {
+          // We are done.
+          resolve();
+        }
+      };
+    });
+  }
+}
+/* unused harmony export SnippetsMap */
+
+
+/**
+ * SnippetsProvider - Initializes a SnippetsMap and loads snippets from a
+ *                    remote location, or else default snippets if the remote
+ *                    snippets cannot be retrieved.
+ */
+class SnippetsProvider {
+  constructor(dispatch) {
+    // Initialize the Snippets Map and attaches it to a global so that
+    // the snippet payload can interact with it.
+    global.gSnippetsMap = new SnippetsMap(dispatch);
+    this._onAction = this._onAction.bind(this);
+  }
+
+  get snippetsMap() {
+    return global.gSnippetsMap;
+  }
+
+  async _refreshSnippets() {
+    // Check if the cached version of of the snippets in snippetsMap. If it's too
+    // old, blow away the entire snippetsMap.
+    const cachedVersion = this.snippetsMap.get("snippets-cached-version");
+
+    if (cachedVersion !== this.appData.version) {
+      this.snippetsMap.clear();
+    }
+
+    // Has enough time passed for us to require an update?
+    const lastUpdate = this.snippetsMap.get("snippets-last-update");
+    const needsUpdate = !(lastUpdate >= 0) || Date.now() - lastUpdate > SNIPPETS_UPDATE_INTERVAL_MS;
+
+    if (needsUpdate && this.appData.snippetsURL) {
+      this.snippetsMap.set("snippets-last-update", Date.now());
+      try {
+        const response = await fetch(this.appData.snippetsURL);
+        if (response.status === 200) {
+          const payload = await response.text();
+
+          this.snippetsMap.set("snippets", payload);
+          this.snippetsMap.set("snippets-cached-version", this.appData.version);
+        }
+      } catch (e) {
+        console.error(e); // eslint-disable-line no-console
+      }
+    }
+  }
+
+  _noSnippetFallback() {
+    // TODO
+  }
+
+  _forceOnboardingVisibility(shouldBeVisible) {
+    const onboardingEl = document.getElementById("onboarding-notification-bar");
+
+    if (onboardingEl) {
+      onboardingEl.style.display = shouldBeVisible ? "" : "none";
+    }
+  }
+
+  _showRemoteSnippets() {
+    const snippetsEl = document.getElementById(this.elementId);
+    const payload = this.snippetsMap.get("snippets");
+
+    if (!snippetsEl) {
+      throw new Error(`No element was found with id '${this.elementId}'.`);
+    }
+
+    // This could happen if fetching failed
+    if (!payload) {
+      throw new Error("No remote snippets were found in gSnippetsMap.");
+    }
+
+    if (typeof payload !== "string") {
+      throw new Error("Snippet payload was incorrectly formatted");
+    }
+
+    // Note that injecting snippets can throw if they're invalid XML.
+    // eslint-disable-next-line no-unsanitized/property
+    snippetsEl.innerHTML = payload;
+
+    // Scripts injected by innerHTML are inactive, so we have to relocate them
+    // through DOM manipulation to activate their contents.
+    for (const scriptEl of snippetsEl.getElementsByTagName("script")) {
+      const relocatedScript = document.createElement("script");
+      relocatedScript.text = scriptEl.text;
+      scriptEl.parentNode.replaceChild(relocatedScript, scriptEl);
+    }
+  }
+
+  _onAction(msg) {
+    if (msg.data.type === __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SNIPPET_BLOCKED) {
+      this.snippetsMap.set("blockList", msg.data.data);
+      document.getElementById("snippets-container").style.display = "none";
+    }
   }
 
-  componentDidUpdate() {
-    this._dispatchTopSitesIconStats();
+  /**
+   * init - Fetch the snippet payload and show snippets
+   *
+   * @param  {obj} options
+   * @param  {str} options.appData.snippetsURL  The URL from which we fetch snippets
+   * @param  {int} options.appData.version  The current snippets version
+   * @param  {str} options.elementId  The id of the element in which to inject snippets
+   * @param  {bool} options.connect  Should gSnippetsMap connect to indexedDB?
+   */
+  async init(options) {
+    Object.assign(this, {
+      appData: {},
+      elementId: "snippets",
+      connect: true
+    }, options);
+
+    // Add listener so we know when snippets are blocked on other pages
+    if (global.addMessageListener) {
+      global.addMessageListener("ActivityStream:MainToContent", this._onAction);
+    }
+
+    // TODO: Requires enabling indexedDB on newtab
+    // Restore the snippets map from indexedDB
+    if (this.connect) {
+      try {
+        await this.snippetsMap.connect();
+      } catch (e) {
+        console.error(e); // eslint-disable-line no-console
+      }
+    }
+
+    // Cache app data values so they can be accessible from gSnippetsMap
+    for (const key of Object.keys(this.appData)) {
+      this.snippetsMap.set(`appData.${key}`, this.appData[key]);
+    }
+
+    // Refresh snippets, if enough time has passed.
+    await this._refreshSnippets();
+
+    // Try showing remote snippets, falling back to defaults if necessary.
+    try {
+      this._showRemoteSnippets();
+    } catch (e) {
+      this._noSnippetFallback(e);
+    }
+
+    window.dispatchEvent(new Event(SNIPPETS_ENABLED_EVENT));
+
+    this._forceOnboardingVisibility(true);
+    this.initialized = true;
+  }
+
+  uninit() {
+    window.dispatchEvent(new Event(SNIPPETS_DISABLED_EVENT));
+    this._forceOnboardingVisibility(false);
+    if (global.removeMessageListener) {
+      global.removeMessageListener("ActivityStream:MainToContent", this._onAction);
+    }
+    this.initialized = false;
   }
-
-  componentDidMount() {
-    this._dispatchTopSitesIconStats();
+}
+/* unused harmony export SnippetsProvider */
+
+
+/**
+ * addSnippetsSubscriber - Creates a SnippetsProvider that Initializes
+ *                         when the store has received the appropriate
+ *                         Snippet data.
+ *
+ * @param  {obj} store   The redux store
+ * @return {obj}         Returns the snippets instance and unsubscribe function
+ */
+function addSnippetsSubscriber(store) {
+  const snippets = new SnippetsProvider(store.dispatch);
+
+  let initializing = false;
+
+  store.subscribe(async () => {
+    const state = store.getState();
+    // state.Prefs.values["feeds.snippets"]:  Should snippets be shown?
+    // state.Snippets.initialized             Is the snippets data initialized?
+    // snippets.initialized:                  Is SnippetsProvider currently initialised?
+    if (state.Prefs.values["feeds.snippets"] && !state.Prefs.values.disableSnippets && state.Snippets.initialized && !snippets.initialized &&
+    // Don't call init multiple times
+    !initializing) {
+      initializing = true;
+      await snippets.init({ appData: state.Snippets });
+      initializing = false;
+    } else if ((state.Prefs.values["feeds.snippets"] === false || state.Prefs.values.disableSnippets === true) && snippets.initialized) {
+      snippets.uninit();
+    }
+  });
+
+  // These values are returned for testing purposes
+  return snippets;
+}
+/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(4)))
+
+/***/ }),
+/* 12 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+
+// EXTERNAL MODULE: ./system-addon/common/Actions.jsm
+var Actions = __webpack_require__(0);
+var Actions_default = /*#__PURE__*/__webpack_require__.n(Actions);
+
+// EXTERNAL MODULE: external "ReactIntl"
+var external__ReactIntl_ = __webpack_require__(2);
+var external__ReactIntl__default = /*#__PURE__*/__webpack_require__.n(external__ReactIntl_);
+
+// EXTERNAL MODULE: external "ReactRedux"
+var external__ReactRedux_ = __webpack_require__(3);
+var external__ReactRedux__default = /*#__PURE__*/__webpack_require__.n(external__ReactRedux_);
+
+// EXTERNAL MODULE: external "React"
+var external__React_ = __webpack_require__(1);
+var external__React__default = /*#__PURE__*/__webpack_require__.n(external__React_);
+
+// CONCATENATED MODULE: ./system-addon/content-src/components/ConfirmDialog/ConfirmDialog.jsx
+
+
+
+
+
+/**
+ * ConfirmDialog component.
+ * One primary action button, one cancel button.
+ *
+ * Content displayed is controlled by `data` prop the component receives.
+ * Example:
+ * data: {
+ *   // Any sort of data needed to be passed around by actions.
+ *   payload: site.url,
+ *   // Primary button SendToMain action.
+ *   action: "DELETE_HISTORY_URL",
+ *   // Primary button USerEvent action.
+ *   userEvent: "DELETE",
+ *   // Array of locale ids to display.
+ *   message_body: ["confirm_history_delete_p1", "confirm_history_delete_notice_p2"],
+ *   // Text for primary button.
+ *   confirm_button_string_id: "menu_action_delete"
+ * },
+ */
+class ConfirmDialog__ConfirmDialog extends external__React__default.a.PureComponent {
+  constructor(props) {
+    super(props);
+    this._handleCancelBtn = this._handleCancelBtn.bind(this);
+    this._handleConfirmBtn = this._handleConfirmBtn.bind(this);
+  }
+
+  _handleCancelBtn() {
+    this.props.dispatch({ type: Actions["actionTypes"].DIALOG_CANCEL });
+    this.props.dispatch(Actions["actionCreators"].UserEvent({ event: Actions["actionTypes"].DIALOG_CANCEL }));
+  }
+
+  _handleConfirmBtn() {
+    this.props.data.onConfirm.forEach(this.props.dispatch);
+  }
+
+  _renderModalMessage() {
+    const message_body = this.props.data.body_string_id;
+
+    if (!message_body) {
+      return null;
+    }
+
+    return external__React__default.a.createElement(
+      "span",
+      null,
+      message_body.map(msg => external__React__default.a.createElement(
+        "p",
+        { key: msg },
+        external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: msg })
+      ))
+    );
   }
 
   render() {
-    const props = this.props;
-    const infoOption = {
-      header: { id: "settings_pane_topsites_header" },
-      body: { id: "settings_pane_topsites_body" }
-    };
-    return React.createElement(
-      ComponentPerfTimer,
-      { id: "topsites", initialized: props.TopSites.initialized, dispatch: props.dispatch },
-      React.createElement(
-        CollapsibleSection,
-        { className: "top-sites", icon: "topsites", title: React.createElement(FormattedMessage, { id: "header_top_sites" }), infoOption: infoOption, prefName: "collapseTopSites", Prefs: props.Prefs, dispatch: props.dispatch },
-        React.createElement(TopSiteList, { TopSites: props.TopSites, TopSitesCount: props.TopSitesCount, dispatch: props.dispatch, intl: props.intl }),
-        React.createElement(TopSitesEdit, props)
-      )
-    );
-  }
-}
-
-module.exports = connect(state => ({ TopSites: state.TopSites, Prefs: state.Prefs, TopSitesCount: state.Prefs.values.topSitesCount }))(TopSites);
-module.exports._unconnected = TopSites;
-
-/***/ }),
-/* 16 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const React = __webpack_require__(1);
-const { FormattedMessage, injectIntl } = __webpack_require__(2);
-const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
-
-const TopSiteForm = __webpack_require__(17);
-const { TopSiteList } = __webpack_require__(7);
-
-const { TOP_SITES_DEFAULT_LENGTH, TOP_SITES_SHOWMORE_LENGTH } = __webpack_require__(6);
-const { TOP_SITES_SOURCE } = __webpack_require__(5);
-
-class TopSitesEdit extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.state = {
-      showEditModal: false,
-      showAddForm: false,
-      showEditForm: false,
-      editIndex: -1 // Index of top site being edited
-    };
-    this.onEditButtonClick = this.onEditButtonClick.bind(this);
-    this.onShowMoreLessClick = this.onShowMoreLessClick.bind(this);
-    this.onModalOverlayClick = this.onModalOverlayClick.bind(this);
-    this.onAddButtonClick = this.onAddButtonClick.bind(this);
-    this.onFormClose = this.onFormClose.bind(this);
-    this.onEdit = this.onEdit.bind(this);
-  }
-  onEditButtonClick() {
-    this.setState({ showEditModal: !this.state.showEditModal });
-    const event = this.state.showEditModal ? "TOP_SITES_EDIT_OPEN" : "TOP_SITES_EDIT_CLOSE";
-    this.props.dispatch(ac.UserEvent({
-      source: TOP_SITES_SOURCE,
-      event
-    }));
-  }
-  onModalOverlayClick() {
-    this.setState({ showEditModal: false, showAddForm: false, showEditForm: false });
-    this.props.dispatch(ac.UserEvent({
-      source: TOP_SITES_SOURCE,
-      event: "TOP_SITES_EDIT_CLOSE"
-    }));
-    this.props.dispatch({ type: at.TOP_SITES_CANCEL_EDIT });
-  }
-  onShowMoreLessClick() {
-    const prefIsSetToDefault = this.props.TopSitesCount === TOP_SITES_DEFAULT_LENGTH;
-    this.props.dispatch(ac.SendToMain({
-      type: at.SET_PREF,
-      data: { name: "topSitesCount", value: prefIsSetToDefault ? TOP_SITES_SHOWMORE_LENGTH : TOP_SITES_DEFAULT_LENGTH }
-    }));
-    this.props.dispatch(ac.UserEvent({
-      source: TOP_SITES_SOURCE,
-      event: prefIsSetToDefault ? "TOP_SITES_EDIT_SHOW_MORE" : "TOP_SITES_EDIT_SHOW_LESS"
-    }));
-  }
-  onAddButtonClick() {
-    this.setState({ showAddForm: true });
-    this.props.dispatch(ac.UserEvent({
-      source: TOP_SITES_SOURCE,
-      event: "TOP_SITES_ADD_FORM_OPEN"
-    }));
-  }
-  onFormClose() {
-    this.setState({ showAddForm: false, showEditForm: false });
-    this.props.dispatch({ type: at.TOP_SITES_CANCEL_EDIT });
-  }
-  onEdit(index) {
-    this.setState({ showEditForm: true, editIndex: index });
-    this.props.dispatch(ac.UserEvent({
-      source: TOP_SITES_SOURCE,
-      event: "TOP_SITES_EDIT_FORM_OPEN"
-    }));
-  }
-  render() {
-    const showEditForm = this.props.TopSites.editForm && this.props.TopSites.editForm.visible || this.state.showEditModal && this.state.showEditForm;
-    let editIndex = this.state.editIndex;
-    if (showEditForm && this.props.TopSites.editForm.visible) {
-      const targetURL = this.props.TopSites.editForm.site.url;
-      editIndex = this.props.TopSites.rows.findIndex(s => s.url === targetURL);
+    if (!this.props.visible) {
+      return null;
     }
-    return React.createElement(
+
+    return external__React__default.a.createElement(
       "div",
-      { className: "edit-topsites-wrapper" },
-      React.createElement(
-        "div",
-        { className: "edit-topsites-button" },
-        React.createElement(
-          "button",
-          {
-            className: "edit",
-            title: this.props.intl.formatMessage({ id: "edit_topsites_button_label" }),
-            onClick: this.onEditButtonClick },
-          React.createElement(FormattedMessage, { id: "edit_topsites_button_text" })
-        )
-      ),
-      this.state.showEditModal && !this.state.showAddForm && !this.state.showEditForm && React.createElement(
+      { className: "confirmation-dialog" },
+      external__React__default.a.createElement("div", { className: "modal-overlay", onClick: this._handleCancelBtn }),
+      external__React__default.a.createElement(
         "div",
-        { className: "edit-topsites" },
-        React.createElement("div", { className: "modal-overlay", onClick: this.onModalOverlayClick }),
-        React.createElement(
-          "div",
-          { className: "modal" },
-          React.createElement(
-            "section",
-            { className: "edit-topsites-inner-wrapper" },
-            React.createElement(
-              "div",
-              { className: "section-top-bar" },
-              React.createElement(
-                "h3",
-                { className: "section-title" },
-                React.createElement("span", { className: `icon icon-small-spacer icon-topsites` }),
-                React.createElement(FormattedMessage, { id: "header_top_sites" })
-              )
-            ),
-            React.createElement(TopSiteList, { TopSites: this.props.TopSites, TopSitesCount: this.props.TopSitesCount, onEdit: this.onEdit, dispatch: this.props.dispatch, intl: this.props.intl })
+        { className: "modal" },
+        external__React__default.a.createElement(
+          "section",
+          { className: "modal-message" },
+          this.props.data.icon && external__React__default.a.createElement("span", { className: `icon icon-spacer icon-${this.props.data.icon}` }),
+          this._renderModalMessage()
+        ),
+        external__React__default.a.createElement(
+          "section",
+          { className: "actions" },
+          external__React__default.a.createElement(
+            "button",
+            { onClick: this._handleCancelBtn },
+            external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: this.props.data.cancel_button_string_id })
           ),
-          React.createElement(
-            "section",
-            { className: "actions" },
-            React.createElement(
-              "button",
-              { className: "add", onClick: this.onAddButtonClick },
-              React.createElement(FormattedMessage, { id: "edit_topsites_add_button" })
-            ),
-            React.createElement(
-              "button",
-              { className: `icon icon-topsites show-${this.props.TopSitesCount === TOP_SITES_DEFAULT_LENGTH ? "more" : "less"}`, onClick: this.onShowMoreLessClick },
-              React.createElement(FormattedMessage, { id: `edit_topsites_show${this.props.TopSitesCount === TOP_SITES_DEFAULT_LENGTH ? "more" : "less"}_button` })
-            ),
-            React.createElement(
-              "button",
-              { className: "done", onClick: this.onEditButtonClick },
-              React.createElement(FormattedMessage, { id: "edit_topsites_done_button" })
-            )
+          external__React__default.a.createElement(
+            "button",
+            { className: "done", onClick: this._handleConfirmBtn },
+            external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: this.props.data.confirm_button_string_id })
           )
         )
-      ),
-      this.state.showEditModal && this.state.showAddForm && React.createElement(
-        "div",
-        { className: "edit-topsites" },
-        React.createElement("div", { className: "modal-overlay", onClick: this.onModalOverlayClick }),
-        React.createElement(
-          "div",
-          { className: "modal" },
-          React.createElement(TopSiteForm, { onClose: this.onFormClose, dispatch: this.props.dispatch, intl: this.props.intl })
-        )
-      ),
-      showEditForm && React.createElement(
-        "div",
-        { className: "edit-topsites" },
-        React.createElement("div", { className: "modal-overlay", onClick: this.onModalOverlayClick }),
-        React.createElement(
-          "div",
-          { className: "modal" },
-          React.createElement(TopSiteForm, {
-            label: this.props.TopSites.rows[editIndex].label || this.props.TopSites.rows[editIndex].hostname,
-            url: this.props.TopSites.rows[editIndex].url,
-            index: editIndex,
-            editMode: true,
-            onClose: this.onFormClose,
-            dispatch: this.props.dispatch,
-            intl: this.props.intl })
-        )
       )
     );
   }
 }
 
-module.exports = injectIntl(TopSitesEdit);
-module.exports._unconnected = TopSitesEdit;
-
-/***/ }),
-/* 17 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const React = __webpack_require__(1);
-const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
-const { FormattedMessage } = __webpack_require__(2);
-
-const { TOP_SITES_SOURCE } = __webpack_require__(5);
-
-class TopSiteForm extends React.PureComponent {
+const ConfirmDialog = Object(external__ReactRedux_["connect"])(state => state.Dialog)(ConfirmDialog__ConfirmDialog);
+// CONCATENATED MODULE: ./system-addon/content-src/components/ManualMigration/ManualMigration.jsx
+
+
+
+
+
+/**
+ * Manual migration component used to start the profile import wizard.
+ * Message is presented temporarily and will go away if:
+ * 1.  User clicks "No Thanks"
+ * 2.  User completed the data import
+ * 3.  After 3 active days
+ * 4.  User clicks "Cancel" on the import wizard (currently not implemented).
+ */
+class ManualMigration__ManualMigration extends external__React__default.a.PureComponent {
+  constructor(props) {
+    super(props);
+    this.onLaunchTour = this.onLaunchTour.bind(this);
+    this.onCancelTour = this.onCancelTour.bind(this);
+  }
+  onLaunchTour() {
+    this.props.dispatch(Actions["actionCreators"].SendToMain({ type: Actions["actionTypes"].MIGRATION_START }));
+    this.props.dispatch(Actions["actionCreators"].UserEvent({ event: Actions["actionTypes"].MIGRATION_START }));
+  }
+
+  onCancelTour() {
+    this.props.dispatch(Actions["actionCreators"].SendToMain({ type: Actions["actionTypes"].MIGRATION_CANCEL }));
+    this.props.dispatch(Actions["actionCreators"].UserEvent({ event: Actions["actionTypes"].MIGRATION_CANCEL }));
+  }
+
+  render() {
+    return external__React__default.a.createElement(
+      "div",
+      { className: "manual-migration-container" },
+      external__React__default.a.createElement(
+        "p",
+        null,
+        external__React__default.a.createElement("span", { className: "icon icon-import" }),
+        external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "manual_migration_explanation2" })
+      ),
+      external__React__default.a.createElement(
+        "div",
+        { className: "manual-migration-actions actions" },
+        external__React__default.a.createElement(
+          "button",
+          { className: "dismiss", onClick: this.onCancelTour },
+          external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "manual_migration_cancel_button" })
+        ),
+        external__React__default.a.createElement(
+          "button",
+          { onClick: this.onLaunchTour },
+          external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "manual_migration_import_button" })
+        )
+      )
+    );
+  }
+}
+
+const ManualMigration = Object(external__ReactRedux_["connect"])()(ManualMigration__ManualMigration);
+// EXTERNAL MODULE: ./system-addon/common/Reducers.jsm
+var Reducers = __webpack_require__(5);
+var Reducers_default = /*#__PURE__*/__webpack_require__.n(Reducers);
+
+// CONCATENATED MODULE: ./system-addon/content-src/components/PreferencesPane/PreferencesPane.jsx
+
+
+
+
+
+
+const getFormattedMessage = message => typeof message === "string" ? external__React__default.a.createElement(
+  "span",
+  null,
+  message
+) : external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], message);
+
+const PreferencesInput = props => external__React__default.a.createElement(
+  "section",
+  null,
+  external__React__default.a.createElement("input", { type: "checkbox", id: props.prefName, name: props.prefName, checked: props.value, disabled: props.disabled, onChange: props.onChange, className: props.className }),
+  external__React__default.a.createElement(
+    "label",
+    { htmlFor: props.prefName, className: props.labelClassName },
+    getFormattedMessage(props.titleString)
+  ),
+  props.descString && external__React__default.a.createElement(
+    "p",
+    { className: "prefs-input-description" },
+    getFormattedMessage(props.descString)
+  ),
+  external__React__default.a.Children.map(props.children, child => external__React__default.a.createElement(
+    "div",
+    { className: `options${child.props.disabled ? " disabled" : ""}` },
+    child
+  ))
+);
+
+class PreferencesPane__PreferencesPane extends external__React__default.a.PureComponent {
+  constructor(props) {
+    super(props);
+    this.handleClickOutside = this.handleClickOutside.bind(this);
+    this.handlePrefChange = this.handlePrefChange.bind(this);
+    this.handleSectionChange = this.handleSectionChange.bind(this);
+    this.togglePane = this.togglePane.bind(this);
+    this.onWrapperMount = this.onWrapperMount.bind(this);
+  }
+  componentDidUpdate(prevProps, prevState) {
+    if (prevProps.PreferencesPane.visible !== this.props.PreferencesPane.visible) {
+      // While the sidebar is open, listen for all document clicks.
+      if (this.isSidebarOpen()) {
+        document.addEventListener("click", this.handleClickOutside);
+      } else {
+        document.removeEventListener("click", this.handleClickOutside);
+      }
+    }
+  }
+  isSidebarOpen() {
+    return this.props.PreferencesPane.visible;
+  }
+  handleClickOutside(event) {
+    // if we are showing the sidebar and there is a click outside, close it.
+    if (this.isSidebarOpen() && !this.wrapper.contains(event.target)) {
+      this.togglePane();
+    }
+  }
+  handlePrefChange(event) {
+    const target = event.target;
+    const { name, checked } = target;
+    let value = checked;
+    if (name === "topSitesCount") {
+      value = checked ? Reducers["TOP_SITES_SHOWMORE_LENGTH"] : Reducers["TOP_SITES_DEFAULT_LENGTH"];
+    }
+    this.props.dispatch(Actions["actionCreators"].SetPref(name, value));
+  }
+  handleSectionChange(event) {
+    const target = event.target;
+    const id = target.name;
+    const type = target.checked ? Actions["actionTypes"].SECTION_ENABLE : Actions["actionTypes"].SECTION_DISABLE;
+    this.props.dispatch(Actions["actionCreators"].SendToMain({ type, data: id }));
+  }
+  togglePane() {
+    if (this.isSidebarOpen()) {
+      this.props.dispatch({ type: Actions["actionTypes"].SETTINGS_CLOSE });
+      this.props.dispatch(Actions["actionCreators"].UserEvent({ event: "CLOSE_NEWTAB_PREFS" }));
+    } else {
+      this.props.dispatch({ type: Actions["actionTypes"].SETTINGS_OPEN });
+      this.props.dispatch(Actions["actionCreators"].UserEvent({ event: "OPEN_NEWTAB_PREFS" }));
+    }
+  }
+  onWrapperMount(wrapper) {
+    this.wrapper = wrapper;
+  }
+  render() {
+    const props = this.props;
+    const prefs = props.Prefs.values;
+    const sections = props.Sections;
+    const isVisible = this.isSidebarOpen();
+    return external__React__default.a.createElement(
+      "div",
+      { className: "prefs-pane-wrapper", ref: this.onWrapperMount },
+      external__React__default.a.createElement(
+        "div",
+        { className: "prefs-pane-button" },
+        external__React__default.a.createElement("button", {
+          className: `prefs-button icon ${isVisible ? "icon-dismiss" : "icon-settings"}`,
+          title: props.intl.formatMessage({ id: isVisible ? "settings_pane_done_button" : "settings_pane_button_label" }),
+          onClick: this.togglePane })
+      ),
+      external__React__default.a.createElement(
+        "div",
+        { className: "prefs-pane" },
+        external__React__default.a.createElement(
+          "div",
+          { className: `sidebar ${isVisible ? "" : "hidden"}` },
+          external__React__default.a.createElement(
+            "div",
+            { className: "prefs-modal-inner-wrapper" },
+            external__React__default.a.createElement(
+              "h1",
+              null,
+              external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "settings_pane_header" })
+            ),
+            external__React__default.a.createElement(
+              "p",
+              null,
+              external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "settings_pane_body2" })
+            ),
+            external__React__default.a.createElement(PreferencesInput, {
+              className: "showSearch",
+              prefName: "showSearch",
+              value: prefs.showSearch,
+              onChange: this.handlePrefChange,
+              titleString: { id: "settings_pane_search_header" },
+              descString: { id: "settings_pane_search_body" } }),
+            external__React__default.a.createElement("hr", null),
+            external__React__default.a.createElement(
+              PreferencesInput,
+              {
+                className: "showTopSites",
+                prefName: "showTopSites",
+                value: prefs.showTopSites,
+                onChange: this.handlePrefChange,
+                titleString: { id: "settings_pane_topsites_header" },
+                descString: { id: "settings_pane_topsites_body" } },
+              external__React__default.a.createElement(PreferencesInput, {
+                className: "showMoreTopSites",
+                prefName: "topSitesCount",
+                disabled: !prefs.showTopSites,
+                value: prefs.topSitesCount !== Reducers["TOP_SITES_DEFAULT_LENGTH"],
+                onChange: this.handlePrefChange,
+                titleString: { id: "settings_pane_topsites_options_showmore" },
+                labelClassName: "icon icon-topsites" })
+            ),
+            sections.filter(section => !section.shouldHidePref).map(({ id, title, enabled, pref }) => external__React__default.a.createElement(
+              PreferencesInput,
+              {
+                key: id,
+                className: "showSection",
+                prefName: pref && pref.feed || id,
+                value: enabled,
+                onChange: pref && pref.feed ? this.handlePrefChange : this.handleSectionChange,
+                titleString: pref && pref.titleString || title,
+                descString: pref && pref.descString },
+              pref.nestedPrefs && pref.nestedPrefs.map(nestedPref => external__React__default.a.createElement(PreferencesInput, {
+                key: nestedPref.name,
+                prefName: nestedPref.name,
+                disabled: !enabled,
+                value: prefs[nestedPref.name],
+                onChange: this.handlePrefChange,
+                titleString: nestedPref.titleString,
+                labelClassName: `icon ${nestedPref.icon}` }))
+            )),
+            !prefs.disableSnippets && external__React__default.a.createElement("hr", null),
+            !prefs.disableSnippets && external__React__default.a.createElement(PreferencesInput, { className: "showSnippets", prefName: "feeds.snippets",
+              value: prefs["feeds.snippets"], onChange: this.handlePrefChange,
+              titleString: { id: "settings_pane_snippets_header" },
+              descString: { id: "settings_pane_snippets_body" } })
+          ),
+          external__React__default.a.createElement(
+            "section",
+            { className: "actions" },
+            external__React__default.a.createElement(
+              "button",
+              { className: "done", onClick: this.togglePane },
+              external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "settings_pane_done_button" })
+            )
+          )
+        )
+      )
+    );
+  }
+}
+
+const PreferencesPane = Object(external__ReactRedux_["connect"])(state => ({
+  Prefs: state.Prefs,
+  PreferencesPane: state.PreferencesPane,
+  Sections: state.Sections
+}))(Object(external__ReactIntl_["injectIntl"])(PreferencesPane__PreferencesPane));
+// EXTERNAL MODULE: ./system-addon/common/PrerenderData.jsm
+var PrerenderData = __webpack_require__(14);
+var PrerenderData_default = /*#__PURE__*/__webpack_require__.n(PrerenderData);
+
+// EXTERNAL MODULE: ./system-addon/content-src/lib/constants.js
+var constants = __webpack_require__(15);
+
+// CONCATENATED MODULE: ./system-addon/content-src/components/Search/Search.jsx
+/* globals ContentSearchUIController */
+
+
+
+
+
+
+
+
+class Search__Search extends external__React__default.a.PureComponent {
+  constructor(props) {
+    super(props);
+    this.onClick = this.onClick.bind(this);
+    this.onInputMount = this.onInputMount.bind(this);
+  }
+
+  handleEvent(event) {
+    // Also track search events with our own telemetry
+    if (event.detail.type === "Search") {
+      this.props.dispatch(Actions["actionCreators"].UserEvent({ event: "SEARCH" }));
+    }
+  }
+  onClick(event) {
+    window.gContentSearchController.search(event);
+  }
+  componentWillUnmount() {
+    delete window.gContentSearchController;
+  }
+  onInputMount(input) {
+    if (input) {
+      // The "healthReportKey" and needs to be "newtab" or "abouthome" so that
+      // BrowserUsageTelemetry.jsm knows to handle events with this name, and
+      // can add the appropriate telemetry probes for search. Without the correct
+      // name, certain tests like browser_UsageTelemetry_content.js will fail
+      // (See github ticket #2348 for more details)
+      const healthReportKey = constants["a" /* IS_NEWTAB */] ? "newtab" : "abouthome";
+
+      // The "searchSource" needs to be "newtab" or "homepage" and is sent with
+      // the search data and acts as context for the search request (See
+      // nsISearchEngine.getSubmission). It is necessary so that search engine
+      // plugins can correctly atribute referrals. (See github ticket #3321 for
+      // more details)
+      const searchSource = constants["a" /* IS_NEWTAB */] ? "newtab" : "homepage";
+
+      // gContentSearchController needs to exist as a global so that tests for
+      // the existing about:home can find it; and so it allows these tests to pass.
+      // In the future, when activity stream is default about:home, this can be renamed
+      window.gContentSearchController = new ContentSearchUIController(input, input.parentNode, healthReportKey, searchSource);
+      addEventListener("ContentSearchClient", this);
+    } else {
+      window.gContentSearchController = null;
+      removeEventListener("ContentSearchClient", this);
+    }
+  }
+
+  /*
+   * Do not change the ID on the input field, as legacy newtab code
+   * specifically looks for the id 'newtab-search-text' on input fields
+   * in order to execute searches in various tests
+   */
+  render() {
+    return external__React__default.a.createElement(
+      "div",
+      { className: "search-wrapper" },
+      external__React__default.a.createElement(
+        "label",
+        { htmlFor: "newtab-search-text", className: "search-label" },
+        external__React__default.a.createElement(
+          "span",
+          { className: "sr-only" },
+          external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "search_web_placeholder" })
+        )
+      ),
+      external__React__default.a.createElement("input", {
+        id: "newtab-search-text",
+        maxLength: "256",
+        placeholder: this.props.intl.formatMessage({ id: "search_web_placeholder" }),
+        ref: this.onInputMount,
+        title: this.props.intl.formatMessage({ id: "search_web_placeholder" }),
+        type: "search" }),
+      external__React__default.a.createElement(
+        "button",
+        {
+          id: "searchSubmit",
+          className: "search-button",
+          onClick: this.onClick,
+          title: this.props.intl.formatMessage({ id: "search_button" }) },
+        external__React__default.a.createElement(
+          "span",
+          { className: "sr-only" },
+          external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "search_button" })
+        )
+      )
+    );
+  }
+}
+
+const Search = Object(external__ReactRedux_["connect"])()(Object(external__ReactIntl_["injectIntl"])(Search__Search));
+// EXTERNAL MODULE: ./system-addon/content-src/components/Sections/Sections.jsx
+var Sections = __webpack_require__(16);
+
+// CONCATENATED MODULE: ./system-addon/content-src/components/TopSites/TopSitesConstants.js
+const TOP_SITES_SOURCE = "TOP_SITES";
+const TOP_SITES_CONTEXT_MENU_OPTIONS = ["CheckPinTopSite", "EditTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl", "DeleteUrl"];
+// 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;
+// EXTERNAL MODULE: ./system-addon/content-src/components/CollapsibleSection/CollapsibleSection.jsx
+var CollapsibleSection = __webpack_require__(7);
+
+// EXTERNAL MODULE: ./system-addon/content-src/components/ComponentPerfTimer/ComponentPerfTimer.jsx
+var ComponentPerfTimer = __webpack_require__(8);
+
+// EXTERNAL MODULE: ./system-addon/content-src/components/LinkMenu/LinkMenu.jsx + 2 modules
+var LinkMenu = __webpack_require__(6);
+
+// CONCATENATED MODULE: ./system-addon/content-src/components/TopSites/TopSite.jsx
+var _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; };
+
+
+
+
+
+
+const TopSiteLink = props => {
+  const { link, title } = props;
+  const topSiteOuterClassName = `top-site-outer${props.className ? ` ${props.className}` : ""}`;
+  const { tippyTopIcon, faviconSize } = link;
+  const letterFallback = title[0];
+  let imageClassName;
+  let imageStyle;
+  let showSmallFavicon = false;
+  let smallFaviconStyle;
+  let smallFaviconFallback;
+  if (tippyTopIcon || faviconSize >= MIN_RICH_FAVICON_SIZE) {
+    // styles and class names for top sites with rich icons
+    imageClassName = "top-site-icon rich-icon";
+    imageStyle = {
+      backgroundColor: link.backgroundColor,
+      backgroundImage: `url(${tippyTopIcon || link.favicon})`
+    };
+  } else {
+    // styles and class names for top sites with screenshot + small icon in top left corner
+    imageClassName = `screenshot${link.screenshot ? " active" : ""}`;
+    imageStyle = { backgroundImage: link.screenshot ? `url(${link.screenshot})` : "none" };
+
+    // only show a favicon in top left if it's greater than 16x16
+    if (faviconSize >= MIN_CORNER_FAVICON_SIZE) {
+      showSmallFavicon = true;
+      smallFaviconStyle = { backgroundImage: `url(${link.favicon})` };
+    } else if (link.screenshot) {
+      // Don't show a small favicon if there is no screenshot, because that
+      // would result in two fallback icons
+      showSmallFavicon = true;
+      smallFaviconFallback = true;
+    }
+  }
+  return external__React__default.a.createElement(
+    "li",
+    { className: topSiteOuterClassName, key: link.guid || link.url },
+    external__React__default.a.createElement(
+      "a",
+      { href: link.url, onClick: props.onClick },
+      external__React__default.a.createElement(
+        "div",
+        { className: "tile", "aria-hidden": true, "data-fallback": letterFallback },
+        external__React__default.a.createElement("div", { className: imageClassName, style: imageStyle }),
+        showSmallFavicon && external__React__default.a.createElement("div", {
+          className: "top-site-icon default-icon",
+          "data-fallback": smallFaviconFallback && letterFallback,
+          style: smallFaviconStyle })
+      ),
+      external__React__default.a.createElement(
+        "div",
+        { className: `title ${link.isPinned ? "pinned" : ""}` },
+        link.isPinned && external__React__default.a.createElement("div", { className: "icon icon-pin-small" }),
+        external__React__default.a.createElement(
+          "span",
+          { dir: "auto" },
+          title
+        )
+      )
+    ),
+    props.children
+  );
+};
+TopSiteLink.defaultProps = {
+  title: "",
+  link: {}
+};
+
+class TopSite_TopSite extends external__React__default.a.PureComponent {
+  constructor(props) {
+    super(props);
+    this.state = { showContextMenu: false, activeTile: null };
+    this.onLinkClick = this.onLinkClick.bind(this);
+    this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
+    this.onMenuUpdate = this.onMenuUpdate.bind(this);
+    this.onDismissButtonClick = this.onDismissButtonClick.bind(this);
+    this.onPinButtonClick = this.onPinButtonClick.bind(this);
+    this.onEditButtonClick = this.onEditButtonClick.bind(this);
+  }
+  toggleContextMenu(event, index) {
+    this.setState({
+      activeTile: index,
+      showContextMenu: true
+    });
+  }
+  userEvent(event) {
+    this.props.dispatch(Actions["actionCreators"].UserEvent({
+      event,
+      source: TOP_SITES_SOURCE,
+      action_position: this.props.index
+    }));
+  }
+  onLinkClick(ev) {
+    if (this.props.onEdit) {
+      // Ignore clicks if we are in the edit modal.
+      ev.preventDefault();
+      return;
+    }
+    this.userEvent("CLICK");
+  }
+  onMenuButtonClick(event) {
+    event.preventDefault();
+    this.toggleContextMenu(event, this.props.index);
+  }
+  onMenuUpdate(showContextMenu) {
+    this.setState({ showContextMenu });
+  }
+  onDismissButtonClick() {
+    const { link } = this.props;
+    if (link.isPinned) {
+      this.props.dispatch(Actions["actionCreators"].SendToMain({
+        type: Actions["actionTypes"].TOP_SITES_UNPIN,
+        data: { site: { url: link.url } }
+      }));
+    }
+    this.props.dispatch(Actions["actionCreators"].SendToMain({
+      type: Actions["actionTypes"].BLOCK_URL,
+      data: link.url
+    }));
+    this.userEvent("BLOCK");
+  }
+  onPinButtonClick() {
+    const { link, index } = this.props;
+    if (link.isPinned) {
+      this.props.dispatch(Actions["actionCreators"].SendToMain({
+        type: Actions["actionTypes"].TOP_SITES_UNPIN,
+        data: { site: { url: link.url } }
+      }));
+      this.userEvent("UNPIN");
+    } else {
+      this.props.dispatch(Actions["actionCreators"].SendToMain({
+        type: Actions["actionTypes"].TOP_SITES_PIN,
+        data: { site: { url: link.url }, index }
+      }));
+      this.userEvent("PIN");
+    }
+  }
+  onEditButtonClick() {
+    this.props.onEdit(this.props.index);
+  }
+  render() {
+    const { props } = this;
+    const { link } = props;
+    const isContextMenuOpen = this.state.showContextMenu && this.state.activeTile === props.index;
+    const title = link.label || link.hostname;
+    return external__React__default.a.createElement(
+      TopSiteLink,
+      _extends({}, props, { onClick: this.onLinkClick, className: isContextMenuOpen ? "active" : "", title: title }),
+      !props.onEdit && external__React__default.a.createElement(
+        "div",
+        null,
+        external__React__default.a.createElement(
+          "button",
+          { className: "context-menu-button icon", onClick: this.onMenuButtonClick },
+          external__React__default.a.createElement(
+            "span",
+            { className: "sr-only" },
+            `Open context menu for ${title}`
+          )
+        ),
+        external__React__default.a.createElement(LinkMenu["a" /* LinkMenu */], {
+          dispatch: props.dispatch,
+          index: props.index,
+          onUpdate: this.onMenuUpdate,
+          options: TOP_SITES_CONTEXT_MENU_OPTIONS,
+          site: link,
+          source: TOP_SITES_SOURCE,
+          visible: isContextMenuOpen })
+      ),
+      props.onEdit && external__React__default.a.createElement(
+        "div",
+        { className: "edit-menu" },
+        external__React__default.a.createElement("button", {
+          className: `icon icon-${link.isPinned ? "unpin" : "pin"}`,
+          title: this.props.intl.formatMessage({ id: `edit_topsites_${link.isPinned ? "unpin" : "pin"}_button` }),
+          onClick: this.onPinButtonClick }),
+        external__React__default.a.createElement("button", {
+          className: "icon icon-edit",
+          title: this.props.intl.formatMessage({ id: "edit_topsites_edit_button" }),
+          onClick: this.onEditButtonClick }),
+        external__React__default.a.createElement("button", {
+          className: "icon icon-dismiss",
+          title: this.props.intl.formatMessage({ id: "edit_topsites_dismiss_button" }),
+          onClick: this.onDismissButtonClick })
+      )
+    );
+  }
+}
+TopSite_TopSite.defaultProps = { link: {} };
+
+const TopSitePlaceholder = () => external__React__default.a.createElement(TopSiteLink, { className: "placeholder" });
+
+const TopSiteList = props => {
+  const topSites = props.TopSites.rows.slice(0, props.TopSitesCount);
+  const topSitesUI = [];
+  for (let i = 0, l = props.TopSitesCount; i < l; i++) {
+    const link = topSites[i];
+    topSitesUI.push(!link ? external__React__default.a.createElement(TopSitePlaceholder, { key: i }) : external__React__default.a.createElement(TopSite_TopSite, {
+      key: link.guid || link.url,
+      dispatch: props.dispatch,
+      link: link,
+      index: i,
+      intl: props.intl,
+      onEdit: props.onEdit }));
+  }
+  return external__React__default.a.createElement(
+    "ul",
+    { className: "top-sites-list" },
+    topSitesUI
+  );
+};
+// CONCATENATED MODULE: ./system-addon/content-src/components/TopSites/TopSiteForm.jsx
+
+
+
+
+
+class TopSiteForm_TopSiteForm extends external__React__default.a.PureComponent {
   constructor(props) {
     super(props);
     this.state = {
       label: props.label || "",
       url: props.url || "",
       validationError: false
     };
     this.onLabelChange = this.onLabelChange.bind(this);
@@ -2011,39 +2747,39 @@ class TopSiteForm extends React.PureComp
   }
   onAddButtonClick(ev) {
     ev.preventDefault();
     if (this.validateForm()) {
       let site = { url: this.cleanUrl() };
       if (this.state.label !== "") {
         site.label = this.state.label;
       }
-      this.props.dispatch(ac.SendToMain({
-        type: at.TOP_SITES_ADD,
+      this.props.dispatch(Actions["actionCreators"].SendToMain({
+        type: Actions["actionTypes"].TOP_SITES_ADD,
         data: { site }
       }));
-      this.props.dispatch(ac.UserEvent({
+      this.props.dispatch(Actions["actionCreators"].UserEvent({
         source: TOP_SITES_SOURCE,
         event: "TOP_SITES_ADD"
       }));
       this.props.onClose();
     }
   }
   onSaveButtonClick(ev) {
     ev.preventDefault();
     if (this.validateForm()) {
       let site = { url: this.cleanUrl() };
       if (this.state.label !== "") {
         site.label = this.state.label;
       }
-      this.props.dispatch(ac.SendToMain({
-        type: at.TOP_SITES_PIN,
+      this.props.dispatch(Actions["actionCreators"].SendToMain({
+        type: Actions["actionTypes"].TOP_SITES_PIN,
         data: { site, index: this.props.index }
       }));
-      this.props.dispatch(ac.UserEvent({
+      this.props.dispatch(Actions["actionCreators"].UserEvent({
         source: TOP_SITES_SOURCE,
         event: "TOP_SITES_EDIT",
         action_position: this.props.index
       }));
       this.props.onClose();
     }
   }
   cleanUrl() {
@@ -2075,302 +2811,446 @@ class TopSiteForm extends React.PureComp
       return false;
     }
     return true;
   }
   onUrlInputMount(input) {
     this.inputUrl = input;
   }
   render() {
-    return React.createElement(
+    return external__React__default.a.createElement(
       "form",
       { className: "topsite-form" },
-      React.createElement(
+      external__React__default.a.createElement(
         "section",
         { className: "edit-topsites-inner-wrapper" },
-        React.createElement(
+        external__React__default.a.createElement(
           "div",
           { className: "form-wrapper" },
-          React.createElement(
+          external__React__default.a.createElement(
             "h3",
             { className: "section-title" },
-            React.createElement(FormattedMessage, { id: this.props.editMode ? "topsites_form_edit_header" : "topsites_form_add_header" })
+            external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: this.props.editMode ? "topsites_form_edit_header" : "topsites_form_add_header" })
           ),
-          React.createElement(
+          external__React__default.a.createElement(
             "div",
             { className: "field title" },
-            React.createElement("input", {
+            external__React__default.a.createElement("input", {
               type: "text",
               value: this.state.label,
               onChange: this.onLabelChange,
               placeholder: this.props.intl.formatMessage({ id: "topsites_form_title_placeholder" }) })
           ),
-          React.createElement(
+          external__React__default.a.createElement(
             "div",
             { className: `field url${this.state.validationError ? " invalid" : ""}` },
-            React.createElement("input", {
+            external__React__default.a.createElement("input", {
               type: "text",
               ref: this.onUrlInputMount,
               value: this.state.url,
               onChange: this.onUrlChange,
               placeholder: this.props.intl.formatMessage({ id: "topsites_form_url_placeholder" }) }),
-            this.state.validationError && React.createElement(
+            this.state.validationError && external__React__default.a.createElement(
               "aside",
               { className: "error-tooltip" },
-              React.createElement(FormattedMessage, { id: "topsites_form_url_validation" })
+              external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "topsites_form_url_validation" })
             )
           )
         )
       ),
-      React.createElement(
+      external__React__default.a.createElement(
         "section",
         { className: "actions" },
-        React.createElement(
+        external__React__default.a.createElement(
           "button",
           { className: "cancel", type: "button", onClick: this.onCancelButtonClick },
-          React.createElement(FormattedMessage, { id: "topsites_form_cancel_button" })
+          external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "topsites_form_cancel_button" })
         ),
-        this.props.editMode && React.createElement(
+        this.props.editMode && external__React__default.a.createElement(
           "button",
           { className: "done save", type: "submit", onClick: this.onSaveButtonClick },
-          React.createElement(FormattedMessage, { id: "topsites_form_save_button" })
+          external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "topsites_form_save_button" })
         ),
-        !this.props.editMode && React.createElement(
+        !this.props.editMode && external__React__default.a.createElement(
           "button",
           { className: "done add", type: "submit", onClick: this.onAddButtonClick },
-          React.createElement(FormattedMessage, { id: "topsites_form_add_button" })
+          external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "topsites_form_add_button" })
         )
       )
     );
   }
 }
 
-TopSiteForm.defaultProps = {
+TopSiteForm_TopSiteForm.defaultProps = {
   label: "",
   url: "",
   index: 0,
   editMode: false // by default we are in "Add New Top Site" mode
 };
-
-module.exports = TopSiteForm;
-
-/***/ }),
-/* 18 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const React = __webpack_require__(1);
-
-class ContextMenu extends React.PureComponent {
+// CONCATENATED MODULE: ./system-addon/content-src/components/TopSites/TopSitesEdit.jsx
+
+
+
+
+
+
+
+
+class TopSitesEdit__TopSitesEdit extends external__React__default.a.PureComponent {
   constructor(props) {
     super(props);
-    this.hideContext = this.hideContext.bind(this);
+    this.state = {
+      showEditModal: false,
+      showAddForm: false,
+      showEditForm: false,
+      editIndex: -1 // Index of top site being edited
+    };
+    this.onEditButtonClick = this.onEditButtonClick.bind(this);
+    this.onShowMoreLessClick = this.onShowMoreLessClick.bind(this);
+    this.onModalOverlayClick = this.onModalOverlayClick.bind(this);
+    this.onAddButtonClick = this.onAddButtonClick.bind(this);
+    this.onFormClose = this.onFormClose.bind(this);
+    this.onEdit = this.onEdit.bind(this);
   }
-  hideContext() {
-    this.props.onUpdate(false);
+  onEditButtonClick() {
+    this.setState({ showEditModal: !this.state.showEditModal });
+    const event = this.state.showEditModal ? "TOP_SITES_EDIT_OPEN" : "TOP_SITES_EDIT_CLOSE";
+    this.props.dispatch(Actions["actionCreators"].UserEvent({
+      source: TOP_SITES_SOURCE,
+      event
+    }));
   }
-  componentWillMount() {
-    this.hideContext();
+  onModalOverlayClick() {
+    this.setState({ showEditModal: false, showAddForm: false, showEditForm: false });
+    this.props.dispatch(Actions["actionCreators"].UserEvent({
+      source: TOP_SITES_SOURCE,
+      event: "TOP_SITES_EDIT_CLOSE"
+    }));
+    this.props.dispatch({ type: Actions["actionTypes"].TOP_SITES_CANCEL_EDIT });
   }
-  componentDidUpdate(prevProps) {
-    if (this.props.visible && !prevProps.visible) {
-      setTimeout(() => {
-        window.addEventListener("click", this.hideContext);
-      }, 0);
-    }
-    if (!this.props.visible && prevProps.visible) {
-      window.removeEventListener("click", this.hideContext);
-    }
+  onShowMoreLessClick() {
+    const prefIsSetToDefault = this.props.TopSitesCount === Reducers["TOP_SITES_DEFAULT_LENGTH"];
+    this.props.dispatch(Actions["actionCreators"].SendToMain({
+      type: Actions["actionTypes"].SET_PREF,
+      data: { name: "topSitesCount", value: prefIsSetToDefault ? Reducers["TOP_SITES_SHOWMORE_LENGTH"] : Reducers["TOP_SITES_DEFAULT_LENGTH"] }
+    }));
+    this.props.dispatch(Actions["actionCreators"].UserEvent({
+      source: TOP_SITES_SOURCE,
+      event: prefIsSetToDefault ? "TOP_SITES_EDIT_SHOW_MORE" : "TOP_SITES_EDIT_SHOW_LESS"
+    }));
   }
-  componentWillUnmount() {
-    window.removeEventListener("click", this.hideContext);
+  onAddButtonClick() {
+    this.setState({ showAddForm: true });
+    this.props.dispatch(Actions["actionCreators"].UserEvent({
+      source: TOP_SITES_SOURCE,
+      event: "TOP_SITES_ADD_FORM_OPEN"
+    }));
+  }
+  onFormClose() {
+    this.setState({ showAddForm: false, showEditForm: false });
+    this.props.dispatch({ type: Actions["actionTypes"].TOP_SITES_CANCEL_EDIT });
+  }
+  onEdit(index) {
+    this.setState({ showEditForm: true, editIndex: index });
+    this.props.dispatch(Actions["actionCreators"].UserEvent({
+      source: TOP_SITES_SOURCE,
+      event: "TOP_SITES_EDIT_FORM_OPEN"
+    }));
   }
   render() {
-    return React.createElement(
-      "span",
-      { hidden: !this.props.visible, className: "context-menu" },
-      React.createElement(
-        "ul",
-        { role: "menu", className: "context-menu-list" },
-        this.props.options.map((option, i) => option.type === "separator" ? React.createElement("li", { key: i, className: "separator" }) : React.createElement(ContextMenuItem, { key: i, option: option, hideContext: this.hideContext }))
-      )
-    );
-  }
-}
-
-class ContextMenuItem extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.onClick = this.onClick.bind(this);
-    this.onKeyDown = this.onKeyDown.bind(this);
-  }
-  onClick() {
-    this.props.hideContext();
-    this.props.option.onClick();
-  }
-  onKeyDown(event) {
-    const { option } = this.props;
-    switch (event.key) {
-      case "Tab":
-        // tab goes down in context menu, shift + tab goes up in context menu
-        // if we're on the last item, one more tab will close the context menu
-        // similarly, if we're on the first item, one more shift + tab will close it
-        if (event.shiftKey && option.first || !event.shiftKey && option.last) {
-          this.props.hideContext();
-        }
-        break;
-      case "Enter":
-        this.props.hideContext();
-        option.onClick();
-        break;
+    const showEditForm = this.props.TopSites.editForm && this.props.TopSites.editForm.visible || this.state.showEditModal && this.state.showEditForm;
+    let editIndex = this.state.editIndex;
+    if (showEditForm && this.props.TopSites.editForm.visible) {
+      const targetURL = this.props.TopSites.editForm.site.url;
+      editIndex = this.props.TopSites.rows.findIndex(s => s.url === targetURL);
     }
-  }
-  render() {
-    const { option } = this.props;
-    return React.createElement(
-      "li",
-      { role: "menuitem", className: "context-menu-item" },
-      React.createElement(
-        "a",
-        { onClick: this.onClick, onKeyDown: this.onKeyDown, tabIndex: "0" },
-        option.icon && React.createElement("span", { className: `icon icon-spacer icon-${option.icon}` }),
-        option.label
+    return external__React__default.a.createElement(
+      "div",
+      { className: "edit-topsites-wrapper" },
+      external__React__default.a.createElement(
+        "div",
+        { className: "edit-topsites-button" },
+        external__React__default.a.createElement(
+          "button",
+          {
+            className: "edit",
+            title: this.props.intl.formatMessage({ id: "edit_topsites_button_label" }),
+            onClick: this.onEditButtonClick },
+          external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "edit_topsites_button_text" })
+        )
+      ),
+      this.state.showEditModal && !this.state.showAddForm && !this.state.showEditForm && external__React__default.a.createElement(
+        "div",
+        { className: "edit-topsites" },
+        external__React__default.a.createElement("div", { className: "modal-overlay", onClick: this.onModalOverlayClick }),
+        external__React__default.a.createElement(
+          "div",
+          { className: "modal" },
+          external__React__default.a.createElement(
+            "section",
+            { className: "edit-topsites-inner-wrapper" },
+            external__React__default.a.createElement(
+              "div",
+              { className: "section-top-bar" },
+              external__React__default.a.createElement(
+                "h3",
+                { className: "section-title" },
+                external__React__default.a.createElement("span", { className: `icon icon-small-spacer icon-topsites` }),
+                external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "header_top_sites" })
+              )
+            ),
+            external__React__default.a.createElement(TopSiteList, { TopSites: this.props.TopSites, TopSitesCount: this.props.TopSitesCount, onEdit: this.onEdit, dispatch: this.props.dispatch, intl: this.props.intl })
+          ),
+          external__React__default.a.createElement(
+            "section",
+            { className: "actions" },
+            external__React__default.a.createElement(
+              "button",
+              { className: "add", onClick: this.onAddButtonClick },
+              external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "edit_topsites_add_button" })
+            ),
+            external__React__default.a.createElement(
+              "button",
+              { className: `icon icon-topsites show-${this.props.TopSitesCount === Reducers["TOP_SITES_DEFAULT_LENGTH"] ? "more" : "less"}`, onClick: this.onShowMoreLessClick },
+              external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: `edit_topsites_show${this.props.TopSitesCount === Reducers["TOP_SITES_DEFAULT_LENGTH"] ? "more" : "less"}_button` })
+            ),
+            external__React__default.a.createElement(
+              "button",
+              { className: "done", onClick: this.onEditButtonClick },
+              external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "edit_topsites_done_button" })
+            )
+          )
+        )
+      ),
+      this.state.showEditModal && this.state.showAddForm && external__React__default.a.createElement(
+        "div",
+        { className: "edit-topsites" },
+        external__React__default.a.createElement("div", { className: "modal-overlay", onClick: this.onModalOverlayClick }),
+        external__React__default.a.createElement(
+          "div",
+          { className: "modal" },
+          external__React__default.a.createElement(TopSiteForm_TopSiteForm, { onClose: this.onFormClose, dispatch: this.props.dispatch, intl: this.props.intl })
+        )
+      ),
+      showEditForm && external__React__default.a.createElement(
+        "div",
+        { className: "edit-topsites" },
+        external__React__default.a.createElement("div", { className: "modal-overlay", onClick: this.onModalOverlayClick }),
+        external__React__default.a.createElement(
+          "div",
+          { className: "modal" },
+          external__React__default.a.createElement(TopSiteForm_TopSiteForm, {
+            label: this.props.TopSites.rows[editIndex].label || this.props.TopSites.rows[editIndex].hostname,
+            url: this.props.TopSites.rows[editIndex].url,
+            index: editIndex,
+            editMode: true,
+            onClose: this.onFormClose,
+            dispatch: this.props.dispatch,
+            intl: this.props.intl })
+        )
       )
     );
   }
 }
 
-module.exports = ContextMenu;
-module.exports.ContextMenu = ContextMenu;
-module.exports.ContextMenuItem = ContextMenuItem;
-
-/***/ }),
-/* 19 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const { actionTypes: at, actionCreators: ac } = __webpack_require__(0);
+const TopSitesEdit = Object(external__ReactIntl_["injectIntl"])(TopSitesEdit__TopSitesEdit);
+// CONCATENATED MODULE: ./system-addon/content-src/components/TopSites/TopSites.jsx
+
+
+
+
+
+
+
+
+
 
 /**
- * List of functions that return items that can be included as menu options in a
- * LinkMenu. All functions take the site as the first parameter, and optionally
- * the index of the site.
+ * Iterates through TopSites and counts types of images.
+ * @param acc Accumulator for reducer.
+ * @param topsite Entry in TopSites.
  */
-module.exports = {
-  Separator: () => ({ type: "separator" }),
-  RemoveBookmark: site => ({
-    id: "menu_action_remove_bookmark",
-    icon: "bookmark-added",
-    action: ac.SendToMain({
-      type: at.DELETE_BOOKMARK_BY_ID,
-      data: site.bookmarkGuid
-    }),
-    userEvent: "BOOKMARK_DELETE"
-  }),
-  AddBookmark: site => ({
-    id: "menu_action_bookmark",
-    icon: "bookmark-hollow",
-    action: ac.SendToMain({
-      type: at.BOOKMARK_URL,
-      data: { url: site.url, title: site.title, type: site.type }
-    }),
-    userEvent: "BOOKMARK_ADD"
-  }),
-  OpenInNewWindow: site => ({
-    id: "menu_action_open_new_window",
-    icon: "new-window",
-    action: ac.SendToMain({
-      type: at.OPEN_NEW_WINDOW,
-      data: { url: site.url, referrer: site.referrer }
-    }),
-    userEvent: "OPEN_NEW_WINDOW"
-  }),
-  OpenInPrivateWindow: site => ({
-    id: "menu_action_open_private_window",
-    icon: "new-window-private",
-    action: ac.SendToMain({
-      type: at.OPEN_PRIVATE_WINDOW,
-      data: { url: site.url, referrer: site.referrer }
-    }),
-    userEvent: "OPEN_PRIVATE_WINDOW"
-  }),
-  BlockUrl: (site, index, eventSource) => ({
-    id: "menu_action_dismiss",
-    icon: "dismiss",
-    action: ac.SendToMain({
-      type: at.BLOCK_URL,
-      data: site.url
-    }),
-    impression: ac.ImpressionStats({
-      source: eventSource,
-      block: 0,
-      tiles: [{ id: site.guid, pos: index }]
-    }),
-    userEvent: "BLOCK"
-  }),
-  DeleteUrl: site => ({
-    id: "menu_action_delete",
-    icon: "delete",
-    action: {
-      type: at.DIALOG_OPEN,
-      data: {
-        onConfirm: [ac.SendToMain({ type: at.DELETE_HISTORY_URL, data: { url: site.url, forceBlock: site.bookmarkGuid } }), ac.UserEvent({ event: "DELETE" })],
-        body_string_id: ["confirm_history_delete_p1", "confirm_history_delete_notice_p2"],
-        confirm_button_string_id: "menu_action_delete",
-        cancel_button_string_id: "topsites_form_cancel_button",
-        icon: "modal-delete"
-      }
-    },
-    userEvent: "DIALOG_OPEN"
-  }),
-  PinTopSite: (site, index) => ({
-    id: "menu_action_pin",
-    icon: "pin",
-    action: ac.SendToMain({
-      type: at.TOP_SITES_PIN,
-      data: { site: { url: site.url }, index }
-    }),
-    userEvent: "PIN"
-  }),
-  UnpinTopSite: site => ({
-    id: "menu_action_unpin",
-    icon: "unpin",
-    action: ac.SendToMain({
-      type: at.TOP_SITES_UNPIN,
-      data: { site: { url: site.url } }
-    }),
-    userEvent: "UNPIN"
-  }),
-  SaveToPocket: (site, index, eventSource) => ({
-    id: "menu_action_save_to_pocket",
-    icon: "pocket",
-    action: ac.SendToMain({
-      type: at.SAVE_TO_POCKET,
-      data: { site: { url: site.url, title: site.title } }
-    }),
-    impression: ac.ImpressionStats({
-      source: eventSource,
-      pocket: 0,
-      tiles: [{ id: site.guid, pos: index }]
-    }),
-    userEvent: "SAVE_TO_POCKET"
-  }),
-  EditTopSite: site => ({
-    id: "edit_topsites_button_text",
-    icon: "edit",
-    action: {
-      type: at.TOP_SITES_EDIT,
-      data: { url: site.url, label: site.label }
+function countTopSitesIconsTypes(topSites) {
+  const countTopSitesTypes = (acc, link) => {
+    if (link.tippyTopIcon || link.faviconRef === "tippytop") {
+      acc.tippytop++;
+    } else if (link.faviconSize >= MIN_RICH_FAVICON_SIZE) {
+      acc.rich_icon++;
+    } else if (link.screenshot && link.faviconSize >= MIN_CORNER_FAVICON_SIZE) {
+      acc.screenshot_with_icon++;
+    } else if (link.screenshot) {
+      acc.screenshot++;
+    } else {
+      acc.no_image++;
     }
-  })
-};
-
-module.exports.CheckBookmark = site => site.bookmarkGuid ? module.exports.RemoveBookmark(site) : module.exports.AddBookmark(site);
-module.exports.CheckPinTopSite = (site, index) => site.isPinned ? module.exports.UnpinTopSite(site) : module.exports.PinTopSite(site, index);
+
+    return acc;
+  };
+
+  return topSites.reduce(countTopSitesTypes, {
+    "screenshot_with_icon": 0,
+    "screenshot": 0,
+    "tippytop": 0,
+    "rich_icon": 0,
+    "no_image": 0
+  });
+}
+
+class TopSites__TopSites extends external__React__default.a.PureComponent {
+  /**
+   * Dispatch session statistics about the quality of TopSites icons and pinned count.
+   */
+  _dispatchTopSitesStats() {
+    const topSites = this._getTopSites();
+    const topSitesIconsStats = countTopSitesIconsTypes(topSites);
+    const topSitesPinned = topSites.filter(site => !!site.isPinned).length;
+    // Dispatch telemetry event with the count of TopSites images types.
+    this.props.dispatch(Actions["actionCreators"].SendToMain({
+      type: Actions["actionTypes"].SAVE_SESSION_PERF_DATA,
+      data: { topsites_icon_stats: topSitesIconsStats, topsites_pinned: topSitesPinned }
+    }));
+  }
+
+  /**
+   * Return the TopSites to display based on prefs.
+   */
+  _getTopSites() {
+    return this.props.TopSites.rows.slice(0, this.props.TopSitesCount);
+  }
+
+  componentDidUpdate() {
+    this._dispatchTopSitesStats();
+  }
+
+  componentDidMount() {
+    this._dispatchTopSitesStats();
+  }
+
+  render() {
+    const props = this.props;
+    const infoOption = {
+      header: { id: "settings_pane_topsites_header" },
+      body: { id: "settings_pane_topsites_body" }
+    };
+    return external__React__default.a.createElement(
+      ComponentPerfTimer["a" /* ComponentPerfTimer */],
+      { id: "topsites", initialized: props.TopSites.initialized, dispatch: props.dispatch },
+      external__React__default.a.createElement(
+        CollapsibleSection["a" /* CollapsibleSection */],
+        { className: "top-sites", icon: "topsites", title: external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "header_top_sites" }), infoOption: infoOption, prefName: "collapseTopSites", Prefs: props.Prefs, dispatch: props.dispatch },
+        external__React__default.a.createElement(TopSiteList, { TopSites: props.TopSites, TopSitesCount: props.TopSitesCount, dispatch: props.dispatch, intl: props.intl }),
+        external__React__default.a.createElement(TopSitesEdit, props)
+      )
+    );
+  }
+}
+
+const TopSites = Object(external__ReactRedux_["connect"])(state => ({
+  TopSites: state.TopSites,
+  Prefs: state.Prefs,
+  TopSitesCount: state.Prefs.values.topSitesCount
+}))(TopSites__TopSites);
+// CONCATENATED MODULE: ./system-addon/content-src/components/Base/Base.jsx
+
+
+
+
+
+
+
+
+
+
+
+
+// Add the locale data for pluralization and relative-time formatting for now,
+// this just uses english locale data. We can make this more sophisticated if
+// more features are needed.
+function addLocaleDataForReactIntl(locale) {
+  Object(external__ReactIntl_["addLocaleData"])([{ locale, parentLocale: "en" }]);
+}
+
+class Base__Base extends external__React__default.a.PureComponent {
+  componentWillMount() {
+    const { App, locale } = this.props;
+    this.sendNewTabRehydrated(App);
+    addLocaleDataForReactIntl(locale);
+  }
+
+  componentDidMount() {
+    // Request state AFTER the first render to ensure we don't cause the
+    // prerendered DOM to be unmounted. Otherwise, NEW_TAB_STATE_REQUEST is
+    // dispatched right after the store is ready.
+    if (this.props.isPrerendered) {
+      this.props.dispatch(Actions["actionCreators"].SendToMain({ type: Actions["actionTypes"].NEW_TAB_STATE_REQUEST }));
+      this.props.dispatch(Actions["actionCreators"].SendToMain({ type: Actions["actionTypes"].PAGE_PRERENDERED }));
+    }
+  }
+
+  componentWillUpdate({ App }) {
+    this.sendNewTabRehydrated(App);
+  }
+
+  // The NEW_TAB_REHYDRATED event is used to inform feeds that their
+  // data has been consumed e.g. for counting the number of tabs that
+  // have rendered that data.
+  sendNewTabRehydrated(App) {
+    if (App && App.initialized && !this.renderNotified) {
+      this.props.dispatch(Actions["actionCreators"].SendToMain({ type: Actions["actionTypes"].NEW_TAB_REHYDRATED, data: {} }));
+      this.renderNotified = true;
+    }
+  }
+
+  render() {
+    const props = this.props;
+    const { App, locale, strings } = props;
+    const { initialized } = App;
+    const prefs = props.Prefs.values;
+
+    const shouldBeFixedToTop = PrerenderData["PrerenderData"].arePrefsValid(name => prefs[name]);
+
+    const outerClassName = `outer-wrapper${shouldBeFixedToTop ? " fixed-to-top" : ""}`;
+
+    if (!props.isPrerendered && !initialized) {
+      return null;
+    }
+
+    return external__React__default.a.createElement(
+      external__ReactIntl_["IntlProvider"],
+      { locale: locale, messages: strings },
+      external__React__default.a.createElement(
+        "div",
+        { className: outerClassName },
+        external__React__default.a.createElement(
+          "main",
+          null,
+          prefs.showSearch && external__React__default.a.createElement(Search, null),
+          external__React__default.a.createElement(
+            "div",
+            { className: `body-wrapper${initialized ? " on" : ""}` },
+            !prefs.migrationExpired && external__React__default.a.createElement(ManualMigration, null),
+            prefs.showTopSites && external__React__default.a.createElement(TopSites, null),
+            external__React__default.a.createElement(Sections["a" /* Sections */], null)
+          ),
+          external__React__default.a.createElement(ConfirmDialog, null)
+        ),
+        initialized && external__React__default.a.createElement(PreferencesPane, null)
+      )
+    );
+  }
+}
+/* unused harmony export _Base */
+
+
+const Base = Object(external__ReactRedux_["connect"])(state => ({ App: state.App, Prefs: state.Prefs }))(Base__Base);
+/* harmony export (immutable) */ __webpack_exports__["a"] = Base;
+
 
 /***/ }),
-/* 20 */
+/* 13 */
 /***/ (function(module, exports) {
 
 var Dedupe = class Dedupe {
   constructor(createKey) {
     this.createKey = createKey || this.defaultCreateKey;
   }
 
   defaultCreateKey(item) {
@@ -2400,530 +3280,166 @@ var Dedupe = class Dedupe {
     return result.map(m => Array.from(m.values()));
   }
 };
 module.exports = {
   Dedupe
 };
 
 /***/ }),
-/* 21 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-/* globals ContentSearchUIController */
-
-
-const React = __webpack_require__(1);
-const { connect } = __webpack_require__(3);
-const { FormattedMessage, injectIntl } = __webpack_require__(2);
-const { actionCreators: ac } = __webpack_require__(0);
-const { IS_NEWTAB } = __webpack_require__(22);
-
-class Search extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.onClick = this.onClick.bind(this);
-    this.onInputMount = this.onInputMount.bind(this);
+/* 14 */
+/***/ (function(module, exports) {
+
+class _PrerenderData {
+  constructor(options) {
+    this.initialPrefs = options.initialPrefs;
+    this.initialSections = options.initialSections;
+    this._setValidation(options.validation);
   }
 
-  handleEvent(event) {
-    // Also track search events with our own telemetry
-    if (event.detail.type === "Search") {
-      this.props.dispatch(ac.UserEvent({ event: "SEARCH" }));
-    }
+  get validation() {
+    return this._validation;
   }
-  onClick(event) {
-    window.gContentSearchController.search(event);
+
+  set validation(value) {
+    this._setValidation(value);
   }
-  componentWillUnmount() {
-    delete window.gContentSearchController;
+
+  get invalidatingPrefs() {
+    return this._invalidatingPrefs;
   }
-  onInputMount(input) {
-    if (input) {
-      // The "healthReportKey" and needs to be "newtab" or "abouthome" so that
-      // BrowserUsageTelemetry.jsm knows to handle events with this name, and
-      // can add the appropriate telemetry probes for search. Without the correct
-      // name, certain tests like browser_UsageTelemetry_content.js will fail
-      // (See github ticket #2348 for more details)
-      const healthReportKey = IS_NEWTAB ? "newtab" : "abouthome";
-
-      // The "searchSource" needs to be "newtab" or "homepage" and is sent with
-      // the search data and acts as context for the search request (See
-      // nsISearchEngine.getSubmission). It is necessary so that search engine
-      // plugins can correctly atribute referrals. (See github ticket #3321 for
-      // more details)
-      const searchSource = IS_NEWTAB ? "newtab" : "homepage";
-
-      // gContentSearchController needs to exist as a global so that tests for
-      // the existing about:home can find it; and so it allows these tests to pass.
-      // In the future, when activity stream is default about:home, this can be renamed
-      window.gContentSearchController = new ContentSearchUIController(input, input.parentNode, healthReportKey, searchSource);
-      addEventListener("ContentSearchClient", this);
-    } else {
-      window.gContentSearchController = null;
-      removeEventListener("ContentSearchClient", this);
-    }
+
+  // This is needed so we can use it in the constructor
+  _setValidation(value = []) {
+    this._validation = value;
+    this._invalidatingPrefs = value.reduce((result, next) => {
+      if (typeof next === "string") {
+        result.push(next);
+        return result;
+      } else if (next && next.oneOf) {
+        return result.concat(next.oneOf);
+      }
+      throw new Error("Your validation configuration is not properly configured");
+    }, []);
   }
 
-  /*
-   * Do not change the ID on the input field, as legacy newtab code
-   * specifically looks for the id 'newtab-search-text' on input fields
-   * in order to execute searches in various tests
-   */
-  render() {
-    return React.createElement(
-      "div",
-      { className: "search-wrapper" },
-      React.createElement(
-        "label",
-        { htmlFor: "newtab-search-text", className: "search-label" },
-        React.createElement(
-          "span",
-          { className: "sr-only" },
-          React.createElement(FormattedMessage, { id: "search_web_placeholder" })
-        )
-      ),
-      React.createElement("input", {
-        id: "newtab-search-text",
-        maxLength: "256",
-        placeholder: this.props.intl.formatMessage({ id: "search_web_placeholder" }),
-        ref: this.onInputMount,
-        title: this.props.intl.formatMessage({ id: "search_web_placeholder" }),
-        type: "search" }),
-      React.createElement(
-        "button",
-        {
-          id: "searchSubmit",
-          className: "search-button",
-          onClick: this.onClick,
-          title: this.props.intl.formatMessage({ id: "search_button" }) },
-        React.createElement(
-          "span",
-          { className: "sr-only" },
-          React.createElement(FormattedMessage, { id: "search_button" })
-        )
-      )
-    );
+  arePrefsValid(getPref) {
+    for (const prefs of this.validation) {
+      // {oneOf: ["foo", "bar"]}
+      if (prefs && prefs.oneOf && !prefs.oneOf.some(name => getPref(name) === this.initialPrefs[name])) {
+        return false;
+
+        // "foo"
+      } else if (getPref(prefs) !== this.initialPrefs[prefs]) {
+        return false;
+      }
+    }
+    return true;
   }
 }
 
-module.exports = connect()(injectIntl(Search));
-module.exports._unconnected = Search;
-
-/***/ }),
-/* 22 */
-/***/ (function(module, exports, __webpack_require__) {
-
-/* WEBPACK VAR INJECTION */(function(global) {module.exports = {
-  // constant to know if the page is about:newtab or about:home
-  IS_NEWTAB: global.document && global.document.documentURI === "about:newtab"
+var PrerenderData = new _PrerenderData({
+  initialPrefs: {
+    "migrationExpired": true,
+    "showTopSites": true,
+    "showSearch": true,
+    "topSitesCount": 6,
+    "collapseTopSites": false,
+    "section.highlights.collapsed": false,
+    "section.topstories.collapsed": false,
+    "feeds.section.topstories": true,
+    "feeds.section.highlights": true
+  },
+  // Prefs listed as invalidating will prevent the prerendered version
+  // of AS from being used if their value is something other than what is listed
+  // here. This is required because some preferences cause the page layout to be
+  // too different for the prerendered version to be used. Unfortunately, this
+  // will result in users who have modified some of their preferences not being
+  // able to get the benefits of prerendering.
+  validation: ["showTopSites", "showSearch", "collapseTopSites", "section.highlights.collapsed", "section.topstories.collapsed",
+  // This means if either of these are set to their default values,
+  // prerendering can be used.
+  { oneOf: ["feeds.section.topstories", "feeds.section.highlights"] }],
+  initialSections: [{
+    enabled: true,
+    icon: "pocket",
+    id: "topstories",
+    order: 1,
+    title: { id: "header_recommended_by", values: { provider: "Pocket" } }
+  }, {
+    enabled: true,
+    id: "highlights",
+    icon: "highlights",
+    order: 2,
+    title: { id: "header_highlights" }
+  }]
+});
+module.exports = {
+  PrerenderData,
+  _PrerenderData
 };
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
-
-/***/ }),
-/* 23 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const React = __webpack_require__(1);
-const { connect } = __webpack_require__(3);
-const { FormattedMessage } = __webpack_require__(2);
-const { actionTypes, actionCreators: ac } = __webpack_require__(0);
-
-/**
- * ConfirmDialog component.
- * One primary action button, one cancel button.
- *
- * Content displayed is controlled by `data` prop the component receives.
- * Example:
- * data: {
- *   // Any sort of data needed to be passed around by actions.
- *   payload: site.url,
- *   // Primary button SendToMain action.
- *   action: "DELETE_HISTORY_URL",
- *   // Primary button USerEvent action.
- *   userEvent: "DELETE",
- *   // Array of locale ids to display.
- *   message_body: ["confirm_history_delete_p1", "confirm_history_delete_notice_p2"],
- *   // Text for primary button.
- *   confirm_button_string_id: "menu_action_delete"
- * },
- */
-class ConfirmDialog extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this._handleCancelBtn = this._handleCancelBtn.bind(this);
-    this._handleConfirmBtn = this._handleConfirmBtn.bind(this);
-  }
-
-  _handleCancelBtn() {
-    this.props.dispatch({ type: actionTypes.DIALOG_CANCEL });
-    this.props.dispatch(ac.UserEvent({ event: actionTypes.DIALOG_CANCEL }));
-  }
-
-  _handleConfirmBtn() {
-    this.props.data.onConfirm.forEach(this.props.dispatch);
-  }
-
-  _renderModalMessage() {
-    const message_body = this.props.data.body_string_id;
-
-    if (!message_body) {
-      return null;
-    }
-
-    return React.createElement(
-      "span",
-      null,
-      message_body.map(msg => React.createElement(
-        "p",
-        { key: msg },
-        React.createElement(FormattedMessage, { id: msg })
-      ))
-    );
-  }
-
-  render() {
-    if (!this.props.visible) {
-      return null;
-    }
-
-    return React.createElement(
-      "div",
-      { className: "confirmation-dialog" },
-      React.createElement("div", { className: "modal-overlay", onClick: this._handleCancelBtn }),
-      React.createElement(
-        "div",
-        { className: "modal" },
-        React.createElement(
-          "section",
-          { className: "modal-message" },
-          this.props.data.icon && React.createElement("span", { className: `icon icon-spacer icon-${this.props.data.icon}` }),
-          this._renderModalMessage()
-        ),
-        React.createElement(
-          "section",
-          { className: "actions" },
-          React.createElement(
-            "button",
-            { onClick: this._handleCancelBtn },
-            React.createElement(FormattedMessage, { id: this.props.data.cancel_button_string_id })
-          ),
-          React.createElement(
-            "button",
-            { className: "done", onClick: this._handleConfirmBtn },
-            React.createElement(FormattedMessage, { id: this.props.data.confirm_button_string_id })
-          )
-        )
-      )
-    );
-  }
-}
-
-module.exports = connect(state => state.Dialog)(ConfirmDialog);
-module.exports._unconnected = ConfirmDialog;
-module.exports.Dialog = ConfirmDialog;
 
 /***/ }),
-/* 24 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const React = __webpack_require__(1);
-const { connect } = __webpack_require__(3);
-const { FormattedMessage } = __webpack_require__(2);
-const { actionTypes: at, actionCreators: ac } = __webpack_require__(0);
-
-/**
- * Manual migration component used to start the profile import wizard.
- * Message is presented temporarily and will go away if:
- * 1.  User clicks "No Thanks"
- * 2.  User completed the data import
- * 3.  After 3 active days
- * 4.  User clicks "Cancel" on the import wizard (currently not implemented).
- */
-class ManualMigration extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.onLaunchTour = this.onLaunchTour.bind(this);
-    this.onCancelTour = this.onCancelTour.bind(this);
-  }
-  onLaunchTour() {
-    this.props.dispatch(ac.SendToMain({ type: at.MIGRATION_START }));
-    this.props.dispatch(ac.UserEvent({ event: at.MIGRATION_START }));
-  }
-
-  onCancelTour() {
-    this.props.dispatch(ac.SendToMain({ type: at.MIGRATION_CANCEL }));
-    this.props.dispatch(ac.UserEvent({ event: at.MIGRATION_CANCEL }));
-  }
-
-  render() {
-    return React.createElement(
-      "div",
-      { className: "manual-migration-container" },
-      React.createElement(
-        "p",
-        null,
-        React.createElement("span", { className: "icon icon-import" }),
-        React.createElement(FormattedMessage, { id: "manual_migration_explanation2" })
-      ),
-      React.createElement(
-        "div",
-        { className: "manual-migration-actions actions" },
-        React.createElement(
-          "button",
-          { className: "dismiss", onClick: this.onCancelTour },
-          React.createElement(FormattedMessage, { id: "manual_migration_cancel_button" })
-        ),
-        React.createElement(
-          "button",
-          { onClick: this.onLaunchTour },
-          React.createElement(FormattedMessage, { id: "manual_migration_import_button" })
-        )
-      )
-    );
-  }
-}
-
-module.exports = connect()(ManualMigration);
-module.exports._unconnected = ManualMigration;
+/* 15 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+/* WEBPACK VAR INJECTION */(function(global) {const IS_NEWTAB = global.document && global.document.documentURI === "about:newtab";
+/* harmony export (immutable) */ __webpack_exports__["a"] = IS_NEWTAB;
+
+/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(4)))
 
 /***/ }),
-/* 25 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const React = __webpack_require__(1);
-const { connect } = __webpack_require__(3);
-const { injectIntl, FormattedMessage } = __webpack_require__(2);
-const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
-const { TOP_SITES_DEFAULT_LENGTH, TOP_SITES_SHOWMORE_LENGTH } = __webpack_require__(6);
-
-const getFormattedMessage = message => typeof message === "string" ? React.createElement(
-  "span",
-  null,
-  message
-) : React.createElement(FormattedMessage, message);
-
-const PreferencesInput = props => React.createElement(
-  "section",
-  null,
-  React.createElement("input", { type: "checkbox", id: props.prefName, name: props.prefName, checked: props.value, disabled: props.disabled, onChange: props.onChange, className: props.className }),
-  React.createElement(
-    "label",
-    { htmlFor: props.prefName, className: props.labelClassName },
-    getFormattedMessage(props.titleString)
-  ),
-  props.descString && React.createElement(
-    "p",
-    { className: "prefs-input-description" },
-    getFormattedMessage(props.descString)
-  ),
-  React.Children.map(props.children, child => React.createElement(
-    "div",
-    { className: `options${child.props.disabled ? " disabled" : ""}` },
-    child
-  ))
-);
-
-class PreferencesPane extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.handleClickOutside = this.handleClickOutside.bind(this);
-    this.handlePrefChange = this.handlePrefChange.bind(this);
-    this.handleSectionChange = this.handleSectionChange.bind(this);
-    this.togglePane = this.togglePane.bind(this);
-    this.onWrapperMount = this.onWrapperMount.bind(this);
-  }
-  componentDidUpdate(prevProps, prevState) {
-    if (prevProps.PreferencesPane.visible !== this.props.PreferencesPane.visible) {
-      // While the sidebar is open, listen for all document clicks.
-      if (this.isSidebarOpen()) {
-        document.addEventListener("click", this.handleClickOutside);
-      } else {
-        document.removeEventListener("click", this.handleClickOutside);
-      }
-    }
-  }
-  isSidebarOpen() {
-    return this.props.PreferencesPane.visible;
-  }
-  handleClickOutside(event) {
-    // if we are showing the sidebar and there is a click outside, close it.
-    if (this.isSidebarOpen() && !this.wrapper.contains(event.target)) {
-      this.togglePane();
-    }
-  }
-  handlePrefChange(event) {
-    const target = event.target;
-    const { name, checked } = target;
-    let value = checked;
-    if (name === "topSitesCount") {
-      value = checked ? TOP_SITES_SHOWMORE_LENGTH : TOP_SITES_DEFAULT_LENGTH;
-    }
-    this.props.dispatch(ac.SetPref(name, value));
-  }
-  handleSectionChange(event) {
-    const target = event.target;
-    const id = target.name;
-    const type = target.checked ? at.SECTION_ENABLE : at.SECTION_DISABLE;
-    this.props.dispatch(ac.SendToMain({ type, data: id }));
-  }
-  togglePane() {
-    if (this.isSidebarOpen()) {
-      this.props.dispatch({ type: at.SETTINGS_CLOSE });
-      this.props.dispatch(ac.UserEvent({ event: "CLOSE_NEWTAB_PREFS" }));
-    } else {
-      this.props.dispatch({ type: at.SETTINGS_OPEN });
-      this.props.dispatch(ac.UserEvent({ event: "OPEN_NEWTAB_PREFS" }));
-    }
-  }
-  onWrapperMount(wrapper) {
-    this.wrapper = wrapper;
-  }
-  render() {
-    const props = this.props;
-    const prefs = props.Prefs.values;
-    const sections = props.Sections;
-    const isVisible = this.isSidebarOpen();
-    return React.createElement(
-      "div",
-      { className: "prefs-pane-wrapper", ref: this.onWrapperMount },
-      React.createElement(
-        "div",
-        { className: "prefs-pane-button" },
-        React.createElement("button", {
-          className: `prefs-button icon ${isVisible ? "icon-dismiss" : "icon-settings"}`,
-          title: props.intl.formatMessage({ id: isVisible ? "settings_pane_done_button" : "settings_pane_button_label" }),
-          onClick: this.togglePane })
-      ),
-      React.createElement(
-        "div",
-        { className: "prefs-pane" },
-        React.createElement(
-          "div",
-          { className: `sidebar ${isVisible ? "" : "hidden"}` },
-          React.createElement(
-            "div",
-            { className: "prefs-modal-inner-wrapper" },
-            React.createElement(
-              "h1",
-              null,
-              React.createElement(FormattedMessage, { id: "settings_pane_header" })
-            ),
-            React.createElement(
-              "p",
-              null,
-              React.createElement(FormattedMessage, { id: "settings_pane_body2" })
-            ),
-            React.createElement(PreferencesInput, {
-              className: "showSearch",
-              prefName: "showSearch",
-              value: prefs.showSearch,
-              onChange: this.handlePrefChange,
-              titleString: { id: "settings_pane_search_header" },
-              descString: { id: "settings_pane_search_body" } }),
-            React.createElement("hr", null),
-            React.createElement(
-              PreferencesInput,
-              {
-                className: "showTopSites",
-                prefName: "showTopSites",
-                value: prefs.showTopSites,
-                onChange: this.handlePrefChange,
-                titleString: { id: "settings_pane_topsites_header" },
-                descString: { id: "settings_pane_topsites_body" } },
-              React.createElement(PreferencesInput, {
-                className: "showMoreTopSites",
-                prefName: "topSitesCount",
-                disabled: !prefs.showTopSites,
-                value: prefs.topSitesCount !== TOP_SITES_DEFAULT_LENGTH,
-                onChange: this.handlePrefChange,
-                titleString: { id: "settings_pane_topsites_options_showmore" },
-                labelClassName: "icon icon-topsites" })
-            ),
-            sections.filter(section => !section.shouldHidePref).map(({ id, title, enabled, pref }) => React.createElement(
-              PreferencesInput,
-              {
-                key: id,
-                className: "showSection",
-                prefName: pref && pref.feed || id,
-                value: enabled,
-                onChange: pref && pref.feed ? this.handlePrefChange : this.handleSectionChange,
-                titleString: pref && pref.titleString || title,
-                descString: pref && pref.descString },
-              pref.nestedPrefs && pref.nestedPrefs.map(nestedPref => React.createElement(PreferencesInput, {
-                key: nestedPref.name,
-                prefName: nestedPref.name,
-                disabled: !enabled,
-                value: prefs[nestedPref.name],
-                onChange: this.handlePrefChange,
-                titleString: nestedPref.titleString,
-                labelClassName: `icon ${nestedPref.icon}` }))
-            )),
-            !prefs.disableSnippets && React.createElement("hr", null),
-            !prefs.disableSnippets && React.createElement(PreferencesInput, { className: "showSnippets", prefName: "feeds.snippets",
-              value: prefs["feeds.snippets"], onChange: this.handlePrefChange,
-              titleString: { id: "settings_pane_snippets_header" },
-              descString: { id: "settings_pane_snippets_body" } })
-          ),
-          React.createElement(
-            "section",
-            { className: "actions" },
-            React.createElement(
-              "button",
-              { className: "done", onClick: this.togglePane },
-              React.createElement(FormattedMessage, { id: "settings_pane_done_button" })
-            )
-          )
-        )
-      )
-    );
-  }
-}
-
-module.exports = connect(state => ({ Prefs: state.Prefs, PreferencesPane: state.PreferencesPane, Sections: state.Sections }))(injectIntl(PreferencesPane));
-module.exports.PreferencesPane = PreferencesPane;
-module.exports.PreferencesInput = PreferencesInput;
-
-/***/ }),
-/* 26 */
-/***/ (function(module, exports, __webpack_require__) {
-
-/* WEBPACK VAR INJECTION */(function(global) {var _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; };
-
-const React = __webpack_require__(1);
-const { connect } = __webpack_require__(3);
-const { injectIntl, FormattedMessage } = __webpack_require__(2);
-const Card = __webpack_require__(27);
-const { PlaceholderCard } = Card;
-const Topics = __webpack_require__(29);
-const { actionCreators: ac } = __webpack_require__(0);
-const CollapsibleSection = __webpack_require__(9);
-const ComponentPerfTimer = __webpack_require__(10);
+/* 16 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+/* WEBPACK VAR INJECTION */(function(global) {/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_content_src_components_Card_Card__ = __webpack_require__(17);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_react_intl__ = __webpack_require__(2);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_react_intl___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_react_intl__);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_common_Actions_jsm__ = __webpack_require__(0);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_common_Actions_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_common_Actions_jsm__);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3_content_src_components_CollapsibleSection_CollapsibleSection__ = __webpack_require__(7);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4_content_src_components_ComponentPerfTimer_ComponentPerfTimer__ = __webpack_require__(8);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5_react_redux__ = __webpack_require__(3);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5_react_redux___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_5_react_redux__);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_react__ = __webpack_require__(1);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_react___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6_react__);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7_content_src_components_Topics_Topics__ = __webpack_require__(18);
+var _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; };
+
+
+
+
+
+
+
+
+
 
 const VISIBLE = "visible";
 const VISIBILITY_CHANGE_EVENT = "visibilitychange";
 const CARDS_PER_ROW = 3;
 
 function getFormattedMessage(message) {
-  return typeof message === "string" ? React.createElement(
+  return typeof message === "string" ? __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(
     "span",
     null,
     message
-  ) : React.createElement(FormattedMessage, message);
+  ) : __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_1_react_intl__["FormattedMessage"], message);
 }
 
-class Section extends React.PureComponent {
+class Section extends __WEBPACK_IMPORTED_MODULE_6_react___default.a.PureComponent {
   _dispatchImpressionStats() {
     const { props } = this;
     const maxCards = 3 * props.maxRows;
     const cards = props.rows.slice(0, maxCards);
 
     if (this.needsImpressionStats(cards)) {
-      props.dispatch(ac.ImpressionStats({
+      props.dispatch(__WEBPACK_IMPORTED_MODULE_2_common_Actions_jsm__["actionCreators"].ImpressionStats({
         source: props.eventSource,
         tiles: cards.map(link => ({ id: link.guid }))
       }));
       this.impressionCardGuids = cards.map(link => link.guid);
     }
   }
 
   // This sends an event when a user sees a set of new content. If content
@@ -3027,105 +3543,147 @@ class Section extends React.PureComponen
     const placeholders = this.numberOfPlaceholders(realRows.length);
 
     // The empty state should only be shown after we have initialized and there is no content.
     // Otherwise, we should show placeholders.
     const shouldShowEmptyState = initialized && !rows.length;
 
     // <Section> <-- React component
     // <section> <-- HTML5 element
-    return React.createElement(
-      ComponentPerfTimer,
+    return __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(
+      __WEBPACK_IMPORTED_MODULE_4_content_src_components_ComponentPerfTimer_ComponentPerfTimer__["a" /* ComponentPerfTimer */],
       this.props,
-      React.createElement(
-        CollapsibleSection,
+      __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(
+        __WEBPACK_IMPORTED_MODULE_3_content_src_components_CollapsibleSection_CollapsibleSection__["a" /* CollapsibleSection */],
         { className: "section", icon: icon, title: getFormattedMessage(title),
           infoOption: infoOption,
           id: id,
           eventSource: eventSource,
           disclaimer: disclaimer,
           prefName: `section.${id}.collapsed`,
           Prefs: this.props.Prefs,
           dispatch: this.props.dispatch },
-        !shouldShowEmptyState && React.createElement(
+        !shouldShowEmptyState && __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(
           "ul",
           { className: "section-list", style: { padding: 0 } },
-          realRows.map((link, index) => link && React.createElement(Card, { key: index, index: index, dispatch: dispatch, link: link, contextMenuOptions: contextMenuOptions,
+          realRows.map((link, index) => link && __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_0_content_src_components_Card_Card__["a" /* Card */], { key: index, index: index, dispatch: dispatch, link: link, contextMenuOptions: contextMenuOptions,
             eventSource: eventSource, shouldSendImpressionStats: this.props.shouldSendImpressionStats })),
-          placeholders > 0 && [...new Array(placeholders)].map((_, i) => React.createElement(PlaceholderCard, { key: i }))
+          placeholders > 0 && [...new Array(placeholders)].map((_, i) => __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_0_content_src_components_Card_Card__["b" /* PlaceholderCard */], { key: i }))
         ),
-        shouldShowEmptyState && React.createElement(
+        shouldShowEmptyState && __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(
           "div",
           { className: "section-empty-state" },
-          React.createElement(
+          __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(
             "div",
             { className: "empty-state" },
-            emptyState.icon && emptyState.icon.startsWith("moz-extension://") ? React.createElement("img", { className: "empty-state-icon icon", style: { "background-image": `url('${emptyState.icon}')` } }) : React.createElement("img", { className: `empty-state-icon icon icon-${emptyState.icon}` }),
-            React.createElement(
+            emptyState.icon && emptyState.icon.startsWith("moz-extension://") ? __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement("img", { className: "empty-state-icon icon", style: { "background-image": `url('${emptyState.icon}')` } }) : __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement("img", { className: `empty-state-icon icon icon-${emptyState.icon}` }),
+            __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(
               "p",
               { className: "empty-state-message" },
               getFormattedMessage(emptyState.message)
             )
           )
         ),
-        shouldShowTopics && React.createElement(Topics, { topics: this.props.topics, read_more_endpoint: this.props.read_more_endpoint })
+        shouldShowTopics && __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_7_content_src_components_Topics_Topics__["a" /* Topics */], { topics: this.props.topics, read_more_endpoint: this.props.read_more_endpoint })
       )
     );
   }
 }
+/* unused harmony export Section */
+
 
 Section.defaultProps = {
   document: global.document,
   rows: [],
   emptyState: {},
   title: ""
 };
 
-const SectionIntl = injectIntl(Section);
-
-class Sections extends React.PureComponent {
+const SectionIntl = Object(__WEBPACK_IMPORTED_MODULE_1_react_intl__["injectIntl"])(Section);
+/* unused harmony export SectionIntl */
+
+
+class _Sections extends __WEBPACK_IMPORTED_MODULE_6_react___default.a.PureComponent {
   render() {
     const sections = this.props.Sections;
-    return React.createElement(
+    return __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(
       "div",
       { className: "sections-list" },
-      sections.filter(section => section.enabled).map(section => React.createElement(SectionIntl, _extends({ key: section.id }, section, { Prefs: this.props.Prefs, dispatch: this.props.dispatch })))
+      sections.filter(section => section.enabled).map(section => __WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(SectionIntl, _extends({ key: section.id }, section, { Prefs: this.props.Prefs, dispatch: this.props.dispatch })))
     );
   }
 }
-
-module.exports = connect(state => ({ Sections: state.Sections, Prefs: state.Prefs }))(Sections);
-module.exports._unconnected = Sections;
-module.exports.SectionIntl = SectionIntl;
-module.exports._unconnectedSection = Section;
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
+/* unused harmony export _Sections */
+
+
+const Sections = Object(__WEBPACK_IMPORTED_MODULE_5_react_redux__["connect"])(state => ({ Sections: state.Sections, Prefs: state.Prefs }))(_Sections);
+/* harmony export (immutable) */ __webpack_exports__["a"] = Sections;
+
+/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(4)))
 
 /***/ }),
-/* 27 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const React = __webpack_require__(1);
-const LinkMenu = __webpack_require__(8);
-const { FormattedMessage } = __webpack_require__(2);
-const cardContextTypes = __webpack_require__(28);
-const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
+/* 17 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+
+// EXTERNAL MODULE: ./system-addon/common/Actions.jsm
+var Actions = __webpack_require__(0);
+var Actions_default = /*#__PURE__*/__webpack_require__.n(Actions);
+
+// CONCATENATED MODULE: ./system-addon/content-src/components/Card/types.js
+const cardContextTypes = {
+  history: {
+    intlID: "type_label_visited",
+    icon: "historyItem"
+  },
+  bookmark: {
+    intlID: "type_label_bookmarked",
+    icon: "bookmark-added"
+  },
+  trending: {
+    intlID: "type_label_recommended",
+    icon: "trending"
+  },
+  now: {
+    intlID: "type_label_now",
+    icon: "now"
+  }
+};
+// EXTERNAL MODULE: external "ReactIntl"
+var external__ReactIntl_ = __webpack_require__(2);
+var external__ReactIntl__default = /*#__PURE__*/__webpack_require__.n(external__ReactIntl_);
+
+// EXTERNAL MODULE: ./system-addon/content-src/components/LinkMenu/LinkMenu.jsx + 2 modules
+var LinkMenu = __webpack_require__(6);
+
+// EXTERNAL MODULE: external "React"
+var external__React_ = __webpack_require__(1);
+var external__React__default = /*#__PURE__*/__webpack_require__.n(external__React_);
+
+// CONCATENATED MODULE: ./system-addon/content-src/components/Card/Card.jsx
+
+
+
+
+
 
 // Keep track of pending image loads to only request once
 const gImageLoading = new Map();
 
 /**
  * Card component.
  * Cards are found within a Section component and contain information about a link such
  * as preview image, page title, page description, and some context about if the page
  * was visited, bookmarked, trending etc...
  * Each Section can make an unordered list of Cards which will create one instane of
  * this class. Each card will then get a context menu which reflects the actions that
  * can be done on this Card.
  */
-class Card extends React.PureComponent {
+class Card_Card extends external__React__default.a.PureComponent {
   constructor(props) {
     super(props);
     this.state = {
       activeCard: null,
       imageLoaded: false,
       showContextMenu: false
     };
     this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
@@ -3169,27 +3727,27 @@ class Card extends React.PureComponent {
     this.setState({
       activeCard: this.props.index,
       showContextMenu: true
     });
   }
   onLinkClick(event) {
     event.preventDefault();
     const { altKey, button, ctrlKey, metaKey, shiftKey } = event;
-    this.props.dispatch(ac.SendToMain({
-      type: at.OPEN_LINK,
+    this.props.dispatch(Actions["actionCreators"].SendToMain({
+      type: Actions["actionTypes"].OPEN_LINK,
       data: Object.assign(this.props.link, { event: { altKey, button, ctrlKey, metaKey, shiftKey } })
     }));
-    this.props.dispatch(ac.UserEvent({
+    this.props.dispatch(Actions["actionCreators"].UserEvent({
       event: "CLICK",
       source: this.props.eventSource,
       action_position: this.props.index
     }));
     if (this.props.shouldSendImpressionStats) {
-      this.props.dispatch(ac.ImpressionStats({
+      this.props.dispatch(Actions["actionCreators"].ImpressionStats({
         source: this.props.eventSource,
         click: 0,
         tiles: [{ id: this.props.link.guid, pos: this.props.index }]
       }));
     }
   }
   onMenuUpdate(showContextMenu) {
     this.setState({ showContextMenu });
@@ -3210,280 +3768,261 @@ class Card extends React.PureComponent {
     const { index, link, dispatch, contextMenuOptions, eventSource, shouldSendImpressionStats } = this.props;
     const { props } = this;
     const isContextMenuOpen = this.state.showContextMenu && this.state.activeCard === index;
     // Display "now" as "trending" until we have new strings #3402
     const { icon, intlID } = cardContextTypes[link.type === "now" ? "trending" : link.type] || {};
     const hasImage = link.image || link.hasImage;
     const imageStyle = { backgroundImage: link.image ? `url(${link.image})` : "none" };
 
-    return React.createElement(
+    return external__React__default.a.createElement(
       "li",
       { className: `card-outer${isContextMenuOpen ? " active" : ""}${props.placeholder ? " placeholder" : ""}` },
-      React.createElement(
+      external__React__default.a.createElement(
         "a",
         { href: link.url, onClick: !props.placeholder && this.onLinkClick },
-        React.createElement(
+        external__React__default.a.createElement(
           "div",
           { className: "card" },
-          hasImage && React.createElement(
+          hasImage && external__React__default.a.createElement(
             "div",
             { className: "card-preview-image-outer" },
-            React.createElement("div", { className: `card-preview-image${this.state.imageLoaded ? " loaded" : ""}`, style: imageStyle })
+            external__React__default.a.createElement("div", { className: `card-preview-image${this.state.imageLoaded ? " loaded" : ""}`, style: imageStyle })
           ),
-          React.createElement(
+          external__React__default.a.createElement(
             "div",
             { className: `card-details${hasImage ? "" : " no-image"}` },
-            link.hostname && React.createElement(
+            link.hostname && external__React__default.a.createElement(
               "div",
               { className: "card-host-name" },
               link.hostname
             ),
-            React.createElement(
+            external__React__default.a.createElement(
               "div",
               { className: ["card-text", icon ? "" : "no-context", link.description ? "" : "no-description", link.hostname ? "" : "no-host-name", hasImage ? "" : "no-image"].join(" ") },
-              React.createElement(
+              external__React__default.a.createElement(
                 "h4",
                 { className: "card-title", dir: "auto" },
                 link.title
               ),
-              React.createElement(
+              external__React__default.a.createElement(
                 "p",
                 { className: "card-description", dir: "auto" },
                 link.description
               )
             ),
-            React.createElement(
+            external__React__default.a.createElement(
               "div",
               { className: "card-context" },
-              icon && !link.context && React.createElement("span", { className: `card-context-icon icon icon-${icon}` }),
-              link.icon && link.context && React.createElement("span", { className: "card-context-icon icon", style: { backgroundImage: `url('${link.icon}')` } }),
-              intlID && !link.context && React.createElement(
+              icon && !link.context && external__React__default.a.createElement("span", { className: `card-context-icon icon icon-${icon}` }),
+              link.icon && link.context && external__React__default.a.createElement("span", { className: "card-context-icon icon", style: { backgroundImage: `url('${link.icon}')` } }),
+              intlID && !link.context && external__React__default.a.createElement(
                 "div",
                 { className: "card-context-label" },
-                React.createElement(FormattedMessage, { id: intlID, defaultMessage: "Visited" })
+                external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: intlID, defaultMessage: "Visited" })
               ),
-              link.context && React.createElement(
+              link.context && external__React__default.a.createElement(
                 "div",
                 { className: "card-context-label" },
                 link.context
               )
             )
           )
         )
       ),
-      !props.placeholder && React.createElement(
+      !props.placeholder && external__React__default.a.createElement(
         "button",
         { className: "context-menu-button icon",
           onClick: this.onMenuButtonClick },
-        React.createElement(
+        external__React__default.a.createElement(
           "span",
           { className: "sr-only" },
           `Open context menu for ${link.title}`
         )
       ),
-      !props.placeholder && React.createElement(LinkMenu, {
+      !props.placeholder && external__React__default.a.createElement(LinkMenu["a" /* LinkMenu */], {
         dispatch: dispatch,
         index: index,
         source: eventSource,
         onUpdate: this.onMenuUpdate,
         options: link.contextMenuOptions || contextMenuOptions,
         site: link,
         visible: isContextMenuOpen,
         shouldSendImpressionStats: shouldSendImpressionStats })
     );
   }
 }
-Card.defaultProps = { link: {} };
-
-const PlaceholderCard = () => React.createElement(Card, { placeholder: true });
-
-module.exports = Card;
-module.exports.PlaceholderCard = PlaceholderCard;
+/* harmony export (immutable) */ __webpack_exports__["a"] = Card_Card;
+
+Card_Card.defaultProps = { link: {} };
+
+const PlaceholderCard = () => external__React__default.a.createElement(Card_Card, { placeholder: true });
+/* harmony export (immutable) */ __webpack_exports__["b"] = PlaceholderCard;
+
 
 /***/ }),
-/* 28 */
-/***/ (function(module, exports) {
-
-module.exports = {
-  history: {
-    intlID: "type_label_visited",
-    icon: "historyItem"
-  },
-  bookmark: {
-    intlID: "type_label_bookmarked",
-    icon: "bookmark-added"
-  },
-  trending: {
-    intlID: "type_label_recommended",
-    icon: "trending"
-  },
-  now: {
-    intlID: "type_label_now",
-    icon: "now"
-  }
-};
-
-/***/ }),
-/* 29 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const React = __webpack_require__(1);
-const { FormattedMessage } = __webpack_require__(2);
-
-class Topic extends React.PureComponent {
+/* 18 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_react_intl__ = __webpack_require__(2);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_react_intl___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_react_intl__);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_react__ = __webpack_require__(1);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_react___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_react__);
+
+
+
+class Topic extends __WEBPACK_IMPORTED_MODULE_1_react___default.a.PureComponent {
   render() {
     const { url, name } = this.props;
-    return React.createElement(
+    return __WEBPACK_IMPORTED_MODULE_1_react___default.a.createElement(
       "li",
       null,
-      React.createElement(
+      __WEBPACK_IMPORTED_MODULE_1_react___default.a.createElement(
         "a",
         { key: name, className: "topic-link", href: url },
         name
       )
     );
   }
 }
-
-class Topics extends React.PureComponent {
+/* unused harmony export Topic */
+
+
+class Topics extends __WEBPACK_IMPORTED_MODULE_1_react___default.a.PureComponent {
   render() {
     const { topics, read_more_endpoint } = this.props;
-    return React.createElement(
+    return __WEBPACK_IMPORTED_MODULE_1_react___default.a.createElement(
       "div",
       { className: "topic" },
-      React.createElement(
+      __WEBPACK_IMPORTED_MODULE_1_react___default.a.createElement(
         "span",
         null,
-        React.createElement(FormattedMessage, { id: "pocket_read_more" })
+        __WEBPACK_IMPORTED_MODULE_1_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_0_react_intl__["FormattedMessage"], { id: "pocket_read_more" })
       ),
-      React.createElement(
+      __WEBPACK_IMPORTED_MODULE_1_react___default.a.createElement(
         "ul",
         null,
-        topics && topics.map(t => React.createElement(Topic, { key: t.name, url: t.url, name: t.name }))
+        topics && topics.map(t => __WEBPACK_IMPORTED_MODULE_1_react___default.a.createElement(Topic, { key: t.name, url: t.url, name: t.name }))
       ),
-      read_more_endpoint && React.createElement(
+      read_more_endpoint && __WEBPACK_IMPORTED_MODULE_1_react___default.a.createElement(
         "a",
         { className: "topic-read-more", href: read_more_endpoint },
-        React.createElement(FormattedMessage, { id: "pocket_read_even_more" })
+        __WEBPACK_IMPORTED_MODULE_1_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_0_react_intl__["FormattedMessage"], { id: "pocket_read_even_more" })
       )
     );
   }
 }
-
-module.exports = Topics;
-module.exports._unconnected = Topics;
-module.exports.Topic = Topic;
+/* harmony export (immutable) */ __webpack_exports__["a"] = Topics;
+
 
 /***/ }),
-/* 30 */
-/***/ (function(module, exports) {
-
-class _PrerenderData {
-  constructor(options) {
-    this.initialPrefs = options.initialPrefs;
-    this.initialSections = options.initialSections;
-    this._setValidation(options.validation);
-  }
-
-  get validation() {
-    return this._validation;
-  }
-
-  set validation(value) {
-    this._setValidation(value);
-  }
-
-  get invalidatingPrefs() {
-    return this._invalidatingPrefs;
+/* 19 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+/* WEBPACK VAR INJECTION */(function(global) {/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__ = __webpack_require__(0);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm__ = __webpack_require__(9);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm__);
+
+
+
+const VISIBLE = "visible";
+const VISIBILITY_CHANGE_EVENT = "visibilitychange";
+
+class DetectUserSessionStart {
+  constructor(store, options = {}) {
+    this._store = store;
+    // Overrides for testing
+    this.document = options.document || global.document;
+    this._perfService = options.perfService || __WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm__["perfService"];
+    this._onVisibilityChange = this._onVisibilityChange.bind(this);
   }
 
-  // This is needed so we can use it in the constructor
-  _setValidation(value = []) {
-    this._validation = value;
-    this._invalidatingPrefs = value.reduce((result, next) => {
-      if (typeof next === "string") {
-        result.push(next);
-        return result;
-      } else if (next && next.oneOf) {
-        return result.concat(next.oneOf);
-      }
-      throw new Error("Your validation configuration is not properly configured");
-    }, []);
+  /**
+   * sendEventOrAddListener - Notify immediately if the page is already visible,
+   *                    or else set up a listener for when visibility changes.
+   *                    This is needed for accurate session tracking for telemetry,
+   *                    because tabs are pre-loaded.
+   */
+  sendEventOrAddListener() {
+    if (this.document.visibilityState === VISIBLE) {
+      // If the document is already visible, to the user, send a notification
+      // immediately that a session has started.
+      this._sendEvent();
+    } else {
+      // If the document is not visible, listen for when it does become visible.
+      this.document.addEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange);
+    }
   }
 
-  arePrefsValid(getPref) {
-    for (const prefs of this.validation) {
-      // {oneOf: ["foo", "bar"]}
-      if (prefs && prefs.oneOf && !prefs.oneOf.some(name => getPref(name) === this.initialPrefs[name])) {
-        return false;
-
-        // "foo"
-      } else if (getPref(prefs) !== this.initialPrefs[prefs]) {
-        return false;
-      }
+  /**
+   * _sendEvent - Sends a message to the main process to indicate the current
+   *              tab is now visible to the user, includes the
+   *              visibility_event_rcvd_ts time in ms from the UNIX epoch.
+   */
+  _sendEvent() {
+    this._perfService.mark("visibility_event_rcvd_ts");
+
+    try {
+      let visibility_event_rcvd_ts = this._perfService.getMostRecentAbsMarkStartByName("visibility_event_rcvd_ts");
+
+      this._store.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({
+        type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SAVE_SESSION_PERF_DATA,
+        data: { visibility_event_rcvd_ts }
+      }));
+    } catch (ex) {
+      // If this failed, it's likely because the `privacy.resistFingerprinting`
+      // pref is true.  We should at least not blow up.
     }
-    return true;
+  }
+
+  /**
+   * _onVisibilityChange - If the visibility has changed to visible, sends a notification
+   *                      and removes the event listener. This should only be called once per tab.
+   */
+  _onVisibilityChange() {
+    if (this.document.visibilityState === VISIBLE) {
+      this._sendEvent();
+      this.document.removeEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange);
+    }
   }
 }
-
-var PrerenderData = new _PrerenderData({
-  initialPrefs: {
-    "migrationExpired": true,
-    "showTopSites": true,
-    "showSearch": true,
-    "topSitesCount": 6,
-    "collapseTopSites": false,
-    "section.highlights.collapsed": false,
-    "section.topstories.collapsed": false,
-    "feeds.section.topstories": true,
-    "feeds.section.highlights": true
-  },
-  // Prefs listed as invalidating will prevent the prerendered version
-  // of AS from being used if their value is something other than what is listed
-  // here. This is required because some preferences cause the page layout to be
-  // too different for the prerendered version to be used. Unfortunately, this
-  // will result in users who have modified some of their preferences not being
-  // able to get the benefits of prerendering.
-  validation: ["showTopSites", "showSearch", "collapseTopSites", "section.highlights.collapsed", "section.topstories.collapsed",
-  // This means if either of these are set to their default values,
-  // prerendering can be used.
-  { oneOf: ["feeds.section.topstories", "feeds.section.highlights"] }],
-  initialSections: [{
-    enabled: true,
-    icon: "pocket",
-    id: "topstories",
-    order: 1,
-    title: { id: "header_recommended_by", values: { provider: "Pocket" } }
-  }, {
-    enabled: true,
-    id: "highlights",
-    icon: "highlights",
-    order: 2,
-    title: { id: "header_highlights" }
-  }]
-});
-module.exports = {
-  PrerenderData,
-  _PrerenderData
-};
+/* harmony export (immutable) */ __webpack_exports__["a"] = DetectUserSessionStart;
+
+/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(4)))
 
 /***/ }),
-/* 31 */
-/***/ (function(module, exports, __webpack_require__) {
-
-/* WEBPACK VAR INJECTION */(function(global) {/* eslint-env mozilla/frame-script */
-
-const { createStore, combineReducers, applyMiddleware } = __webpack_require__(32);
-const { actionTypes: at, actionCreators: ac, actionUtils: au } = __webpack_require__(0);
+/* 20 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+/* WEBPACK VAR INJECTION */(function(global) {/* harmony export (immutable) */ __webpack_exports__["a"] = initStore;
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__ = __webpack_require__(0);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_redux__ = __webpack_require__(21);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_redux___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_redux__);
+/* eslint-env mozilla/frame-script */
+
+
+
 
 const MERGE_STORE_ACTION = "NEW_TAB_INITIAL_STATE";
+/* unused harmony export MERGE_STORE_ACTION */
+
 const OUTGOING_MESSAGE_NAME = "ActivityStream:ContentToMain";
+/* unused harmony export OUTGOING_MESSAGE_NAME */
+
 const INCOMING_MESSAGE_NAME = "ActivityStream:MainToContent";
-const EARLY_QUEUED_ACTIONS = [at.SAVE_SESSION_PERF_DATA, at.PAGE_PRERENDERED];
+/* unused harmony export INCOMING_MESSAGE_NAME */
+
+const EARLY_QUEUED_ACTIONS = [__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SAVE_SESSION_PERF_DATA, __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].PAGE_PRERENDERED];
+/* unused harmony export EARLY_QUEUED_ACTIONS */
+
 
 /**
  * A higher-order function which returns a reducer that, on MERGE_STORE action,
  * will return the action.data object merged into the previous state.
  *
  * For all other actions, it merely calls mainReducer.
  *
  * Because we want this to merge the entire state object, it's written as a
@@ -3505,562 +4044,120 @@ function mergeStateReducer(mainReducer) 
     return mainReducer(prevState, action);
   };
 }
 
 /**
  * messageMiddleware - Middleware that looks for SentToMain type actions, and sends them if necessary
  */
 const messageMiddleware = store => next => action => {
-  if (au.isSendToMain(action)) {
+  if (__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionUtils"].isSendToMain(action)) {
     sendAsyncMessage(OUTGOING_MESSAGE_NAME, action);
   }
   next(action);
 };
 
 const rehydrationMiddleware = store => next => action => {
   if (store._didRehydrate) {
     return next(action);
   }
 
   const isMergeStoreAction = action.type === MERGE_STORE_ACTION;
-  const isRehydrationRequest = action.type === at.NEW_TAB_STATE_REQUEST;
+  const isRehydrationRequest = action.type === __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].NEW_TAB_STATE_REQUEST;
 
   if (isRehydrationRequest) {
     store._didRequestInitialState = true;
     return next(action);
   }
 
   if (isMergeStoreAction) {
     store._didRehydrate = true;
     return next(action);
   }
 
   // If init happened after our request was made, we need to re-request
-  if (store._didRequestInitialState && action.type === at.INIT) {
-    return next(ac.SendToMain({ type: at.NEW_TAB_STATE_REQUEST }));
+  if (store._didRequestInitialState && action.type === __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].INIT) {
+    return next(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].NEW_TAB_STATE_REQUEST }));
   }
 
-  if (au.isBroadcastToContent(action) || au.isSendToContent(action)) {
+  if (__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionUtils"].isBroadcastToContent(action) || __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionUtils"].isSendToContent(action)) {
     // Note that actions received before didRehydrate will not be dispatched
     // because this could negatively affect preloading and the the state
     // will be replaced by rehydration anyway.
     return null;
   }
 
   return next(action);
 };
+/* unused harmony export rehydrationMiddleware */
+
 
 /**
  * This middleware queues up all the EARLY_QUEUED_ACTIONS until it receives
  * the first action from main. This is useful for those actions for main which
  * require higher reliability, i.e. the action will not be lost in the case
  * that it gets sent before the main is ready to receive it. Conversely, any
  * actions allowed early are accepted to be ignorable or re-sendable.
  */
 const queueEarlyMessageMiddleware = store => next => action => {
   if (store._receivedFromMain) {
     next(action);
-  } else if (au.isFromMain(action)) {
+  } else if (__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionUtils"].isFromMain(action)) {
     next(action);
     store._receivedFromMain = true;
     // Sending out all the early actions as main is ready now
     if (store._earlyActionQueue) {
       store._earlyActionQueue.forEach(next);
       store._earlyActionQueue = [];
     }
   } else if (EARLY_QUEUED_ACTIONS.includes(action.type)) {
     store._earlyActionQueue = store._earlyActionQueue || [];
     store._earlyActionQueue.push(action);
   } else {
     // Let any other type of action go through
     next(action);
   }
 };
+/* unused harmony export queueEarlyMessageMiddleware */
+
 
 /**
  * initStore - Create a store and listen for incoming actions
  *
  * @param  {object} reducers An object containing Redux reducers
  * @param  {object} intialState (optional) The initial state of the store, if desired
  * @return {object}          A redux store
  */
-module.exports = function initStore(reducers, initialState) {
-  const store = createStore(mergeStateReducer(combineReducers(reducers)), initialState, global.addMessageListener && applyMiddleware(rehydrationMiddleware, queueEarlyMessageMiddleware, messageMiddleware));
+function initStore(reducers, initialState) {
+  const store = Object(__WEBPACK_IMPORTED_MODULE_1_redux__["createStore"])(mergeStateReducer(Object(__WEBPACK_IMPORTED_MODULE_1_redux__["combineReducers"])(reducers)), initialState, global.addMessageListener && Object(__WEBPACK_IMPORTED_MODULE_1_redux__["applyMiddleware"])(rehydrationMiddleware, queueEarlyMessageMiddleware, messageMiddleware));
 
   store._didRehydrate = false;
   store._didRequestInitialState = false;
 
   if (global.addMessageListener) {
     global.addMessageListener(INCOMING_MESSAGE_NAME, msg => {
       try {
         store.dispatch(msg.data);
       } catch (ex) {
         console.error("Content msg:", msg, "Dispatch error: ", ex); // eslint-disable-line no-console
         dump(`Content msg: ${JSON.stringify(msg)}\nDispatch error: ${ex}\n${ex.stack}`);
       }
     });
   }
 
   return store;
-};
-
-module.exports.rehydrationMiddleware = rehydrationMiddleware;
-module.exports.queueEarlyMessageMiddleware = queueEarlyMessageMiddleware;
-module.exports.MERGE_STORE_ACTION = MERGE_STORE_ACTION;
-module.exports.OUTGOING_MESSAGE_NAME = OUTGOING_MESSAGE_NAME;
-module.exports.INCOMING_MESSAGE_NAME = INCOMING_MESSAGE_NAME;
-module.exports.EARLY_QUEUED_ACTIONS = EARLY_QUEUED_ACTIONS;
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
+}
+/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(4)))
 
 /***/ }),
-/* 32 */
+/* 21 */
 /***/ (function(module, exports) {
 
 module.exports = Redux;
 
 /***/ }),
-/* 33 */
-/***/ (function(module, exports, __webpack_require__) {
-
-/* WEBPACK VAR INJECTION */(function(global) {const { actionTypes: at, actionCreators: ac } = __webpack_require__(0);
-const { perfService: perfSvc } = __webpack_require__(11);
-
-const VISIBLE = "visible";
-const VISIBILITY_CHANGE_EVENT = "visibilitychange";
-
-module.exports = class DetectUserSessionStart {
-  constructor(store, options = {}) {
-    this._store = store;
-    // Overrides for testing
-    this.document = options.document || global.document;
-    this._perfService = options.perfService || perfSvc;
-    this._onVisibilityChange = this._onVisibilityChange.bind(this);
-  }
-
-  /**
-   * sendEventOrAddListener - Notify immediately if the page is already visible,
-   *                    or else set up a listener for when visibility changes.
-   *                    This is needed for accurate session tracking for telemetry,
-   *                    because tabs are pre-loaded.
-   */
-  sendEventOrAddListener() {
-    if (this.document.visibilityState === VISIBLE) {
-      // If the document is already visible, to the user, send a notification
-      // immediately that a session has started.
-      this._sendEvent();
-    } else {
-      // If the document is not visible, listen for when it does become visible.
-      this.document.addEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange);
-    }
-  }
-
-  /**
-   * _sendEvent - Sends a message to the main process to indicate the current
-   *              tab is now visible to the user, includes the
-   *              visibility_event_rcvd_ts time in ms from the UNIX epoch.
-   */
-  _sendEvent() {
-    this._perfService.mark("visibility_event_rcvd_ts");
-
-    try {
-      let visibility_event_rcvd_ts = this._perfService.getMostRecentAbsMarkStartByName("visibility_event_rcvd_ts");
-
-      this._store.dispatch(ac.SendToMain({
-        type: at.SAVE_SESSION_PERF_DATA,
-        data: { visibility_event_rcvd_ts }
-      }));
-    } catch (ex) {
-      // If this failed, it's likely because the `privacy.resistFingerprinting`
-      // pref is true.  We should at least not blow up.
-    }
-  }
-
-  /**
-   * _onVisibilityChange - If the visibility has changed to visible, sends a notification
-   *                      and removes the event listener. This should only be called once per tab.
-   */
-  _onVisibilityChange() {
-    if (this.document.visibilityState === VISIBLE) {
-      this._sendEvent();
-      this.document.removeEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange);
-    }
-  }
-};
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
-
-/***/ }),
-/* 34 */
-/***/ (function(module, exports, __webpack_require__) {
-
-/* WEBPACK VAR INJECTION */(function(global) {const DATABASE_NAME = "snippets_db";
-const DATABASE_VERSION = 1;
-const SNIPPETS_OBJECTSTORE_NAME = "snippets";
-const SNIPPETS_UPDATE_INTERVAL_MS = 14400000; // 4 hours.
-
-const SNIPPETS_ENABLED_EVENT = "Snippets:Enabled";
-const SNIPPETS_DISABLED_EVENT = "Snippets:Disabled";
-
-const { actionTypes: at, actionCreators: ac } = __webpack_require__(0);
-
-/**
- * SnippetsMap - A utility for cacheing values related to the snippet. It has
- *               the same interface as a Map, but is optionally backed by
- *               indexedDB for persistent storage.
- *               Call .connect() to open a database connection and restore any
- *               previously cached data, if necessary.
- *
- */
-class SnippetsMap extends Map {
-  constructor(dispatch) {
-    super();
-    this._db = null;
-    this._dispatch = dispatch;
-  }
-
-  set(key, value) {
-    super.set(key, value);
-    return this._dbTransaction(db => db.put(value, key));
-  }
-
-  delete(key) {
-    super.delete(key);
-    return this._dbTransaction(db => db.delete(key));
-  }
-
-  clear() {
-    super.clear();
-    return this._dbTransaction(db => db.clear());
-  }
-
-  get blockList() {
-    return this.get("blockList") || [];
-  }
-
-  /**
-   * blockSnippetById - Blocks a snippet given an id
-   *
-   * @param  {str|int} id   The id of the snippet
-   * @return {Promise}      Resolves when the id has been written to indexedDB,
-   *                        or immediately if the snippetMap is not connected
-   */
-  async blockSnippetById(id) {
-    if (!id) {
-      return;
-    }
-    let blockList = this.blockList;
-    if (!blockList.includes(id)) {
-      blockList.push(id);
-      this._dispatch(ac.SendToMain({ type: at.SNIPPETS_BLOCKLIST_UPDATED, data: blockList }));
-      await this.set("blockList", blockList);
-    }
-  }
-
-  disableOnboarding() {
-    this._dispatch(ac.SendToMain({ type: at.DISABLE_ONBOARDING }));
-  }
-
-  showFirefoxAccounts() {
-    this._dispatch(ac.SendToMain({ type: at.SHOW_FIREFOX_ACCOUNTS }));
-  }
-
-  /**
-   * connect - Attaches an indexedDB back-end to the Map so that any set values
-   *           are also cached in a store. It also restores any existing values
-   *           that are already stored in the indexedDB store.
-   *
-   * @return {type}  description
-   */
-  async connect() {
-    // Open the connection
-    const db = await this._openDB();
-
-    // Restore any existing values
-    await this._restoreFromDb(db);
-
-    // Attach a reference to the db
-    this._db = db;
-  }
-
-  /**
-   * _dbTransaction - Returns a db transaction wrapped with the given modifier
-   *                  function as a Promise. If the db has not been connected,
-   *                  it resolves immediately.
-   *
-   * @param  {func} modifier A function to call with the transaction
-   * @return {obj}           A Promise that resolves when the transaction has
-   *                         completed or errored
-   */
-  _dbTransaction(modifier) {
-    if (!this._db) {
-      return Promise.resolve();
-    }
-    return new Promise((resolve, reject) => {
-      const transaction = modifier(this._db.transaction(SNIPPETS_OBJECTSTORE_NAME, "readwrite").objectStore(SNIPPETS_OBJECTSTORE_NAME));
-      transaction.onsuccess = event => resolve();
-
-      /* istanbul ignore next */
-      transaction.onerror = event => reject(transaction.error);
-    });
-  }
-
-  _openDB() {
-    return new Promise((resolve, reject) => {
-      const openRequest = indexedDB.open(DATABASE_NAME, DATABASE_VERSION);
-
-      /* istanbul ignore next */
-      openRequest.onerror = event => {
-        // Try to delete the old database so that we can start this process over
-        // next time.
-        indexedDB.deleteDatabase(DATABASE_NAME);
-        reject(event);
-      };
-
-      openRequest.onupgradeneeded = event => {
-        const db = event.target.result;
-        if (!db.objectStoreNames.contains(SNIPPETS_OBJECTSTORE_NAME)) {
-          db.createObjectStore(SNIPPETS_OBJECTSTORE_NAME);
-        }
-      };
-
-      openRequest.onsuccess = event => {
-        let db = event.target.result;
-
-        /* istanbul ignore next */
-        db.onerror = err => console.error(err); // eslint-disable-line no-console
-        /* istanbul ignore next */
-        db.onversionchange = versionChangeEvent => versionChangeEvent.target.close();
-
-        resolve(db);
-      };
-    });
-  }
-
-  _restoreFromDb(db) {
-    return new Promise((resolve, reject) => {
-      let cursorRequest;
-      try {
-        cursorRequest = db.transaction(SNIPPETS_OBJECTSTORE_NAME).objectStore(SNIPPETS_OBJECTSTORE_NAME).openCursor();
-      } catch (err) {
-        // istanbul ignore next
-        reject(err);
-        // istanbul ignore next
-        return;
-      }
-
-      /* istanbul ignore next */
-      cursorRequest.onerror = event => reject(event);
-
-      cursorRequest.onsuccess = event => {
-        let cursor = event.target.result;
-        // Populate the cache from the persistent storage.
-        if (cursor) {
-          this.set(cursor.key, cursor.value);
-          cursor.continue();
-        } else {
-          // We are done.
-          resolve();
-        }
-      };
-    });
-  }
-}
-
-/**
- * SnippetsProvider - Initializes a SnippetsMap and loads snippets from a
- *                    remote location, or else default snippets if the remote
- *                    snippets cannot be retrieved.
- */
-class SnippetsProvider {
-  constructor(dispatch) {
-    // Initialize the Snippets Map and attaches it to a global so that
-    // the snippet payload can interact with it.
-    global.gSnippetsMap = new SnippetsMap(dispatch);
-    this._onAction = this._onAction.bind(this);
-  }
-
-  get snippetsMap() {
-    return global.gSnippetsMap;
-  }
-
-  async _refreshSnippets() {
-    // Check if the cached version of of the snippets in snippetsMap. If it's too
-    // old, blow away the entire snippetsMap.
-    const cachedVersion = this.snippetsMap.get("snippets-cached-version");
-
-    if (cachedVersion !== this.appData.version) {
-      this.snippetsMap.clear();
-    }
-
-    // Has enough time passed for us to require an update?
-    const lastUpdate = this.snippetsMap.get("snippets-last-update");
-    const needsUpdate = !(lastUpdate >= 0) || Date.now() - lastUpdate > SNIPPETS_UPDATE_INTERVAL_MS;
-
-    if (needsUpdate && this.appData.snippetsURL) {
-      this.snippetsMap.set("snippets-last-update", Date.now());
-      try {
-        const response = await fetch(this.appData.snippetsURL);
-        if (response.status === 200) {
-          const payload = await response.text();
-
-          this.snippetsMap.set("snippets", payload);
-          this.snippetsMap.set("snippets-cached-version", this.appData.version);
-        }
-      } catch (e) {
-        console.error(e); // eslint-disable-line no-console
-      }
-    }
-  }
-
-  _noSnippetFallback() {
-    // TODO
-  }
-
-  _forceOnboardingVisibility(shouldBeVisible) {
-    const onboardingEl = document.getElementById("onboarding-notification-bar");
-
-    if (onboardingEl) {
-      onboardingEl.style.display = shouldBeVisible ? "" : "none";
-    }
-  }
-
-  _showRemoteSnippets() {
-    const snippetsEl = document.getElementById(this.elementId);
-    const payload = this.snippetsMap.get("snippets");
-
-    if (!snippetsEl) {
-      throw new Error(`No element was found with id '${this.elementId}'.`);
-    }
-
-    // This could happen if fetching failed
-    if (!payload) {
-      throw new Error("No remote snippets were found in gSnippetsMap.");
-    }
-
-    if (typeof payload !== "string") {
-      throw new Error("Snippet payload was incorrectly formatted");
-    }
-
-    // Note that injecting snippets can throw if they're invalid XML.
-    // eslint-disable-next-line no-unsanitized/property
-    snippetsEl.innerHTML = payload;
-
-    // Scripts injected by innerHTML are inactive, so we have to relocate them
-    // through DOM manipulation to activate their contents.
-    for (const scriptEl of snippetsEl.getElementsByTagName("script")) {
-      const relocatedScript = document.createElement("script");
-      relocatedScript.text = scriptEl.text;
-      scriptEl.parentNode.replaceChild(relocatedScript, scriptEl);
-    }
-  }
-
-  _onAction(msg) {
-    if (msg.data.type === at.SNIPPET_BLOCKED) {
-      this.snippetsMap.set("blockList", msg.data.data);
-      document.getElementById("snippets-container").style.display = "none";
-    }
-  }
-
-  /**
-   * init - Fetch the snippet payload and show snippets
-   *
-   * @param  {obj} options
-   * @param  {str} options.appData.snippetsURL  The URL from which we fetch snippets
-   * @param  {int} options.appData.version  The current snippets version
-   * @param  {str} options.elementId  The id of the element in which to inject snippets
-   * @param  {bool} options.connect  Should gSnippetsMap connect to indexedDB?
-   */
-  async init(options) {
-    Object.assign(this, {
-      appData: {},
-      elementId: "snippets",
-      connect: true
-    }, options);
-
-    // Add listener so we know when snippets are blocked on other pages
-    if (global.addMessageListener) {
-      global.addMessageListener("ActivityStream:MainToContent", this._onAction);
-    }
-
-    // TODO: Requires enabling indexedDB on newtab
-    // Restore the snippets map from indexedDB
-    if (this.connect) {
-      try {
-        await this.snippetsMap.connect();
-      } catch (e) {
-        console.error(e); // eslint-disable-line no-console
-      }
-    }
-
-    // Cache app data values so they can be accessible from gSnippetsMap
-    for (const key of Object.keys(this.appData)) {
-      this.snippetsMap.set(`appData.${key}`, this.appData[key]);
-    }
-
-    // Refresh snippets, if enough time has passed.
-    await this._refreshSnippets();
-
-    // Try showing remote snippets, falling back to defaults if necessary.
-    try {
-      this._showRemoteSnippets();
-    } catch (e) {
-      this._noSnippetFallback(e);
-    }
-
-    window.dispatchEvent(new Event(SNIPPETS_ENABLED_EVENT));
-
-    this._forceOnboardingVisibility(true);
-    this.initialized = true;
-  }
-
-  uninit() {
-    window.dispatchEvent(new Event(SNIPPETS_DISABLED_EVENT));
-    this._forceOnboardingVisibility(false);
-    if (global.removeMessageListener) {
-      global.removeMessageListener("ActivityStream:MainToContent", this._onAction);
-    }
-    this.initialized = false;
-  }
-}
-
-/**
- * addSnippetsSubscriber - Creates a SnippetsProvider that Initializes
- *                         when the store has received the appropriate
- *                         Snippet data.
- *
- * @param  {obj} store   The redux store
- * @return {obj}         Returns the snippets instance and unsubscribe function
- */
-function addSnippetsSubscriber(store) {
-  const snippets = new SnippetsProvider(store.dispatch);
-
-  let initializing = false;
-
-  store.subscribe(async () => {
-    const state = store.getState();
-    // state.Prefs.values["feeds.snippets"]:  Should snippets be shown?
-    // state.Snippets.initialized             Is the snippets data initialized?
-    // snippets.initialized:                  Is SnippetsProvider currently initialised?
-    if (state.Prefs.values["feeds.snippets"] && !state.Prefs.values.disableSnippets && state.Snippets.initialized && !snippets.initialized &&
-    // Don't call init multiple times
-    !initializing) {
-      initializing = true;
-      await snippets.init({ appData: state.Snippets });
-      initializing = false;
-    } else if ((state.Prefs.values["feeds.snippets"] === false || state.Prefs.values.disableSnippets === true) && snippets.initialized) {
-      snippets.uninit();
-    }
-  });
-
-  // These values are returned for testing purposes
-  return snippets;
-}
-
-module.exports = {
-  addSnippetsSubscriber,
-  SnippetsMap,
-  SnippetsProvider,
-  SNIPPETS_UPDATE_INTERVAL_MS
-};
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
+/* 22 */
+/***/ (function(module, exports) {
+
+module.exports = ReactDOM;
 
 /***/ })
 /******/ ]);
\ No newline at end of file
--- a/browser/extensions/activity-stream/install.rdf.in
+++ b/browser/extensions/activity-stream/install.rdf.in
@@ -3,17 +3,17 @@
 #filter substitution
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
   <Description about="urn:mozilla:install-manifest">
     <em:id>activity-stream@mozilla.org</em:id>
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
     <em:unpack>false</em:unpack>
-    <em:version>2017.12.08.1270-dc9d0c0e</em:version>
+    <em:version>2017.12.20.1328-00d79b97</em:version>
     <em:name>Activity Stream</em:name>
     <em:description>A rich visual history feed and a reimagined home page make it easier than ever to find exactly what you're looking for in Firefox.</em:description>
     <em:multiprocessCompatible>true</em:multiprocessCompatible>
 
     <em:targetApplication>
       <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
         <em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
--- a/browser/extensions/activity-stream/prerendered/locales/ar/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/ar/activity-stream-strings.js
@@ -33,19 +33,19 @@ window.gActivityStreamStrings = {
   "search_for_something_with": "ابحث عن {search_term} مستخدما:",
   "search_button": "ابحث",
   "search_header": "بحث {search_engine_name}",
   "search_web_placeholder": "ابحث في الوِب",
   "search_settings": "غيّر إعدادات البحث",
   "section_info_option": "المعلومات",
   "section_info_send_feedback": "أرسل انطباعك",
   "section_info_privacy_notice": "تنويه الخصوصية",
-  "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.",
-  "section_disclaimer_topstories_linktext": "Learn how it works.",
-  "section_disclaimer_topstories_buttontext": "Okay, got it",
+  "section_disclaimer_topstories": "أكثر القصص تشويقًا على الإنترنت، مختارة بعناية بناء على ما تقرأه. من بوكِت، و الذي أصبح جزءًا من موزيلا.",
+  "section_disclaimer_topstories_linktext": "اطلع على طريقة عملها.",
+  "section_disclaimer_topstories_buttontext": "حسنًا، فهمت",
   "welcome_title": "مرحبًا في لسان جديد",
   "welcome_body": "سيستخدم فيرفكس هذا المكان لعرض أكثر العلامات، و المقالات، و الفيديوهات والصفحات التي زرتها مؤخرا، ليمكنك العودة إليها بسهولة.",
   "welcome_label": "تعرّف على أهم الأخبار",
   "time_label_less_than_minute": "< دقيقة",
   "time_label_minute": "{number} دقيقة",
   "time_label_hour": "{number} ساعة",
   "time_label_day": "{number} يوم",
   "settings_pane_button_label": "خصص صفحة اللسان الجديد",
@@ -62,17 +62,17 @@ window.gActivityStreamStrings = {
   "settings_pane_visit_again_body": "سيعرض لك فَيَرفُكس بعضًا من تأريخ تصفحك الذي قد تود تذكّره لاحقًا.",
   "settings_pane_highlights_header": "أهم الأحداث",
   "settings_pane_highlights_body2": "ارجع للأشياء المهمة التي زرتها مؤخرًا أو العلامات.",
   "settings_pane_highlights_options_bookmarks": "العلامات",
   "settings_pane_highlights_options_visited": "المواقع المُزارة",
   "settings_pane_snippets_header": "المقتطفات",
   "settings_pane_snippets_body": "اقرأ تحديثات قصيرة و جميلة من موزيلا عن فَيَرفُكس، و ثقافة الإنترنت، و أحيانا صرعة عشوائية من الإنترنت.",
   "settings_pane_done_button": "تمّ",
-  "settings_pane_topstories_options_sponsored": "Show Sponsored Stories",
+  "settings_pane_topstories_options_sponsored": "اعرض الأخبار الممولة",
   "edit_topsites_button_text": "حرِّر",
   "edit_topsites_button_label": "خصص قسم المواقع الأكثر زيارة",
   "edit_topsites_showmore_button": "اعرض المزيد",
   "edit_topsites_showless_button": "اعرض أقل",
   "edit_topsites_done_button": "تمّ",
   "edit_topsites_pin_button": "ثبّت هذا الموقع",
   "edit_topsites_unpin_button": "افصل هذا الموقع",
   "edit_topsites_edit_button": "حرّر هذا الموقع",
--- a/browser/extensions/activity-stream/prerendered/locales/br/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/br/activity-stream-strings.js
@@ -33,19 +33,19 @@ window.gActivityStreamStrings = {
   "search_for_something_with": "Klask {search_term} gant:",
   "search_button": "Klask",
   "search_header": "Klask {search_engine_name}",
   "search_web_placeholder": "Klask er web",
   "search_settings": "Kemmañ an arventennoù klask",
   "section_info_option": "Titouroù",
   "section_info_send_feedback": "Kas ho meno",
   "section_info_privacy_notice": "Evezhiadennoù a-fet buhez prevez",
-  "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.",
-  "section_disclaimer_topstories_linktext": "Learn how it works.",
-  "section_disclaimer_topstories_buttontext": "Okay, got it",
+  "section_disclaimer_topstories": "An istorioù dedennusañ er web, dibabet hervez ar pezh a lennit. Diwar Pocket, ul lodenn eus Mozilla.",
+  "section_disclaimer_topstories_linktext": "Deskit penaos ec'h a en-dro.",
+  "section_disclaimer_topstories_buttontext": "Mat eo, komprenet am eus",
   "welcome_title": "Donemat war un ivinell nevez",
   "welcome_body": "Firefox a implijo al lec'h-mañ evit diskouez deoc'h sinedoù, pennadoù, videoioù ha pajennoù bet gweladennet ganeoc'h, evit adkavout anezho en un doare aes.",
   "welcome_label": "Naoudiañ ho mareoù pouezus",
   "time_label_less_than_minute": "< 1 m",
   "time_label_minute": "{number}m",
   "time_label_hour": "{number}e",
   "time_label_day": "{number}d",
   "settings_pane_button_label": "Personelait ho pajenn Ivinell Nevez",
@@ -62,17 +62,17 @@ window.gActivityStreamStrings = {
   "settings_pane_visit_again_body": "Firefox a ziskouezo deoc'h ul lodenn eus ho roll istor a c'hallfec'h kaout c'hoant da zerc'hel soñj pe da zistreiñ eno.",
   "settings_pane_highlights_header": "Mareoù pouezus",
   "settings_pane_highlights_body2": "Adkavit an traoù dedennus gweladennet pe lakaet er sinedoù nevez ’zo.",
   "settings_pane_highlights_options_bookmarks": "Sinedoù",
   "settings_pane_highlights_options_visited": "Lec'hiennoù gweladennet",
   "settings_pane_snippets_header": "Notennigoù",
   "settings_pane_snippets_body": "Lennit an hizivadurioù berr ha dous graet gant Mozilla evit Firefox, sevenadur ar genrouedad, hag ur mem dre-zegouezh ur wech an amzer.",
   "settings_pane_done_button": "Graet",
-  "settings_pane_topstories_options_sponsored": "Show Sponsored Stories",
+  "settings_pane_topstories_options_sponsored": "Diskouez an istorioù kevelet",
   "edit_topsites_button_text": "Embann",
   "edit_topsites_button_label": "Personelaat ar gevrenn “lec'hiennoù gweladennet ar muiañ”",
   "edit_topsites_showmore_button": "Diskouez muioc'h",
   "edit_topsites_showless_button": "Diskouez nebeutoc'h",
   "edit_topsites_done_button": "Graet",
   "edit_topsites_pin_button": "Spilhennañ al lec'hienn-mañ",
   "edit_topsites_unpin_button": "Dispilhennañ al lec'hienn-mañ",
   "edit_topsites_edit_button": "Embann al lec'hienn-mañ",
--- a/browser/extensions/activity-stream/prerendered/locales/el/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/el/activity-stream-strings.js
@@ -33,19 +33,19 @@ window.gActivityStreamStrings = {
   "search_for_something_with": "Αναζήτηση για {search_term} με:",
   "search_button": "Αναζήτηση",
   "search_header": "Αναζήτηση {search_engine_name}",
   "search_web_placeholder": "Αναζήτηση στον ιστό",
   "search_settings": "Αλλαγή ρυθμίσεων αναζήτησης",
   "section_info_option": "Πληροφορίες",
   "section_info_send_feedback": "Αποστολή σχολίων",
   "section_info_privacy_notice": "Σημείωση απορρήτου",
-  "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.",
-  "section_disclaimer_topstories_linktext": "Learn how it works.",
-  "section_disclaimer_topstories_buttontext": "Okay, got it",
+  "section_disclaimer_topstories": "Οι πιο ενδιαφέρουσες ιστορίες στο διαδίκτυο, επιλεγμένες βάσει όσων διαβάζετε. Από το Pocket, πλέον μέλος της Mozilla.",
+  "section_disclaimer_topstories_linktext": "Μάθετε πώς λειτουργεί.",
+  "section_disclaimer_topstories_buttontext": "Εντάξει, το 'πιασα",
   "welcome_title": "Καλώς ορίσατε στη νέα καρτέλα",
   "welcome_body": "Το Firefox θα χρησιμοποιήσει αυτό το χώρο για να εμφανίσει τους πιο σχετικούς σελιδοδείκτες, άρθρα, βίντεο και σελίδες που επισκεφθήκατε πρόσφατα, ώστε να έχετε εύκολη πρόσβαση.",
   "welcome_label": "Αναγνώριση κορυφαίων στιγμών",
   "time_label_less_than_minute": "<1λ",
   "time_label_minute": "{number}λ",
   "time_label_hour": "{number}ώ",
   "time_label_day": "{number}η",
   "settings_pane_button_label": "Προσαρμογή της σελίδας Νέας Καρτέλας",
@@ -62,17 +62,17 @@ window.gActivityStreamStrings = {
   "settings_pane_visit_again_body": "Το Firefox θα σάς δείξει μέρη του ιστορικού περιήγησής σας που ίσως θέλετε να θυμηθείτε ή να επισκεφθείτε ξανά.",
   "settings_pane_highlights_header": "Κορυφαίες στιγμές",
   "settings_pane_highlights_body2": "Βρείτε ξανά κάτι ενδιαφέρον που έχετε επισκεφθεί πρόσφατα ή έχετε αποθηκεύσει στους σελιδοδείκτες σας.",
   "settings_pane_highlights_options_bookmarks": "Σελιδοδείκτες",
   "settings_pane_highlights_options_visited": "Πρόσφατες ιστοσελίδες",
   "settings_pane_snippets_header": "Αποσπάσματα",
   "settings_pane_snippets_body": "Διαβάστε σύντομες και όμορφες ενημερώσεις από τη Mozilla σχετικά με το Firefox, το διαδικτυακό πολιτισμό και τα περιστασιακά, τυχαία memes.",
   "settings_pane_done_button": "Τέλος",
-  "settings_pane_topstories_options_sponsored": "Show Sponsored Stories",
+  "settings_pane_topstories_options_sponsored": "Εμφάνιση χορηγούμενων ιστοριών",
   "edit_topsites_button_text": "Επεξεργασία",
   "edit_topsites_button_label": "Προσαρμογή της ενότητας Κορυφαίες Ιστοσελίδες",
   "edit_topsites_showmore_button": "Εμφάνιση περισσότερων",
   "edit_topsites_showless_button": "Εμφάνιση λιγότερων",
   "edit_topsites_done_button": "Τέλος",
   "edit_topsites_pin_button": "Καρφίτσωμα ιστοσελίδας",
   "edit_topsites_unpin_button": "Ξεκαρφίτσωμα ιστοσελίδας",
   "edit_topsites_edit_button": "Επεξεργασία ιστοσελίδας",
--- a/browser/extensions/activity-stream/prerendered/locales/ff/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ff/activity-stream-prerendered.html
@@ -2,17 +2,17 @@
 <html lang="ff" dir="ltr">
   <head>
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy-Report-Only" content="script-src 'unsafe-inline'; img-src http: https: data: blob:; style-src 'unsafe-inline'; child-src 'none'; object-src 'none'; report-uri https://tiles.services.mozilla.com/v4/links/activity-stream/csp">
     <link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
     <link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
   </head>
   <body class="activity-stream">
-    <div id="root"><div class="outer-wrapper fixed-to-top" data-reactroot="" data-reactid="1" data-react-checksum="-705457528"><main data-reactid="2"><div class="search-wrapper" data-reactid="3"><label for="newtab-search-text" class="search-label" data-reactid="4"><span class="sr-only" data-reactid="5"><span data-reactid="6">Search the Web</span></span></label><input type="search" id="newtab-search-text" maxlength="256" placeholder="Search the Web" title="Search the Web" data-reactid="7"/><button id="searchSubmit" class="search-button" title="Search" data-reactid="8"><span class="sr-only" data-reactid="9"><span data-reactid="10">Search</span></span></button></div><div class="body-wrapper" data-reactid="11"><section class="collapsible-section top-sites animation-enabled" data-reactid="12"><div class="section-top-bar" data-reactid="13"><h3 class="section-title" data-reactid="14"><span class="click-target" data-reactid="15"><span class="icon icon-small-spacer icon-topsites" data-reactid="16"></span><span data-reactid="17">Lowe dowrowe</span><span class="icon icon-arrowhead-down" data-reactid="18"></span></span></h3><span class="section-info-option" data-reactid="19"><img class="info-option-icon" title="Info" aria-haspopup="true" aria-controls="info-option" aria-expanded="false" role="note" tabindex="0" data-reactid="20"/><div class="info-option" data-reactid="21"><div class="info-option-header" role="heading" data-reactid="22"><span data-reactid="23">Top Sites</span></div><p class="info-option-body" data-reactid="24"><span data-reactid="25">Access the websites you visit most.</span></p><div class="info-option-manage" data-reactid="26"><button data-reactid="27"><span data-reactid="28">New Tab Preferences</span></button></div></div></span></div><div class="section-body" data-reactid="29"><ul class="top-sites-list" data-reactid="30"><li class="top-site-outer placeholder" data-reactid="31"><a data-reactid="32"><div class="tile" aria-hidden="true" data-reactid="33"><div class="screenshot" style="background-image:none;" data-reactid="34"></div></div><div class="title " data-reactid="35"><span dir="auto" data-reactid="36"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="37"><a data-reactid="38"><div class="tile" aria-hidden="true" data-reactid="39"><div class="screenshot" style="background-image:none;" data-reactid="40"></div></div><div class="title " data-reactid="41"><span dir="auto" data-reactid="42"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="43"><a data-reactid="44"><div class="tile" aria-hidden="true" data-reactid="45"><div class="screenshot" style="background-image:none;" data-reactid="46"></div></div><div class="title " data-reactid="47"><span dir="auto" data-reactid="48"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="49"><a data-reactid="50"><div class="tile" aria-hidden="true" data-reactid="51"><div class="screenshot" style="background-image:none;" data-reactid="52"></div></div><div class="title " data-reactid="53"><span dir="auto" data-reactid="54"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="55"><a data-reactid="56"><div class="tile" aria-hidden="true" data-reactid="57"><div class="screenshot" style="background-image:none;" data-reactid="58"></div></div><div class="title " data-reactid="59"><span dir="auto" data-reactid="60"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="61"><a data-reactid="62"><div class="tile" aria-hidden="true" data-reactid="63"><div class="screenshot" style="background-image:none;" data-reactid="64"></div></div><div class="title " data-reactid="65"><span dir="auto" data-reactid="66"></span></div></a></li></ul><div class="edit-topsites-wrapper" data-reactid="67"><div class="edit-topsites-button" data-reactid="68"><button class="edit" title="Customize your Top Sites section" data-reactid="69"><span data-reactid="70">Edit</span></button></div></div></div></section><div class="sections-list" data-reactid="71"><section class="collapsible-section section animation-enabled" data-reactid="72"><div class="section-top-bar" data-reactid="73"><h3 class="section-title" data-reactid="74"><span class="click-target" data-reactid="75"><span class="icon icon-small-spacer icon-pocket" data-reactid="76"></span><span data-reactid="77">Recommended by Pocket</span><span class="icon icon-arrowhead-down" data-reactid="78"></span></span></h3></div><div class="section-body" data-reactid="79"><ul class="section-list" style="padding:0;" data-reactid="80"><li class="card-outer placeholder" data-reactid="81"><a data-reactid="82"><div class="card" data-reactid="83"><div class="card-details no-image" data-reactid="84"><div class="card-text no-context no-description no-host-name no-image" data-reactid="85"><h4 class="card-title" dir="auto" data-reactid="86"></h4><p class="card-description" dir="auto" data-reactid="87"></p></div><div class="card-context" data-reactid="88"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="89"><a data-reactid="90"><div class="card" data-reactid="91"><div class="card-details no-image" data-reactid="92"><div class="card-text no-context no-description no-host-name no-image" data-reactid="93"><h4 class="card-title" dir="auto" data-reactid="94"></h4><p class="card-description" dir="auto" data-reactid="95"></p></div><div class="card-context" data-reactid="96"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="97"><a data-reactid="98"><div class="card" data-reactid="99"><div class="card-details no-image" data-reactid="100"><div class="card-text no-context no-description no-host-name no-image" data-reactid="101"><h4 class="card-title" dir="auto" data-reactid="102"></h4><p class="card-description" dir="auto" data-reactid="103"></p></div><div class="card-context" data-reactid="104"></div></div></div></a></li></ul><div class="topic" data-reactid="105"><span data-reactid="106"><span data-reactid="107">Popular Topics:</span></span><ul data-reactid="108"></ul></div></div></section><section class="collapsible-section section animation-enabled" data-reactid="109"><div class="section-top-bar" data-reactid="110"><h3 class="section-title" data-reactid="111"><span class="click-target" data-reactid="112"><span class="icon icon-small-spacer icon-highlights" data-reactid="113"></span><span data-reactid="114">Highlights</span><span class="icon icon-arrowhead-down" data-reactid="115"></span></span></h3></div><div class="section-body" data-reactid="116"><ul class="section-list" style="padding:0;" data-reactid="117"><li class="card-outer placeholder" data-reactid="118"><a data-reactid="119"><div class="card" data-reactid="120"><div class="card-details no-image" data-reactid="121"><div class="card-text no-context no-description no-host-name no-image" data-reactid="122"><h4 class="card-title" dir="auto" data-reactid="123"></h4><p class="card-description" dir="auto" data-reactid="124"></p></div><div class="card-context" data-reactid="125"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="126"><a data-reactid="127"><div class="card" data-reactid="128"><div class="card-details no-image" data-reactid="129"><div class="card-text no-context no-description no-host-name no-image" data-reactid="130"><h4 class="card-title" dir="auto" data-reactid="131"></h4><p class="card-description" dir="auto" data-reactid="132"></p></div><div class="card-context" data-reactid="133"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="134"><a data-reactid="135"><div class="card" data-reactid="136"><div class="card-details no-image" data-reactid="137"><div class="card-text no-context no-description no-host-name no-image" data-reactid="138"><h4 class="card-title" dir="auto" data-reactid="139"></h4><p class="card-description" dir="auto" data-reactid="140"></p></div><div class="card-context" data-reactid="141"></div></div></div></a></li></ul></div></section></div></div><!-- react-empty: 142 --></main></div></div>
+    <div id="root"><div class="outer-wrapper fixed-to-top" data-reactroot="" data-reactid="1" data-react-checksum="2103940063"><main data-reactid="2"><div class="search-wrapper" data-reactid="3"><label for="newtab-search-text" class="search-label" data-reactid="4"><span class="sr-only" data-reactid="5"><span data-reactid="6">Yiylo geese</span></span></label><input type="search" id="newtab-search-text" maxlength="256" placeholder="Yiylo geese" title="Yiylo geese" data-reactid="7"/><button id="searchSubmit" class="search-button" title="Yiylo" data-reactid="8"><span class="sr-only" data-reactid="9"><span data-reactid="10">Yiylo</span></span></button></div><div class="body-wrapper" data-reactid="11"><section class="collapsible-section top-sites animation-enabled" data-reactid="12"><div class="section-top-bar" data-reactid="13"><h3 class="section-title" data-reactid="14"><span class="click-target" data-reactid="15"><span class="icon icon-small-spacer icon-topsites" data-reactid="16"></span><span data-reactid="17">Lowe dowrowe</span><span class="icon icon-arrowhead-down" data-reactid="18"></span></span></h3><span class="section-info-option" data-reactid="19"><img class="info-option-icon" title="Kabaruuji" aria-haspopup="true" aria-controls="info-option" aria-expanded="false" role="note" tabindex="0" data-reactid="20"/><div class="info-option" data-reactid="21"><div class="info-option-header" role="heading" data-reactid="22"><span data-reactid="23">Top Sites</span></div><p class="info-option-body" data-reactid="24"><span data-reactid="25">Access the websites you visit most.</span></p><div class="info-option-manage" data-reactid="26"><button data-reactid="27"><span data-reactid="28">New Tab Preferences</span></button></div></div></span></div><div class="section-body" data-reactid="29"><ul class="top-sites-list" data-reactid="30"><li class="top-site-outer placeholder" data-reactid="31"><a data-reactid="32"><div class="tile" aria-hidden="true" data-reactid="33"><div class="screenshot" style="background-image:none;" data-reactid="34"></div></div><div class="title " data-reactid="35"><span dir="auto" data-reactid="36"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="37"><a data-reactid="38"><div class="tile" aria-hidden="true" data-reactid="39"><div class="screenshot" style="background-image:none;" data-reactid="40"></div></div><div class="title " data-reactid="41"><span dir="auto" data-reactid="42"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="43"><a data-reactid="44"><div class="tile" aria-hidden="true" data-reactid="45"><div class="screenshot" style="background-image:none;" data-reactid="46"></div></div><div class="title " data-reactid="47"><span dir="auto" data-reactid="48"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="49"><a data-reactid="50"><div class="tile" aria-hidden="true" data-reactid="51"><div class="screenshot" style="background-image:none;" data-reactid="52"></div></div><div class="title " data-reactid="53"><span dir="auto" data-reactid="54"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="55"><a data-reactid="56"><div class="tile" aria-hidden="true" data-reactid="57"><div class="screenshot" style="background-image:none;" data-reactid="58"></div></div><div class="title " data-reactid="59"><span dir="auto" data-reactid="60"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="61"><a data-reactid="62"><div class="tile" aria-hidden="true" data-reactid="63"><div class="screenshot" style="background-image:none;" data-reactid="64"></div></div><div class="title " data-reactid="65"><span dir="auto" data-reactid="66"></span></div></a></li></ul><div class="edit-topsites-wrapper" data-reactid="67"><div class="edit-topsites-button" data-reactid="68"><button class="edit" title="Customize your Top Sites section" data-reactid="69"><span data-reactid="70">Edit</span></button></div></div></div></section><div class="sections-list" data-reactid="71"><section class="collapsible-section section animation-enabled" data-reactid="72"><div class="section-top-bar" data-reactid="73"><h3 class="section-title" data-reactid="74"><span class="click-target" data-reactid="75"><span class="icon icon-small-spacer icon-pocket" data-reactid="76"></span><span data-reactid="77">Recommended by Pocket</span><span class="icon icon-arrowhead-down" data-reactid="78"></span></span></h3></div><div class="section-body" data-reactid="79"><ul class="section-list" style="padding:0;" data-reactid="80"><li class="card-outer placeholder" data-reactid="81"><a data-reactid="82"><div class="card" data-reactid="83"><div class="card-details no-image" data-reactid="84"><div class="card-text no-context no-description no-host-name no-image" data-reactid="85"><h4 class="card-title" dir="auto" data-reactid="86"></h4><p class="card-description" dir="auto" data-reactid="87"></p></div><div class="card-context" data-reactid="88"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="89"><a data-reactid="90"><div class="card" data-reactid="91"><div class="card-details no-image" data-reactid="92"><div class="card-text no-context no-description no-host-name no-image" data-reactid="93"><h4 class="card-title" dir="auto" data-reactid="94"></h4><p class="card-description" dir="auto" data-reactid="95"></p></div><div class="card-context" data-reactid="96"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="97"><a data-reactid="98"><div class="card" data-reactid="99"><div class="card-details no-image" data-reactid="100"><div class="card-text no-context no-description no-host-name no-image" data-reactid="101"><h4 class="card-title" dir="auto" data-reactid="102"></h4><p class="card-description" dir="auto" data-reactid="103"></p></div><div class="card-context" data-reactid="104"></div></div></div></a></li></ul><div class="topic" data-reactid="105"><span data-reactid="106"><span data-reactid="107">Loowdiiji lolluɗi:</span></span><ul data-reactid="108"></ul></div></div></section><section class="collapsible-section section animation-enabled" data-reactid="109"><div class="section-top-bar" data-reactid="110"><h3 class="section-title" data-reactid="111"><span class="click-target" data-reactid="112"><span class="icon icon-small-spacer icon-highlights" data-reactid="113"></span><span data-reactid="114">Jalbine</span><span class="icon icon-arrowhead-down" data-reactid="115"></span></span></h3></div><div class="section-body" data-reactid="116"><ul class="section-list" style="padding:0;" data-reactid="117"><li class="card-outer placeholder" data-reactid="118"><a data-reactid="119"><div class="card" data-reactid="120"><div class="card-details no-image" data-reactid="121"><div class="card-text no-context no-description no-host-name no-image" data-reactid="122"><h4 class="card-title" dir="auto" data-reactid="123"></h4><p class="card-description" dir="auto" data-reactid="124"></p></div><div class="card-context" data-reactid="125"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="126"><a data-reactid="127"><div class="card" data-reactid="128"><div class="card-details no-image" data-reactid="129"><div class="card-text no-context no-description no-host-name no-image" data-reactid="130"><h4 class="card-title" dir="auto" data-reactid="131"></h4><p class="card-description" dir="auto" data-reactid="132"></p></div><div class="card-context" data-reactid="133"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="134"><a data-reactid="135"><div class="card" data-reactid="136"><div class="card-details no-image" data-reactid="137"><div class="card-text no-context no-description no-host-name no-image" data-reactid="138"><h4 class="card-title" dir="auto" data-reactid="139"></h4><p class="card-description" dir="auto" data-reactid="140"></p></div><div class="card-context" data-reactid="141"></div></div></div></a></li></ul></div></section></div></div><!-- react-empty: 142 --></main></div></div>
     <div id="snippets-container">
       <div id="snippets"></div>
     </div>
     <script>
 // Don't directly load the following scripts as part of html to let the page
 // finish loading to render the content sooner.
 for (const src of [
   "resource://activity-stream/prerendered/static/activity-stream-initial-state.js",
--- a/browser/extensions/activity-stream/prerendered/locales/ff/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/ff/activity-stream-strings.js
@@ -1,98 +1,98 @@
 // Note - this is a generated file.
 window.gActivityStreamStrings = {
   "newtab_page_title": "Tabbere hesere",
   "default_label_loading": "Ina loowa...",
   "header_top_sites": "Lowe dowrowe",
   "header_stories": "Jaŋte dowrowe",
-  "header_highlights": "Highlights",
+  "header_highlights": "Jalbine",
   "header_visit_again": "Yillo kadi",
   "header_bookmarks": "Maanto ɗerewol kesol",
   "header_recommended_by": "Recommended by {provider}",
   "header_bookmarks_placeholder": "A alaa hay maanto ɗerewol gootol jooni.",
   "header_stories_from": "ummoraade e",
   "type_label_visited": "Yilliima",
   "type_label_bookmarked": "Bookmarked",
   "type_label_synced": "Synced from another device",
-  "type_label_recommended": "Trending",
+  "type_label_recommended": "Ina tiindii",
   "type_label_open": "Uddit",
   "type_label_topic": "Loowdi",
   "type_label_now": "Jooni",
   "menu_action_bookmark": "Maanto ɗerewol",
   "menu_action_remove_bookmark": "Momtu maanto ɗerewol",
   "menu_action_copy_address": "Copy Address",
   "menu_action_email_link": "Email Link…",
   "menu_action_open_new_window": "Open in a New Window",
-  "menu_action_open_private_window": "Open in a New Private Window",
+  "menu_action_open_private_window": "Uddit e Henorde Suturo Hesere",
   "menu_action_dismiss": "Salo",
   "menu_action_delete": "Delete from History",
-  "menu_action_pin": "Pin",
-  "menu_action_unpin": "Unpin",
-  "confirm_history_delete_p1": "Are you sure you want to delete every instance of this page from your history?",
-  "confirm_history_delete_notice_p2": "This action cannot be undone.",
-  "menu_action_save_to_pocket": "Save to Pocket",
-  "search_for_something_with": "Search for {search_term} with:",
-  "search_button": "Search",
-  "search_header": "{search_engine_name} Search",
-  "search_web_placeholder": "Search the Web",
-  "search_settings": "Change Search Settings",
-  "section_info_option": "Info",
-  "section_info_send_feedback": "Send Feedback",
-  "section_info_privacy_notice": "Privacy Notice",
-  "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.",
-  "section_disclaimer_topstories_linktext": "Learn how it works.",
-  "section_disclaimer_topstories_buttontext": "Okay, got it",
+  "menu_action_pin": "Ñippu",
+  "menu_action_unpin": "Ñippit",
+  "confirm_history_delete_p1": "Aɗa yananaa yiɗde momtude kala cilol ngoo hello e to aslol maa?",
+  "confirm_history_delete_notice_p2": "Ngal baɗal waawaa firteede.",
+  "menu_action_save_to_pocket": "Danndu e Pocket",
+  "search_for_something_with": "Yiyloro {search_term} ɗumɗoo:",
+  "search_button": "Yiylo",
+  "search_header": "{search_engine_name} Yiylo",
+  "search_web_placeholder": "Yiylo geese",
+  "search_settings": "Waylu Teelte Njiylawu",
+  "section_info_option": "Kabaruuji",
+  "section_info_send_feedback": "Neldu duttinal",
+  "section_info_privacy_notice": "Tintinol Suturo",
+  "section_disclaimer_topstories": "Daarti ɓurɗi teeŋtude e geese ɗee, cuɓaaɗi e yowitaade e ko tarɗaa. Ummoraade e Pocket, jeyaaɗo jooni e Mozilla.",
+  "section_disclaimer_topstories_linktext": "Humpito hol no ɗum gollortoo.",
+  "section_disclaimer_topstories_buttontext": "Eey, mi faamii",
   "welcome_title": "Welcome to new tab",
   "welcome_body": "Firefox will use this space to show your most relevant bookmarks, articles, videos, and pages you’ve recently visited, so you can get back to them easily.",
-  "welcome_label": "Identifying your Highlights",
+  "welcome_label": "Heɓtinde Jalbine maa",
   "time_label_less_than_minute": "<1m",
   "time_label_minute": "{number}m",
   "time_label_hour": "{number}h",
   "time_label_day": "{number}d",
   "settings_pane_button_label": "Customize your New Tab page",
   "settings_pane_header": "New Tab Preferences",
-  "settings_pane_body2": "Choose what you see on this page.",
+  "settings_pane_body2": "Suɓo ko njiyataa e ngoo hello.",
   "settings_pane_search_header": "Search",
   "settings_pane_search_body": "Search the Web from your new tab.",
   "settings_pane_topsites_header": "Top Sites",
   "settings_pane_topsites_body": "Access the websites you visit most.",
   "settings_pane_topsites_options_showmore": "Show two rows",
-  "settings_pane_bookmarks_header": "Recent Bookmarks",
-  "settings_pane_bookmarks_body": "Your newly created bookmarks in one handy location.",
+  "settings_pane_bookmarks_header": "Maantore kese",
+  "settings_pane_bookmarks_body": "Maantore maa cosaaɗe ko ɓooyaani e nder nokku gooto beemtinɗo.",
   "settings_pane_visit_again_header": "Visit Again",
-  "settings_pane_visit_again_body": "Firefox will show you parts of your browsing history that you might want to remember or get back to.",
-  "settings_pane_highlights_header": "Highlights",
-  "settings_pane_highlights_body2": "Find your way back to interesting things you’ve recently visited or bookmarked.",
-  "settings_pane_highlights_options_bookmarks": "Bookmarks",
-  "settings_pane_highlights_options_visited": "Yilliima lowe",
-  "settings_pane_snippets_header": "Snippets",
+  "settings_pane_visit_again_body": "Firefox maa holloye huunde e aslol banngogol maa ɗi pot-ɗaa yiɗde siiftorde walla ruttaade heen.",
+  "settings_pane_highlights_header": "Jalbine",
+  "settings_pane_highlights_body2": "Yiytu laawol maa ruttaade e geɗe maantinɗe jilliɗaa ko ɓooyaani walla maantoraaɗe.",
+  "settings_pane_highlights_options_bookmarks": "Maantore",
+  "settings_pane_highlights_options_visited": "Lowe Jillaaɗe",
+  "settings_pane_snippets_header": "Taƴitine",
   "settings_pane_snippets_body": "Read short and sweet updates from Mozilla about Firefox, internet culture, and the occasional random meme.",
   "settings_pane_done_button": "Done",
   "settings_pane_topstories_options_sponsored": "Show Sponsored Stories",
   "edit_topsites_button_text": "Edit",
   "edit_topsites_button_label": "Customize your Top Sites section",
   "edit_topsites_showmore_button": "Show More",
   "edit_topsites_showless_button": "Show Fewer",
   "edit_topsites_done_button": "Done",
   "edit_topsites_pin_button": "Pin this site",
-  "edit_topsites_unpin_button": "Unpin this site",
+  "edit_topsites_unpin_button": "Ñippit ndee lowre",
   "edit_topsites_edit_button": "Edit this site",
   "edit_topsites_dismiss_button": "Dismiss this site",
-  "edit_topsites_add_button": "Add",
+  "edit_topsites_add_button": "Ɓeydu",
   "topsites_form_add_header": "New Top Site",
   "topsites_form_edit_header": "Edit Top Site",
   "topsites_form_title_placeholder": "Naatnu tiitoonde",
-  "topsites_form_url_placeholder": "Type or paste a URL",
-  "topsites_form_add_button": "Add",
-  "topsites_form_save_button": "Save",
+  "topsites_form_url_placeholder": "Tappu walla ɗakku URL",
+  "topsites_form_add_button": "Ɓeydu",
+  "topsites_form_save_button": "Danndu",
   "topsites_form_cancel_button": "Haaytu",
-  "topsites_form_url_validation": "Valid URL required",
-  "pocket_read_more": "Popular Topics:",
+  "topsites_form_url_validation": "URL Moƴƴo ina naamnaa",
+  "pocket_read_more": "Loowdiiji lolluɗi:",
   "pocket_read_even_more": "View More Stories",
   "pocket_feedback_header": "The best of the web, curated by over 25 million people.",
   "pocket_description": "Discover high-quality content you might otherwise miss, with help from Pocket, now part of Mozilla.",
   "highlights_empty_state": "Start browsing, and we’ll show some of the great articles, videos, and other pages you’ve recently visited or bookmarked here.",
   "topstories_empty_state": "You’ve caught up. Check back later for more top stories from {provider}. Can’t wait? Select a popular topic to find more great stories from around the web.",
   "manual_migration_explanation2": "Try Firefox with the bookmarks, history and passwords from another browser.",
-  "manual_migration_cancel_button": "No Thanks",
-  "manual_migration_import_button": "Import Now"
+  "manual_migration_cancel_button": "Alaa, moƴƴii",
+  "manual_migration_import_button": "Jiggo Jooni"
 };
new file mode 100644
--- /dev/null
+++ b/browser/extensions/activity-stream/prerendered/locales/gn/activity-stream-prerendered.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<html lang="gn" dir="ltr">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="Content-Security-Policy-Report-Only" content="script-src 'unsafe-inline'; img-src http: https: data: blob:; style-src 'unsafe-inline'; child-src 'none'; object-src 'none'; report-uri https://tiles.services.mozilla.com/v4/links/activity-stream/csp">
+    <link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
+    <link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
+  </head>
+  <body class="activity-stream">
+    <div id="root"><div class="outer-wrapper fixed-to-top" data-reactroot="" data-reactid="1" data-react-checksum="1247799377"><main data-reactid="2"><div class="search-wrapper" data-reactid="3"><label for="newtab-search-text" class="search-label" data-reactid="4"><span class="sr-only" data-reactid="5"><span data-reactid="6">Ñandutivevépe Jeheka</span></span></label><input type="search" id="newtab-search-text" maxlength="256" placeholder="Ñandutivevépe Jeheka" title="Ñandutivevépe Jeheka" data-reactid="7"/><button id="searchSubmit" class="search-button" title="Eheka" data-reactid="8"><span class="sr-only" data-reactid="9"><span data-reactid="10">Eheka</span></span></button></div><div class="body-wrapper" data-reactid="11"><section class="collapsible-section top-sites animation-enabled" data-reactid="12"><div class="section-top-bar" data-reactid="13"><h3 class="section-title" data-reactid="14"><span class="click-target" data-reactid="15"><span class="icon icon-small-spacer icon-topsites" data-reactid="16"></span><span data-reactid="17">Tenda Ojehechavéva</span><span class="icon icon-arrowhead-down" data-reactid="18"></span></span></h3><span class="section-info-option" data-reactid="19"><img class="info-option-icon" title="Kuaarã" aria-haspopup="true" aria-controls="info-option" aria-expanded="false" role="note" tabindex="0" data-reactid="20"/><div class="info-option" data-reactid="21"><div class="info-option-header" role="heading" data-reactid="22"><span data-reactid="23">Tenda Ojeikevéva</span></div><p class="info-option-body" data-reactid="24"><span data-reactid="25">Eike ñandutirenda rehechajepivévape.</span></p><div class="info-option-manage" data-reactid="26"><button data-reactid="27"><span data-reactid="28">Tendayke Pyahu Jeguererohoryrã</span></button></div></div></span></div><div class="section-body" data-reactid="29"><ul class="top-sites-list" data-reactid="30"><li class="top-site-outer placeholder" data-reactid="31"><a data-reactid="32"><div class="tile" aria-hidden="true" data-reactid="33"><div class="screenshot" style="background-image:none;" data-reactid="34"></div></div><div class="title " data-reactid="35"><span dir="auto" data-reactid="36"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="37"><a data-reactid="38"><div class="tile" aria-hidden="true" data-reactid="39"><div class="screenshot" style="background-image:none;" data-reactid="40"></div></div><div class="title " data-reactid="41"><span dir="auto" data-reactid="42"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="43"><a data-reactid="44"><div class="tile" aria-hidden="true" data-reactid="45"><div class="screenshot" style="background-image:none;" data-reactid="46"></div></div><div class="title " data-reactid="47"><span dir="auto" data-reactid="48"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="49"><a data-reactid="50"><div class="tile" aria-hidden="true" data-reactid="51"><div class="screenshot" style="background-image:none;" data-reactid="52"></div></div><div class="title " data-reactid="53"><span dir="auto" data-reactid="54"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="55"><a data-reactid="56"><div class="tile" aria-hidden="true" data-reactid="57"><div class="screenshot" style="background-image:none;" data-reactid="58"></div></div><div class="title " data-reactid="59"><span dir="auto" data-reactid="60"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="61"><a data-reactid="62"><div class="tile" aria-hidden="true" data-reactid="63"><div class="screenshot" style="background-image:none;" data-reactid="64"></div></div><div class="title " data-reactid="65"><span dir="auto" data-reactid="66"></span></div></a></li></ul><div class="edit-topsites-wrapper" data-reactid="67"><div class="edit-topsites-button" data-reactid="68"><button class="edit" title="Eñemomba&#x27;e Tendáre ojehechaukahápe Togue Ojehechavéva" data-reactid="69"><span data-reactid="70">Mbosako&#x27;i</span></button></div></div></div></section><div class="sections-list" data-reactid="71"><section class="collapsible-section section animation-enabled" data-reactid="72"><div class="section-top-bar" data-reactid="73"><h3 class="section-title" data-reactid="74"><span class="click-target" data-reactid="75"><span class="icon icon-small-spacer icon-pocket" data-reactid="76"></span><span data-reactid="77">Pocket he&#x27;i ndéve reike hag̃ua</span><span class="icon icon-arrowhead-down" data-reactid="78"></span></span></h3></div><div class="section-body" data-reactid="79"><ul class="section-list" style="padding:0;" data-reactid="80"><li class="card-outer placeholder" data-reactid="81"><a data-reactid="82"><div class="card" data-reactid="83"><div class="card-details no-image" data-reactid="84"><div class="card-text no-context no-description no-host-name no-image" data-reactid="85"><h4 class="card-title" dir="auto" data-reactid="86"></h4><p class="card-description" dir="auto" data-reactid="87"></p></div><div class="card-context" data-reactid="88"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="89"><a data-reactid="90"><div class="card" data-reactid="91"><div class="card-details no-image" data-reactid="92"><div class="card-text no-context no-description no-host-name no-image" data-reactid="93"><h4 class="card-title" dir="auto" data-reactid="94"></h4><p class="card-description" dir="auto" data-reactid="95"></p></div><div class="card-context" data-reactid="96"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="97"><a data-reactid="98"><div class="card" data-reactid="99"><div class="card-details no-image" data-reactid="100"><div class="card-text no-context no-description no-host-name no-image" data-reactid="101"><h4 class="card-title" dir="auto" data-reactid="102"></h4><p class="card-description" dir="auto" data-reactid="103"></p></div><div class="card-context" data-reactid="104"></div></div></div></a></li></ul><div class="topic" data-reactid="105"><span data-reactid="106"><span data-reactid="107">Ñe&#x27;ẽmbyrã Ojehayhuvéva:</span></span><ul data-reactid="108"></ul></div></div></section><section class="collapsible-section section animation-enabled" data-reactid="109"><div class="section-top-bar" data-reactid="110"><h3 class="section-title" data-reactid="111"><span class="click-target" data-reactid="112"><span class="icon icon-small-spacer icon-highlights" data-reactid="113"></span><span data-reactid="114">Mba&#x27;eporãitéva</span><span class="icon icon-arrowhead-down" data-reactid="115"></span></span></h3></div><div class="section-body" data-reactid="116"><ul class="section-list" style="padding:0;" data-reactid="117"><li class="card-outer placeholder" data-reactid="118"><a data-reactid="119"><div class="card" data-reactid="120"><div class="card-details no-image" data-reactid="121"><div class="card-text no-context no-description no-host-name no-image" data-reactid="122"><h4 class="card-title" dir="auto" data-reactid="123"></h4><p class="card-description" dir="auto" data-reactid="124"></p></div><div class="card-context" data-reactid="125"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="126"><a data-reactid="127"><div class="card" data-reactid="128"><div class="card-details no-image" data-reactid="129"><div class="card-text no-context no-description no-host-name no-image" data-reactid="130"><h4 class="card-title" dir="auto" data-reactid="131"></h4><p class="card-description" dir="auto" data-reactid="132"></p></div><div class="card-context" data-reactid="133"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="134"><a data-reactid="135"><div class="card" data-reactid="136"><div class="card-details no-image" data-reactid="137"><div class="card-text no-context no-description no-host-name no-image" data-reactid="138"><h4 class="card-title" dir="auto" data-reactid="139"></h4><p class="card-description" dir="auto" data-reactid="140"></p></div><div class="card-context" data-reactid="141"></div></div></div></a></li></ul></div></section></div></div><!-- react-empty: 142 --></main></div></div>
+    <div id="snippets-container">
+      <div id="snippets"></div>
+    </div>
+    <script>
+// Don't directly load the following scripts as part of html to let the page
+// finish loading to render the content sooner.
+for (const src of [
+  "resource://activity-stream/prerendered/static/activity-stream-initial-state.js",
+  "chrome://browser/content/contentSearchUI.js",
+  "resource://activity-stream/vendor/react.js",
+  "resource://activity-stream/vendor/react-dom.js",
+  "resource://activity-stream/vendor/prop-types.js",
+  "resource://activity-stream/vendor/react-intl.js",
+  "resource://activity-stream/vendor/redux.js",
+  "resource://activity-stream/vendor/react-redux.js",
+  "resource://activity-stream/prerendered/gn/activity-stream-strings.js",
+  "resource://activity-stream/data/content/activity-stream.bundle.js"
+]) {
+  // These dynamically inserted scripts by default are async, but we need them
+  // to load in the desired order (i.e., bundle last).
+  const script = document.body.appendChild(document.createElement("script"));
+  script.async = false;
+  script.src = src;
+}
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/extensions/activity-stream/prerendered/locales/gn/activity-stream-strings.js
@@ -0,0 +1,98 @@
+// Note - this is a generated file.
+window.gActivityStreamStrings = {
+  "newtab_page_title": "Tendayke Pyahu",
+  "default_label_loading": "Oñemyenyhẽhína…",
+  "header_top_sites": "Tenda Ojehechavéva",
+  "header_stories": "Mombe'upy Oñemoñe'ẽvéva",
+  "header_highlights": "Mba'eporãitéva",
+  "header_visit_again": "Eikejey Ehechávo",
+  "header_bookmarks": "Tendayke Ramoguáva",
+  "header_recommended_by": "{provider} he'i ndéve reike hag̃ua",
+  "header_bookmarks_placeholder": "Ndererekói gueteri techaukaha ñongatupyre.",
+  "header_stories_from": "omombe'úva",
+  "type_label_visited": "Jeikepyre",
+  "type_label_bookmarked": "Oñeñongatuva'ekue techaukaháramo",
+  "type_label_synced": "Oñembojuehepyre ambue mba'e'oka ndive",
+  "type_label_recommended": "Ojehechajepíva",
+  "type_label_open": "Jeike",
+  "type_label_topic": "Ñe'ẽmbyrã",
+  "type_label_now": "Ko'ág̃a",
+  "menu_action_bookmark": "Techaukaha",
+  "menu_action_remove_bookmark": "Techaukaha Mboguete",
+  "menu_action_copy_address": "Kundaharape Mbohasarã",
+  "menu_action_email_link": "Joajuha Mondo…",
+  "menu_action_open_new_window": "Jeike Ovetã Pyahúpe",
+  "menu_action_open_private_window": "Jeike Ovetã Ñemi Pyahúpe",
+  "menu_action_dismiss": "Emboyke",
+  "menu_action_delete": "Tembiasakue Rysýigui Ñeguenohẽ",
+  "menu_action_pin": "Mboja",
+  "menu_action_unpin": "Mboja'ỹ",
+  "confirm_history_delete_p1": "Añetehápepa renohẽse oimeraẽva mba'e ko toguepegua tembiasakue rysýigui?",
+  "confirm_history_delete_notice_p2": "Ko ojejapóva ndaikatuvéima oñemboguevi.",
+  "menu_action_save_to_pocket": "Eñongatu Pocket-pe",
+  "search_for_something_with": "Eheka {search_term} eipurúvo:",
+  "search_button": "Eheka",
+  "search_header": "Eheka {search_engine_name} ndive",
+  "search_web_placeholder": "Ñandutivevépe Jeheka",
+  "search_settings": "Jeheka Reko Moambue",
+  "section_info_option": "Kuaarã",
+  "section_info_send_feedback": "Temimo'ã Ñemondo",
+  "section_info_privacy_notice": "Tekovepypegua Rehegua",
+  "section_disclaimer_topstories": "Mba'erechapyrã ñandutivevepegua ojeiporavóva ndéve g̃uarã ojejesareko rupi remoñe'ẽva jepi rehe. Pocket guive ha'éva ko'ág̃a Mozilla mba'e.",
+  "section_disclaimer_topstories_linktext": "Eikuaave mba'éichapa oiko.",
+  "section_disclaimer_topstories_buttontext": "Oĩma, hesakãma chéve",
+  "welcome_title": "Tereg̃uahẽ porãite ko tendayke pyahúpe",
+  "welcome_body": "Firefox oipurúta ko tenda ohechaukávo ndéve techaukaha. moñe'ẽrã, ta'ãngamýi ha togue rehecharamovéva kuri ikatuháicha reikejey umívape pya'eve.",
+  "welcome_label": "Ojehechahína Mba'erechapyrã nemba'éva",
+  "time_label_less_than_minute": "<1m",
+  "time_label_minute": "{number}m",
+  "time_label_hour": "{number}h",
+  "time_label_day": "{number}d",
+  "settings_pane_button_label": "Eñemomba'e ne Tendayke Pyahu roguére",
+  "settings_pane_header": "Tendayke Pyahu Jeguererohoryrã",
+  "settings_pane_body2": "Eiporavo rehechaséva ko toguépe.",
+  "settings_pane_search_header": "Eheka",
+  "settings_pane_search_body": "Eheka ñandutivevépe tendayke pyahu guive.",
+  "settings_pane_topsites_header": "Tenda Ojeikevéva",
+  "settings_pane_topsites_body": "Eike ñandutirenda rehechajepivévape.",
+  "settings_pane_topsites_options_showmore": "Mokõi tysýi jechauka",
+  "settings_pane_bookmarks_header": "Techaukaha Ramovéva",
+  "settings_pane_bookmarks_body": "Techaukaha remoheñóiva peteĩ tenda porãvévape.",
+  "settings_pane_visit_again_header": "Eike Jey",
+  "settings_pane_visit_again_body": "Firefox ohechaukáta ndéve rechámava'ekue ha oiméne nemandu'aséva hese ýrõ rehechasejeýva.",
+  "settings_pane_highlights_header": "Mba'erechapyrã",
+  "settings_pane_highlights_body2": "Ehekajey tape reipuruva'ekue reg̃uahẽvo ñandutiroguépe reguerohorýva'ekue.",
+  "settings_pane_highlights_options_bookmarks": "Techaukaha",
+  "settings_pane_highlights_options_visited": "Tenda Ojeikemáva",
+  "settings_pane_snippets_header": "Mba'epehẽ",
+  "settings_pane_snippets_body": "Emoñe'ẽ mba'epyahu oĩva Firefox, Ñandutiveve reko térã memekuéra rehegua.",
+  "settings_pane_done_button": "Oĩmbáma",
+  "settings_pane_topstories_options_sponsored": "Ehechauka Mombe'upy ojehepyme'ẽva'ekue hese",
+  "edit_topsites_button_text": "Mbosako'i",
+  "edit_topsites_button_label": "Eñemomba'e Tendáre ojehechaukahápe Togue Ojehechavéva",
+  "edit_topsites_showmore_button": "Ahechaseve",
+  "edit_topsites_showless_button": "Ahechase Mbykyve",
+  "edit_topsites_done_button": "Oĩmbáma",
+  "edit_topsites_pin_button": "Emboja ko tenda",
+  "edit_topsites_unpin_button": "Emboja'ỹ ko tenda",
+  "edit_topsites_edit_button": "Embosako'i ko tenda",
+  "edit_topsites_dismiss_button": "Emboguete ko tenda",
+  "edit_topsites_add_button": "Embojoapy",
+  "topsites_form_add_header": "Tenda Pyahu Ojeikevéva",
+  "topsites_form_edit_header": "Tenda Ojeikevéva Mbosako'i",
+  "topsites_form_title_placeholder": "Ehai herarã",
+  "topsites_form_url_placeholder": "Ehai térã emboja peteĩ URL",
+  "topsites_form_add_button": "Embojoapy",
+  "topsites_form_save_button": "Ñongatu",
+  "topsites_form_cancel_button": "Heja",
+  "topsites_form_url_validation": "Oñeikotevẽ URL oiko porãva",
+  "pocket_read_more": "Ñe'ẽmbyrã Ojehayhuvéva:",
+  "pocket_read_even_more": "Ahechaseve Mombe'upy",
+  "pocket_feedback_header": "Mba'e porãvéva ñandutivevepegua, oiporavóva ndéve g̃uarã 25 su tapicha.",
+  "pocket_description": "Ejuhu mba'erecharã iporãitéva Pocket rupive, ha'éva ko'ág̃a Mozilla rembipuru.",
+  "highlights_empty_state": "Eñepyrũ eikundaha ha rohechaukáta ndéve mba'ehai, mba'erecharã oĩva ha ambue ñandutirenda reikeva'ekue ýrõ rembotechaukava'ekue.",
+  "topstories_empty_state": "Ko'ág̃a reikuaapáma ipyahúva. Eikejey ag̃ave ápe eikuaávo mombe'upy pyahu {provider} oikuave'ẽva ndéve. Ndaikatuvéima reha'ãrõ? Eiporavo peteĩ ñe'ẽmbyrã ha emoñe'ẽve oĩvéva ñande yvy ape ári.",
+  "manual_migration_explanation2": "Eipuru Firefox reheve techaukaha, tembiasakue ha ñe'ẽñemi ambue kundaharapegua.",
+  "manual_migration_cancel_button": "Ag̃amiénte",
+  "manual_migration_import_button": "Egueroike Ko'ág̃a"
+};
new file mode 100644
--- /dev/null
+++ b/browser/extensions/activity-stream/prerendered/locales/gn/activity-stream.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html lang="gn" dir="ltr">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="Content-Security-Policy-Report-Only" content="script-src 'unsafe-inline'; img-src http: https: data: blob:; style-src 'unsafe-inline'; child-src 'none'; object-src 'none'; report-uri https://tiles.services.mozilla.com/v4/links/activity-stream/csp">
+    <link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
+    <link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
+  </head>
+  <body class="activity-stream">
+    <div id="root"></div>
+    <div id="snippets-container">
+      <div id="snippets"></div>
+    </div>
+    <script>
+// Don't directly load the following scripts as part of html to let the page
+// finish loading to render the content sooner.
+for (const src of [
+  "chrome://browser/content/contentSearchUI.js",
+  "resource://activity-stream/vendor/react.js",
+  "resource://activity-stream/vendor/react-dom.js",
+  "resource://activity-stream/vendor/prop-types.js",
+  "resource://activity-stream/vendor/react-intl.js",
+  "resource://activity-stream/vendor/redux.js",
+  "resource://activity-stream/vendor/react-redux.js",
+  "resource://activity-stream/prerendered/gn/activity-stream-strings.js",
+  "resource://activity-stream/data/content/activity-stream.bundle.js"
+]) {
+  // These dynamically inserted scripts by default are async, but we need them
+  // to load in the desired order (i.e., bundle last).
+  const script = document.body.appendChild(document.createElement("script"));
+  script.async = false;
+  script.src = src;
+}
+    </script>
+  </body>
+</html>
--- a/browser/extensions/activity-stream/prerendered/locales/ka/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/ka/activity-stream-strings.js
@@ -4,17 +4,17 @@ window.gActivityStreamStrings = {
   "default_label_loading": "იტვირთება…",
   "header_top_sites": "რჩეული საიტები",
   "header_stories": "რჩეული სტატიები",
   "header_highlights": "მნიშვნელოვანი საიტები",
   "header_visit_again": "ხელახლა ნახვა",
   "header_bookmarks": "ბოლოს ჩანიშნულები",
   "header_recommended_by": "რეკომენდებულია {provider}-ის მიერ",
   "header_bookmarks_placeholder": "სანიშნები ჯერ არაა დამატებული.",
-  "header_stories_from": "-იდან",
+  "header_stories_from": "მომწოდებელი:",
   "type_label_visited": "მონახულებული",
   "type_label_bookmarked": "ჩანიშნული",
   "type_label_synced": "სხვა მოწყობილობიდან დასინქრონებული",
   "type_label_recommended": "პოპულარული",
   "type_label_open": "გახსნა",
   "type_label_topic": "თემა",
   "type_label_now": "ახლა",
   "menu_action_bookmark": "ჩანიშვნა",
--- a/browser/extensions/activity-stream/prerendered/locales/kab/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/kab/activity-stream-strings.js
@@ -33,19 +33,19 @@ window.gActivityStreamStrings = {
   "search_for_something_with": "Nadi γef {search_term} s:",
   "search_button": "Nadi",
   "search_header": "Anadi {search_engine_name}",
   "search_web_placeholder": "Nadi di Web",
   "search_settings": "Snifel iγewwaṛen n unadi",
   "section_info_option": "Talɣut",
   "section_info_send_feedback": "Azen tikti",
   "section_info_privacy_notice": "Tasertit n tbaḍnit",
-  "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.",
-  "section_disclaimer_topstories_linktext": "Learn how it works.",
-  "section_disclaimer_topstories_buttontext": "Okay, got it",
+  "section_disclaimer_topstories": "Tiqṣiḍin ifazen ak deg Web, ttwafernent ilmend n wayen teqqareḍ. Seg Pocket i yuγal akka d aḥric n Mozilla.",
+  "section_disclaimer_topstories_linktext": "Issin amek i teddu.",
+  "section_disclaimer_topstories_buttontext": "Ih, awi-t-id",
   "welcome_title": "Ansuf ar yiccer amaynut",
   "welcome_body": "Firefox ad iseqdec tallunt akken ad d-yesken akk ticraḍ n isebtar iwulmen, imagraden, tividyutin, akked isebtar aniɣer terziḍ melmi kan, ihi tzemreḍ ad d-uɣaleḍ ɣer-sen s wudem fessusen.",
   "welcome_label": "Asulu n iferdisen tisura",
   "time_label_less_than_minute": "<1 n tesdat",
   "time_label_minute": "{number} n tesdatin",
   "time_label_hour": "{number} n isragen",
   "time_label_day": "{number}n wussan",
   "settings_pane_button_label": "Sagen asebter n yiccer-ik amaynut",
@@ -62,17 +62,17 @@ window.gActivityStreamStrings = {
   "settings_pane_visit_again_body": "Firefox ad d-yesken tukkist n umazray-ik n tunigin i tzemreḍ ad twalid tikelt-nniḍen.",
   "settings_pane_highlights_header": "Asebrureq",
   "settings_pane_highlights_body2": "Aff abrid-ik γer wayen i tḥemmleḍ i γef terziḍ yakan neγ tcerḍeḍ-t.",
   "settings_pane_highlights_options_bookmarks": "Ticraḍ n isebtar",
   "settings_pane_highlights_options_visited": "Ismal yettwarzan",
   "settings_pane_snippets_header": "Tiwzillin",
   "settings_pane_snippets_body": "Wali issalen n Mozilla γef Firefox, adlis internet, akked issalen nniṣen sya γer da.",
   "settings_pane_done_button": "Immed",
-  "settings_pane_topstories_options_sponsored": "Show Sponsored Stories",
+  "settings_pane_topstories_options_sponsored": "Sken Imagraden yesɛan imasayen",
   "edit_topsites_button_text": "Ẓreg",
   "edit_topsites_button_label": "Sagen tigezmi n ismal ifazen",
   "edit_topsites_showmore_button": "Sken ugar",
   "edit_topsites_showless_button": "Sken qel",
   "edit_topsites_done_button": "Immed",
   "edit_topsites_pin_button": "Ṭṭef asmel-agi",
   "edit_topsites_unpin_button": "Serreḥ asmel-agi",
   "edit_topsites_edit_button": "Ẓreg asmel-agi",
--- a/browser/extensions/activity-stream/prerendered/locales/ko/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/ko/activity-stream-strings.js
@@ -33,17 +33,17 @@ window.gActivityStreamStrings = {
   "search_for_something_with": "다음에서 {search_term} 검색:",
   "search_button": "검색",
   "search_header": "{search_engine_name} 검색",
   "search_web_placeholder": "웹 검색",
   "search_settings": "검색 설정 바꾸기",
   "section_info_option": "정보",
   "section_info_send_feedback": "의견 보내기",
   "section_info_privacy_notice": "개인 정보 보호 정책",
-  "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.",
+  "section_disclaimer_topstories": "Pocket을 통해서 사용자가 읽은 글을 기반으로 가장 재미있는 글을 찾아주는 글들을 읽어보세요. 이제 Mozilla와 함께 합니다.",
   "section_disclaimer_topstories_linktext": "어떻게 작동 하는지 알아봅시다.",
   "section_disclaimer_topstories_buttontext": "알겠습니다.",
   "welcome_title": "새 탭을 소개합니다",
   "welcome_body": "최근에 방문한 관련있는 즐겨찾기나 글, 동영상, 페이지를 Firefox가 여기에 표시해서 쉽게 다시 찾아볼 수 있게 할 것입니다.",
   "welcome_label": "하이라이트 확인",
   "time_label_less_than_minute": "<1분",
   "time_label_minute": "{number}분",
   "time_label_hour": "{number}시",
@@ -54,23 +54,23 @@ window.gActivityStreamStrings = {
   "settings_pane_search_header": "검색",
   "settings_pane_search_body": "새 탭에서 웹을 검색하세요.",
   "settings_pane_topsites_header": "상위 사이트",
   "settings_pane_topsites_body": "가장 많이 방문한 웹 사이트에 접근하세요.",
   "settings_pane_topsites_options_showmore": "두 줄로 보기",
   "settings_pane_bookmarks_header": "최근 북마크",
   "settings_pane_bookmarks_body": "최근 북마크가 편리하게 한 곳에 나타납니다.",
   "settings_pane_visit_again_header": "다시 방문",
-  "settings_pane_visit_again_body": "Firefox will show you parts of your browsing history that you might want to remember or get back to.",
+  "settings_pane_visit_again_body": "Firefox가 사용자가 기억하거나 다시 보고 싶어하는 검색 기록의 일부를 보여줍니다.",
   "settings_pane_highlights_header": "하이라이트",
-  "settings_pane_highlights_body2": "Find your way back to interesting things you’ve recently visited or bookmarked.",
+  "settings_pane_highlights_body2": "최근에 방문했거나 북마크한 흥미로운 것들로 돌아갈 수 있는 방법입니다.",
   "settings_pane_highlights_options_bookmarks": "즐겨찾기",
   "settings_pane_highlights_options_visited": "방문한 사이트",
-  "settings_pane_snippets_header": "Snippets",
-  "settings_pane_snippets_body": "Read short and sweet updates from Mozilla about Firefox, internet culture, and the occasional random meme.",
+  "settings_pane_snippets_header": "짧은 요약",
+  "settings_pane_snippets_body": "Mozilla, Firefox, 인터넷 문화나 가끔 무작위 밈에 대해서 읽어보세요.",
   "settings_pane_done_button": "완료",
   "settings_pane_topstories_options_sponsored": "후원된 스토리",
   "edit_topsites_button_text": "수정",
   "edit_topsites_button_label": "상위 사이트 영역 꾸미기",
   "edit_topsites_showmore_button": "더보기",
   "edit_topsites_showless_button": "줄이기",
   "edit_topsites_done_button": "완료",
   "edit_topsites_pin_button": "이 사이트 고정",
@@ -86,13 +86,13 @@ window.gActivityStreamStrings = {
   "topsites_form_save_button": "저장",
   "topsites_form_cancel_button": "취소",
   "topsites_form_url_validation": "유효한 URL이 필요합니다",
   "pocket_read_more": "인기 주제:",
   "pocket_read_even_more": "더 많은 이야기 보기",
   "pocket_feedback_header": "2천 5백만 명에 의해 추천되는 최고의 웹입니다.",
   "pocket_description": "Mozilla와 하나가 된 Pocket의 도움으로 놓칠지도 모르는 고품질의 컨텐츠를 접해보세요.",
   "highlights_empty_state": "브라우징을 시작하면 최근 방문하거나 북마크한 좋은 글이나 영상, 페이지를 여기에 보여줍니다.",
-  "topstories_empty_state": "You’ve caught up. Check back later for more top stories from {provider}. Can’t wait? Select a popular topic to find more great stories from around the web.",
+  "topstories_empty_state": "다 왔습니다. {provider}에서 제공하는 주요 기사를 다시 확인해 보세요. 기다릴 수가 없나요? 주제를 선택하면 웹에서 볼 수 있는 가장 재미있는 글을 볼 수 있습니다.",
   "manual_migration_explanation2": "다른 브라우저에 있는 북마크, 기록, 비밀번호를 사용해 Firefox를 이용해 보세요.",
   "manual_migration_cancel_button": "괜찮습니다",
   "manual_migration_import_button": "지금 가져오기"
 };
--- a/browser/extensions/activity-stream/prerendered/locales/lo/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/lo/activity-stream-strings.js
@@ -1,16 +1,16 @@
 // Note - this is a generated file.
 window.gActivityStreamStrings = {
   "newtab_page_title": "ແທັບໃຫມ່",
   "default_label_loading": "ກຳລັງໂຫລດ…",
   "header_top_sites": "ເວັບໄຊຕ໌ຍອດນິຍົມ",
-  "header_stories": "Top Stories",
+  "header_stories": "ເລື່ອງເດັ່ນ​ໆ",
   "header_highlights": "ລາຍການເດັ່ນ",
-  "header_visit_again": "Visit Again",
+  "header_visit_again": "ຢ້ຽມຢາມອີກຄັ້ງ",
   "header_bookmarks": "Recent Bookmarks",
   "header_recommended_by": "Recommended by {provider}",
   "header_bookmarks_placeholder": "You don’t have any bookmarks yet.",
   "header_stories_from": "from",
   "type_label_visited": "ເຂົ້າໄປເບິງມາແລ້ວ",
   "type_label_bookmarked": "ບຸກມາກໄວ້ແລ້ວ",
   "type_label_synced": "ໄດ້ Sync ມາຈາກອຸປະກອນອື່ນ",
   "type_label_recommended": "Trending",
@@ -77,22 +77,22 @@ window.gActivityStreamStrings = {
   "edit_topsites_unpin_button": "Unpin this site",
   "edit_topsites_edit_button": "ແກ້ໄຂເວັບໄຊທ໌ນີ້",
   "edit_topsites_dismiss_button": "ຍົກເລີກເວັບໄຊທ໌ນີ້",
   "edit_topsites_add_button": "Add",
   "topsites_form_add_header": "New Top Site",
   "topsites_form_edit_header": "Edit Top Site",
   "topsites_form_title_placeholder": "ປ້ອນຊື່ເລື່ອງ",
   "topsites_form_url_placeholder": "Type or paste a URL",
-  "topsites_form_add_button": "Add",
-  "topsites_form_save_button": "Save",
-  "topsites_form_cancel_button": "Cancel",
+  "topsites_form_add_button": "ເພີ່ມ",
+  "topsites_form_save_button": "ບັນທຶກ",
+  "topsites_form_cancel_button": "ຍົກເລີກ",
   "topsites_form_url_validation": "Valid URL required",
   "pocket_read_more": "ຫົວຂໍ້ຍອດນິຍົມ:",
   "pocket_read_even_more": "View More Stories",
   "pocket_feedback_header": "The best of the web, curated by over 25 million people.",
   "pocket_description": "Discover high-quality content you might otherwise miss, with help from Pocket, now part of Mozilla.",
   "highlights_empty_state": "Start browsing, and we’ll show some of the great articles, videos, and other pages you’ve recently visited or bookmarked here.",
   "topstories_empty_state": "You’ve caught up. Check back later for more top stories from {provider}. Can’t wait? Select a popular topic to find more great stories from around the web.",
   "manual_migration_explanation2": "Try Firefox with the bookmarks, history and passwords from another browser.",
-  "manual_migration_cancel_button": "No Thanks",
+  "manual_migration_cancel_button": "ບໍ່, ຂອບໃຈ",
   "manual_migration_import_button": "ນຳເຂົ້າຕອນນີ້"
 };
--- a/browser/extensions/activity-stream/prerendered/locales/pt-BR/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/pt-BR/activity-stream-strings.js
@@ -37,17 +37,17 @@ window.gActivityStreamStrings = {
   "search_settings": "Alterar configurações de pesquisa",
   "section_info_option": "Info",
   "section_info_send_feedback": "Enviar feedback",
   "section_info_privacy_notice": "Política de Privacidade",
   "section_disclaimer_topstories": "As histórias mais interessantes na web, selecionadas baseadas no que você lê. Do Pocket, agora parte da Mozilla.",
   "section_disclaimer_topstories_linktext": "Saiba como funciona.",
   "section_disclaimer_topstories_buttontext": "Ok, entendi",
   "welcome_title": "Bem-vindo a nova aba",
-  "welcome_body": "O Firefox usará este espaço para mostrar seus favoritos, artigos, vídeos e páginas mais relevantes visitados recentemente, assim você pode voltar mais facilmente.",
+  "welcome_body": "O Firefox usará este espaço para mostrar seus favoritos, artigos, vídeos e páginas que você visitou recentemente, assim você pode voltar mais facilmente.",
   "welcome_label": "Identificando seus destaques",
   "time_label_less_than_minute": "<1m",
   "time_label_minute": "{number}m",
   "time_label_hour": "{number}h",
   "time_label_day": "{number}d",
   "settings_pane_button_label": "Personalizar sua página de nova aba",
   "settings_pane_header": "Preferências de novas abas",
   "settings_pane_body2": "Escolha o que verá nessa página.",
@@ -80,17 +80,17 @@ window.gActivityStreamStrings = {
   "edit_topsites_add_button": "Adicionar",
   "topsites_form_add_header": "Novo site popular",
   "topsites_form_edit_header": "Editar site popular",
   "topsites_form_title_placeholder": "Digite um título",
   "topsites_form_url_placeholder": "Digite ou cole um URL",
   "topsites_form_add_button": "Adicionar",
   "topsites_form_save_button": "Salvar",
   "topsites_form_cancel_button": "Cancelar",
-  "topsites_form_url_validation": "É necessário uma URL válida",
+  "topsites_form_url_validation": "É necessário um URL válido",
   "pocket_read_more": "Tópicos populares:",
   "pocket_read_even_more": "Ver mais histórias",
   "pocket_feedback_header": "O melhor da web, com curadoria de mais de 25 milhões de pessoas.",
   "pocket_description": "Descubra conteúdo de alta qualidade que você poderia ter perdido, com a ajuda do Pocket, agora parte da Mozilla.",
   "highlights_empty_state": "Comece a navegar e nós mostraremos aqui alguns ótimos artigos, vídeos e outras páginas que você favoritou ou visitou recentemente.",
   "topstories_empty_state": "Você já viu tudo. Volte mais tarde para mais histórias do {provider}. Não consegue esperar? Escolha um assunto popular para encontrar mais grandes histórias através da web.",
   "manual_migration_explanation2": "Experimente o Firefox com os favoritos, histórico e senhas salvas em outros navegador.",
   "manual_migration_cancel_button": "Não, obrigado",
--- a/browser/extensions/activity-stream/prerendered/locales/sl/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/sl/activity-stream-strings.js
@@ -80,17 +80,17 @@ window.gActivityStreamStrings = {
   "edit_topsites_add_button": "Dodaj",
   "topsites_form_add_header": "Nova glavna stran",
   "topsites_form_edit_header": "Uredi glavno stran",
   "topsites_form_title_placeholder": "Vnesite ime",
   "topsites_form_url_placeholder": "Vnesite ali prilepite spletni naslov",
   "topsites_form_add_button": "Dodaj",
   "topsites_form_save_button": "Shrani",
   "topsites_form_cancel_button": "Prekliči",
-  "topsites_form_url_validation": "Vnesite veljaven URL",
+  "topsites_form_url_validation": "Vnesite veljaven spletni naslov",
   "pocket_read_more": "Priljubljene teme:",
   "pocket_read_even_more": "Prikaži več vesti",
   "pocket_feedback_header": "Najboljše s spleta, kar je izbralo več kot 25 milijonov ljudi.",
   "pocket_description": "Odkrijte kakovostno vsebino, ki bi jo sicer spregledali, s pomočjo Pocketa (zdaj dela Mozille).",
   "highlights_empty_state": "Začnite z brskanjem, mi pa vam bomo tu prikazovali odlične članke, videoposnetke ter druge strani, ki ste jih nedavno obiskali ali shranili med zaznamke.",
   "topstories_empty_state": "Zdaj ste seznanjeni z novicami. Vrnite se pozneje in si oglejte nove prispevke iz {provider}. Komaj čakate? Izberite priljubljeno temo in odkrijte več velikih zgodb na spletu.",
   "manual_migration_explanation2": "Preskusite Firefox z zaznamki, zgodovino in gesli iz drugega brskalnika.",
   "manual_migration_cancel_button": "Ne, hvala",
--- a/browser/extensions/activity-stream/prerendered/locales/sr/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/sr/activity-stream-prerendered.html
@@ -2,17 +2,17 @@
 <html lang="sr" dir="ltr">
   <head>
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy-Report-Only" content="script-src 'unsafe-inline'; img-src http: https: data: blob:; style-src 'unsafe-inline'; child-src 'none'; object-src 'none'; report-uri https://tiles.services.mozilla.com/v4/links/activity-stream/csp">
     <link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
     <link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
   </head>
   <body class="activity-stream">
-    <div id="root"><div class="outer-wrapper fixed-to-top" data-reactroot="" data-reactid="1" data-react-checksum="1963381674"><main data-reactid="2"><div class="search-wrapper" data-reactid="3"><label for="newtab-search-text" class="search-label" data-reactid="4"><span class="sr-only" data-reactid="5"><span data-reactid="6">Претражујте веб</span></span></label><input type="search" id="newtab-search-text" maxlength="256" placeholder="Претражујте веб" title="Претражујте веб" data-reactid="7"/><button id="searchSubmit" class="search-button" title="Претражи" data-reactid="8"><span class="sr-only" data-reactid="9"><span data-reactid="10">Претражи</span></span></button></div><div class="body-wrapper" data-reactid="11"><section class="collapsible-section top-sites animation-enabled" data-reactid="12"><div class="section-top-bar" data-reactid="13"><h3 class="section-title" data-reactid="14"><span class="click-target" data-reactid="15"><span class="icon icon-small-spacer icon-topsites" data-reactid="16"></span><span data-reactid="17">Омиљени сајтови</span><span class="icon icon-arrowhead-down" data-reactid="18"></span></span></h3><span class="section-info-option" data-reactid="19"><img class="info-option-icon" title="Инфо" aria-haspopup="true" aria-controls="info-option" aria-expanded="false" role="note" tabindex="0" data-reactid="20"/><div class="info-option" data-reactid="21"><div class="info-option-header" role="heading" data-reactid="22"><span data-reactid="23">Омиљени сајтови</span></div><p class="info-option-body" data-reactid="24"><span data-reactid="25">Приступите најпосећенијим веб сајтовима.</span></p><div class="info-option-manage" data-reactid="26"><button data-reactid="27"><span data-reactid="28">Поставке новог језичка</span></button></div></div></span></div><div class="section-body" data-reactid="29"><ul class="top-sites-list" data-reactid="30"><li class="top-site-outer placeholder" data-reactid="31"><a data-reactid="32"><div class="tile" aria-hidden="true" data-reactid="33"><div class="screenshot" style="background-image:none;" data-reactid="34"></div></div><div class="title " data-reactid="35"><span dir="auto" data-reactid="36"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="37"><a data-reactid="38"><div class="tile" aria-hidden="true" data-reactid="39"><div class="screenshot" style="background-image:none;" data-reactid="40"></div></div><div class="title " data-reactid="41"><span dir="auto" data-reactid="42"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="43"><a data-reactid="44"><div class="tile" aria-hidden="true" data-reactid="45"><div class="screenshot" style="background-image:none;" data-reactid="46"></div></div><div class="title " data-reactid="47"><span dir="auto" data-reactid="48"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="49"><a data-reactid="50"><div class="tile" aria-hidden="true" data-reactid="51"><div class="screenshot" style="background-image:none;" data-reactid="52"></div></div><div class="title " data-reactid="53"><span dir="auto" data-reactid="54"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="55"><a data-reactid="56"><div class="tile" aria-hidden="true" data-reactid="57"><div class="screenshot" style="background-image:none;" data-reactid="58"></div></div><div class="title " data-reactid="59"><span dir="auto" data-reactid="60"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="61"><a data-reactid="62"><div class="tile" aria-hidden="true" data-reactid="63"><div class="screenshot" style="background-image:none;" data-reactid="64"></div></div><div class="title " data-reactid="65"><span dir="auto" data-reactid="66"></span></div></a></li></ul><div class="edit-topsites-wrapper" data-reactid="67"><div class="edit-topsites-button" data-reactid="68"><button class="edit" title="Прилагодите секцију омиљених сајтова" data-reactid="69"><span data-reactid="70">Уреди</span></button></div></div></div></section><div class="sections-list" data-reactid="71"><section class="collapsible-section section animation-enabled" data-reactid="72"><div class="section-top-bar" data-reactid="73"><h3 class="section-title" data-reactid="74"><span class="click-target" data-reactid="75"><span class="icon icon-small-spacer icon-pocket" data-reactid="76"></span><span data-reactid="77">Предложио Pocket</span><span class="icon icon-arrowhead-down" data-reactid="78"></span></span></h3></div><div class="section-body" data-reactid="79"><ul class="section-list" style="padding:0;" data-reactid="80"><li class="card-outer placeholder" data-reactid="81"><a data-reactid="82"><div class="card" data-reactid="83"><div class="card-details no-image" data-reactid="84"><div class="card-text no-context no-description no-host-name no-image" data-reactid="85"><h4 class="card-title" dir="auto" data-reactid="86"></h4><p class="card-description" dir="auto" data-reactid="87"></p></div><div class="card-context" data-reactid="88"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="89"><a data-reactid="90"><div class="card" data-reactid="91"><div class="card-details no-image" data-reactid="92"><div class="card-text no-context no-description no-host-name no-image" data-reactid="93"><h4 class="card-title" dir="auto" data-reactid="94"></h4><p class="card-description" dir="auto" data-reactid="95"></p></div><div class="card-context" data-reactid="96"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="97"><a data-reactid="98"><div class="card" data-reactid="99"><div class="card-details no-image" data-reactid="100"><div class="card-text no-context no-description no-host-name no-image" data-reactid="101"><h4 class="card-title" dir="auto" data-reactid="102"></h4><p class="card-description" dir="auto" data-reactid="103"></p></div><div class="card-context" data-reactid="104"></div></div></div></a></li></ul><div class="topic" data-reactid="105"><span data-reactid="106"><span data-reactid="107">Популарне теме:</span></span><ul data-reactid="108"></ul></div></div></section><section class="collapsible-section section animation-enabled" data-reactid="109"><div class="section-top-bar" data-reactid="110"><h3 class="section-title" data-reactid="111"><span class="click-target" data-reactid="112"><span class="icon icon-small-spacer icon-highlights" data-reactid="113"></span><span data-reactid="114">Истакнуто</span><span class="icon icon-arrowhead-down" data-reactid="115"></span></span></h3></div><div class="section-body" data-reactid="116"><ul class="section-list" style="padding:0;" data-reactid="117"><li class="card-outer placeholder" data-reactid="118"><a data-reactid="119"><div class="card" data-reactid="120"><div class="card-details no-image" data-reactid="121"><div class="card-text no-context no-description no-host-name no-image" data-reactid="122"><h4 class="card-title" dir="auto" data-reactid="123"></h4><p class="card-description" dir="auto" data-reactid="124"></p></div><div class="card-context" data-reactid="125"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="126"><a data-reactid="127"><div class="card" data-reactid="128"><div class="card-details no-image" data-reactid="129"><div class="card-text no-context no-description no-host-name no-image" data-reactid="130"><h4 class="card-title" dir="auto" data-reactid="131"></h4><p class="card-description" dir="auto" data-reactid="132"></p></div><div class="card-context" data-reactid="133"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="134"><a data-reactid="135"><div class="card" data-reactid="136"><div class="card-details no-image" data-reactid="137"><div class="card-text no-context no-description no-host-name no-image" data-reactid="138"><h4 class="card-title" dir="auto" data-reactid="139"></h4><p class="card-description" dir="auto" data-reactid="140"></p></div><div class="card-context" data-reactid="141"></div></div></div></a></li></ul></div></section></div></div><!-- react-empty: 142 --></main></div></div>
+    <div id="root"><div class="outer-wrapper fixed-to-top" data-reactroot="" data-reactid="1" data-react-checksum="-1908626148"><main data-reactid="2"><div class="search-wrapper" data-reactid="3"><label for="newtab-search-text" class="search-label" data-reactid="4"><span class="sr-only" data-reactid="5"><span data-reactid="6">Претражи веб</span></span></label><input type="search" id="newtab-search-text" maxlength="256" placeholder="Претражи веб" title="Претражи веб" data-reactid="7"/><button id="searchSubmit" class="search-button" title="Претражи" data-reactid="8"><span class="sr-only" data-reactid="9"><span data-reactid="10">Претражи</span></span></button></div><div class="body-wrapper" data-reactid="11"><section class="collapsible-section top-sites animation-enabled" data-reactid="12"><div class="section-top-bar" data-reactid="13"><h3 class="section-title" data-reactid="14"><span class="click-target" data-reactid="15"><span class="icon icon-small-spacer icon-topsites" data-reactid="16"></span><span data-reactid="17">Омиљени сајтови</span><span class="icon icon-arrowhead-down" data-reactid="18"></span></span></h3><span class="section-info-option" data-reactid="19"><img class="info-option-icon" title="Инфо" aria-haspopup="true" aria-controls="info-option" aria-expanded="false" role="note" tabindex="0" data-reactid="20"/><div class="info-option" data-reactid="21"><div class="info-option-header" role="heading" data-reactid="22"><span data-reactid="23">Омиљени сајтови</span></div><p class="info-option-body" data-reactid="24"><span data-reactid="25">Приступите најпосећенијим веб сајтовима.</span></p><div class="info-option-manage" data-reactid="26"><button data-reactid="27"><span data-reactid="28">Поставке новог језичка</span></button></div></div></span></div><div class="section-body" data-reactid="29"><ul class="top-sites-list" data-reactid="30"><li class="top-site-outer placeholder" data-reactid="31"><a data-reactid="32"><div class="tile" aria-hidden="true" data-reactid="33"><div class="screenshot" style="background-image:none;" data-reactid="34"></div></div><div class="title " data-reactid="35"><span dir="auto" data-reactid="36"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="37"><a data-reactid="38"><div class="tile" aria-hidden="true" data-reactid="39"><div class="screenshot" style="background-image:none;" data-reactid="40"></div></div><div class="title " data-reactid="41"><span dir="auto" data-reactid="42"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="43"><a data-reactid="44"><div class="tile" aria-hidden="true" data-reactid="45"><div class="screenshot" style="background-image:none;" data-reactid="46"></div></div><div class="title " data-reactid="47"><span dir="auto" data-reactid="48"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="49"><a data-reactid="50"><div class="tile" aria-hidden="true" data-reactid="51"><div class="screenshot" style="background-image:none;" data-reactid="52"></div></div><div class="title " data-reactid="53"><span dir="auto" data-reactid="54"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="55"><a data-reactid="56"><div class="tile" aria-hidden="true" data-reactid="57"><div class="screenshot" style="background-image:none;" data-reactid="58"></div></div><div class="title " data-reactid="59"><span dir="auto" data-reactid="60"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="61"><a data-reactid="62"><div class="tile" aria-hidden="true" data-reactid="63"><div class="screenshot" style="background-image:none;" data-reactid="64"></div></div><div class="title " data-reactid="65"><span dir="auto" data-reactid="66"></span></div></a></li></ul><div class="edit-topsites-wrapper" data-reactid="67"><div class="edit-topsites-button" data-reactid="68"><button class="edit" title="Прилагодите секцију омиљених сајтова" data-reactid="69"><span data-reactid="70">Уреди</span></button></div></div></div></section><div class="sections-list" data-reactid="71"><section class="collapsible-section section animation-enabled" data-reactid="72"><div class="section-top-bar" data-reactid="73"><h3 class="section-title" data-reactid="74"><span class="click-target" data-reactid="75"><span class="icon icon-small-spacer icon-pocket" data-reactid="76"></span><span data-reactid="77">Предложио Pocket</span><span class="icon icon-arrowhead-down" data-reactid="78"></span></span></h3></div><div class="section-body" data-reactid="79"><ul class="section-list" style="padding:0;" data-reactid="80"><li class="card-outer placeholder" data-reactid="81"><a data-reactid="82"><div class="card" data-reactid="83"><div class="card-details no-image" data-reactid="84"><div class="card-text no-context no-description no-host-name no-image" data-reactid="85"><h4 class="card-title" dir="auto" data-reactid="86"></h4><p class="card-description" dir="auto" data-reactid="87"></p></div><div class="card-context" data-reactid="88"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="89"><a data-reactid="90"><div class="card" data-reactid="91"><div class="card-details no-image" data-reactid="92"><div class="card-text no-context no-description no-host-name no-image" data-reactid="93"><h4 class="card-title" dir="auto" data-reactid="94"></h4><p class="card-description" dir="auto" data-reactid="95"></p></div><div class="card-context" data-reactid="96"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="97"><a data-reactid="98"><div class="card" data-reactid="99"><div class="card-details no-image" data-reactid="100"><div class="card-text no-context no-description no-host-name no-image" data-reactid="101"><h4 class="card-title" dir="auto" data-reactid="102"></h4><p class="card-description" dir="auto" data-reactid="103"></p></div><div class="card-context" data-reactid="104"></div></div></div></a></li></ul><div class="topic" data-reactid="105"><span data-reactid="106"><span data-reactid="107">Популарне теме:</span></span><ul data-reactid="108"></ul></div></div></section><section class="collapsible-section section animation-enabled" data-reactid="109"><div class="section-top-bar" data-reactid="110"><h3 class="section-title" data-reactid="111"><span class="click-target" data-reactid="112"><span class="icon icon-small-spacer icon-highlights" data-reactid="113"></span><span data-reactid="114">Истакнуто</span><span class="icon icon-arrowhead-down" data-reactid="115"></span></span></h3></div><div class="section-body" data-reactid="116"><ul class="section-list" style="padding:0;" data-reactid="117"><li class="card-outer placeholder" data-reactid="118"><a data-reactid="119"><div class="card" data-reactid="120"><div class="card-details no-image" data-reactid="121"><div class="card-text no-context no-description no-host-name no-image" data-reactid="122"><h4 class="card-title" dir="auto" data-reactid="123"></h4><p class="card-description" dir="auto" data-reactid="124"></p></div><div class="card-context" data-reactid="125"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="126"><a data-reactid="127"><div class="card" data-reactid="128"><div class="card-details no-image" data-reactid="129"><div class="card-text no-context no-description no-host-name no-image" data-reactid="130"><h4 class="card-title" dir="auto" data-reactid="131"></h4><p class="card-description" dir="auto" data-reactid="132"></p></div><div class="card-context" data-reactid="133"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="134"><a data-reactid="135"><div class="card" data-reactid="136"><div class="card-details no-image" data-reactid="137"><div class="card-text no-context no-description no-host-name no-image" data-reactid="138"><h4 class="card-title" dir="auto" data-reactid="139"></h4><p class="card-description" dir="auto" data-reactid="140"></p></div><div class="card-context" data-reactid="141"></div></div></div></a></li></ul></div></section></div></div><!-- react-empty: 142 --></main></div></div>
     <div id="snippets-container">
       <div id="snippets"></div>
     </div>
     <script>
 // Don't directly load the following scripts as part of html to let the page
 // finish loading to render the content sooner.
 for (const src of [
   "resource://activity-stream/prerendered/static/activity-stream-initial-state.js",
--- a/browser/extensions/activity-stream/prerendered/locales/sr/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/sr/activity-stream-strings.js
@@ -28,17 +28,17 @@ window.gActivityStreamStrings = {
   "menu_action_pin": "Закачи",
   "menu_action_unpin": "Откачи",
   "confirm_history_delete_p1": "Да ли сте сигурни да желите да обришете све посете ове странице из ваше историје?",
   "confirm_history_delete_notice_p2": "Ова радња се не може опозвати.",
   "menu_action_save_to_pocket": "Сачувај на Pocket",
   "search_for_something_with": "Претражите {search_term} са:",
   "search_button": "Претражи",
   "search_header": "{search_engine_name} претрага",
-  "search_web_placeholder": "Претражујте веб",
+  "search_web_placeholder": "Претражи веб",
   "search_settings": "Измените подешавања претраге",
   "section_info_option": "Инфо",
   "section_info_send_feedback": "Пошаљите повратне податке",
   "section_info_privacy_notice": "Обавештење о приватности",
   "section_disclaimer_topstories": "Најинтересантније приче на вебу, изабране на основу онога што читате. Од Pocket-а, који је сада део Mozilla-е.",
   "section_disclaimer_topstories_linktext": "Сазнајте како ради.",
   "section_disclaimer_topstories_buttontext": "У реду",
   "welcome_title": "Добродошли на нови језичак",
@@ -60,17 +60,17 @@ window.gActivityStreamStrings = {
   "settings_pane_bookmarks_body": "Ваша нова забелешка на јединственом месту.",
   "settings_pane_visit_again_header": "Посетите поново",
   "settings_pane_visit_again_body": "Firefox ће вам приказивати делове ваше историје прегледања коју можда желите да запамтите или да им се вратите.",
   "settings_pane_highlights_header": "Истакнуто",
   "settings_pane_highlights_body2": "Поново погледајте занимљиве ствари које сте скоро посетили или забележили.",
   "settings_pane_highlights_options_bookmarks": "Забелешке",
   "settings_pane_highlights_options_visited": "Посећени сајтови",
   "settings_pane_snippets_header": "Исечци",
-  "settings_pane_snippets_body": "Читајте кратке и слатке новости од Mozilla-е о Firefox-у, интернет култури и погледајте понеки насумични мем.",
+  "settings_pane_snippets_body": "Читајте кратке новости од Mozilla-е о Firefox-у, интернет култури и погледајте понеки насумични мем.",
   "settings_pane_done_button": "Готово",
   "settings_pane_topstories_options_sponsored": "Прикажи промовисане приче",
   "edit_topsites_button_text": "Уреди",
   "edit_topsites_button_label": "Прилагодите секцију омиљених сајтова",
   "edit_topsites_showmore_button": "Прикажи више",
   "edit_topsites_showless_button": "Прикажи мање",
   "edit_topsites_done_button": "Готово",
   "edit_topsites_pin_button": "Закачи овај сајт",
--- a/browser/extensions/activity-stream/prerendered/locales/ta/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/ta/activity-stream-strings.js
@@ -34,45 +34,45 @@ window.gActivityStreamStrings = {
   "search_button": "தேடு",
   "search_header": "{search_engine_name} தேடுபொறியில் தேடு",
   "search_web_placeholder": "இணையத்தில் தேடு",
   "search_settings": "தேடல் அமைவுகளை மாற்று",
   "section_info_option": "தகவல்",
   "section_info_send_feedback": "பின்னூட்டம் அனுப்பு",
   "section_info_privacy_notice": "தனியுரிம கொள்கை",
   "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.",
-  "section_disclaimer_topstories_linktext": "Learn how it works.",
-  "section_disclaimer_topstories_buttontext": "Okay, got it",
+  "section_disclaimer_topstories_linktext": "இது எப்படி வேலை செய்கிறது என்று தெரிந்துகொள்ளவும்.",
+  "section_disclaimer_topstories_buttontext": "சரி, புரிந்தது",
   "welcome_title": "புதிய கீற்றுக்கு வருக",
   "welcome_body": "உங்களுக்கு மிகவும் பொருத்தமான புத்தகக்குறிகள், கட்டுரைகள், காணொளிகள் மற்றும் சமீபத்தில் பார்வையிட்ட பக்கங்களைக் காண்பிக்க பயர்பாக்ஸ் இந்த இடத்தைப் பயன்படுத்தும், எனவே நீங்கள் அவற்றை எளிதாகத் திரும்பப் பெறலாம்.",
   "welcome_label": "உங்களின் முக்கியம்சங்களை அடையாளம் காண்கிறோம்",
   "time_label_less_than_minute": "<1நி",
   "time_label_minute": "{number}நி",
   "time_label_hour": "{number}ம",
   "time_label_day": "{number}நா",
   "settings_pane_button_label": "உங்கள் புதிய கீற்றுப் பக்கத்தை விருப்பமை",
   "settings_pane_header": "புதிய கீற்றின் முன்னுரிமைகள்",
-  "settings_pane_body2": "Choose what you see on this page.",
+  "settings_pane_body2": "இந்த பக்கத்தில் நீங்கள் பார்ப்பதை தேர்வு செய்யவும்.",
   "settings_pane_search_header": "தேடல்",
   "settings_pane_search_body": "புதிய கீற்றிலீருந்து இணையத்தை தேடு.",
   "settings_pane_topsites_header": "சிறந்த தளங்கள்",
   "settings_pane_topsites_body": "நீங்கள் அடிக்கடி பார்க்கும் தளங்களை அணுகவும்.",
   "settings_pane_topsites_options_showmore": "இரு வரிசைகளைக் காண்பி",
   "settings_pane_bookmarks_header": "சமீபத்திய புத்தகக்குறிகள்",
   "settings_pane_bookmarks_body": "ஒரு வசதியான இடத்தில் உங்கள் புதிதாக உருவாக்கப்பட்ட புத்தகக்குறிகள்.",
   "settings_pane_visit_again_header": "மீண்டும் வருக",
   "settings_pane_visit_again_body": "பயர்பாக்ஸ் நீங்கள் நினைவுப்படுத்த (அ) திரும்பப் பெற விரும்பும் உங்கள் உலாவல் வரலாற்றின் சில பகுதிகளைக் காட்டும்.",
   "settings_pane_highlights_header": "மிளிர்ப்புகள்",
   "settings_pane_highlights_body2": "Find your way back to interesting things you’ve recently visited or bookmarked.",
   "settings_pane_highlights_options_bookmarks": "புத்தகக்குறிகள்",
   "settings_pane_highlights_options_visited": "பார்வையிடப்பட்ட தளம்",
-  "settings_pane_snippets_header": "Snippets",
+  "settings_pane_snippets_header": "துணுக்குகள்",
   "settings_pane_snippets_body": "Read short and sweet updates from Mozilla about Firefox, internet culture, and the occasional random meme.",
   "settings_pane_done_button": "முடிந்தது",
-  "settings_pane_topstories_options_sponsored": "Show Sponsored Stories",
+  "settings_pane_topstories_options_sponsored": "விளம்பரங்களைக் காட்டு",
   "edit_topsites_button_text": "தொகு",
   "edit_topsites_button_label": "உங்களின் சிறந்த தளங்களுக்கான தொகுதியை விருப்பமை",
   "edit_topsites_showmore_button": "கூடுதலாகக் காட்டுக",
   "edit_topsites_showless_button": "குறைவாகக் காண்பி",
   "edit_topsites_done_button": "முடிந்தது",
   "edit_topsites_pin_button": "இத்தளத்தை இடமுனையில் வை",
   "edit_topsites_unpin_button": "முனையிலிருந்து நீக்கு",
   "edit_topsites_edit_button": "இத்தளத்தை தொகு",
@@ -85,14 +85,14 @@ window.gActivityStreamStrings = {
   "topsites_form_add_button": "சேர்",
   "topsites_form_save_button": "சேமி",
   "topsites_form_cancel_button": "தவிர்",
   "topsites_form_url_validation": "சரியான URL தேவை",
   "pocket_read_more": "பிரபலமான தலைப்புகள்:",
   "pocket_read_even_more": "இன்னும் கதைகளைப் பார்க்கவும்",
   "pocket_feedback_header": "இணையத்தின் சிறந்த செயலி, 250 இலட்ச மக்களால் தேர்ந்தெடுக்கப்பட்டது.",
   "pocket_description": "Discover high-quality content you might otherwise miss, with help from Pocket, now part of Mozilla.",
-  "highlights_empty_state": "Start browsing, and we’ll show some of the great articles, videos, and other pages you’ve recently visited or bookmarked here.",
+  "highlights_empty_state": "உலாவலைத் தொடங்கவும், மேலும் நாங்கள் சில சிறந்த கட்டுரைகள், காணொளிகள், மற்றும் நீங்கள் சமீபத்தில் பார்த்த பிற பக்கங்கள் அல்லது இங்கே புத்தகக்குறியிட்டவற்றைக் காட்டுவோம்.",
   "topstories_empty_state": "You’ve caught up. Check back later for more top stories from {provider}. Can’t wait? Select a popular topic to find more great stories from around the web.",
   "manual_migration_explanation2": "Try Firefox with the bookmarks, history and passwords from another browser.",
   "manual_migration_cancel_button": "பரவாயில்லை",
   "manual_migration_import_button": "இப்போது இறக்கு"
 };
--- a/browser/extensions/activity-stream/prerendered/locales/th/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/th/activity-stream-strings.js
@@ -33,18 +33,18 @@ window.gActivityStreamStrings = {
   "search_for_something_with": "ค้นหา {search_term} ด้วย:",
   "search_button": "ค้นหา",
   "search_header": "ค้นหา {search_engine_name}",
   "search_web_placeholder": "ค้นหาเว็บ",
   "search_settings": "เปลี่ยนการตั้งค่าการค้นหา",
   "section_info_option": "ข้อมูล",
   "section_info_send_feedback": "ส่งข้อคิดเห็น",
   "section_info_privacy_notice": "ประกาศความเป็นส่วนตัว",
-  "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.",
-  "section_disclaimer_topstories_linktext": "Learn how it works.",
+  "section_disclaimer_topstories": "เรื่องราวที่น่าสนใจที่สุดบนเว็บ เลือกตามสิ่งที่คุณอ่าน จาก Pocket ซึ่งขณะนี้เป็นส่วนหนึ่งของ Mozilla",
+  "section_disclaimer_topstories_linktext": "เรียนรู้วิธีการทำงาน",
   "section_disclaimer_topstories_buttontext": "ตกลง เข้าใจแล้ว",
   "welcome_title": "ยินดีต้อนรับสู่แท็บใหม่",
   "welcome_body": "Firefox จะใช้พื้นที่นี้เพื่อแสดงที่คั่นหน้า, บทความ, วิดีโอ และหน้าที่คุณได้เยี่ยมชมล่าสุดที่เกี่ยวข้องกับคุณมากที่สุด เพื่อให้คุณสามารถกลับมาชมได้อย่างง่ายดาย",
   "welcome_label": "กำลังระบุรายการเด่นของคุณ",
   "time_label_less_than_minute": "<1 นาที",
   "time_label_minute": "{number} นาที",
   "time_label_hour": "{number} ชั่วโมง",
   "time_label_day": "{number} วัน",
--- a/browser/extensions/activity-stream/test/.eslintrc.js
+++ b/browser/extensions/activity-stream/test/.eslintrc.js
@@ -5,11 +5,12 @@ module.exports = {
     "mocha": true
   },
   "globals": {
     "assert": true,
     "sinon": true,
     "chai": true
   },
   "rules": {
+    "import/no-commonjs": 2,
     "react/jsx-no-bind": 0
   }
 };
--- a/browser/extensions/activity-stream/test/schemas/pings.js
+++ b/browser/extensions/activity-stream/test/schemas/pings.js
@@ -1,36 +1,36 @@
-const Joi = require("joi-browser");
-const {MAIN_MESSAGE_TYPE, CONTENT_MESSAGE_TYPE} = require("common/Actions.jsm");
+import {CONTENT_MESSAGE_TYPE, MAIN_MESSAGE_TYPE} from "common/Actions.jsm";
+import Joi from "joi-browser";
 
-const baseKeys = {
+export const baseKeys = {
   // client_id will be set by PingCentre if it doesn't exist.
   client_id: Joi.string().optional(),
   addon_version: Joi.string().required(),
   locale: Joi.string().required(),
   session_id: Joi.string(),
   page: Joi.valid(["about:home", "about:newtab", "unknown"]),
   user_prefs: Joi.number().integer().required()
 };
 
-const BasePing = Joi.object().keys(baseKeys).options({allowUnknown: true});
+export const BasePing = Joi.object().keys(baseKeys).options({allowUnknown: true});
 
-const UserEventPing = Joi.object().keys(Object.assign({}, baseKeys, {
+export const UserEventPing = Joi.object().keys(Object.assign({}, baseKeys, {
   session_id: baseKeys.session_id.required(),
   page: baseKeys.page.required(),
   source: Joi.string().required(),
   event: Joi.string().required(),
   action: Joi.valid("activity_stream_user_event").required(),
   metadata_source: Joi.string(),
   highlight_type: Joi.valid(["bookmarks", "recommendation", "history"]),
   recommender_type: Joi.string()
 }));
 
 // Use this to validate actions generated from Redux
-const UserEventAction = Joi.object().keys({
+export const UserEventAction = Joi.object().keys({
   type: Joi.string().required(),
   data: Joi.object().keys({
     event: Joi.valid([
       "CLICK",
       "SEARCH",
       "BLOCK",
       "DELETE",
       "DELETE_CONFIRM",
@@ -50,48 +50,48 @@ const UserEventAction = Joi.object().key
     action_position: Joi.number().integer()
   }).required(),
   meta: Joi.object().keys({
     to: Joi.valid(MAIN_MESSAGE_TYPE).required(),
     from: Joi.valid(CONTENT_MESSAGE_TYPE).required()
   }).required()
 });
 
-const UndesiredPing = Joi.object().keys(Object.assign({}, baseKeys, {
+export const UndesiredPing = Joi.object().keys(Object.assign({}, baseKeys, {
   source: Joi.string().required(),
   event: Joi.string().required(),
   action: Joi.valid("activity_stream_undesired_event").required(),
   value: Joi.number().required()
 }));
 
-const TileSchema = Joi.object().keys({
+export const TileSchema = Joi.object().keys({
   id: Joi.number().integer().required(),
   pos: Joi.number().integer()
 });
 
-const ImpressionStatsPing = Joi.object().keys(Object.assign({}, baseKeys, {
+export const ImpressionStatsPing = Joi.object().keys(Object.assign({}, baseKeys, {
   source: Joi.string().required(),
   impression_id: Joi.string().required(),
   client_id: Joi.valid("n/a").required(),
   session_id: Joi.valid("n/a").required(),
   action: Joi.valid("activity_stream_impression_stats").required(),
   tiles: Joi.array().items(TileSchema).required(),
   click: Joi.number().integer(),
   block: Joi.number().integer(),
   pocket: Joi.number().integer()
 }));
 
-const PerfPing = Joi.object().keys(Object.assign({}, baseKeys, {
+export const PerfPing = Joi.object().keys(Object.assign({}, baseKeys, {
   source: Joi.string(),
   event: Joi.string().required(),
   action: Joi.valid("activity_stream_performance_event").required(),
   value: Joi.number().required()
 }));
 
-const SessionPing = Joi.object().keys(Object.assign({}, baseKeys, {
+export const SessionPing = Joi.object().keys(Object.assign({}, baseKeys, {
   session_id: baseKeys.session_id.required(),
   page: baseKeys.page.required(),
   session_duration: Joi.number().integer(),
   action: Joi.valid("activity_stream_session").required(),
   perf: Joi.object().keys({
     // How long it took in ms for data to be ready for display.
     highlights_data_late_by_ms: Joi.number().positive(),
 
@@ -125,16 +125,19 @@ const SessionPing = Joi.object().keys(Ob
     topsites_icon_stats: Joi.object().keys({
       rich_icon: Joi.number(),
       screenshot: Joi.number(),
       screenshot_with_icon: Joi.number(),
       tippytop: Joi.number(),
       no_image: Joi.number()
     }),
 
+    // The count of pinned Top Sites.
+    topsites_pinned: Joi.number(),
+
     // When the page itself receives an event that document.visibilityState
     // == visible.
     //
     // Not required at least for the (error?) case where the
     // visibility_event doesn't fire.  (It's not clear whether this
     // can happen in practice, but if it does, we'd like to know about it).
     visibility_event_rcvd_ts: Joi.number().positive()
       .notes(["server counter", "server counter alert"]),
@@ -142,17 +145,17 @@ const SessionPing = Joi.object().keys(Ob
     // The boolean to signify whether the page is preloaded or not.
     is_preloaded: Joi.bool().required(),
 
     // The boolean to signify whether the page is prerendered or not.
     is_prerendered: Joi.bool().required()
   }).required()
 }));
 
-function chaiAssertions(_chai, utils) {
+export function chaiAssertions(_chai, utils) {
   const {Assertion} = _chai;
 
   Assertion.addMethod("validate", function(schema, schemaName) {
     const {error} = Joi.validate(this._obj, schema, {allowUnknown: false});
     this.assert(
       !error,
       `Expected to be ${schemaName ? `a valid ${schemaName}` : "valid"} but there were errors: ${error}`
     );
@@ -176,20 +179,8 @@ function chaiAssertions(_chai, utils) {
      */
     isUserEventAction(actual) {
       new Assertion(actual).validate(UserEventAction, "UserEventAction");
     }
   };
 
   Object.assign(_chai.assert, assertions);
 }
-
-module.exports = {
-  baseKeys,
-  BasePing,
-  UndesiredPing,
-  UserEventPing,
-  UserEventAction,
-  ImpressionStatsPing,
-  PerfPing,
-  SessionPing,
-  chaiAssertions
-};
--- a/browser/extensions/activity-stream/test/unit/activity-stream-prerender.test.jsx
+++ b/browser/extensions/activity-stream/test/unit/activity-stream-prerender.test.jsx
@@ -1,12 +1,12 @@
-const prerender = require("content-src/activity-stream-prerender");
-const {prerenderStore} = prerender;
-const {PrerenderData} = require("common/PrerenderData.jsm");
-const messages = require("data/locales.json")["en-US"];
+import {prerender, prerenderStore} from "content-src/activity-stream-prerender";
+import {PrerenderData} from "common/PrerenderData.jsm";
+
+const messages = require("data/locales.json")["en-US"]; // eslint-disable-line import/no-commonjs
 
 describe("prerenderStore", () => {
   it("should start uninitialized", () => {
     const store = prerenderStore();
 
     const state = store.getState();
     assert.equal(state.App.initialized, false);
   });
--- a/browser/extensions/activity-stream/test/unit/common/Actions.test.js
+++ b/browser/extensions/activity-stream/test/unit/common/Actions.test.js
@@ -1,18 +1,19 @@
-const {
-  actionTypes: at,
-  actionCreators: ac,
-  actionUtils: au,
+import {
+
+  actionCreators as ac,
+  actionTypes as at,
+  actionUtils as au,
+  BACKGROUND_PROCESS,
+  CONTENT_MESSAGE_TYPE,
+  globalImportContext,
   MAIN_MESSAGE_TYPE,
-  CONTENT_MESSAGE_TYPE,
-  UI_CODE,
-  BACKGROUND_PROCESS,
-  globalImportContext
-} = require("common/Actions.jsm");
+  UI_CODE
+} from "common/Actions.jsm";
 
 describe("Actions", () => {
   it("should set globalImportContext to UI_CODE", () => {
     assert.equal(globalImportContext, UI_CODE);
   });
 });
 
 describe("ActionTypes", () => {
--- a/browser/extensions/activity-stream/test/unit/common/Dedupe.test.js
+++ b/browser/extensions/activity-stream/test/unit/common/Dedupe.test.js
@@ -1,9 +1,9 @@
-const {Dedupe} = require("common/Dedupe.jsm");
+import {Dedupe} from "common/Dedupe.jsm";
 
 describe("Dedupe", () => {
   let instance;
   beforeEach(() => {
     instance = new Dedupe();
   });
   describe("group", () => {
     it("should remove duplicates inside the groups", () => {
--- a/browser/extensions/activity-stream/test/unit/common/PerfService.test.js
+++ b/browser/extensions/activity-stream/test/unit/common/PerfService.test.js
@@ -1,11 +1,11 @@
 /* globals assert, beforeEach, describe, it */
-const {_PerfService} = require("common/PerfService.jsm");
-const {FakePerformance} = require("test/unit/utils.js");
+import {_PerfService} from "common/PerfService.jsm";
+import {FakePerformance} from "test/unit/utils.js";
 
 let perfService;
 
 describe("_PerfService", () => {
   let sandbox;
   let fakePerfObj;
 
   beforeEach(() => {
--- a/browser/extensions/activity-stream/test/unit/common/PrerenderData.test.js
+++ b/browser/extensions/activity-stream/test/unit/common/PrerenderData.test.js
@@ -1,9 +1,9 @@
-const {PrerenderData, _PrerenderData} = require("common/PrerenderData.jsm");
+import {_PrerenderData, PrerenderData} from "common/PrerenderData.jsm";
 
 describe("_PrerenderData", () => {
   describe("properties", () => {
     it("should set .initialPrefs", () => {
       const initialPrefs = {foo: true};
       const instance = new _PrerenderData({initialPrefs});
 
       assert.equal(instance.initialPrefs, initialPrefs);
--- a/browser/extensions/activity-stream/test/unit/common/Reducers.test.js
+++ b/browser/extensions/activity-stream/test/unit/common/Reducers.test.js
@@ -1,12 +1,11 @@
-const {reducers, INITIAL_STATE, insertPinned} = require("common/Reducers.jsm");
+import {INITIAL_STATE, insertPinned, reducers} from "common/Reducers.jsm";
 const {TopSites, App, Snippets, Prefs, Dialog, Sections, PreferencesPane} = reducers;
-
-const {actionTypes: at} = require("common/Actions.jsm");
+import {actionTypes as at} from "common/Actions.jsm";
 
 describe("Reducers", () => {
   describe("App", () => {
     it("should return the initial state", () => {
       const nextState = App(undefined, {type: "FOO"});
       assert.equal(nextState, INITIAL_STATE.App);
     });
     it("should set initialized to true on INIT", () => {
--- a/browser/extensions/activity-stream/test/unit/lib/ActivityStream.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/ActivityStream.test.js
@@ -1,10 +1,10 @@
-const injector = require("inject!lib/ActivityStream.jsm");
-const {CONTENT_MESSAGE_TYPE} = require("common/Actions.jsm");
+import {CONTENT_MESSAGE_TYPE} from "common/Actions.jsm";
+import injector from "inject!lib/ActivityStream.jsm";
 
 const REASON_ADDON_UNINSTALL = 6;
 
 describe("ActivityStream", () => {
   let sandbox;
   let as;
   let ActivityStream;
   let PREFS_CONFIG;
--- a/browser/extensions/activity-stream/test/unit/lib/ActivityStreamMessageChannel.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/ActivityStreamMessageChannel.test.js
@@ -1,12 +1,12 @@
-const {ActivityStreamMessageChannel, DEFAULT_OPTIONS} = require("lib/ActivityStreamMessageChannel.jsm");
-const {addNumberReducer, GlobalOverrider} = require("test/unit/utils");
-const {createStore, applyMiddleware} = require("redux");
-const {actionTypes: at, actionCreators: ac} = require("common/Actions.jsm");
+import {actionCreators as ac, actionTypes as at} from "common/Actions.jsm";
+import {ActivityStreamMessageChannel, DEFAULT_OPTIONS} from "lib/ActivityStreamMessageChannel.jsm";
+import {addNumberReducer, GlobalOverrider} from "test/unit/utils";
+import {applyMiddleware, createStore} from "redux";
 
 const OPTIONS = ["pageURL, outgoingMessageName", "incomingMessageName", "dispatch"];
 
 describe("ActivityStreamMessageChannel", () => {
   let globals;
   let dispatch;
   let mm;
   let RPmessagePorts;
--- a/browser/extensions/activity-stream/test/unit/lib/ActivityStreamPrefs.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/ActivityStreamPrefs.test.js
@@ -1,10 +1,10 @@
 const ACTIVITY_STREAM_PREF_BRANCH = "browser.newtabpage.activity-stream.";
-const {Prefs, DefaultPrefs} = require("lib/ActivityStreamPrefs.jsm");
+import {DefaultPrefs, Prefs} from "lib/ActivityStreamPrefs.jsm";
 
 const TEST_PREF_CONFIG = new Map([
   ["foo", {value: true}],
   ["bar", {value: "BAR"}],
   ["baz", {value: 1}],
   ["qux", {value: "foo", value_local_dev: "foofoo"}]
 ]);
 
--- a/browser/extensions/activity-stream/test/unit/lib/FaviconFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/FaviconFeed.test.js
@@ -1,12 +1,12 @@
 "use strict";
-const {FaviconFeed} = require("lib/FaviconFeed.jsm");
-const {GlobalOverrider} = require("test/unit/utils");
-const {actionTypes: at} = require("common/Actions.jsm");
+import {actionTypes as at} from "common/Actions.jsm";
+import {FaviconFeed} from "lib/FaviconFeed.jsm";
+import {GlobalOverrider} from "test/unit/utils";
 
 const FAKE_ENDPOINT = "https://foo.com/";
 
 describe("FaviconFeed", () => {
   let feed;
   let globals;
   let sandbox;
   let clock;
--- a/browser/extensions/activity-stream/test/unit/lib/FilterAdult.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/FilterAdult.test.js
@@ -1,25 +1,25 @@
+import {filterAdult} from "lib/FilterAdult.jsm";
+
 describe("filterAdult", () => {
-  let filterAdult;
   let hashStub;
   let hashValue;
 
   beforeEach(() => {
     hashStub = {
       finish: sinon.stub().callsFake(() => hashValue),
       init: sinon.stub(),
       update: sinon.stub()
     };
     global.Components.classes["@mozilla.org/security/hash;1"] = {
       createInstance() {
         return hashStub;
       }
     };
-    filterAdult = require("lib/FilterAdult.jsm").filterAdult;
   });
 
   it("should default to include on unexpected urls", () => {
     const empty = {};
 
     const result = filterAdult([empty]);
 
     assert.equal(result.length, 1);
--- a/browser/extensions/activity-stream/test/unit/lib/HighlightsFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/HighlightsFeed.test.js
@@ -1,14 +1,15 @@
 "use strict";
-const injector = require("inject!lib/HighlightsFeed.jsm");
-const {Screenshots} = require("lib/Screenshots.jsm");
-const {GlobalOverrider} = require("test/unit/utils");
-const {actionTypes: at} = require("common/Actions.jsm");
-const {Dedupe} = require("common/Dedupe.jsm");
+
+import {actionTypes as at} from "common/Actions.jsm";
+import {Dedupe} from "common/Dedupe.jsm";
+import {GlobalOverrider} from "test/unit/utils";
+import injector from "inject!lib/HighlightsFeed.jsm";
+import {Screenshots} from "lib/Screenshots.jsm";
 
 const FAKE_LINKS = new Array(9).fill(null).map((v, i) => ({url: `http://www.site${i}.com`}));
 const FAKE_IMAGE = "data123";
 
 describe("Highlights Feed", () => {
   let HighlightsFeed;
   let SECTION_ID;
   let feed;
--- a/browser/extensions/activity-stream/test/unit/lib/ManualMigration.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/ManualMigration.test.js
@@ -1,11 +1,11 @@
-const injector = require("inject!lib/ManualMigration.jsm");
-const {actionCreators: ac, actionTypes: at} = require("common/Actions.jsm");
-const {GlobalOverrider} = require("test/unit/utils");
+import {actionCreators as ac, actionTypes as at} from "common/Actions.jsm";
+import {GlobalOverrider} from "test/unit/utils";
+import injector from "inject!lib/ManualMigration.jsm";
 
 describe("ManualMigration", () => {
   let dispatch;
   let store;
   let instance;
   let globals;
 
   let migrationWizardStub;
--- a/browser/extensions/activity-stream/test/unit/lib/NewTabInit.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/NewTabInit.test.js
@@ -1,10 +1,10 @@
-const {NewTabInit} = require("lib/NewTabInit.jsm");
-const {actionTypes: at, actionCreators: ac} = require("common/Actions.jsm");
+import {actionCreators as ac, actionTypes as at} from "common/Actions.jsm";
+import {NewTabInit} from "lib/NewTabInit.jsm";
 
 describe("NewTabInit", () => {
   let instance;
   let store;
   let STATE;
   const requestFromTab = portID => instance.onAction(ac.SendToMain(
     {type: at.NEW_TAB_STATE_REQUEST}, portID));
   beforeEach(() => {
--- a/browser/extensions/activity-stream/test/unit/lib/PersistentCache.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/PersistentCache.test.js
@@ -1,12 +1,10 @@
-"use strict";
-
-const {PersistentCache} = require("lib/PersistentCache.jsm");
-const {GlobalOverrider} = require("test/unit/utils");
+import {GlobalOverrider} from "test/unit/utils";
+import {PersistentCache} from "lib/PersistentCache.jsm";
 
 describe("PersistentCache", () => {
   let fakeOS;
   let fakeTextDecoder;
   let cache;
   let filename = "cache.json";
   let globals;
 
--- a/browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js
@@ -1,12 +1,12 @@
-const {PlacesFeed} = require("lib/PlacesFeed.jsm");
+import {actionTypes as at} from "common/Actions.jsm";
+import {GlobalOverrider} from "test/unit/utils";
+import {PlacesFeed} from "lib/PlacesFeed.jsm";
 const {HistoryObserver, BookmarksObserver} = PlacesFeed;
-const {GlobalOverrider} = require("test/unit/utils");
-const {actionTypes: at} = require("common/Actions.jsm");
 
 const FAKE_BOOKMARK = {bookmarkGuid: "xi31", bookmarkTitle: "Foo", dateAdded: 123214232, url: "foo.com"};
 const TYPE_BOOKMARK = 0; // This is fake, for testing
 const SOURCES = {
   DEFAULT: 0,
   IMPORT_REPLACE: 3
 };
 
--- a/browser/extensions/activity-stream/test/unit/lib/PrefsFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/PrefsFeed.test.js
@@ -1,11 +1,11 @@
-const {PrefsFeed} = require("lib/PrefsFeed.jsm");
-const {actionTypes: at, actionCreators: ac} = require("common/Actions.jsm");
-const {PrerenderData} = require("common/PrerenderData.jsm");
+import {actionCreators as ac, actionTypes as at} from "common/Actions.jsm";
+import {PrefsFeed} from "lib/PrefsFeed.jsm";
+import {PrerenderData} from "common/PrerenderData.jsm";
 const {initialPrefs} = PrerenderData;
 
 const PRERENDER_PREF_NAME = "prerender";
 const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
 
 describe("PrefsFeed", () => {
   let feed;
   let FAKE_PREFS;
--- a/browser/extensions/activity-stream/test/unit/lib/Screenshots.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/Screenshots.test.js
@@ -1,11 +1,12 @@
 "use strict";
-const {Screenshots} = require("lib/Screenshots.jsm");
-const {GlobalOverrider} = require("test/unit/utils");
+import {GlobalOverrider} from "test/unit/utils";
+import {Screenshots} from "lib/Screenshots.jsm";
+
 const URL = "foo.com";
 const FAKE_THUMBNAIL_PATH = "fake/path/thumb.jpg";
 
 describe("Screenshots", () => {
   let globals;
   let sandbox;
 
   beforeEach(() => {
--- a/browser/extensions/activity-stream/test/unit/lib/SectionsManager.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/SectionsManager.test.js
@@ -1,12 +1,12 @@
 "use strict";
-const {SectionsFeed, SectionsManager} = require("lib/SectionsManager.jsm");
-const {EventEmitter, GlobalOverrider} = require("test/unit/utils");
-const {MAIN_MESSAGE_TYPE, CONTENT_MESSAGE_TYPE} = require("common/Actions.jsm");
+import {CONTENT_MESSAGE_TYPE, MAIN_MESSAGE_TYPE} from "common/Actions.jsm";
+import {EventEmitter, GlobalOverrider} from "test/unit/utils";
+import {SectionsFeed, SectionsManager} from "lib/SectionsManager.jsm";
 
 const FAKE_ID = "FAKE_ID";
 const FAKE_OPTIONS = {icon: "FAKE_ICON", title: "FAKE_TITLE"};
 const FAKE_ROWS = [{url: "1.example.com"}, {url: "2.example.com"}, {"url": "3.example.com"}];
 const FAKE_URL = "2.example.com";
 const FAKE_CARD_OPTIONS = {title: "Some fake title"};
 
 describe("SectionsManager", () => {
--- a/browser/extensions/activity-stream/test/unit/lib/ShortUrl.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/ShortUrl.test.js
@@ -1,10 +1,10 @@
-const {shortURL} = require("lib/ShortURL.jsm");
-const {GlobalOverrider} = require("test/unit/utils");
+import {GlobalOverrider} from "test/unit/utils";
+import {shortURL} from "lib/ShortURL.jsm";
 
 const puny = "xn--kpry57d";
 const idn = "台灣";
 
 describe("shortURL", () => {
   let globals;
   let IDNStub;
   let getPublicSuffixFromHostStub;
--- a/browser/extensions/activity-stream/test/unit/lib/SnippetsFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/SnippetsFeed.test.js
@@ -1,11 +1,11 @@
-const {SnippetsFeed} = require("lib/SnippetsFeed.jsm");
-const {actionCreators: ac, actionTypes: at} = require("common/Actions.jsm");
-const {GlobalOverrider} = require("test/unit/utils");
+import {actionCreators as ac, actionTypes as at} from "common/Actions.jsm";
+import {GlobalOverrider} from "test/unit/utils";
+import {SnippetsFeed} from "lib/SnippetsFeed.jsm";
 
 const WEEK_IN_MS = 7 * 24 * 60 * 60 * 1000;
 const searchData = {searchEngineIdentifier: "google", engines: ["searchEngine-google", "searchEngine-bing"]};
 const signUpUrl = "https://accounts.firefox.com/signup?service=sync&context=fx_desktop_v3&entrypoint=snippets";
 
 let overrider = new GlobalOverrider();
 
 describe("SnippetsFeed", () => {
--- a/browser/extensions/activity-stream/test/unit/lib/Store.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/Store.test.js
@@ -1,12 +1,12 @@
-const injector = require("inject!lib/Store.jsm");
-const {createStore} = require("redux");
-const {addNumberReducer} = require("test/unit/utils");
-const {FakePrefs} = require("test/unit/utils");
+import {addNumberReducer, FakePrefs} from "test/unit/utils";
+import {createStore} from "redux";
+import injector from "inject!lib/Store.jsm";
+
 describe("Store", () => {
   let Store;
   let sandbox;
   let store;
   beforeEach(() => {
     sandbox = sinon.sandbox.create();
     function ActivityStreamMessageChannel(options) {
       this.dispatch = options.dispatch;
--- a/browser/extensions/activity-stream/test/unit/lib/SystemTickFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/SystemTickFeed.test.js
@@ -1,11 +1,10 @@
-"use strict";
-const injector = require("inject!lib/SystemTickFeed.jsm");
-const {actionTypes: at} = require("common/Actions.jsm");
+import {actionTypes as at} from "common/Actions.jsm";
+import injector from "inject!lib/SystemTickFeed.jsm";
 
 describe("System Tick Feed", () => {
   let SystemTickFeed;
   let SYSTEM_TICK_INTERVAL;
   let instance;
   let clock;
 
   beforeEach(() => {
--- a/browser/extensions/activity-stream/test/unit/lib/TelemetryFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/TelemetryFeed.test.js
@@ -1,21 +1,20 @@
 /* global Services */
-
-const injector = require("inject!lib/TelemetryFeed.jsm");
-const {GlobalOverrider, FakePrefs} = require("test/unit/utils");
-const {actionCreators: ac, actionTypes: at} = require("common/Actions.jsm");
-const {
+import {actionCreators as ac, actionTypes as at} from "common/Actions.jsm";
+import {
   BasePing,
-  UndesiredPing,
-  UserEventPing,
   ImpressionStatsPing,
   PerfPing,
-  SessionPing
-} = require("test/schemas/pings");
+  SessionPing,
+  UndesiredPing,
+  UserEventPing
+} from "test/schemas/pings";
+import {FakePrefs, GlobalOverrider} from "test/unit/utils";
+import injector from "inject!lib/TelemetryFeed.jsm";
 
 const FAKE_UUID = "{foo-123-foo}";
 
 describe("TelemetryFeed", () => {
   let globals;
   let sandbox;
   let expectedUserPrefs;
   let browser = {getAttribute() { return "true"; }};
@@ -160,35 +159,37 @@ describe("TelemetryFeed", () => {
       // Create a ping referencing the session
       const ping = instance.createSessionEndEvent(session);
       assert.validate(ping, SessionPing);
       assert.propertyVal(instance.sessions.get("foo").perf,
                          "highlights_data_late_by_ms", 20);
       assert.propertyVal(instance.sessions.get("foo").perf,
                          "topsites_data_late_by_ms", 10);
     });
-    it("should be a valid ping with the topsites_icon_stats perf", () => {
+    it("should be a valid ping with the topsites stats perf", () => {
       // Add a session
       const portID = "foo";
       const session = instance.addSession(portID, "about:home");
       instance.saveSessionPerfData("foo", {
         topsites_icon_stats: {
           "screenshot_with_icon": 2,
           "screenshot": 1,
           "tippytop": 2,
           "rich_icon": 1,
           "no_image": 0
-        }
+        },
+        topsites_pinned: 3
       });
 
       // Create a ping referencing the session
       const ping = instance.createSessionEndEvent(session);
       assert.validate(ping, SessionPing);
       assert.propertyVal(instance.sessions.get("foo").perf.topsites_icon_stats,
         "screenshot_with_icon", 2);
+      assert.equal(instance.sessions.get("foo").perf.topsites_pinned, 3);
     });
   });
 
   describe("#browserOpenNewtabStart", () => {
     it("should call perfService.mark with browser-open-newtab-start", () => {
       sandbox.stub(perfService, "mark");
 
       instance.browserOpenNewtabStart();
--- a/browser/extensions/activity-stream/test/unit/lib/TippyTopProvider.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/TippyTopProvider.test.js
@@ -1,11 +1,10 @@
-"use strict";
-const {TippyTopProvider} = require("lib/TippyTopProvider.jsm");
-const {GlobalOverrider} = require("test/unit/utils");
+import {GlobalOverrider} from "test/unit/utils";
+import {TippyTopProvider} from "lib/TippyTopProvider.jsm";
 
 describe("TippyTopProvider", () => {
   let instance;
   let globals;
   beforeEach(async () => {
     globals = new GlobalOverrider();
     let fetchStub = globals.sandbox.stub();
     globals.set("fetch", fetchStub);
--- a/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
@@ -1,14 +1,15 @@
 "use strict";
-const injector = require("inject!lib/TopSitesFeed.jsm");
-const {Screenshots} = require("lib/Screenshots.jsm");
-const {FakePrefs, GlobalOverrider} = require("test/unit/utils");
-const {actionTypes: at, actionCreators: ac} = require("common/Actions.jsm");
-const {insertPinned, TOP_SITES_SHOWMORE_LENGTH} = require("common/Reducers.jsm");
+
+import {actionCreators as ac, actionTypes as at} from "common/Actions.jsm";
+import {FakePrefs, GlobalOverrider} from "test/unit/utils";
+import {insertPinned, TOP_SITES_SHOWMORE_LENGTH} from "common/Reducers.jsm";
+import injector from "inject!lib/TopSitesFeed.jsm";
+import {Screenshots} from "lib/Screenshots.jsm";
 
 const FAKE_FAVICON = "data987";
 const FAKE_FAVICON_SIZE = 128;
 const FAKE_FRECENCY = 200;
 const FAKE_LINKS = new Array(TOP_SITES_SHOWMORE_LENGTH).fill(null).map((v, i) => ({
   frecency: FAKE_FRECENCY,
   url: `http://www.site${i}.com`
 }));
--- a/browser/extensions/activity-stream/test/unit/lib/TopStoriesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/TopStoriesFeed.test.js
@@ -1,13 +1,11 @@
-"use strict";
-const injector = require("inject!lib/TopStoriesFeed.jsm");
-const {FakePrefs} = require("test/unit/utils");
-const {actionTypes: at} = require("common/Actions.jsm");
-const {GlobalOverrider} = require("test/unit/utils");
+import {FakePrefs, GlobalOverrider} from "test/unit/utils";
+import {actionTypes as at} from "common/Actions.jsm";
+import injector from "inject!lib/TopStoriesFeed.jsm";
 
 describe("Top Stories Feed", () => {
   let TopStoriesFeed;
   let STORIES_UPDATE_TIME;
   let TOPICS_UPDATE_TIME;
   let SECTION_ID;
   let SPOC_IMPRESSION_TRACKING_PREF;
   let REC_IMPRESSION_TRACKING_PREF;
--- a/browser/extensions/activity-stream/test/unit/lib/UserDomainAffinityProvider.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/UserDomainAffinityProvider.test.js
@@ -1,11 +1,10 @@
-"use strict";
-const injector = require("inject!lib/UserDomainAffinityProvider.jsm");
-const {GlobalOverrider} = require("test/unit/utils");
+import {GlobalOverrider} from "test/unit/utils";
+import injector from "inject!lib/UserDomainAffinityProvider.jsm";
 
 const TIME_SEGMENTS = [
   {"id": "hour", "startTime": 3600, "endTime": 0, "weightPosition": 1},
   {"id": "day", "startTime": 86400, "endTime": 3600, "weightPosition": 0.75},
   {"id": "week", "startTime": 604800, "endTime": 86400, "weightPosition": 0.5},
   {"id": "weekPlus", "startTime": null, "endTime": 604800, "weightPosition": 0.25}
 ];
 
--- a/browser/extensions/activity-stream/test/unit/lib/init-store.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/init-store.test.js
@@ -1,32 +1,39 @@
-const initStore = require("content-src/lib/init-store");
-const {MERGE_STORE_ACTION, EARLY_QUEUED_ACTIONS, rehydrationMiddleware, queueEarlyMessageMiddleware} = initStore;
-const {GlobalOverrider, addNumberReducer} = require("test/unit/utils");
-const {actionCreators: ac, actionTypes: at} = require("common/Actions.jsm");
+import {actionCreators as ac, actionTypes as at} from "common/Actions.jsm";
+import {addNumberReducer, GlobalOverrider} from "test/unit/utils";
+import {
+  EARLY_QUEUED_ACTIONS,
+  INCOMING_MESSAGE_NAME,
+  initStore,
+  MERGE_STORE_ACTION,
+  OUTGOING_MESSAGE_NAME,
+  queueEarlyMessageMiddleware,
+  rehydrationMiddleware
+} from "content-src/lib/init-store";
 
 describe("initStore", () => {
   let globals;
   let store;
   beforeEach(() => {
     globals = new GlobalOverrider();
     globals.set("sendAsyncMessage", globals.sandbox.spy());
     globals.set("addMessageListener", globals.sandbox.spy());
     store = initStore({number: addNumberReducer});
   });
   afterEach(() => globals.restore());
   it("should create a store with the provided reducers", () => {
     assert.ok(store);
     assert.property(store.getState(), "number");
   });
   it("should add a listener that dispatches actions", () => {
-    assert.calledWith(global.addMessageListener, initStore.INCOMING_MESSAGE_NAME);
+    assert.calledWith(global.addMessageListener, INCOMING_MESSAGE_NAME);
     const listener = global.addMessageListener.firstCall.args[1];
     globals.sandbox.spy(store, "dispatch");
-    const message = {name: initStore.INCOMING_MESSAGE_NAME, data: {type: "FOO"}};
+    const message = {name: INCOMING_MESSAGE_NAME, data: {type: "FOO"}};
 
     listener(message);
 
     assert.calledWith(store.dispatch, message.data);
   });
   it("should not throw if addMessageListener is not defined", () => {
     // Note: this is being set/restored by GlobalOverrider
     delete global.addMessageListener;
@@ -38,29 +45,29 @@ describe("initStore", () => {
 
     assert.equal(store.getState().number, 42);
   });
   it("should log errors from failed messages", () => {
     const callback = global.addMessageListener.firstCall.args[1];
     globals.sandbox.stub(global.console, "error");
     globals.sandbox.stub(store, "dispatch").throws(Error("failed"));
 
-    const message = {name: initStore.INCOMING_MESSAGE_NAME, data: {type: MERGE_STORE_ACTION}};
+    const message = {name: INCOMING_MESSAGE_NAME, data: {type: MERGE_STORE_ACTION}};
     callback(message);
 
     assert.calledOnce(global.console.error);
   });
   it("should replace the state if a MERGE_STORE_ACTION is dispatched", () => {
-    store.dispatch({type: initStore.MERGE_STORE_ACTION, data: {number: 42}});
+    store.dispatch({type: MERGE_STORE_ACTION, data: {number: 42}});
     assert.deepEqual(store.getState(), {number: 42});
   });
   it("should send out SendToMain actions", () => {
     const action = ac.SendToMain({type: "FOO"});
     store.dispatch(action);
-    assert.calledWith(global.sendAsyncMessage, initStore.OUTGOING_MESSAGE_NAME, action);
+    assert.calledWith(global.sendAsyncMessage, OUTGOING_MESSAGE_NAME, action);
   });
   it("should not send out other types of actions", () => {
     store.dispatch({type: "FOO"});
     assert.notCalled(global.sendAsyncMessage);
   });
   describe("rehydrationMiddleware", () => {
     it("should allow NEW_TAB_STATE_REQUEST to go through", () => {
       const action = ac.SendToMain({type: at.NEW_TAB_STATE_REQUEST});
--- a/browser/extensions/activity-stream/test/unit/unit-entry.js
+++ b/browser/extensions/activity-stream/test/unit/unit-entry.js
@@ -1,13 +1,13 @@
-const {GlobalOverrider, FakePrefs, FakePerformance, EventEmitter} = require("test/unit/utils");
-const {chaiAssertions} = require("test/schemas/pings");
+import {EventEmitter, FakePerformance, FakePrefs, GlobalOverrider} from "test/unit/utils";
+import Adapter from "enzyme-adapter-react-15";
+import {chaiAssertions} from "test/schemas/pings";
+import enzyme from "enzyme";
 
-const Adapter = require("enzyme-adapter-react-15");
-const enzyme = require("enzyme");
 enzyme.configure({adapter: new Adapter()});
 
 // Cause React warnings to make tests that trigger them fail
 const origConsoleError = console.error; // eslint-disable-line no-console
 console.error = function(msg, ...args) { // eslint-disable-line no-console
   // eslint-disable-next-line no-console
   origConsoleError.apply(console, [msg, ...args]);
 
--- a/browser/extensions/activity-stream/test/unit/utils.js
+++ b/browser/extensions/activity-stream/test/unit/utils.js
@@ -1,20 +1,23 @@
-const React = require("react");
-const {mount, shallow} = require("enzyme");
-const {IntlProvider, intlShape} = require("react-intl");
-const messages = require("data/locales.json")["en-US"];
+import {IntlProvider, intlShape} from "react-intl";
+import {mount, shallow} from "enzyme";
+import React from "react";
+
+const messages = require("data/locales.json")["en-US"]; // eslint-disable-line import/no-commonjs
+
 const intlProvider = new IntlProvider({locale: "en-US", messages});
+
 const {intl} = intlProvider.getChildContext();
 
 /**
  * GlobalOverrider - Utility that allows you to override properties on the global object.
  *                   See unit-entry.js for example usage.
  */
-class GlobalOverrider {
+export class GlobalOverrider {
   constructor() {
     this.originalGlobals = new Map();
     this.sandbox = sinon.sandbox.create();
   }
 
   /**
    * _override - Internal method to override properties on the global object.
    *             The first time a given key is overridden, we cache the original
@@ -82,17 +85,17 @@ class GlobalOverrider {
  * things aren't yet supported.  Feel free to add them in.
  *
  * @param {Object} args - optional arguments
  * @param {Function} args.initHook - if present, will be called back
  *                   inside the constructor. Typically used from tests
  *                   to save off a pointer to the created instance so that
  *                   stubs and spies can be inspected by the test code.
  */
-class FakensIPrefBranch {
+export class FakensIPrefBranch {
   constructor(args) {
     if (args) {
       if ("initHook" in args) {
         args.initHook.call(this);
       }
     }
     this._prefBranch = {};
     this.observers = {};
@@ -121,17 +124,17 @@ class FakensIPrefBranch {
   }
 }
 FakensIPrefBranch.prototype.prefs = {};
 
 /**
  * Very simple fake for the most basic semantics of Preferences.jsm.
  * Extends FakensIPrefBranch.
  */
-class FakePrefs extends FakensIPrefBranch {
+export class FakePrefs extends FakensIPrefBranch {
   observe(prefName, callback) {
     super.addObserver(prefName, callback);
   }
   ignore(prefName, callback) {
     super.removeObserver(prefName, callback);
   }
   set(prefName, value) {
     this.prefs[prefName] = value;
@@ -140,17 +143,17 @@ class FakePrefs extends FakensIPrefBranc
       this.observers[prefName](value);
     }
   }
 }
 
 /**
  * Slimmed down version of toolkit/modules/EventEmitter.jsm
  */
-function EventEmitter() {}
+export function EventEmitter() {}
 EventEmitter.decorate = function(objectToDecorate) {
   let emitter = new EventEmitter();
   objectToDecorate.on = emitter.on.bind(emitter);
   objectToDecorate.off = emitter.off.bind(emitter);
   objectToDecorate.once = emitter.once.bind(emitter);
   objectToDecorate.emit = emitter.emit.bind(emitter);
 };
 EventEmitter.prototype = {
@@ -209,17 +212,17 @@ EventEmitter.prototype = {
         } catch (ex) {
           // error with a listener
         }
       }
     }
   }
 };
 
-function FakePerformance() {}
+export function FakePerformance() {}
 FakePerformance.prototype = {
   marks: new Map(),
   now() {
     return window.performance.now();
   },
   timing: {navigationStart: 222222.123},
   get timeOrigin() {
     return 10000.234;
@@ -252,39 +255,29 @@ FakePerformance.prototype = {
 
     this.marks.set(name, [markObj]);
   }
 };
 
 /**
  * addNumberReducer - a simple dummy reducer for testing that adds a number
  */
-function addNumberReducer(prevState = 0, action) {
+export function addNumberReducer(prevState = 0, action) {
   return action.type === "ADD" ? prevState + action.data : prevState;
 }
 
 /**
  * Helper functions to test components that need IntlProvider as an ancestor
  */
 function nodeWithIntlProp(node) {
   return React.cloneElement(node, {intl});
 }
 
-function shallowWithIntl(node, options = {}) {
+export function shallowWithIntl(node, options = {}) {
   return shallow(nodeWithIntlProp(node), Object.assign({}, options, {context: {intl}}));
 }
 
-function mountWithIntl(node, options = {}) {
+export function mountWithIntl(node, options = {}) {
   return mount(nodeWithIntlProp(node), Object.assign({}, options, {
     context: {intl},
     childContextTypes: {intl: intlShape}
   }));
 }
-
-module.exports = {
-  FakePerformance,
-  FakePrefs,
-  EventEmitter,
-  GlobalOverrider,
-  addNumberReducer,
-  mountWithIntl,
-  shallowWithIntl
-};