Bug 1426203 - Add ES modules, pinned counts and bug fixes to Activity Stream. r=dmose
authorEd Lee <edilee@mozilla.com>
Wed, 20 Dec 2017 12:10:35 -0800
changeset 448826 6e443911baf01e082d846c571170db7106083a01
parent 448825 24e90541f05637a7399f705bbce71bb12bfb5813
child 448827 ac05441603b174d6c1eedbfab2c2d4400fc9ea84
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdmose
bugs1426203
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1426203 - Add ES modules, pinned counts and bug fixes to Activity Stream. r=dmose 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
-};