Bug 1392384 - Add Photon styles, section ordering and bug fixes to Activity Stream. r?dmose draft
authorEd Lee <edilee@mozilla.com>
Fri, 25 Aug 2017 03:37:52 -0700
changeset 653552 75a9d272ba9609266a1c408e9aa09c59bc59ae4d
parent 653538 31465a03c03d1eec31cd4dd5d6b803724dcb29cd
child 728354 48b14c9e536ee8887c299850a742502660cc85e2
push id76343
push userbmo:edilee@mozilla.com
push dateSat, 26 Aug 2017 02:40:02 +0000
reviewersdmose
bugs1392384
milestone57.0a1
Bug 1392384 - Add Photon styles, section ordering and bug fixes to Activity Stream. r?dmose
browser/extensions/activity-stream/common/Actions.jsm
browser/extensions/activity-stream/common/Reducers.jsm
browser/extensions/activity-stream/common/ShortURL.jsm
browser/extensions/activity-stream/data/content/activity-stream.bundle.js
browser/extensions/activity-stream/data/content/activity-stream.css
browser/extensions/activity-stream/data/content/assets/glyph-bookmark-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-bookmark-remove-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-delete-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-dismiss-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-forward-16-white.svg
browser/extensions/activity-stream/data/content/assets/glyph-forward-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-historyItem-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-info-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-info-option-12.svg
browser/extensions/activity-stream/data/content/assets/glyph-more-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-newWindow-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-newWindow-private-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-now-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-pin-12.svg
browser/extensions/activity-stream/data/content/assets/glyph-pin-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-pocket-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-search-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-settings-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-topsites-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-trending-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-unpin-16.svg
browser/extensions/activity-stream/data/content/assets/glyph-webextension-16.svg
browser/extensions/activity-stream/data/content/assets/topic-show-more-12.svg
browser/extensions/activity-stream/data/locales.json
browser/extensions/activity-stream/lib/ActivityStream.jsm
browser/extensions/activity-stream/lib/ActivityStreamMessageChannel.jsm
browser/extensions/activity-stream/lib/LocalizationFeed.jsm
browser/extensions/activity-stream/lib/SectionsManager.jsm
browser/extensions/activity-stream/lib/ShortURL.jsm
browser/extensions/activity-stream/lib/TopSitesFeed.jsm
browser/extensions/activity-stream/lib/TopStoriesFeed.jsm
browser/extensions/activity-stream/test/functional/mochitest/browser.ini
browser/extensions/activity-stream/test/functional/mochitest/browser_getScreenshots.js
browser/extensions/activity-stream/test/unit/common/Reducers.test.js
browser/extensions/activity-stream/test/unit/common/ShortUrl.test.js
browser/extensions/activity-stream/test/unit/lib/ActivityStreamMessageChannel.test.js
browser/extensions/activity-stream/test/unit/lib/LocalizationFeed.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/TopSitesFeed.test.js
browser/extensions/activity-stream/test/unit/lib/TopStoriesFeed.test.js
browser/extensions/activity-stream/test/unit/utils.js
--- a/browser/extensions/activity-stream/common/Actions.jsm
+++ b/browser/extensions/activity-stream/common/Actions.jsm
@@ -51,18 +51,20 @@ for (const type of [
   "PLACES_LINK_BLOCKED",
   "PLACES_LINK_DELETED",
   "PREFS_INITIAL_VALUES",
   "PREF_CHANGED",
   "SAVE_SESSION_PERF_DATA",
   "SAVE_TO_POCKET",
   "SCREENSHOT_UPDATED",
   "SECTION_DEREGISTER",
+  "SECTION_DISABLE",
+  "SECTION_ENABLE",
   "SECTION_REGISTER",
-  "SECTION_ROWS_UPDATE",
+  "SECTION_UPDATE",
   "SET_PREF",
   "SNIPPETS_DATA",
   "SNIPPETS_RESET",
   "SYSTEM_TICK",
   "TELEMETRY_IMPRESSION_STATS",
   "TELEMETRY_PERFORMANCE_EVENT",
   "TELEMETRY_UNDESIRED_EVENT",
   "TELEMETRY_USER_EVENT",
--- a/browser/extensions/activity-stream/common/Reducers.jsm
+++ b/browser/extensions/activity-stream/common/Reducers.jsm
@@ -184,24 +184,37 @@ function Sections(prevState = INITIAL_ST
       // If section exists in prevState, update it
       newState = prevState.map(section => {
         if (section && section.id === action.data.id) {
           hasMatch = true;
           return Object.assign({}, section, action.data);
         }
         return section;
       });
-      // If section doesn't exist in prevState, create a new section object and
-      // append it to the sections state
+
+      // Invariant: Sections array sorted in increasing order of property `order`.
+      // If section doesn't exist in prevState, create a new section object. If
+      // the section has an order, insert it at the correct place in the array.
+      // Otherwise, prepend it and set the order to be minimal.
       if (!hasMatch) {
         const initialized = action.data.rows && action.data.rows.length > 0;
-        newState.push(Object.assign({title: "", initialized, rows: []}, action.data));
+        let order;
+        let index;
+        if (prevState.length > 0) {
+          order = action.data.order || prevState[0].order - 1;
+          index = newState.findIndex(section => section.order >= order);
+        } else {
+          order = action.data.order || 1;
+          index = 0;
+        }
+        const section = Object.assign({title: "", initialized, rows: [], order, enabled: false}, action.data);
+        newState.splice(index, 0, section);
       }
       return newState;
-    case at.SECTION_ROWS_UPDATE:
+    case at.SECTION_UPDATE:
       return prevState.map(section => {
         if (section && section.id === action.data.id) {
           return Object.assign({}, section, action.data);
         }
         return section;
       });
     case at.PLACES_BOOKMARK_ADDED:
       if (!action.data) {
deleted file mode 100644
--- a/browser/extensions/activity-stream/common/ShortURL.jsm
+++ /dev/null
@@ -1,32 +0,0 @@
-Components.utils.importGlobalProperties(["URL"]);
-
-/**
- * shortURL - Creates a short version of a link's url, used for display purposes
- *            e.g. {url: http://www.foosite.com, eTLD: "com"}  =>  "foosite"
- *
- * @param  {obj} link A link object
- *         {str} link.url (required)- The url of the link
- *         {str} link.eTLD (required) - The tld of the link
- *               e.g. for https://foo.org, the tld would be "org"
- *               Note that this property is added in various queries for ActivityStream
- *               via Services.eTLD.getPublicSuffix
- *         {str} link.hostname (optional) - The hostname of the url
- *               e.g. for http://www.hello.com/foo/bar, the hostname would be "www.hello.com"
- *         {str} link.title (optional) - The title of the link
- * @return {str}   A short url
- */
-this.shortURL = function shortURL(link) {
-  if (!link.url && !link.hostname) {
-    return "";
-  }
-  const {eTLD} = link;
-  const hostname = (link.hostname || new URL(link.url).hostname).replace(/^www\./i, "");
-
-  // Remove the eTLD (e.g., com, net) and the preceding period from the hostname
-  const eTLDLength = (eTLD || "").length || (hostname.match(/\.com$/) && 3);
-  const eTLDExtra = eTLDLength > 0 ? -(eTLDLength + 1) : Infinity;
-  // If URL and hostname are not present fallback to page title.
-  return hostname.slice(0, eTLDExtra).toLowerCase() || hostname || link.title || link.url;
-};
-
-this.EXPORTED_SYMBOLS = ["shortURL"];
--- a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
+++ b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
@@ -1,19 +1,19 @@
 /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
 /******/
 /******/ 	// The require function
 /******/ 	function __webpack_require__(moduleId) {
 /******/
 /******/ 		// Check if module is in cache
-/******/ 		if(installedModules[moduleId])
+/******/ 		if(installedModules[moduleId]) {
 /******/ 			return installedModules[moduleId].exports;
-/******/
+/******/ 		}
 /******/ 		// Create a new module (and put it into the cache)
 /******/ 		var module = installedModules[moduleId] = {
 /******/ 			i: moduleId,
 /******/ 			l: false,
 /******/ 			exports: {}
 /******/ 		};
 /******/
 /******/ 		// Execute the module function
@@ -28,19 +28,16 @@
 /******/
 /******/
 /******/ 	// expose the modules object (__webpack_modules__)
 /******/ 	__webpack_require__.m = modules;
 /******/
 /******/ 	// expose the module cache
 /******/ 	__webpack_require__.c = installedModules;
 /******/
-/******/ 	// identity function for calling harmony imports with the correct context
-/******/ 	__webpack_require__.i = function(value) { return value; };
-/******/
 /******/ 	// define getter function for harmony exports
 /******/ 	__webpack_require__.d = function(exports, name, getter) {
 /******/ 		if(!__webpack_require__.o(exports, name)) {
 /******/ 			Object.defineProperty(exports, name, {
 /******/ 				configurable: false,
 /******/ 				enumerable: true,
 /******/ 				get: getter
 /******/ 			});
@@ -58,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 = 25);
+/******/ 	return __webpack_require__(__webpack_require__.s = 7);
 /******/ })
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports) {
 
 module.exports = React;
 
@@ -98,17 +95,17 @@ const globalImportContext = typeof Windo
 
 
 // Create an object that avoids accidental differing key/value pairs:
 // {
 //   INIT: "INIT",
 //   UNINIT: "UNINIT"
 // }
 const actionTypes = {};
-for (const type of ["BLOCK_URL", "BOOKMARK_URL", "DELETE_BOOKMARK_BY_ID", "DELETE_HISTORY_URL", "DELETE_HISTORY_URL_CONFIRM", "DIALOG_CANCEL", "DIALOG_OPEN", "FEED_INIT", "INIT", "LOCALE_UPDATED", "MIGRATION_CANCEL", "MIGRATION_START", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_UNLOAD", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "PINNED_SITES_UPDATED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_REGISTER", "SECTION_ROWS_UPDATE", "SET_PREF", "SNIPPETS_DATA", "SNIPPETS_RESET", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_EDIT_CLOSE", "TOP_SITES_EDIT_OPEN", "TOP_SITES_PIN", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "UNINIT"]) {
+for (const type of ["BLOCK_URL", "BOOKMARK_URL", "DELETE_BOOKMARK_BY_ID", "DELETE_HISTORY_URL", "DELETE_HISTORY_URL_CONFIRM", "DIALOG_CANCEL", "DIALOG_OPEN", "FEED_INIT", "INIT", "LOCALE_UPDATED", "MIGRATION_CANCEL", "MIGRATION_START", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_UNLOAD", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "PINNED_SITES_UPDATED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_REGISTER", "SECTION_UPDATE", "SET_PREF", "SNIPPETS_DATA", "SNIPPETS_RESET", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_EDIT_CLOSE", "TOP_SITES_EDIT_OPEN", "TOP_SITES_PIN", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "UNINIT"]) {
   actionTypes[type] = type;
 }
 
 // Helper function for creating routed actions between content and main
 // Not intended to be used by consumers
 function _RouteMessage(action, options) {
   const meta = action.meta ? Object.assign({}, action.meta) : {};
   if (!options || !options.from || !options.to) {
@@ -319,23 +316,23 @@ module.exports = ReactRedux;
 
 
 const React = __webpack_require__(0);
 
 var _require = __webpack_require__(2);
 
 const injectIntl = _require.injectIntl;
 
-const ContextMenu = __webpack_require__(16);
+const ContextMenu = __webpack_require__(11);
 
 var _require2 = __webpack_require__(1);
 
 const ac = _require2.actionCreators;
 
-const linkMenuOptions = __webpack_require__(23);
+const linkMenuOptions = __webpack_require__(12);
 const DEFAULT_SITE_MENU_OPTIONS = ["CheckPinTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"];
 
 class LinkMenu extends React.Component {
   getOptions() {
     const props = this.props;
     const site = props.site,
           index = props.index,
           source = props.source;
@@ -541,32 +538,78 @@ module.exports = g;
 /***/ }),
 /* 7 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 const React = __webpack_require__(0);
+const ReactDOM = __webpack_require__(8);
+const Base = __webpack_require__(9);
+
+var _require = __webpack_require__(3);
+
+const Provider = _require.Provider;
+
+const initStore = __webpack_require__(21);
+
+var _require2 = __webpack_require__(23);
+
+const reducers = _require2.reducers;
+
+const DetectUserSessionStart = __webpack_require__(24);
+
+var _require3 = __webpack_require__(25);
+
+const addSnippetsSubscriber = _require3.addSnippetsSubscriber;
+
+
+new DetectUserSessionStart().sendEventOrAddListener();
+
+const store = initStore(reducers);
+
+ReactDOM.render(React.createElement(
+  Provider,
+  { store: store },
+  React.createElement(Base, null)
+), document.getElementById("root"));
+
+addSnippetsSubscriber(store);
+
+/***/ }),
+/* 8 */
+/***/ (function(module, exports) {
+
+module.exports = ReactDOM;
+
+/***/ }),
+/* 9 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const React = __webpack_require__(0);
 
 var _require = __webpack_require__(3);
 
 const connect = _require.connect;
 
 var _require2 = __webpack_require__(2);
 
 const addLocaleData = _require2.addLocaleData,
       IntlProvider = _require2.IntlProvider;
 
-const TopSites = __webpack_require__(21);
-const Search = __webpack_require__(19);
-const ConfirmDialog = __webpack_require__(15);
-const ManualMigration = __webpack_require__(17);
-const PreferencesPane = __webpack_require__(18);
-const Sections = __webpack_require__(20);
+const TopSites = __webpack_require__(10);
+const Search = __webpack_require__(13);
+const ConfirmDialog = __webpack_require__(14);
+const ManualMigration = __webpack_require__(15);
+const PreferencesPane = __webpack_require__(16);
+const Sections = __webpack_require__(17);
 
 // Locales that should be displayed RTL
 const RTL_LIST = ["ar", "he", "fa", "ur"];
 
 // 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(_ref) {
@@ -634,17 +677,1983 @@ class Base extends React.Component {
       )
     );
   }
 }
 
 module.exports = connect(state => ({ App: state.App, Prefs: state.Prefs }))(Base);
 
 /***/ }),
-/* 8 */
+/* 10 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const React = __webpack_require__(0);
+
+var _require = __webpack_require__(3);
+
+const connect = _require.connect;
+
+var _require2 = __webpack_require__(2);
+
+const FormattedMessage = _require2.FormattedMessage,
+      injectIntl = _require2.injectIntl;
+
+const LinkMenu = __webpack_require__(4);
+
+var _require3 = __webpack_require__(1);
+
+const ac = _require3.actionCreators,
+      at = _require3.actionTypes;
+
+var _require4 = __webpack_require__(5);
+
+const perfSvc = _require4.perfService;
+
+const TOP_SITES_SOURCE = "TOP_SITES";
+const TOP_SITES_CONTEXT_MENU_OPTIONS = ["CheckPinTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl", "DeleteUrl"];
+
+class TopSite extends React.Component {
+  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);
+  }
+  toggleContextMenu(event, index) {
+    this.setState({
+      activeTile: index,
+      showContextMenu: true
+    });
+  }
+  userEvent(event) {
+    this.props.dispatch(ac.UserEvent({
+      event,
+      source: TOP_SITES_SOURCE,
+      action_position: this.props.index
+    }));
+  }
+  onLinkClick(ev) {
+    if (this.props.editMode) {
+      // 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.link;
+
+    if (link.isPinned) {
+      this.props.dispatch(ac.SendToMain({
+        type: at.TOP_SITES_UNPIN,
+        data: { site: { url: link.url } }
+      }));
+    }
+    this.props.dispatch(ac.SendToMain({
+      type: at.BLOCK_URL,
+      data: link.url
+    }));
+    this.userEvent("BLOCK");
+  }
+  onPinButtonClick() {
+    var _props = this.props;
+    const link = _props.link,
+          index = _props.index;
+
+    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");
+    }
+  }
+  render() {
+    var _props2 = this.props;
+    const link = _props2.link,
+          index = _props2.index,
+          dispatch = _props2.dispatch,
+          editMode = _props2.editMode;
+
+    const isContextMenuOpen = this.state.showContextMenu && this.state.activeTile === index;
+    const title = link.hostname;
+    const topSiteOuterClassName = `top-site-outer${isContextMenuOpen ? " active" : ""}`;
+    const tippyTopIcon = link.tippyTopIcon;
+
+    let imageClassName;
+    let imageStyle;
+    if (tippyTopIcon) {
+      imageClassName = "tippy-top-icon";
+      imageStyle = {
+        backgroundColor: link.backgroundColor,
+        backgroundImage: `url(${tippyTopIcon})`
+      };
+    } else {
+      imageClassName = `screenshot${link.screenshot ? " active" : ""}`;
+      imageStyle = { backgroundImage: link.screenshot ? `url(${link.screenshot})` : "none" };
+    }
+    return React.createElement(
+      "li",
+      { className: topSiteOuterClassName, key: link.guid || link.url },
+      React.createElement(
+        "a",
+        { href: link.url, onClick: this.onLinkClick },
+        React.createElement(
+          "div",
+          { className: "tile", "aria-hidden": true },
+          React.createElement(
+            "span",
+            { className: "letter-fallback" },
+            title[0]
+          ),
+          React.createElement("div", { className: imageClassName, style: imageStyle })
+        ),
+        React.createElement(
+          "div",
+          { className: `title ${link.isPinned ? "pinned" : ""}` },
+          link.isPinned && React.createElement("div", { className: "icon icon-pin-small" }),
+          React.createElement(
+            "span",
+            { dir: "auto" },
+            title
+          )
+        )
+      ),
+      !editMode && 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: dispatch,
+          index: index,
+          onUpdate: this.onMenuUpdate,
+          options: TOP_SITES_CONTEXT_MENU_OPTIONS,
+          site: link,
+          source: TOP_SITES_SOURCE,
+          visible: isContextMenuOpen })
+      ),
+      editMode && 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-dismiss",
+          title: this.props.intl.formatMessage({ id: "edit_topsites_dismiss_button" }),
+          onClick: this.onDismissButtonClick })
+      )
+    );
+  }
+}
+
+TopSite.defaultProps = { editMode: false };
+
+/**
+ * A proxy class that uses double requestAnimationFrame from
+ * componentDidMount to dispatch a SAVE_SESSION_PERF_DATA to the main procsess
+ * after the paint.
+ *
+ * This uses two callbacks because, after one callback, this part of the tree
+ * may have rendered but not yet reflowed.  This strategy is modeled after
+ * https://stackoverflow.com/a/34999925 but uses a double rFA because
+ * we want to get to the closest reliable paint for measuring, and
+ * setTimeout is often throttled or queued by browsers in ways that could
+ * make it lag too long.
+ *
+ * XXX Should be made more generic by using this.props.children, or potentially
+ * even split out into a higher-order component to wrap whatever.
+ *
+ * @class TopSitesPerfTimer
+ * @extends {React.Component}
+ */
+class TopSitesPerfTimer extends React.Component {
+  constructor(props) {
+    super(props);
+    // Just for test dependency injection:
+    this.perfSvc = this.props.perfSvc || perfSvc;
+
+    this._sendPaintedEvent = this._sendPaintedEvent.bind(this);
+    this._timestampHandled = false;
+  }
+
+  componentDidMount() {
+    this._maybeSendPaintedEvent();
+  }
+
+  componentDidUpdate() {
+    this._maybeSendPaintedEvent();
+  }
+
+  /**
+   * Call the given callback after the upcoming frame paints.
+   *
+   * @note Both setTimeout and requestAnimationFrame are throttled when the page
+   * is hidden, so this callback may get called up to a second or so after the
+   * requestAnimationFrame "paint" for hidden tabs.
+   *
+   * Newtabs hidden while loading will presumably be fairly rare (other than
+   * preloaded tabs, which we will be filtering out on the server side), so such
+   * cases should get lost in the noise.
+   *
+   * If we decide that it's important to find out when something that's hidden
+   * has "painted", however, another option is to post a message to this window.
+   * That should happen even faster than setTimeout, and, at least as of this
+   * writing, it's not throttled in hidden windows in Firefox.
+   *
+   * @param {Function} callback
+   *
+   * @returns void
+   */
+  _afterFramePaint(callback) {
+    requestAnimationFrame(() => setTimeout(callback, 0));
+  }
+
+  _maybeSendPaintedEvent() {
+    // We don't want this to ever happen, but sometimes it does.  And when it
+    // does (typically on the first newtab at startup time calling
+    // componentDidMount), the paint(s) we care about will be later (eg
+    // in a subsequent componentDidUpdate).
+    if (!this.props.TopSites.initialized) {
+      // XXX should send bad event
+      return;
+    }
+
+    // If we've already handled a timestamp, don't do it again
+    if (this._timestampHandled) {
+      return;
+    }
+
+    // And if we haven't, we're doing so now, so remember that. Even if
+    // something goes wrong in the callback, we can't try again, as we'd be
+    // sending back the wrong data, and we have to do it here, so that other
+    // calls to this method while waiting for the next frame won't also try to
+    // handle handle it.
+    this._timestampHandled = true;
+
+    this._afterFramePaint(this._sendPaintedEvent);
+  }
+
+  _sendPaintedEvent() {
+    this.perfSvc.mark("topsites_first_painted_ts");
+
+    try {
+      let topsites_first_painted_ts = this.perfSvc.getMostRecentAbsMarkStartByName("topsites_first_painted_ts");
+
+      this.props.dispatch(ac.SendToMain({
+        type: at.SAVE_SESSION_PERF_DATA,
+        data: { topsites_first_painted_ts }
+      }));
+    } 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.
+    }
+  }
+
+  render() {
+    return React.createElement(TopSites, this.props);
+  }
+}
+
+const TopSites = props => React.createElement(
+  "section",
+  { className: "top-sites" },
+  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(
+    "ul",
+    { className: "top-sites-list" },
+    props.TopSites.rows.map((link, index) => link && React.createElement(TopSite, {
+      key: link.guid || link.url,
+      dispatch: props.dispatch,
+      link: link,
+      index: index,
+      intl: props.intl }))
+  ),
+  React.createElement(TopSitesEditIntl, props)
+);
+
+class TopSitesEdit extends React.Component {
+  constructor(props) {
+    super(props);
+    this.state = { showEditModal: false };
+    this.onEditButtonClick = this.onEditButtonClick.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
+    }));
+  }
+  render() {
+    return React.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 && React.createElement(
+        "div",
+        { className: "edit-topsites" },
+        React.createElement("div", { className: "modal-overlay" }),
+        React.createElement(
+          "div",
+          { className: "modal" },
+          React.createElement(
+            "section",
+            { className: "edit-topsites-inner-wrapper" },
+            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(
+              "ul",
+              { className: "top-sites-list" },
+              this.props.TopSites.rows.map((link, index) => link && React.createElement(TopSite, {
+                key: link.guid || link.url,
+                dispatch: this.props.dispatch,
+                link: link,
+                index: index,
+                intl: this.props.intl,
+                editMode: true }))
+            )
+          ),
+          React.createElement(
+            "section",
+            { className: "actions" },
+            React.createElement(
+              "button",
+              { className: "done", onClick: this.onEditButtonClick },
+              React.createElement(FormattedMessage, { id: "edit_topsites_done_button" })
+            )
+          )
+        )
+      )
+    );
+  }
+}
+
+const TopSitesEditIntl = injectIntl(TopSitesEdit);
+
+module.exports = connect(state => ({ TopSites: state.TopSites }))(TopSitesPerfTimer);
+module.exports._unconnected = TopSitesPerfTimer;
+module.exports.TopSite = TopSite;
+module.exports.TopSites = TopSites;
+module.exports.TopSitesEdit = TopSitesEdit;
+
+/***/ }),
+/* 11 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const React = __webpack_require__(0);
+
+class ContextMenu extends React.Component {
+  constructor(props) {
+    super(props);
+    this.hideContext = this.hideContext.bind(this);
+  }
+  hideContext() {
+    this.props.onUpdate(false);
+  }
+  componentWillMount() {
+    this.hideContext();
+  }
+  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);
+    }
+  }
+  componentWillUnmount() {
+    window.removeEventListener("click", this.hideContext);
+  }
+  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.Component {
+  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.option;
+
+    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.option;
+
+    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
+      )
+    );
+  }
+}
+
+module.exports = ContextMenu;
+module.exports.ContextMenu = ContextMenu;
+module.exports.ContextMenuItem = ContextMenuItem;
+
+/***/ }),
+/* 12 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var _require = __webpack_require__(1);
+
+const at = _require.actionTypes,
+      ac = _require.actionCreators;
+
+/**
+ * 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.
+ */
+
+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 }
+    }),
+    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,
+      incognito: true,
+      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: site.url }), ac.UserEvent({ event: "DELETE" })],
+        body_string_id: ["confirm_history_delete_p1", "confirm_history_delete_notice_p2"],
+        confirm_button_string_id: "menu_action_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,
+      incognito: true,
+      tiles: [{ id: site.guid, pos: index }]
+    }),
+    userEvent: "SAVE_TO_POCKET"
+  })
+};
+
+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);
+
+/***/ }),
+/* 13 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/* globals ContentSearchUIController */
+
+
+const React = __webpack_require__(0);
+
+var _require = __webpack_require__(3);
+
+const connect = _require.connect;
+
+var _require2 = __webpack_require__(2);
+
+const FormattedMessage = _require2.FormattedMessage,
+      injectIntl = _require2.injectIntl;
+
+var _require3 = __webpack_require__(1);
+
+const ac = _require3.actionCreators;
+
+
+class Search extends React.Component {
+  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(ac.UserEvent({ event: "SEARCH" }));
+    }
+  }
+  onClick(event) {
+    this.controller.search(event);
+  }
+  onInputMount(input) {
+    if (input) {
+      // The first "newtab" parameter here is called the "healthReportKey" and needs
+      // to be "newtab" 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)
+      this.controller = new ContentSearchUIController(input, input.parentNode, "newtab", "newtab");
+      addEventListener("ContentSearchClient", this);
+    } else {
+      this.controller = 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 React.createElement(
+      "form",
+      { 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",
+        {
+          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" })
+        )
+      )
+    );
+  }
+}
+
+module.exports = connect()(injectIntl(Search));
+module.exports._unconnected = Search;
+
+/***/ }),
+/* 14 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const React = __webpack_require__(0);
+
+var _require = __webpack_require__(3);
+
+const connect = _require.connect;
+
+var _require2 = __webpack_require__(2);
+
+const FormattedMessage = _require2.FormattedMessage;
+
+var _require3 = __webpack_require__(1);
+
+const actionTypes = _require3.actionTypes,
+      ac = _require3.actionCreators;
+
+/**
+ * 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"
+ * },
+ */
+
+const ConfirmDialog = React.createClass({
+  displayName: "ConfirmDialog",
+
+  getDefaultProps() {
+    return {
+      visible: false,
+      data: {}
+    };
+  },
+
+  _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", ref: "modal" },
+        React.createElement(
+          "section",
+          { className: "modal-message" },
+          this._renderModalMessage()
+        ),
+        React.createElement(
+          "section",
+          { className: "actions" },
+          React.createElement(
+            "button",
+            { ref: "cancelButton", onClick: this._handleCancelBtn },
+            React.createElement(FormattedMessage, { id: "topsites_form_cancel_button" })
+          ),
+          React.createElement(
+            "button",
+            { ref: "confirmButton", 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;
+
+/***/ }),
+/* 15 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const React = __webpack_require__(0);
+
+var _require = __webpack_require__(3);
+
+const connect = _require.connect;
+
+var _require2 = __webpack_require__(2);
+
+const FormattedMessage = _require2.FormattedMessage;
+
+var _require3 = __webpack_require__(1);
+
+const at = _require3.actionTypes,
+      ac = _require3.actionCreators;
+
+/**
+ * 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.Component {
+  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-info" }),
+        React.createElement(FormattedMessage, { id: "manual_migration_explanation" })
+      ),
+      React.createElement(
+        "div",
+        { className: "manual-migration-actions actions" },
+        React.createElement(
+          "button",
+          { onClick: this.onCancelTour },
+          React.createElement(FormattedMessage, { id: "manual_migration_cancel_button" })
+        ),
+        React.createElement(
+          "button",
+          { className: "done", onClick: this.onLaunchTour },
+          React.createElement(FormattedMessage, { id: "manual_migration_import_button" })
+        )
+      )
+    );
+  }
+}
+
+module.exports = connect()(ManualMigration);
+module.exports._unconnected = ManualMigration;
+
+/***/ }),
+/* 16 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const React = __webpack_require__(0);
+
+var _require = __webpack_require__(3);
+
+const connect = _require.connect;
+
+var _require2 = __webpack_require__(2);
+
+const injectIntl = _require2.injectIntl,
+      FormattedMessage = _require2.FormattedMessage;
+
+var _require3 = __webpack_require__(1);
+
+const ac = _require3.actionCreators,
+      at = _require3.actionTypes;
+
+
+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, onChange: props.onChange, className: props.className }),
+  React.createElement(
+    "label",
+    { htmlFor: props.prefName },
+    getFormattedMessage(props.titleString)
+  ),
+  props.descString && React.createElement(
+    "p",
+    { className: "prefs-input-description" },
+    getFormattedMessage(props.descString)
+  )
+);
+
+class PreferencesPane extends React.Component {
+  constructor(props) {
+    super(props);
+    this.state = { visible: false };
+    this.handleClickOutside = this.handleClickOutside.bind(this);
+    this.handlePrefChange = this.handlePrefChange.bind(this);
+    this.handleSectionChange = this.handleSectionChange.bind(this);
+    this.togglePane = this.togglePane.bind(this);
+  }
+  componentDidMount() {
+    document.addEventListener("click", this.handleClickOutside);
+  }
+  componentWillUnmount() {
+    document.removeEventListener("click", this.handleClickOutside);
+  }
+  handleClickOutside(event) {
+    // if we are showing the sidebar and there is a click outside, close it.
+    if (this.state.visible && !this.refs.wrapper.contains(event.target)) {
+      this.togglePane();
+    }
+  }
+  handlePrefChange(event) {
+    const target = event.target;
+    this.props.dispatch(ac.SetPref(target.name, target.checked));
+  }
+  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() {
+    this.setState({ visible: !this.state.visible });
+    const event = this.state.visible ? "CLOSE_NEWTAB_PREFS" : "OPEN_NEWTAB_PREFS";
+    this.props.dispatch(ac.UserEvent({ event }));
+  }
+  render() {
+    const props = this.props;
+    const prefs = props.Prefs.values;
+    const sections = props.Sections;
+    const isVisible = this.state.visible;
+    return React.createElement(
+      "div",
+      { className: "prefs-pane-wrapper", ref: "wrapper" },
+      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_body" })
+            ),
+            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(PreferencesInput, { className: "showTopSites", prefName: "showTopSites", value: prefs.showTopSites, onChange: this.handlePrefChange,
+              titleString: { id: "settings_pane_topsites_header" }, descString: { id: "settings_pane_topsites_body" } }),
+            sections.filter(section => !section.shouldHidePref).map((_ref) => {
+              let id = _ref.id,
+                  title = _ref.title,
+                  enabled = _ref.enabled,
+                  pref = _ref.pref;
+              return 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 });
+            })
+          ),
+          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, Sections: state.Sections }))(injectIntl(PreferencesPane));
+module.exports.PreferencesPane = PreferencesPane;
+module.exports.PreferencesInput = PreferencesInput;
+
+/***/ }),
+/* 17 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/* 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__(0);
+
+var _require = __webpack_require__(3);
+
+const connect = _require.connect;
+
+var _require2 = __webpack_require__(2);
+
+const injectIntl = _require2.injectIntl,
+      FormattedMessage = _require2.FormattedMessage;
+
+const Card = __webpack_require__(18);
+const Topics = __webpack_require__(20);
+
+var _require3 = __webpack_require__(1);
+
+const ac = _require3.actionCreators;
+
+
+const VISIBLE = "visible";
+const VISIBILITY_CHANGE_EVENT = "visibilitychange";
+
+class Section extends React.Component {
+  constructor(props) {
+    super(props);
+    this.onInfoEnter = this.onInfoEnter.bind(this);
+    this.onInfoLeave = this.onInfoLeave.bind(this);
+    this.state = { infoActive: false };
+  }
+
+  /**
+   * Take a truthy value to conditionally change the infoActive state.
+   */
+  _setInfoState(nextActive) {
+    const infoActive = !!nextActive;
+    if (infoActive !== this.state.infoActive) {
+      this.setState({ infoActive });
+    }
+  }
+
+  onInfoEnter() {
+    // We're getting focus or hover, so info state should be true if not yet.
+    this._setInfoState(true);
+  }
+
+  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));
+  }
+
+  getFormattedMessage(message) {
+    return typeof message === "string" ? React.createElement(
+      "span",
+      null,
+      message
+    ) : React.createElement(FormattedMessage, message);
+  }
+
+  _dispatchImpressionStats() {
+    const props = this.props;
+
+    const maxCards = 3 * props.maxRows;
+    props.dispatch(ac.ImpressionStats({
+      source: props.eventSource,
+      tiles: props.rows.slice(0, maxCards).map(link => ({ id: link.guid }))
+    }));
+  }
+
+  // This sends an event when a user sees a set of new content. If content
+  // changes while the page is hidden (i.e. preloaded or on a hidden tab),
+  // only send the event if the page becomes visible again.
+  sendImpressionStatsOrAddListener() {
+    const props = this.props;
+
+
+    if (!props.dispatch) {
+      return;
+    }
+
+    if (props.document.visibilityState === VISIBLE) {
+      this._dispatchImpressionStats();
+    } else {
+      // We should only ever send the latest impression stats ping, so remove any
+      // older listeners.
+      if (this._onVisibilityChange) {
+        props.document.removeEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange);
+      }
+
+      // When the page becoems visible, send the impression stats ping.
+      this._onVisibilityChange = () => {
+        if (props.document.visibilityState === VISIBLE) {
+          this._dispatchImpressionStats();
+          props.document.removeEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange);
+        }
+      };
+      props.document.addEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange);
+    }
+  }
+
+  componentDidMount() {
+    if (this.props.rows.length) {
+      this.sendImpressionStatsOrAddListener();
+    }
+  }
+
+  componentDidUpdate(prevProps) {
+    const props = this.props;
+
+    if (
+    // Don't send impression stats for the empty state
+    props.rows.length &&
+    // We only want to send impression stats if the content of the cards has changed
+    props.rows !== prevProps.rows) {
+      this.sendImpressionStatsOrAddListener();
+    }
+  }
+
+  render() {
+    var _props = this.props;
+    const id = _props.id,
+          eventSource = _props.eventSource,
+          title = _props.title,
+          icon = _props.icon,
+          rows = _props.rows,
+          infoOption = _props.infoOption,
+          emptyState = _props.emptyState,
+          dispatch = _props.dispatch,
+          maxRows = _props.maxRows,
+          contextMenuOptions = _props.contextMenuOptions,
+          intl = _props.intl;
+
+    const maxCards = 3 * maxRows;
+    const initialized = rows && rows.length > 0;
+    const shouldShowTopics = id === "topstories" && this.props.topics && this.props.topics.length > 0 && this.props.read_more_endpoint;
+
+    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" });
+
+    // <Section> <-- React component
+    // <section> <-- HTML5 element
+    return React.createElement(
+      "section",
+      null,
+      React.createElement(
+        "div",
+        { className: "section-top-bar" },
+        React.createElement(
+          "h3",
+          { className: "section-title" },
+          icon && icon.startsWith("moz-extension://") ? React.createElement("span", { className: "icon icon-small-spacer", style: { "background-image": `url('${icon}')` } }) : React.createElement("span", { className: `icon icon-small-spacer icon-${icon || "webextension"}` }),
+          this.getFormattedMessage(title)
+        ),
+        infoOption && React.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
+          }, infoOptionIconA11yAttrs)),
+          React.createElement(
+            "div",
+            { className: "info-option" },
+            infoOption.header && React.createElement(
+              "div",
+              { className: "info-option-header", role: "heading" },
+              this.getFormattedMessage(infoOption.header)
+            ),
+            infoOption.body && React.createElement(
+              "p",
+              { className: "info-option-body" },
+              this.getFormattedMessage(infoOption.body)
+            ),
+            infoOption.link && React.createElement(
+              "a",
+              { href: infoOption.link.href, target: "_blank", rel: "noopener noreferrer", className: "info-option-link" },
+              this.getFormattedMessage(infoOption.link.title || infoOption.link)
+            )
+          )
+        )
+      ),
+      React.createElement(
+        "ul",
+        { className: "section-list", style: { padding: 0 } },
+        rows.slice(0, maxCards).map((link, index) => link && React.createElement(Card, { index: index, dispatch: dispatch, link: link, contextMenuOptions: contextMenuOptions, eventSource: eventSource }))
+      ),
+      !initialized && React.createElement(
+        "div",
+        { className: "section-empty-state" },
+        React.createElement(
+          "div",
+          { className: "empty-state" },
+          React.createElement("img", { className: `empty-state-icon icon icon-${emptyState.icon}` }),
+          React.createElement(
+            "p",
+            { className: "empty-state-message" },
+            this.getFormattedMessage(emptyState.message)
+          )
+        )
+      ),
+      shouldShowTopics && React.createElement(Topics, { topics: this.props.topics, read_more_endpoint: this.props.read_more_endpoint })
+    );
+  }
+}
+
+Section.defaultProps = { document: global.document };
+
+const SectionIntl = injectIntl(Section);
+
+class Sections extends React.Component {
+  render() {
+    const sections = this.props.Sections;
+    return React.createElement(
+      "div",
+      { className: "sections-list" },
+      sections.filter(section => section.enabled).map(section => React.createElement(SectionIntl, _extends({ key: section.id }, section, { dispatch: this.props.dispatch })))
+    );
+  }
+}
+
+module.exports = connect(state => ({ Sections: state.Sections }))(Sections);
+module.exports._unconnected = Sections;
+module.exports.SectionIntl = SectionIntl;
+module.exports._unconnectedSection = Section;
+/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(6)))
+
+/***/ }),
+/* 18 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const React = __webpack_require__(0);
+const LinkMenu = __webpack_require__(4);
+
+var _require = __webpack_require__(2);
+
+const FormattedMessage = _require.FormattedMessage;
+
+const cardContextTypes = __webpack_require__(19);
+
+var _require2 = __webpack_require__(1);
+
+const ac = _require2.actionCreators,
+      at = _require2.actionTypes;
+
+/**
+ * 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.Component {
+  constructor(props) {
+    super(props);
+    this.state = { showContextMenu: false, activeCard: null };
+    this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
+    this.onMenuUpdate = this.onMenuUpdate.bind(this);
+    this.onLinkClick = this.onLinkClick.bind(this);
+  }
+  onMenuButtonClick(event) {
+    event.preventDefault();
+    this.setState({
+      activeCard: this.props.index,
+      showContextMenu: true
+    });
+  }
+  onLinkClick(event) {
+    event.preventDefault();
+    const altKey = event.altKey,
+          button = event.button,
+          ctrlKey = event.ctrlKey,
+          metaKey = event.metaKey,
+          shiftKey = event.shiftKey;
+
+    this.props.dispatch(ac.SendToMain({
+      type: at.OPEN_LINK,
+      data: Object.assign(this.props.link, { event: { altKey, button, ctrlKey, metaKey, shiftKey } })
+    }));
+    this.props.dispatch(ac.UserEvent({
+      event: "CLICK",
+      source: this.props.eventSource,
+      action_position: this.props.index
+    }));
+    this.props.dispatch(ac.ImpressionStats({
+      source: this.props.eventSource,
+      click: 0,
+      incognito: true,
+      tiles: [{ id: this.props.link.guid, pos: this.props.index }]
+    }));
+  }
+  onMenuUpdate(showContextMenu) {
+    this.setState({ showContextMenu });
+  }
+  render() {
+    var _props = this.props;
+    const index = _props.index,
+          link = _props.link,
+          dispatch = _props.dispatch,
+          contextMenuOptions = _props.contextMenuOptions,
+          eventSource = _props.eventSource;
+
+    const isContextMenuOpen = this.state.showContextMenu && this.state.activeCard === index;
+
+    var _ref = link.type ? cardContextTypes[link.type] : {};
+
+    const icon = _ref.icon,
+          intlID = _ref.intlID;
+
+
+    return React.createElement(
+      "li",
+      { className: `card-outer${isContextMenuOpen ? " active" : ""}` },
+      React.createElement(
+        "a",
+        { href: link.url, onClick: this.onLinkClick },
+        React.createElement(
+          "div",
+          { className: "card" },
+          link.image && React.createElement("div", { className: "card-preview-image", style: { backgroundImage: `url(${link.image})` } }),
+          React.createElement(
+            "div",
+            { className: `card-details${link.image ? "" : " no-image"}` },
+            link.hostname && React.createElement(
+              "div",
+              { className: "card-host-name" },
+              link.hostname
+            ),
+            React.createElement(
+              "div",
+              { className: `card-text${link.image ? "" : " no-image"}${link.hostname ? "" : " no-host-name"}${icon ? "" : " no-context"}` },
+              React.createElement(
+                "h4",
+                { className: "card-title", dir: "auto" },
+                link.title
+              ),
+              React.createElement(
+                "p",
+                { className: "card-description", dir: "auto" },
+                link.description
+              )
+            ),
+            icon && React.createElement(
+              "div",
+              { className: "card-context" },
+              React.createElement("span", { className: `card-context-icon icon icon-${icon}` }),
+              React.createElement(
+                "div",
+                { className: "card-context-label" },
+                React.createElement(FormattedMessage, { id: intlID, defaultMessage: "Visited" })
+              )
+            )
+          )
+        )
+      ),
+      React.createElement(
+        "button",
+        { className: "context-menu-button icon",
+          onClick: this.onMenuButtonClick },
+        React.createElement(
+          "span",
+          { className: "sr-only" },
+          `Open context menu for ${link.title}`
+        )
+      ),
+      React.createElement(LinkMenu, {
+        dispatch: dispatch,
+        index: index,
+        source: eventSource,
+        onUpdate: this.onMenuUpdate,
+        options: link.context_menu_options || contextMenuOptions,
+        site: link,
+        visible: isContextMenuOpen })
+    );
+  }
+}
+module.exports = Card;
+
+/***/ }),
+/* 19 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+module.exports = {
+  history: {
+    intlID: "type_label_visited",
+    icon: "historyItem"
+  },
+  bookmark: {
+    intlID: "type_label_bookmarked",
+    icon: "bookmark"
+  },
+  trending: {
+    intlID: "type_label_recommended",
+    icon: "trending"
+  },
+  now: {
+    intlID: "type_label_now",
+    icon: "now"
+  }
+};
+
+/***/ }),
+/* 20 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const React = __webpack_require__(0);
+
+var _require = __webpack_require__(2);
+
+const FormattedMessage = _require.FormattedMessage;
+
+
+class Topic extends React.Component {
+  render() {
+    var _props = this.props;
+    const url = _props.url,
+          name = _props.name;
+
+    return React.createElement(
+      "li",
+      null,
+      React.createElement(
+        "a",
+        { key: name, className: "topic-link", href: url },
+        name
+      )
+    );
+  }
+}
+
+class Topics extends React.Component {
+  render() {
+    var _props2 = this.props;
+    const topics = _props2.topics,
+          read_more_endpoint = _props2.read_more_endpoint;
+
+    return React.createElement(
+      "div",
+      { className: "topic" },
+      React.createElement(
+        "span",
+        null,
+        React.createElement(FormattedMessage, { id: "pocket_read_more" })
+      ),
+      React.createElement(
+        "ul",
+        null,
+        topics.map(t => React.createElement(Topic, { key: t.name, url: t.url, name: t.name }))
+      ),
+      React.createElement(
+        "a",
+        { className: "topic-read-more", href: read_more_endpoint },
+        React.createElement(FormattedMessage, { id: "pocket_read_even_more" }),
+        React.createElement("span", { className: "topic-read-more-logo" })
+      )
+    );
+  }
+}
+
+module.exports = Topics;
+module.exports._unconnected = Topics;
+module.exports.Topic = Topic;
+
+/***/ }),
+/* 21 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+/* eslint-env mozilla/frame-script */
+
+var _require = __webpack_require__(22);
+
+const createStore = _require.createStore,
+      combineReducers = _require.combineReducers,
+      applyMiddleware = _require.applyMiddleware;
+
+var _require2 = __webpack_require__(1);
+
+const au = _require2.actionUtils;
+
+
+const MERGE_STORE_ACTION = "NEW_TAB_INITIAL_STATE";
+const OUTGOING_MESSAGE_NAME = "ActivityStream:ContentToMain";
+const INCOMING_MESSAGE_NAME = "ActivityStream:MainToContent";
+
+/**
+ * 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
+ * higher order function which takes the main reducer (itself often a call to
+ * combineReducers) as a parameter.
+ *
+ * @param  {function} mainReducer reducer to call if action != MERGE_STORE_ACTION
+ * @return {function}             a reducer that, on MERGE_STORE_ACTION action,
+ *                                will return the action.data object merged
+ *                                into the previous state, and the result
+ *                                of calling mainReducer otherwise.
+ */
+function mergeStateReducer(mainReducer) {
+  return (prevState, action) => {
+    if (action.type === MERGE_STORE_ACTION) {
+      return Object.assign({}, prevState, action.data);
+    }
+
+    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)) {
+    sendAsyncMessage(OUTGOING_MESSAGE_NAME, action);
+  }
+  next(action);
+};
+
+/**
+ * initStore - Create a store and listen for incoming actions
+ *
+ * @param  {object} reducers An object containing Redux reducers
+ * @return {object}          A redux store
+ */
+module.exports = function initStore(reducers) {
+  const store = createStore(mergeStateReducer(combineReducers(reducers)), applyMiddleware(messageMiddleware));
+
+  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.MERGE_STORE_ACTION = MERGE_STORE_ACTION;
+module.exports.OUTGOING_MESSAGE_NAME = OUTGOING_MESSAGE_NAME;
+module.exports.INCOMING_MESSAGE_NAME = INCOMING_MESSAGE_NAME;
+
+/***/ }),
+/* 22 */
+/***/ (function(module, exports) {
+
+module.exports = Redux;
+
+/***/ }),
+/* 23 */
+/***/ (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/. */
+
+
+var _require = __webpack_require__(1);
+
+const at = _require.actionTypes;
+
+
+const TOP_SITES_SHOWMORE_LENGTH = 12;
+
+const INITIAL_STATE = {
+  App: {
+    // Have we received real data from the app yet?
+    initialized: false,
+    // The locale of the browser
+    locale: "",
+    // Localized strings with defaults
+    strings: null,
+    // The version of the system-addon
+    version: null
+  },
+  Snippets: { initialized: false },
+  TopSites: {
+    // Have we received real data from history yet?
+    initialized: false,
+    // The history (and possibly default) links
+    rows: []
+  },
+  Prefs: {
+    initialized: false,
+    values: {}
+  },
+  Dialog: {
+    visible: false,
+    data: {}
+  },
+  Sections: []
+};
+
+function App() {
+  let prevState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : INITIAL_STATE.App;
+  let action = arguments[1];
+
+  switch (action.type) {
+    case at.INIT:
+      return Object.assign({}, prevState, action.data || {}, { initialized: true });
+    case at.LOCALE_UPDATED:
+      {
+        if (!action.data) {
+          return prevState;
+        }
+        var _action$data = action.data;
+        let locale = _action$data.locale,
+            strings = _action$data.strings;
+
+        return Object.assign({}, prevState, {
+          locale,
+          strings
+        });
+      }
+    default:
+      return prevState;
+  }
+}
+
+/**
+ * insertPinned - Inserts pinned links in their specified slots
+ *
+ * @param {array} a list of links
+ * @param {array} a list of pinned links
+ * @return {array} resulting list of links with pinned links inserted
+ */
+function insertPinned(links, pinned) {
+  // Remove any pinned links
+  const pinnedUrls = pinned.map(link => link && link.url);
+  let newLinks = links.filter(link => link ? !pinnedUrls.includes(link.url) : false);
+  newLinks = newLinks.map(link => {
+    if (link && link.isPinned) {
+      delete link.isPinned;
+      delete link.pinIndex;
+    }
+    return link;
+  });
+
+  // Then insert them in their specified location
+  pinned.forEach((val, index) => {
+    if (!val) {
+      return;
+    }
+    let link = Object.assign({}, val, { isPinned: true, pinIndex: index });
+    if (index > newLinks.length) {
+      newLinks[index] = link;
+    } else {
+      newLinks.splice(index, 0, link);
+    }
+  });
+
+  return newLinks;
+}
+
+function TopSites() {
+  let prevState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : INITIAL_STATE.TopSites;
+  let action = arguments[1];
+
+  let hasMatch;
+  let newRows;
+  let pinned;
+  switch (action.type) {
+    case at.TOP_SITES_UPDATED:
+      if (!action.data) {
+        return prevState;
+      }
+      return Object.assign({}, prevState, { initialized: true, rows: action.data });
+    case at.SCREENSHOT_UPDATED:
+      newRows = prevState.rows.map(row => {
+        if (row && row.url === action.data.url) {
+          hasMatch = true;
+          return Object.assign({}, row, { screenshot: action.data.screenshot });
+        }
+        return row;
+      });
+      return hasMatch ? Object.assign({}, prevState, { rows: newRows }) : prevState;
+    case at.PLACES_BOOKMARK_ADDED:
+      if (!action.data) {
+        return prevState;
+      }
+      newRows = prevState.rows.map(site => {
+        if (site && site.url === action.data.url) {
+          var _action$data2 = action.data;
+          const bookmarkGuid = _action$data2.bookmarkGuid,
+                bookmarkTitle = _action$data2.bookmarkTitle,
+                lastModified = _action$data2.lastModified;
+
+          return Object.assign({}, site, { bookmarkGuid, bookmarkTitle, bookmarkDateCreated: lastModified });
+        }
+        return site;
+      });
+      return Object.assign({}, prevState, { rows: newRows });
+    case at.PLACES_BOOKMARK_REMOVED:
+      if (!action.data) {
+        return prevState;
+      }
+      newRows = prevState.rows.map(site => {
+        if (site && site.url === action.data.url) {
+          const newSite = Object.assign({}, site);
+          delete newSite.bookmarkGuid;
+          delete newSite.bookmarkTitle;
+          delete newSite.bookmarkDateCreated;
+          return newSite;
+        }
+        return site;
+      });
+      return Object.assign({}, prevState, { rows: newRows });
+    case at.PLACES_LINK_DELETED:
+    case at.PLACES_LINK_BLOCKED:
+      newRows = prevState.rows.filter(val => val && val.url !== action.data.url);
+      return Object.assign({}, prevState, { rows: newRows });
+    case at.PINNED_SITES_UPDATED:
+      pinned = action.data;
+      newRows = insertPinned(prevState.rows, pinned).slice(0, TOP_SITES_SHOWMORE_LENGTH);
+      return Object.assign({}, prevState, { rows: newRows });
+    default:
+      return prevState;
+  }
+}
+
+function Dialog() {
+  let prevState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : INITIAL_STATE.Dialog;
+  let action = arguments[1];
+
+  switch (action.type) {
+    case at.DIALOG_OPEN:
+      return Object.assign({}, prevState, { visible: true, data: action.data });
+    case at.DIALOG_CANCEL:
+      return Object.assign({}, prevState, { visible: false });
+    case at.DELETE_HISTORY_URL:
+      return Object.assign({}, INITIAL_STATE.Dialog);
+    default:
+      return prevState;
+  }
+}
+
+function Prefs() {
+  let prevState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : INITIAL_STATE.Prefs;
+  let action = arguments[1];
+
+  let newValues;
+  switch (action.type) {
+    case at.PREFS_INITIAL_VALUES:
+      return Object.assign({}, prevState, { initialized: true, values: action.data });
+    case at.PREF_CHANGED:
+      newValues = Object.assign({}, prevState.values);
+      newValues[action.data.name] = action.data.value;
+      return Object.assign({}, prevState, { values: newValues });
+    default:
+      return prevState;
+  }
+}
+
+function Sections() {
+  let prevState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : INITIAL_STATE.Sections;
+  let action = arguments[1];
+
+  let hasMatch;
+  let newState;
+  switch (action.type) {
+    case at.SECTION_DEREGISTER:
+      return prevState.filter(section => section.id !== action.data);
+    case at.SECTION_REGISTER:
+      // If section exists in prevState, update it
+      newState = prevState.map(section => {
+        if (section && section.id === action.data.id) {
+          hasMatch = true;
+          return Object.assign({}, section, action.data);
+        }
+        return section;
+      });
+
+      // Invariant: Sections array sorted in increasing order of property `order`.
+      // If section doesn't exist in prevState, create a new section object. If
+      // the section has an order, insert it at the correct place in the array.
+      // Otherwise, prepend it and set the order to be minimal.
+      if (!hasMatch) {
+        const initialized = action.data.rows && action.data.rows.length > 0;
+        let order;
+        let index;
+        if (prevState.length > 0) {
+          order = action.data.order || prevState[0].order - 1;
+          index = newState.findIndex(section => section.order >= order);
+        } else {
+          order = action.data.order || 1;
+          index = 0;
+        }
+        const section = Object.assign({ title: "", initialized, rows: [], order, enabled: false }, action.data);
+        newState.splice(index, 0, section);
+      }
+      return newState;
+    case at.SECTION_UPDATE:
+      return prevState.map(section => {
+        if (section && section.id === action.data.id) {
+          return Object.assign({}, section, action.data);
+        }
+        return section;
+      });
+    case at.PLACES_BOOKMARK_ADDED:
+      if (!action.data) {
+        return prevState;
+      }
+      return prevState.map(section => Object.assign({}, section, {
+        rows: section.rows.map(item => {
+          // find the item within the rows that is attempted to be bookmarked
+          if (item.url === action.data.url) {
+            var _action$data3 = action.data;
+            const bookmarkGuid = _action$data3.bookmarkGuid,
+                  bookmarkTitle = _action$data3.bookmarkTitle,
+                  lastModified = _action$data3.lastModified;
+
+            Object.assign(item, { bookmarkGuid, bookmarkTitle, bookmarkDateCreated: lastModified });
+          }
+          return item;
+        })
+      }));
+    case at.PLACES_BOOKMARK_REMOVED:
+      if (!action.data) {
+        return prevState;
+      }
+      return prevState.map(section => Object.assign({}, section, {
+        rows: section.rows.map(item => {
+          // find the bookmark within the rows that is attempted to be removed
+          if (item.url === action.data.url) {
+            const newSite = Object.assign({}, item);
+            delete newSite.bookmarkGuid;
+            delete newSite.bookmarkTitle;
+            delete newSite.bookmarkDateCreated;
+            return newSite;
+          }
+          return item;
+        })
+      }));
+    case at.PLACES_LINK_DELETED:
+    case at.PLACES_LINK_BLOCKED:
+      return prevState.map(section => Object.assign({}, section, { rows: section.rows.filter(site => site.url !== action.data.url) }));
+    default:
+      return prevState;
+  }
+}
+
+function Snippets() {
+  let prevState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : INITIAL_STATE.Snippets;
+  let action = arguments[1];
+
+  switch (action.type) {
+    case at.SNIPPETS_DATA:
+      return Object.assign({}, prevState, { initialized: true }, action.data);
+    case at.SNIPPETS_RESET:
+      return INITIAL_STATE.Snippets;
+    default:
+      return prevState;
+  }
+}
+
+var reducers = { TopSites, App, Snippets, Prefs, Dialog, Sections };
+module.exports = {
+  reducers,
+  INITIAL_STATE,
+  insertPinned,
+  TOP_SITES_SHOWMORE_LENGTH
+};
+
+/***/ }),
+/* 24 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 var _require = __webpack_require__(1);
 
 const at = _require.actionTypes;
@@ -714,102 +2723,17 @@ module.exports = class DetectUserSession
     if (this.document.visibilityState === VISIBLE) {
       this._sendEvent();
       this.document.removeEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange);
     }
   }
 };
 
 /***/ }),
-/* 9 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* eslint-env mozilla/frame-script */
-
-var _require = __webpack_require__(24);
-
-const createStore = _require.createStore,
-      combineReducers = _require.combineReducers,
-      applyMiddleware = _require.applyMiddleware;
-
-var _require2 = __webpack_require__(1);
-
-const au = _require2.actionUtils;
-
-
-const MERGE_STORE_ACTION = "NEW_TAB_INITIAL_STATE";
-const OUTGOING_MESSAGE_NAME = "ActivityStream:ContentToMain";
-const INCOMING_MESSAGE_NAME = "ActivityStream:MainToContent";
-
-/**
- * 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
- * higher order function which takes the main reducer (itself often a call to
- * combineReducers) as a parameter.
- *
- * @param  {function} mainReducer reducer to call if action != MERGE_STORE_ACTION
- * @return {function}             a reducer that, on MERGE_STORE_ACTION action,
- *                                will return the action.data object merged
- *                                into the previous state, and the result
- *                                of calling mainReducer otherwise.
- */
-function mergeStateReducer(mainReducer) {
-  return (prevState, action) => {
-    if (action.type === MERGE_STORE_ACTION) {
-      return Object.assign({}, prevState, action.data);
-    }
-
-    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)) {
-    sendAsyncMessage(OUTGOING_MESSAGE_NAME, action);
-  }
-  next(action);
-};
-
-/**
- * initStore - Create a store and listen for incoming actions
- *
- * @param  {object} reducers An object containing Redux reducers
- * @return {object}          A redux store
- */
-module.exports = function initStore(reducers) {
-  const store = createStore(mergeStateReducer(combineReducers(reducers)), applyMiddleware(messageMiddleware));
-
-  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.MERGE_STORE_ACTION = MERGE_STORE_ACTION;
-module.exports.OUTGOING_MESSAGE_NAME = OUTGOING_MESSAGE_NAME;
-module.exports.INCOMING_MESSAGE_NAME = INCOMING_MESSAGE_NAME;
-
-/***/ }),
-/* 10 */
+/* 25 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 /* WEBPACK VAR INJECTION */(function(global) {
 
 const DATABASE_NAME = "snippets_db";
 const DATABASE_VERSION = 1;
 const SNIPPETS_OBJECTSTORE_NAME = "snippets";
@@ -1112,1914 +3036,10 @@ function addSnippetsSubscriber(store) {
 module.exports = {
   addSnippetsSubscriber,
   SnippetsMap,
   SnippetsProvider,
   SNIPPETS_UPDATE_INTERVAL_MS
 };
 /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(6)))
 
-/***/ }),
-/* 11 */
-/***/ (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/. */
-
-
-var _require = __webpack_require__(1);
-
-const at = _require.actionTypes;
-
-
-const TOP_SITES_SHOWMORE_LENGTH = 12;
-
-const INITIAL_STATE = {
-  App: {
-    // Have we received real data from the app yet?
-    initialized: false,
-    // The locale of the browser
-    locale: "",
-    // Localized strings with defaults
-    strings: null,
-    // The version of the system-addon
-    version: null
-  },
-  Snippets: { initialized: false },
-  TopSites: {
-    // Have we received real data from history yet?
-    initialized: false,
-    // The history (and possibly default) links
-    rows: []
-  },
-  Prefs: {
-    initialized: false,
-    values: {}
-  },
-  Dialog: {
-    visible: false,
-    data: {}
-  },
-  Sections: []
-};
-
-function App() {
-  let prevState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : INITIAL_STATE.App;
-  let action = arguments[1];
-
-  switch (action.type) {
-    case at.INIT:
-      return Object.assign({}, prevState, action.data || {}, { initialized: true });
-    case at.LOCALE_UPDATED:
-      {
-        if (!action.data) {
-          return prevState;
-        }
-        var _action$data = action.data;
-        let locale = _action$data.locale,
-            strings = _action$data.strings;
-
-        return Object.assign({}, prevState, {
-          locale,
-          strings
-        });
-      }
-    default:
-      return prevState;
-  }
-}
-
-/**
- * insertPinned - Inserts pinned links in their specified slots
- *
- * @param {array} a list of links
- * @param {array} a list of pinned links
- * @return {array} resulting list of links with pinned links inserted
- */
-function insertPinned(links, pinned) {
-  // Remove any pinned links
-  const pinnedUrls = pinned.map(link => link && link.url);
-  let newLinks = links.filter(link => link ? !pinnedUrls.includes(link.url) : false);
-  newLinks = newLinks.map(link => {
-    if (link && link.isPinned) {
-      delete link.isPinned;
-      delete link.pinIndex;
-    }
-    return link;
-  });
-
-  // Then insert them in their specified location
-  pinned.forEach((val, index) => {
-    if (!val) {
-      return;
-    }
-    let link = Object.assign({}, val, { isPinned: true, pinIndex: index });
-    if (index > newLinks.length) {
-      newLinks[index] = link;
-    } else {
-      newLinks.splice(index, 0, link);
-    }
-  });
-
-  return newLinks;
-}
-
-function TopSites() {
-  let prevState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : INITIAL_STATE.TopSites;
-  let action = arguments[1];
-
-  let hasMatch;
-  let newRows;
-  let pinned;
-  switch (action.type) {
-    case at.TOP_SITES_UPDATED:
-      if (!action.data) {
-        return prevState;
-      }
-      return Object.assign({}, prevState, { initialized: true, rows: action.data });
-    case at.SCREENSHOT_UPDATED:
-      newRows = prevState.rows.map(row => {
-        if (row && row.url === action.data.url) {
-          hasMatch = true;
-          return Object.assign({}, row, { screenshot: action.data.screenshot });
-        }
-        return row;
-      });
-      return hasMatch ? Object.assign({}, prevState, { rows: newRows }) : prevState;
-    case at.PLACES_BOOKMARK_ADDED:
-      if (!action.data) {
-        return prevState;
-      }
-      newRows = prevState.rows.map(site => {
-        if (site && site.url === action.data.url) {
-          var _action$data2 = action.data;
-          const bookmarkGuid = _action$data2.bookmarkGuid,
-                bookmarkTitle = _action$data2.bookmarkTitle,
-                lastModified = _action$data2.lastModified;
-
-          return Object.assign({}, site, { bookmarkGuid, bookmarkTitle, bookmarkDateCreated: lastModified });
-        }
-        return site;
-      });
-      return Object.assign({}, prevState, { rows: newRows });
-    case at.PLACES_BOOKMARK_REMOVED:
-      if (!action.data) {
-        return prevState;
-      }
-      newRows = prevState.rows.map(site => {
-        if (site && site.url === action.data.url) {
-          const newSite = Object.assign({}, site);
-          delete newSite.bookmarkGuid;
-          delete newSite.bookmarkTitle;
-          delete newSite.bookmarkDateCreated;
-          return newSite;
-        }
-        return site;
-      });
-      return Object.assign({}, prevState, { rows: newRows });
-    case at.PLACES_LINK_DELETED:
-    case at.PLACES_LINK_BLOCKED:
-      newRows = prevState.rows.filter(val => val && val.url !== action.data.url);
-      return Object.assign({}, prevState, { rows: newRows });
-    case at.PINNED_SITES_UPDATED:
-      pinned = action.data;
-      newRows = insertPinned(prevState.rows, pinned).slice(0, TOP_SITES_SHOWMORE_LENGTH);
-      return Object.assign({}, prevState, { rows: newRows });
-    default:
-      return prevState;
-  }
-}
-
-function Dialog() {
-  let prevState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : INITIAL_STATE.Dialog;
-  let action = arguments[1];
-
-  switch (action.type) {
-    case at.DIALOG_OPEN:
-      return Object.assign({}, prevState, { visible: true, data: action.data });
-    case at.DIALOG_CANCEL:
-      return Object.assign({}, prevState, { visible: false });
-    case at.DELETE_HISTORY_URL:
-      return Object.assign({}, INITIAL_STATE.Dialog);
-    default:
-      return prevState;
-  }
-}
-
-function Prefs() {
-  let prevState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : INITIAL_STATE.Prefs;
-  let action = arguments[1];
-
-  let newValues;
-  switch (action.type) {
-    case at.PREFS_INITIAL_VALUES:
-      return Object.assign({}, prevState, { initialized: true, values: action.data });
-    case at.PREF_CHANGED:
-      newValues = Object.assign({}, prevState.values);
-      newValues[action.data.name] = action.data.value;
-      return Object.assign({}, prevState, { values: newValues });
-    default:
-      return prevState;
-  }
-}
-
-function Sections() {
-  let prevState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : INITIAL_STATE.Sections;
-  let action = arguments[1];
-
-  let hasMatch;
-  let newState;
-  switch (action.type) {
-    case at.SECTION_DEREGISTER:
-      return prevState.filter(section => section.id !== action.data);
-    case at.SECTION_REGISTER:
-      // If section exists in prevState, update it
-      newState = prevState.map(section => {
-        if (section && section.id === action.data.id) {
-          hasMatch = true;
-          return Object.assign({}, section, action.data);
-        }
-        return section;
-      });
-      // If section doesn't exist in prevState, create a new section object and
-      // append it to the sections state
-      if (!hasMatch) {
-        const initialized = action.data.rows && action.data.rows.length > 0;
-        newState.push(Object.assign({ title: "", initialized, rows: [] }, action.data));
-      }
-      return newState;
-    case at.SECTION_ROWS_UPDATE:
-      return prevState.map(section => {
-        if (section && section.id === action.data.id) {
-          return Object.assign({}, section, action.data);
-        }
-        return section;
-      });
-    case at.PLACES_BOOKMARK_ADDED:
-      if (!action.data) {
-        return prevState;
-      }
-      return prevState.map(section => Object.assign({}, section, {
-        rows: section.rows.map(item => {
-          // find the item within the rows that is attempted to be bookmarked
-          if (item.url === action.data.url) {
-            var _action$data3 = action.data;
-            const bookmarkGuid = _action$data3.bookmarkGuid,
-                  bookmarkTitle = _action$data3.bookmarkTitle,
-                  lastModified = _action$data3.lastModified;
-
-            Object.assign(item, { bookmarkGuid, bookmarkTitle, bookmarkDateCreated: lastModified });
-          }
-          return item;
-        })
-      }));
-    case at.PLACES_BOOKMARK_REMOVED:
-      if (!action.data) {
-        return prevState;
-      }
-      return prevState.map(section => Object.assign({}, section, {
-        rows: section.rows.map(item => {
-          // find the bookmark within the rows that is attempted to be removed
-          if (item.url === action.data.url) {
-            const newSite = Object.assign({}, item);
-            delete newSite.bookmarkGuid;
-            delete newSite.bookmarkTitle;
-            delete newSite.bookmarkDateCreated;
-            return newSite;
-          }
-          return item;
-        })
-      }));
-    case at.PLACES_LINK_DELETED:
-    case at.PLACES_LINK_BLOCKED:
-      return prevState.map(section => Object.assign({}, section, { rows: section.rows.filter(site => site.url !== action.data.url) }));
-    default:
-      return prevState;
-  }
-}
-
-function Snippets() {
-  let prevState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : INITIAL_STATE.Snippets;
-  let action = arguments[1];
-
-  switch (action.type) {
-    case at.SNIPPETS_DATA:
-      return Object.assign({}, prevState, { initialized: true }, action.data);
-    case at.SNIPPETS_RESET:
-      return INITIAL_STATE.Snippets;
-    default:
-      return prevState;
-  }
-}
-
-var reducers = { TopSites, App, Snippets, Prefs, Dialog, Sections };
-module.exports = {
-  reducers,
-  INITIAL_STATE,
-  insertPinned,
-  TOP_SITES_SHOWMORE_LENGTH
-};
-
-/***/ }),
-/* 12 */
-/***/ (function(module, exports) {
-
-module.exports = ReactDOM;
-
-/***/ }),
-/* 13 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-const React = __webpack_require__(0);
-const LinkMenu = __webpack_require__(4);
-
-var _require = __webpack_require__(2);
-
-const FormattedMessage = _require.FormattedMessage;
-
-const cardContextTypes = __webpack_require__(14);
-
-var _require2 = __webpack_require__(1);
-
-const ac = _require2.actionCreators,
-      at = _require2.actionTypes;
-
-/**
- * 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.Component {
-  constructor(props) {
-    super(props);
-    this.state = { showContextMenu: false, activeCard: null };
-    this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
-    this.onMenuUpdate = this.onMenuUpdate.bind(this);
-    this.onLinkClick = this.onLinkClick.bind(this);
-  }
-  onMenuButtonClick(event) {
-    event.preventDefault();
-    this.setState({
-      activeCard: this.props.index,
-      showContextMenu: true
-    });
-  }
-  onLinkClick(event) {
-    event.preventDefault();
-    const altKey = event.altKey,
-          button = event.button,
-          ctrlKey = event.ctrlKey,
-          metaKey = event.metaKey,
-          shiftKey = event.shiftKey;
-
-    this.props.dispatch(ac.SendToMain({
-      type: at.OPEN_LINK,
-      data: Object.assign(this.props.link, { event: { altKey, button, ctrlKey, metaKey, shiftKey } })
-    }));
-    this.props.dispatch(ac.UserEvent({
-      event: "CLICK",
-      source: this.props.eventSource,
-      action_position: this.props.index
-    }));
-    this.props.dispatch(ac.ImpressionStats({
-      source: this.props.eventSource,
-      click: 0,
-      incognito: true,
-      tiles: [{ id: this.props.link.guid, pos: this.props.index }]
-    }));
-  }
-  onMenuUpdate(showContextMenu) {
-    this.setState({ showContextMenu });
-  }
-  render() {
-    var _props = this.props;
-    const index = _props.index,
-          link = _props.link,
-          dispatch = _props.dispatch,
-          contextMenuOptions = _props.contextMenuOptions,
-          eventSource = _props.eventSource;
-
-    const isContextMenuOpen = this.state.showContextMenu && this.state.activeCard === index;
-
-    var _ref = link.type ? cardContextTypes[link.type] : {};
-
-    const icon = _ref.icon,
-          intlID = _ref.intlID;
-
-
-    return React.createElement(
-      "li",
-      { className: `card-outer${isContextMenuOpen ? " active" : ""}` },
-      React.createElement(
-        "a",
-        { href: link.url, onClick: this.onLinkClick },
-        React.createElement(
-          "div",
-          { className: "card" },
-          link.image && React.createElement("div", { className: "card-preview-image", style: { backgroundImage: `url(${link.image})` } }),
-          React.createElement(
-            "div",
-            { className: `card-details${link.image ? "" : " no-image"}` },
-            link.hostname && React.createElement(
-              "div",
-              { className: "card-host-name" },
-              link.hostname
-            ),
-            React.createElement(
-              "div",
-              { className: `card-text${link.image ? "" : " no-image"}${link.hostname ? "" : " no-host-name"}${icon ? "" : " no-context"}` },
-              React.createElement(
-                "h4",
-                { className: "card-title", dir: "auto" },
-                link.title
-              ),
-              React.createElement(
-                "p",
-                { className: "card-description", dir: "auto" },
-                link.description
-              )
-            ),
-            icon && React.createElement(
-              "div",
-              { className: "card-context" },
-              React.createElement("span", { className: `card-context-icon icon icon-${icon}` }),
-              React.createElement(
-                "div",
-                { className: "card-context-label" },
-                React.createElement(FormattedMessage, { id: intlID, defaultMessage: "Visited" })
-              )
-            )
-          )
-        )
-      ),
-      React.createElement(
-        "button",
-        { className: "context-menu-button",
-          onClick: this.onMenuButtonClick },
-        React.createElement(
-          "span",
-          { className: "sr-only" },
-          `Open context menu for ${link.title}`
-        )
-      ),
-      React.createElement(LinkMenu, {
-        dispatch: dispatch,
-        index: index,
-        source: eventSource,
-        onUpdate: this.onMenuUpdate,
-        options: link.context_menu_options || contextMenuOptions,
-        site: link,
-        visible: isContextMenuOpen })
-    );
-  }
-}
-module.exports = Card;
-
-/***/ }),
-/* 14 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-module.exports = {
-  history: {
-    intlID: "type_label_visited",
-    icon: "historyItem"
-  },
-  bookmark: {
-    intlID: "type_label_bookmarked",
-    icon: "bookmark"
-  },
-  trending: {
-    intlID: "type_label_recommended",
-    icon: "trending"
-  },
-  now: {
-    intlID: "type_label_now",
-    icon: "now"
-  }
-};
-
-/***/ }),
-/* 15 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-const React = __webpack_require__(0);
-
-var _require = __webpack_require__(3);
-
-const connect = _require.connect;
-
-var _require2 = __webpack_require__(2);
-
-const FormattedMessage = _require2.FormattedMessage;
-
-var _require3 = __webpack_require__(1);
-
-const actionTypes = _require3.actionTypes,
-      ac = _require3.actionCreators;
-
-/**
- * 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"
- * },
- */
-
-const ConfirmDialog = React.createClass({
-  displayName: "ConfirmDialog",
-
-  getDefaultProps() {
-    return {
-      visible: false,
-      data: {}
-    };
-  },
-
-  _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", ref: "modal" },
-        React.createElement(
-          "section",
-          { className: "modal-message" },
-          this._renderModalMessage()
-        ),
-        React.createElement(
-          "section",
-          { className: "actions" },
-          React.createElement(
-            "button",
-            { ref: "cancelButton", onClick: this._handleCancelBtn },
-            React.createElement(FormattedMessage, { id: "topsites_form_cancel_button" })
-          ),
-          React.createElement(
-            "button",
-            { ref: "confirmButton", 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;
-
-/***/ }),
-/* 16 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-const React = __webpack_require__(0);
-
-class ContextMenu extends React.Component {
-  constructor(props) {
-    super(props);
-    this.hideContext = this.hideContext.bind(this);
-  }
-  hideContext() {
-    this.props.onUpdate(false);
-  }
-  componentWillMount() {
-    this.hideContext();
-  }
-  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);
-    }
-  }
-  componentWillUnmount() {
-    window.removeEventListener("click", this.hideContext);
-  }
-  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.Component {
-  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.option;
-
-    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.option;
-
-    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
-      )
-    );
-  }
-}
-
-module.exports = ContextMenu;
-module.exports.ContextMenu = ContextMenu;
-module.exports.ContextMenuItem = ContextMenuItem;
-
-/***/ }),
-/* 17 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-const React = __webpack_require__(0);
-
-var _require = __webpack_require__(3);
-
-const connect = _require.connect;
-
-var _require2 = __webpack_require__(2);
-
-const FormattedMessage = _require2.FormattedMessage;
-
-var _require3 = __webpack_require__(1);
-
-const at = _require3.actionTypes,
-      ac = _require3.actionCreators;
-
-/**
- * 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.Component {
-  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-info" }),
-        React.createElement(FormattedMessage, { id: "manual_migration_explanation" })
-      ),
-      React.createElement(
-        "div",
-        { className: "manual-migration-actions actions" },
-        React.createElement(
-          "button",
-          { onClick: this.onCancelTour },
-          React.createElement(FormattedMessage, { id: "manual_migration_cancel_button" })
-        ),
-        React.createElement(
-          "button",
-          { className: "done", onClick: this.onLaunchTour },
-          React.createElement(FormattedMessage, { id: "manual_migration_import_button" })
-        )
-      )
-    );
-  }
-}
-
-module.exports = connect()(ManualMigration);
-module.exports._unconnected = ManualMigration;
-
-/***/ }),
-/* 18 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-const React = __webpack_require__(0);
-
-var _require = __webpack_require__(3);
-
-const connect = _require.connect;
-
-var _require2 = __webpack_require__(2);
-
-const injectIntl = _require2.injectIntl,
-      FormattedMessage = _require2.FormattedMessage;
-
-var _require3 = __webpack_require__(1);
-
-const ac = _require3.actionCreators;
-
-
-const PreferencesInput = props => React.createElement(
-  "section",
-  null,
-  React.createElement("input", { type: "checkbox", id: props.prefName, name: props.prefName, checked: props.value, onChange: props.onChange, className: props.className }),
-  React.createElement(
-    "label",
-    { htmlFor: props.prefName },
-    React.createElement(FormattedMessage, { id: props.titleStringId, values: props.titleStringValues })
-  ),
-  props.descStringId && React.createElement(
-    "p",
-    { className: "prefs-input-description" },
-    React.createElement(FormattedMessage, { id: props.descStringId })
-  )
-);
-
-class PreferencesPane extends React.Component {
-  constructor(props) {
-    super(props);
-    this.state = { visible: false };
-    this.handleClickOutside = this.handleClickOutside.bind(this);
-    this.handleChange = this.handleChange.bind(this);
-    this.togglePane = this.togglePane.bind(this);
-
-    // TODO This is temporary until sections register their PreferenceInput component automatically
-    const optionJSON = props.Prefs.values["feeds.section.topstories.options"];
-    if (optionJSON) {
-      try {
-        this.topStoriesOptions = JSON.parse(optionJSON);
-      } catch (e) {
-        console.error("Problem parsing feeds.section.topstories.options", e); // eslint-disable-line no-console
-      }
-    }
-  }
-  componentDidMount() {
-    document.addEventListener("click", this.handleClickOutside);
-  }
-  componentWillUnmount() {
-    document.removeEventListener("click", this.handleClickOutside);
-  }
-  handleClickOutside(event) {
-    // if we are showing the sidebar and there is a click outside, close it.
-    if (this.state.visible && !this.refs.wrapper.contains(event.target)) {
-      this.togglePane();
-    }
-  }
-  handleChange(event) {
-    const target = event.target;
-    this.props.dispatch(ac.SetPref(target.name, target.checked));
-  }
-  togglePane() {
-    this.setState({ visible: !this.state.visible });
-    const event = this.state.visible ? "CLOSE_NEWTAB_PREFS" : "OPEN_NEWTAB_PREFS";
-    this.props.dispatch(ac.UserEvent({ event }));
-  }
-  render() {
-    const props = this.props;
-    const prefs = props.Prefs.values;
-    const isVisible = this.state.visible;
-    return React.createElement(
-      "div",
-      { className: "prefs-pane-wrapper", ref: "wrapper" },
-      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_body" })
-            ),
-            React.createElement(PreferencesInput, { className: "showSearch", prefName: "showSearch", value: prefs.showSearch, onChange: this.handleChange,
-              titleStringId: "settings_pane_search_header", descStringId: "settings_pane_search_body" }),
-            React.createElement(PreferencesInput, { className: "showTopSites", prefName: "showTopSites", value: prefs.showTopSites, onChange: this.handleChange,
-              titleStringId: "settings_pane_topsites_header", descStringId: "settings_pane_topsites_body" }),
-            this.topStoriesOptions && !this.topStoriesOptions.hidden && React.createElement(PreferencesInput, { className: "showTopStories", prefName: "feeds.section.topstories",
-              value: prefs["feeds.section.topstories"], onChange: this.handleChange,
-              titleStringId: "header_recommended_by", titleStringValues: { provider: this.topStoriesOptions.provider_name },
-              descStringId: this.topStoriesOptions.provider_description })
-          ),
-          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 }))(injectIntl(PreferencesPane));
-module.exports.PreferencesPane = PreferencesPane;
-module.exports.PreferencesInput = PreferencesInput;
-
-/***/ }),
-/* 19 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-/* globals ContentSearchUIController */
-
-
-const React = __webpack_require__(0);
-
-var _require = __webpack_require__(3);
-
-const connect = _require.connect;
-
-var _require2 = __webpack_require__(2);
-
-const FormattedMessage = _require2.FormattedMessage,
-      injectIntl = _require2.injectIntl;
-
-var _require3 = __webpack_require__(1);
-
-const ac = _require3.actionCreators;
-
-
-class Search extends React.Component {
-  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(ac.UserEvent({ event: "SEARCH" }));
-    }
-  }
-  onClick(event) {
-    this.controller.search(event);
-  }
-  onInputMount(input) {
-    if (input) {
-      // The first "newtab" parameter here is called the "healthReportKey" and needs
-      // to be "newtab" 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)
-      this.controller = new ContentSearchUIController(input, input.parentNode, "newtab", "newtab");
-      addEventListener("ContentSearchClient", this);
-    } else {
-      this.controller = 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 React.createElement(
-      "form",
-      { 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",
-        {
-          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" })
-        )
-      )
-    );
-  }
-}
-
-module.exports = connect()(injectIntl(Search));
-module.exports._unconnected = Search;
-
-/***/ }),
-/* 20 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-/* 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__(0);
-
-var _require = __webpack_require__(3);
-
-const connect = _require.connect;
-
-var _require2 = __webpack_require__(2);
-
-const injectIntl = _require2.injectIntl,
-      FormattedMessage = _require2.FormattedMessage;
-
-const Card = __webpack_require__(13);
-const Topics = __webpack_require__(22);
-
-var _require3 = __webpack_require__(1);
-
-const ac = _require3.actionCreators;
-
-
-const VISIBLE = "visible";
-const VISIBILITY_CHANGE_EVENT = "visibilitychange";
-
-class Section extends React.Component {
-  constructor(props) {
-    super(props);
-    this.onInfoEnter = this.onInfoEnter.bind(this);
-    this.onInfoLeave = this.onInfoLeave.bind(this);
-    this.state = { infoActive: false };
-  }
-
-  /**
-   * Take a truthy value to conditionally change the infoActive state.
-   */
-  _setInfoState(nextActive) {
-    const infoActive = !!nextActive;
-    if (infoActive !== this.state.infoActive) {
-      this.setState({ infoActive });
-    }
-  }
-
-  onInfoEnter() {
-    // We're getting focus or hover, so info state should be true if not yet.
-    this._setInfoState(true);
-  }
-
-  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));
-  }
-
-  getFormattedMessage(message) {
-    return typeof message === "string" ? React.createElement(
-      "span",
-      null,
-      message
-    ) : React.createElement(FormattedMessage, message);
-  }
-
-  _dispatchImpressionStats() {
-    const props = this.props;
-
-    const maxCards = 3 * props.maxRows;
-    props.dispatch(ac.ImpressionStats({
-      source: props.eventSource,
-      tiles: props.rows.slice(0, maxCards).map(link => ({ id: link.guid }))
-    }));
-  }
-
-  // This sends an event when a user sees a set of new content. If content
-  // changes while the page is hidden (i.e. preloaded or on a hidden tab),
-  // only send the event if the page becomes visible again.
-  sendImpressionStatsOrAddListener() {
-    const props = this.props;
-
-
-    if (!props.dispatch) {
-      return;
-    }
-
-    if (props.document.visibilityState === VISIBLE) {
-      this._dispatchImpressionStats();
-    } else {
-      // We should only ever send the latest impression stats ping, so remove any
-      // older listeners.
-      if (this._onVisibilityChange) {
-        props.document.removeEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange);
-      }
-
-      // When the page becoems visible, send the impression stats ping.
-      this._onVisibilityChange = () => {
-        if (props.document.visibilityState === VISIBLE) {
-          this._dispatchImpressionStats();
-          props.document.removeEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange);
-        }
-      };
-      props.document.addEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange);
-    }
-  }
-
-  componentDidMount() {
-    if (this.props.rows.length) {
-      this.sendImpressionStatsOrAddListener();
-    }
-  }
-
-  componentDidUpdate(prevProps) {
-    const props = this.props;
-
-    if (
-    // Don't send impression stats for the empty state
-    props.rows.length &&
-    // We only want to send impression stats if the content of the cards has changed
-    props.rows !== prevProps.rows) {
-      this.sendImpressionStatsOrAddListener();
-    }
-  }
-
-  render() {
-    var _props = this.props;
-    const id = _props.id,
-          eventSource = _props.eventSource,
-          title = _props.title,
-          icon = _props.icon,
-          rows = _props.rows,
-          infoOption = _props.infoOption,
-          emptyState = _props.emptyState,
-          dispatch = _props.dispatch,
-          maxRows = _props.maxRows,
-          contextMenuOptions = _props.contextMenuOptions,
-          intl = _props.intl;
-
-    const maxCards = 3 * maxRows;
-    const initialized = rows && rows.length > 0;
-    const shouldShowTopics = id === "TopStories" && this.props.topics && this.props.topics.length > 0 && this.props.read_more_endpoint;
-
-    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" });
-
-    // <Section> <-- React component
-    // <section> <-- HTML5 element
-    return React.createElement(
-      "section",
-      null,
-      React.createElement(
-        "div",
-        { className: "section-top-bar" },
-        React.createElement(
-          "h3",
-          { className: "section-title" },
-          icon && icon.startsWith("moz-extension://") ? React.createElement("span", { className: "icon icon-small-spacer", style: { "background-image": `url('${icon}')` } }) : React.createElement("span", { className: `icon icon-small-spacer icon-${icon || "webextension"}` }),
-          this.getFormattedMessage(title)
-        ),
-        infoOption && React.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
-          }, infoOptionIconA11yAttrs)),
-          React.createElement(
-            "div",
-            { className: "info-option" },
-            infoOption.header && React.createElement(
-              "div",
-              { className: "info-option-header", role: "heading" },
-              this.getFormattedMessage(infoOption.header)
-            ),
-            infoOption.body && React.createElement(
-              "p",
-              { className: "info-option-body" },
-              this.getFormattedMessage(infoOption.body)
-            ),
-            infoOption.link && React.createElement(
-              "a",
-              { href: infoOption.link.href, target: "_blank", rel: "noopener noreferrer", className: "info-option-link" },
-              this.getFormattedMessage(infoOption.link.title || infoOption.link)
-            )
-          )
-        )
-      ),
-      React.createElement(
-        "ul",
-        { className: "section-list", style: { padding: 0 } },
-        rows.slice(0, maxCards).map((link, index) => link && React.createElement(Card, { index: index, dispatch: dispatch, link: link, contextMenuOptions: contextMenuOptions, eventSource: eventSource }))
-      ),
-      !initialized && React.createElement(
-        "div",
-        { className: "section-empty-state" },
-        React.createElement(
-          "div",
-          { className: "empty-state" },
-          React.createElement("img", { className: `empty-state-icon icon icon-${emptyState.icon}` }),
-          React.createElement(
-            "p",
-            { className: "empty-state-message" },
-            this.getFormattedMessage(emptyState.message)
-          )
-        )
-      ),
-      shouldShowTopics && React.createElement(Topics, { topics: this.props.topics, read_more_endpoint: this.props.read_more_endpoint })
-    );
-  }
-}
-
-Section.defaultProps = { document: global.document };
-
-const SectionIntl = injectIntl(Section);
-
-class Sections extends React.Component {
-  render() {
-    const sections = this.props.Sections;
-    return React.createElement(
-      "div",
-      { className: "sections-list" },
-      sections.map(section => React.createElement(SectionIntl, _extends({ key: section.id }, section, { dispatch: this.props.dispatch })))
-    );
-  }
-}
-
-module.exports = connect(state => ({ Sections: state.Sections }))(Sections);
-module.exports._unconnected = Sections;
-module.exports.SectionIntl = SectionIntl;
-module.exports._unconnectedSection = Section;
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(6)))
-
-/***/ }),
-/* 21 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-const React = __webpack_require__(0);
-
-var _require = __webpack_require__(3);
-
-const connect = _require.connect;
-
-var _require2 = __webpack_require__(2);
-
-const FormattedMessage = _require2.FormattedMessage,
-      injectIntl = _require2.injectIntl;
-
-const LinkMenu = __webpack_require__(4);
-
-var _require3 = __webpack_require__(1);
-
-const ac = _require3.actionCreators,
-      at = _require3.actionTypes;
-
-var _require4 = __webpack_require__(5);
-
-const perfSvc = _require4.perfService;
-
-const TOP_SITES_SOURCE = "TOP_SITES";
-const TOP_SITES_CONTEXT_MENU_OPTIONS = ["CheckPinTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl", "DeleteUrl"];
-
-class TopSite extends React.Component {
-  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);
-  }
-  toggleContextMenu(event, index) {
-    this.setState({
-      activeTile: index,
-      showContextMenu: true
-    });
-  }
-  userEvent(event) {
-    this.props.dispatch(ac.UserEvent({
-      event,
-      source: TOP_SITES_SOURCE,
-      action_position: this.props.index
-    }));
-  }
-  onLinkClick(ev) {
-    if (this.props.editMode) {
-      // 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.link;
-
-    if (link.isPinned) {
-      this.props.dispatch(ac.SendToMain({
-        type: at.TOP_SITES_UNPIN,
-        data: { site: { url: link.url } }
-      }));
-    }
-    this.props.dispatch(ac.SendToMain({
-      type: at.BLOCK_URL,
-      data: link.url
-    }));
-    this.userEvent("BLOCK");
-  }
-  onPinButtonClick() {
-    var _props = this.props;
-    const link = _props.link,
-          index = _props.index;
-
-    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");
-    }
-  }
-  render() {
-    var _props2 = this.props;
-    const link = _props2.link,
-          index = _props2.index,
-          dispatch = _props2.dispatch,
-          editMode = _props2.editMode;
-
-    const isContextMenuOpen = this.state.showContextMenu && this.state.activeTile === index;
-    const title = link.hostname;
-    const topSiteOuterClassName = `top-site-outer${isContextMenuOpen ? " active" : ""}`;
-    const tippyTopIcon = link.tippyTopIcon;
-
-    let imageClassName;
-    let imageStyle;
-    if (tippyTopIcon) {
-      imageClassName = "tippy-top-icon";
-      imageStyle = {
-        backgroundColor: link.backgroundColor,
-        backgroundImage: `url(${tippyTopIcon})`
-      };
-    } else {
-      imageClassName = `screenshot${link.screenshot ? " active" : ""}`;
-      imageStyle = { backgroundImage: link.screenshot ? `url(${link.screenshot})` : "none" };
-    }
-    return React.createElement(
-      "li",
-      { className: topSiteOuterClassName, key: link.guid || link.url },
-      React.createElement(
-        "a",
-        { href: link.url, onClick: this.onLinkClick },
-        React.createElement(
-          "div",
-          { className: "tile", "aria-hidden": true },
-          React.createElement(
-            "span",
-            { className: "letter-fallback" },
-            title[0]
-          ),
-          React.createElement("div", { className: imageClassName, style: imageStyle })
-        ),
-        React.createElement(
-          "div",
-          { className: `title ${link.isPinned ? "pinned" : ""}` },
-          link.isPinned && React.createElement("div", { className: "icon icon-pin-small" }),
-          React.createElement(
-            "span",
-            { dir: "auto" },
-            title
-          )
-        )
-      ),
-      !editMode && React.createElement(
-        "div",
-        null,
-        React.createElement(
-          "button",
-          { className: "context-menu-button", onClick: this.onMenuButtonClick },
-          React.createElement(
-            "span",
-            { className: "sr-only" },
-            `Open context menu for ${title}`
-          )
-        ),
-        React.createElement(LinkMenu, {
-          dispatch: dispatch,
-          index: index,
-          onUpdate: this.onMenuUpdate,
-          options: TOP_SITES_CONTEXT_MENU_OPTIONS,
-          site: link,
-          source: TOP_SITES_SOURCE,
-          visible: isContextMenuOpen })
-      ),
-      editMode && 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-dismiss",
-          title: this.props.intl.formatMessage({ id: "edit_topsites_dismiss_button" }),
-          onClick: this.onDismissButtonClick })
-      )
-    );
-  }
-}
-
-TopSite.defaultProps = { editMode: false };
-
-/**
- * A proxy class that uses double requestAnimationFrame from
- * componentDidMount to dispatch a SAVE_SESSION_PERF_DATA to the main procsess
- * after the paint.
- *
- * This uses two callbacks because, after one callback, this part of the tree
- * may have rendered but not yet reflowed.  This strategy is modeled after
- * https://stackoverflow.com/a/34999925 but uses a double rFA because
- * we want to get to the closest reliable paint for measuring, and
- * setTimeout is often throttled or queued by browsers in ways that could
- * make it lag too long.
- *
- * XXX Should be made more generic by using this.props.children, or potentially
- * even split out into a higher-order component to wrap whatever.
- *
- * @class TopSitesPerfTimer
- * @extends {React.Component}
- */
-class TopSitesPerfTimer extends React.Component {
-  constructor(props) {
-    super(props);
-    // Just for test dependency injection:
-    this.perfSvc = this.props.perfSvc || perfSvc;
-
-    this._sendPaintedEvent = this._sendPaintedEvent.bind(this);
-    this._timestampHandled = false;
-  }
-
-  componentDidMount() {
-    this._maybeSendPaintedEvent();
-  }
-
-  componentDidUpdate() {
-    this._maybeSendPaintedEvent();
-  }
-
-  /**
-   * Call the given callback after the upcoming frame paints.
-   *
-   * @note Both setTimeout and requestAnimationFrame are throttled when the page
-   * is hidden, so this callback may get called up to a second or so after the
-   * requestAnimationFrame "paint" for hidden tabs.
-   *
-   * Newtabs hidden while loading will presumably be fairly rare (other than
-   * preloaded tabs, which we will be filtering out on the server side), so such
-   * cases should get lost in the noise.
-   *
-   * If we decide that it's important to find out when something that's hidden
-   * has "painted", however, another option is to post a message to this window.
-   * That should happen even faster than setTimeout, and, at least as of this
-   * writing, it's not throttled in hidden windows in Firefox.
-   *
-   * @param {Function} callback
-   *
-   * @returns void
-   */
-  _afterFramePaint(callback) {
-    requestAnimationFrame(() => setTimeout(callback, 0));
-  }
-
-  _maybeSendPaintedEvent() {
-    // We don't want this to ever happen, but sometimes it does.  And when it
-    // does (typically on the first newtab at startup time calling
-    // componentDidMount), the paint(s) we care about will be later (eg
-    // in a subsequent componentDidUpdate).
-    if (!this.props.TopSites.initialized) {
-      // XXX should send bad event
-      return;
-    }
-
-    // If we've already handled a timestamp, don't do it again
-    if (this._timestampHandled) {
-      return;
-    }
-
-    // And if we haven't, we're doing so now, so remember that. Even if
-    // something goes wrong in the callback, we can't try again, as we'd be
-    // sending back the wrong data, and we have to do it here, so that other
-    // calls to this method while waiting for the next frame won't also try to
-    // handle handle it.
-    this._timestampHandled = true;
-
-    this._afterFramePaint(this._sendPaintedEvent);
-  }
-
-  _sendPaintedEvent() {
-    this.perfSvc.mark("topsites_first_painted_ts");
-
-    try {
-      let topsites_first_painted_ts = this.perfSvc.getMostRecentAbsMarkStartByName("topsites_first_painted_ts");
-
-      this.props.dispatch(ac.SendToMain({
-        type: at.SAVE_SESSION_PERF_DATA,
-        data: { topsites_first_painted_ts }
-      }));
-    } 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.
-    }
-  }
-
-  render() {
-    return React.createElement(TopSites, this.props);
-  }
-}
-
-const TopSites = props => React.createElement(
-  "section",
-  { className: "top-sites" },
-  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(
-    "ul",
-    { className: "top-sites-list" },
-    props.TopSites.rows.map((link, index) => link && React.createElement(TopSite, {
-      key: link.guid || link.url,
-      dispatch: props.dispatch,
-      link: link,
-      index: index,
-      intl: props.intl }))
-  ),
-  React.createElement(TopSitesEditIntl, props)
-);
-
-class TopSitesEdit extends React.Component {
-  constructor(props) {
-    super(props);
-    this.state = { showEditModal: false };
-    this.onEditButtonClick = this.onEditButtonClick.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
-    }));
-  }
-  render() {
-    return React.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 && React.createElement(
-        "div",
-        { className: "edit-topsites" },
-        React.createElement("div", { className: "modal-overlay" }),
-        React.createElement(
-          "div",
-          { className: "modal" },
-          React.createElement(
-            "section",
-            { className: "edit-topsites-inner-wrapper" },
-            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(
-              "ul",
-              { className: "top-sites-list" },
-              this.props.TopSites.rows.map((link, index) => link && React.createElement(TopSite, {
-                key: link.guid || link.url,
-                dispatch: this.props.dispatch,
-                link: link,
-                index: index,
-                intl: this.props.intl,
-                editMode: true }))
-            )
-          ),
-          React.createElement(
-            "section",
-            { className: "actions" },
-            React.createElement(
-              "button",
-              { className: "done", onClick: this.onEditButtonClick },
-              React.createElement(FormattedMessage, { id: "edit_topsites_done_button" })
-            )
-          )
-        )
-      )
-    );
-  }
-}
-
-const TopSitesEditIntl = injectIntl(TopSitesEdit);
-
-module.exports = connect(state => ({ TopSites: state.TopSites }))(TopSitesPerfTimer);
-module.exports._unconnected = TopSitesPerfTimer;
-module.exports.TopSite = TopSite;
-module.exports.TopSites = TopSites;
-module.exports.TopSitesEdit = TopSitesEdit;
-
-/***/ }),
-/* 22 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-const React = __webpack_require__(0);
-
-var _require = __webpack_require__(2);
-
-const FormattedMessage = _require.FormattedMessage;
-
-
-class Topic extends React.Component {
-  render() {
-    var _props = this.props;
-    const url = _props.url,
-          name = _props.name;
-
-    return React.createElement(
-      "li",
-      null,
-      React.createElement(
-        "a",
-        { key: name, className: "topic-link", href: url },
-        name
-      )
-    );
-  }
-}
-
-class Topics extends React.Component {
-  render() {
-    var _props2 = this.props;
-    const topics = _props2.topics,
-          read_more_endpoint = _props2.read_more_endpoint;
-
-    return React.createElement(
-      "div",
-      { className: "topic" },
-      React.createElement(
-        "span",
-        null,
-        React.createElement(FormattedMessage, { id: "pocket_read_more" })
-      ),
-      React.createElement(
-        "ul",
-        null,
-        topics.map(t => React.createElement(Topic, { key: t.name, url: t.url, name: t.name }))
-      ),
-      React.createElement(
-        "a",
-        { className: "topic-read-more", href: read_more_endpoint },
-        React.createElement(FormattedMessage, { id: "pocket_read_even_more" }),
-        React.createElement("span", { className: "topic-read-more-logo" })
-      )
-    );
-  }
-}
-
-module.exports = Topics;
-module.exports._unconnected = Topics;
-module.exports.Topic = Topic;
-
-/***/ }),
-/* 23 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-var _require = __webpack_require__(1);
-
-const at = _require.actionTypes,
-      ac = _require.actionCreators;
-
-/**
- * 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.
- */
-
-module.exports = {
-  Separator: () => ({ type: "separator" }),
-  RemoveBookmark: site => ({
-    id: "menu_action_remove_bookmark",
-    icon: "bookmark-remove",
-    action: ac.SendToMain({
-      type: at.DELETE_BOOKMARK_BY_ID,
-      data: site.bookmarkGuid
-    }),
-    userEvent: "BOOKMARK_DELETE"
-  }),
-  AddBookmark: site => ({
-    id: "menu_action_bookmark",
-    icon: "bookmark",
-    action: ac.SendToMain({
-      type: at.BOOKMARK_URL,
-      data: { url: site.url, title: site.title }
-    }),
-    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,
-      incognito: true,
-      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: site.url }), ac.UserEvent({ event: "DELETE" })],
-        body_string_id: ["confirm_history_delete_p1", "confirm_history_delete_notice_p2"],
-        confirm_button_string_id: "menu_action_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,
-      incognito: true,
-      tiles: [{ id: site.guid, pos: index }]
-    }),
-    userEvent: "SAVE_TO_POCKET"
-  })
-};
-
-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);
-
-/***/ }),
-/* 24 */
-/***/ (function(module, exports) {
-
-module.exports = Redux;
-
-/***/ }),
-/* 25 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-const React = __webpack_require__(0);
-const ReactDOM = __webpack_require__(12);
-const Base = __webpack_require__(7);
-
-var _require = __webpack_require__(3);
-
-const Provider = _require.Provider;
-
-const initStore = __webpack_require__(9);
-
-var _require2 = __webpack_require__(11);
-
-const reducers = _require2.reducers;
-
-const DetectUserSessionStart = __webpack_require__(8);
-
-var _require3 = __webpack_require__(10);
-
-const addSnippetsSubscriber = _require3.addSnippetsSubscriber;
-
-
-new DetectUserSessionStart().sendEventOrAddListener();
-
-const store = initStore(reducers);
-
-ReactDOM.render(React.createElement(
-  Provider,
-  { store: store },
-  React.createElement(Base, null)
-), document.getElementById("root"));
-
-addSnippetsSubscriber(store);
-
 /***/ })
 /******/ ]);
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/content/activity-stream.css
+++ b/browser/extensions/activity-stream/data/content/activity-stream.css
@@ -23,49 +23,51 @@ input {
 
 .icon {
   display: inline-block;
   width: 16px;
   height: 16px;
   background-size: 16px;
   background-position: center center;
   background-repeat: no-repeat;
-  vertical-align: middle; }
+  vertical-align: middle;
+  fill: rgba(12, 12, 13, 0.8);
+  -moz-context-properties: fill; }
   .icon.icon-spacer {
     margin-inline-end: 8px; }
   .icon.icon-small-spacer {
     margin-inline-end: 6px; }
-  .icon.icon-bookmark {
-    background-image: url("assets/glyph-bookmark-16.svg"); }
-  .icon.icon-bookmark-remove {
-    background-image: url("assets/glyph-bookmark-remove-16.svg"); }
+  .icon.icon-bookmark-added {
+    background-image: url("chrome://browser/skin/bookmark.svg"); }
+  .icon.icon-bookmark-hollow {
+    background-image: url("chrome://browser/skin/bookmark-hollow.svg"); }
   .icon.icon-delete {
     background-image: url("assets/glyph-delete-16.svg"); }
   .icon.icon-dismiss {
     background-image: url("assets/glyph-dismiss-16.svg"); }
   .icon.icon-info {
     background-image: url("assets/glyph-info-16.svg"); }
   .icon.icon-new-window {
     background-image: url("assets/glyph-newWindow-16.svg"); }
   .icon.icon-new-window-private {
-    background-image: url("assets/glyph-newWindow-private-16.svg"); }
+    background-image: url("chrome://browser/skin/privateBrowsing.svg"); }
   .icon.icon-settings {
     background-image: url("assets/glyph-settings-16.svg"); }
   .icon.icon-pin {
     background-image: url("assets/glyph-pin-16.svg"); }
   .icon.icon-unpin {
     background-image: url("assets/glyph-unpin-16.svg"); }
   .icon.icon-pocket {
     background-image: url("assets/glyph-pocket-16.svg"); }
   .icon.icon-historyItem {
     background-image: url("assets/glyph-historyItem-16.svg"); }
   .icon.icon-trending {
     background-image: url("assets/glyph-trending-16.svg"); }
   .icon.icon-now {
-    background-image: url("assets/glyph-now-16.svg"); }
+    background-image: url("chrome://browser/skin/history.svg"); }
   .icon.icon-topsites {
     background-image: url("assets/glyph-topsites-16.svg"); }
   .icon.icon-pin-small {
     background-image: url("assets/glyph-pin-12.svg");
     background-size: 12px;
     height: 12px;
     width: 12px; }
   .icon.icon-check {
@@ -74,43 +76,43 @@ input {
     background-image: url("assets/glyph-webextension-16.svg"); }
 
 html,
 body,
 #root {
   height: 100%; }
 
 body {
-  background: #F6F6F8;
+  background: #EDEDF0;
   color: #0C0C0D;
   font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Ubuntu', 'Helvetica Neue', sans-serif;
   font-size: 16px; }
 
 h1,
 h2 {
   font-weight: normal; }
 
 a {
-  color: #00AFF7;
+  color: #008EA4;
   text-decoration: none; }
   a:hover {
-    color: #2bc1ff; }
+    color: #00C8D7; }
 
 .sr-only {
   position: absolute;
   width: 1px;
   height: 1px;
   padding: 0;
   margin: -1px;
   overflow: hidden;
   clip: rect(0, 0, 0, 0);
   border: 0; }
 
 .inner-border {
-  border: 1px solid rgba(0, 0, 0, 0.1);
+  border: 1px solid #D7D7DB;
   border-radius: 3px;
   position: absolute;
   top: 0;
   left: 0;
   width: 100%;
   height: 100%;
   pointer-events: none;
   z-index: 100; }
@@ -124,62 +126,62 @@ a {
 .show-on-init {
   opacity: 0;
   transition: opacity 0.2s ease-in; }
   .show-on-init.on {
     opacity: 1;
     animation: fadeIn 0.2s; }
 
 .actions {
-  border-top: solid 1px rgba(0, 0, 0, 0.1);
+  border-top: 1px solid #D7D7DB;
   display: flex;
   flex-direction: row;
   margin: 0;
   padding: 15px 25px;
   justify-content: flex-start; }
   .actions button {
-    background: #FBFBFB;
-    border: solid 1px #BFBFBF;
+    background: #F9F9FA;
+    border: 1px solid #B1B1B3;
     border-radius: 5px;
     color: #0C0C0D;
     cursor: pointer;
     padding: 10px 30px; }
     .actions button:hover {
-      box-shadow: 0 0 0 5px rgba(0, 0, 0, 0.1);
+      box-shadow: 0 0 0 5px #D7D7DB;
       transition: box-shadow 150ms; }
     .actions button.done {
-      background: #0695F9;
-      border: solid 1px #1677CF;
+      background: #0A84FF;
+      border: solid 1px #0060DF;
       color: #FFF;
       margin-inline-start: auto; }
 
 .outer-wrapper {
   display: flex;
   flex-grow: 1;
-  padding: 62px 32px 32px;
+  padding: 40px 32px 32px;
   height: 100%; }
 
 main {
   margin: auto;
   width: 224px;
   padding-bottom: 120px; }
   @media (min-width: 416px) {
     main {
       width: 352px; } }
   @media (min-width: 544px) {
     main {
       width: 480px; } }
   @media (min-width: 800px) {
     main {
       width: 736px; } }
   main section {
-    margin-bottom: 32px; }
+    margin-bottom: 40px;
+    position: relative; }
 
 .section-title {
-  color: #6E707E;
   font-size: 13px;
   font-weight: bold;
   text-transform: uppercase; }
   .section-title span {
     vertical-align: middle; }
 
 .top-sites-list {
   list-style: none;
@@ -234,54 +236,54 @@ main {
     margin-inline-end: 32px; }
   .top-sites-list .top-site-outer {
     position: relative; }
     .top-sites-list .top-site-outer > a {
       display: block;
       color: inherit;
       outline: none; }
       .top-sites-list .top-site-outer > a.active .tile, .top-sites-list .top-site-outer > a:focus .tile {
-        box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px rgba(0, 0, 0, 0.1);
+        box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px #D7D7DB;
         transition: box-shadow 150ms; }
     .top-sites-list .top-site-outer .context-menu-button {
       cursor: pointer;
       position: absolute;
       top: -13.5px;
       offset-inline-end: -13.5px;
       width: 27px;
       height: 27px;
       background-color: #FFF;
-      background-image: url("assets/glyph-more-16.svg");
-      background-position: 65%;
-      background-repeat: no-repeat;
+      background-image: url("chrome://browser/skin/page-action.svg");
+      background-position: 55%;
       background-clip: padding-box;
-      border: 1px solid rgba(0, 0, 0, 0.2);
+      border: 1px solid #B1B1B3;
       border-radius: 100%;
-      box-shadow: 0 2px 0 rgba(0, 0, 0, 0.1);
+      box-shadow: 0 2px rgba(12, 12, 13, 0.1);
+      fill: rgba(12, 12, 13, 0.8);
       transform: scale(0.25);
       opacity: 0;
       transition-property: transform, opacity;
       transition-duration: 200ms;
       z-index: 399; }
       .top-sites-list .top-site-outer .context-menu-button:focus, .top-sites-list .top-site-outer .context-menu-button:active {
         transform: scale(1);
         opacity: 1; }
     .top-sites-list .top-site-outer:hover .tile, .top-sites-list .top-site-outer:focus .tile, .top-sites-list .top-site-outer.active .tile {
-      box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px rgba(0, 0, 0, 0.1);
+      box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px #D7D7DB;
       transition: box-shadow 150ms; }
     .top-sites-list .top-site-outer:hover .context-menu-button, .top-sites-list .top-site-outer:focus .context-menu-button, .top-sites-list .top-site-outer.active .context-menu-button {
       transform: scale(1);
       opacity: 1; }
     .top-sites-list .top-site-outer .tile {
       position: relative;
       height: 96px;
       width: 96px;
       border-radius: 6px;
-      box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
-      color: #A0A0A0;
+      box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 1px 4px 0 rgba(12, 12, 13, 0.1);
+      color: #737373;
       font-weight: 200;
       font-size: 32px;
       text-transform: uppercase;
       display: flex;
       align-items: center;
       justify-content: center; }
     .top-sites-list .top-site-outer .screenshot {
       position: absolute;
@@ -306,149 +308,146 @@ main {
       width: 100%;
       border-radius: 6px;
       box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
       background-position: center center;
       background-size: 96px;
       background-repeat: no-repeat; }
     .top-sites-list .top-site-outer .title {
       font: message-box;
-      height: 30px;
+      height: 20px;
       line-height: 30px;
       text-align: center;
       width: 96px;
       position: relative; }
       .top-sites-list .top-site-outer .title .icon {
+        fill: rgba(12, 12, 13, 0.6);
         offset-inline-start: 0;
         position: absolute;
         top: 10px; }
       .top-sites-list .top-site-outer .title span {
         display: block;
         overflow: hidden;
         text-overflow: ellipsis;
         white-space: nowrap; }
       .top-sites-list .top-site-outer .title.pinned span {
         padding: 0 13px; }
     .top-sites-list .top-site-outer .edit-menu {
       background: #FFF;
-      border: 1px solid rgba(0, 0, 0, 0.2);
+      border: 1px solid #B1B1B3;
       border-radius: 12.5px;
-      box-shadow: 0 2px 0 rgba(0, 0, 0, 0.1);
+      box-shadow: 0 2px rgba(12, 12, 13, 0.1);
       height: 25px;
       position: absolute;
       offset-inline-end: -12.5px;
       opacity: 0;
       overflow: hidden;
       top: -12.5px;
       transform: scale(0.25);
       transition-property: transform, opacity;
       transition-duration: 200ms;
       z-index: 1000; }
       .top-sites-list .top-site-outer .edit-menu:focus, .top-sites-list .top-site-outer .edit-menu:active {
         transform: scale(1);
         opacity: 1; }
       .top-sites-list .top-site-outer .edit-menu button {
         border: 0;
-        border-right: 1px solid rgba(0, 0, 0, 0.2);
+        border-right: 1px solid #B1B1B3;
         background-color: #FFF;
         cursor: pointer;
         height: 100%;
         width: 25px; }
         .top-sites-list .top-site-outer .edit-menu button:hover {
-          background-color: #FBFBFB; }
+          background-color: #F9F9FA; }
         .top-sites-list .top-site-outer .edit-menu button:last-child:dir(ltr) {
           border-right: 0; }
         .top-sites-list .top-site-outer .edit-menu button:first-child:dir(rtl) {
           border-right: 0; }
     .top-sites-list .top-site-outer:hover .edit-menu, .top-sites-list .top-site-outer:focus .edit-menu, .top-sites-list .top-site-outer.active .edit-menu {
       transform: scale(1);
       opacity: 1; }
 
-.top-sites {
-  position: relative; }
-
 .edit-topsites-wrapper .edit-topsites-button {
   position: absolute;
   offset-inline-end: 0;
   top: -2px; }
   .edit-topsites-wrapper .edit-topsites-button button {
     background: none;
     border: 0;
-    color: #A0A0A0;
+    color: #737373;
     cursor: pointer;
     font-size: 12px;
     padding: 0; }
     .edit-topsites-wrapper .edit-topsites-button button:focus {
-      background: #EBEBEB;
-      border-bottom: dotted 1px #A0A0A0; }
+      background: #F9F9FA;
+      border-bottom: dotted 1px #737373; }
 
 .edit-topsites-wrapper .modal {
   offset-inline-start: -31px;
   position: absolute;
   top: -29px;
   width: calc(100% + 62px); }
 
 .edit-topsites-wrapper .edit-topsites-inner-wrapper {
   margin: 0;
   padding: 15px 30px; }
 
 .sections-list .section-top-bar {
   position: relative;
   height: 16px;
   margin-bottom: 18px; }
-  .sections-list .section-top-bar .section-title {
-    float: left; }
   .sections-list .section-top-bar .section-info-option {
-    float: right;
-    margin-top: 14px; }
+    offset-inline-end: 0;
+    position: absolute;
+    top: 0; }
   .sections-list .section-top-bar .info-option-icon {
     background-image: url("assets/glyph-info-option-12.svg");
     background-size: 12px 12px;
     background-repeat: no-repeat;
     background-position: center;
+    fill: rgba(12, 12, 13, 0.6);
+    -moz-context-properties: fill;
     height: 16px;
     width: 16px;
     display: inline-block; }
   .sections-list .section-top-bar .section-info-option .info-option {
     visibility: hidden;
     opacity: 0;
     transition: visibility 0.2s, opacity 0.2s ease-out;
     transition-delay: 0.5s; }
   .sections-list .section-top-bar .info-option-icon[aria-expanded="true"] + .info-option {
     visibility: visible;
     opacity: 1;
     transition: visibility 0.2s, opacity 0.2s ease-out; }
   .sections-list .section-top-bar .info-option {
     z-index: 9999;
     position: absolute;
     background: #FFF;
-    border: solid 1px rgba(0, 0, 0, 0.1);
+    border: 1px solid #D7D7DB;
     border-radius: 3px;
     font-size: 13px;
-    color: #0C0C0D;
     line-height: 120%;
     width: 320px;
     right: 0;
     top: 23px;
     margin-right: -1px;
     padding: 24px;
     -moz-user-select: none; }
   .sections-list .section-top-bar .info-option-header {
     font-size: 15px;
     font-weight: 600; }
   .sections-list .section-top-bar .info-option-body {
     margin: 0;
     margin-top: 12px; }
   .sections-list .section-top-bar .info-option-link {
     display: block;
     margin-top: 12px;
-    color: #0A84FF; }
+    color: #008EA4; }
 
 .sections-list .section-list {
-  clear: both;
   margin: 0;
   display: grid;
   grid-template-columns: repeat(auto-fit, 224px);
   grid-gap: 32px; }
   @media (max-width: 544px) {
     .sections-list .section-list .context-menu {
       margin-inline-start: auto;
       margin-inline-end: 5px;
@@ -466,42 +465,42 @@ main {
       margin-inline-end: 5px;
       offset-inline-start: auto;
       offset-inline-end: 0; } }
 
 .sections-list .section-empty-state {
   width: 100%;
   height: 266px;
   display: flex;
-  border: solid 1px rgba(0, 0, 0, 0.1);
+  border: 1px solid #D7D7DB;
   border-radius: 3px;
   margin-bottom: 16px; }
   .sections-list .section-empty-state .empty-state {
     margin: auto;
     max-width: 350px; }
     .sections-list .section-empty-state .empty-state .empty-state-icon {
       background-size: 50px 50px;
       background-repeat: no-repeat;
       background-position: center;
-      fill: rgba(160, 160, 160, 0.4);
+      fill: rgba(12, 12, 13, 0.6);
       -moz-context-properties: fill;
       height: 50px;
       width: 50px;
       margin: 0 auto;
       display: block; }
     .sections-list .section-empty-state .empty-state .empty-state-message {
       margin-bottom: 0;
       font-size: 13px;
       font-weight: 300;
-      color: #A0A0A0;
+      color: #737373;
       text-align: center; }
 
 .topic {
   font-size: 12px;
-  color: #BFC0C7;
+  color: #737373;
   margin-top: 12px;
   line-height: 1.6; }
   @media (min-width: 800px) {
     .topic {
       line-height: 16px;
       min-width: 780px; } }
   .topic ul {
     margin: 0;
@@ -529,95 +528,89 @@ main {
       .topic .topic-read-more {
         margin-right: 40px;
         float: right; } }
   .topic .topic-read-more-logo {
     padding-right: 10px;
     margin-left: 5px;
     background-image: url("assets/topic-show-more-12.svg");
     background-repeat: no-repeat;
+    fill: #008EA4;
+    -moz-context-properties: fill;
     vertical-align: middle; }
 
 .search-wrapper {
   cursor: default;
   display: flex;
   position: relative;
   margin: 0 0 40px;
   width: 100%;
   height: 36px; }
   .search-wrapper input {
     border: 0;
-    box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.1);
-    flex-grow: 1;
-    margin: 0;
-    outline: none;
-    padding: 0 12px 0 35px;
-    height: 100%;
-    border-radius: 4px 0 0 4px;
-    padding-inline-start: 35px; }
+    box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
+    border-radius: 4px;
+    padding: 0;
+    padding-inline-end: 36px;
+    padding-inline-start: 35px;
+    width: 100%; }
     .search-wrapper input:focus {
-      border-color: #0996F8;
-      box-shadow: 0 0 0 2px #0996F8;
+      border-color: #0A84FF;
+      box-shadow: 0 0 0 2px #0A84FF;
       z-index: 1; }
     .search-wrapper input:focus + .search-button {
       z-index: 1;
-      box-shadow: 0 0 0 2px #0996F8;
-      background-color: #0996F8;
-      background-image: url("assets/glyph-forward-16-white.svg");
-      color: #FFF; }
+      background-color: #0A84FF;
+      background-image: url("chrome://browser/skin/forward.svg");
+      fill: #FFF;
+      -moz-context-properties: fill; }
     .search-wrapper input[aria-expanded="true"] {
-      border-radius: 4px 0 0 0; }
-    .search-wrapper input:dir(rtl) {
-      border-radius: 0 4px 4px 0; }
-      .search-wrapper input:dir(rtl)[aria-expanded="true"] {
-        border-radius: 0 4px 0 0; }
+      border-radius: 4px 4px 0 0; }
   .search-wrapper .search-label {
     background: url("assets/glyph-search-16.svg") no-repeat center center/20px;
+    fill: rgba(12, 12, 13, 0.6);
+    -moz-context-properties: fill;
     position: absolute;
-    top: 0;
     offset-inline-start: 0;
     height: 100%;
     width: 35px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
     z-index: 2; }
   .search-wrapper .search-button {
+    background: url("chrome://browser/skin/forward.svg") no-repeat center center;
     border-radius: 0 3px 3px 0;
-    margin-inline-start: -1px;
     border: 0;
     width: 36px;
-    padding: 0;
-    box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.1);
-    background: #FFF url("assets/glyph-forward-16.svg") no-repeat center center;
-    background-size: 16px 16px; }
+    fill: rgba(12, 12, 13, 0.6);
+    -moz-context-properties: fill;
+    background-size: 16px 16px;
+    height: 100%;
+    offset-inline-end: 0;
+    position: absolute; }
     .search-wrapper .search-button:hover {
       z-index: 1;
-      box-shadow: 0 1px 0 0 rgba(0, 0, 1, 0.5);
-      background-color: #0996F8;
-      background-image: url("assets/glyph-forward-16-white.svg");
-      color: #FFF;
+      background-color: #0A84FF;
+      fill: #FFF;
       cursor: pointer; }
     .search-wrapper .search-button:dir(rtl) {
       transform: scaleX(-1); }
   .search-wrapper .contentSearchSuggestionTable {
-    transform: translate(-2px, 2px); }
-    .search-wrapper .contentSearchSuggestionTable:dir(rtl) {
-      transform: translate(2px, 2px); }
+    border: 0;
+    box-shadow: 0 0 0 2px #0A84FF;
+    transform: translateY(2px); }
 
 .context-menu {
   display: block;
   position: absolute;
   font-size: 14px;
   box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 0, 0, 0.2);
   top: 6.75px;
   offset-inline-start: 100%;
   margin-inline-start: 5px;
   z-index: 10000;
-  background: #FBFBFB;
+  background: #F9F9FA;
   border-radius: 5px; }
   .context-menu > ul {
     margin: 0;
     padding: 5px 0;
     list-style: none; }
     .context-menu > ul > li {
       margin: 0;
       width: 100%; }
@@ -629,42 +622,44 @@ main {
         cursor: pointer;
         color: inherit;
         white-space: nowrap;
         padding: 3px 12px;
         line-height: 16px;
         display: flex;
         align-items: center; }
         .context-menu > ul > li > a:hover, .context-menu > ul > li > a:focus {
-          background: #2B99FF;
+          background: #0A84FF;
           color: #FFF; }
           .context-menu > ul > li > a:hover a, .context-menu > ul > li > a:focus a {
             color: #0C0C0D; }
           .context-menu > ul > li > a:hover:hover, .context-menu > ul > li > a:hover:focus, .context-menu > ul > li > a:focus:hover, .context-menu > ul > li > a:focus:focus {
             color: #FFF; }
 
 .prefs-pane {
   font-size: 13px; }
   .prefs-pane .sidebar {
     background: #FFF;
-    border-left: solid 1px rgba(0, 0, 0, 0.1);
-    box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.08);
+    border-left: 1px solid #D7D7DB;
+    box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
     min-height: 100%;
     offset-inline-end: 0;
     padding: 40px;
     position: fixed;
     top: 0;
     transition: 0.1s cubic-bezier(0, 0, 0, 1);
-    transition-property: left, right;
+    transition-property: transform;
     width: 400px;
     z-index: 12000; }
     .prefs-pane .sidebar.hidden {
-      offset-inline-end: -400px; }
+      transform: translateX(100%); }
+      .prefs-pane .sidebar.hidden:dir(rtl) {
+        transform: translateX(-100%); }
     .prefs-pane .sidebar h1 {
-      border-bottom: solid 1px rgba(0, 0, 0, 0.1);
+      border-bottom: 1px solid #D7D7DB;
       font-size: 24px;
       margin: 0;
       padding: 20px 0; }
   .prefs-pane .prefs-modal-inner-wrapper section {
     margin: 20px 0; }
     .prefs-pane .prefs-modal-inner-wrapper section p {
       margin: 5px 0 5px 30px; }
     .prefs-pane .prefs-modal-inner-wrapper section label {
@@ -672,18 +667,18 @@ main {
       .prefs-pane .prefs-modal-inner-wrapper section label input {
         offset-inline-start: -30px;
         position: absolute;
         top: 0; }
     .prefs-pane .prefs-modal-inner-wrapper section > label {
       font-size: 16px;
       font-weight: bold; }
     .prefs-pane .prefs-modal-inner-wrapper section .options {
-      background: #FBFBFB;
-      border: solid 1px rgba(0, 0, 0, 0.1);
+      background: #F9F9FA;
+      border: 1px solid #D7D7DB;
       border-radius: 3px;
       margin: 15px 0;
       margin-inline-start: 30px;
       padding: 10px; }
       .prefs-pane .prefs-modal-inner-wrapper section .options label {
         background-position: 35px center;
         background-repeat: no-repeat;
         display: inline-block;
@@ -705,17 +700,17 @@ main {
   .prefs-pane [type='checkbox']:not(:checked) + label,
   .prefs-pane [type='checkbox']:checked + label {
     cursor: pointer;
     padding: 0 30px;
     position: relative; }
   .prefs-pane [type='checkbox']:not(:checked) + label::before,
   .prefs-pane [type='checkbox']:checked + label::before {
     background: #FFF;
-    border: 1px solid #C1C1C1;
+    border: 1px solid #B1B1B3;
     border-radius: 3px;
     content: '';
     height: 21px;
     offset-inline-start: 0;
     position: absolute;
     top: 0;
     width: 21px; }
   .prefs-pane [type='checkbox']:not(:checked) + label::after,
@@ -723,40 +718,41 @@ main {
     background: url("chrome://global/skin/in-content/check.svg") no-repeat center center;
     content: '';
     height: 21px;
     offset-inline-start: 0;
     position: absolute;
     top: 0;
     width: 21px;
     -moz-context-properties: fill, stroke;
-    fill: #1691D2;
+    fill: #0A84FF;
     stroke: none; }
   .prefs-pane [type='checkbox']:not(:checked) + label::after {
     opacity: 0; }
   .prefs-pane [type='checkbox']:checked + label::after {
     opacity: 1; }
   .prefs-pane [type='checkbox'] + label:hover::before {
-    border: 1px solid #1691D2; }
+    border: 1px solid #0A84FF; }
   .prefs-pane [type='checkbox']:checked:focus + label::before,
   .prefs-pane [type='checkbox']:not(:checked):focus + label::before {
-    border: 1px dotted #1691D2; }
+    border: 1px dotted #0A84FF; }
 
 .prefs-pane-button button {
   background-color: transparent;
   border: 0;
   cursor: pointer;
-  opacity: 0.7;
   padding: 15px;
   position: fixed;
   right: 15px;
   top: 15px;
   z-index: 12001; }
   .prefs-pane-button button:hover {
-    background-color: #EBEBEB; }
+    background-color: #F9F9FA; }
+  .prefs-pane-button button:active {
+    background-color: #EDEDF0; }
   .prefs-pane-button button:dir(rtl) {
     left: 5px;
     right: auto; }
 
 .confirmation-dialog .modal {
   position: fixed;
   width: 400px;
   top: 20%;
@@ -774,28 +770,28 @@ main {
   justify-content: flex-end; }
   .confirmation-dialog .actions button {
     margin-inline-end: 16px; }
     .confirmation-dialog .actions button.done {
       margin-inline-start: 0;
       margin-inline-end: 0; }
 
 .modal-overlay {
-  background: #FBFBFB;
+  background: #F9F9FA;
   height: 100%;
   left: 0;
   opacity: 0.8;
   position: fixed;
   top: 0;
   width: 100%;
   z-index: 11001; }
 
 .modal {
   background: #FFF;
-  border: solid 1px rgba(0, 0, 0, 0.1);
+  border: 1px solid #D7D7DB;
   border-radius: 3px;
   font-size: 14px;
   z-index: 11002; }
 
 .card-outer {
   background: #FFF;
   display: inline-block;
   margin-inline-end: 32px;
@@ -806,65 +802,63 @@ main {
   .card-outer .context-menu-button {
     cursor: pointer;
     position: absolute;
     top: -13.5px;
     offset-inline-end: -13.5px;
     width: 27px;
     height: 27px;
     background-color: #FFF;
-    background-image: url("assets/glyph-more-16.svg");
-    background-position: 65%;
-    background-repeat: no-repeat;
+    background-image: url("chrome://browser/skin/page-action.svg");
+    background-position: 55%;
     background-clip: padding-box;
-    border: 1px solid rgba(0, 0, 0, 0.2);
+    border: 1px solid #B1B1B3;
     border-radius: 100%;
-    box-shadow: 0 2px 0 rgba(0, 0, 0, 0.1);
+    box-shadow: 0 2px rgba(12, 12, 13, 0.1);
+    fill: rgba(12, 12, 13, 0.8);
     transform: scale(0.25);
     opacity: 0;
     transition-property: transform, opacity;
     transition-duration: 200ms;
     z-index: 399; }
     .card-outer .context-menu-button:focus, .card-outer .context-menu-button:active {
       transform: scale(1);
       opacity: 1; }
   .card-outer .card {
     height: 100%;
     border-radius: 3px;
-    box-shadow: 0 1px 4px 0 rgba(9, 6, 13, 0.1); }
+    box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); }
   .card-outer > a {
     display: block;
     color: inherit;
     height: 100%;
     outline: none;
     position: absolute;
     width: 224px; }
     .card-outer > a.active .card, .card-outer > a:focus .card {
-      box-shadow: 0 0 0 5px rgba(0, 0, 0, 0.1);
+      box-shadow: 0 0 0 5px #D7D7DB;
       transition: box-shadow 150ms; }
     .card-outer > a.active .card-title, .card-outer > a:focus .card-title {
-      color: #00AFF7; }
+      color: #008EA4; }
   .card-outer:hover, .card-outer:focus, .card-outer.active {
     outline: none;
-    box-shadow: 0 0 0 5px rgba(0, 0, 0, 0.1);
+    box-shadow: 0 0 0 5px #D7D7DB;
     transition: box-shadow 150ms; }
     .card-outer:hover .context-menu-button, .card-outer:focus .context-menu-button, .card-outer.active .context-menu-button {
       transform: scale(1);
       opacity: 1; }
     .card-outer:hover .card-title, .card-outer:focus .card-title, .card-outer.active .card-title {
-      color: #00AFF7; }
+      color: #008EA4; }
   .card-outer .card-preview-image {
     position: relative;
     background-size: cover;
     background-position: center;
     background-repeat: no-repeat;
     height: 122px;
-    border-bottom-color: rgba(0, 0, 0, 0.1);
-    border-bottom-style: solid;
-    border-bottom-width: 1px;
+    border-bottom: 1px solid #D7D7DB;
     border-radius: 3px 3px 0 0; }
   .card-outer .card-details {
     padding: 15px 16px 12px; }
     .card-outer .card-details.no-image {
       padding-top: 16px; }
   .card-outer .card-text {
     overflow: hidden;
     max-height: 78px; }
@@ -874,17 +868,17 @@ main {
       max-height: 97px; }
     .card-outer .card-text.no-image.no-host-name, .card-outer .card-text.no-image.no-context {
       max-height: 211px; }
     .card-outer .card-text.no-host-name.no-context {
       max-height: 116px; }
     .card-outer .card-text.no-image.no-host-name.no-context {
       max-height: 230px; }
   .card-outer .card-host-name {
-    color: #858585;
+    color: #737373;
     font-size: 10px;
     padding-bottom: 4px;
     text-transform: uppercase; }
   .card-outer .card-title {
     margin: 0 0 2px;
     font-size: 14px;
     word-wrap: break-word;
     line-height: 19px; }
@@ -895,32 +889,34 @@ main {
     overflow: hidden;
     line-height: 19px; }
   .card-outer .card-context {
     padding: 16px 16px 8px 14px;
     position: absolute;
     bottom: 0;
     left: 0;
     right: 0;
-    color: #A0A0A0;
+    color: #737373;
     font-size: 11px;
     display: flex; }
   .card-outer .card-context-icon {
-    opacity: 0.5;
+    fill: rgba(12, 12, 13, 0.6);
     font-size: 13px;
     margin-inline-end: 6px;
     display: block; }
   .card-outer .card-context-label {
     flex-grow: 1;
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap; }
 
 .manual-migration-container {
-  background: rgba(215, 215, 219, 0.5);
+  background: #F9F9FA;
+  border: 1px solid #D7D7DB;
+  box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
   font-size: 13px;
   border-radius: 2px;
   margin-bottom: 40px;
   padding: 20px;
   text-align: center; }
   @media (min-width: 544px) {
     .manual-migration-container {
       display: flex;
@@ -940,16 +936,17 @@ main {
         align-self: center;
         display: flex;
         justify-content: space-between; } }
   .manual-migration-container .icon {
     display: none; }
     @media (min-width: 544px) {
       .manual-migration-container .icon {
         display: block;
+        fill: rgba(12, 12, 13, 0.6);
         margin: 0;
         margin-inline-end: 12px;
         align-self: center; } }
 
 .manual-migration-actions {
   border: none;
   display: block;
   padding: 10px 0 0 0;
deleted file mode 100644
--- a/browser/extensions/activity-stream/data/content/assets/glyph-bookmark-16.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
-  <style>
-    path {
-      fill-rule: evenodd;
-      fill:#4d4d4d;
-    }
-  </style>
-  <path d="M198.992,18a0.955,0.955,0,0,0-.772.651l-1.984,4.122-4.332.72a0.851,0.851,0,0,0-.53,1.563l3.112,3.262-0.69,4.589c-0.1.69,0.173,1.094,0.658,1.094a1.4,1.4,0,0,0,.635-0.181l3.9-2.075,3.9,2.075a1.4,1.4,0,0,0,.634.181c0.485,0,.761-0.4.659-1.094L203.5,28.317l3.108-3.259a0.853,0.853,0,0,0-.53-1.566l-4.3-.719-2.016-4.122A0.953,0.953,0,0,0,198.992,18h0Z" transform="translate(-191 -18)"/>
-</svg>
deleted file mode 100644
--- a/browser/extensions/activity-stream/data/content/assets/glyph-bookmark-remove-16.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
-  <style>
-    path {
-      fill-rule: evenodd;
-      fill:#4d4d4d;
-    }
-  </style>
-  <path d="M199.008,47.642l0.983,2.01,0.452,0.924,1.015,0.17,2.324,0.389-1.719,1.8-0.676.708,0.145,0.968,0.36,2.4-1.953-1.038-0.938-.5-0.939.5-1.951,1.037,0.36-2.4,0.146-.969-0.676-.709-1.718-1.8,2.349-.39,1.024-.17,0.45-.935,0.962-2M199,44a0.953,0.953,0,0,0-.772.651l-1.984,4.122-4.332.72a0.851,0.851,0,0,0-.53,1.563l3.112,3.262-0.69,4.589c-0.1.69,0.172,1.094,0.658,1.094a1.394,1.394,0,0,0,.634-0.181L199,57.744l3.9,2.075a1.4,1.4,0,0,0,.635.181c0.485,0,.761-0.4.658-1.094l-0.687-4.589,3.108-3.259a0.853,0.853,0,0,0-.53-1.566l-4.3-.72-2.016-4.122A0.953,0.953,0,0,0,199,44h0Z" transform="translate(-191 -44)"/>
-</svg>
--- a/browser/extensions/activity-stream/data/content/assets/glyph-delete-16.svg
+++ b/browser/extensions/activity-stream/data/content/assets/glyph-delete-16.svg
@@ -1,13 +1,1 @@
-<?xml version="1.0"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
-  <style>
-    path {
-      fill-rule: evenodd;
-      fill:#4d4d4d;
-    }
-  </style>
-  <path d="M426,22H416a1,1,0,0,1,0-2h3a1,1,0,0,1,1-1h2a1,1,0,0,1,1,1h3A1,1,0,0,1,426,22Zm-0.9,10a1.132,1.132,0,0,1-1.1,1H418a1.125,1.125,0,0,1-1.1-1L416,23h10Z" transform="translate(-413 -18)"/>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><g fill="context-fill"><path d="M6.5 12a.5.5 0 0 0 .5-.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 0 .5.5zm2 0a.5.5 0 0 0 .5-.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 0 .5.5zm2 0a.5.5 0 0 0 .5-.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 0 .5.5z"/><path d="M14 2h-3.05a2.5 2.5 0 0 0-4.9 0H3a1 1 0 0 0 0 2v9a3 3 0 0 0 3 3h5a3 3 0 0 0 3-3V4a1 1 0 0 0 0-2zM8.5 1a1.489 1.489 0 0 1 1.391 1H7.109A1.489 1.489 0 0 1 8.5 1zM12 13a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4h7z"/></g></svg>
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/content/assets/glyph-dismiss-16.svg
+++ b/browser/extensions/activity-stream/data/content/assets/glyph-dismiss-16.svg
@@ -1,13 +1,1 @@
-<?xml version="1.0"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
-  <style>
-    path {
-      fill-rule: evenodd;
-      fill:#4d4d4d;
-    }
-  </style>
-  <path d="M422.414,52l3.531-3.531a1,1,0,1,0-1.414-1.414L421,50.586l-3.531-3.531a1,1,0,1,0-1.414,1.414L419.586,52l-3.531,3.531a1,1,0,1,0,1.414,1.414L421,53.414l3.531,3.531a1,1,0,1,0,1.414-1.414Z" transform="translate(-413 -44)"/>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="context-fill" d="M9.414 8l3.531-3.531a1 1 0 1 0-1.414-1.414L8 6.586 4.469 3.055a1 1 0 1 0-1.414 1.414L6.586 8l-3.531 3.531a1 1 0 1 0 1.414 1.414L8 9.414l3.531 3.531a1 1 0 1 0 1.414-1.414z"/></svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/extensions/activity-stream/data/content/assets/glyph-forward-16-white.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
-  <title>Forward - 16</title>
-  <g>
-    <polyline points="9 2 15 8 9 14" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
-    <line x1="14" y1="8" x2="1" y2="8" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
-  </g>
-</svg>
deleted file mode 100644
--- a/browser/extensions/activity-stream/data/content/assets/glyph-forward-16.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
-  <title>Forward - 16</title>
-  <g>
-    <polyline points="9 2 15 8 9 14" fill="none" stroke="#a0a0a0" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
-    <line x1="14" y1="8" x2="1" y2="8" fill="none" stroke="#a0a0a0" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
-  </g>
-</svg>
--- a/browser/extensions/activity-stream/data/content/assets/glyph-historyItem-16.svg
+++ b/browser/extensions/activity-stream/data/content/assets/glyph-historyItem-16.svg
@@ -1,6 +1,1 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
-  <path fill="#4d4d4d" d="M365,190a4,4,0,1,1,4-4A4,4,0,0,1,365,190Zm0-6a2,2,0,1,0,2,2A2,2,0,0,0,365,184Z" transform="translate(-357 -178)"/>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="context-fill" d="M8 12a4 4 0 1 1 4-4 4 4 0 0 1-4 4zm0-6a2 2 0 1 0 2 2 2 2 0 0 0-2-2z"/></svg>
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/content/assets/glyph-info-16.svg
+++ b/browser/extensions/activity-stream/data/content/assets/glyph-info-16.svg
@@ -1,6 +1,1 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
-  <path fill="context-fill" d="M8 1a7 7 0 1 0 7 7 7.008 7.008 0 0 0-7-7zm0 13a6 6 0 1 1 6-6 6.007 6.007 0 0 1-6 6zm0-7a1 1 0 0 0-1 1v3a1 1 0 1 0 2 0V8a1 1 0 0 0-1-1zm0-3.188A1.188 1.188 0 1 0 9.188 5 1.188 1.188 0 0 0 8 3.812z"/>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="context-fill" d="M8 1a7 7 0 1 0 7 7 7.008 7.008 0 0 0-7-7zm0 13a6 6 0 1 1 6-6 6.007 6.007 0 0 1-6 6zm0-7a1 1 0 0 0-1 1v3a1 1 0 1 0 2 0V8a1 1 0 0 0-1-1zm0-3.188A1.188 1.188 0 1 0 9.188 5 1.188 1.188 0 0 0 8 3.812z"/></svg>
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/content/assets/glyph-info-option-12.svg
+++ b/browser/extensions/activity-stream/data/content/assets/glyph-info-option-12.svg
@@ -1,1 +1,1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="#999" d="M6 0a6 6 0 1 0 6 6 6 6 0 0 0-6-6zm.7 10.26a1.13 1.13 0 0 1-.78.28 1.13 1.13 0 0 1-.78-.28 1 1 0 0 1 0-1.42 1.13 1.13 0 0 1 .78-.28 1.13 1.13 0 0 1 .78.28 1 1 0 0 1 0 1.42zM8.55 5a3 3 0 0 1-.62.81l-.67.63a1.58 1.58 0 0 0-.4.57 2.24 2.24 0 0 0-.12.74H5.06a3.82 3.82 0 0 1 .19-1.35 2.11 2.11 0 0 1 .63-.86 4.17 4.17 0 0 0 .66-.67 1.09 1.09 0 0 0 .23-.67.73.73 0 0 0-.77-.86.71.71 0 0 0-.57.26 1.1 1.1 0 0 0-.23.7h-2A2.36 2.36 0 0 1 4 2.47a2.94 2.94 0 0 1 2-.65 3.06 3.06 0 0 1 2 .6 2.12 2.12 0 0 1 .72 1.72 2 2 0 0 1-.17.86z"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="context-fill" d="M6 0a6 6 0 1 0 6 6 6 6 0 0 0-6-6zm.7 10.26a1.13 1.13 0 0 1-.78.28 1.13 1.13 0 0 1-.78-.28 1 1 0 0 1 0-1.42 1.13 1.13 0 0 1 .78-.28 1.13 1.13 0 0 1 .78.28 1 1 0 0 1 0 1.42zM8.55 5a3 3 0 0 1-.62.81l-.67.63a1.58 1.58 0 0 0-.4.57 2.24 2.24 0 0 0-.12.74H5.06a3.82 3.82 0 0 1 .19-1.35 2.11 2.11 0 0 1 .63-.86 4.17 4.17 0 0 0 .66-.67 1.09 1.09 0 0 0 .23-.67.73.73 0 0 0-.77-.86.71.71 0 0 0-.57.26 1.1 1.1 0 0 0-.23.7h-2A2.36 2.36 0 0 1 4 2.47a2.94 2.94 0 0 1 2-.65 3.06 3.06 0 0 1 2 .6 2.12 2.12 0 0 1 .72 1.72 2 2 0 0 1-.17.86z"/></svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/extensions/activity-stream/data/content/assets/glyph-more-16.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
-  <style>
-    circle {
-      fill: #4d4d4d;
-    }
-  </style>
-  
-  <g>
-    <circle cx="2" cy="8" r="2"/>
-    <circle cx="7" cy="8" r="2"/>
-    <circle cx="12" cy="8" r="2"/>
-  </g>
-</svg>
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/content/assets/glyph-newWindow-16.svg
+++ b/browser/extensions/activity-stream/data/content/assets/glyph-newWindow-16.svg
@@ -1,13 +1,1 @@
-<?xml version="1.0"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
-  <style>
-    path {
-      fill-rule: evenodd;
-      fill:#4d4d4d;
-    }
-  </style>
-  <path d="M382,20.007A1,1,0,0,1,383,19h14a1,1,0,0,1,1,1.007V31.993A1,1,0,0,1,397,33H383a1,1,0,0,1-1-1.007V20.007ZM384,23h12v8H384V23Zm0.5-3a0.5,0.5,0,1,1-.5.5A0.5,0.5,0,0,1,384.5,20Zm2,0a0.5,0.5,0,1,1-.5.5A0.5,0.5,0,0,1,386.5,20Zm2,0a0.5,0.5,0,1,1-.5.5A0.5,0.5,0,0,1,388.5,20Z" transform="translate(-382 -18)"/>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><g fill="context-fill"><path d="M14.923 1.618A1 1 0 0 0 14 1H9a1 1 0 0 0 0 2h2.586L8.293 6.293a1 1 0 1 0 1.414 1.414L13 4.414V7a1 1 0 0 0 2 0V2a1 1 0 0 0-.077-.382z"/><path d="M14 10a1 1 0 0 0-1 1v2H3V3h2a1 1 0 0 0 0-2H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1z"/></g></svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/extensions/activity-stream/data/content/assets/glyph-newWindow-private-16.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
-  <style>
-    path {
-      fill-rule: evenodd;
-      fill:#4d4d4d;
-    }
-  </style>
-  <path d="M356.994,24.619c-1.954.47-1.714,1.625-1.714,1.625s2.264,0.849,3.368.258a8.76,8.76,0,0,0,1.167-.668s-1.493-1.534-2.821-1.215m-5.987,0c-1.328-.32-2.821,1.215-2.821,1.215a8.76,8.76,0,0,0,1.167.668c1.1,0.591,3.368-.258,3.368-0.258s0.24-1.155-1.714-1.625M362,24.667c0,2.006-.647,5.334-3.755,5.333-1.143,0-3.1-1.993-4.245-1.993S350.9,30,349.755,30C346.647,30,346,26.673,346,24.667c0-2.094.984-2.813,3.628-2.638,2.739,0.181,3.066,1.087,4.372,1.087s1.8-.906,4.373-1.087c2.713-.191,3.627.544,3.627,2.638" transform="translate(-346 -18)"/>
-</svg>
deleted file mode 100644
--- a/browser/extensions/activity-stream/data/content/assets/glyph-now-16.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
-  <path fill="#4d4d4d" d="M8 0a8 8 0 1 0 8 8 8.009 8.009 0 0 0-8-8zm0 14a6 6 0 1 1 6-6 6.007 6.007 0 0 1-6 6zm3.5-6H8V4.5a.5.5 0 0 0-1 0v4a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 0-1z"/>
-</svg>
--- a/browser/extensions/activity-stream/data/content/assets/glyph-pin-12.svg
+++ b/browser/extensions/activity-stream/data/content/assets/glyph-pin-12.svg
@@ -1,8 +1,1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
-  <style>
-    path {
-      fill: #D7D7DB;
-    }
-  </style>
-  <path d="M10.53,9.47,8.25,7.19,9.8,5.643a.694.694,0,0,0,0-.98,3.04,3.04,0,0,0-2.161-.894H7.517A1.673,1.673,0,0,1,5.846,2.1V1.692A.693.693,0,0,0,4.664,1.2L1.2,4.664a.693.693,0,0,0,.49,1.182H2.1A1.672,1.672,0,0,1,3.769,7.517v.117a2.8,2.8,0,0,0,.925,2.192A.693.693,0,0,0,5.643,9.8L7.19,8.251l2.28,2.28A.75.75,0,0,0,10.53,9.47Z"/>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><path fill="context-fill" d="M10.53 9.47L8.25 7.19 9.8 5.643a.694.694 0 0 0 0-.98 3.04 3.04 0 0 0-2.161-.894h-.122A1.673 1.673 0 0 1 5.846 2.1v-.408A.693.693 0 0 0 4.664 1.2L1.2 4.664a.693.693 0 0 0 .49 1.182h.41a1.672 1.672 0 0 1 1.669 1.671v.117a2.8 2.8 0 0 0 .925 2.192.693.693 0 0 0 .949-.026L7.19 8.251l2.28 2.28a.75.75 0 0 0 1.06-1.061z"/></svg>
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/content/assets/glyph-pin-16.svg
+++ b/browser/extensions/activity-stream/data/content/assets/glyph-pin-16.svg
@@ -1,8 +1,1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
-  <style>
-    path {
-      fill: #4d4d4d;
-    }
-  </style>
-  <path d="M14.707,13.293,11.414,10l2.293-2.293a1,1,0,0,0,0-1.414A4.384,4.384,0,0,0,10.586,5h-.172A2.415,2.415,0,0,1,8,2.586V2a1,1,0,0,0-1.707-.707l-5,5A1,1,0,0,0,2,8h.586A2.415,2.415,0,0,1,5,10.414v.169a4.036,4.036,0,0,0,1.337,3.166,1,1,0,0,0,1.37-.042L10,11.414l3.293,3.293a1,1,0,0,0,1.414-1.414ZM7.129,11.456A2.684,2.684,0,0,1,7,10.583v-.169A4.386,4.386,0,0,0,5.708,7.293,4.414,4.414,0,0,0,4.136,6.278L6.279,4.136A4.4,4.4,0,0,0,7.292,5.707,4.384,4.384,0,0,0,10.414,7h.172a2.4,2.4,0,0,1,.848.152Z"/>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="context-fill" d="M14.707 13.293L11.414 10l2.293-2.293a1 1 0 0 0 0-1.414A4.384 4.384 0 0 0 10.586 5h-.172A2.415 2.415 0 0 1 8 2.586V2a1 1 0 0 0-1.707-.707l-5 5A1 1 0 0 0 2 8h.586A2.415 2.415 0 0 1 5 10.414v.169a4.036 4.036 0 0 0 1.337 3.166 1 1 0 0 0 1.37-.042L10 11.414l3.293 3.293a1 1 0 0 0 1.414-1.414zm-7.578-1.837A2.684 2.684 0 0 1 7 10.583v-.169a4.386 4.386 0 0 0-1.292-3.121 4.414 4.414 0 0 0-1.572-1.015l2.143-2.142a4.4 4.4 0 0 0 1.013 1.571A4.384 4.384 0 0 0 10.414 7h.172a2.4 2.4 0 0 1 .848.152z"/></svg>
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/content/assets/glyph-pocket-16.svg
+++ b/browser/extensions/activity-stream/data/content/assets/glyph-pocket-16.svg
@@ -1,6 +1,1 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
-  <path fill="#4d4d4d" d="M8 15a8 8 0 0 1-8-8V3a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v4a8 8 0 0 1-8 8zm3.985-10.032a.99.99 0 0 0-.725.319L7.978 8.57 4.755 5.336A.984.984 0 0 0 4 4.968a1 1 0 0 0-.714 1.7l-.016.011 3.293 3.306.707.707a1 1 0 0 0 1.414 0l.707-.707L12.7 6.679a1 1 0 0 0-.715-1.711z"/>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="context-fill" d="M8 15a8 8 0 0 1-8-8V3a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v4a8 8 0 0 1-8 8zm3.985-10.032a.99.99 0 0 0-.725.319L7.978 8.57 4.755 5.336A.984.984 0 0 0 4 4.968a1 1 0 0 0-.714 1.7l-.016.011 3.293 3.306.707.707a1 1 0 0 0 1.414 0l.707-.707L12.7 6.679a1 1 0 0 0-.715-1.711z"/></svg>
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/content/assets/glyph-search-16.svg
+++ b/browser/extensions/activity-stream/data/content/assets/glyph-search-16.svg
@@ -1,13 +1,1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
-  <defs>
-    <style>
-      .cls-1 {
-        fill: #a0a0a0;
-        fill-rule: evenodd;
-      }
-    </style>
-  </defs>
-  <g id="glyph-search-16">
-    <path id="Icon_-_Search_-_16" data-name="Icon - Search - 16" class="cls-1" d="M226.989,348.571l-2.2,2.2-9.533-9.534a11.436,11.436,0,1,1,2.2-2.2ZM208.37,323.745a8.407,8.407,0,1,0,8.406,8.406A8.406,8.406,0,0,0,208.37,323.745Z" transform="translate(-196 -320)"/>
-  </g>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M30.989 28.571l-2.2 2.2-9.533-9.534a11.436 11.436 0 1 1 2.2-2.2zM12.37 3.745a8.407 8.407 0 1 0 8.406 8.406 8.406 8.406 0 0 0-8.406-8.406z" fill="context-fill"/></svg>
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/content/assets/glyph-settings-16.svg
+++ b/browser/extensions/activity-stream/data/content/assets/glyph-settings-16.svg
@@ -1,1 +1,1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><g fill="none" stroke="#4d4d4d" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M8 1v3M8 12v3M4.5 11.5l-1.45 1.45M12.95 3.05L11 5M1 8h3M12 8h3"/><circle cx="8" cy="8" r="4"/><path d="M3.05 3.05L5 5M11 11l1.95 1.95"/></g></svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><g fill="none" stroke="context-fill" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M8 1v3m0 8v3m-3.5-3.5l-1.45 1.45m9.9-9.9L11 5M1 8h3m8 0h3"/><circle cx="8" cy="8" r="4"/><path d="M3.05 3.05L5 5m6 6l1.95 1.95"/></g></svg>
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/content/assets/glyph-topsites-16.svg
+++ b/browser/extensions/activity-stream/data/content/assets/glyph-topsites-16.svg
@@ -1,11 +1,1 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
-  <g fill="#4d4d4d">
-    <rect x="1" y="1" width="6" height="6" rx="1" ry="1"/>
-    <rect x="9" y="1" width="6" height="6" rx="1" ry="1"/>
-    <rect x="1" y="9" width="6" height="6" rx="1" ry="1"/>
-    <rect x="9" y="9" width="6" height="6" rx="1" ry="1"/>
-  </g>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><g fill="context-fill"><rect x="1" y="1" width="6" height="6" rx="1" ry="1"/><rect x="9" y="1" width="6" height="6" rx="1" ry="1"/><rect x="1" y="9" width="6" height="6" rx="1" ry="1"/><rect x="9" y="9" width="6" height="6" rx="1" ry="1"/></g></svg>
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/content/assets/glyph-trending-16.svg
+++ b/browser/extensions/activity-stream/data/content/assets/glyph-trending-16.svg
@@ -1,8 +1,1 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="Context-/-Pocket-Trending" fill="#999999">
-            <path d="M12.164765,5.74981818 C12.4404792,5.74981818 12.5976221,6.06981818 12.4233364,6.28509091 C10.7404792,8.37236364 4.26619353,15.6829091 4.15905067,15.744 C5.70047924,12.3301818 7.1276221,8.976 7.1276221,8.976 L4.3276221,8.976 C4.09905067,8.976 3.9376221,8.74472727 4.02333638,8.52654545 C4.70047924,6.77672727 6.86190781,1.32945455 7.30476495,0.216727273 C7.35333638,0.0916363636 7.46190781,0.0174545455 7.59476495,0.016 C8.32476495,0.0130909091 10.7904792,0.00290909091 12.5790507,0 C12.844765,0 12.9976221,0.305454545 12.8433364,0.525090909 L9.17190781,5.74981818 L12.164765,5.74981818 Z" id="Fill-1"></path>
-        </g>
-    </g>
-</svg>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M12.165 5.75a.33.33 0 0 1 .258.535c-1.683 2.087-8.157 9.398-8.264 9.459a385.997 385.997 0 0 0 2.969-6.768h-2.8a.328.328 0 0 1-.305-.45C4.7 6.777 6.862 1.33 7.305.217a.305.305 0 0 1 .29-.2C8.325.013 10.79.003 12.579 0c.266 0 .419.305.264.525L9.172 5.75h2.993z" fill="context-fill" fill-rule="evenodd"/></svg>
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/content/assets/glyph-unpin-16.svg
+++ b/browser/extensions/activity-stream/data/content/assets/glyph-unpin-16.svg
@@ -1,11 +1,1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
-  <style>
-    path {
-      fill: #4d4d4d;
-    }
-  </style>
-  <g>
-    <path d="M11.414,10l2.293-2.293a1,1,0,0,0,0-1.414,4.418,4.418,0,0,0-.8-.622L11.425,7.15l.008,0-4.3,4.3,0-.017-1.48,1.476a3.865,3.865,0,0,0,.692.834,1,1,0,0,0,1.37-.042L10,11.414l3.293,3.293a1,1,0,0,0,1.414-1.414Z"/>
-    <path d="M14.707,1.293a1,1,0,0,0-1.414,0L9.7,4.882A2.382,2.382,0,0,1,8,2.586V2a1,1,0,0,0-1.707-.707l-5,5A1,1,0,0,0,2,8h.586a2.382,2.382,0,0,1,2.3,1.7L1.293,13.293a1,1,0,1,0,1.414,1.414l12-12A1,1,0,0,0,14.707,1.293Zm-9,6A4.414,4.414,0,0,0,4.136,6.278L6.279,4.136A4.4,4.4,0,0,0,7.292,5.707a4.191,4.191,0,0,0,.9.684l-1.8,1.8A4.2,4.2,0,0,0,5.708,7.293Z"/>
-  </g>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M11.414 10l2.293-2.293a1 1 0 0 0 0-1.414 4.418 4.418 0 0 0-.8-.622L11.425 7.15h.008l-4.3 4.3v-.017l-1.48 1.476a3.865 3.865 0 0 0 .692.834 1 1 0 0 0 1.37-.042L10 11.414l3.293 3.293a1 1 0 0 0 1.414-1.414zm3.293-8.707a1 1 0 0 0-1.414 0L9.7 4.882A2.382 2.382 0 0 1 8 2.586V2a1 1 0 0 0-1.707-.707l-5 5A1 1 0 0 0 2 8h.586a2.382 2.382 0 0 1 2.3 1.7l-3.593 3.593a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414zm-9 6a4.414 4.414 0 0 0-1.571-1.015l2.143-2.142a4.4 4.4 0 0 0 1.013 1.571 4.191 4.191 0 0 0 .9.684l-1.8 1.8a4.2 4.2 0 0 0-.684-.898z" fill="context-fill"/></svg>
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/content/assets/glyph-webextension-16.svg
+++ b/browser/extensions/activity-stream/data/content/assets/glyph-webextension-16.svg
@@ -1,6 +1,1 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
-  <path fill="#4d4d4d" d="M14.5 8c-.971 0-1 1-1.75 1a.765.765 0 0 1-.75-.75V5a1 1 0 0 0-1-1H7.75A.765.765 0 0 1 7 3.25c0-.75 1-.779 1-1.75C8 .635 7.1 0 6 0S4 .635 4 1.5c0 .971 1 1 1 1.75a.765.765 0 0 1-.75.75H1a1 1 0 0 0-1 1v2.25A.765.765 0 0 0 .75 8c.75 0 .779-1 1.75-1C3.365 7 4 7.9 4 9s-.635 2-1.5 2c-.971 0-1-1-1.75-1a.765.765 0 0 0-.75.75V15a1 1 0 0 0 1 1h3.25a.765.765 0 0 0 .75-.75c0-.75-1-.779-1-1.75 0-.865.9-1.5 2-1.5s2 .635 2 1.5c0 .971-1 1-1 1.75a.765.765 0 0 0 .75.75H11a1 1 0 0 0 1-1v-3.25a.765.765 0 0 1 .75-.75c.75 0 .779 1 1.75 1 .865 0 1.5-.9 1.5-2s-.635-2-1.5-2z"/>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="context-fill" d="M14.5 8c-.971 0-1 1-1.75 1a.765.765 0 0 1-.75-.75V5a1 1 0 0 0-1-1H7.75A.765.765 0 0 1 7 3.25c0-.75 1-.779 1-1.75C8 .635 7.1 0 6 0S4 .635 4 1.5c0 .971 1 1 1 1.75a.765.765 0 0 1-.75.75H1a1 1 0 0 0-1 1v2.25A.765.765 0 0 0 .75 8c.75 0 .779-1 1.75-1C3.365 7 4 7.9 4 9s-.635 2-1.5 2c-.971 0-1-1-1.75-1a.765.765 0 0 0-.75.75V15a1 1 0 0 0 1 1h3.25a.765.765 0 0 0 .75-.75c0-.75-1-.779-1-1.75 0-.865.9-1.5 2-1.5s2 .635 2 1.5c0 .971-1 1-1 1.75a.765.765 0 0 0 .75.75H11a1 1 0 0 0 1-1v-3.25a.765.765 0 0 1 .75-.75c.75 0 .779 1 1.75 1 .865 0 1.5-.9 1.5-2s-.635-2-1.5-2z"/></svg>
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/content/assets/topic-show-more-12.svg
+++ b/browser/extensions/activity-stream/data/content/assets/topic-show-more-12.svg
@@ -1,12 +1,1 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
-    <title>Icon / &gt;</title>
-    <desc>Created with Sketch.</desc>
-    <defs></defs>
-    <g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
-        <g id="Icon-/-&gt;" stroke-width="2" stroke="#008EA4">
-            <polyline id="Path-2" points="4 2 8 6 4 10"></polyline>
-        </g>
-    </g>
-</svg>
\ No newline at end of file
+<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><path d="M4 2l4 4-4 4" stroke-width="2" stroke="context-fill" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/locales.json
+++ b/browser/extensions/activity-stream/data/locales.json
@@ -1,65 +1,94 @@
 {
   "ach": {
     "newtab_page_title": "Dirica matidi manyen",
     "default_label_loading": "Tye ka cano…",
     "header_top_sites": "Kakube maloyo",
+    "header_stories": "Lok madito",
+    "header_visit_again": "Lim doki",
+    "header_bookmarks": "Alamabuk ma cok coki",
+    "header_recommended_by": "Lami tam obedo {provider}",
     "header_bookmarks_placeholder": "Pud i pee ki alamabuk.",
+    "header_stories_from": "ki bot",
     "type_label_visited": "Kilimo",
     "type_label_bookmarked": "Kiketo alamabuk",
     "type_label_synced": "Kiribo ki i nyonyo mukene",
+    "type_label_recommended": "Ma cuke lamal",
     "type_label_open": "Tye ayaba",
     "type_label_topic": "Lok",
+    "type_label_now": "Kombedi",
     "menu_action_bookmark": "Alamabuk",
     "menu_action_remove_bookmark": "Kwany alamabuk",
     "menu_action_copy_address": "Lok kabedo",
     "menu_action_email_link": "Kakube me email…",
     "menu_action_open_new_window": "Yab i dirica manyen",
     "menu_action_open_private_window": "Yab i dirica manyen me mung",
     "menu_action_dismiss": "Kwer",
     "menu_action_delete": "Kwany ki ii gin mukato",
     "menu_action_pin": "Mwon",
+    "menu_action_unpin": "War",
+    "confirm_history_delete_p1": "Imoko ni imito kwanyo nyig jami weng me potbuk man ki i gin mukato mamegi?",
+    "confirm_history_delete_notice_p2": "Pe ki twero gonyo tic man.",
     "menu_action_save_to_pocket": "Gwoki i jaba",
     "search_for_something_with": "Yeny pi {search_term} ki:",
     "search_button": "Yeny",
     "search_header": "Yeny me {search_engine_name}",
     "search_web_placeholder": "Yeny kakube",
     "search_settings": "Lok ter me yeny",
+    "section_info_option": "Ngec",
     "welcome_title": "Wajoli i dirica matidi manyen",
     "welcome_body": "Firefox bi tic ki kabedo man me nyuto alamabukke mamegi, coc akwana, vidio, ki potbukke ma ilimo cokcoki ma pi gi tego loyo, wek i dok ii gi ma yot.",
     "welcome_label": "Tye ka kube ki wiye madito mamegi",
     "time_label_less_than_minute": "<dakika1",
     "time_label_minute": "dakika{number}",
     "time_label_hour": "cawa{number}",
     "time_label_day": "nino{number}",
     "settings_pane_button_label": "Yub potbuk me dirica matidi mamegi manyen",
     "settings_pane_header": "Ter me dirica matidi manyen",
     "settings_pane_body": "Yer ngo ma i neno ka i yabo dirica matidi manyen.",
     "settings_pane_search_header": "Yeny",
     "settings_pane_search_body": "Yeny Kakube ki i dirica ni matidi manyen.",
     "settings_pane_topsites_header": "Kakube ma gi loyo",
     "settings_pane_topsites_body": "Nong kakube ma ilimo loyo.",
     "settings_pane_topsites_options_showmore": "Nyut rek ariyo",
     "settings_pane_bookmarks_header": "Alamabuk ma cocoki",
+    "settings_pane_bookmarks_body": "Alamabukke ni ma kicweyo manyen i kabedo acel macek.",
     "settings_pane_visit_again_header": "Lim Kidoco",
+    "settings_pane_visit_again_body": "Firefox bi nyuti but gin mukato me yeny mamegi ma itwero mito me poo ikome onyo dok cen iyie.",
+    "settings_pane_pocketstories_header": "Lok madito",
+    "settings_pane_pocketstories_body": "Pocket, but jo me Mozilla, bi konyi me kube i jami mabeco loyo ma twero bedo ni pe i nongo.",
     "settings_pane_done_button": "Otum",
     "edit_topsites_button_text": "Yubi",
     "edit_topsites_button_label": "Yub bute pi kakubi ni ma giloyo",
     "edit_topsites_showmore_button": "Nyut mukene",
     "edit_topsites_showless_button": "Nyut manok",
     "edit_topsites_done_button": "Otum",
     "edit_topsites_pin_button": "Mwon kakube man",
+    "edit_topsites_unpin_button": "War kakube man",
     "edit_topsites_edit_button": "Yub kakube man",
     "edit_topsites_dismiss_button": "Kwer kakube man",
     "edit_topsites_add_button": "Medi",
+    "topsites_form_add_header": "Kakube maloyo manyen",
     "topsites_form_edit_header": "Yub Kakube maloyo",
+    "topsites_form_title_placeholder": "Ket wiye",
+    "topsites_form_url_placeholder": "Coo onyo mwon URL",
     "topsites_form_add_button": "Medi",
     "topsites_form_save_button": "Gwoki",
-    "topsites_form_cancel_button": "Kwer"
+    "topsites_form_cancel_button": "Kwer",
+    "topsites_form_url_validation": "URL ma tye atir mite",
+    "pocket_read_more": "Lok macuk gi lamal:",
+    "pocket_read_even_more": "Nen Lok mapol",
+    "pocket_feedback_header": "Kakube maber loyo, dano makato milion 25 aye oyubo.",
+    "pocket_feedback_body": "Pocket, but jo me Mozilla, bi konyi me kube i jami mabeco loyo ma twero bedo ni pe i nongo.",
+    "pocket_send_feedback": "Cwal adwogi",
+    "topstories_empty_state": "Ityeko weng. Rot doki lacen pi lok madito mapol ki bot {provider}. Pe itwero kuro? Yer lok macuke lamal me nongo lok mabeco mapol ki i but kakube.",
+    "manual_migration_explanation": "Tem Firefox ki kakube ki alamabuk ni ma imaro loyo ki i layeny mukene.",
+    "manual_migration_cancel_button": "Pe Apwoyo",
+    "manual_migration_import_button": "Kel kombedi"
   },
   "af": {},
   "an": {},
   "ar": {
     "newtab_page_title": "لسان جديد",
     "default_label_loading": "يُحمّل…",
     "header_top_sites": "المواقع الأكثر زيارة",
     "header_stories": "أهم الأخبار",
@@ -222,17 +251,21 @@
     "topsites_form_add_button": "Əlavə et",
     "topsites_form_save_button": "Saxla",
     "topsites_form_cancel_button": "Ləğv et",
     "topsites_form_url_validation": "Doğru ünvan tələb olunur",
     "pocket_read_more": "Məşhur Mövzular:",
     "pocket_read_even_more": "Daha çox hekayə gör",
     "pocket_feedback_header": "25 milyon nəfərin dəstəyi ilə internetin ən yaxşıları.",
     "pocket_feedback_body": "Pocket, Mozilla ailəsinin üzvü, yüksək keyfiyyətli məzmunları kəşf etməyinizə kömək edəcək.",
-    "pocket_send_feedback": "Əks-əlaqə göndər"
+    "pocket_send_feedback": "Əks-əlaqə göndər",
+    "topstories_empty_state": "Hamısını oxudunuz. Yeni {provider} məqalələri üçün daha sonra təkrar yoxlayın. Gözləyə bilmirsiz? Məşhur mövzu seçərək internetdən daha çox gözəl məqalələr tapın.",
+    "manual_migration_explanation": "Firefox səyyahını digər səyyahınızdan olan sevimli sayt və əlfəcinlərinizlə yoxlayın.",
+    "manual_migration_cancel_button": "Xeyr, Təşəkkürlər",
+    "manual_migration_import_button": "İndi idxal et"
   },
   "be": {
     "newtab_page_title": "Новая картка",
     "default_label_loading": "Загрузка…",
     "header_top_sites": "Папулярныя сайты",
     "header_stories": "Галоўныя навіны",
     "header_visit_again": "Наведаць зноў",
     "header_bookmarks": "Нядаўнія закладкі",
@@ -403,24 +436,26 @@
   },
   "bn-BD": {
     "newtab_page_title": "নতুন ট্যাব",
     "default_label_loading": "লোড হচ্ছে…",
     "header_top_sites": "শীর্ঘ সাইট",
     "header_stories": "শীর্ষ গল্প",
     "header_visit_again": "পুনরায় ভিজিট করুন",
     "header_bookmarks": "সাম্প্রতিক বুকমার্ক",
+    "header_recommended_by": "{provider} দ্বারা সুপারিশকৃত",
     "header_bookmarks_placeholder": "এখনও কোন বুকমার্ক নেই।",
     "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": "বুকমার্ক",
     "menu_action_remove_bookmark": "বুকমার্ক মুছে দিন",
     "menu_action_copy_address": "ঠিকানা কপি করুন",
     "menu_action_email_link": "ইমেইল লিঙ্ক…",
     "menu_action_open_new_window": "নতুন উইন্ডোতে খুলুন",
     "menu_action_open_private_window": "নতুন ব্যক্তিগত উইন্ডোতে খুলুন",
     "menu_action_dismiss": "বাতিল",
     "menu_action_delete": "ইতিহাস থেকে মুছে ফেলুন",
@@ -429,16 +464,17 @@
     "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_settings": "সার্চ সেটিংস বদল করুন",
+    "section_info_option": "তথ্য",
     "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": "আপনার নতুন ট্যাব পেজটি কাস্টমাইজ করুন",
@@ -447,38 +483,47 @@
     "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 আপনার ব্রাউজিং ইতিহাসের এমন একটি অংশ দেখাবে যা আপনি মনে রাখতে চান বা যাতে আবার ফিরে যেতে চান।",
     "settings_pane_pocketstories_header": "শীর্ষ গল্প",
+    "settings_pane_pocketstories_body": "Pocket, হচ্ছে Mozilla পরিবারের একটি অংশ, যা আপনাকে ভালো-মানের কন্টেন্টের সাথে যুক্ত করবে যা আপনি অন্য কোথাও পাবেন না।",
     "settings_pane_done_button": "হয়েছে",
     "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": "সাইটটি সম্পাদনা করুন",
     "edit_topsites_dismiss_button": "সাইটটি মুছে দিন",
     "edit_topsites_add_button": "যোগ",
     "topsites_form_add_header": "নতুন শীর্ষ সাইট",
     "topsites_form_edit_header": "শীর্ষ সাইট সম্পাদনা করুন",
     "topsites_form_title_placeholder": "নাম দিন",
+    "topsites_form_url_placeholder": "টাইপ করুন অথবা পেস্ট করুন URL",
     "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_send_feedback": "প্রতিক্রিয়া জানান"
+    "pocket_feedback_header": "ওয়েব জগতের সেরা, যা ২.৫ লক্ষ মানুষ রক্ষণাবেক্ষণ করে।",
+    "pocket_feedback_body": "Pocket, হচ্ছে Mozilla পরিবারের একটি অংশ, যা আপনাকে ভালো-মানের কন্টেন্টের সাথে যুক্ত করবে যা আপনি অন্য কোথাও পাবেন না।",
+    "pocket_send_feedback": "প্রতিক্রিয়া জানান",
+    "topstories_empty_state": "কিছু একটা ঠিক নেই। {provider} এর শীর্ষ গল্পগুলো পেতে কিছুক্ষণ পর আবার দেখুন। অপেক্ষা করতে চান না? বিশ্বের সেরা গল্পগুলো পেতে কোন জনপ্রিয় বিষয় নির্বাচন করুন।",
+    "manual_migration_explanation": "অন্য ব্রাউজার থেকে আপনার পছন্দের সাইট এবং বুকমার্কগুলো নিয়ে Firefox ব্যবহার করুন।",
+    "manual_migration_cancel_button": "প্রয়োজন নেই",
+    "manual_migration_import_button": "এখনই ইম্পোর্ট করুন"
   },
   "bn-IN": {},
   "br": {},
   "bs": {},
   "ca": {
     "newtab_page_title": "Pestanya nova",
     "default_label_loading": "S'està carregant…",
     "header_top_sites": "Llocs principals",
@@ -559,17 +604,75 @@
     "pocket_feedback_header": "El millor del web, seleccionat per més de 25 milions de persones.",
     "pocket_feedback_body": "El Pocket, membre de la família Mozilla, us permet accedir a contingut d'alta qualitat que d'altra manera potser no trobaríeu.",
     "pocket_send_feedback": "Doneu la vostra opinió",
     "topstories_empty_state": "Ja esteu al dia. Torneu més tard per veure més articles populars de {provider}. No podeu esperar? Trieu un tema popular per descobrir els articles més interessants de tot el web.",
     "manual_migration_explanation": "Proveu el Firefox amb els vostres llocs preferits i les adreces d'interès d'un altre navegador.",
     "manual_migration_cancel_button": "No, gràcies",
     "manual_migration_import_button": "Importa-ho ara"
   },
-  "cak": {},
+  "cak": {
+    "newtab_page_title": "K'ak'a' ruwi'",
+    "default_label_loading": "Tajin nusamajij…",
+    "header_top_sites": "Utziläj taq Ruxaq K'amaya'l",
+    "header_stories": "Utziläj taq B'anob'äl",
+    "header_visit_again": "Titz'et chik",
+    "header_bookmarks": "K'ak'a' taq Yaketal",
+    "header_recommended_by": "Chilab'en ruma {provider}",
+    "header_bookmarks_placeholder": "K'a majani k'o jujun taq ayaketal.",
+    "header_stories_from": "richin",
+    "type_label_visited": "Tz'eton",
+    "type_label_bookmarked": "Yakon retal",
+    "type_label_synced": "Ximon rik'in jun chik okisaxel",
+    "type_label_recommended": "Rujawaxik",
+    "type_label_open": "Tijaq",
+    "type_label_now": "Wakami",
+    "menu_action_bookmark": "Yaketal",
+    "menu_action_remove_bookmark": "Tiyuj el ri yaketal",
+    "menu_action_copy_address": "Tiwachib'ëx Ochochib'äl",
+    "menu_action_email_link": "Titaq Ximonel Tzij…",
+    "menu_action_open_new_window": "Tijaq pa jun K'ak'a' Tzuwäch",
+    "menu_action_open_private_window": "Tijaq pa jun K'ak'a' Ichinan Tzuwäch",
+    "menu_action_dismiss": "Tichup ruwäch",
+    "menu_action_delete": "Tiyuj el pa ri Natab'äl",
+    "confirm_history_delete_p1": "¿La kan nawajo ye'ayüj el ronojel ri kib'eyal re taq ruxaq re' chi kikojol ri anatab'al?",
+    "confirm_history_delete_notice_p2": "Man yatikïr ta najäl re b'anïk re'.",
+    "menu_action_save_to_pocket": "Tiyak pa Pocket",
+    "search_for_something_with": "Tikanoj {search_term} rik'in:",
+    "search_button": "Tikanöx",
+    "search_header": "{search_engine_name} Tikanöx",
+    "search_web_placeholder": "Tikanöx pa Ajk'amaya'l",
+    "search_settings": "Tijal Runuk'ulem Kanoxïk",
+    "section_info_option": "Rutzijol",
+    "welcome_title": "Ütz apetik pa ri k'ak'a' ruwi'",
+    "welcome_body": "Firefox xtrokisaj re k'ojlib'äl re' richin xtuk'üt ri taq ruwi', rutzijol, tzuwäch chuqa' taq ruxaq yalan kejqalem ri k'a ja' xe'atz'ët, richin chanin yatikïr yatok jun mul chik.",
+    "welcome_label": "Tiya' ketal ri Nïm taq K'ojlib'äl",
+    "time_label_less_than_minute": "<1m",
+    "time_label_minute": "{number}m",
+    "time_label_hour": "{number}m",
+    "time_label_day": "{ajilab'äl}m",
+    "settings_pane_button_label": "Tawichinaj ri ruxaq richin K'ak'a' Ruwi'",
+    "settings_pane_header": "K'ak'a' Ruwi' Taq Ajowab'äl",
+    "settings_pane_body": "Tacha' ri natz'ët toq najäq jun k'ak'a' ruwi'.",
+    "settings_pane_search_header": "Tikanöx",
+    "settings_pane_search_body": "Tikanoj ri k'ak'a' taq ruwi' pa ri K'amaya'l.",
+    "settings_pane_topsites_header": "Utziläj taq ruxaq K'amaya'l",
+    "settings_pane_topsites_body": "Katok pa ri taq ajk'amaya'l yalan ye'atz'ët.",
+    "settings_pane_topsites_options_showmore": "Kek'ut pe ka'i' cholaj",
+    "settings_pane_bookmarks_header": "K'ak'a' taq Yaketal",
+    "settings_pane_bookmarks_body": "Ri taq awajowab'äl k'a ri xenuk' pa jun utziläj k'ojlib'äl.",
+    "settings_pane_visit_again_header": "Tab'etz'eta' chik",
+    "settings_pane_visit_again_body": "Firefox  xtuk'ut pe jalajoj taq rub'eyal ri b'anob'äl richin rukusaxik ri k'amaya'l rik'in jub'a' nawajo' nanataj chuqa' yatikir natzu' chik.",
+    "settings_pane_pocketstories_header": "Utziläj taq B'anob'äl",
+    "settings_pane_done_button": "Xk'is",
+    "edit_topsites_button_text": "Tinuk'",
+    "edit_topsites_button_label": "Tab'ana' runuk'ulem ri kitanaj Nimaläj taq Ruxaq K'amaya'l",
+    "topsites_form_cancel_button": "Tiq'at",
+    "manual_migration_import_button": "Tijik' pe"
+  },
   "cs": {
     "newtab_page_title": "Nový panel",
     "default_label_loading": "Načítání…",
     "header_top_sites": "Top stránky",
     "header_stories": "Nejlepší příběhy",
     "header_visit_again": "Znovu navštívit",
     "header_bookmarks": "Nedávno přidané záložky",
     "header_recommended_by": "Doporučení ze služby {provider}",
@@ -1158,17 +1261,16 @@
     "pocket_read_even_more": "View More Stories",
     "pocket_feedback_header": "The best of the web, curated by over 25 million people.",
     "pocket_feedback_body": "Pocket, a part of the Mozilla family, will help connect you to high-quality content that you may not have found otherwise.",
     "pocket_send_feedback": "Send Feedback"
   },
   "en-US": {
     "newtab_page_title": "New Tab",
     "default_label_loading": "Loading…",
-    "home_page_title": "{build} Start Page",
     "header_top_sites": "Top Sites",
     "header_stories": "Top Stories",
     "header_visit_again": "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": "Visited",
@@ -1744,17 +1846,87 @@
     "topsites_form_cancel_button": "Tühista",
     "topsites_form_url_validation": "URL peab olema korrektne",
     "pocket_read_more": "Populaarsed teemad:",
     "pocket_read_even_more": "Rohkem lugusid",
     "pocket_feedback_header": "Parim osa veebist, mille on kokku pannud rohkem kui 25 miljonit inimest.",
     "pocket_feedback_body": "Pocket, osana Mozilla perekonnast, aitab sul leida kvaliteetset sisu, mida sa muidu poleks ehk leidnud.",
     "pocket_send_feedback": "Saada tagasisidet"
   },
-  "eu": {},
+  "eu": {
+    "newtab_page_title": "Fitxa berria",
+    "default_label_loading": "Kargatzen…",
+    "header_top_sites": "Gune erabilienak",
+    "header_visit_again": "Bisitatu berriro",
+    "header_bookmarks": "Azken laster-markak",
+    "header_recommended_by": "{provider} hornitzaileak gomendatuta",
+    "header_bookmarks_placeholder": "Ez daukazu laster-markarik oraindik.",
+    "type_label_visited": "Bisitatuta",
+    "type_label_bookmarked": "Laster-marka eginda",
+    "type_label_synced": "Beste gailu batetik sinkronizatuta",
+    "type_label_recommended": "Joerak",
+    "type_label_open": "Ireki",
+    "type_label_topic": "Gaia",
+    "type_label_now": "Orain",
+    "menu_action_bookmark": "Egin laster-marka",
+    "menu_action_remove_bookmark": "Kendu laster-marka",
+    "menu_action_copy_address": "Kopiatu helbidea",
+    "menu_action_email_link": "Bidali lotura postaz…",
+    "menu_action_open_new_window": "Ireki leiho berri batean",
+    "menu_action_open_private_window": "Ireki leiho pribatu berrian",
+    "menu_action_dismiss": "Baztertu",
+    "menu_action_delete": "Ezabatu historiatik",
+    "menu_action_pin": "Ainguratu",
+    "menu_action_unpin": "Desainguratu",
+    "confirm_history_delete_p1": "Ziur zaude orri honen agerpen guztiak ezabatu nahi dituzula historiatik?",
+    "confirm_history_delete_notice_p2": "Ekintza hau ezin da desegin.",
+    "menu_action_save_to_pocket": "Gorde Pocket-en",
+    "search_for_something_with": "Bilatu {search_term} honekin:",
+    "search_button": "Bilatu",
+    "search_header": "{search_engine_name} bilaketa",
+    "search_web_placeholder": "Bilatu webean",
+    "search_settings": "Aldatu bilaketa-ezarpenak",
+    "section_info_option": "Informazioa",
+    "welcome_title": "Ongi etorri fitxa berrira",
+    "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": "Pertsonalizatu fitxa berriaren orria",
+    "settings_pane_header": "Fitxa berriaren hobespenak",
+    "settings_pane_body": "Aukeratu fitxa berria irekitzean ikusten duzuna.",
+    "settings_pane_search_header": "Bilatu",
+    "settings_pane_search_body": "Bilatu webean zure fitxa berritik.",
+    "settings_pane_topsites_header": "Gune erabilienak",
+    "settings_pane_topsites_options_showmore": "Erakutsi bi errenkada",
+    "settings_pane_bookmarks_header": "Azken laster-markak",
+    "settings_pane_visit_again_header": "Bisitatu berriro",
+    "settings_pane_visit_again_body": "Gogoratu edo itzuli nahiko duzun historiaren zatia erakutsiko dizu Firefoxek.",
+    "settings_pane_pocketstories_body": "Pocket-ek, Mozilla familiakideak, bestela aurkituko ez zenukeen kalitate handiko edukiarekin konektatzen lagunduko dizu.",
+    "settings_pane_done_button": "Eginda",
+    "edit_topsites_button_text": "Editatu",
+    "edit_topsites_button_label": "Pertsonalizatu gune erabilienen atala",
+    "edit_topsites_showmore_button": "Erakutsi gehiago",
+    "edit_topsites_showless_button": "Erakutsi gutxiago",
+    "edit_topsites_done_button": "Eginda",
+    "edit_topsites_pin_button": "Ainguratu gune hau",
+    "edit_topsites_unpin_button": "Desainguratu gune hau",
+    "edit_topsites_edit_button": "Editatu gune hau",
+    "edit_topsites_dismiss_button": "Baztertu gune hau",
+    "edit_topsites_add_button": "Gehitu",
+    "topsites_form_title_placeholder": "Idatzi izenburua",
+    "topsites_form_url_placeholder": "Idatzi edo itsatsi URLa",
+    "topsites_form_add_button": "Gehitu",
+    "topsites_form_save_button": "Gorde",
+    "topsites_form_cancel_button": "Utzi",
+    "topsites_form_url_validation": "Baliozko URLa behar da",
+    "pocket_send_feedback": "Bidali iritzia",
+    "manual_migration_cancel_button": "Ez, eskerrik asko",
+    "manual_migration_import_button": "Inportatu orain"
+  },
   "fa": {
     "newtab_page_title": "زبانه جدید",
     "default_label_loading": "در حال بارگیری…",
     "header_top_sites": "سایت‌های برتر",
     "header_stories": "برترین داستان‌ها",
     "header_visit_again": "مشاهده دوباره",
     "header_bookmarks": "نشانک‌های اخیر",
     "header_recommended_by": "پیشنهاد شده توسط {provider}",
@@ -1826,35 +1998,39 @@
     "topsites_form_save_button": "ذخیره",
     "topsites_form_cancel_button": "انصراف",
     "topsites_form_url_validation": "URL معتبر الزامی است",
     "pocket_read_more": "موضوع‌های محبوب:",
     "pocket_read_even_more": "مشاهده داستان‌های بیشتر",
     "pocket_feedback_header": "بهترین‌های وب، گزینش شده توسط بیش از ۲۵ میلیون نفر.",
     "pocket_feedback_body": "Pocket، بخشی از خانواده موزیلا، کمک خواهد کرد تا به محتوایی با کیفیت بالا مرتبط شوید که در غیر این صورت ممکن بود پیدا نکنید.",
     "pocket_send_feedback": "ارسال بازخورد",
+    "topstories_empty_state": "فعلا تموم شد. بعدا دوباره سر بزن تا مطالب جدید از {provider} ببینی. نمی‌تونی صبر کنی؟ یک موضوع محبوب رو انتخاب کن تا مطالب جالب مرتبط از سراسر دنیا رو پیدا کنی.",
     "manual_migration_explanation": "فایرفاکس را با سایت‌های مورد علاقه و نشانک‌های خود در سایر مرورگرها امتحان کنید.",
     "manual_migration_cancel_button": "نه ممنون",
     "manual_migration_import_button": "هم‌اکنون وارد شوند"
   },
   "ff": {},
   "fi": {
     "newtab_page_title": "Uusi välilehti",
     "default_label_loading": "Ladataan…",
     "header_top_sites": "Ykkössivustot",
     "header_stories": "Ykkösjutut",
     "header_visit_again": "Käy toistekin",
     "header_bookmarks": "Uusimmat kirjanmerkit",
+    "header_recommended_by": "Suositukset lähteestä {provider}",
     "header_bookmarks_placeholder": "Sinulla ei ole vielä kirjanmerkkejä.",
+    "header_stories_from": "Lähde",
     "type_label_visited": "Vierailtu",
     "type_label_bookmarked": "Kirjanmerkki",
     "type_label_synced": "Synkronoitu toiselta laitteelta",
     "type_label_recommended": "Pinnalla",
     "type_label_open": "Avoin",
     "type_label_topic": "Aihe",
+    "type_label_now": "Nyt",
     "menu_action_bookmark": "Lisää kirjanmerkki",
     "menu_action_remove_bookmark": "Poista kirjanmerkki",
     "menu_action_copy_address": "Kopioi osoite",
     "menu_action_email_link": "Lähetä linkki…",
     "menu_action_open_new_window": "Avaa uuteen ikkunaan",
     "menu_action_open_private_window": "Avaa uuteen yksityiseen ikkunaan",
     "menu_action_dismiss": "Hylkää",
     "menu_action_delete": "Poista historiasta",
@@ -1863,34 +2039,38 @@
     "confirm_history_delete_p1": "Haluatko varmasti poistaa tämän sivun kaikkialta historiastasi?",
     "confirm_history_delete_notice_p2": "Tämä toiminto on peruuttamaton.",
     "menu_action_save_to_pocket": "Tallenna Pocket-palveluun",
     "search_for_something_with": "Hae {search_term} palvelusta:",
     "search_button": "Haku",
     "search_header": "{search_engine_name}-haku",
     "search_web_placeholder": "Verkkohaku",
     "search_settings": "Muuta hakuasetuksia",
+    "section_info_option": "Tietoa",
     "welcome_title": "Tervetuloa uuteen välilehteen",
     "welcome_body": "Firefox käyttää tätä tilaa näyttämään olennaisimmat kirjanmerkit, artikkelit, videot ja sivut, joita olet katsellut, jotta pääset niihin takaisin nopeasti.",
     "welcome_label": "Tunnistetaan nostojasi",
     "time_label_less_than_minute": "<1 min",
     "time_label_minute": "{number} min",
     "time_label_hour": "{number} h",
     "time_label_day": "{number} pv",
     "settings_pane_button_label": "Muokkaa Uusi välilehti -sivua",
     "settings_pane_header": "Uuden välilehden asetukset",
     "settings_pane_body": "Valitse mitä näet, kun avaat uuden välilehden.",
     "settings_pane_search_header": "Haku",
     "settings_pane_search_body": "Tee verkkohakuja uudesta välilehdestä.",
     "settings_pane_topsites_header": "Ykkössivustot",
+    "settings_pane_topsites_body": "Näe eniten vierailemasi sivustot.",
     "settings_pane_topsites_options_showmore": "Näytä kaksi riviä",
     "settings_pane_bookmarks_header": "Uusimmat kirjanmerkit",
     "settings_pane_bookmarks_body": "Uusimmat kirjanmerkkisi, yhdessä kätevässä paikassa.",
     "settings_pane_visit_again_header": "Käy toistekin",
+    "settings_pane_visit_again_body": "Firefox näyttää selaushistoriastasi palasia, jotka saatat haluta muistaa tai joissa haluat ehkä käydä.",
     "settings_pane_pocketstories_header": "Ykkösjutut",
+    "settings_pane_pocketstories_body": "Pocket, osa Mozilla-perhettä, auttaa löytämään laadukasta sisältöä, jota et ehkä olisi muuten löytänyt.",
     "settings_pane_done_button": "Valmis",
     "edit_topsites_button_text": "Muokkaa",
     "edit_topsites_button_label": "Muokkaa Ykkössivustot-osiota",
     "edit_topsites_showmore_button": "Näytä enemmän",
     "edit_topsites_showless_button": "Näytä vähemmän",
     "edit_topsites_done_button": "Valmis",
     "edit_topsites_pin_button": "Kiinnitä tämä sivusto",
     "edit_topsites_unpin_button": "Poista tämän sivuston kiinnitys",
@@ -1903,26 +2083,31 @@
     "topsites_form_url_placeholder": "Kirjoita tai liitä osoite",
     "topsites_form_add_button": "Lisää",
     "topsites_form_save_button": "Tallenna",
     "topsites_form_cancel_button": "Peruuta",
     "topsites_form_url_validation": "Kelvollinen osoite vaaditaan",
     "pocket_read_more": "Suositut aiheet:",
     "pocket_read_even_more": "Katso lisää juttuja",
     "pocket_feedback_header": "Netin parhaat palat, valikoitu yli 25 miljoonan ihmisen voimin.",
-    "pocket_send_feedback": "Lähetä palautetta"
+    "pocket_feedback_body": "Pocket, osa Mozilla-perhettä, auttaa löytämään laadukasta sisältöä, jota et ehkä olisi muuten löytänyt.",
+    "pocket_send_feedback": "Lähetä palautetta",
+    "topstories_empty_state": "Ei enempää suosituksia juuri nyt. Katso myöhemmin uudestaan lisää ykkösjuttuja lähteestä {provider}. Etkö malta odottaa? Valitse suosittu aihe ja löydä lisää hyviä juttuja ympäri verkkoa.",
+    "manual_migration_explanation": "Kokeile Firefoxia toisesta selaimesta tuotujen suosikkisivustojesi ja kirjanmerkkiesi kanssa.",
+    "manual_migration_cancel_button": "Ei kiitos",
+    "manual_migration_import_button": "Tuo nyt"
   },
   "fr": {
     "newtab_page_title": "Nouvel onglet",
     "default_label_loading": "Chargement…",
     "header_top_sites": "Sites les plus visités",
     "header_stories": "Articles populaires",
     "header_visit_again": "Visiter à nouveau",
     "header_bookmarks": "Marque-pages récents",
-    "header_recommended_by": "Recommandé par {provider}",
+    "header_recommended_by": "Recommandations par {provider}",
     "header_bookmarks_placeholder": "Vous ne possédez aucun marque-page pour l’instant.",
     "header_stories_from": "par",
     "type_label_visited": "Visité",
     "type_label_bookmarked": "Ajouté aux marque-pages",
     "type_label_synced": "Synchronisé depuis un autre appareil",
     "type_label_recommended": "Tendance",
     "type_label_open": "Ouvert",
     "type_label_topic": "Thème",
@@ -1935,17 +2120,17 @@
     "menu_action_open_private_window": "Ouvrir dans une nouvelle fenêtre privée",
     "menu_action_dismiss": "Retirer",
     "menu_action_delete": "Supprimer de l’historique",
     "menu_action_pin": "Épingler",
     "menu_action_unpin": "Détacher",
     "confirm_history_delete_p1": "Voulez-vous vraiment supprimer de l’historique toutes les occurrences de cette page ?",
     "confirm_history_delete_notice_p2": "Cette action est irréversible.",
     "menu_action_save_to_pocket": "Enregistrer dans Pocket",
-    "search_for_something_with": "Recherche pour {search_term} avec :",
+    "search_for_something_with": "Rechercher {search_term} avec :",
     "search_button": "Rechercher",
     "search_header": "Recherche {search_engine_name}",
     "search_web_placeholder": "Rechercher sur le Web",
     "search_settings": "Paramètres de recherche",
     "section_info_option": "Informations",
     "welcome_title": "Bienvenue sur la page Nouvel onglet",
     "welcome_body": "Firefox utilisera cet espace pour afficher des éléments pertinents, comme des marque-pages, des articles, des vidéos, et des pages que vous avez visitées, afin que vous les retrouviez facilement.",
     "welcome_label": "Identification des éléments-clés",
@@ -2163,24 +2348,26 @@
   },
   "gd": {
     "newtab_page_title": "Taba ùr",
     "default_label_loading": "’Ga luchdadh…",
     "header_top_sites": "Brod nan làrach",
     "header_stories": "Brod nan sgeul",
     "header_visit_again": "Tadhail a-rithist",
     "header_bookmarks": "Comharran-lìn o chionn goirid",
+    "header_recommended_by": "’Ga mholadh le {provider}",
     "header_bookmarks_placeholder": "Chan eil comharra-lìn sam bith agad fhathast.",
     "header_stories_from": "o",
     "type_label_visited": "Na thadhail thu air",
     "type_label_bookmarked": "’Nan comharran-lìn",
     "type_label_synced": "Sioncronaichte o uidheam eile",
     "type_label_recommended": "A’ treandadh",
     "type_label_open": "Fosgailte",
     "type_label_topic": "Cuspair",
+    "type_label_now": "An-dràsta",
     "menu_action_bookmark": "Comharra-lìn",
     "menu_action_remove_bookmark": "Thoir an comharra-lìn air falbh",
     "menu_action_copy_address": "Dèan lethbhreac dhen t-seòladh",
     "menu_action_email_link": "Cuir an ceangal air a’ phost-d…",
     "menu_action_open_new_window": "Fosgail ann an uinneag ùr",
     "menu_action_open_private_window": "Fosgail ann an uinneag phrìobhaideach ùr",
     "menu_action_dismiss": "Leig seachad",
     "menu_action_delete": "Sguab às an eachdraidh",
@@ -2189,16 +2376,17 @@
     "confirm_history_delete_p1": "A bheil thu cinnteach gu bheil thu airson gach ionstans na duilleige seo a sguabadh às an eachdraidh agad?",
     "confirm_history_delete_notice_p2": "Cha ghabh seo a neo-dhèanamh.",
     "menu_action_save_to_pocket": "Sàbhail sa phòcaid",
     "search_for_something_with": "Lorg {search_term} le:",
     "search_button": "Lorg",
     "search_header": "Lorg le {search_engine_name}",
     "search_web_placeholder": "Lorg air an lìon",
     "search_settings": "Atharraich roghainnean an luirg",
+    "section_info_option": "Fiosrachadh",
     "welcome_title": "Fàilte gun taba ùr",
     "welcome_body": "Seallaidh Firefox na comharran-lìn, artaigealan, videothan is duilleagan as iomchaidhe dhut, an fheadhainn air an do thadhail thu o chionn goirid, ach an ruig thu iad gu luath.",
     "welcome_label": "Ag aithneachadh nan highlights agad",
     "time_label_less_than_minute": "<1m",
     "time_label_minute": "{number}m",
     "time_label_hour": "{number}u",
     "time_label_day": "{number}l",
     "settings_pane_button_label": "Gnàthaich duilleag nan tabaichean ùra agad",
@@ -2233,17 +2421,21 @@
     "topsites_form_add_button": "Cuir ris",
     "topsites_form_save_button": "Sàbhail",
     "topsites_form_cancel_button": "Sguir dheth",
     "topsites_form_url_validation": "Tha feum air URL dligheach",
     "pocket_read_more": "Cuspairean fèillmhor:",
     "pocket_read_even_more": "Seall barrachd sgeul",
     "pocket_feedback_header": "Brod an eadar-lìn, air a dheasachadh le barrachd air 25 millean duine.",
     "pocket_feedback_body": "Pocket, ball de theaghlach Mozilla, a cheanglas tu ri susbaint fhìor-mhath nach biodh tu air fhaicinn air dòigh eile.",
-    "pocket_send_feedback": "Dè do bheachd air?"
+    "pocket_send_feedback": "Dè do bheachd air?",
+    "topstories_empty_state": "Sin na naidheachdan uile o {provider} an-dràsta ach bidh barrachd ann a dh’aithghearr. No thoir sùil air cuspair air a bheil fèill mhòr is leugh na tha a’ dol mun cuairt air an lìon an-dràsta.",
+    "manual_migration_explanation": "Feuch Firefox leis na làraichean is comharran-lìn as fhearr leat o bhrabhsair eile.",
+    "manual_migration_cancel_button": "Chan eil, tapadh leibh",
+    "manual_migration_import_button": "Ion-phortaich an-dràsta"
   },
   "gl": {},
   "gn": {},
   "gu-IN": {
     "newtab_page_title": "નવું ટૅબ",
     "default_label_loading": "લોડ કરી રહ્યું છે...",
     "header_top_sites": "ટોપ સાઇટ્સ",
     "header_highlights": "હાઇલાઇટ્સ",
@@ -2274,24 +2466,26 @@
   },
   "he": {
     "newtab_page_title": "לשונית חדשה",
     "default_label_loading": "בטעינה…",
     "header_top_sites": "אתרים מובילים",
     "header_stories": "סיפורים מובילים",
     "header_visit_again": "ביקור חוזר",
     "header_bookmarks": "סימניות אחרונות",
+    "header_recommended_by": "מומלץ על ידי {provider}",
     "header_bookmarks_placeholder": "אין לך סימניות עדיין.",
     "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": "הוספת סימניה",
     "menu_action_remove_bookmark": "הסרת סימניה",
     "menu_action_copy_address": "העתקת כתובת",
     "menu_action_email_link": "שליחת קישור בדוא״ל…",
     "menu_action_open_new_window": "פתיחה בחלון חדש",
     "menu_action_open_private_window": "פתיחה בלשונית פרטית חדשה",
     "menu_action_dismiss": "הסרה",
     "menu_action_delete": "מחיקה מההיסטוריה",
@@ -2300,16 +2494,17 @@
     "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_settings": "שינוי הגדרות חיפוש",
+    "section_info_option": "מידע",
     "welcome_title": "ברוכים הבאים לדף הלשונית החדשה",
     "welcome_body": "Firefox ישתמש באזור זה כדי להציג את הסימניות הרלוונטיות ביותר, מאמרים, סרטוני וידאו ודפים שביקרת בהם לאחרונה, כך שניתן יהיה לגשת אליהם שוב בקלות.",
     "welcome_label": "תחומי העניין שלך מזוהים",
     "time_label_less_than_minute": "פחות מדקה",
     "time_label_minute": "{number} דקות",
     "time_label_hour": "{number} שעות",
     "time_label_day": "{number} ימים",
     "settings_pane_button_label": "התאמה אישית של דף הלשונית החדשה שלך",
@@ -2326,35 +2521,38 @@
     "settings_pane_visit_again_body": "Firefox תציג לך חלקים מהיסטוריית הגלישה שלך שאולי יעניין אותך להיזכר בהם או לחזור אליהם.",
     "settings_pane_pocketstories_header": "סיפורים מובילים",
     "settings_pane_pocketstories_body": "Pocket, חלק ממשפחת Mozilla, יסייע לך להתחבר לתוכן באיכות גבוהה שיתכן שלא היה מגיע אליך בדרך אחרת.",
     "settings_pane_done_button": "סיום",
     "edit_topsites_button_text": "עריכה",
     "edit_topsites_button_label": "התאמת אגף האתרים המובילים שלך",
     "edit_topsites_showmore_button": "להציג יותר",
     "edit_topsites_showless_button": "להציג פחות",
-    "edit_topsites_done_button": "בוצע",
+    "edit_topsites_done_button": "סיום",
     "edit_topsites_pin_button": "נעיצת אתר זה",
     "edit_topsites_unpin_button": "ביטול הצמדת אתר זה",
     "edit_topsites_edit_button": "עריכת אתר זה",
-    "edit_topsites_dismiss_button": "התעלמות מאתר זה",
+    "edit_topsites_dismiss_button": "הסרת אתר זה",
     "edit_topsites_add_button": "הוספה",
     "topsites_form_add_header": "אתר מוביל חדש",
     "topsites_form_edit_header": "עריכת אתר מוביל",
     "topsites_form_title_placeholder": "נא להזין כותרת",
     "topsites_form_url_placeholder": "נא להקליד או להזין כתובת",
     "topsites_form_add_button": "הוספה",
     "topsites_form_save_button": "שמירה",
     "topsites_form_cancel_button": "ביטול",
     "topsites_form_url_validation": "נדרשת כתובת תקינה",
     "pocket_read_more": "נושאים פופולריים:",
     "pocket_read_even_more": "צפייה בחדשות נוספות",
     "pocket_feedback_header": "המיטב מרחבי האינטרנט, נאסף על ידי 25 מיליון אנשים.",
     "pocket_feedback_body": "Pocket, חלק ממשפחת Mozilla, יסייע לך להתחבר לתוכן באיכות גבוהה שיתכן שלא היה מגיע אליך בדרך אחרת.",
-    "pocket_send_feedback": "שליחת משוב"
+    "pocket_send_feedback": "שליחת משוב",
+    "manual_migration_explanation": "נסו את Firefox עם האתרים המועדפים עליך והסימניות שלך מדפדפן אחר.",
+    "manual_migration_cancel_button": "לא תודה",
+    "manual_migration_import_button": "ייבוא כעת"
   },
   "hi-IN": {
     "newtab_page_title": "नया टैब",
     "default_label_loading": "लोड हो रहा है…",
     "header_top_sites": "सर्वोच्च साइटें",
     "header_visit_again": "पुनः पधारें",
     "header_bookmarks": "हाल के पुस्तचिह्न",
     "header_stories_from": "के द्वारा",
@@ -2403,61 +2601,97 @@
     "edit_topsites_dismiss_button": "इस साइट को ख़ारिज करें",
     "pocket_read_even_more": "और कहानियाँ देखें",
     "pocket_send_feedback": "प्रतिक्रिया भेजें"
   },
   "hr": {
     "newtab_page_title": "Nova kartica",
     "default_label_loading": "Učitavanje…",
     "header_top_sites": "Najbolje stranice",
-    "header_highlights": "Istaknuto",
+    "header_stories": "Najbolje priče",
+    "header_visit_again": "Posjetite ponovno",
+    "header_bookmarks": "Nedavne zabilješke",
+    "header_recommended_by": "Preporučeno od {provider}",
+    "header_bookmarks_placeholder": "Još nemate niti jednu zabilješku.",
+    "header_stories_from": "od",
     "type_label_visited": "Posjećeno",
     "type_label_bookmarked": "Zabilježeno",
     "type_label_synced": "Sinkronizirano s drugog uređaja",
+    "type_label_recommended": "Popularno",
     "type_label_open": "Otvori",
     "type_label_topic": "Tema",
+    "type_label_now": "Sada",
     "menu_action_bookmark": "Zabilježi stranicu",
     "menu_action_remove_bookmark": "Ukloni zabilješku",
     "menu_action_copy_address": "Kopiraj adresu",
     "menu_action_email_link": "Pošalji poveznicu e-poštom…",
     "menu_action_open_new_window": "Otvori u novom prozoru",
     "menu_action_open_private_window": "Otvori u novom privatnom prozoru",
     "menu_action_dismiss": "Odbaci",
     "menu_action_delete": "Obriši iz povijesti",
+    "menu_action_pin": "Zakači",
+    "menu_action_unpin": "Otkači",
+    "confirm_history_delete_p1": "Jeste li sigurni da želite obrisati sve primjere ove stranice iz vaše povijesti?",
+    "confirm_history_delete_notice_p2": "Ova radnja je nepovratna.",
+    "menu_action_save_to_pocket": "Spremi u Pocket",
     "search_for_something_with": "Traži {search_term} s:",
     "search_button": "Traži",
     "search_header": "{search_engine_name} pretraživanje",
     "search_web_placeholder": "Pretraži web",
     "search_settings": "Promijeni postavke pretraživanja",
+    "section_info_option": "Info",
     "welcome_title": "Dobro došli u novu karticu",
     "welcome_body": "Firefox će koristiti ovaj prostor kako bi vam pokazao najbitnije zabilješke, članke, video uratke i stranice koje ste nedavno posjetili, tako da se možete lako vratiti na njih.",
     "welcome_label": "Identificiranje istaknutog",
     "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": "Prilagodite svoju početnu stranicu nove kartice",
     "settings_pane_header": "Postavke nove kartice",
     "settings_pane_body": "Odaberite što ćete vidjeti kada otvorite novu karticu.",
     "settings_pane_search_header": "Traži",
     "settings_pane_search_body": "Pretražite Web iz nove kartice.",
     "settings_pane_topsites_header": "Najbolje stranice",
     "settings_pane_topsites_body": "Pristupite stranicama koje najčešće posjećujete.",
     "settings_pane_topsites_options_showmore": "Prikaži dva reda",
-    "settings_pane_highlights_header": "Istaknuto",
-    "settings_pane_highlights_body": "Osvrnite se na nedavno posjećene stranice i nove zabilješke.",
+    "settings_pane_bookmarks_header": "Nedavne zabilješke",
+    "settings_pane_bookmarks_body": "Vaše novo stvorene zabilješke na jednom praktičnom mjestu.",
+    "settings_pane_visit_again_header": "Posjetite ponovno",
+    "settings_pane_visit_again_body": "Firefox će vam prikazati dijelove vaše povijesti pretraživanja koje možda želite zapamtiti ili posjetiti ponovno.",
+    "settings_pane_pocketstories_header": "Najbolje priče",
+    "settings_pane_pocketstories_body": "Pocket, dio Mozilla obitelji, povezat će vas sa sadržajem visoke kvalitete koji možda inače ne biste pronašli.",
     "settings_pane_done_button": "Gotovo",
     "edit_topsites_button_text": "Uredi",
     "edit_topsites_button_label": "Prilagodite odjel s najboljim stranicama",
     "edit_topsites_showmore_button": "Prikaži više",
     "edit_topsites_showless_button": "Prikaži manje",
     "edit_topsites_done_button": "Gotovo",
     "edit_topsites_pin_button": "Zakači stranicu",
+    "edit_topsites_unpin_button": "Otkači ovu stranicu",
     "edit_topsites_edit_button": "Uredi ovu stranicu",
-    "edit_topsites_dismiss_button": "Odbaci stranicu"
+    "edit_topsites_dismiss_button": "Odbaci stranicu",
+    "edit_topsites_add_button": "Dodaj",
+    "topsites_form_add_header": "Nova najbolja stranica",
+    "topsites_form_edit_header": "Uredi najbolju stranicu",
+    "topsites_form_title_placeholder": "Unesi naslov",
+    "topsites_form_url_placeholder": "Utipkajte ili zalijepite URL",
+    "topsites_form_add_button": "Dodaj",
+    "topsites_form_save_button": "Spremi",
+    "topsites_form_cancel_button": "Otkaži",
+    "topsites_form_url_validation": "Potrebno je unijeti ispravan URL",
+    "pocket_read_more": "Popularne teme:",
+    "pocket_read_even_more": "Prikaži više priča",
+    "pocket_feedback_header": "Najbolje od interneta, birano od preko 25 miliona ljudi.",
+    "pocket_feedback_body": "Pocket, dio Mozilla obitelji, povezat će vas sa sadržajem visoke kvalitete koji možda inače ne biste pronašli.",
+    "pocket_send_feedback": "Pošaljite povratnu informaciju",
+    "topstories_empty_state": "Provjerite kasnije za više najpopularnijih priča od {provider}. Ne možete čekati? Odaberite popularne teme kako biste pronašli više kvalitetnih priča s cijelog weba.",
+    "manual_migration_explanation": "Probajte Firefox s vašim omiljenim stranicama i zabilješkama iz drugog pretraživača.",
+    "manual_migration_cancel_button": "Ne hvala",
+    "manual_migration_import_button": "Uvezi sada"
   },
   "hsb": {
     "newtab_page_title": "Nowy rajtark",
     "default_label_loading": "Začituje so…",
     "header_top_sites": "Najhusćišo wopytane sydła",
     "header_stories": "Najhusćišo přečitane zdźělenki",
     "header_visit_again": "Hišće raz wopytać",
     "header_bookmarks": "Najnowše zapołožki",
@@ -3462,17 +3696,102 @@
     "manual_migration_cancel_button": "Ačiū, ne",
     "manual_migration_import_button": "Importuoti dabar"
   },
   "ltg": {},
   "lv": {
     "newtab_page_title": "Jauna cilne"
   },
   "mai": {},
-  "mk": {},
+  "mk": {
+    "newtab_page_title": "Ново јазиче",
+    "default_label_loading": "Се вчитува…",
+    "header_top_sites": "Врвни мрежни места",
+    "header_stories": "Врвни написи",
+    "header_visit_again": "Посети повторно",
+    "header_bookmarks": "Скорешни обележувачи",
+    "header_recommended_by": "Препорачано од {provider}",
+    "header_bookmarks_placeholder": "Сѐ уште немате обележувачи.",
+    "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": "Обележувач",
+    "menu_action_remove_bookmark": "Отстрани обележувач",
+    "menu_action_copy_address": "Копирај адреса",
+    "menu_action_email_link": "Испрати врска…",
+    "menu_action_open_new_window": "Отвори во нов прозорец",
+    "menu_action_open_private_window": "Отвори во нов приватен прозорец",
+    "menu_action_dismiss": "Откажи",
+    "menu_action_delete": "Избриши од историја",
+    "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_settings": "Промени поставувања за пребарување",
+    "section_info_option": "Инфо",
+    "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": "Прилагодете ја страницата на Вашето Ново јазиче",
+    "settings_pane_header": "Преференци за Ново јазиче",
+    "settings_pane_body": "Изберете што ќе гледате кога ќе отворите ново јазиче.",
+    "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 ќе прикаже делови од Вашата историја на прелистување кои можеби би сакале да ги запомните или пак да им се навратите.",
+    "settings_pane_pocketstories_header": "Врвни написи",
+    "settings_pane_pocketstories_body": "Pocket, дел од семејството на Mozilla, ќе Ви помогне да стигнете до високо-квалитетни содржини кои можеби не би ги откриле на друг начин.",
+    "settings_pane_done_button": "Готово",
+    "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": "Уреди го ова место",
+    "edit_topsites_dismiss_button": "Отфрли го ова место",
+    "edit_topsites_add_button": "Додај",
+    "topsites_form_add_header": "Ново врвно мрежно место",
+    "topsites_form_edit_header": "Уреди врвно мрежно место",
+    "topsites_form_title_placeholder": "Внесете наслов",
+    "topsites_form_url_placeholder": "Внесете или вметнете URL",
+    "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": "Најдоброто од Интернет, одбрано од повеќе од 25 милиони луѓе.",
+    "pocket_feedback_body": "Pocket, дел од семејството на Mozilla, ќе Ви помогне да стигнете до високо-квалитетни содржини кои можеби не би ги откриле на друг начин.",
+    "pocket_send_feedback": "Остави коментар",
+    "topstories_empty_state": "Имате видено сѐ! Навратете се подоцна за нови содржини од {provider}. Не можете да чекате? Изберете популарна тема и откријте уште одлични содржини ширум Интернет.",
+    "manual_migration_explanation": "Пробајте го Firefox со Вашите омилени мрежни места и обележувачи од друг прелистувач.",
+    "manual_migration_cancel_button": "Не, благодарам",
+    "manual_migration_import_button": "Увези сега"
+  },
   "ml": {
     "newtab_page_title": "പുതിയ ടാബ്",
     "default_label_loading": "ലോഡ്ചെയ്യുന്നു…",
     "header_top_sites": "മികച്ച സൈറ്റുകൾ",
     "header_highlights": "ഹൈലൈറ്റുകൾ",
     "header_stories": "മികച്ച ലേഖനങ്ങൾ",
     "header_stories_from": "എവിടെ നിന്നും",
     "type_label_visited": "സന്ദർശിച്ചത്‌",
@@ -3798,40 +4117,81 @@
     "manual_migration_explanation": "Prøv Firefox med dine favorittnettsteder og bokmerker fra en annen nettleser.",
     "manual_migration_cancel_button": "Nei takk",
     "manual_migration_import_button": "Importer nå"
   },
   "ne-NP": {
     "newtab_page_title": "नयाँ ट्याब",
     "default_label_loading": "लोड हुदैँछ...",
     "header_top_sites": "शीर्ष साइटहरु",
-    "header_highlights": "विशेषताहरू",
+    "header_stories": "शीर्ष साइटहरु",
+    "header_visit_again": "फेरि भ्रमण गर्नुहोस्",
+    "header_bookmarks": "भर्खरैका पुस्तकचिनोहरु",
+    "header_recommended_by": "{provider} द्वारा सिफारिस गरिएको",
+    "header_bookmarks_placeholder": "तपाइँसँग अहिले सम्म कुनै पुस्तकचिनोहरु छैन ।",
+    "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": "पुस्तकचिनो",
     "menu_action_remove_bookmark": "पुस्तकचिनो हटाउनुहोस्",
     "menu_action_copy_address": "ठेगाना प्रतिलिपि गर्नुहोस्",
     "menu_action_email_link": "लिङ्कलाई इमेल गर्नुहोस्...",
     "menu_action_open_new_window": "नयाँ सञ्झ्यालमा खोल्नुहोस्",
     "menu_action_open_private_window": "नयाँ निजी सञ्झ्यालमा खोल्नुहोस्",
     "menu_action_dismiss": "खारेज गर्नुहोस्",
     "menu_action_delete": "इतिहासबाट मेट्नुहोस्",
+    "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_settings": "खोजी सेटिङ परिवर्तन गर्नुहोस्",
+    "section_info_option": "जानकारी",
     "welcome_title": "नयाँ ट्याबमा स्वागत छ",
+    "welcome_body": "Firefoxले यस ठाउँको प्रयोग तपाईंको सबैभन्दा सान्दर्भिक पुस्तकचिनो, लेखहरू, भिडियोहरू, र तपाईंले हालै भ्रमण गर्नु भएको पृष्ठहरूलाई राख्न प्रयोग गर्दछ, जसले गर्दा तपाइँ तिनीहरूलाई सजिलै भेटाउन सक्नुहुनेछ ।",
     "welcome_label": "तपाईँका विशेषताहरु पत्ता लगाउँदै",
     "time_label_less_than_minute": "< १ मिनेट",
     "time_label_minute": "{number} मिनेट",
     "time_label_hour": "{number} घण्टा",
-    "time_label_day": "{number} दिन"
+    "time_label_day": "{number} दिन",
+    "settings_pane_button_label": "तपाईंको नयाँ ट्याब पृष्ठ अनुकूलन गर्नुहोस्",
+    "settings_pane_header": "नयाँ ट्याब प्राथमिकताहरू",
+    "settings_pane_body": "तपाईंले नयाँ ट्याब खोल्ने बेलामा के देख्नु चाहनुहुन्छ भन्ने छनौट गर्नुहोस् ।",
+    "settings_pane_search_header": "खोजी गर्नुहोस्",
+    "settings_pane_search_body": "तपाईंको नयाँ ट्याबबाट वेबमा खोज्नुहोस् ।",
+    "settings_pane_topsites_body": "तपाईले धेरै भ्रमण गर्नुभएका वेबसाइटहरूमा पहुँच गर्नुहोस् ।",
+    "settings_pane_topsites_options_showmore": "दुई पङ्क्तिहरू देखाउनुहोस्",
+    "settings_pane_bookmarks_header": "भर्खरैका पुस्तकचिनोहरु",
+    "settings_pane_bookmarks_body": "तपाईंको नयाँ सिर्जना गरिएको पुस्तकचिनोहरुहरू एउटा सजिलो स्थानमा ।",
+    "settings_pane_visit_again_header": "फेरि भ्रमण गर्नुहोस्",
+    "settings_pane_pocketstories_header": "शीर्ष कथाहरू",
+    "settings_pane_done_button": "सम्पन्न भयो",
+    "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": "यस साइटलाई सम्पादन गर्नुहोस्",
+    "edit_topsites_dismiss_button": "यस साइटलाई खारेज गर्नुहोस्",
+    "edit_topsites_add_button": "थप्नुहोस्",
+    "topsites_form_add_header": "नयाँ शीर्ष साइट",
+    "topsites_form_edit_header": "शीर्ष साइट सम्पादन गर्नुहोस्",
+    "manual_migration_cancel_button": "पर्दैन, धन्यबाद",
+    "manual_migration_import_button": "अहिले आयात गर्नुहोस्"
   },
   "nl": {
     "newtab_page_title": "Nieuw tabblad",
     "default_label_loading": "Laden…",
     "header_top_sites": "Topwebsites",
     "header_stories": "Topverhalen",
     "header_visit_again": "Nogmaals bezoeken",
     "header_bookmarks": "Recente bladwijzers",
@@ -3932,17 +4292,17 @@
     "type_label_topic": "Emne",
     "type_label_now": "No",
     "menu_action_bookmark": "Bokmerke",
     "menu_action_remove_bookmark": "Fjern bokmerke",
     "menu_action_copy_address": "Kopier adresse",
     "menu_action_email_link": "E-postlenke…",
     "menu_action_open_new_window": "Opne i nytt vindauge",
     "menu_action_open_private_window": "Opne i eit nytt privat vindauge",
-    "menu_action_dismiss": "Avslå",
+    "menu_action_dismiss": "Avvis",
     "menu_action_delete": "Slett frå historikk",
     "menu_action_pin": "Fest",
     "menu_action_unpin": "L:ys",
     "confirm_history_delete_p1": "Er du sikker på at du vil slette alle førekomstar av denne sida frå historikken din?",
     "confirm_history_delete_notice_p2": "Denne handlinga kan ikkje angrast.",
     "menu_action_save_to_pocket": "Lagre til Pocket",
     "search_for_something_with": "Søk etter {search_term} med:",
     "search_button": "Søk",
@@ -4188,17 +4548,17 @@
     "settings_pane_topsites_header": "Sites preferidos",
     "settings_pane_topsites_body": "Acesse os sites que você mais visita.",
     "settings_pane_topsites_options_showmore": "Mostrar duas linhas",
     "settings_pane_bookmarks_header": "Favoritos recentes",
     "settings_pane_bookmarks_body": "Seus favoritos recém criados em uma posição acessível.",
     "settings_pane_visit_again_header": "Visite novamente",
     "settings_pane_visit_again_body": "Firefox irá exibir a você partes do seu histórico de navegação que você pode querer relembrar ou acessar novamente.",
     "settings_pane_pocketstories_header": "Histórias populares",
-    "settings_pane_pocketstories_body": "O Pocket, parte da família Mozilla, irá ajudar a conecta-se a conteúdo de alta qualidade que talvez não tenha encontrado de outra forma.",
+    "settings_pane_pocketstories_body": "O Pocket, parte da família Mozilla, ajudará a conectá-lo a conteúdo de alta qualidade que talvez você não encontre de outra forma.",
     "settings_pane_done_button": "Concluído",
     "edit_topsites_button_text": "Editar",
     "edit_topsites_button_label": "Personalizar a sua seção de sites preferidos",
     "edit_topsites_showmore_button": "Mostrar mais",
     "edit_topsites_showless_button": "Mostrar menos",
     "edit_topsites_done_button": "Concluído",
     "edit_topsites_pin_button": "Fixar este site",
     "edit_topsites_unpin_button": "Desafixar este site",
@@ -4211,17 +4571,17 @@
     "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",
     "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_feedback_body": "O Pocket, parte da família Mozilla, irá ajudar a conecta-se a conteúdo de alta qualidade que talvez não tenha encontrado de outra forma.",
+    "pocket_feedback_body": "O Pocket, parte da família Mozilla, ajudará a conectá-lo a conteúdo de alta qualidade que talvez você não encontre de outra forma.",
     "pocket_send_feedback": "Enviar feedback",
     "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_explanation": "Use o Firefox com seus sites favoritos de outro navegador.",
     "manual_migration_cancel_button": "Não, obrigado",
     "manual_migration_import_button": "Importar agora"
   },
   "pt-PT": {
     "newtab_page_title": "Novo separador",
@@ -4394,53 +4754,55 @@
     "manual_migration_explanation": "Utilisescha Firefox cun tias paginas preferidas e tes segnapaginas d'in auter navigatur.",
     "manual_migration_cancel_button": "Na, grazia",
     "manual_migration_import_button": "Importar ussa"
   },
   "ro": {
     "newtab_page_title": "Filă nouă",
     "default_label_loading": "Se încarcă…",
     "header_top_sites": "Site-uri de top",
-    "header_highlights": "Evidențieri",
+    "header_recommended_by": "Recomandat de {provider}",
     "header_stories_from": "de la",
     "type_label_visited": "Vizitate",
     "type_label_bookmarked": "Însemnat",
     "type_label_synced": "Sincronizat de pe alt dispozitiv",
     "type_label_open": "Deschise",
     "type_label_topic": "Subiect",
+    "type_label_now": "Acum",
     "menu_action_bookmark": "Însemnează",
     "menu_action_remove_bookmark": "Elimină semnul de carte",
     "menu_action_copy_address": "Copiază adresa",
     "menu_action_email_link": "Deschide linkul…",
     "menu_action_open_new_window": "Deschide într-o fereastră nouă",
     "menu_action_open_private_window": "Deschide într-o fereastră privată nouă",
     "menu_action_dismiss": "Înlătură",
     "menu_action_delete": "Șterge din istoric",
+    "confirm_history_delete_notice_p2": "Această acțiune este ireversibilă.",
+    "menu_action_save_to_pocket": "Salvează în Pocket",
     "search_for_something_with": "Caută {search_term} cu: ",
     "search_button": "Caută",
     "search_header": "Căutare {search_engine_name}",
     "search_web_placeholder": "Caută pe web",
     "search_settings": "Schimbă setările de căutare",
+    "section_info_option": "Informații",
     "welcome_title": "Bun venit în noua filă",
     "welcome_body": "Firefox va folosi acest spațiu pentru a arăta cele mai relevante semne de carte, articole, videouri și pagini vizitate recent pentru a reveni la acestea ușor.",
     "welcome_label": "Se identifică evidențierile tale",
     "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": "Particularizează pagina de filă nouă",
     "settings_pane_header": "Preferințe pentru filă nouă",
     "settings_pane_body": "Alege ce să vezi la deschiderea unei noi file.",
     "settings_pane_search_header": "Caută",
     "settings_pane_search_body": "Caută pe web din noua filă.",
     "settings_pane_topsites_header": "Site-uri de top",
     "settings_pane_topsites_body": "Accesează site-urile pe care le vizitezi mai des.",
     "settings_pane_topsites_options_showmore": "Arată două rânduri",
-    "settings_pane_highlights_header": "Evidențieri",
-    "settings_pane_highlights_body": "Privește înapoi la istoricul de navigare recent și noile semne de carte create.",
     "settings_pane_done_button": "Gata",
     "edit_topsites_button_text": "Editează",
     "edit_topsites_button_label": "Particularizează secțiunea site-urilor de top",
     "edit_topsites_showmore_button": "Arată mai mult",
     "edit_topsites_showless_button": "Arată mai puțin",
     "edit_topsites_done_button": "Gata",
     "edit_topsites_pin_button": "Fixează acest site",
     "edit_topsites_edit_button": "Editează acest site",
@@ -4450,17 +4812,18 @@
     "topsites_form_edit_header": "Editează site-ul de top",
     "topsites_form_title_placeholder": "Introdu un titlu",
     "topsites_form_url_placeholder": "Tastează sau lipește un URL",
     "topsites_form_add_button": "Adaugă",
     "topsites_form_save_button": "Salvează",
     "topsites_form_cancel_button": "Renunță",
     "topsites_form_url_validation": "URL valid necesar",
     "pocket_read_more": "Subiecte populare:",
-    "pocket_send_feedback": "Trimite feedback"
+    "pocket_send_feedback": "Trimite feedback",
+    "manual_migration_cancel_button": "Nu, mulțumesc"
   },
   "ru": {
     "newtab_page_title": "Новая вкладка",
     "default_label_loading": "Загрузка…",
     "header_top_sites": "Топ сайтов",
     "header_stories": "Топ статей",
     "header_visit_again": "Посетить снова",
     "header_bookmarks": "Недавние закладки",
@@ -4671,18 +5034,18 @@
     "welcome_label": "Zbiranje poudarkov",
     "time_label_less_than_minute": "<1 min",
     "time_label_minute": "{number} min",
     "time_label_hour": "{number} ur",
     "time_label_day": "{number} dni",
     "settings_pane_button_label": "Prilagodite stran novega zavihka",
     "settings_pane_header": "Nastavitve novega zavihka",
     "settings_pane_body": "Izberite, kaj naj se prikaže, ko odprete nov zavihek.",
-    "settings_pane_search_header": "Išči",
-    "settings_pane_search_body": "Iščite po spletu s strani novega zavihka.",
+    "settings_pane_search_header": "Iskanje",
+    "settings_pane_search_body": "Iščite po spletu z novega zavihka.",
     "settings_pane_topsites_header": "Glavne strani",
     "settings_pane_topsites_body": "Priročen dostop do najbolj obiskanih strani.",
     "settings_pane_topsites_options_showmore": "Prikaži dve vrsti",
     "settings_pane_bookmarks_header": "Nedavni zaznamki",
     "settings_pane_bookmarks_body": "Vaši novo ustvarjeni zaznamki na enem mestu.",
     "settings_pane_visit_again_header": "Obiščite znova",
     "settings_pane_visit_again_body": "Firefox vam bo prikazoval dele zgodovine brskanja, ki bi se jih morda želeli spomniti ali se nanje vrniti.",
     "settings_pane_pocketstories_header": "Glavne vesti",
@@ -4941,24 +5304,26 @@
   },
   "ta": {
     "newtab_page_title": "புதிய கீற்று",
     "default_label_loading": "ஏற்றுகிறது…",
     "header_top_sites": "சிறந்த தளங்கள்",
     "header_stories": "முக்கிய கதைகள்",
     "header_visit_again": "மீண்டும் வருக",
     "header_bookmarks": "சமீபத்திய புத்தகக்குறிகள்",
+    "header_recommended_by": "{provider} என்பவரால் பரிந்துரைக்கப்பட்டது",
     "header_bookmarks_placeholder": "நீங்கள் புத்தகக்குறிகளைக் கொண்டிருக்கவில்லை .",
     "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": "புத்தகக்குறி",
     "menu_action_remove_bookmark": "புத்தகக்குறியை நீக்கு",
     "menu_action_copy_address": "முகவரியை நகலெடு",
     "menu_action_email_link": "மின்னஞ்சல் தொடுப்பு…",
     "menu_action_open_new_window": "ஒரு புதிய சாளரத்தில் திற",
     "menu_action_open_private_window": "ஒரு புதிய அந்தரங்க சாளரத்தில் திற",
     "menu_action_dismiss": "வெளியேற்று",
     "menu_action_delete": "வரலாற்றிலருந்து அழி",
@@ -4967,16 +5332,17 @@
     "confirm_history_delete_p1": "இப்பக்கத்தை உங்களின் வரலாற்றிலிருந்து முழுமையாக நீக்க விரும்புகிறீர்களா?",
     "confirm_history_delete_notice_p2": "இச்செயலை மீட்க முடியாது.",
     "menu_action_save_to_pocket": "பாக்கட்டில் சேமி",
     "search_for_something_with": "{search_term} சொல்லிற்காகத் தேடு:",
     "search_button": "தேடு",
     "search_header": "{search_engine_name} தேடுபொறியில் தேடு",
     "search_web_placeholder": "இணையத்தில் தேடு",
     "search_settings": "தேடல் அமைவுகளை மாற்று",
+    "section_info_option": "தகவல்",
     "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": "உங்கள் புதிய கீற்றுப் பக்கத்தை விருப்பமை",
@@ -5011,52 +5377,57 @@
     "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_feedback_body": "Pocket, ஒரு மொசில்லா குடும்ப உறுப்பினராக, உயர்தர உள்ளடக்கங்களுடன் இணைய உதவுகிறது, இது இல்லையேல் அது சாத்தியமாகது.",
-    "pocket_send_feedback": "கருத்துகளைத் தெறிவிக்கவும்"
+    "pocket_send_feedback": "கருத்துகளைத் தெறிவிக்கவும்",
+    "manual_migration_cancel_button": "பரவாயில்லை",
+    "manual_migration_import_button": "இப்போது இறக்கு"
   },
   "ta-LK": {},
   "te": {
     "newtab_page_title": "కొత్త ట్యాబు",
     "default_label_loading": "వస్తోంది…",
     "header_top_sites": "మేటి సైట్లు",
     "header_stories": "ముఖ్య కథనాలు",
     "header_visit_again": "మళ్లీ సందర్శించండి",
     "header_bookmarks": "ఇటీవలి ఇష్టాంశములు",
-    "header_bookmarks_placeholder": "మీకు ఇంకా బుక్మార్క్లు లేవు.",
+    "header_recommended_by": "{provider}చే సిఫార్సు చేయబడినది",
+    "header_bookmarks_placeholder": "మీకు ఇంకా ఎటువంటి ఇష్టాంశాలు లేవు.",
     "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": "ఇష్టాంశము",
     "menu_action_remove_bookmark": "ఇష్టాంశాన్ని తొలగించు",
     "menu_action_copy_address": "చిరునామా కాపీ చెయ్యండి",
     "menu_action_email_link": "ఈమెయిలు లింకు…",
     "menu_action_open_new_window": "కొత్త విండోలో తెరువు",
     "menu_action_open_private_window": "కొత్త వ్యక్తిగత విండోలో తెరువు",
     "menu_action_dismiss": "విస్మరించు",
     "menu_action_delete": "చరిత్ర నుంచి తీసివేయి",
-    "menu_action_pin": "పిన్",
-    "menu_action_unpin": "పిన్ తీసివేయి",
+    "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_settings": "శోధన అమరికలు మార్చు",
+    "section_info_option": "సమాచారం",
     "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": "మీ కొత్త ట్యాబు పేజీని మలచుకోండి",
@@ -5075,33 +5446,37 @@
     "settings_pane_pocketstories_body": "Mozilla కుటుంబం యొక్క Pocket, మీరు కనుగొనలేకపోయే అధిక-నాణ్యత విషయముకి మిమ్మల్ని అనుసంధానించడానికి సహాయపడుతుంది.",
     "settings_pane_done_button": "పూర్తయింది",
     "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_unpin_button": "ఈ సైటుకి పిన్నుని తీసివేయండి",
     "edit_topsites_edit_button": "ఈ సైటును మార్చు",
     "edit_topsites_dismiss_button": "ఈ సైటుని తీసివేయి",
     "edit_topsites_add_button": "జోడించు",
     "topsites_form_add_header": "కొత్త టాప్ సైట్",
     "topsites_form_edit_header": "టాప్ సైట్ను సవరించండి",
     "topsites_form_title_placeholder": "శీర్షికను నమోదు చేయండి",
     "topsites_form_url_placeholder": "URL ను టైప్ చేయండి లేదా అతికించండి",
     "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": "వెబ్లో అత్యుత్తమమైనది, 25 మిలియన్లకు పైగా ప్రజలు పర్యవేక్షించినవి.",
     "pocket_feedback_body": "Mozilla కుటుంబం యొక్క Pocket, మీరు కనుగొనలేకపోయే అధిక-నాణ్యత విషయముకి మిమ్మల్ని అనుసంధానించడానికి సహాయపడుతుంది.",
-    "pocket_send_feedback": "అభిప్రాయాన్ని పంపండి"
+    "pocket_send_feedback": "అభిప్రాయాన్ని పంపండి",
+    "topstories_empty_state": "మీరు పట్టుబడ్డారు. {provider} నుండి మరింత అగ్ర కథనాల కోసం తరువాత తనిఖీ చేయండి. వేచి ఉండలేరా? జాలములోని అంతటి నుండి మరింత గొప్ప కథనాలను కనుగొనడానికి ప్రసిద్ధ అంశం ఎంచుకోండి.",
+    "manual_migration_explanation": "మరొక విహరణి నుండి మీకు ఇష్టమైన సైట్లు మరియు ఇష్టంశాలతో Firefox ను ప్రయత్నించండి.",
+    "manual_migration_cancel_button": "అడిగినందుకు ధన్యవాదాలు, వద్దు",
+    "manual_migration_import_button": "ఇప్పుడే దిగుమతి చేయండి"
   },
   "th": {
     "newtab_page_title": "แท็บใหม่",
     "default_label_loading": "กำลังโหลด…",
     "header_top_sites": "ไซต์เด่น",
     "header_stories": "เรื่องราวเด่น",
     "header_visit_again": "เยี่ยมชมอีกครั้ง",
     "header_bookmarks": "ที่คั่นหน้าเมื่อเร็ว ๆ นี้",
@@ -5119,17 +5494,17 @@
     "menu_action_remove_bookmark": "เอาที่คั่นหน้าออก",
     "menu_action_copy_address": "คัดลอกที่อยู่",
     "menu_action_email_link": "ส่งอีเมลลิงก์…",
     "menu_action_open_new_window": "เปิดในหน้าต่างใหม่",
     "menu_action_open_private_window": "เปิดในหน้าต่างส่วนตัวใหม่",
     "menu_action_dismiss": "ยกเลิก",
     "menu_action_delete": "ลบออกจากประวัติ",
     "menu_action_pin": "ปักหมุด",
-    "menu_action_unpin": "ถอดหมุด",
+    "menu_action_unpin": "ถอนหมุด",
     "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_settings": "เปลี่ยนการตั้งค่าการค้นหา",
     "section_info_option": "ข้อมูล",
@@ -5154,17 +5529,17 @@
     "settings_pane_pocketstories_body": "Pocket ส่วนหนึ่งของครอบครัว Mozilla จะช่วยเชื่อมต่อคุณกับเนื้อหาคุณภาพสูงที่คุณอาจไม่พบที่อื่น",
     "settings_pane_done_button": "เสร็จสิ้น",
     "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_unpin_button": "ถอนหมุดไซต์นี้",
     "edit_topsites_edit_button": "แก้ไขไซต์นี้",
     "edit_topsites_dismiss_button": "ไม่สนใจไซต์นี้",
     "edit_topsites_add_button": "เพิ่ม",
     "topsites_form_add_header": "ไซต์เด่นใหม่",
     "topsites_form_edit_header": "แก้ไขไซต์เด่น",
     "topsites_form_title_placeholder": "ป้อนชื่อเรื่อง",
     "topsites_form_url_placeholder": "พิมพ์หรือวาง URL",
     "topsites_form_add_button": "เพิ่ม",
@@ -5339,17 +5714,17 @@
     "menu_action_email_link": "Надіслати посилання…",
     "menu_action_open_new_window": "Відкрити в новому вікні",
     "menu_action_open_private_window": "Відкрити в приватному вікні",
     "menu_action_dismiss": "Сховати",
     "menu_action_delete": "Видалити з історії",
     "menu_action_pin": "Прикріпити",
     "menu_action_unpin": "Відкріпити",
     "confirm_history_delete_p1": "Ви справді хочете видалити всі записи про цю сторінку з історії?",
-    "confirm_history_delete_notice_p2": "Цю дію неможливо відмінити.",
+    "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_settings": "Змінити налаштування пошуку",
     "section_info_option": "Інфо",
     "welcome_title": "Вітаємо на новій вкладці",
@@ -5404,40 +5779,44 @@
   },
   "ur": {
     "newtab_page_title": "نیا ٹیب",
     "default_label_loading": "لوڈ کر رہا ہے…",
     "header_top_sites": "بہترین سائٹیں",
     "header_stories": "بہترین کہانیاں",
     "header_visit_again": "دوبارہ دورہ کریں",
     "header_bookmarks": "حالیہ نشانیاں",
+    "header_recommended_by": "{provider} کی جانب سے تجویز کردہ",
     "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": "نشانی",
     "menu_action_remove_bookmark": "نشانى ہٹائيں",
     "menu_action_copy_address": "پتہ نقل کریں",
     "menu_action_email_link": "ربط ای میل کریں…",
     "menu_action_open_new_window": "نئے دریچے میں کھولیں",
     "menu_action_open_private_window": "نئی نجی دریچے میں کھولیں",
     "menu_action_dismiss": "برخاست کریں",
     "menu_action_delete": "تاریخ سے حذف کریں",
     "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_settings": "تلاش  کی سیٹکگیں تبدیل کریں",
+    "section_info_option": "معلومات",
     "welcome_title": "نئے ٹیب میں خوش آمدید",
     "welcome_body": "اس جگہ کا استعمال کرنے ہوئے Firefox آپکی متعلقہ نشانیاں، عبارات، وڈیوز اور صفحات جن کا حال ہی میں ص آُپ نے دورہ کیا ہے دکھائے گا۔ تاکہ آپ ان تک واپس آسانی سے پہنچ سکیں۔",
     "welcome_label": "آپکی جھلکیوں کی نشاندہی کر رہا ہے",
     "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": "اپنے نئے ٹیب کہ صفحہ کی تخصیص کریں",
@@ -5450,16 +5829,17 @@
     "settings_pane_topsites_options_showmore": "دو قطاریں دکھائیں",
     "settings_pane_bookmarks_header": "حالیہ نشانیاں",
     "settings_pane_visit_again_header": "دوبارہ دورہ کریں",
     "settings_pane_pocketstories_header": "بہترین کہانیاں",
     "settings_pane_done_button": "ہوگیا",
     "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": "اس سائٹ کی تدوین کریں",
     "edit_topsites_dismiss_button": "اس سائٹ کو برخاست کریں",
     "edit_topsites_add_button": "آظافہ کریں",
     "topsites_form_add_header": "نئی بہترین سائٹ",
     "topsites_form_edit_header": "بہترین سائٹٹ کیی تدوین کریں",
@@ -5467,35 +5847,41 @@
     "topsites_form_url_placeholder": "ٹائپ کریں یا ایک URL چسباں کریں",
     "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_body": "Pocket ایک جصہ ہے Mozilla کے خاندان کا،آپ کو اعلی میعار کے مواد سے جڑنے میں مدد دے گا جو شاید آپ بصورت دیگر نہ ڈھونڈ سکتے۔",
-    "pocket_send_feedback": "جواب الجواب ارسال کریں"
+    "pocket_send_feedback": "جواب الجواب ارسال کریں",
+    "manual_migration_cancel_button": "نہیں شکریہ",
+    "manual_migration_import_button": "ابھی درآمد کری"
   },
-  "uz": {},
+  "uz": {
+    "pocket_send_feedback": "Fikr-mulohaza joʻnatish",
+    "manual_migration_cancel_button": "Yoʻq, kerak emas"
+  },
   "vi": {
     "newtab_page_title": "Tab mới",
     "default_label_loading": "Đang tải…",
     "header_top_sites": "Trang web hàng đầu",
     "header_stories": "Câu chuyện hàng đầu",
     "header_visit_again": "Truy cập lại",
     "header_bookmarks": "Các bookmark gần đây",
     "header_recommended_by": "Được đề nghị bởi {provider}",
     "header_bookmarks_placeholder": "Bạn chưa có bookmark nào.",
     "header_stories_from": "từ",
     "type_label_visited": "Đã truy cập",
     "type_label_bookmarked": "Đã được đánh dấu",
     "type_label_synced": "Đồng bộ từ thiết bị khác",
     "type_label_recommended": "Xu hướng",
     "type_label_open": "Mở",
     "type_label_topic": "Chủ đề",
+    "type_label_now": "Bây giờ",
     "menu_action_bookmark": "Đánh dấu",
     "menu_action_remove_bookmark": "Xóa đánh dấu",
     "menu_action_copy_address": "Chép địa chỉ",
     "menu_action_email_link": "Liên kết Email...",
     "menu_action_open_new_window": "Mở trong Cửa Sổ Mới",
     "menu_action_open_private_window": "Mở trong cửa sổ riêng tư mới",
     "menu_action_dismiss": "Bỏ qua",
     "menu_action_delete": "Xóa từ lịch xử",
@@ -5509,19 +5895,22 @@
     "search_web_placeholder": "Tìm trên mạng",
     "search_settings": "Thay đổi thiết lập tìm kiếm",
     "section_info_option": "Thông tin",
     "welcome_title": "Chào mừng đến với tab mới",
     "time_label_less_than_minute": "<1phút",
     "time_label_minute": "{number}phút",
     "time_label_hour": "{number}giờ",
     "time_label_day": "{number}ngày",
+    "settings_pane_button_label": "Tùy biến trang Tab mới",
     "settings_pane_header": "Tùy chỉnh cho tab mới",
     "settings_pane_body": "Chọn cái bạn muốn tải khi một tab mới được mở ra.",
     "settings_pane_search_header": "Tìm kiếm",
+    "settings_pane_topsites_body": "Truy cập vào các trang web mà bạn truy cập vào nhiều nhất.",
+    "settings_pane_topsites_options_showmore": "Hiển thị hai hàng",
     "settings_pane_done_button": "Xong",
     "edit_topsites_button_text": "Chỉnh sửa",
     "edit_topsites_showmore_button": "Xem thêm",
     "edit_topsites_showless_button": "Hiển thị ngắn gọn lại",
     "edit_topsites_done_button": "Xong",
     "edit_topsites_pin_button": "Ghim trang này",
     "edit_topsites_unpin_button": "Bỏ ghim trang này",
     "edit_topsites_edit_button": "Chỉnh sửa trang web này",
@@ -5535,17 +5924,17 @@
     "manual_migration_cancel_button": "Không, cảm ơn"
   },
   "wo": {},
   "xh": {},
   "zh-CN": {
     "newtab_page_title": "新标签页",
     "default_label_loading": "正在载入…",
     "header_top_sites": "常用网站",
-    "header_stories": "热门报道",
+    "header_stories": "热门文章",
     "header_visit_again": "再次造访",
     "header_bookmarks": "最近的书签",
     "header_recommended_by": "{provider} 推荐",
     "header_bookmarks_placeholder": "您还没有最近的书签。",
     "header_stories_from": "出自",
     "type_label_visited": "曾经访问",
     "type_label_bookmarked": "已加书签",
     "type_label_synced": "从其他设备同步而来",
@@ -5553,18 +5942,18 @@
     "type_label_open": "打开",
     "type_label_topic": "主题",
     "type_label_now": "现在",
     "menu_action_bookmark": "添加书签",
     "menu_action_remove_bookmark": "移除书签",
     "menu_action_copy_address": "复制地址",
     "menu_action_email_link": "用邮件发送链接…",
     "menu_action_open_new_window": "在新窗口中打开",
-    "menu_action_open_private_window": "在新的隐私浏览窗口中打开",
-    "menu_action_dismiss": "消除",
+    "menu_action_open_private_window": "在新的隐私窗口中打开",
+    "menu_action_dismiss": "隐藏",
     "menu_action_delete": "从历史记录中删除",
     "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": "搜索",
@@ -5576,28 +5965,28 @@
     "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": "定制您的新标签页",
     "settings_pane_header": "新标签页选项",
-    "settings_pane_body": "选择您在新标签页上看到哪些组件。",
+    "settings_pane_body": "选择打开新标签页时想看到什么。",
     "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 在此显示您可能想记住或再次访问的浏览记录。",
+    "settings_pane_visit_again_body": "Firefox 在此显示您可能想记住或将再次访问的浏览记录。",
     "settings_pane_pocketstories_header": "热门报道",
-    "settings_pane_pocketstories_body": "Pocket,Mozilla 家族的一员,它可以帮助您找到更多不易发现的高品质内容。",
+    "settings_pane_pocketstories_body": "Pocket 是 Mozilla 家族的一员,可以将您未发现的高品质内容带到眼前。",
     "settings_pane_done_button": "完成",
     "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": "取消固定此网站",
@@ -5608,21 +5997,21 @@
     "topsites_form_edit_header": "编辑常用网站",
     "topsites_form_title_placeholder": "输入标题",
     "topsites_form_url_placeholder": "输入或粘贴一个网址",
     "topsites_form_add_button": "添加",
     "topsites_form_save_button": "保存",
     "topsites_form_cancel_button": "取消",
     "topsites_form_url_validation": "需要有效的网址",
     "pocket_read_more": "热门主题:",
-    "pocket_read_even_more": "查看更多报道",
-    "pocket_feedback_header": "超过2500万人构成的互联网。",
-    "pocket_feedback_body": "Pocket,Mozilla 家族的一员,它可以帮助您找到更多不易发现的高品质内容。",
+    "pocket_read_even_more": "查看更多文章",
+    "pocket_feedback_header": "由超过 2500 万人挑选出来的网上精华内容。",
+    "pocket_feedback_body": "Pocket 是 Mozilla 家族的一员,可以将您未发现的高品质内容带到眼前。",
     "pocket_send_feedback": "发送反馈",
-    "topstories_empty_state": "已经都看过了。请过会再来查看 {provider} 提供的热门故事。不想等待?选择一个热门话题,找到网络上的更多好故事。",
+    "topstories_empty_state": "所有文章都读完啦!晚点再来,{provider} 将推荐更多热门文章。等不及了?选择一个热门话题,找到更多网上的好文章。",
     "manual_migration_explanation": "将您在其他浏览器中常用的网站和书签导入 Firefox,体验别具一格的浏览器。",
     "manual_migration_cancel_button": "不用了",
     "manual_migration_import_button": "立即导入"
   },
   "zh-TW": {
     "newtab_page_title": "新分頁",
     "default_label_loading": "載入中…",
     "header_top_sites": "熱門網站",
--- a/browser/extensions/activity-stream/lib/ActivityStream.jsm
+++ b/browser/extensions/activity-stream/lib/ActivityStream.jsm
@@ -46,25 +46,25 @@ const PREFS_CONFIG = new Map([
   }],
   ["feeds.section.topstories.options", {
     title: "Configuration options for top stories feed",
     // This is a dynamic pref as it depends on the feed being shown or not
     getValue: args => JSON.stringify({
       api_key_pref: "extensions.pocket.oAuthConsumerKey",
       // Use the opposite value as what default value the feed would have used
       hidden: !PREFS_CONFIG.get("feeds.section.topstories").getValue(args),
-      learn_more_endpoint: "https://getpocket.com/firefox_learnmore?src=ff_newtab",
+      learn_more_endpoint: "https://getpocket.cdn.mozilla.net/firefox_learnmore?src=ff_newtab",
       provider_description: "pocket_feedback_body",
       provider_icon: "pocket",
       provider_name: "Pocket",
-      read_more_endpoint: "https://getpocket.com/explore/trending?src=ff_new_tab",
-      stories_endpoint: `https://getpocket.com/v3/firefox/global-recs?consumer_key=$apiKey&locale_lang=${args.locale}`,
+      read_more_endpoint: "https://getpocket.cdn.mozilla.net/explore/trending?src=ff_new_tab",
+      stories_endpoint: `https://getpocket.cdn.mozilla.net/v3/firefox/global-recs?version=2&consumer_key=$apiKey&locale_lang=${args.locale}`,
       stories_referrer: "https://getpocket.com/recommendations",
       survey_link: "https://www.surveymonkey.com/r/newtabffx",
-      topics_endpoint: `https://getpocket.com/v3/firefox/trending-topics?consumer_key=$apiKey&locale_lang=${args.locale}`
+      topics_endpoint: `https://getpocket.cdn.mozilla.net/v3/firefox/trending-topics?version=2&consumer_key=$apiKey&locale_lang=${args.locale}`
     })
   }],
   ["migrationExpired", {
     title: "Boolean flag that decides whether to show the migration message or not.",
     value: false
   }],
   ["migrationLastShownDate", {
     title: "Timestamp when migration message was last shown. In seconds.",
--- a/browser/extensions/activity-stream/lib/ActivityStreamMessageChannel.jsm
+++ b/browser/extensions/activity-stream/lib/ActivityStreamMessageChannel.jsm
@@ -132,18 +132,20 @@ this.ActivityStreamMessageChannel = clas
     this.channel = channel || new RemotePages([ABOUT_HOME_URL, ABOUT_NEW_TAB_URL]);
     this.channel.addMessageListener("RemotePage:Init", this.onNewTabInit);
     this.channel.addMessageListener("RemotePage:Load", this.onNewTabLoad);
     this.channel.addMessageListener("RemotePage:Unload", this.onNewTabUnload);
     this.channel.addMessageListener(this.incomingMessageName, this.onMessage);
 
     // Some pages might have already loaded, so we won't get the usual message
     for (const {loaded, portID} of this.channel.messagePorts) {
+      const simulatedMsg = {target: {portID}};
+      this.onNewTabInit(simulatedMsg);
       if (loaded) {
-        this.onNewTabLoad({target: {portID}});
+        this.onNewTabLoad(simulatedMsg);
       }
     }
   }
 
   /**
    * destroyChannel - Destroys the RemotePages channel
    */
   destroyChannel() {
--- a/browser/extensions/activity-stream/lib/LocalizationFeed.jsm
+++ b/browser/extensions/activity-stream/lib/LocalizationFeed.jsm
@@ -25,31 +25,28 @@ this.LocalizationFeed = class Localizati
 
     this.updateLocale();
   }
   uninit() {
     Services.obs.removeObserver(this, LOCALES_CHANGE_TOPIC);
   }
 
   updateLocale() {
-    // Just take the first element in the result array, as it should be
-    // the best locale
-    let locale = Services.locale.negotiateLanguages(
+    // Order locales based on what we have available with fallback always first
+    const locales = Services.locale.negotiateLanguages(
       Services.locale.getAppLocalesAsLangTags(), // desired locales
       Object.keys(this.allStrings), // available locales
       DEFAULT_LOCALE // fallback
-    )[0];
-
-    let strings = this.allStrings[locale];
+    ).reverse();
 
-    // Use the default strings for any that are missing
-    if (locale !== DEFAULT_LOCALE) {
-      strings = Object.assign({}, this.allStrings[DEFAULT_LOCALE], strings || {});
-    }
+    // Start with default (first) locale then merge in strings of better locales
+    const strings = Object.assign({}, ...locales.map(l => this.allStrings[l]));
 
+    // Use the best (last) locale as the primary locale
+    const locale = locales.pop();
     this.store.dispatch(ac.BroadcastToContent({
       type: at.LOCALE_UPDATED,
       data: {
         locale,
         strings
       }
     }));
   }
--- a/browser/extensions/activity-stream/lib/SectionsManager.jsm
+++ b/browser/extensions/activity-stream/lib/SectionsManager.jsm
@@ -2,92 +2,173 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {utils: Cu} = Components;
 const {actionCreators: ac, actionTypes: at} = Cu.import("resource://activity-stream/common/Actions.jsm", {});
 Cu.import("resource://gre/modules/EventEmitter.jsm");
 
+/*
+ * Generators for built in sections, keyed by the pref name for their feed.
+ * Built in sections may depend on options stored as serialised JSON in the pref
+ * `${feed_pref_name}.options`.
+ */
+const BUILT_IN_SECTIONS = {
+  "feeds.section.topstories": options => ({
+    id: "topstories",
+    pref: {
+      titleString: {id: "header_recommended_by", values: {provider: options.provider_name}},
+      descString: {id: options.provider_description}
+    },
+    shouldHidePref:  options.hidden,
+    eventSource: "TOP_STORIES",
+    icon: options.provider_icon,
+    title: {id: "header_recommended_by", values: {provider: options.provider_name}},
+    maxRows: 1,
+    contextMenuOptions: ["CheckBookmark", "SaveToPocket", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"],
+    infoOption: {
+      header: {id: "pocket_feedback_header"},
+      body: {id: options.provider_description},
+      link: {href: options.survey_link, id: "pocket_send_feedback"}
+    },
+    emptyState: {
+      message: {id: "topstories_empty_state", values: {provider: options.provider_name}},
+      icon: "check"
+    }
+  })
+};
+
 const SectionsManager = {
   ACTIONS_TO_PROXY: ["SYSTEM_TICK", "NEW_TAB_LOAD"],
   initialized: false,
-  sections: new Set(),
+  sections: new Map(),
+  init(prefs = {}) {
+    for (const feedPrefName of Object.keys(BUILT_IN_SECTIONS)) {
+      const optionsPrefName = `${feedPrefName}.options`;
+      this.addBuiltInSection(feedPrefName, prefs[optionsPrefName]);
+    }
+    this.initialized = true;
+    this.emit(this.INIT);
+  },
+  addBuiltInSection(feedPrefName, optionsPrefValue = "{}") {
+    let options;
+    try {
+      options = JSON.parse(optionsPrefValue);
+    } catch (e) {
+      options = {};
+      Cu.reportError("Problem parsing options pref", e);
+    }
+    const section = BUILT_IN_SECTIONS[feedPrefName](options);
+    section.pref.feed = feedPrefName;
+    this.addSection(section.id, Object.assign(section, {options}));
+  },
   addSection(id, options) {
-    this.sections.add(id);
+    this.sections.set(id, options);
     this.emit(this.ADD_SECTION, id, options);
   },
   removeSection(id) {
     this.emit(this.REMOVE_SECTION, id);
     this.sections.delete(id);
   },
-  updateRows(id, rows, shouldBroadcast) {
+  enableSection(id) {
+    this.updateSection(id, {enabled: true}, true);
+  },
+  disableSection(id) {
+    this.updateSection(id, {enabled: false, rows: []}, true);
+  },
+  updateSection(id, options, shouldBroadcast) {
     if (this.sections.has(id)) {
-      this.emit(this.UPDATE_ROWS, id, rows, shouldBroadcast);
+      this.sections.set(id, Object.assign(this.sections.get(id), options));
+      this.emit(this.UPDATE_SECTION, id, options, shouldBroadcast);
+    }
+  },
+  onceInitialized(callback) {
+    if (this.initialized) {
+      callback();
+    } else {
+      this.once(this.INIT, callback);
     }
   }
 };
 
 for (const action of [
   "ACTION_DISPATCHED",
   "ADD_SECTION",
   "REMOVE_SECTION",
-  "UPDATE_ROWS",
+  "UPDATE_SECTION",
   "INIT",
   "UNINIT"
 ]) {
   SectionsManager[action] = action;
 }
 
 EventEmitter.decorate(SectionsManager);
 
 class SectionsFeed {
   constructor() {
+    this.init = this.init.bind(this);
     this.onAddSection = this.onAddSection.bind(this);
     this.onRemoveSection = this.onRemoveSection.bind(this);
-    this.onUpdateRows = this.onUpdateRows.bind(this);
+    this.onUpdateSection = this.onUpdateSection.bind(this);
   }
 
   init() {
     SectionsManager.on(SectionsManager.ADD_SECTION, this.onAddSection);
     SectionsManager.on(SectionsManager.REMOVE_SECTION, this.onRemoveSection);
-    SectionsManager.on(SectionsManager.UPDATE_ROWS, this.onUpdateRows);
-    SectionsManager.initialized = true;
-    SectionsManager.emit(SectionsManager.INIT);
+    SectionsManager.on(SectionsManager.UPDATE_SECTION, this.onUpdateSection);
+    // Catch any sections that have already been added
+    SectionsManager.sections.forEach((section, id) =>
+      this.onAddSection(SectionsManager.ADD_SECTION, id, section));
   }
 
   uninit() {
     SectionsManager.initialized = false;
     SectionsManager.emit(SectionsManager.UNINIT);
     SectionsManager.off(SectionsManager.ADD_SECTION, this.onAddSection);
     SectionsManager.off(SectionsManager.REMOVE_SECTION, this.onRemoveSection);
-    SectionsManager.off(SectionsManager.UPDATE_ROWS, this.onUpdateRows);
+    SectionsManager.off(SectionsManager.UPDATE_SECTION, this.onUpdateSection);
   }
 
   onAddSection(event, id, options) {
     if (options) {
       this.store.dispatch(ac.BroadcastToContent({type: at.SECTION_REGISTER, data: Object.assign({id}, options)}));
     }
   }
 
   onRemoveSection(event, id) {
     this.store.dispatch(ac.BroadcastToContent({type: at.SECTION_DEREGISTER, data: id}));
   }
 
-  onUpdateRows(event, id, rows, shouldBroadcast = false) {
-    if (rows) {
-      const action = {type: at.SECTION_ROWS_UPDATE, data: {id, rows}};
+  onUpdateSection(event, id, options, shouldBroadcast = false) {
+    if (options) {
+      const action = {type: at.SECTION_UPDATE, data: Object.assign(options, {id})};
       this.store.dispatch(shouldBroadcast ? ac.BroadcastToContent(action) : action);
     }
   }
 
   onAction(action) {
     switch (action.type) {
       case at.INIT:
-        this.init();
+        SectionsManager.onceInitialized(this.init);
+        break;
+      // Wait for pref values, as some sections have options stored in prefs
+      case at.PREFS_INITIAL_VALUES:
+        SectionsManager.init(action.data);
+        break;
+      case at.PREF_CHANGED:
+        if (action.data && action.data.name.match(/^feeds.section.(\S+).options$/i)) {
+          SectionsManager.addBuiltInSection(action.data.name.slice(0, -8), action.data.value);
+        }
+        break;
+      case at.SECTION_DISABLE:
+        SectionsManager.disableSection(action.data);
+        break;
+      case at.SECTION_ENABLE:
+        SectionsManager.enableSection(action.data);
         break;
     }
     if (SectionsManager.ACTIONS_TO_PROXY.includes(action.type) && SectionsManager.sections.size > 0) {
       SectionsManager.emit(SectionsManager.ACTION_DISPATCHED, action.type, action.data);
     }
   }
 }
 
new file mode 100644
--- /dev/null
+++ b/browser/extensions/activity-stream/lib/ShortURL.jsm
@@ -0,0 +1,53 @@
+const {utils: Cu} = Components;
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "IDNService", "@mozilla.org/network/idn-service;1", "nsIIDNService");
+
+Cu.importGlobalProperties(["URL"]);
+
+/**
+ * Properly convert internationalized domain names.
+ * @param {string} host Domain hostname.
+ * @returns {string} Hostname suitable to be displayed.
+ */
+function handleIDNHost(hostname) {
+  try {
+    return IDNService.convertToDisplayIDN(hostname, {});
+  } catch (e) {
+    // If something goes wrong (e.g. host is an IP address) just fail back
+    // to the full domain.
+    return hostname;
+  }
+}
+
+/**
+ * shortURL - Creates a short version of a link's url, used for display purposes
+ *            e.g. {url: http://www.foosite.com, eTLD: "com"}  =>  "foosite"
+ *
+ * @param  {obj} link A link object
+ *         {str} link.url (required)- The url of the link
+ *         {str} link.eTLD (required) - The tld of the link
+ *               e.g. for https://foo.org, the tld would be "org"
+ *               Note that this property is added in various queries for ActivityStream
+ *               via Services.eTLD.getPublicSuffix
+ *         {str} link.hostname (optional) - The hostname of the url
+ *               e.g. for http://www.hello.com/foo/bar, the hostname would be "www.hello.com"
+ *         {str} link.title (optional) - The title of the link
+ * @return {str}   A short url
+ */
+this.shortURL = function shortURL(link) {
+  if (!link.url && !link.hostname) {
+    return "";
+  }
+  const {eTLD} = link;
+  const asciiHost = (link.hostname || new URL(link.url).hostname).replace(/^www\./i, "");
+  const hostname = handleIDNHost(asciiHost);
+
+  // Remove the eTLD (e.g., com, net) and the preceding period from the hostname
+  const eTLDLength = (eTLD || "").length || (hostname.match(/\.com$/) && 3);
+  const eTLDExtra = eTLDLength > 0 ? -(eTLDLength + 1) : Infinity;
+  // If URL and hostname are not present fallback to page title.
+  return hostname.slice(0, eTLDExtra).toLowerCase() || hostname || link.title || link.url;
+};
+
+this.EXPORTED_SYMBOLS = ["shortURL"];
--- a/browser/extensions/activity-stream/lib/TopSitesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/TopSitesFeed.jsm
@@ -5,26 +5,27 @@
 
 const {utils: Cu} = Components;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const {actionCreators: ac, actionTypes: at} = Cu.import("resource://activity-stream/common/Actions.jsm", {});
 const {TippyTopProvider} = Cu.import("resource://activity-stream/lib/TippyTopProvider.jsm", {});
 const {insertPinned, TOP_SITES_SHOWMORE_LENGTH} = Cu.import("resource://activity-stream/common/Reducers.jsm", {});
 const {Dedupe} = Cu.import("resource://activity-stream/common/Dedupe.jsm", {});
-const {shortURL} = Cu.import("resource://activity-stream/common/ShortURL.jsm", {});
+const {shortURL} = Cu.import("resource://activity-stream/lib/ShortURL.jsm", {});
 
 XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
   "resource://gre/modules/NewTabUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Screenshots",
   "resource://activity-stream/lib/Screenshots.jsm");
 
 const UPDATE_TIME = 15 * 60 * 1000; // 15 minutes
 const DEFAULT_SITES_PREF = "default.sites";
 const DEFAULT_TOP_SITES = [];
+const FRECENCY_THRESHOLD = 100; // 1 visit (skip first-run/one-time pages)
 
 this.TopSitesFeed = class TopSitesFeed {
   constructor() {
     this.lastUpdated = 0;
     this._tippyTopProvider = new TippyTopProvider();
     this.dedupe = new Dedupe(this._dedupeKey);
   }
   _dedupeKey(site) {
@@ -57,17 +58,19 @@ this.TopSitesFeed = class TopSitesFeed {
     pinned = pinned.map(site => site && Object.assign({}, site, {
       isDefault: defaultUrls.indexOf(site.url) !== -1,
       hostname: shortURL(site)
     }));
 
     if (!frecent) {
       frecent = [];
     } else {
-      frecent = frecent.filter(link => link && link.type !== "affiliate");
+      // Get the best history links that pass the frecency threshold
+      frecent = frecent.filter(link => link && link.type !== "affiliate" &&
+        link.frecency > FRECENCY_THRESHOLD);
     }
 
     // Group together websites that require deduping.
     let topsitesGroup = [];
     for (const group of [pinned, frecent, notBlockedDefaultSites]) {
       topsitesGroup.push(group.filter(site => site).map(site => Object.assign({}, site, {hostname: shortURL(site)})));
     }
 
@@ -149,16 +152,19 @@ this.TopSitesFeed = class TopSitesFeed {
           (Date.now() - this.lastUpdated >= UPDATE_TIME)
         ) {
           this.refresh(action.meta.fromTarget);
         }
         break;
       case at.PLACES_HISTORY_CLEARED:
         this.refresh();
         break;
+      case at.BLOCK_URL: // Topsite blocked, we want to get a new one in.
+        this.refresh();
+        break;
       case at.PREF_CHANGED:
         if (action.data.name === DEFAULT_SITES_PREF) {
           this.refreshDefaults(action.data.value);
         }
         break;
       case at.PREFS_INITIAL_VALUES:
         this.refreshDefaults(action.data[DEFAULT_SITES_PREF]);
         break;
--- a/browser/extensions/activity-stream/lib/TopStoriesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/TopStoriesFeed.jsm
@@ -4,109 +4,88 @@
 "use strict";
 
 const {utils: Cu} = Components;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NewTabUtils.jsm");
 Cu.importGlobalProperties(["fetch"]);
 
-const {actionCreators: ac, actionTypes: at} = Cu.import("resource://activity-stream/common/Actions.jsm", {});
+const {actionTypes: at} = Cu.import("resource://activity-stream/common/Actions.jsm", {});
 const {Prefs} = Cu.import("resource://activity-stream/lib/ActivityStreamPrefs.jsm", {});
-const {shortURL} = Cu.import("resource://activity-stream/common/ShortURL.jsm", {});
+const {shortURL} = Cu.import("resource://activity-stream/lib/ShortURL.jsm", {});
+const {SectionsManager} = Cu.import("resource://activity-stream/lib/SectionsManager.jsm", {});
 
 const STORIES_UPDATE_TIME = 30 * 60 * 1000; // 30 minutes
 const TOPICS_UPDATE_TIME = 3 * 60 * 60 * 1000; // 3 hours
 const STORIES_NOW_THRESHOLD = 24 * 60 * 60 * 1000; // 24 hours
-const SECTION_ID = "TopStories";
+const SECTION_ID = "topstories";
 const FEED_PREF = "feeds.section.topstories";
-const SECTION_OPTIONS_PREF = "feeds.section.topstories.options";
 
 this.TopStoriesFeed = class TopStoriesFeed {
 
   init() {
+    this.storiesLastUpdated = 0;
+    this.topicsLastUpdated = 0;
+
+    SectionsManager.onceInitialized(this.parseOptions.bind(this));
+  }
+
+  parseOptions() {
+    SectionsManager.enableSection(SECTION_ID);
+    const options = SectionsManager.sections.get(SECTION_ID).options;
     try {
-      this.storiesLastUpdated = 0;
-      this.topicsLastUpdated = 0;
-
-      const prefs = new Prefs();
-      const options = JSON.parse(prefs.get(SECTION_OPTIONS_PREF));
       const apiKey = this._getApiKeyFromPref(options.api_key_pref);
       this.stories_endpoint = this._produceFinalEndpointUrl(options.stories_endpoint, apiKey);
       this.topics_endpoint = this._produceFinalEndpointUrl(options.topics_endpoint, apiKey);
 
       this.read_more_endpoint = options.read_more_endpoint;
       this.stories_referrer = options.stories_referrer;
 
-      // TODO https://github.com/mozilla/activity-stream/issues/2902
-      const sectionOptions = {
-        id: SECTION_ID,
-        eventSource: "TOP_STORIES",
-        icon: options.provider_icon,
-        title: {id: "header_recommended_by", values: {provider: options.provider_name}},
-        rows: [],
-        maxRows: 1,
-        contextMenuOptions: ["CheckBookmark", "SaveToPocket", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"],
-        infoOption: {
-          header: {id: "pocket_feedback_header"},
-          body: {id: options.provider_description},
-          link: {
-            href: options.survey_link,
-            id: "pocket_send_feedback"
-          }
-        },
-        emptyState: {
-          message: {id: "topstories_empty_state", values: {provider: options.provider_name}},
-          icon: "check"
-        }
-      };
-      this.store.dispatch(ac.BroadcastToContent({type: at.SECTION_REGISTER, data: sectionOptions}));
-
       this.fetchStories();
       this.fetchTopics();
     } catch (e) {
       Cu.reportError(`Problem initializing top stories feed: ${e.message}`);
     }
   }
 
   uninit() {
-    this.store.dispatch(ac.BroadcastToContent({type: at.SECTION_DEREGISTER, data: SECTION_ID}));
+    SectionsManager.disableSection(SECTION_ID);
   }
 
   async fetchStories() {
     if (this.stories_endpoint) {
       const stories = await fetch(this.stories_endpoint)
         .then(response => {
           if (response.ok) {
             return response.text();
           }
           throw new Error(`Stories endpoint returned unexpected status: ${response.status}`);
         })
         .then(body => {
-          let items = JSON.parse(body).list;
+          let items = JSON.parse(body).recommendations;
           items = items
-            .filter(s => !NewTabUtils.blockedLinks.isBlocked({"url": s.dedupe_url}))
+            .filter(s => !NewTabUtils.blockedLinks.isBlocked({"url": s.url}))
             .map(s => ({
               "guid": s.id,
-              "hostname": shortURL(Object.assign({}, s, {url: s.dedupe_url})),
+              "hostname": shortURL(Object.assign({}, s, {url: s.url})),
               "type": (Date.now() - (s.published_timestamp * 1000)) <= STORIES_NOW_THRESHOLD ? "now" : "trending",
               "title": s.title,
               "description": s.excerpt,
               "image": this._normalizeUrl(s.image_src),
               "referrer": this.stories_referrer,
-              "url": s.dedupe_url,
-              "eTLD": this._addETLD(s.dedupe_url)
+              "url": s.url,
+              "eTLD": this._addETLD(s.url)
             }));
           return items;
         })
         .catch(error => Cu.reportError(`Failed to fetch content: ${error.message}`));
 
       if (stories) {
-        this.dispatchUpdateEvent(this.storiesLastUpdated,
-          {"type": at.SECTION_ROWS_UPDATE, "data": {"id": SECTION_ID, "rows": stories}});
+        this.dispatchUpdateEvent(this.storiesLastUpdated, {rows: stories});
         this.storiesLastUpdated = Date.now();
       }
     }
   }
 
   async fetchTopics() {
     if (this.topics_endpoint) {
       const topics = await fetch(this.topics_endpoint)
@@ -115,29 +94,24 @@ this.TopStoriesFeed = class TopStoriesFe
             return response.text();
           }
           throw new Error(`Topics endpoint returned unexpected status: ${response.status}`);
         })
         .then(body => JSON.parse(body).topics)
         .catch(error => Cu.reportError(`Failed to fetch topics: ${error.message}`));
 
       if (topics) {
-        this.dispatchUpdateEvent(this.topicsLastUpdated,
-          {"type": at.SECTION_ROWS_UPDATE, "data": {"id": SECTION_ID, "topics": topics, "read_more_endpoint": this.read_more_endpoint}});
+        this.dispatchUpdateEvent(this.topicsLastUpdated, {topics, read_more_endpoint: this.read_more_endpoint});
         this.topicsLastUpdated = Date.now();
       }
     }
   }
 
-  dispatchUpdateEvent(lastUpdated, evt) {
-    if (lastUpdated === 0) {
-      this.store.dispatch(ac.BroadcastToContent(evt));
-    } else {
-      this.store.dispatch(evt);
-    }
+  dispatchUpdateEvent(lastUpdated, data) {
+    SectionsManager.updateSection(SECTION_ID, data, lastUpdated === 0);
   }
 
   _getApiKeyFromPref(apiKeyPref) {
     if (!apiKeyPref) {
       return apiKeyPref;
     }
 
     return new Prefs().get(apiKeyPref) || Services.prefs.getCharPref(apiKeyPref);
@@ -186,23 +160,17 @@ this.TopStoriesFeed = class TopStoriesFe
       case at.UNINIT:
         this.uninit();
         break;
       case at.FEED_INIT:
         if (action.data === FEED_PREF) {
           this.init();
         }
         break;
-      case at.PREF_CHANGED:
-        if (action.data.name === SECTION_OPTIONS_PREF) {
-          this.init();
-        }
-        break;
     }
   }
 };
 
 this.STORIES_UPDATE_TIME = STORIES_UPDATE_TIME;
 this.TOPICS_UPDATE_TIME = TOPICS_UPDATE_TIME;
 this.SECTION_ID = SECTION_ID;
 this.FEED_PREF = FEED_PREF;
-this.SECTION_OPTIONS_PREF = SECTION_OPTIONS_PREF;
-this.EXPORTED_SYMBOLS = ["TopStoriesFeed", "STORIES_UPDATE_TIME", "TOPICS_UPDATE_TIME", "SECTION_ID", "FEED_PREF", "SECTION_OPTIONS_PREF"];
+this.EXPORTED_SYMBOLS = ["TopStoriesFeed", "STORIES_UPDATE_TIME", "TOPICS_UPDATE_TIME", "SECTION_ID", "FEED_PREF"];
--- a/browser/extensions/activity-stream/test/functional/mochitest/browser.ini
+++ b/browser/extensions/activity-stream/test/functional/mochitest/browser.ini
@@ -1,9 +1,8 @@
 [DEFAULT]
 support-files =
   blue_page.html
   head.js
 
 [browser_as_load_location.js]
 [browser_as_render.js]
 [browser_getScreenshots.js]
-skip-if=true # issue 2851
--- a/browser/extensions/activity-stream/test/functional/mochitest/browser_getScreenshots.js
+++ b/browser/extensions/activity-stream/test/functional/mochitest/browser_getScreenshots.js
@@ -5,18 +5,16 @@
 
 let Cu = Components.utils;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 // a blue page
 const TEST_URL = "https://example.com/browser/browser/extensions/activity-stream/test/functional/mochitest/blue_page.html";
 const XHTMLNS = "http://www.w3.org/1999/xhtml";
 
-SpecialPowers.pushPrefEnv({set: [["browser.pagethumbnails.capturing_disabled", false]]});
-
 XPCOMUtils.defineLazyModuleGetter(this, "Screenshots", "resource://activity-stream/lib/Screenshots.jsm");
 
 function get_pixels_for_data_uri(dataURI, width, height) {
   return new Promise(resolve => {
     // get the pixels out of the screenshot that we just took
     let img = document.createElementNS(XHTMLNS, "img");
     img.setAttribute("src", dataURI);
     img.addEventListener("load", () => {
@@ -27,16 +25,18 @@ function get_pixels_for_data_uri(dataURI
       ctx.drawImage(img, 0, 0, width, height);
       const result = ctx.getImageData(0, 0, width, height).data;
       resolve(result);
     }, {once: true});
   });
 }
 
 add_task(async function test_screenshot() {
+  await SpecialPowers.pushPrefEnv({set: [["browser.pagethumbnails.capturing_disabled", false]]});
+
   // take a screenshot of a blue page and save it as a data URI
   const screenshotAsDataURI = await Screenshots.getScreenshotForURL(TEST_URL);
   let pixels = await get_pixels_for_data_uri(screenshotAsDataURI, 10, 10);
   let rgbaCount = {r: 0, g: 0, b: 0, a: 0};
   while (pixels.length) {
     // break the pixels into arrays of 4 components [red, green, blue, alpha]
     let [r, g, b, a, ...rest] = pixels;
     pixels = rest;
--- a/browser/extensions/activity-stream/test/unit/common/Reducers.test.js
+++ b/browser/extensions/activity-stream/test/unit/common/Reducers.test.js
@@ -205,17 +205,18 @@ describe("Reducers", () => {
   describe("Sections", () => {
     let oldState;
 
     beforeEach(() => {
       oldState = new Array(5).fill(null).map((v, i) => ({
         id: `foo_bar_${i}`,
         title: `Foo Bar ${i}`,
         initialized: false,
-        rows: [{url: "www.foo.bar"}, {url: "www.other.url"}]
+        rows: [{url: "www.foo.bar"}, {url: "www.other.url"}],
+        order: i
       }));
     });
 
     it("should return INITIAL_STATE by default", () => {
       assert.equal(INITIAL_STATE.Sections, Sections(undefined, {type: "non_existent"}));
     });
     it("should remove the correct section on SECTION_DEREGISTER", () => {
       const newState = Sections(oldState, {type: at.SECTION_DEREGISTER, data: "foo_bar_2"});
@@ -225,41 +226,64 @@ describe("Reducers", () => {
     });
     it("should add a section on SECTION_REGISTER if it doesn't already exist", () => {
       const action = {type: at.SECTION_REGISTER, data: {id: "foo_bar_5", title: "Foo Bar 5"}};
       const newState = Sections(oldState, action);
       assert.lengthOf(newState, 6);
       const insertedSection = newState.find(section => section.id === "foo_bar_5");
       assert.propertyVal(insertedSection, "title", action.data.title);
     });
+    it("should ensure sections are sorted by property `order` (increasing) on SECTION_REGISTER", () => {
+      let newState = [];
+      const state = Object.assign([], oldState);
+      state.forEach(section => {
+        Object.assign(section, {order: 5 - section.order});
+        const action = {type: at.SECTION_REGISTER, data: section};
+        newState = Sections(newState, action);
+      });
+      // Should have been inserted into newState in reverse order
+      assert.deepEqual(newState.map(section => section.id), state.map(section => section.id).reverse());
+      const newSection = {id: "new_section", order: 2.5};
+      const action = {type: at.SECTION_REGISTER, data: newSection};
+      newState = Sections(newState, action);
+      // Should have been inserted at index 2, between second and third section
+      assert.equal(newState[2].id, newSection.id);
+    });
+    it("should insert sections without an `order` at the top on SECTION_REGISTER", () => {
+      const newSection = {id: "new_section"};
+      const action = {type: at.SECTION_REGISTER, data: newSection};
+      const newState = Sections(oldState, action);
+      assert.equal(newState[0].id, newSection.id);
+      assert.ok(newState[0].order < newState[1].order);
+    });
     it("should set newSection.rows === [] if no rows are provided on SECTION_REGISTER", () => {
       const action = {type: at.SECTION_REGISTER, data: {id: "foo_bar_5", title: "Foo Bar 5"}};
       const newState = Sections(oldState, action);
       const insertedSection = newState.find(section => section.id === "foo_bar_5");
       assert.deepEqual(insertedSection.rows, []);
     });
     it("should update a section on SECTION_REGISTER if it already exists", () => {
       const NEW_TITLE = "New Title";
       const action = {type: at.SECTION_REGISTER, data: {id: "foo_bar_2", title: NEW_TITLE}};
       const newState = Sections(oldState, action);
       assert.lengthOf(newState, 5);
       const updatedSection = newState.find(section => section.id === "foo_bar_2");
       assert.ok(updatedSection && updatedSection.title === NEW_TITLE);
     });
-    it("should have no effect on SECTION_ROWS_UPDATE if the id doesn't exist", () => {
-      const action = {type: at.SECTION_ROWS_UPDATE, data: {id: "fake_id", data: "fake_data"}};
+    it("should have no effect on SECTION_UPDATE if the id doesn't exist", () => {
+      const action = {type: at.SECTION_UPDATE, data: {id: "fake_id", data: "fake_data"}};
       const newState = Sections(oldState, action);
       assert.deepEqual(oldState, newState);
     });
-    it("should update the section rows with the correct data on SECTION_ROWS_UPDATE", () => {
-      const FAKE_DATA = ["some", "fake", "data"];
-      const action = {type: at.SECTION_ROWS_UPDATE, data: {id: "foo_bar_2", rows: FAKE_DATA}};
+    it("should update the section with the correct data on SECTION_UPDATE", () => {
+      const FAKE_DATA = {rows: ["some", "fake", "data"], foo: "bar"};
+      const action = {type: at.SECTION_UPDATE, data: Object.assign(FAKE_DATA, {id: "foo_bar_2"})};
       const newState = Sections(oldState, action);
       const updatedSection = newState.find(section => section.id === "foo_bar_2");
-      assert.equal(updatedSection.rows, FAKE_DATA);
+      assert.include(updatedSection, FAKE_DATA);
     });
     it("should remove blocked and deleted urls from all rows in all sections", () => {
       const blockAction = {type: at.PLACES_LINK_BLOCKED, data: {url: "www.foo.bar"}};
       const deleteAction = {type: at.PLACES_LINK_DELETED, data: {url: "www.foo.bar"}};
       const newBlockState = Sections(oldState, blockAction);
       const newDeleteState = Sections(oldState, deleteAction);
       newBlockState.concat(newDeleteState).forEach(section => {
         assert.deepEqual(section.rows, [{url: "www.other.url"}]);
deleted file mode 100644
--- a/browser/extensions/activity-stream/test/unit/common/ShortUrl.test.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const {shortURL} = require("common/ShortURL.jsm");
-
-describe("shortURL", () => {
-  it("should return a blank string if url and hostname is falsey", () => {
-    assert.equal(shortURL({url: ""}), "");
-    assert.equal(shortURL({hostname: null}), "");
-  });
-
-  it("should remove the eTLD, if provided", () => {
-    assert.equal(shortURL({hostname: "com.blah.com", eTLD: "com"}), "com.blah");
-  });
-
-  it("should use the hostname, if provided", () => {
-    assert.equal(shortURL({hostname: "foo.com", url: "http://bar.com", eTLD: "com"}), "foo");
-  });
-
-  it("should get the hostname from .url if necessary", () => {
-    assert.equal(shortURL({url: "http://bar.com", eTLD: "com"}), "bar");
-  });
-
-  it("should not strip out www if not first subdomain", () => {
-    assert.equal(shortURL({hostname: "foo.www.com", eTLD: "com"}), "foo.www");
-  });
-
-  it("should convert to lowercase", () => {
-    assert.equal(shortURL({url: "HTTP://FOO.COM", eTLD: "com"}), "foo");
-  });
-
-  it("should return hostname for localhost", () => {
-    assert.equal(shortURL({url: "http://localhost:8000/", eTLD: "localhost"}), "localhost");
-  });
-
-  it("should fallback to link title if it exists", () => {
-    const link = {
-      url: "file:///Users/voprea/Work/activity-stream/logs/coverage/system-addon/report-html/index.html",
-      title: "Code coverage report"
-    };
-
-    assert.equal(shortURL(link), link.title);
-  });
-
-  it("should return the url if no hostname or title is provided", () => {
-    const url = "file://foo/bar.txt";
-    assert.equal(shortURL({url, eTLD: "foo"}), url);
-  });
-});
--- a/browser/extensions/activity-stream/test/unit/lib/ActivityStreamMessageChannel.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/ActivityStreamMessageChannel.test.js
@@ -75,16 +75,26 @@ describe("ActivityStreamMessageChannel",
         mm.createChannel();
         assert.ok(mm.channel.isFromAboutNewTab);
       });
       it("should not override AboutNewTab if the pageURL is not about:newtab", () => {
         mm = new ActivityStreamMessageChannel({pageURL: "foo.html"});
         mm.createChannel();
         assert.notCalled(global.AboutNewTab.override);
       });
+      it("should simluate init for existing ports", () => {
+        sinon.stub(mm, "onActionFromContent");
+        RPmessagePorts.push({loaded: false, portID: "inited"});
+        RPmessagePorts.push({loaded: true, portID: "loaded"});
+
+        mm.createChannel();
+
+        assert.calledWith(mm.onActionFromContent.firstCall, {type: at.NEW_TAB_INIT}, "inited");
+        assert.calledWith(mm.onActionFromContent.secondCall, {type: at.NEW_TAB_INIT}, "loaded");
+      });
       it("should simluate load for loaded ports", () => {
         sinon.stub(mm, "onActionFromContent");
         RPmessagePorts.push({loaded: true, portID: "foo"});
 
         mm.createChannel();
 
         assert.calledWith(mm.onActionFromContent, {type: at.NEW_TAB_LOAD}, "foo");
       });
--- a/browser/extensions/activity-stream/test/unit/lib/LocalizationFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/LocalizationFeed.test.js
@@ -56,52 +56,69 @@ describe("Localization Feed", () => {
       const arg = feed.store.dispatch.firstCall.args[0];
       assert.propertyVal(arg, "type", at.LOCALE_UPDATED);
       assert.propertyVal(arg.data, "locale", locale);
       assert.deepEqual(arg.data.strings, TEST_STRINGS[locale]);
     });
     it("should use strings for other locale", () => {
       const locale = "it";
       sandbox.stub(global.Services.locale, "negotiateLanguages")
-        .returns([locale]);
+        .returns([locale, DEFAULT_LOCALE]);
 
       feed.updateLocale();
 
       assert.calledOnce(feed.store.dispatch);
       const arg = feed.store.dispatch.firstCall.args[0];
       assert.propertyVal(arg, "type", at.LOCALE_UPDATED);
       assert.propertyVal(arg.data, "locale", locale);
       assert.deepEqual(arg.data.strings, TEST_STRINGS[locale]);
     });
     it("should use some fallback strings for partial locale", () => {
       const locale = "ru";
       sandbox.stub(global.Services.locale, "negotiateLanguages")
-        .returns([locale]);
+        .returns([locale, DEFAULT_LOCALE]);
 
       feed.updateLocale();
 
       assert.calledOnce(feed.store.dispatch);
       const arg = feed.store.dispatch.firstCall.args[0];
       assert.propertyVal(arg, "type", at.LOCALE_UPDATED);
       assert.propertyVal(arg.data, "locale", locale);
       assert.deepEqual(arg.data.strings, {
         foo: TEST_STRINGS[locale].foo,
         too: TEST_STRINGS[DEFAULT_LOCALE].too
       });
     });
-    it("should use all default strings for unknown locale", () => {
-      const locale = "xyz";
+    it("should use multiple fallback strings before default", () => {
+      const primaryLocale = "ru";
+      const secondaryLocale = "it";
       sandbox.stub(global.Services.locale, "negotiateLanguages")
-        .returns([locale]);
+        .returns([primaryLocale, secondaryLocale, DEFAULT_LOCALE]);
+
       feed.updateLocale();
 
       assert.calledOnce(feed.store.dispatch);
       const arg = feed.store.dispatch.firstCall.args[0];
       assert.propertyVal(arg, "type", at.LOCALE_UPDATED);
-      assert.propertyVal(arg.data, "locale", locale);
+      assert.propertyVal(arg.data, "locale", primaryLocale);
+      assert.deepEqual(arg.data.strings, {
+        foo: TEST_STRINGS[primaryLocale].foo,
+        too: TEST_STRINGS[secondaryLocale].too
+      });
+    });
+    it("should use all default strings for unknown locale", () => {
+      sandbox.stub(global.Services.locale, "negotiateLanguages")
+        .returns([DEFAULT_LOCALE]);
+
+      feed.updateLocale();
+
+      assert.calledOnce(feed.store.dispatch);
+      const arg = feed.store.dispatch.firstCall.args[0];
+      assert.propertyVal(arg, "type", at.LOCALE_UPDATED);
+      assert.propertyVal(arg.data, "locale", DEFAULT_LOCALE);
       assert.deepEqual(arg.data.strings, TEST_STRINGS[DEFAULT_LOCALE]);
     });
   });
 
   describe("#observe", () => {
     it("should update locale on locale change event", () => {
       sinon.stub(feed, "updateLocale");
 
--- a/browser/extensions/activity-stream/test/unit/lib/SectionsManager.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/SectionsManager.test.js
@@ -1,25 +1,57 @@
 "use strict";
 const {SectionsFeed, SectionsManager} = require("lib/SectionsManager.jsm");
-const {EventEmitter} = require("test/unit/utils");
+const {EventEmitter, GlobalOverrider} = require("test/unit/utils");
 const {MAIN_MESSAGE_TYPE, CONTENT_MESSAGE_TYPE} = require("common/Actions.jsm");
 
 const FAKE_ID = "FAKE_ID";
 const FAKE_OPTIONS = {icon: "FAKE_ICON", title: "FAKE_TITLE"};
 const FAKE_ROWS = [{url: "1"}, {url: "2"}, {"url": "3"}];
 
+let globals;
+
+beforeEach(() => {
+  globals = new GlobalOverrider();
+});
+
 afterEach(() => {
+  globals.restore();
   // Redecorate SectionsManager to remove any listeners that have been added
   EventEmitter.decorate(SectionsManager);
+  SectionsManager.init();
 });
 
 describe("SectionsManager", () => {
-  it("should be initialised with .initialized == false", () => {
-    assert.notOk(SectionsManager.initialized);
+  describe("#init", () => {
+    it("should initialise the sections map with the built in sections", () => {
+      SectionsManager.sections.clear();
+      SectionsManager.initialized = false;
+      SectionsManager.init();
+      assert.equal(SectionsManager.sections.size, 1);
+      assert.ok(SectionsManager.sections.has("topstories"));
+    });
+    it("should set .initialized to true", () => {
+      SectionsManager.sections.clear();
+      SectionsManager.initialized = false;
+      SectionsManager.init();
+      assert.ok(SectionsManager.initialized);
+    });
+  });
+  describe("#addBuiltInSection", () => {
+    it("should not report an error if options is undefined", () => {
+      globals.sandbox.spy(global.Components.utils, "reportError");
+      SectionsManager.addBuiltInSection("feeds.section.topstories", undefined);
+      assert.notCalled(Components.utils.reportError);
+    });
+    it("should report an error if options is malformed", () => {
+      globals.sandbox.spy(global.Components.utils, "reportError");
+      SectionsManager.addBuiltInSection("feeds.section.topstories", "invalid");
+      assert.calledOnce(Components.utils.reportError);
+    });
   });
   describe("#addSection", () => {
     it("should add the id to sections and emit an ADD_SECTION event", () => {
       const spy = sinon.spy();
       SectionsManager.on(SectionsManager.ADD_SECTION, spy);
       SectionsManager.addSection(FAKE_ID, FAKE_OPTIONS);
       assert.ok(SectionsManager.sections.has(FAKE_ID));
       assert.calledOnce(spy);
@@ -33,39 +65,77 @@ describe("SectionsManager", () => {
       const spy = sinon.spy();
       SectionsManager.on(SectionsManager.REMOVE_SECTION, spy);
       SectionsManager.removeSection(FAKE_ID);
       assert.notOk(SectionsManager.sections.has(FAKE_ID));
       assert.calledOnce(spy);
       assert.calledWith(spy, SectionsManager.REMOVE_SECTION, FAKE_ID);
     });
   });
-  describe("#updateRows", () => {
-    it("should emit an UPDATE_ROWS event with correct arguments", () => {
+  describe("#enableSection", () => {
+    it("should call updateSection with {enabled: true}", () => {
+      sinon.spy(SectionsManager, "updateSection");
+      SectionsManager.addSection(FAKE_ID, FAKE_OPTIONS);
+      SectionsManager.enableSection(FAKE_ID);
+      assert.calledOnce(SectionsManager.updateSection);
+      assert.calledWith(SectionsManager.updateSection, FAKE_ID, {enabled: true}, true);
+      SectionsManager.updateSection.restore();
+    });
+  });
+  describe("#disableSection", () => {
+    it("should call updateSection with {enabled: false, rows: []}", () => {
+      sinon.spy(SectionsManager, "updateSection");
+      SectionsManager.addSection(FAKE_ID, FAKE_OPTIONS);
+      SectionsManager.disableSection(FAKE_ID);
+      assert.calledOnce(SectionsManager.updateSection);
+      assert.calledWith(SectionsManager.updateSection, FAKE_ID, {enabled: false, rows: []}, true);
+      SectionsManager.updateSection.restore();
+    });
+  });
+  describe("#updateSection", () => {
+    it("should emit an UPDATE_SECTION event with correct arguments", () => {
       SectionsManager.addSection(FAKE_ID, FAKE_OPTIONS);
       const spy = sinon.spy();
-      SectionsManager.on(SectionsManager.UPDATE_ROWS, spy);
-      SectionsManager.updateRows(FAKE_ID, FAKE_ROWS, true);
+      SectionsManager.on(SectionsManager.UPDATE_SECTION, spy);
+      SectionsManager.updateSection(FAKE_ID, {rows: FAKE_ROWS}, true);
       assert.calledOnce(spy);
-      assert.calledWith(spy, SectionsManager.UPDATE_ROWS, FAKE_ID, FAKE_ROWS, true);
+      assert.calledWith(spy, SectionsManager.UPDATE_SECTION, FAKE_ID, {rows: FAKE_ROWS}, true);
     });
     it("should do nothing if the section doesn't exist", () => {
       SectionsManager.removeSection(FAKE_ID);
       const spy = sinon.spy();
-      SectionsManager.on(SectionsManager.UPDATE_ROWS, spy);
-      SectionsManager.updateRows(FAKE_ID, FAKE_ROWS, true);
+      SectionsManager.on(SectionsManager.UPDATE_SECTION, spy);
+      SectionsManager.updateSection(FAKE_ID, {rows: FAKE_ROWS}, true);
       assert.notCalled(spy);
     });
   });
+  describe("#onceInitialized", () => {
+    it("should call the callback immediately if SectionsManager is initialised", () => {
+      SectionsManager.initialized = true;
+      const callback = sinon.spy();
+      SectionsManager.onceInitialized(callback);
+      assert.calledOnce(callback);
+    });
+    it("should bind the callback to .once(INIT) if SectionsManager is not initialised", () => {
+      SectionsManager.initialized = false;
+      sinon.spy(SectionsManager, "once");
+      const callback = () => {};
+      SectionsManager.onceInitialized(callback);
+      assert.calledOnce(SectionsManager.once);
+      assert.calledWith(SectionsManager.once, SectionsManager.INIT, callback);
+    });
+  });
 });
 
 describe("SectionsFeed", () => {
   let feed;
 
   beforeEach(() => {
+    SectionsManager.sections.clear();
+    SectionsManager.initialized = false;
     feed = new SectionsFeed();
     feed.store = {dispatch: sinon.spy()};
   });
   afterEach(() => {
     feed.uninit();
   });
   describe("#init", () => {
     it("should create a SectionsFeed", () => {
@@ -73,39 +143,41 @@ describe("SectionsFeed", () => {
     });
     it("should bind appropriate listeners", () => {
       sinon.spy(SectionsManager, "on");
       feed.init();
       assert.calledThrice(SectionsManager.on);
       for (const [event, listener] of [
         [SectionsManager.ADD_SECTION, feed.onAddSection],
         [SectionsManager.REMOVE_SECTION, feed.onRemoveSection],
-        [SectionsManager.UPDATE_ROWS, feed.onUpdateRows]
+        [SectionsManager.UPDATE_SECTION, feed.onUpdateSection]
       ]) {
         assert.calledWith(SectionsManager.on, event, listener);
       }
     });
-    it("should emit an INIT event and set SectionsManager.initialized to true", () => {
-      const spy = sinon.spy();
-      SectionsManager.on(SectionsManager.INIT, spy);
+    it("should call onAddSection for any already added sections in SectionsManager", () => {
+      SectionsManager.init();
+      assert.ok(SectionsManager.sections.has("topstories"));
+      const topstories = SectionsManager.sections.get("topstories");
+      sinon.spy(feed, "onAddSection");
       feed.init();
-      assert.calledOnce(spy);
-      assert.ok(SectionsManager.initialized);
+      assert.calledOnce(feed.onAddSection);
+      assert.calledWith(feed.onAddSection, SectionsManager.ADD_SECTION, "topstories", topstories);
     });
   });
   describe("#uninit", () => {
     it("should unbind all listeners", () => {
       sinon.spy(SectionsManager, "off");
       feed.init();
       feed.uninit();
       assert.calledThrice(SectionsManager.off);
       for (const [event, listener] of [
         [SectionsManager.ADD_SECTION, feed.onAddSection],
         [SectionsManager.REMOVE_SECTION, feed.onRemoveSection],
-        [SectionsManager.UPDATE_ROWS, feed.onUpdateRows]
+        [SectionsManager.UPDATE_SECTION, feed.onUpdateSection]
       ]) {
         assert.calledWith(SectionsManager.off, event, listener);
       }
     });
     it("should emit an UNINIT event and set SectionsManager.initialized to false", () => {
       const spy = sinon.spy();
       SectionsManager.on(SectionsManager.UNINIT, spy);
       feed.init();
@@ -130,50 +202,78 @@ describe("SectionsFeed", () => {
       const action = feed.store.dispatch.firstCall.args[0];
       assert.equal(action.type, "SECTION_DEREGISTER");
       assert.deepEqual(action.data, FAKE_ID);
       // Should be broadcast
       assert.equal(action.meta.from, MAIN_MESSAGE_TYPE);
       assert.equal(action.meta.to, CONTENT_MESSAGE_TYPE);
     });
   });
-  describe("#onUpdateRows", () => {
+  describe("#onUpdateSection", () => {
     it("should do nothing if no rows are provided", () => {
-      feed.onUpdateRows(null, FAKE_ID, null);
+      feed.onUpdateSection(null, FAKE_ID, null);
       assert.notCalled(feed.store.dispatch);
     });
-    it("should dispatch a SECTION_ROWS_UPDATE action with the correct data", () => {
-      feed.onUpdateRows(null, FAKE_ID, FAKE_ROWS);
+    it("should dispatch a SECTION_UPDATE action with the correct data", () => {
+      feed.onUpdateSection(null, FAKE_ID, {rows: FAKE_ROWS});
       const action = feed.store.dispatch.firstCall.args[0];
-      assert.equal(action.type, "SECTION_ROWS_UPDATE");
+      assert.equal(action.type, "SECTION_UPDATE");
       assert.deepEqual(action.data, {id: FAKE_ID, rows: FAKE_ROWS});
       // Should be not broadcast by default, so meta should not exist
       assert.notOk(action.meta);
     });
     it("should broadcast the action only if shouldBroadcast is true", () => {
-      feed.onUpdateRows(null, FAKE_ID, FAKE_ROWS, true);
+      feed.onUpdateSection(null, FAKE_ID, {rows: FAKE_ROWS}, true);
       const action = feed.store.dispatch.firstCall.args[0];
       // Should be broadcast
       assert.equal(action.meta.from, MAIN_MESSAGE_TYPE);
       assert.equal(action.meta.to, CONTENT_MESSAGE_TYPE);
     });
   });
   describe("#onAction", () => {
-    it("should call init() on action INIT", () => {
-      sinon.spy(feed, "init");
+    it("should bind this.init to SectionsManager.INIT on INIT", () => {
+      sinon.spy(SectionsManager, "once");
       feed.onAction({type: "INIT"});
-      assert.calledOnce(feed.init);
+      assert.calledOnce(SectionsManager.once);
+      assert.calledWith(SectionsManager.once, SectionsManager.INIT, feed.init);
+    });
+    it("should call SectionsManager.init on action PREFS_INITIAL_VALUES", () => {
+      sinon.spy(SectionsManager, "init");
+      feed.onAction({type: "PREFS_INITIAL_VALUES", data: {foo: "bar"}});
+      assert.calledOnce(SectionsManager.init);
+      assert.calledWith(SectionsManager.init, {foo: "bar"});
+    });
+    it("should call SectionsManager.addBuiltInSection on suitable PREF_CHANGED events", () => {
+      sinon.spy(SectionsManager, "addBuiltInSection");
+      feed.onAction({type: "PREF_CHANGED", data: {name: "feeds.section.topstories.options", value: "foo"}});
+      assert.calledOnce(SectionsManager.addBuiltInSection);
+      assert.calledWith(SectionsManager.addBuiltInSection, "feeds.section.topstories", "foo");
+    });
+    it("should call SectionsManager.disableSection on SECTION_DISABLE", () => {
+      sinon.spy(SectionsManager, "disableSection");
+      feed.onAction({type: "SECTION_DISABLE", data: 1234});
+      assert.calledOnce(SectionsManager.disableSection);
+      assert.calledWith(SectionsManager.disableSection, 1234);
+      SectionsManager.disableSection.restore();
+    });
+    it("should call SectionsManager.enableSection on SECTION_ENABLE", () => {
+      sinon.spy(SectionsManager, "enableSection");
+      feed.onAction({type: "SECTION_ENABLE", data: 1234});
+      assert.calledOnce(SectionsManager.enableSection);
+      assert.calledWith(SectionsManager.enableSection, 1234);
+      SectionsManager.enableSection.restore();
     });
     it("should emit a ACTION_DISPATCHED event and forward any action in ACTIONS_TO_PROXY if there are any sections", () => {
       const spy = sinon.spy();
       const allowedActions = SectionsManager.ACTIONS_TO_PROXY;
       const disallowedActions = ["PREF_CHANGED", "OPEN_PRIVATE_WINDOW"];
+      feed.init();
       SectionsManager.on(SectionsManager.ACTION_DISPATCHED, spy);
       // Make sure we start with no sections - no event should be emitted
-      assert.equal(SectionsManager.sections.size, 0);
+      SectionsManager.sections.clear();
       feed.onAction({type: allowedActions[0]});
       assert.notCalled(spy);
       // Then add a section and check correct behaviour
       SectionsManager.addSection(FAKE_ID, FAKE_OPTIONS);
       for (const action of allowedActions.concat(disallowedActions)) {
         feed.onAction({type: action});
       }
       for (const action of allowedActions) {
new file mode 100644
--- /dev/null
+++ b/browser/extensions/activity-stream/test/unit/lib/ShortUrl.test.js
@@ -0,0 +1,68 @@
+const {shortURL} = require("lib/ShortURL.jsm");
+const {GlobalOverrider} = require("test/unit/utils");
+
+describe("shortURL", () => {
+  let globals;
+  let IDNStub;
+
+  beforeEach(() => {
+    IDNStub = sinon.stub().callsFake(id => id);
+
+    globals = new GlobalOverrider();
+    globals.set("IDNService", {convertToDisplayIDN: IDNStub});
+  });
+
+  afterEach(() => {
+    globals.restore();
+  });
+
+  it("should return a blank string if url and hostname is falsey", () => {
+    assert.equal(shortURL({url: ""}), "");
+    assert.equal(shortURL({hostname: null}), "");
+  });
+
+  it("should remove the eTLD, if provided", () => {
+    assert.equal(shortURL({hostname: "com.blah.com", eTLD: "com"}), "com.blah");
+  });
+
+  it("should call convertToDisplayIDN when calling shortURL", () => {
+    shortURL({hostname: "com.blah.com", eTLD: "com"});
+
+    assert.calledOnce(IDNStub);
+    assert.calledWithExactly(IDNStub, "com.blah.com", {});
+  });
+
+  it("should use the hostname, if provided", () => {
+    assert.equal(shortURL({hostname: "foo.com", url: "http://bar.com", eTLD: "com"}), "foo");
+  });
+
+  it("should get the hostname from .url if necessary", () => {
+    assert.equal(shortURL({url: "http://bar.com", eTLD: "com"}), "bar");
+  });
+
+  it("should not strip out www if not first subdomain", () => {
+    assert.equal(shortURL({hostname: "foo.www.com", eTLD: "com"}), "foo.www");
+  });
+
+  it("should convert to lowercase", () => {
+    assert.equal(shortURL({url: "HTTP://FOO.COM", eTLD: "com"}), "foo");
+  });
+
+  it("should return hostname for localhost", () => {
+    assert.equal(shortURL({url: "http://localhost:8000/", eTLD: "localhost"}), "localhost");
+  });
+
+  it("should fallback to link title if it exists", () => {
+    const link = {
+      url: "file:///Users/voprea/Work/activity-stream/logs/coverage/system-addon/report-html/index.html",
+      title: "Code coverage report"
+    };
+
+    assert.equal(shortURL(link), link.title);
+  });
+
+  it("should return the url if no hostname or title is provided", () => {
+    const url = "file://foo/bar.txt";
+    assert.equal(shortURL({url, eTLD: "foo"}), url);
+  });
+});
--- a/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
@@ -1,16 +1,21 @@
 "use strict";
 const injector = require("inject!lib/TopSitesFeed.jsm");
 const {UPDATE_TIME} = require("lib/TopSitesFeed.jsm");
 const {FakePrefs, GlobalOverrider} = require("test/unit/utils");
 const action = {meta: {fromTarget: {}}};
 const {actionCreators: ac, actionTypes: at} = require("common/Actions.jsm");
 const {insertPinned, TOP_SITES_SHOWMORE_LENGTH} = require("common/Reducers.jsm");
-const FAKE_LINKS = new Array(TOP_SITES_SHOWMORE_LENGTH).fill(null).map((v, i) => ({url: `http://www.site${i}.com`}));
+
+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`
+}));
 const FAKE_SCREENSHOT = "data123";
 
 function FakeTippyTopProvider() {}
 FakeTippyTopProvider.prototype = {
   async init() {},
   processSite(site) { return site; }
 };
 
@@ -48,17 +53,17 @@ describe("Top Sites Feed", () => {
     globals.set("NewTabUtils", fakeNewTabUtils);
     FakePrefs.prototype.prefs["default.sites"] = "https://foo.com/";
     ({TopSitesFeed, DEFAULT_TOP_SITES} = injector({
       "lib/ActivityStreamPrefs.jsm": {Prefs: FakePrefs},
       "common/Dedupe.jsm": {Dedupe: fakeDedupe},
       "common/Reducers.jsm": {insertPinned, TOP_SITES_SHOWMORE_LENGTH},
       "lib/Screenshots.jsm": {Screenshots: fakeScreenshot},
       "lib/TippyTopProvider.jsm": {TippyTopProvider: FakeTippyTopProvider},
-      "common/ShortURL.jsm": {shortURL: shortURLStub}
+      "lib/ShortURL.jsm": {shortURL: shortURLStub}
     }));
     feed = new TopSitesFeed();
     feed.store = {dispatch: sinon.spy(), getState() { return {TopSites: {rows: Array(12).fill("site")}}; }};
     feed.dedupe.group = sites => sites;
     links = FAKE_LINKS;
     clock = sinon.useFakeTimers();
   });
   afterEach(() => {
@@ -101,20 +106,37 @@ describe("Top Sites Feed", () => {
 
     it("should get the links from NewTabUtils", async () => {
       const result = await feed.getLinksWithDefaults();
       const reference = links.map(site => Object.assign({}, site, {hostname: shortURLStub(site)}));
 
       assert.deepEqual(result, reference);
       assert.calledOnce(global.NewTabUtils.activityStreamLinks.getTopSites);
     });
+    it("should filter out low frecency links", async () => {
+      links = [
+        {frecency: FAKE_FRECENCY, url: "https://enough/visited"},
+        {frecency: 100, url: "https://visited/once"},
+        {frecency: 0, url: "https://unvisited/page"}
+      ];
+
+      const result = await feed.getLinksWithDefaults();
+
+      assert.equal(result[0].url, links[0].url);
+      assert.notEqual(result[1].url, links[1].url);
+      assert.notEqual(result[1].url, links[2].url);
+    });
     it("should filter out the defaults that have been blocked", async () => {
       // make sure we only have one top site, and we block the only default site we have to show
       const url = "www.myonlytopsite.com";
-      const topsite = {url, hostname: shortURLStub({url})};
+      const topsite = {
+        frecency: FAKE_FRECENCY,
+        hostname: shortURLStub({url}),
+        url
+      };
       const blockedDefaultSite = {url: "https://foo.com"};
       fakeNewTabUtils.activityStreamLinks.getTopSites = () => [topsite];
       fakeNewTabUtils.blockedLinks.isBlocked = site => (site.url === blockedDefaultSite.url);
       const result = await feed.getLinksWithDefaults();
 
       // what we should be left with is just the top site we added, and not the default site we blocked
       assert.lengthOf(result, 1);
       assert.deepEqual(result[0], topsite);
@@ -129,27 +151,27 @@ describe("Top Sites Feed", () => {
     });
     it("should dedupe the links by hostname", async () => {
       const site = {url: "foo", hostname: "bar"};
       const result = feed._dedupeKey(site);
 
       assert.equal(result, site.hostname);
     });
     it("should add defaults if there are are not enough links", async () => {
-      links = [{url: "foo.com"}];
+      links = [{frecency: FAKE_FRECENCY, url: "foo.com"}];
 
       const result = await feed.getLinksWithDefaults();
       const reference = [...links, ...DEFAULT_TOP_SITES].map(s => Object.assign({}, s, {hostname: shortURLStub(s)}));
 
       assert.deepEqual(result, reference);
     });
     it("should only add defaults up to TOP_SITES_SHOWMORE_LENGTH", async () => {
       links = [];
       for (let i = 0; i < TOP_SITES_SHOWMORE_LENGTH - 1; i++) {
-        links.push({url: `foo${i}.com`});
+        links.push({frecency: FAKE_FRECENCY, url: `foo${i}.com`});
       }
       const result = await feed.getLinksWithDefaults();
       const reference = [...links, DEFAULT_TOP_SITES[0]].map(s => Object.assign({}, s, {hostname: shortURLStub(s)}));
 
       assert.lengthOf(result, TOP_SITES_SHOWMORE_LENGTH);
       assert.deepEqual(result, reference);
     });
     it("should not throw if NewTabUtils returns null", () => {
@@ -353,10 +375,15 @@ describe("Top Sites Feed", () => {
       assert.calledOnce(feed.store.dispatch);
       assert.propertyVal(feed.store.dispatch.firstCall.args[0], "type", at.TOP_SITES_UPDATED);
     });
     it("should call refresh on INIT action", async () => {
       sinon.stub(feed, "refresh");
       await feed.onAction({type: at.INIT});
       assert.calledOnce(feed.refresh);
     });
+    it("should call refresh on BLOCK_URL action", async () => {
+      sinon.stub(feed, "refresh");
+      await feed.onAction({type: at.BLOCK_URL});
+      assert.calledOnce(feed.refresh);
+    });
   });
 });
--- a/browser/extensions/activity-stream/test/unit/lib/TopStoriesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/TopStoriesFeed.test.js
@@ -1,167 +1,152 @@
 "use strict";
 const injector = require("inject!lib/TopStoriesFeed.jsm");
 const {FakePrefs} = require("test/unit/utils");
-const {actionCreators: ac, actionTypes: at} = require("common/Actions.jsm");
+const {actionTypes: at} = require("common/Actions.jsm");
 const {GlobalOverrider} = require("test/unit/utils");
 
 describe("Top Stories Feed", () => {
   let TopStoriesFeed;
   let STORIES_UPDATE_TIME;
   let TOPICS_UPDATE_TIME;
   let SECTION_ID;
   let FEED_PREF;
-  let SECTION_OPTIONS_PREF;
   let instance;
   let clock;
   let globals;
+  let sectionsManagerStub;
   let shortURLStub;
 
+  const FAKE_OPTIONS = {
+    "stories_endpoint": "https://somedomain.org/stories?key=$apiKey",
+    "stories_referrer": "https://somedomain.org/referrer",
+    "topics_endpoint": "https://somedomain.org/topics?key=$apiKey",
+    "survey_link": "https://www.surveymonkey.com/r/newtabffx",
+    "api_key_pref": "apiKeyPref",
+    "provider_name": "test-provider",
+    "provider_icon": "provider-icon",
+    "provider_description": "provider_desc"
+  };
+
   beforeEach(() => {
-    FakePrefs.prototype.prefs["feeds.section.topstories.options"] = `{
-      "stories_endpoint": "https://somedomain.org/stories?key=$apiKey",
-      "stories_referrer": "https://somedomain.org/referrer",
-      "topics_endpoint": "https://somedomain.org/topics?key=$apiKey",
-      "survey_link": "https://www.surveymonkey.com/r/newtabffx",
-      "api_key_pref": "apiKeyPref",
-      "provider_name": "test-provider",
-      "provider_icon": "provider-icon",
-      "provider_description": "provider_desc"
-    }`;
     FakePrefs.prototype.prefs.apiKeyPref = "test-api-key";
 
     globals = new GlobalOverrider();
     globals.set("Services", {locale: {getRequestedLocale: () => "en-CA"}});
+
+    sectionsManagerStub = {
+      onceInitialized: sinon.stub().callsFake(callback => callback()),
+      enableSection: sinon.spy(),
+      disableSection: sinon.spy(),
+      updateSection: sinon.spy(),
+      sections: new Map([["topstories", {options: FAKE_OPTIONS}]])
+    };
+
     clock = sinon.useFakeTimers();
 
     shortURLStub = sinon.stub().callsFake(site => site.url);
 
-    ({TopStoriesFeed, STORIES_UPDATE_TIME, TOPICS_UPDATE_TIME, SECTION_ID, FEED_PREF, SECTION_OPTIONS_PREF} = injector({
+    ({TopStoriesFeed, STORIES_UPDATE_TIME, TOPICS_UPDATE_TIME, SECTION_ID, FEED_PREF} = injector({
       "lib/ActivityStreamPrefs.jsm": {Prefs: FakePrefs},
-      "common/ShortURL.jsm": {shortURL: shortURLStub}
+      "lib/ShortURL.jsm": {shortURL: shortURLStub},
+      "lib/SectionsManager.jsm": {SectionsManager: sectionsManagerStub}
     }));
     instance = new TopStoriesFeed();
     instance.store = {getState() { return {}; }, dispatch: sinon.spy()};
     instance.storiesLastUpdated = 0;
     instance.topicsLastUpdated = 0;
   });
   afterEach(() => {
     globals.restore();
     clock.restore();
   });
   describe("#init", () => {
     it("should create a TopStoriesFeed", () => {
       assert.instanceOf(instance, TopStoriesFeed);
     });
-    it("should initialize endpoints based on prefs", () => {
+    it("should bind parseOptions to SectionsManager.onceInitialized", () => {
+      instance.onAction({type: at.INIT});
+      assert.calledOnce(sectionsManagerStub.onceInitialized);
+    });
+    it("should initialize endpoints based on options", () => {
       instance.onAction({type: at.INIT});
       assert.equal("https://somedomain.org/stories?key=test-api-key", instance.stories_endpoint);
       assert.equal("https://somedomain.org/referrer", instance.stories_referrer);
       assert.equal("https://somedomain.org/topics?key=test-api-key", instance.topics_endpoint);
     });
-    it("should register section", () => {
-      const expectedSectionOptions = {
-        id: SECTION_ID,
-        eventSource: "TOP_STORIES",
-        icon: "provider-icon",
-        title: {id: "header_recommended_by", values: {provider: "test-provider"}},
-        rows: [],
-        maxRows: 1,
-        contextMenuOptions: ["CheckBookmark", "SaveToPocket", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"],
-        infoOption: {
-          header: {id: "pocket_feedback_header"},
-          body: {id: "provider_desc"},
-          link: {
-            href: "https://www.surveymonkey.com/r/newtabffx",
-            id: "pocket_send_feedback"
-          }
-        },
-        emptyState: {
-          message: {id: "topstories_empty_state", values: {provider: "test-provider"}},
-          icon: "check"
-        }
-      };
-
+    it("should enable its section", () => {
       instance.onAction({type: at.INIT});
-      assert.calledOnce(instance.store.dispatch);
-      assert.propertyVal(instance.store.dispatch.firstCall.args[0], "type", at.SECTION_REGISTER);
-      assert.calledWith(instance.store.dispatch, ac.BroadcastToContent({
-        type: at.SECTION_REGISTER,
-        data: expectedSectionOptions
-      }));
+      assert.calledOnce(sectionsManagerStub.enableSection);
+      assert.calledWith(sectionsManagerStub.enableSection, SECTION_ID);
     });
     it("should fetch stories on init", () => {
       instance.fetchStories = sinon.spy();
       instance.fetchTopics = sinon.spy();
       instance.onAction({type: at.INIT});
       assert.calledOnce(instance.fetchStories);
     });
     it("should fetch topics on init", () => {
       instance.fetchStories = sinon.spy();
       instance.fetchTopics = sinon.spy();
       instance.onAction({type: at.INIT});
       assert.calledOnce(instance.fetchTopics);
     });
     it("should not fetch if endpoint not configured", () => {
       let fetchStub = globals.sandbox.stub();
       globals.set("fetch", fetchStub);
-      FakePrefs.prototype.prefs["feeds.section.topstories.options"] = "{}";
+      sectionsManagerStub.sections.set("topstories", {options: {}});
       instance.init();
       assert.notCalled(fetchStub);
     });
     it("should report error for invalid configuration", () => {
       globals.sandbox.spy(global.Components.utils, "reportError");
-      FakePrefs.prototype.prefs["feeds.section.topstories.options"] = "invalid";
+      sectionsManagerStub.sections.set("topstories", {options: {api_key_pref: "invalid"}});
       instance.init();
 
       assert.called(Components.utils.reportError);
     });
     it("should report error for missing api key", () => {
       let fakeServices = {prefs: {getCharPref: sinon.spy()}, locale: {getRequestedLocale: sinon.spy()}};
       globals.set("Services", fakeServices);
       globals.sandbox.spy(global.Components.utils, "reportError");
-      FakePrefs.prototype.prefs["feeds.section.topstories.options"] = `{
-        "stories_endpoint": "https://somedomain.org/stories?key=$apiKey",
-        "topics_endpoint": "https://somedomain.org/topics?key=$apiKey"
-      }`;
+      sectionsManagerStub.sections.set("topstories", {
+        options: {
+          "stories_endpoint": "https://somedomain.org/stories?key=$apiKey",
+          "topics_endpoint": "https://somedomain.org/topics?key=$apiKey"
+        }
+      });
       instance.init();
 
       assert.called(Components.utils.reportError);
     });
-    it("should deregister section", () => {
-      instance.onAction({type: at.UNINIT});
-      assert.calledOnce(instance.store.dispatch);
-      assert.calledWith(instance.store.dispatch, ac.BroadcastToContent({
-        type: at.SECTION_DEREGISTER,
-        data: SECTION_ID
-      }));
-    });
     it("should initialize on FEED_INIT", () => {
       instance.init = sinon.spy();
       instance.onAction({type: at.FEED_INIT, data: FEED_PREF});
       assert.calledOnce(instance.init);
     });
-    it("should initialize on PREF_CHANGED", () => {
-      instance.init = sinon.spy();
-      instance.onAction({type: at.PREF_CHANGED, data: {name: SECTION_OPTIONS_PREF}});
-      assert.calledOnce(instance.init);
+  });
+  describe("#uninit", () => {
+    it("should disable its section", () => {
+      instance.onAction({type: at.UNINIT});
+      assert.calledOnce(sectionsManagerStub.disableSection);
+      assert.calledWith(sectionsManagerStub.disableSection, SECTION_ID);
     });
   });
   describe("#fetch", () => {
     it("should fetch stories and send event", async () => {
       let fetchStub = globals.sandbox.stub();
       globals.set("fetch", fetchStub);
       globals.set("NewTabUtils", {blockedLinks: {isBlocked: globals.sandbox.spy()}});
 
-      const response = `{"list": [{"id" : "1",
+      const response = `{"recommendations": [{"id" : "1",
         "title": "title",
         "excerpt": "description",
         "image_src": "image-url",
-        "dedupe_url": "rec-url",
+        "url": "rec-url",
         "published_timestamp" : "123"
       }]}`;
       const stories = [{
         "guid": "1",
         "type": "now",
         "title": "title",
         "description": "description",
         "image": "image-url",
@@ -174,77 +159,73 @@ describe("Top Stories Feed", () => {
       instance.stories_endpoint = "stories-endpoint";
       instance.stories_referrer = "referrer";
       fetchStub.resolves({ok: true, status: 200, text: () => response});
       await instance.fetchStories();
 
       assert.calledOnce(fetchStub);
       assert.calledOnce(shortURLStub);
       assert.calledWithExactly(fetchStub, instance.stories_endpoint);
-      assert.calledOnce(instance.store.dispatch);
-      assert.propertyVal(instance.store.dispatch.firstCall.args[0], "type", at.SECTION_ROWS_UPDATE);
-      assert.deepEqual(instance.store.dispatch.firstCall.args[0].data.id, SECTION_ID);
-      assert.deepEqual(instance.store.dispatch.firstCall.args[0].data.rows, stories);
+      assert.calledOnce(sectionsManagerStub.updateSection);
+      assert.calledWith(sectionsManagerStub.updateSection, SECTION_ID, {rows: stories});
     });
-    it("should dispatch events", () => {
+    it("should call SectionsManager.updateSection", () => {
       instance.dispatchUpdateEvent(123, {});
-      assert.calledOnce(instance.store.dispatch);
+      assert.calledOnce(sectionsManagerStub.updateSection);
     });
     it("should report error for unexpected stories response", async () => {
       let fetchStub = globals.sandbox.stub();
       globals.set("fetch", fetchStub);
       globals.sandbox.spy(global.Components.utils, "reportError");
 
       instance.stories_endpoint = "stories-endpoint";
       fetchStub.resolves({ok: false, status: 400});
       await instance.fetchStories();
 
       assert.calledOnce(fetchStub);
       assert.calledWithExactly(fetchStub, instance.stories_endpoint);
-      assert.notCalled(instance.store.dispatch);
+      assert.notCalled(sectionsManagerStub.updateSection);
       assert.called(Components.utils.reportError);
     });
     it("should exclude blocked (dismissed) URLs", async () => {
       let fetchStub = globals.sandbox.stub();
       globals.set("fetch", fetchStub);
       globals.set("NewTabUtils", {blockedLinks: {isBlocked: site => site.url === "blocked"}});
 
-      const response = `{"list": [{"dedupe_url" : "blocked"}, {"dedupe_url" : "not_blocked"}]}`;
+      const response = `{"recommendations": [{"url" : "blocked"}, {"url" : "not_blocked"}]}`;
       instance.stories_endpoint = "stories-endpoint";
       fetchStub.resolves({ok: true, status: 200, text: () => response});
       await instance.fetchStories();
 
-      assert.calledOnce(instance.store.dispatch);
-      assert.propertyVal(instance.store.dispatch.firstCall.args[0], "type", at.SECTION_ROWS_UPDATE);
-      assert.equal(instance.store.dispatch.firstCall.args[0].data.rows.length, 1);
-      assert.equal(instance.store.dispatch.firstCall.args[0].data.rows[0].url, "not_blocked");
+      assert.calledOnce(sectionsManagerStub.updateSection);
+      assert.equal(sectionsManagerStub.updateSection.firstCall.args[1].rows.length, 1);
+      assert.equal(sectionsManagerStub.updateSection.firstCall.args[1].rows[0].url, "not_blocked");
     });
     it("should mark stories as new", async () => {
       let fetchStub = globals.sandbox.stub();
       globals.set("fetch", fetchStub);
       globals.set("NewTabUtils", {blockedLinks: {isBlocked: globals.sandbox.spy()}});
       clock.restore();
       const response = JSON.stringify({
-        "list": [
+        "recommendations": [
           {"published_timestamp": Date.now() / 1000},
           {"published_timestamp": "0"},
           {"published_timestamp": (Date.now() - 2 * 24 * 60 * 60 * 1000) / 1000}
         ]
       });
 
       instance.stories_endpoint = "stories-endpoint";
       fetchStub.resolves({ok: true, status: 200, text: () => response});
 
       await instance.fetchStories();
-      assert.calledOnce(instance.store.dispatch);
-      assert.propertyVal(instance.store.dispatch.firstCall.args[0], "type", at.SECTION_ROWS_UPDATE);
-      assert.equal(instance.store.dispatch.firstCall.args[0].data.rows.length, 3);
-      assert.equal(instance.store.dispatch.firstCall.args[0].data.rows[0].type, "now");
-      assert.equal(instance.store.dispatch.firstCall.args[0].data.rows[1].type, "trending");
-      assert.equal(instance.store.dispatch.firstCall.args[0].data.rows[2].type, "trending");
+      assert.calledOnce(sectionsManagerStub.updateSection);
+      assert.equal(sectionsManagerStub.updateSection.firstCall.args[1].rows.length, 3);
+      assert.equal(sectionsManagerStub.updateSection.firstCall.args[1].rows[0].type, "now");
+      assert.equal(sectionsManagerStub.updateSection.firstCall.args[1].rows[1].type, "trending");
+      assert.equal(sectionsManagerStub.updateSection.firstCall.args[1].rows[2].type, "trending");
     });
     it("should fetch topics and send event", async () => {
       let fetchStub = globals.sandbox.stub();
       globals.set("fetch", fetchStub);
 
       const response = `{"topics": [{"name" : "topic1", "url" : "url-topic1"}, {"name" : "topic2", "url" : "url-topic2"}]}`;
       const topics = [{
         "name": "topic1",
@@ -255,20 +236,18 @@ describe("Top Stories Feed", () => {
       }];
 
       instance.topics_endpoint = "topics-endpoint";
       fetchStub.resolves({ok: true, status: 200, text: () => response});
       await instance.fetchTopics();
 
       assert.calledOnce(fetchStub);
       assert.calledWithExactly(fetchStub, instance.topics_endpoint);
-      assert.calledOnce(instance.store.dispatch);
-      assert.propertyVal(instance.store.dispatch.firstCall.args[0], "type", at.SECTION_ROWS_UPDATE);
-      assert.deepEqual(instance.store.dispatch.firstCall.args[0].data.id, SECTION_ID);
-      assert.deepEqual(instance.store.dispatch.firstCall.args[0].data.topics, topics);
+      assert.calledOnce(sectionsManagerStub.updateSection);
+      assert.calledWithMatch(sectionsManagerStub.updateSection, SECTION_ID, {topics});
     });
     it("should report error for unexpected topics response", async () => {
       let fetchStub = globals.sandbox.stub();
       globals.set("fetch", fetchStub);
       globals.sandbox.spy(global.Components.utils, "reportError");
 
       instance.topics_endpoint = "topics-endpoint";
       fetchStub.resolves({ok: false, status: 400});
@@ -277,25 +256,27 @@ describe("Top Stories Feed", () => {
       assert.calledOnce(fetchStub);
       assert.calledWithExactly(fetchStub, instance.topics_endpoint);
       assert.notCalled(instance.store.dispatch);
       assert.called(Components.utils.reportError);
     });
   });
   describe("#update", () => {
     it("should fetch stories after update interval", () => {
+      instance.init();
       instance.fetchStories = sinon.spy();
       instance.onAction({type: at.SYSTEM_TICK});
       assert.notCalled(instance.fetchStories);
 
       clock.tick(STORIES_UPDATE_TIME);
       instance.onAction({type: at.SYSTEM_TICK});
       assert.calledOnce(instance.fetchStories);
     });
     it("should fetch topics after update interval", () => {
+      instance.init();
       instance.fetchTopics = sinon.spy();
       instance.onAction({type: at.SYSTEM_TICK});
       assert.notCalled(instance.fetchTopics);
 
       clock.tick(TOPICS_UPDATE_TIME);
       instance.onAction({type: at.SYSTEM_TICK});
       assert.calledOnce(instance.fetchTopics);
     });
--- a/browser/extensions/activity-stream/test/unit/utils.js
+++ b/browser/extensions/activity-stream/test/unit/utils.js
@@ -122,16 +122,17 @@ FakePrefs.prototype = {
 /**
  * Slimmed down version of toolkit/modules/EventEmitter.jsm
  */
 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 = {
   on(event, listener) {
     if (!this._eventEmitterListeners) {
       this._eventEmitterListeners = new Map();
     }
     if (!this._eventEmitterListeners.has(event)) {
@@ -145,16 +146,30 @@ EventEmitter.prototype = {
     }
     let listeners = this._eventEmitterListeners.get(event);
     if (listeners) {
       this._eventEmitterListeners.set(event, listeners.filter(
         l => l !== listener && l._originalListener !== listener
       ));
     }
   },
+  once(event, listener) {
+    return new Promise(resolve => {
+      let handler = (_, first, ...rest) => {
+        this.off(event, handler);
+        if (listener) {
+          listener(event, first, ...rest);
+        }
+        resolve(first);
+      };
+
+      handler._originalListener = listener;
+      this.on(event, handler);
+    });
+  },
   // All arguments to this method will be sent to listeners
   emit(event, ...args) {
     if (!this._eventEmitterListeners || !this._eventEmitterListeners.has(event)) {
       return;
     }
     let originalListeners = this._eventEmitterListeners.get(event);
     for (let listener of this._eventEmitterListeners.get(event)) {
       // If the object was destroyed during event emission, stop