Bug 1405053 - Export 7eee3552 - Merge pull request #3628 from rlr/gh3626/hack-week-hakcs draft
authorEd Lee <edilee@mozilla.com>
Mon, 02 Oct 2017 10:49:07 -0700
changeset 673691 e07ad29851d792bcfb17d14786b259ecb60503f5
parent 673690 6a6283df4d9eced885ca5a81e0426b4987b38bc1
child 673692 2da0a53a8fc12536c9a5888b15a320123885ac7a
push id82614
push userbmo:edilee@mozilla.com
push dateMon, 02 Oct 2017 17:49:46 +0000
bugs1405053
milestone57.0
Bug 1405053 - Export 7eee3552 - Merge pull request #3628 from rlr/gh3626/hack-week-hakcs MozReview-Commit-ID: G6mrLlacweB
browser/extensions/activity-stream/common/Actions.jsm
browser/extensions/activity-stream/common/Reducers.jsm
browser/extensions/activity-stream/data/content/activity-stream-initial-state.js
browser/extensions/activity-stream/data/content/activity-stream-prerendered.html
browser/extensions/activity-stream/data/content/activity-stream.bundle.js
browser/extensions/activity-stream/data/content/activity-stream.css
browser/extensions/activity-stream/install.rdf.in
browser/extensions/activity-stream/lib/SectionsManager.jsm
browser/extensions/activity-stream/test/unit/common/Reducers.test.js
--- a/browser/extensions/activity-stream/common/Actions.jsm
+++ b/browser/extensions/activity-stream/common/Actions.jsm
@@ -59,16 +59,18 @@ for (const type of [
   "SCREENSHOT_UPDATED",
   "SECTION_DEREGISTER",
   "SECTION_DISABLE",
   "SECTION_ENABLE",
   "SECTION_OPTIONS_CHANGED",
   "SECTION_REGISTER",
   "SECTION_UPDATE",
   "SECTION_UPDATE_CARD",
+  "SETTINGS_CLOSE",
+  "SETTINGS_OPEN",
   "SET_PREF",
   "SHOW_FIREFOX_ACCOUNTS",
   "SNIPPETS_DATA",
   "SNIPPETS_RESET",
   "SYSTEM_TICK",
   "TELEMETRY_IMPRESSION_STATS",
   "TELEMETRY_PERFORMANCE_EVENT",
   "TELEMETRY_UNDESIRED_EVENT",
--- a/browser/extensions/activity-stream/common/Reducers.jsm
+++ b/browser/extensions/activity-stream/common/Reducers.jsm
@@ -40,17 +40,18 @@ const INITIAL_STATE = {
   Prefs: {
     initialized: false,
     values: {}
   },
   Dialog: {
     visible: false,
     data: {}
   },
-  Sections: []
+  Sections: [],
+  PreferencesPane: {visible: false}
 };
 
 function App(prevState = INITIAL_STATE.App, action) {
   switch (action.type) {
     case at.INIT:
       return Object.assign({}, prevState, action.data || {}, {initialized: true});
     case at.LOCALE_UPDATED: {
       if (!action.data) {
@@ -305,16 +306,27 @@ function Snippets(prevState = INITIAL_ST
       return Object.assign({}, prevState, {initialized: true}, action.data);
     case at.SNIPPETS_RESET:
       return INITIAL_STATE.Snippets;
     default:
       return prevState;
   }
 }
 
+function PreferencesPane(prevState = INITIAL_STATE.PreferencesPane, action) {
+  switch (action.type) {
+    case at.SETTINGS_OPEN:
+      return Object.assign({}, prevState, {visible: true});
+    case at.SETTINGS_CLOSE:
+      return Object.assign({}, prevState, {visible: false});
+    default:
+      return prevState;
+  }
+}
+
 this.INITIAL_STATE = INITIAL_STATE;
 this.TOP_SITES_DEFAULT_LENGTH = TOP_SITES_DEFAULT_LENGTH;
 this.TOP_SITES_SHOWMORE_LENGTH = TOP_SITES_SHOWMORE_LENGTH;
 
-this.reducers = {TopSites, App, Snippets, Prefs, Dialog, Sections};
+this.reducers = {TopSites, App, Snippets, Prefs, Dialog, Sections, PreferencesPane};
 this.insertPinned = insertPinned;
 
 this.EXPORTED_SYMBOLS = ["reducers", "INITIAL_STATE", "insertPinned", "TOP_SITES_DEFAULT_LENGTH", "TOP_SITES_SHOWMORE_LENGTH"];
--- a/browser/extensions/activity-stream/data/content/activity-stream-initial-state.js
+++ b/browser/extensions/activity-stream/data/content/activity-stream-initial-state.js
@@ -146,10 +146,13 @@
       },
       "rows": [],
       "order": 2,
       "enabled": true,
       "id": "highlights",
       "icon": "highlights",
       "initialized": false
     }
-  ]
+  ],
+  "PreferencesPane": {
+    "visible": false
+  }
 };
--- a/browser/extensions/activity-stream/data/content/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/data/content/activity-stream-prerendered.html
@@ -4,17 +4,17 @@
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy-Report-Only" content="script-src 'unsafe-inline'; img-src http: https: data: blob:; style-src 'unsafe-inline'; child-src 'none'; object-src 'none'; report-uri https://tiles.services.mozilla.com/v4/links/activity-stream/csp">
     <title></title>
     <link rel="icon" type="image/png" id="favicon" href="chrome://branding/content/icon32.png"/>
     <link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
     <link rel="stylesheet" href="resource://activity-stream/data/content/activity-stream.css" />
   </head>
   <body class="activity-stream">
-    <div id="root"><div class="outer-wrapper fixed-to-top" data-reactroot="" data-reactid="1" data-react-checksum="-1946542515"><main data-reactid="2"><div class="search-wrapper" data-reactid="3"><label for="newtab-search-text" class="search-label" data-reactid="4"><span class="sr-only" data-reactid="5"><span data-reactid="6">Search the Web</span></span></label><input type="search" id="newtab-search-text" maxlength="256" placeholder="Search the Web" title="Search the Web" data-reactid="7"/><button id="searchSubmit" class="search-button" title=" " data-reactid="8"><span class="sr-only" data-reactid="9"><span data-reactid="10"> </span></span></button></div><div class="body-wrapper" data-reactid="11"><section class="top-sites" data-reactid="12"><div class="section-top-bar" data-reactid="13"><h3 class="section-title" data-reactid="14"><span class="icon icon-small-spacer icon-topsites" data-reactid="15"></span><span data-reactid="16"> </span></h3></div><ul class="top-sites-list" data-reactid="17"><li class="top-site-outer placeholder" data-reactid="18"><a data-reactid="19"><div class="tile" aria-hidden="true" data-reactid="20"><span class="letter-fallback" data-reactid="21"></span><div class="screenshot" style="background-image:none;" data-reactid="22"></div></div><div class="title " data-reactid="23"><span dir="auto" data-reactid="24"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="25"><a data-reactid="26"><div class="tile" aria-hidden="true" data-reactid="27"><span class="letter-fallback" data-reactid="28"></span><div class="screenshot" style="background-image:none;" data-reactid="29"></div></div><div class="title " data-reactid="30"><span dir="auto" data-reactid="31"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="32"><a data-reactid="33"><div class="tile" aria-hidden="true" data-reactid="34"><span class="letter-fallback" data-reactid="35"></span><div class="screenshot" style="background-image:none;" data-reactid="36"></div></div><div class="title " data-reactid="37"><span dir="auto" data-reactid="38"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="39"><a data-reactid="40"><div class="tile" aria-hidden="true" data-reactid="41"><span class="letter-fallback" data-reactid="42"></span><div class="screenshot" style="background-image:none;" data-reactid="43"></div></div><div class="title " data-reactid="44"><span dir="auto" data-reactid="45"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="46"><a data-reactid="47"><div class="tile" aria-hidden="true" data-reactid="48"><span class="letter-fallback" data-reactid="49"></span><div class="screenshot" style="background-image:none;" data-reactid="50"></div></div><div class="title " data-reactid="51"><span dir="auto" data-reactid="52"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="53"><a data-reactid="54"><div class="tile" aria-hidden="true" data-reactid="55"><span class="letter-fallback" data-reactid="56"></span><div class="screenshot" style="background-image:none;" data-reactid="57"></div></div><div class="title " data-reactid="58"><span dir="auto" data-reactid="59"></span></div></a></li></ul><div class="edit-topsites-wrapper" data-reactid="60"><div class="edit-topsites-button" data-reactid="61"><button class="edit" title=" " data-reactid="62"><span data-reactid="63"> </span></button></div></div></section><div class="sections-list" data-reactid="64"><section data-reactid="65"><div class="section-top-bar" data-reactid="66"><h3 class="section-title" data-reactid="67"><span class="icon icon-small-spacer icon-pocket" data-reactid="68"></span><span data-reactid="69"> </span></h3></div><ul class="section-list" style="padding:0;" data-reactid="70"><li class="card-outer placeholder" data-reactid="71"><a data-reactid="72"><div class="card" data-reactid="73"><div class="card-details no-image" data-reactid="74"><div class="card-text no-context no-description no-host-name no-image" data-reactid="75"><h4 class="card-title" dir="auto" data-reactid="76"></h4><p class="card-description" dir="auto" data-reactid="77"></p></div><div class="card-context" data-reactid="78"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="79"><a data-reactid="80"><div class="card" data-reactid="81"><div class="card-details no-image" data-reactid="82"><div class="card-text no-context no-description no-host-name no-image" data-reactid="83"><h4 class="card-title" dir="auto" data-reactid="84"></h4><p class="card-description" dir="auto" data-reactid="85"></p></div><div class="card-context" data-reactid="86"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="87"><a data-reactid="88"><div class="card" data-reactid="89"><div class="card-details no-image" data-reactid="90"><div class="card-text no-context no-description no-host-name no-image" data-reactid="91"><h4 class="card-title" dir="auto" data-reactid="92"></h4><p class="card-description" dir="auto" data-reactid="93"></p></div><div class="card-context" data-reactid="94"></div></div></div></a></li></ul><div class="topic" data-reactid="95"><span data-reactid="96"><span data-reactid="97"> </span></span><ul data-reactid="98"></ul></div></section><section data-reactid="99"><div class="section-top-bar" data-reactid="100"><h3 class="section-title" data-reactid="101"><span class="icon icon-small-spacer icon-highlights" data-reactid="102"></span><span data-reactid="103"> </span></h3></div><ul class="section-list" style="padding:0;" data-reactid="104"><li class="card-outer placeholder" data-reactid="105"><a data-reactid="106"><div class="card" data-reactid="107"><div class="card-details no-image" data-reactid="108"><div class="card-text no-context no-description no-host-name no-image" data-reactid="109"><h4 class="card-title" dir="auto" data-reactid="110"></h4><p class="card-description" dir="auto" data-reactid="111"></p></div><div class="card-context" data-reactid="112"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="113"><a data-reactid="114"><div class="card" data-reactid="115"><div class="card-details no-image" data-reactid="116"><div class="card-text no-context no-description no-host-name no-image" data-reactid="117"><h4 class="card-title" dir="auto" data-reactid="118"></h4><p class="card-description" dir="auto" data-reactid="119"></p></div><div class="card-context" data-reactid="120"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="121"><a data-reactid="122"><div class="card" data-reactid="123"><div class="card-details no-image" data-reactid="124"><div class="card-text no-context no-description no-host-name no-image" data-reactid="125"><h4 class="card-title" dir="auto" data-reactid="126"></h4><p class="card-description" dir="auto" data-reactid="127"></p></div><div class="card-context" data-reactid="128"></div></div></div></a></li></ul></section></div></div><!-- react-empty: 129 --></main></div></div>
+    <div id="root"><div class="outer-wrapper fixed-to-top" data-reactroot="" data-reactid="1" data-react-checksum="-1695158303"><main data-reactid="2"><div class="search-wrapper" data-reactid="3"><label for="newtab-search-text" class="search-label" data-reactid="4"><span class="sr-only" data-reactid="5"><span data-reactid="6">Search the Web</span></span></label><input type="search" id="newtab-search-text" maxlength="256" placeholder="Search the Web" title="Search the Web" data-reactid="7"/><button id="searchSubmit" class="search-button" title=" " data-reactid="8"><span class="sr-only" data-reactid="9"><span data-reactid="10"> </span></span></button></div><div class="body-wrapper" data-reactid="11"><section class="section top-sites" data-reactid="12"><div class="section-top-bar" data-reactid="13"><h3 class="section-title" data-reactid="14"><span class="icon icon-small-spacer icon-topsites" data-reactid="15"></span><span data-reactid="16"> </span></h3><span class="section-info-option" data-reactid="17"><img class="info-option-icon" title=" " aria-haspopup="true" aria-controls="info-option" aria-expanded="false" role="note" tabindex="0" data-reactid="18"/><div class="info-option" data-reactid="19"><div class="info-option-header" role="heading" data-reactid="20"><span data-reactid="21"> </span></div><p class="info-option-body" data-reactid="22"><span data-reactid="23"> </span></p><div class="info-option-manage" data-reactid="24"><button data-reactid="25"><span data-reactid="26"> </span></button></div></div></span></div><ul class="top-sites-list" data-reactid="27"><li class="top-site-outer placeholder" data-reactid="28"><a data-reactid="29"><div class="tile" aria-hidden="true" data-reactid="30"><span class="letter-fallback" data-reactid="31"></span><div class="screenshot" style="background-image:none;" data-reactid="32"></div></div><div class="title " data-reactid="33"><span dir="auto" data-reactid="34"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="35"><a data-reactid="36"><div class="tile" aria-hidden="true" data-reactid="37"><span class="letter-fallback" data-reactid="38"></span><div class="screenshot" style="background-image:none;" data-reactid="39"></div></div><div class="title " data-reactid="40"><span dir="auto" data-reactid="41"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="42"><a data-reactid="43"><div class="tile" aria-hidden="true" data-reactid="44"><span class="letter-fallback" data-reactid="45"></span><div class="screenshot" style="background-image:none;" data-reactid="46"></div></div><div class="title " data-reactid="47"><span dir="auto" data-reactid="48"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="49"><a data-reactid="50"><div class="tile" aria-hidden="true" data-reactid="51"><span class="letter-fallback" data-reactid="52"></span><div class="screenshot" style="background-image:none;" data-reactid="53"></div></div><div class="title " data-reactid="54"><span dir="auto" data-reactid="55"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="56"><a data-reactid="57"><div class="tile" aria-hidden="true" data-reactid="58"><span class="letter-fallback" data-reactid="59"></span><div class="screenshot" style="background-image:none;" data-reactid="60"></div></div><div class="title " data-reactid="61"><span dir="auto" data-reactid="62"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="63"><a data-reactid="64"><div class="tile" aria-hidden="true" data-reactid="65"><span class="letter-fallback" data-reactid="66"></span><div class="screenshot" style="background-image:none;" data-reactid="67"></div></div><div class="title " data-reactid="68"><span dir="auto" data-reactid="69"></span></div></a></li></ul><div class="edit-topsites-wrapper" data-reactid="70"><div class="edit-topsites-button" data-reactid="71"><button class="edit" title=" " data-reactid="72"><span data-reactid="73"> </span></button></div></div></section><div class="sections-list" data-reactid="74"><section class="section" data-reactid="75"><div class="section-top-bar" data-reactid="76"><h3 class="section-title" data-reactid="77"><span class="icon icon-small-spacer icon-pocket" data-reactid="78"></span><span data-reactid="79"> </span></h3></div><ul class="section-list" style="padding:0;" data-reactid="80"><li class="card-outer placeholder" data-reactid="81"><a data-reactid="82"><div class="card" data-reactid="83"><div class="card-details no-image" data-reactid="84"><div class="card-text no-context no-description no-host-name no-image" data-reactid="85"><h4 class="card-title" dir="auto" data-reactid="86"></h4><p class="card-description" dir="auto" data-reactid="87"></p></div><div class="card-context" data-reactid="88"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="89"><a data-reactid="90"><div class="card" data-reactid="91"><div class="card-details no-image" data-reactid="92"><div class="card-text no-context no-description no-host-name no-image" data-reactid="93"><h4 class="card-title" dir="auto" data-reactid="94"></h4><p class="card-description" dir="auto" data-reactid="95"></p></div><div class="card-context" data-reactid="96"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="97"><a data-reactid="98"><div class="card" data-reactid="99"><div class="card-details no-image" data-reactid="100"><div class="card-text no-context no-description no-host-name no-image" data-reactid="101"><h4 class="card-title" dir="auto" data-reactid="102"></h4><p class="card-description" dir="auto" data-reactid="103"></p></div><div class="card-context" data-reactid="104"></div></div></div></a></li></ul><div class="topic" data-reactid="105"><span data-reactid="106"><span data-reactid="107"> </span></span><ul data-reactid="108"></ul></div></section><section class="section" data-reactid="109"><div class="section-top-bar" data-reactid="110"><h3 class="section-title" data-reactid="111"><span class="icon icon-small-spacer icon-highlights" data-reactid="112"></span><span data-reactid="113"> </span></h3></div><ul class="section-list" style="padding:0;" data-reactid="114"><li class="card-outer placeholder" data-reactid="115"><a data-reactid="116"><div class="card" data-reactid="117"><div class="card-details no-image" data-reactid="118"><div class="card-text no-context no-description no-host-name no-image" data-reactid="119"><h4 class="card-title" dir="auto" data-reactid="120"></h4><p class="card-description" dir="auto" data-reactid="121"></p></div><div class="card-context" data-reactid="122"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="123"><a data-reactid="124"><div class="card" data-reactid="125"><div class="card-details no-image" data-reactid="126"><div class="card-text no-context no-description no-host-name no-image" data-reactid="127"><h4 class="card-title" dir="auto" data-reactid="128"></h4><p class="card-description" dir="auto" data-reactid="129"></p></div><div class="card-context" data-reactid="130"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="131"><a data-reactid="132"><div class="card" data-reactid="133"><div class="card-details no-image" data-reactid="134"><div class="card-text no-context no-description no-host-name no-image" data-reactid="135"><h4 class="card-title" dir="auto" data-reactid="136"></h4><p class="card-description" dir="auto" data-reactid="137"></p></div><div class="card-context" data-reactid="138"></div></div></div></a></li></ul></section></div></div><!-- react-empty: 139 --></main></div></div>
     <div id="snippets-container">
       <div id="snippets"></div>
     </div>
     <script>
 // Don't directly load the following scripts as part of html to let the page
 // finish loading to render the content sooner.
 for (const src of [
   "resource://activity-stream/data/content/activity-stream-initial-state.js",
--- a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
+++ b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
@@ -55,17 +55,17 @@
 /******/
 /******/ 	// Object.prototype.hasOwnProperty.call
 /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
 /******/
 /******/ 	// __webpack_public_path__
 /******/ 	__webpack_require__.p = "";
 /******/
 /******/ 	// Load entry module and return exports
-/******/ 	return __webpack_require__(__webpack_require__.s = 10);
+/******/ 	return __webpack_require__(__webpack_require__.s = 11);
 /******/ })
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -89,17 +89,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", "DISABLE_ONBOARDING", "INIT", "LOCALE_UPDATED", "MIGRATION_CANCEL", "MIGRATION_COMPLETED", "MIGRATION_START", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_REHYDRATED", "NEW_TAB_STATE_REQUEST", "NEW_TAB_UNLOAD", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_DELETED", "PLACES_LINK_BLOCKED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SET_PREF", "SHOW_FIREFOX_ACCOUNTS", "SNIPPETS_DATA", "SNIPPETS_RESET", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_ADD", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_EDIT", "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", "DISABLE_ONBOARDING", "INIT", "LOCALE_UPDATED", "MIGRATION_CANCEL", "MIGRATION_COMPLETED", "MIGRATION_START", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_REHYDRATED", "NEW_TAB_STATE_REQUEST", "NEW_TAB_UNLOAD", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_DELETED", "PLACES_LINK_BLOCKED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SETTINGS_CLOSE", "SETTINGS_OPEN", "SET_PREF", "SHOW_FIREFOX_ACCOUNTS", "SNIPPETS_DATA", "SNIPPETS_RESET", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_ADD", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_EDIT", "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) {
@@ -387,17 +387,18 @@ const INITIAL_STATE = {
   Prefs: {
     initialized: false,
     values: {}
   },
   Dialog: {
     visible: false,
     data: {}
   },
-  Sections: []
+  Sections: [],
+  PreferencesPane: { visible: false }
 };
 
 function App(prevState = INITIAL_STATE.App, action) {
   switch (action.type) {
     case at.INIT:
       return Object.assign({}, prevState, action.data || {}, { initialized: true });
     case at.LOCALE_UPDATED:
       {
@@ -653,17 +654,28 @@ function Snippets(prevState = INITIAL_ST
       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 };
+function PreferencesPane(prevState = INITIAL_STATE.PreferencesPane, action) {
+  switch (action.type) {
+    case at.SETTINGS_OPEN:
+      return Object.assign({}, prevState, { visible: true });
+    case at.SETTINGS_CLOSE:
+      return Object.assign({}, prevState, { visible: false });
+    default:
+      return prevState;
+  }
+}
+
+var reducers = { TopSites, App, Snippets, Prefs, Dialog, Sections, PreferencesPane };
 module.exports = {
   reducers,
   INITIAL_STATE,
   insertPinned,
   TOP_SITES_DEFAULT_LENGTH,
   TOP_SITES_SHOWMORE_LENGTH
 };
 
@@ -1021,19 +1033,19 @@ module.exports.TopSiteLink = TopSiteLink
 module.exports.TopSitePlaceholder = TopSitePlaceholder;
 
 /***/ }),
 /* 9 */
 /***/ (function(module, exports, __webpack_require__) {
 
 const React = __webpack_require__(1);
 const { injectIntl } = __webpack_require__(2);
-const ContextMenu = __webpack_require__(17);
+const ContextMenu = __webpack_require__(18);
 const { actionCreators: ac } = __webpack_require__(0);
-const linkMenuOptions = __webpack_require__(18);
+const linkMenuOptions = __webpack_require__(19);
 const DEFAULT_SITE_MENU_OPTIONS = ["CheckPinTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"];
 
 class LinkMenu extends React.PureComponent {
   getOptions() {
     const props = this.props;
     const { site, index, source } = props;
 
     // Handle special case of default site
@@ -1077,19 +1089,308 @@ class LinkMenu extends React.PureCompone
 
 module.exports = injectIntl(LinkMenu);
 module.exports._unconnected = LinkMenu;
 
 /***/ }),
 /* 10 */
 /***/ (function(module, exports, __webpack_require__) {
 
+/* WEBPACK VAR INJECTION */(function(global) {var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+const React = __webpack_require__(1);
+const { connect } = __webpack_require__(3);
+const { injectIntl, FormattedMessage } = __webpack_require__(2);
+const Card = __webpack_require__(20);
+const { PlaceholderCard } = Card;
+const Topics = __webpack_require__(22);
+const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
+
+const VISIBLE = "visible";
+const VISIBILITY_CHANGE_EVENT = "visibilitychange";
+const CARDS_PER_ROW = 3;
+
+function getFormattedMessage(message) {
+  return typeof message === "string" ? React.createElement(
+    "span",
+    null,
+    message
+  ) : React.createElement(FormattedMessage, message);
+}
+
+class Info extends React.PureComponent {
+  constructor(props) {
+    super(props);
+    this.onInfoEnter = this.onInfoEnter.bind(this);
+    this.onInfoLeave = this.onInfoLeave.bind(this);
+    this.onManageClick = this.onManageClick.bind(this);
+    this.state = { infoActive: false };
+  }
+
+  /**
+   * 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));
+  }
+  onManageClick() {
+    this.props.dispatch({ type: at.SETTINGS_OPEN });
+    this.props.dispatch(ac.UserEvent({ event: "OPEN_NEWTAB_PREFS" }));
+  }
+  render() {
+    const { infoOption, intl } = this.props;
+    const infoOptionIconA11yAttrs = {
+      "aria-haspopup": "true",
+      "aria-controls": "info-option",
+      "aria-expanded": this.state.infoActive ? "true" : "false",
+      "role": "note",
+      "tabIndex": 0
+    };
+    const sectionInfoTitle = intl.formatMessage({ id: "section_info_option" });
+
+    return React.createElement(
+      "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" },
+          getFormattedMessage(infoOption.header)
+        ),
+        React.createElement(
+          "p",
+          { className: "info-option-body" },
+          infoOption.body && getFormattedMessage(infoOption.body),
+          infoOption.link && React.createElement(
+            "a",
+            { href: infoOption.link.href, target: "_blank", rel: "noopener noreferrer", className: "info-option-link" },
+            getFormattedMessage(infoOption.link.title || infoOption.link)
+          )
+        ),
+        React.createElement(
+          "div",
+          { className: "info-option-manage" },
+          React.createElement(
+            "button",
+            { onClick: this.onManageClick },
+            React.createElement(FormattedMessage, { id: "settings_pane_header" })
+          )
+        )
+      )
+    );
+  }
+}
+
+const InfoIntl = injectIntl(Info);
+
+class Section extends React.PureComponent {
+  _dispatchImpressionStats() {
+    const { props } = this;
+    const maxCards = 3 * props.maxRows;
+    const cards = props.rows.slice(0, maxCards);
+
+    if (this.needsImpressionStats(cards)) {
+      props.dispatch(ac.ImpressionStats({
+        source: props.eventSource,
+        tiles: cards.map(link => ({ id: link.guid })),
+        incognito: props.options && props.options.personalized
+      }));
+      this.impressionCardGuids = cards.map(link => 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;
+
+    if (!props.shouldSendImpressionStats || !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;
+    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();
+    }
+  }
+
+  needsImpressionStats(cards) {
+    if (!this.impressionCardGuids || this.impressionCardGuids.length !== cards.length) {
+      return true;
+    }
+
+    for (let i = 0; i < cards.length; i++) {
+      if (cards[i].guid !== this.impressionCardGuids[i]) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  numberOfPlaceholders(items) {
+    if (items === 0) {
+      return CARDS_PER_ROW;
+    }
+    const remainder = items % CARDS_PER_ROW;
+    if (remainder === 0) {
+      return 0;
+    }
+    return CARDS_PER_ROW - remainder;
+  }
+
+  render() {
+    const {
+      id, eventSource, title, icon, rows,
+      infoOption, emptyState, dispatch, maxRows,
+      contextMenuOptions, initialized
+    } = this.props;
+    const maxCards = CARDS_PER_ROW * maxRows;
+
+    // Show topics only for top stories and if it's not initialized yet (so
+    // content doesn't shift when it is loaded) or has loaded with topics
+    const shouldShowTopics = id === "topstories" && (!this.props.topics || this.props.topics.length > 0);
+
+    const realRows = rows.slice(0, maxCards);
+    const placeholders = this.numberOfPlaceholders(realRows.length);
+
+    // The empty state should only be shown after we have initialized and there is no content.
+    // Otherwise, we should show placeholders.
+    const shouldShowEmptyState = initialized && !rows.length;
+
+    // <Section> <-- React component
+    // <section> <-- HTML5 element
+    return React.createElement(
+      "section",
+      { className: "section" },
+      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"}` }),
+          getFormattedMessage(title)
+        ),
+        infoOption && React.createElement(InfoIntl, { infoOption: infoOption, dispatch: dispatch })
+      ),
+      !shouldShowEmptyState && React.createElement(
+        "ul",
+        { className: "section-list", style: { padding: 0 } },
+        realRows.map((link, index) => link && React.createElement(Card, { key: index, index: index, dispatch: dispatch, link: link, contextMenuOptions: contextMenuOptions,
+          eventSource: eventSource, shouldSendImpressionStats: this.props.shouldSendImpressionStats })),
+        placeholders > 0 && [...new Array(placeholders)].map((_, i) => React.createElement(PlaceholderCard, { key: i }))
+      ),
+      shouldShowEmptyState && React.createElement(
+        "div",
+        { className: "section-empty-state" },
+        React.createElement(
+          "div",
+          { className: "empty-state" },
+          emptyState.icon && emptyState.icon.startsWith("moz-extension://") ? React.createElement("img", { className: "empty-state-icon icon", style: { "background-image": `url('${emptyState.icon}')` } }) : React.createElement("img", { className: `empty-state-icon icon icon-${emptyState.icon}` }),
+          React.createElement(
+            "p",
+            { className: "empty-state-message" },
+            getFormattedMessage(emptyState.message)
+          )
+        )
+      ),
+      shouldShowTopics && React.createElement(Topics, { topics: this.props.topics, read_more_endpoint: this.props.read_more_endpoint })
+    );
+  }
+}
+
+Section.defaultProps = {
+  document: global.document,
+  rows: [],
+  emptyState: {},
+  title: ""
+};
+
+const SectionIntl = injectIntl(Section);
+
+class Sections extends React.PureComponent {
+  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;
+module.exports.Info = Info;
+module.exports.InfoIntl = InfoIntl;
+/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
+
+/***/ }),
+/* 11 */
+/***/ (function(module, exports, __webpack_require__) {
+
 /* WEBPACK VAR INJECTION */(function(global) {const React = __webpack_require__(1);
-const ReactDOM = __webpack_require__(11);
-const Base = __webpack_require__(12);
+const ReactDOM = __webpack_require__(12);
+const Base = __webpack_require__(13);
 const { Provider } = __webpack_require__(3);
 const initStore = __webpack_require__(29);
 const { reducers } = __webpack_require__(6);
 const DetectUserSessionStart = __webpack_require__(31);
 const { addSnippetsSubscriber } = __webpack_require__(32);
 const { actionTypes: at, actionCreators: ac } = __webpack_require__(0);
 
 new DetectUserSessionStart().sendEventOrAddListener();
@@ -1108,34 +1409,34 @@ ReactDOM.render(React.createElement(
   { store: store },
   React.createElement(Base, { isPrerendered: !!global.gActivityStreamPrerenderedState })
 ), document.getElementById("root"));
 
 addSnippetsSubscriber(store);
 /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
 
 /***/ }),
-/* 11 */
+/* 12 */
 /***/ (function(module, exports) {
 
 module.exports = ReactDOM;
 
 /***/ }),
-/* 12 */
+/* 13 */
 /***/ (function(module, exports, __webpack_require__) {
 
 const React = __webpack_require__(1);
 const { connect } = __webpack_require__(3);
 const { addLocaleData, IntlProvider } = __webpack_require__(2);
-const TopSites = __webpack_require__(13);
-const Search = __webpack_require__(19);
-const ConfirmDialog = __webpack_require__(21);
-const ManualMigration = __webpack_require__(22);
-const PreferencesPane = __webpack_require__(23);
-const Sections = __webpack_require__(24);
+const TopSites = __webpack_require__(14);
+const Search = __webpack_require__(23);
+const ConfirmDialog = __webpack_require__(25);
+const ManualMigration = __webpack_require__(26);
+const PreferencesPane = __webpack_require__(27);
+const Sections = __webpack_require__(10);
 const { actionTypes: at, actionCreators: ac } = __webpack_require__(0);
 const { PrerenderData } = __webpack_require__(28);
 
 // Add the locale data for pluralization and relative-time formatting for now,
 // this just uses english locale data. We can make this more sophisticated if
 // more features are needed.
 function addLocaleDataForReactIntl({ locale, textDirection }) {
   addLocaleData([{ locale, parentLocale: "en" }]);
@@ -1229,45 +1530,51 @@ class Base extends React.PureComponent {
     );
   }
 }
 
 module.exports = connect(state => ({ App: state.App, Prefs: state.Prefs }))(Base);
 module.exports._unconnected = Base;
 
 /***/ }),
-/* 13 */
+/* 14 */
 /***/ (function(module, exports, __webpack_require__) {
 
 const React = __webpack_require__(1);
 const { connect } = __webpack_require__(3);
 const { FormattedMessage } = __webpack_require__(2);
 
-const TopSitesPerfTimer = __webpack_require__(14);
-const TopSitesEdit = __webpack_require__(15);
+const TopSitesPerfTimer = __webpack_require__(15);
+const TopSitesEdit = __webpack_require__(16);
 const { TopSite, TopSitePlaceholder } = __webpack_require__(8);
+const { InfoIntl } = __webpack_require__(10);
 
 const TopSites = props => {
   const realTopSites = props.TopSites.rows.slice(0, props.TopSitesCount);
   const placeholderCount = props.TopSitesCount - realTopSites.length;
+  const infoOption = {
+    header: { id: "settings_pane_topsites_header" },
+    body: { id: "settings_pane_topsites_body" }
+  };
   return React.createElement(
     TopSitesPerfTimer,
     null,
     React.createElement(
       "section",
-      { className: "top-sites" },
+      { className: "section top-sites" },
       React.createElement(
         "div",
         { className: "section-top-bar" },
         React.createElement(
           "h3",
           { className: "section-title" },
           React.createElement("span", { className: `icon icon-small-spacer icon-topsites` }),
           React.createElement(FormattedMessage, { id: "header_top_sites" })
-        )
+        ),
+        React.createElement(InfoIntl, { infoOption: infoOption, dispatch: props.dispatch })
       ),
       React.createElement(
         "ul",
         { className: "top-sites-list" },
         realTopSites.map((link, index) => link && React.createElement(TopSite, {
           key: link.guid || link.url,
           dispatch: props.dispatch,
           link: link,
@@ -1279,17 +1586,17 @@ const TopSites = props => {
     )
   );
 };
 
 module.exports = connect(state => ({ TopSites: state.TopSites, TopSitesCount: state.Prefs.values.topSitesCount }))(TopSites);
 module.exports._unconnected = TopSites;
 
 /***/ }),
-/* 14 */
+/* 15 */
 /***/ (function(module, exports, __webpack_require__) {
 
 const React = __webpack_require__(1);
 const { connect } = __webpack_require__(3);
 const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
 const { perfService: perfSvc } = __webpack_require__(7);
 
 /**
@@ -1398,24 +1705,24 @@ class TopSitesPerfTimer extends React.Pu
     return this.props.children;
   }
 }
 
 module.exports = connect(state => ({ TopSites: state.TopSites }))(TopSitesPerfTimer);
 module.exports._unconnected = TopSitesPerfTimer;
 
 /***/ }),
-/* 15 */
+/* 16 */
 /***/ (function(module, exports, __webpack_require__) {
 
 const React = __webpack_require__(1);
 const { FormattedMessage, injectIntl } = __webpack_require__(2);
 const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
 
-const TopSiteForm = __webpack_require__(16);
+const TopSiteForm = __webpack_require__(17);
 const { TopSite, TopSitePlaceholder } = __webpack_require__(8);
 
 const { TOP_SITES_DEFAULT_LENGTH, TOP_SITES_SHOWMORE_LENGTH } = __webpack_require__(6);
 const { TOP_SITES_SOURCE } = __webpack_require__(5);
 
 class TopSitesEdit extends React.PureComponent {
   constructor(props) {
     super(props);
@@ -1586,17 +1893,17 @@ class TopSitesEdit extends React.PureCom
     );
   }
 }
 
 module.exports = injectIntl(TopSitesEdit);
 module.exports._unconnected = TopSitesEdit;
 
 /***/ }),
-/* 16 */
+/* 17 */
 /***/ (function(module, exports, __webpack_require__) {
 
 const React = __webpack_require__(1);
 const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
 const { FormattedMessage } = __webpack_require__(2);
 
 const { TOP_SITES_SOURCE } = __webpack_require__(5);
 
@@ -1766,17 +2073,17 @@ TopSiteForm.defaultProps = {
   url: "",
   index: 0,
   editMode: false // by default we are in "Add New Top Site" mode
 };
 
 module.exports = TopSiteForm;
 
 /***/ }),
-/* 17 */
+/* 18 */
 /***/ (function(module, exports, __webpack_require__) {
 
 const React = __webpack_require__(1);
 
 class ContextMenu extends React.PureComponent {
   constructor(props) {
     super(props);
     this.hideContext = this.hideContext.bind(this);
@@ -1855,17 +2162,17 @@ class ContextMenuItem extends React.Pure
   }
 }
 
 module.exports = ContextMenu;
 module.exports.ContextMenu = ContextMenu;
 module.exports.ContextMenuItem = ContextMenuItem;
 
 /***/ }),
-/* 18 */
+/* 19 */
 /***/ (function(module, exports, __webpack_require__) {
 
 const { actionTypes: at, actionCreators: ac } = __webpack_require__(0);
 
 /**
  * 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.
@@ -1978,733 +2285,23 @@ module.exports = {
     }
   })
 };
 
 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);
 
 /***/ }),
-/* 19 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-/* globals ContentSearchUIController */
-
-
-const React = __webpack_require__(1);
-const { connect } = __webpack_require__(3);
-const { FormattedMessage, injectIntl } = __webpack_require__(2);
-const { actionCreators: ac } = __webpack_require__(0);
-const { IS_NEWTAB } = __webpack_require__(20);
-
-class Search extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.onClick = this.onClick.bind(this);
-    this.onInputMount = this.onInputMount.bind(this);
-  }
-
-  handleEvent(event) {
-    // Also track search events with our own telemetry
-    if (event.detail.type === "Search") {
-      this.props.dispatch(ac.UserEvent({ event: "SEARCH" }));
-    }
-  }
-  onClick(event) {
-    window.gContentSearchController.search(event);
-  }
-  componentWillUnmount() {
-    delete window.gContentSearchController;
-  }
-  onInputMount(input) {
-    if (input) {
-      // The "healthReportKey" and needs to be "newtab" or "abouthome" so that
-      // BrowserUsageTelemetry.jsm knows to handle events with this name, and
-      // can add the appropriate telemetry probes for search. Without the correct
-      // name, certain tests like browser_UsageTelemetry_content.js will fail
-      // (See github ticket #2348 for more details)
-      const healthReportKey = IS_NEWTAB ? "newtab" : "abouthome";
-
-      // The "searchSource" needs to be "newtab" or "homepage" and is sent with
-      // the search data and acts as context for the search request (See
-      // nsISearchEngine.getSubmission). It is necessary so that search engine
-      // plugins can correctly atribute referrals. (See github ticket #3321 for
-      // more details)
-      const searchSource = IS_NEWTAB ? "newtab" : "homepage";
-
-      // gContentSearchController needs to exist as a global so that tests for
-      // the existing about:home can find it; and so it allows these tests to pass.
-      // In the future, when activity stream is default about:home, this can be renamed
-      window.gContentSearchController = new ContentSearchUIController(input, input.parentNode, healthReportKey, searchSource);
-      addEventListener("ContentSearchClient", this);
-
-      // Focus the search box if we are on about:home
-      if (!IS_NEWTAB) {
-        input.focus();
-      }
-    } else {
-      window.gContentSearchController = null;
-      removeEventListener("ContentSearchClient", this);
-    }
-  }
-
-  /*
-   * Do not change the ID on the input field, as legacy newtab code
-   * specifically looks for the id 'newtab-search-text' on input fields
-   * in order to execute searches in various tests
-   */
-  render() {
-    return React.createElement(
-      "div",
-      { className: "search-wrapper" },
-      React.createElement(
-        "label",
-        { htmlFor: "newtab-search-text", className: "search-label" },
-        React.createElement(
-          "span",
-          { className: "sr-only" },
-          React.createElement(FormattedMessage, { id: "search_web_placeholder" })
-        )
-      ),
-      React.createElement("input", {
-        id: "newtab-search-text",
-        maxLength: "256",
-        placeholder: this.props.intl.formatMessage({ id: "search_web_placeholder" }),
-        ref: this.onInputMount,
-        title: this.props.intl.formatMessage({ id: "search_web_placeholder" }),
-        type: "search" }),
-      React.createElement(
-        "button",
-        {
-          id: "searchSubmit",
-          className: "search-button",
-          onClick: this.onClick,
-          title: this.props.intl.formatMessage({ id: "search_button" }) },
-        React.createElement(
-          "span",
-          { className: "sr-only" },
-          React.createElement(FormattedMessage, { id: "search_button" })
-        )
-      )
-    );
-  }
-}
-
-// initialized is passed to props so that Search will rerender when it receives strings
-module.exports = connect(state => ({ locale: state.App.locale }))(injectIntl(Search));
-module.exports._unconnected = Search;
-
-/***/ }),
 /* 20 */
 /***/ (function(module, exports, __webpack_require__) {
 
-/* WEBPACK VAR INJECTION */(function(global) {module.exports = {
-  // constant to know if the page is about:newtab or about:home
-  IS_NEWTAB: global.document && global.document.documentURI === "about:newtab"
-};
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
-
-/***/ }),
-/* 21 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const React = __webpack_require__(1);
-const { connect } = __webpack_require__(3);
-const { FormattedMessage } = __webpack_require__(2);
-const { actionTypes, actionCreators: ac } = __webpack_require__(0);
-
-/**
- * ConfirmDialog component.
- * One primary action button, one cancel button.
- *
- * Content displayed is controlled by `data` prop the component receives.
- * Example:
- * data: {
- *   // Any sort of data needed to be passed around by actions.
- *   payload: site.url,
- *   // Primary button SendToMain action.
- *   action: "DELETE_HISTORY_URL",
- *   // Primary button USerEvent action.
- *   userEvent: "DELETE",
- *   // Array of locale ids to display.
- *   message_body: ["confirm_history_delete_p1", "confirm_history_delete_notice_p2"],
- *   // Text for primary button.
- *   confirm_button_string_id: "menu_action_delete"
- * },
- */
-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" },
-        React.createElement(
-          "section",
-          { className: "modal-message" },
-          this._renderModalMessage()
-        ),
-        React.createElement(
-          "section",
-          { className: "actions" },
-          React.createElement(
-            "button",
-            { onClick: this._handleCancelBtn },
-            React.createElement(FormattedMessage, { id: "topsites_form_cancel_button" })
-          ),
-          React.createElement(
-            "button",
-            { className: "done", onClick: this._handleConfirmBtn },
-            React.createElement(FormattedMessage, { id: this.props.data.confirm_button_string_id })
-          )
-        )
-      )
-    );
-  }
-});
-
-module.exports = connect(state => state.Dialog)(ConfirmDialog);
-module.exports._unconnected = ConfirmDialog;
-module.exports.Dialog = ConfirmDialog;
-
-/***/ }),
-/* 22 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const React = __webpack_require__(1);
-const { connect } = __webpack_require__(3);
-const { FormattedMessage } = __webpack_require__(2);
-const { actionTypes: at, actionCreators: ac } = __webpack_require__(0);
-
-/**
- * Manual migration component used to start the profile import wizard.
- * Message is presented temporarily and will go away if:
- * 1.  User clicks "No Thanks"
- * 2.  User completed the data import
- * 3.  After 3 active days
- * 4.  User clicks "Cancel" on the import wizard (currently not implemented).
- */
-class ManualMigration extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.onLaunchTour = this.onLaunchTour.bind(this);
-    this.onCancelTour = this.onCancelTour.bind(this);
-  }
-  onLaunchTour() {
-    this.props.dispatch(ac.SendToMain({ type: at.MIGRATION_START }));
-    this.props.dispatch(ac.UserEvent({ event: at.MIGRATION_START }));
-  }
-
-  onCancelTour() {
-    this.props.dispatch(ac.SendToMain({ type: at.MIGRATION_CANCEL }));
-    this.props.dispatch(ac.UserEvent({ event: at.MIGRATION_CANCEL }));
-  }
-
-  render() {
-    return React.createElement(
-      "div",
-      { className: "manual-migration-container" },
-      React.createElement(
-        "p",
-        null,
-        React.createElement("span", { className: "icon icon-import" }),
-        React.createElement(FormattedMessage, { id: "manual_migration_explanation2" })
-      ),
-      React.createElement(
-        "div",
-        { className: "manual-migration-actions actions" },
-        React.createElement(
-          "button",
-          { className: "dismiss", onClick: this.onCancelTour },
-          React.createElement(FormattedMessage, { id: "manual_migration_cancel_button" })
-        ),
-        React.createElement(
-          "button",
-          { onClick: this.onLaunchTour },
-          React.createElement(FormattedMessage, { id: "manual_migration_import_button" })
-        )
-      )
-    );
-  }
-}
-
-module.exports = connect()(ManualMigration);
-module.exports._unconnected = ManualMigration;
-
-/***/ }),
-/* 23 */
-/***/ (function(module, exports, __webpack_require__) {
-
-const React = __webpack_require__(1);
-const { connect } = __webpack_require__(3);
-const { injectIntl, FormattedMessage } = __webpack_require__(2);
-const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
-const { TOP_SITES_DEFAULT_LENGTH, TOP_SITES_SHOWMORE_LENGTH } = __webpack_require__(6);
-
-const getFormattedMessage = message => typeof message === "string" ? React.createElement(
-  "span",
-  null,
-  message
-) : React.createElement(FormattedMessage, message);
-
-const PreferencesInput = props => React.createElement(
-  "section",
-  null,
-  React.createElement("input", { type: "checkbox", id: props.prefName, name: props.prefName, checked: props.value, disabled: props.disabled, onChange: props.onChange, className: props.className }),
-  React.createElement(
-    "label",
-    { htmlFor: props.prefName, className: props.labelClassName },
-    getFormattedMessage(props.titleString)
-  ),
-  props.descString && React.createElement(
-    "p",
-    { className: "prefs-input-description" },
-    getFormattedMessage(props.descString)
-  )
-);
-
-class PreferencesPane extends React.PureComponent {
-  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);
-    this.onWrapperMount = this.onWrapperMount.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.wrapper.contains(event.target)) {
-      this.togglePane();
-    }
-  }
-  handlePrefChange(event) {
-    const target = event.target;
-    const { name, checked } = target;
-    let value = checked;
-    if (name === "topSitesCount") {
-      value = checked ? TOP_SITES_SHOWMORE_LENGTH : TOP_SITES_DEFAULT_LENGTH;
-    }
-    this.props.dispatch(ac.SetPref(name, value));
-  }
-  handleSectionChange(event) {
-    const target = event.target;
-    const id = target.name;
-    const type = target.checked ? at.SECTION_ENABLE : at.SECTION_DISABLE;
-    this.props.dispatch(ac.SendToMain({ type, data: id }));
-  }
-  togglePane() {
-    this.setState({ visible: !this.state.visible });
-    const event = this.state.visible ? "CLOSE_NEWTAB_PREFS" : "OPEN_NEWTAB_PREFS";
-    this.props.dispatch(ac.UserEvent({ event }));
-  }
-  onWrapperMount(wrapper) {
-    this.wrapper = wrapper;
-  }
-  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: this.onWrapperMount },
-      React.createElement(
-        "div",
-        { className: "prefs-pane-button" },
-        React.createElement("button", {
-          className: `prefs-button icon ${isVisible ? "icon-dismiss" : "icon-settings"}`,
-          title: props.intl.formatMessage({ id: isVisible ? "settings_pane_done_button" : "settings_pane_button_label" }),
-          onClick: this.togglePane })
-      ),
-      React.createElement(
-        "div",
-        { className: "prefs-pane" },
-        React.createElement(
-          "div",
-          { className: `sidebar ${isVisible ? "" : "hidden"}` },
-          React.createElement(
-            "div",
-            { className: "prefs-modal-inner-wrapper" },
-            React.createElement(
-              "h1",
-              null,
-              React.createElement(FormattedMessage, { id: "settings_pane_header" })
-            ),
-            React.createElement(
-              "p",
-              null,
-              React.createElement(FormattedMessage, { id: "settings_pane_body2" })
-            ),
-            React.createElement(PreferencesInput, { className: "showSearch", prefName: "showSearch", value: prefs.showSearch, onChange: this.handlePrefChange,
-              titleString: { id: "settings_pane_search_header" }, descString: { id: "settings_pane_search_body" } }),
-            React.createElement("hr", null),
-            React.createElement(PreferencesInput, { className: "showTopSites", prefName: "showTopSites", value: prefs.showTopSites, onChange: this.handlePrefChange,
-              titleString: { id: "settings_pane_topsites_header" }, descString: { id: "settings_pane_topsites_body" } }),
-            React.createElement(
-              "div",
-              { className: `options${prefs.showTopSites ? "" : " disabled"}` },
-              React.createElement(PreferencesInput, { className: "showMoreTopSites", prefName: "topSitesCount", disabled: !prefs.showTopSites,
-                value: prefs.topSitesCount !== TOP_SITES_DEFAULT_LENGTH, onChange: this.handlePrefChange,
-                titleString: { id: "settings_pane_topsites_options_showmore" }, labelClassName: "icon icon-topsites" })
-            ),
-            sections.filter(section => !section.shouldHidePref).map(({ id, title, enabled, pref }) => React.createElement(PreferencesInput, { key: id, className: "showSection", prefName: pref && pref.feed || id,
-              value: enabled, onChange: pref && pref.feed ? this.handlePrefChange : this.handleSectionChange,
-              titleString: pref && pref.titleString || title, descString: pref && pref.descString })),
-            React.createElement("hr", null),
-            React.createElement(PreferencesInput, { className: "showSnippets", prefName: "feeds.snippets",
-              value: prefs["feeds.snippets"], onChange: this.handlePrefChange,
-              titleString: { id: "settings_pane_snippets_header" },
-              descString: { id: "settings_pane_snippets_body" } })
-          ),
-          React.createElement(
-            "section",
-            { className: "actions" },
-            React.createElement(
-              "button",
-              { className: "done", onClick: this.togglePane },
-              React.createElement(FormattedMessage, { id: "settings_pane_done_button" })
-            )
-          )
-        )
-      )
-    );
-  }
-}
-
-module.exports = connect(state => ({ Prefs: state.Prefs, Sections: state.Sections }))(injectIntl(PreferencesPane));
-module.exports.PreferencesPane = PreferencesPane;
-module.exports.PreferencesInput = PreferencesInput;
-
-/***/ }),
-/* 24 */
-/***/ (function(module, exports, __webpack_require__) {
-
-/* WEBPACK VAR INJECTION */(function(global) {var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
-
-const React = __webpack_require__(1);
-const { connect } = __webpack_require__(3);
-const { injectIntl, FormattedMessage } = __webpack_require__(2);
-const Card = __webpack_require__(25);
-const { PlaceholderCard } = Card;
-const Topics = __webpack_require__(27);
-const { actionCreators: ac } = __webpack_require__(0);
-
-const VISIBLE = "visible";
-const VISIBILITY_CHANGE_EVENT = "visibilitychange";
-const CARDS_PER_ROW = 3;
-
-class Section extends React.PureComponent {
-  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;
-    const maxCards = 3 * props.maxRows;
-    const cards = props.rows.slice(0, maxCards);
-
-    if (this.needsImpressionStats(cards)) {
-      props.dispatch(ac.ImpressionStats({
-        source: props.eventSource,
-        tiles: cards.map(link => ({ id: link.guid })),
-        incognito: props.options && props.options.personalized
-      }));
-      this.impressionCardGuids = cards.map(link => 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;
-
-    if (!props.shouldSendImpressionStats || !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;
-    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();
-    }
-  }
-
-  needsImpressionStats(cards) {
-    if (!this.impressionCardGuids || this.impressionCardGuids.length !== cards.length) {
-      return true;
-    }
-
-    for (let i = 0; i < cards.length; i++) {
-      if (cards[i].guid !== this.impressionCardGuids[i]) {
-        return true;
-      }
-    }
-
-    return false;
-  }
-
-  numberOfPlaceholders(items) {
-    if (items === 0) {
-      return CARDS_PER_ROW;
-    }
-    const remainder = items % CARDS_PER_ROW;
-    if (remainder === 0) {
-      return 0;
-    }
-    return CARDS_PER_ROW - remainder;
-  }
-
-  render() {
-    const {
-      id, eventSource, title, icon, rows,
-      infoOption, emptyState, dispatch, maxRows,
-      contextMenuOptions, intl, initialized
-    } = this.props;
-    const maxCards = CARDS_PER_ROW * maxRows;
-
-    // Show topics only for top stories and if it's not initialized yet (so
-    // content doesn't shift when it is loaded) or has loaded with topics
-    const shouldShowTopics = id === "topstories" && (!this.props.topics || this.props.topics.length > 0);
-
-    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" });
-
-    const realRows = rows.slice(0, maxCards);
-    const placeholders = this.numberOfPlaceholders(realRows.length);
-
-    // The empty state should only be shown after we have initialized and there is no content.
-    // Otherwise, we should show placeholders.
-    const shouldShowEmptyState = initialized && !rows.length;
-
-    // <Section> <-- React component
-    // <section> <-- HTML5 element
-    return React.createElement(
-      "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)
-            )
-          )
-        )
-      ),
-      !shouldShowEmptyState && React.createElement(
-        "ul",
-        { className: "section-list", style: { padding: 0 } },
-        realRows.map((link, index) => link && React.createElement(Card, { key: index, index: index, dispatch: dispatch, link: link, contextMenuOptions: contextMenuOptions,
-          eventSource: eventSource, shouldSendImpressionStats: this.props.shouldSendImpressionStats })),
-        placeholders > 0 && [...new Array(placeholders)].map((_, i) => React.createElement(PlaceholderCard, { key: i }))
-      ),
-      shouldShowEmptyState && React.createElement(
-        "div",
-        { className: "section-empty-state" },
-        React.createElement(
-          "div",
-          { className: "empty-state" },
-          emptyState.icon && emptyState.icon.startsWith("moz-extension://") ? React.createElement("img", { className: "empty-state-icon icon", style: { "background-image": `url('${emptyState.icon}')` } }) : React.createElement("img", { className: `empty-state-icon icon icon-${emptyState.icon}` }),
-          React.createElement(
-            "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,
-  rows: [],
-  emptyState: {},
-  title: ""
-};
-
-const SectionIntl = injectIntl(Section);
-
-class Sections extends React.PureComponent {
-  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__(4)))
-
-/***/ }),
-/* 25 */
-/***/ (function(module, exports, __webpack_require__) {
-
 const React = __webpack_require__(1);
 const LinkMenu = __webpack_require__(9);
 const { FormattedMessage } = __webpack_require__(2);
-const cardContextTypes = __webpack_require__(26);
+const cardContextTypes = __webpack_require__(21);
 const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
 
 // Keep track of pending image loads to only request once
 const gImageLoading = new Map();
 
 /**
  * Card component.
  * Cards are found within a Section component and contain information about a link such
@@ -2890,17 +2487,17 @@ class Card extends React.PureComponent {
 Card.defaultProps = { link: {} };
 
 const PlaceholderCard = () => React.createElement(Card, { placeholder: true });
 
 module.exports = Card;
 module.exports.PlaceholderCard = PlaceholderCard;
 
 /***/ }),
-/* 26 */
+/* 21 */
 /***/ (function(module, exports) {
 
 module.exports = {
   history: {
     intlID: "type_label_visited",
     icon: "historyItem"
   },
   bookmark: {
@@ -2913,17 +2510,17 @@ module.exports = {
   },
   now: {
     intlID: "type_label_now",
     icon: "now"
   }
 };
 
 /***/ }),
-/* 27 */
+/* 22 */
 /***/ (function(module, exports, __webpack_require__) {
 
 const React = __webpack_require__(1);
 const { FormattedMessage } = __webpack_require__(2);
 
 class Topic extends React.PureComponent {
   render() {
     const { url, name } = this.props;
@@ -2964,16 +2561,468 @@ class Topics extends React.PureComponent
   }
 }
 
 module.exports = Topics;
 module.exports._unconnected = Topics;
 module.exports.Topic = Topic;
 
 /***/ }),
+/* 23 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/* globals ContentSearchUIController */
+
+
+const React = __webpack_require__(1);
+const { connect } = __webpack_require__(3);
+const { FormattedMessage, injectIntl } = __webpack_require__(2);
+const { actionCreators: ac } = __webpack_require__(0);
+const { IS_NEWTAB } = __webpack_require__(24);
+
+class Search extends React.PureComponent {
+  constructor(props) {
+    super(props);
+    this.onClick = this.onClick.bind(this);
+    this.onInputMount = this.onInputMount.bind(this);
+  }
+
+  handleEvent(event) {
+    // Also track search events with our own telemetry
+    if (event.detail.type === "Search") {
+      this.props.dispatch(ac.UserEvent({ event: "SEARCH" }));
+    }
+  }
+  onClick(event) {
+    window.gContentSearchController.search(event);
+  }
+  componentWillUnmount() {
+    delete window.gContentSearchController;
+  }
+  onInputMount(input) {
+    if (input) {
+      // The "healthReportKey" and needs to be "newtab" or "abouthome" so that
+      // BrowserUsageTelemetry.jsm knows to handle events with this name, and
+      // can add the appropriate telemetry probes for search. Without the correct
+      // name, certain tests like browser_UsageTelemetry_content.js will fail
+      // (See github ticket #2348 for more details)
+      const healthReportKey = IS_NEWTAB ? "newtab" : "abouthome";
+
+      // The "searchSource" needs to be "newtab" or "homepage" and is sent with
+      // the search data and acts as context for the search request (See
+      // nsISearchEngine.getSubmission). It is necessary so that search engine
+      // plugins can correctly atribute referrals. (See github ticket #3321 for
+      // more details)
+      const searchSource = IS_NEWTAB ? "newtab" : "homepage";
+
+      // gContentSearchController needs to exist as a global so that tests for
+      // the existing about:home can find it; and so it allows these tests to pass.
+      // In the future, when activity stream is default about:home, this can be renamed
+      window.gContentSearchController = new ContentSearchUIController(input, input.parentNode, healthReportKey, searchSource);
+      addEventListener("ContentSearchClient", this);
+
+      // Focus the search box if we are on about:home
+      if (!IS_NEWTAB) {
+        input.focus();
+      }
+    } else {
+      window.gContentSearchController = null;
+      removeEventListener("ContentSearchClient", this);
+    }
+  }
+
+  /*
+   * Do not change the ID on the input field, as legacy newtab code
+   * specifically looks for the id 'newtab-search-text' on input fields
+   * in order to execute searches in various tests
+   */
+  render() {
+    return React.createElement(
+      "div",
+      { className: "search-wrapper" },
+      React.createElement(
+        "label",
+        { htmlFor: "newtab-search-text", className: "search-label" },
+        React.createElement(
+          "span",
+          { className: "sr-only" },
+          React.createElement(FormattedMessage, { id: "search_web_placeholder" })
+        )
+      ),
+      React.createElement("input", {
+        id: "newtab-search-text",
+        maxLength: "256",
+        placeholder: this.props.intl.formatMessage({ id: "search_web_placeholder" }),
+        ref: this.onInputMount,
+        title: this.props.intl.formatMessage({ id: "search_web_placeholder" }),
+        type: "search" }),
+      React.createElement(
+        "button",
+        {
+          id: "searchSubmit",
+          className: "search-button",
+          onClick: this.onClick,
+          title: this.props.intl.formatMessage({ id: "search_button" }) },
+        React.createElement(
+          "span",
+          { className: "sr-only" },
+          React.createElement(FormattedMessage, { id: "search_button" })
+        )
+      )
+    );
+  }
+}
+
+// initialized is passed to props so that Search will rerender when it receives strings
+module.exports = connect(state => ({ locale: state.App.locale }))(injectIntl(Search));
+module.exports._unconnected = Search;
+
+/***/ }),
+/* 24 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/* WEBPACK VAR INJECTION */(function(global) {module.exports = {
+  // constant to know if the page is about:newtab or about:home
+  IS_NEWTAB: global.document && global.document.documentURI === "about:newtab"
+};
+/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
+
+/***/ }),
+/* 25 */
+/***/ (function(module, exports, __webpack_require__) {
+
+const React = __webpack_require__(1);
+const { connect } = __webpack_require__(3);
+const { FormattedMessage } = __webpack_require__(2);
+const { actionTypes, actionCreators: ac } = __webpack_require__(0);
+
+/**
+ * ConfirmDialog component.
+ * One primary action button, one cancel button.
+ *
+ * Content displayed is controlled by `data` prop the component receives.
+ * Example:
+ * data: {
+ *   // Any sort of data needed to be passed around by actions.
+ *   payload: site.url,
+ *   // Primary button SendToMain action.
+ *   action: "DELETE_HISTORY_URL",
+ *   // Primary button USerEvent action.
+ *   userEvent: "DELETE",
+ *   // Array of locale ids to display.
+ *   message_body: ["confirm_history_delete_p1", "confirm_history_delete_notice_p2"],
+ *   // Text for primary button.
+ *   confirm_button_string_id: "menu_action_delete"
+ * },
+ */
+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" },
+        React.createElement(
+          "section",
+          { className: "modal-message" },
+          this._renderModalMessage()
+        ),
+        React.createElement(
+          "section",
+          { className: "actions" },
+          React.createElement(
+            "button",
+            { onClick: this._handleCancelBtn },
+            React.createElement(FormattedMessage, { id: "topsites_form_cancel_button" })
+          ),
+          React.createElement(
+            "button",
+            { className: "done", onClick: this._handleConfirmBtn },
+            React.createElement(FormattedMessage, { id: this.props.data.confirm_button_string_id })
+          )
+        )
+      )
+    );
+  }
+});
+
+module.exports = connect(state => state.Dialog)(ConfirmDialog);
+module.exports._unconnected = ConfirmDialog;
+module.exports.Dialog = ConfirmDialog;
+
+/***/ }),
+/* 26 */
+/***/ (function(module, exports, __webpack_require__) {
+
+const React = __webpack_require__(1);
+const { connect } = __webpack_require__(3);
+const { FormattedMessage } = __webpack_require__(2);
+const { actionTypes: at, actionCreators: ac } = __webpack_require__(0);
+
+/**
+ * Manual migration component used to start the profile import wizard.
+ * Message is presented temporarily and will go away if:
+ * 1.  User clicks "No Thanks"
+ * 2.  User completed the data import
+ * 3.  After 3 active days
+ * 4.  User clicks "Cancel" on the import wizard (currently not implemented).
+ */
+class ManualMigration extends React.PureComponent {
+  constructor(props) {
+    super(props);
+    this.onLaunchTour = this.onLaunchTour.bind(this);
+    this.onCancelTour = this.onCancelTour.bind(this);
+  }
+  onLaunchTour() {
+    this.props.dispatch(ac.SendToMain({ type: at.MIGRATION_START }));
+    this.props.dispatch(ac.UserEvent({ event: at.MIGRATION_START }));
+  }
+
+  onCancelTour() {
+    this.props.dispatch(ac.SendToMain({ type: at.MIGRATION_CANCEL }));
+    this.props.dispatch(ac.UserEvent({ event: at.MIGRATION_CANCEL }));
+  }
+
+  render() {
+    return React.createElement(
+      "div",
+      { className: "manual-migration-container" },
+      React.createElement(
+        "p",
+        null,
+        React.createElement("span", { className: "icon icon-import" }),
+        React.createElement(FormattedMessage, { id: "manual_migration_explanation2" })
+      ),
+      React.createElement(
+        "div",
+        { className: "manual-migration-actions actions" },
+        React.createElement(
+          "button",
+          { className: "dismiss", onClick: this.onCancelTour },
+          React.createElement(FormattedMessage, { id: "manual_migration_cancel_button" })
+        ),
+        React.createElement(
+          "button",
+          { onClick: this.onLaunchTour },
+          React.createElement(FormattedMessage, { id: "manual_migration_import_button" })
+        )
+      )
+    );
+  }
+}
+
+module.exports = connect()(ManualMigration);
+module.exports._unconnected = ManualMigration;
+
+/***/ }),
+/* 27 */
+/***/ (function(module, exports, __webpack_require__) {
+
+const React = __webpack_require__(1);
+const { connect } = __webpack_require__(3);
+const { injectIntl, FormattedMessage } = __webpack_require__(2);
+const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
+const { TOP_SITES_DEFAULT_LENGTH, TOP_SITES_SHOWMORE_LENGTH } = __webpack_require__(6);
+
+const getFormattedMessage = message => typeof message === "string" ? React.createElement(
+  "span",
+  null,
+  message
+) : React.createElement(FormattedMessage, message);
+
+const PreferencesInput = props => React.createElement(
+  "section",
+  null,
+  React.createElement("input", { type: "checkbox", id: props.prefName, name: props.prefName, checked: props.value, disabled: props.disabled, onChange: props.onChange, className: props.className }),
+  React.createElement(
+    "label",
+    { htmlFor: props.prefName, className: props.labelClassName },
+    getFormattedMessage(props.titleString)
+  ),
+  props.descString && React.createElement(
+    "p",
+    { className: "prefs-input-description" },
+    getFormattedMessage(props.descString)
+  )
+);
+
+class PreferencesPane extends React.PureComponent {
+  constructor(props) {
+    super(props);
+    this.handleClickOutside = this.handleClickOutside.bind(this);
+    this.handlePrefChange = this.handlePrefChange.bind(this);
+    this.handleSectionChange = this.handleSectionChange.bind(this);
+    this.togglePane = this.togglePane.bind(this);
+    this.onWrapperMount = this.onWrapperMount.bind(this);
+  }
+  componentDidUpdate(prevProps, prevState) {
+    if (prevProps.PreferencesPane.visible !== this.props.PreferencesPane.visible) {
+      // While the sidebar is open, listen for all document clicks.
+      if (this.isSidebarOpen()) {
+        document.addEventListener("click", this.handleClickOutside);
+      } else {
+        document.removeEventListener("click", this.handleClickOutside);
+      }
+    }
+  }
+  isSidebarOpen() {
+    return this.props.PreferencesPane.visible;
+  }
+  handleClickOutside(event) {
+    // if we are showing the sidebar and there is a click outside, close it.
+    if (this.isSidebarOpen() && !this.wrapper.contains(event.target)) {
+      this.togglePane();
+    }
+  }
+  handlePrefChange(event) {
+    const target = event.target;
+    const { name, checked } = target;
+    let value = checked;
+    if (name === "topSitesCount") {
+      value = checked ? TOP_SITES_SHOWMORE_LENGTH : TOP_SITES_DEFAULT_LENGTH;
+    }
+    this.props.dispatch(ac.SetPref(name, value));
+  }
+  handleSectionChange(event) {
+    const target = event.target;
+    const id = target.name;
+    const type = target.checked ? at.SECTION_ENABLE : at.SECTION_DISABLE;
+    this.props.dispatch(ac.SendToMain({ type, data: id }));
+  }
+  togglePane() {
+    if (this.isSidebarOpen()) {
+      this.props.dispatch({ type: at.SETTINGS_CLOSE });
+      this.props.dispatch(ac.UserEvent({ event: "CLOSE_NEWTAB_PREFS" }));
+    } else {
+      this.props.dispatch({ type: at.SETTINGS_OPEN });
+      this.props.dispatch(ac.UserEvent({ event: "OPEN_NEWTAB_PREFS" }));
+    }
+  }
+  onWrapperMount(wrapper) {
+    this.wrapper = wrapper;
+  }
+  render() {
+    const props = this.props;
+    const prefs = props.Prefs.values;
+    const sections = props.Sections;
+    const isVisible = this.isSidebarOpen();
+    return React.createElement(
+      "div",
+      { className: "prefs-pane-wrapper", ref: this.onWrapperMount },
+      React.createElement(
+        "div",
+        { className: "prefs-pane-button" },
+        React.createElement("button", {
+          className: `prefs-button icon ${isVisible ? "icon-dismiss" : "icon-settings"}`,
+          title: props.intl.formatMessage({ id: isVisible ? "settings_pane_done_button" : "settings_pane_button_label" }),
+          onClick: this.togglePane })
+      ),
+      React.createElement(
+        "div",
+        { className: "prefs-pane" },
+        React.createElement(
+          "div",
+          { className: `sidebar ${isVisible ? "" : "hidden"}` },
+          React.createElement(
+            "div",
+            { className: "prefs-modal-inner-wrapper" },
+            React.createElement(
+              "h1",
+              null,
+              React.createElement(FormattedMessage, { id: "settings_pane_header" })
+            ),
+            React.createElement(
+              "p",
+              null,
+              React.createElement(FormattedMessage, { id: "settings_pane_body2" })
+            ),
+            React.createElement(PreferencesInput, { className: "showSearch", prefName: "showSearch", value: prefs.showSearch, onChange: this.handlePrefChange,
+              titleString: { id: "settings_pane_search_header" }, descString: { id: "settings_pane_search_body" } }),
+            React.createElement("hr", null),
+            React.createElement(PreferencesInput, { className: "showTopSites", prefName: "showTopSites", value: prefs.showTopSites, onChange: this.handlePrefChange,
+              titleString: { id: "settings_pane_topsites_header" }, descString: { id: "settings_pane_topsites_body" } }),
+            React.createElement(
+              "div",
+              { className: `options${prefs.showTopSites ? "" : " disabled"}` },
+              React.createElement(PreferencesInput, { className: "showMoreTopSites", prefName: "topSitesCount", disabled: !prefs.showTopSites,
+                value: prefs.topSitesCount !== TOP_SITES_DEFAULT_LENGTH, onChange: this.handlePrefChange,
+                titleString: { id: "settings_pane_topsites_options_showmore" }, labelClassName: "icon icon-topsites" })
+            ),
+            sections.filter(section => !section.shouldHidePref).map(({ id, title, enabled, pref }) => React.createElement(PreferencesInput, { key: id, className: "showSection", prefName: pref && pref.feed || id,
+              value: enabled, onChange: pref && pref.feed ? this.handlePrefChange : this.handleSectionChange,
+              titleString: pref && pref.titleString || title, descString: pref && pref.descString })),
+            React.createElement("hr", null),
+            React.createElement(PreferencesInput, { className: "showSnippets", prefName: "feeds.snippets",
+              value: prefs["feeds.snippets"], onChange: this.handlePrefChange,
+              titleString: { id: "settings_pane_snippets_header" },
+              descString: { id: "settings_pane_snippets_body" } })
+          ),
+          React.createElement(
+            "section",
+            { className: "actions" },
+            React.createElement(
+              "button",
+              { className: "done", onClick: this.togglePane },
+              React.createElement(FormattedMessage, { id: "settings_pane_done_button" })
+            )
+          )
+        )
+      )
+    );
+  }
+}
+
+module.exports = connect(state => ({ Prefs: state.Prefs, PreferencesPane: state.PreferencesPane, Sections: state.Sections }))(injectIntl(PreferencesPane));
+module.exports.PreferencesPane = PreferencesPane;
+module.exports.PreferencesInput = PreferencesInput;
+
+/***/ }),
 /* 28 */
 /***/ (function(module, exports) {
 
 class _PrerenderData {
   constructor(options) {
     this.initialPrefs = options.initialPrefs;
     this.initialSections = options.initialSections;
     this._setValidation(options.validation);
--- a/browser/extensions/activity-stream/data/content/activity-stream.css
+++ b/browser/extensions/activity-stream/data/content/activity-stream.css
@@ -413,18 +413,22 @@ main {
         .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; }
 
 .edit-topsites-wrapper .edit-topsites-button {
   position: absolute;
-  offset-inline-end: 0;
-  top: -2px; }
+  offset-inline-end: 21px;
+  top: -2px;
+  opacity: 0;
+  transition: opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); }
+  .edit-topsites-wrapper .edit-topsites-button:focus, .edit-topsites-wrapper .edit-topsites-button:active {
+    opacity: 1; }
   .edit-topsites-wrapper .edit-topsites-button button {
     background: none;
     border: 0;
     color: #737373;
     cursor: pointer;
     font-size: 12px;
     padding: 0; }
     .edit-topsites-wrapper .edit-topsites-button button:focus {
@@ -452,16 +456,19 @@ main {
   width: auto; }
   .edit-topsites-wrapper .show-more:dir(rtl),
   .edit-topsites-wrapper .show-less:dir(rtl) {
     background-position: right 10px center; }
   .edit-topsites-wrapper .show-more span,
   .edit-topsites-wrapper .show-less span {
     padding-inline-start: 3px; }
 
+section.top-sites:hover .edit-topsites-button {
+  opacity: 1; }
+
 .topsite-form .form-wrapper {
   margin: auto;
   max-width: 350px;
   padding: 15px 0; }
   .topsite-form .form-wrapper .field {
     position: relative; }
   .topsite-form .form-wrapper .url input:not(:placeholder-shown):dir(rtl) {
     direction: ltr;
@@ -512,69 +519,102 @@ main {
 @keyframes fade-up-tt {
   0% {
     opacity: 0;
     transform: translateY(15px); }
   100% {
     opacity: 1;
     transform: translateY(0); } }
 
-.sections-list .section-top-bar {
+.section-top-bar {
   position: relative; }
-  .sections-list .section-top-bar .section-info-option {
+  .section-top-bar .section-info-option {
     offset-inline-end: 0;
     position: absolute;
     top: 0; }
-  .sections-list .section-top-bar .info-option-icon {
+  .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;
-    margin-bottom: -2px; }
-  .sections-list .section-top-bar .info-option-icon[aria-expanded="true"] {
+    margin-bottom: -2px;
+    opacity: 0;
+    transition: opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); }
+    .section-top-bar .info-option-icon:focus, .section-top-bar .info-option-icon:active {
+      opacity: 1; }
+  .section-top-bar .info-option-icon[aria-expanded="true"] {
     fill: rgba(12, 12, 13, 0.8); }
-  .sections-list .section-top-bar .section-info-option .info-option {
+  .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 {
+    transition: visibility 0.2s, opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); }
+    .section-top-bar .section-info-option .info-option::before {
+      content: "";
+      display: block;
+      height: 32px;
+      left: 50%;
+      position: absolute;
+      right: 0;
+      top: -32px; }
+  .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 {
+    transition: visibility 0.2s, opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); }
+  .section-top-bar .info-option {
     z-index: 9999;
     position: absolute;
     background: #FFF;
     border: 1px solid #D7D7DB;
     border-radius: 3px;
     font-size: 13px;
     line-height: 120%;
-    margin-inline-end: -13px;
+    margin-inline-end: -9px;
     offset-inline-end: 0;
     top: 20px;
     width: 320px;
     padding: 24px;
     box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
     -moz-user-select: none; }
-  .sections-list .section-top-bar .info-option-header {
+  .section-top-bar .info-option-header {
     font-size: 15px;
     font-weight: 600; }
-  .sections-list .section-top-bar .info-option-body {
+  .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: #0060DF; }
+  .section-top-bar .info-option-link {
+    color: #0060DF;
+    margin-left: 7px; }
+  .section-top-bar .info-option-manage {
+    margin-top: 24px; }
+    .section-top-bar .info-option-manage button {
+      background: none;
+      border: none;
+      color: #0060DF;
+      cursor: pointer;
+      margin: 0;
+      padding: 0; }
+      .section-top-bar .info-option-manage button::after {
+        background-image: url("assets/topic-show-more-12.svg");
+        background-repeat: no-repeat;
+        content: '';
+        -moz-context-properties: fill;
+        display: inline-block;
+        fill: #0060DF;
+        height: 16px;
+        margin-inline-start: 5px;
+        margin-top: 1px;
+        vertical-align: middle;
+        width: 12px; }
+      .section-top-bar .info-option-manage button:dir(rtl)::after {
+        transform: scaleX(-1); }
 
 .sections-list .section-list {
   margin: 0;
   display: grid;
   grid-template-columns: repeat(auto-fit, 224px);
   grid-gap: 32px; }
   @media (max-width: 544px) {
     .sections-list .section-list .context-menu {
@@ -617,16 +657,19 @@ main {
       display: block; }
     .sections-list .section-empty-state .empty-state .empty-state-message {
       margin-bottom: 0;
       font-size: 13px;
       font-weight: 300;
       color: #737373;
       text-align: center; }
 
+section.section:hover .info-option-icon {
+  opacity: 1; }
+
 .topic {
   font-size: 12px;
   color: #737373;
   margin-top: 12px;
   line-height: 1.6; }
   @media (min-width: 800px) {
     .topic {
       line-height: 16px; } }
--- a/browser/extensions/activity-stream/install.rdf.in
+++ b/browser/extensions/activity-stream/install.rdf.in
@@ -3,17 +3,17 @@
 #filter substitution
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
   <Description about="urn:mozilla:install-manifest">
     <em:id>activity-stream@mozilla.org</em:id>
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
     <em:unpack>false</em:unpack>
-    <em:version>2017.10.02.1067-66ebf91c</em:version>
+    <em:version>2017.10.02.1069-7eee3552</em:version>
     <em:name>Activity Stream</em:name>
     <em:description>A rich visual history feed and a reimagined home page make it easier than ever to find exactly what you're looking for in Firefox.</em:description>
     <em:multiprocessCompatible>true</em:multiprocessCompatible>
 
     <em:targetApplication>
       <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
         <em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
--- a/browser/extensions/activity-stream/lib/SectionsManager.jsm
+++ b/browser/extensions/activity-stream/lib/SectionsManager.jsm
@@ -47,16 +47,20 @@ const BUILT_IN_SECTIONS = {
       descString: {id: "settings_pane_highlights_body2"}
     },
     shouldHidePref:  false,
     eventSource: "HIGHLIGHTS",
     icon: "highlights",
     title: {id: "header_highlights"},
     maxRows: 3,
     availableContextMenuOptions: ["CheckBookmark", "SaveToPocket", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl", "DeleteUrl"],
+    infoOption: {
+      header: {id: "settings_pane_highlights_header"},
+      body: {id: "settings_pane_highlights_body2"}
+    },
     emptyState: {
       message: {id: "highlights_empty_state"},
       icon: "highlights"
     },
     shouldSendImpressionStats: false,
     order: 1
   })
 };
--- a/browser/extensions/activity-stream/test/unit/common/Reducers.test.js
+++ b/browser/extensions/activity-stream/test/unit/common/Reducers.test.js
@@ -1,10 +1,10 @@
 const {reducers, INITIAL_STATE, insertPinned} = require("common/Reducers.jsm");
-const {TopSites, App, Snippets, Prefs, Dialog, Sections} = reducers;
+const {TopSites, App, Snippets, Prefs, Dialog, Sections, PreferencesPane} = reducers;
 
 const {actionTypes: at} = require("common/Actions.jsm");
 
 describe("Reducers", () => {
   describe("App", () => {
     it("should return the initial state", () => {
       const nextState = App(undefined, {type: "FOO"});
       assert.equal(nextState, INITIAL_STATE.App);
@@ -519,9 +519,24 @@ describe("Reducers", () => {
       assert.propertyVal(state, "snippetsURL", data.snippetsURL);
       assert.propertyVal(state, "version", data.version);
     });
     it("should reset to the initial state on a SNIPPETS_RESET action", () => {
       const state = Snippets({initalized: true, foo: "bar"}, {type: at.SNIPPETS_RESET});
       assert.equal(state, INITIAL_STATE.Snippets);
     });
   });
+  describe("PreferencesPane", () => {
+    it("should return INITIAL_STATE by default", () => {
+      assert.equal(INITIAL_STATE.PreferencesPane, PreferencesPane(undefined, {type: "non_existent"}));
+    });
+    it("should toggle visible to true on SETTINGS_OPEN", () => {
+      const action = {type: at.SETTINGS_OPEN};
+      const nextState = PreferencesPane(INITIAL_STATE.PreferencesPane, action);
+      assert.isTrue(nextState.visible);
+    });
+    it("should toggle visible to false on SETTINGS_CLOSE", () => {
+      const action = {type: at.SETTINGS_CLOSE};
+      const nextState = PreferencesPane(INITIAL_STATE.PreferencesPane, action);
+      assert.isFalse(nextState.visible);
+    });
+  });
 });