Bug 1403215 - Fix broken Snippets pref, perceived performance and bug fixes to Activity Stream. r=k88hudson,ursula draft
authorEd Lee <edilee@mozilla.com>
Wed, 27 Sep 2017 08:28:06 -0400
changeset 671739 30553afcaf82a88daaea2ce6a4974036615748de
parent 671268 756e10aa8bbd416cbc49b7739f78fb81d5525477
child 733589 a1bc8c22b607fc040328fd79d2a72cda9af6f975
push id82013
push userbmo:edilee@mozilla.com
push dateThu, 28 Sep 2017 06:31:12 +0000
reviewersk88hudson, ursula
bugs1403215
milestone58.0a1
Bug 1403215 - Fix broken Snippets pref, perceived performance and bug fixes to Activity Stream. r=k88hudson,ursula MozReview-Commit-ID: JSvQB4nxduM
browser/extensions/activity-stream/common/Actions.jsm
browser/extensions/activity-stream/common/PrerenderData.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/data/locales.json
browser/extensions/activity-stream/install.rdf.in
browser/extensions/activity-stream/lib/HighlightsFeed.jsm
browser/extensions/activity-stream/lib/PlacesFeed.jsm
browser/extensions/activity-stream/lib/PrefsFeed.jsm
browser/extensions/activity-stream/lib/SnippetsFeed.jsm
browser/extensions/activity-stream/lib/TelemetryFeed.jsm
browser/extensions/activity-stream/lib/TopSitesFeed.jsm
browser/extensions/activity-stream/test/unit/common/Reducers.test.js
browser/extensions/activity-stream/test/unit/lib/HighlightsFeed.test.js
browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js
browser/extensions/activity-stream/test/unit/lib/PrefsFeed.test.js
browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
browser/extensions/activity-stream/test/unit/unit-entry.js
browser/modules/PingCentre.jsm
--- a/browser/extensions/activity-stream/common/Actions.jsm
+++ b/browser/extensions/activity-stream/common/Actions.jsm
@@ -26,16 +26,17 @@ 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",
@@ -44,18 +45,18 @@ for (const type of [
   "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",
-  "PLACES_LINK_DELETED",
   "PREFS_INITIAL_VALUES",
   "PREF_CHANGED",
   "SAVE_SESSION_PERF_DATA",
   "SAVE_TO_POCKET",
   "SCREENSHOT_UPDATED",
   "SECTION_DEREGISTER",
   "SECTION_DISABLE",
   "SECTION_ENABLE",
--- a/browser/extensions/activity-stream/common/PrerenderData.jsm
+++ b/browser/extensions/activity-stream/common/PrerenderData.jsm
@@ -69,18 +69,17 @@ this.PrerenderData = new _PrerenderData(
     {oneOf: ["feeds.section.topstories", "feeds.section.highlights"]}
   ],
   initialSections: [
     {
       enabled: true,
       icon: "pocket",
       id: "topstories",
       order: 1,
-      title: {id: "header_recommended_by", values: {provider: "Pocket"}},
-      topics: [{}]
+      title: {id: "header_recommended_by", values: {provider: "Pocket"}}
     },
     {
       enabled: true,
       id: "highlights",
       icon: "highlights",
       order: 2,
       title: {id: "header_highlights"}
     }
--- a/browser/extensions/activity-stream/common/Reducers.jsm
+++ b/browser/extensions/activity-stream/common/Reducers.jsm
@@ -283,17 +283,19 @@ function Sections(prevState = INITIAL_ST
             if (!newSite.type || newSite.type === "bookmark") {
               newSite.type = "history";
             }
             return newSite;
           }
           return item;
         })
       }));
-    case at.PLACES_LINK_DELETED:
+    case at.PLACES_LINKS_DELETED:
+      return prevState.map(section => Object.assign({}, section,
+        {rows: section.rows.filter(site => !action.data.includes(site.url))}));
     case at.PLACES_LINK_BLOCKED:
       return prevState.map(section =>
         Object.assign({}, section, {rows: section.rows.filter(site => site.url !== action.data.url)}));
     default:
       return prevState;
   }
 }
 
--- a/browser/extensions/activity-stream/data/content/activity-stream-initial-state.js
+++ b/browser/extensions/activity-stream/data/content/activity-stream-initial-state.js
@@ -133,19 +133,16 @@
           "provider": "Pocket"
         }
       },
       "rows": [],
       "order": 1,
       "enabled": true,
       "icon": "pocket",
       "id": "topstories",
-      "topics": [
-        {}
-      ],
       "initialized": false
     },
     {
       "title": {
         "id": "header_highlights"
       },
       "rows": [],
       "order": 2,
--- 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="-1229441695"><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"><li data-reactid="99"><a class="topic-link" data-reactid="100"></a></li></ul></div></section><section data-reactid="101"><div class="section-top-bar" data-reactid="102"><h3 class="section-title" data-reactid="103"><span class="icon icon-small-spacer icon-highlights" data-reactid="104"></span><span data-reactid="105"> </span></h3></div><ul class="section-list" style="padding:0;" data-reactid="106"><li class="card-outer placeholder" data-reactid="107"><a data-reactid="108"><div class="card" data-reactid="109"><div class="card-details no-image" data-reactid="110"><div class="card-text no-context no-description no-host-name no-image" data-reactid="111"><h4 class="card-title" dir="auto" data-reactid="112"></h4><p class="card-description" dir="auto" data-reactid="113"></p></div><div class="card-context" data-reactid="114"></div></div></div></a></li><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></ul></section></div></div><!-- react-empty: 131 --></main></div></div>
+    <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="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
@@ -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", "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_LINK_BLOCKED", "PLACES_LINK_DELETED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_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", "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) {
@@ -633,17 +633,18 @@ function Sections(prevState = INITIAL_ST
             if (!newSite.type || newSite.type === "bookmark") {
               newSite.type = "history";
             }
             return newSite;
           }
           return item;
         })
       }));
-    case at.PLACES_LINK_DELETED:
+    case at.PLACES_LINKS_DELETED:
+      return prevState.map(section => Object.assign({}, section, { rows: section.rows.filter(site => !action.data.includes(site.url)) }));
     case at.PLACES_LINK_BLOCKED:
       return prevState.map(section => Object.assign({}, section, { rows: section.rows.filter(site => site.url !== action.data.url) }));
     default:
       return prevState;
   }
 }
 
 function Snippets(prevState = INITIAL_STATE.Snippets, action) {
@@ -1506,20 +1507,24 @@ class TopSitesEdit extends React.PureCom
         React.createElement("div", { className: "modal-overlay", onClick: this.onModalOverlayClick }),
         React.createElement(
           "div",
           { className: "modal" },
           React.createElement(
             "section",
             { className: "edit-topsites-inner-wrapper" },
             React.createElement(
-              "h3",
-              { className: "section-title" },
-              React.createElement("span", { className: `icon icon-small-spacer icon-topsites` }),
-              React.createElement(FormattedMessage, { id: "header_top_sites" })
+              "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(
               "ul",
               { className: "top-sites-list" },
               realTopSites.map((link, index) => link && React.createElement(TopSite, {
                 key: link.guid || link.url,
                 dispatch: this.props.dispatch,
                 link: link,
@@ -2564,17 +2569,20 @@ class Section extends React.PureComponen
 
   render() {
     const {
       id, eventSource, title, icon, rows,
       infoOption, emptyState, dispatch, maxRows,
       contextMenuOptions, intl, initialized
     } = this.props;
     const maxCards = CARDS_PER_ROW * maxRows;
-    const shouldShowTopics = id === "topstories" && this.props.topics && this.props.topics.length > 0;
+
+    // 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
     };
@@ -2689,33 +2697,72 @@ module.exports._unconnectedSection = Sec
 /***/ (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 { 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
  * as preview image, page title, page description, and some context about if the page
  * was visited, bookmarked, trending etc...
  * Each Section can make an unordered list of Cards which will create one instane of
  * this class. Each card will then get a context menu which reflects the actions that
  * can be done on this Card.
  */
 class Card extends React.PureComponent {
   constructor(props) {
     super(props);
-    this.state = { showContextMenu: false, activeCard: null };
+    this.state = {
+      activeCard: null,
+      imageLoaded: false,
+      showContextMenu: false
+    };
     this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
     this.onMenuUpdate = this.onMenuUpdate.bind(this);
     this.onLinkClick = this.onLinkClick.bind(this);
   }
+
+  /**
+   * Helper to conditionally load an image and update state when it loads.
+   */
+  async maybeLoadImage() {
+    // No need to load if it's already loaded or no image
+    const { image } = this.props.link;
+    if (!this.state.imageLoaded && image) {
+      // Initialize a promise to share a load across multiple card updates
+      if (!gImageLoading.has(image)) {
+        const loaderPromise = new Promise((resolve, reject) => {
+          const loader = new Image();
+          loader.addEventListener("load", resolve);
+          loader.addEventListener("error", reject);
+          loader.src = image;
+        });
+
+        // Save and remove the promise only while it's pending
+        gImageLoading.set(image, loaderPromise);
+        loaderPromise.catch(ex => ex).then(() => gImageLoading.delete(image)).catch();
+      }
+
+      // Wait for the image whether just started loading or reused promise
+      await gImageLoading.get(image);
+
+      // Only update state if we're still waiting to load the original image
+      if (this.props.link.image === image && !this.state.imageLoaded) {
+        this.setState({ imageLoaded: true });
+      }
+    }
+  }
+
   onMenuButtonClick(event) {
     event.preventDefault();
     this.setState({
       activeCard: this.props.index,
       showContextMenu: true
     });
   }
   onLinkClick(event) {
@@ -2737,16 +2784,28 @@ class Card extends React.PureComponent {
         incognito: true,
         tiles: [{ id: this.props.link.guid, pos: this.props.index }]
       }));
     }
   }
   onMenuUpdate(showContextMenu) {
     this.setState({ showContextMenu });
   }
+  componentDidMount() {
+    this.maybeLoadImage();
+  }
+  componentDidUpdate() {
+    this.maybeLoadImage();
+  }
+  componentWillReceiveProps(nextProps) {
+    // Clear the image state if changing images
+    if (nextProps.link.image !== this.props.link.image) {
+      this.setState({ imageLoaded: false });
+    }
+  }
   render() {
     const { index, link, dispatch, contextMenuOptions, eventSource, shouldSendImpressionStats } = this.props;
     const { props } = this;
     const isContextMenuOpen = this.state.showContextMenu && this.state.activeCard === index;
     // Display "now" as "trending" until we have new strings #3402
     const { icon, intlID } = cardContextTypes[link.type === "now" ? "trending" : link.type] || {};
     const hasImage = link.image || link.hasImage;
     const imageStyle = { backgroundImage: link.image ? `url(${link.image})` : "none" };
@@ -2758,17 +2817,17 @@ class Card extends React.PureComponent {
         "a",
         { href: link.url, onClick: !props.placeholder && this.onLinkClick },
         React.createElement(
           "div",
           { className: "card" },
           hasImage && React.createElement(
             "div",
             { className: "card-preview-image-outer" },
-            React.createElement("div", { className: `card-preview-image${link.image ? " loaded" : ""}`, style: imageStyle })
+            React.createElement("div", { className: `card-preview-image${this.state.imageLoaded ? " loaded" : ""}`, style: imageStyle })
           ),
           React.createElement(
             "div",
             { className: `card-details${hasImage ? "" : " no-image"}` },
             link.hostname && React.createElement(
               "div",
               { className: "card-host-name" },
               link.hostname
@@ -2889,17 +2948,17 @@ class Topics extends React.PureComponent
       React.createElement(
         "span",
         null,
         React.createElement(FormattedMessage, { id: "pocket_read_more" })
       ),
       React.createElement(
         "ul",
         null,
-        topics.map(t => React.createElement(Topic, { key: t.name, url: t.url, name: t.name }))
+        topics && topics.map(t => React.createElement(Topic, { key: t.name, url: t.url, name: t.name }))
       ),
       read_more_endpoint && React.createElement(
         "a",
         { className: "topic-read-more", href: read_more_endpoint },
         React.createElement(FormattedMessage, { id: "pocket_read_even_more" })
       )
     );
   }
@@ -2980,18 +3039,17 @@ var PrerenderData = new _PrerenderData({
   // This means if either of these are set to their default values,
   // prerendering can be used.
   { oneOf: ["feeds.section.topstories", "feeds.section.highlights"] }],
   initialSections: [{
     enabled: true,
     icon: "pocket",
     id: "topstories",
     order: 1,
-    title: { id: "header_recommended_by", values: { provider: "Pocket" } },
-    topics: [{}]
+    title: { id: "header_recommended_by", values: { provider: "Pocket" } }
   }, {
     enabled: true,
     id: "highlights",
     icon: "highlights",
     order: 2,
     title: { id: "header_highlights" }
   }]
 });
@@ -3252,16 +3310,20 @@ class SnippetsMap extends Map {
     }
     let blockList = this.blockList;
     if (!blockList.includes(id)) {
       blockList.push(id);
     }
     await this.set("blockList", blockList);
   }
 
+  disableOnboarding() {
+    this._dispatch(ac.SendToMain({ type: at.DISABLE_ONBOARDING }));
+  }
+
   showFirefoxAccounts() {
     this._dispatch(ac.SendToMain({ type: at.SHOW_FIREFOX_ACCOUNTS }));
   }
 
   /**
    * connect - Attaches an indexedDB back-end to the Map so that any set values
    *           are also cached in a store. It also restores any existing values
    *           that are already stored in the indexedDB store.
@@ -3390,34 +3452,41 @@ class SnippetsProvider {
 
     // Has enough time passed for us to require an update?
     const lastUpdate = this.snippetsMap.get("snippets-last-update");
     const needsUpdate = !(lastUpdate >= 0) || Date.now() - lastUpdate > SNIPPETS_UPDATE_INTERVAL_MS;
 
     if (needsUpdate && this.appData.snippetsURL) {
       this.snippetsMap.set("snippets-last-update", Date.now());
       try {
-        // TODO: timeout?
         const response = await fetch(this.appData.snippetsURL);
         if (response.status === 200) {
           const payload = await response.text();
 
           this.snippetsMap.set("snippets", payload);
           this.snippetsMap.set("snippets-cached-version", this.appData.version);
         }
       } catch (e) {
         console.error(e); // eslint-disable-line no-console
       }
     }
   }
 
-  _showDefaultSnippets() {
+  _noSnippetFallback() {
     // TODO
   }
 
+  _forceOnboardingVisibility(shouldBeVisible) {
+    const onboardingEl = document.getElementById("onboarding-notification-bar");
+
+    if (onboardingEl) {
+      onboardingEl.style.display = shouldBeVisible ? "" : "none";
+    }
+  }
+
   _showRemoteSnippets() {
     const snippetsEl = document.getElementById(this.elementId);
     const payload = this.snippetsMap.get("snippets");
 
     if (!snippetsEl) {
       throw new Error(`No element was found with id '${this.elementId}'.`);
     }
 
@@ -3475,25 +3544,28 @@ class SnippetsProvider {
 
     // Refresh snippets, if enough time has passed.
     await this._refreshSnippets();
 
     // Try showing remote snippets, falling back to defaults if necessary.
     try {
       this._showRemoteSnippets();
     } catch (e) {
-      this._showDefaultSnippets(e);
+      this._noSnippetFallback(e);
     }
 
     window.dispatchEvent(new Event(SNIPPETS_ENABLED_EVENT));
+
+    this._forceOnboardingVisibility(true);
     this.initialized = true;
   }
 
   uninit() {
     window.dispatchEvent(new Event(SNIPPETS_DISABLED_EVENT));
+    this._forceOnboardingVisibility(false);
     this.initialized = false;
   }
 }
 
 /**
  * addSnippetsSubscriber - Creates a SnippetsProvider that Initializes
  *                         when the store has received the appropriate
  *                         Snippet data.
@@ -3503,26 +3575,26 @@ class SnippetsProvider {
  */
 function addSnippetsSubscriber(store) {
   const snippets = new SnippetsProvider(store.dispatch);
 
   let initializing = false;
 
   store.subscribe(async () => {
     const state = store.getState();
-    // state.Snippets.initialized:  Should snippets be initialised?
-    // snippets.initialized:        Is SnippetsProvider currently initialised?
-    if (state.Snippets.initialized && !snippets.initialized && state.Snippets.onboardingFinished) {
-      // Don't call init multiple times
-      if (!initializing) {
-        initializing = true;
-        await snippets.init({ appData: state.Snippets });
-        initializing = false;
-      }
-    } else if (state.Snippets.initialized === false && snippets.initialized) {
+    // state.Prefs.values["feeds.snippets"]:  Should snippets be shown?
+    // state.Snippets.initialized             Is the snippets data initialized?
+    // snippets.initialized:                  Is SnippetsProvider currently initialised?
+    if (state.Prefs.values["feeds.snippets"] && state.Snippets.initialized && !snippets.initialized &&
+    // Don't call init multiple times
+    !initializing) {
+      initializing = true;
+      await snippets.init({ appData: state.Snippets });
+      initializing = false;
+    } else if (state.Prefs.values["feeds.snippets"] === false && snippets.initialized) {
       snippets.uninit();
     }
   });
 
   // These values are returned for testing purposes
   return snippets;
 }
 
--- a/browser/extensions/activity-stream/data/content/activity-stream.css
+++ b/browser/extensions/activity-stream/data/content/activity-stream.css
@@ -452,36 +452,34 @@ main {
   .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; }
 
 .topsite-form .form-wrapper {
+  margin: auto;
+  max-width: 350px;
   padding: 15px 0; }
   .topsite-form .form-wrapper .field {
-    margin-inline-start: 205px;
     position: relative; }
   .topsite-form .form-wrapper .url input:not(:placeholder-shown):dir(rtl) {
     direction: ltr;
     text-align: right; }
   .topsite-form .form-wrapper .section-title {
-    margin-bottom: 5px;
-    margin-inline-start: 205px; }
+    margin-bottom: 5px; }
   .topsite-form .form-wrapper input[type='text'] {
     border: solid 1px rgba(12, 12, 13, 0.2);
     border-radius: 2px;
     margin: 5px 0;
     padding: 7px;
-    width: 350px; }
+    width: 100%; }
     .topsite-form .form-wrapper input[type='text']:focus {
       border: solid 1px rgba(12, 12, 13, 0.4); }
-    .topsite-form .form-wrapper input[type='text']::placeholder {
-      font-style: italic; }
   .topsite-form .form-wrapper .invalid input[type='text'] {
     border: solid 1px #D70022;
     box-shadow: 0 0 0 2px rgba(215, 0, 34, 0.35); }
   .topsite-form .form-wrapper .error-tooltip {
     animation: fade-up-tt 450ms;
     background: #D70022;
     border-radius: 2px;
     color: #FFF;
@@ -671,63 +669,53 @@ main {
 .search-wrapper {
   cursor: default;
   display: flex;
   position: relative;
   margin: 0 0 40px;
   width: 100%;
   height: 36px; }
   .search-wrapper input {
-    border: 0;
+    border: 1px solid rgba(0, 0, 0, 0.15);
+    border-radius: 3px;
     box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
-    border: 1px solid rgba(0, 0, 0, 0.15);
-    box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
-    border-radius: 3px;
-    border-radius: 4px;
     color: inherit;
     padding: 0;
     padding-inline-end: 36px;
     padding-inline-start: 35px;
     width: 100%;
     font-size: 15px; }
-    .search-wrapper input:focus {
-      border-color: #0060DF;
-      box-shadow: 0 0 0 2px #0060DF;
-      z-index: 1; }
-    .search-wrapper input:focus + .search-button {
-      z-index: 1;
-      background-color: #0060DF;
-      background-image: url("chrome://browser/skin/forward.svg");
-      fill: #FFF;
-      -moz-context-properties: fill; }
+  .search-wrapper:active input,
+  .search-wrapper input:focus {
+    border-color: #0A84FF;
+    box-shadow: 0 0 0 2px #0A84FF; }
   .search-wrapper .search-label {
     background: url("chrome://browser/skin/search-glass.svg") no-repeat 12px center/16px;
     fill: rgba(12, 12, 13, 0.4);
     -moz-context-properties: fill;
     position: absolute;
     offset-inline-start: 0;
     height: 100%;
-    width: 35px;
-    z-index: 2; }
+    width: 35px; }
   .search-wrapper .search-button {
     background: url("chrome://browser/skin/forward.svg") no-repeat center center;
     border-radius: 0 3px 3px 0;
     border: 0;
     width: 36px;
     fill: rgba(12, 12, 13, 0.4);
     -moz-context-properties: fill;
     background-size: 16px 16px;
     height: 100%;
     offset-inline-end: 0;
     position: absolute; }
-    .search-wrapper .search-button:hover {
-      z-index: 1;
-      background-color: #0060DF;
-      fill: #FFF;
+    .search-wrapper .search-button:focus, .search-wrapper .search-button:hover {
+      background-color: rgba(12, 12, 13, 0.1);
       cursor: pointer; }
+    .search-wrapper .search-button:active {
+      background-color: rgba(12, 12, 13, 0.15); }
     .search-wrapper .search-button:dir(rtl) {
       transform: scaleX(-1); }
   .search-wrapper .contentSearchSuggestionTable {
     border: 0;
     transform: translateY(2px); }
 
 .context-menu {
   display: block;
@@ -1004,37 +992,35 @@ main {
     box-shadow: 0 0 0 5px #D7D7DB;
     transition: box-shadow 150ms; }
     .card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) .context-menu-button {
       transform: scale(1);
       opacity: 1; }
     .card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) .card-title {
       color: #0060DF; }
   .card-outer .card-preview-image-outer {
+    background-color: #F9F9FA;
     position: relative;
-    background: linear-gradient(135deg, #B1B1B3, #D7D7DB);
     height: 122px;
     border-radius: 3px 3px 0 0;
     overflow: hidden; }
-    .card-outer .card-preview-image-outer:dir(rtl) {
-      background: linear-gradient(225deg, #B1B1B3, #D7D7DB); }
     .card-outer .card-preview-image-outer::after {
       border-bottom: 1px solid rgba(0, 0, 0, 0.05);
       bottom: 0;
       content: " ";
       position: absolute;
       width: 100%; }
     .card-outer .card-preview-image-outer .card-preview-image {
       width: 100%;
       height: 100%;
       background-size: cover;
       background-position: center;
       background-repeat: no-repeat;
-      transition: opacity 1s;
-      opacity: 0; }
+      opacity: 0;
+      transition: opacity 1s cubic-bezier(0.07, 0.95, 0, 1); }
       .card-outer .card-preview-image-outer .card-preview-image.loaded {
         opacity: 1; }
   .card-outer .card-details {
     padding: 15px 16px 12px; }
     .card-outer .card-details.no-image {
       padding-top: 16px; }
   .card-outer .card-text {
     overflow: hidden;
--- a/browser/extensions/activity-stream/data/locales.json
+++ b/browser/extensions/activity-stream/data/locales.json
@@ -575,17 +575,61 @@
     "welcome_title": "Donemat war un ivinell nevez",
     "welcome_body": "Firefox a implijo al lec'h-mañ evit diskouez deoc'h sinedoù, pennadoù, videoioù ha pajennoù bet gweladennet ganeoc'h, evit adkavout anezho en un doare aes.",
     "welcome_label": "Naoudiañ ho mareoù pouezus",
     "time_label_less_than_minute": "< 1 m",
     "time_label_minute": "{number}m",
     "time_label_hour": "{number}e",
     "time_label_day": "{number}d",
     "settings_pane_button_label": "Personelait ho pajenn Ivinell Nevez",
-    "settings_pane_header": "Gwellvezioù an ivinell nevez"
+    "settings_pane_header": "Gwellvezioù an ivinell nevez",
+    "settings_pane_body2": "Dibabit petra a welit war ar bajenn-mañ.",
+    "settings_pane_search_header": "Klask",
+    "settings_pane_search_body": "Klask er web adalek an ivinell nevez.",
+    "settings_pane_topsites_header": "Lec'hiennoù gwellañ",
+    "settings_pane_topsites_body": "Kit war al lec'hiennoù gweladennet ar muiañ ganeoc'h.",
+    "settings_pane_topsites_options_showmore": "Diskouez daou vann",
+    "settings_pane_bookmarks_header": "Sinedoù nevez",
+    "settings_pane_bookmarks_body": "Ho sinedoù nevez strollet en ul lec'h aes da dizhout.",
+    "settings_pane_visit_again_header": "Gweladenniñ en-dro",
+    "settings_pane_visit_again_body": "Firefox a ziskouezo deoc'h ul lodenn eus ho roll istor a c'hallfec'h kaout c'hoant da zerc'hel soñj pe da zistreiñ eno.",
+    "settings_pane_highlights_header": "Mareoù pouezus",
+    "settings_pane_highlights_body2": "Adkavit an traoù dedennus gweladennet pe lakaet er sinedoù nevez ’zo.",
+    "settings_pane_highlights_options_bookmarks": "Sinedoù",
+    "settings_pane_highlights_options_visited": "Lec'hiennoù gweladennet",
+    "settings_pane_snippets_header": "Notennigoù",
+    "settings_pane_snippets_body": "Lennit an hizivadurioù berr ha dous graet gant Mozilla evit Firefox, sevenadur ar genrouedad, hag ur mem dre-zegouezh ur wech an amzer.",
+    "settings_pane_done_button": "Graet",
+    "edit_topsites_button_text": "Embann",
+    "edit_topsites_button_label": "Personelaat ar gevrenn “lec'hiennoù gweladennet ar muiañ”",
+    "edit_topsites_showmore_button": "Diskouez muioc'h",
+    "edit_topsites_showless_button": "Diskouez nebeutoc'h",
+    "edit_topsites_done_button": "Graet",
+    "edit_topsites_pin_button": "Spilhennañ al lec'hienn-mañ",
+    "edit_topsites_unpin_button": "Dispilhennañ al lec'hienn-mañ",
+    "edit_topsites_edit_button": "Embann al lec'hienn-mañ",
+    "edit_topsites_dismiss_button": "Dilemel al lec'hienn-mañ",
+    "edit_topsites_add_button": "Ouzhpennañ",
+    "topsites_form_add_header": "Lec'hiennoù gwellañ nevez",
+    "topsites_form_edit_header": "Embann al Lec'hiennoù Gwellañ",
+    "topsites_form_title_placeholder": "Enankañ un titl",
+    "topsites_form_url_placeholder": "Skrivit pe pegit un URL",
+    "topsites_form_add_button": "Ouzhpennañ",
+    "topsites_form_save_button": "Enrollañ",
+    "topsites_form_cancel_button": "Nullañ",
+    "topsites_form_url_validation": "URL talvoudek azgoulennet",
+    "pocket_read_more": "Danvezioù brudet:",
+    "pocket_read_even_more": "Gwelet muioc'h a istorioù",
+    "pocket_feedback_header": "Ar gwellañ eus ar web, dibabet gant ouzhpenn 25 milion a dud.",
+    "pocket_description": "Dizoloit pennadoù eus an dibab ho pije gellout c'hwitout a-hent all warno, a-drugarez da bPocket, hag a zo bremañ ul lodenn deus Mozilla.",
+    "highlights_empty_state": "Krogit da verdeiñ hag e tiskouezimp deoc’h pennadoù, videoioù ha pajennoù all gweladennet pe lakaet er sinedoù nevez ’zo.",
+    "topstories_empty_state": "Aet oc'h betek penn. Distroit diwezhatoc'h evit muioc’h a istorioù digant {provider}. N’oc'h ket evit gortoz? Dibabit un danvez brudet evit klask muioc’h a bennadoù dedennus eus pep lec’h er web.",
+    "manual_migration_explanation2": "Amprouit Firefox gant sinedoù, roll istor ha gerioù-tremen ur merdeer all.",
+    "manual_migration_cancel_button": "N'am bo ket",
+    "manual_migration_import_button": "Emporzhiañ bremañ"
   },
   "ca": {
     "newtab_page_title": "Pestanya nova",
     "default_label_loading": "S'està carregant…",
     "header_top_sites": "Llocs principals",
     "header_stories": "Articles populars",
     "header_highlights": "Destacats",
     "header_visit_again": "Torneu a visitar",
@@ -950,16 +994,17 @@
     "manual_migration_cancel_button": "Dim Diolch",
     "manual_migration_import_button": "Mewnforio Nawr"
   },
   "da": {
     "newtab_page_title": "Nyt faneblad",
     "default_label_loading": "Indlæser…",
     "header_top_sites": "Mest besøgte websider",
     "header_stories": "Tophistorier",
+    "header_highlights": "Højdepunkter",
     "header_visit_again": "Besøg igen",
     "header_bookmarks": "Seneste bogmærker",
     "header_recommended_by": "Anbefalet af {provider}",
     "header_bookmarks_placeholder": "Du har ingen bogmærker endnu.",
     "header_stories_from": "fra",
     "type_label_visited": "Besøgt",
     "type_label_bookmarked": "Bogmærket",
     "type_label_synced": "Synkroniseret fra en anden enhed",
@@ -981,37 +1026,35 @@
     "confirm_history_delete_notice_p2": "Denne handling kan ikke fortrydes.",
     "menu_action_save_to_pocket": "Gem til Pocket",
     "search_for_something_with": "Søg efter {search_term} med:",
     "search_button": "Søg",
     "search_header": "{search_engine_name}-søgning",
     "search_web_placeholder": "Søg på internettet",
     "search_settings": "Skift søgeindstillinger",
     "section_info_option": "Info",
+    "section_info_send_feedback": "Send feedback",
     "welcome_title": "Velkommen til nyt faneblad",
     "welcome_body": "Firefox vil bruge denne plads til at vise dine mest relevante bogmærker, artikler, videoer og sider, du har besøgt for nylig - så kan du nemmere finde dem.",
     "welcome_label": "Finder dine højdepunkter",
     "time_label_less_than_minute": "<1 m.",
     "time_label_minute": "{number} m.",
     "time_label_hour": "{number} t.",
     "time_label_day": "{number} d.",
     "settings_pane_button_label": "Tilpas siden Nyt faneblad",
     "settings_pane_header": "Indstillinger for Nyt faneblad",
-    "settings_pane_body": "Vælg, hvad der vises, når du åbner et nyt faneblad.",
     "settings_pane_search_header": "Søgning",
     "settings_pane_search_body": "Søg på nettet fra Nyt faneblad.",
     "settings_pane_topsites_header": "Mest besøgte websider",
     "settings_pane_topsites_body": "Adgang til de websider, du besøger oftest.",
     "settings_pane_topsites_options_showmore": "Vis to rækker",
     "settings_pane_bookmarks_header": "Seneste bogmærker",
     "settings_pane_bookmarks_body": "Dine seneste bogmærker samlet ét sted.",
     "settings_pane_visit_again_header": "Besøg igen",
     "settings_pane_visit_again_body": "Firefox viser dig dele af din browserhistorik, som du måske vil huske på eller vende tilbage til.",
-    "settings_pane_pocketstories_header": "Tophistorier",
-    "settings_pane_pocketstories_body": "Pocket, en del af Mozilla-familien, hjælper dig med at opdage indhold af høj kvalitet, som du måske ellers ikke ville have fundet.",
     "settings_pane_done_button": "Færdig",
     "edit_topsites_button_text": "Rediger",
     "edit_topsites_button_label": "Tilpas afsnittet Mest besøgte websider",
     "edit_topsites_showmore_button": "Vis flere",
     "edit_topsites_showless_button": "Vis færre",
     "edit_topsites_done_button": "Færdig",
     "edit_topsites_pin_button": "Fastgør denne webside",
     "edit_topsites_unpin_button": "Frigør denne webside",
@@ -1024,20 +1067,17 @@
     "topsites_form_url_placeholder": "Indtast eller indsæt en URL",
     "topsites_form_add_button": "Tilføj",
     "topsites_form_save_button": "Gem",
     "topsites_form_cancel_button": "Annuller",
     "topsites_form_url_validation": "Gyldig URL påkrævet",
     "pocket_read_more": "Populære emner:",
     "pocket_read_even_more": "Se flere historier",
     "pocket_feedback_header": "Det bedste fra nettet, udvalgt af mere end 25 millioner mennesker.",
-    "pocket_feedback_body": "Pocket, en del af Mozilla-familien, hjælper dig med at opdage indhold af høj kvalitet, som du måske ellers ikke ville have fundet.",
-    "pocket_send_feedback": "Send feedback",
     "topstories_empty_state": "Der er ikke flere nye historier. Kom tilbage senere for at se flere tophistorier fra {provider}. Kan du ikke vente? Vælg et populært emne og find flere spændende historier fra hele verden.",
-    "manual_migration_explanation": "Prøv Firefox med dine favorit-websteder og bogmærker fra en anden browser.",
     "manual_migration_cancel_button": "Nej tak",
     "manual_migration_import_button": "Importer nu"
   },
   "de": {
     "newtab_page_title": "Neuer Tab",
     "default_label_loading": "Wird geladen…",
     "header_top_sites": "Meistbesuchte Seiten",
     "header_stories": "Meistgelesene Meldungen",
@@ -3783,18 +3823,18 @@
     "topstories_empty_state": "Ulac wiyaḍ. Uɣal-d ticki s wugar n imagraden seg {provider}. Ur tebɣiḍ ara ad terǧuḍ? Fren asentel seg wid yettwasnen akken ad twaliḍ imagraden yelhan di Web.",
     "manual_migration_explanation2": "Σreḍ Firefox s ticṛaḍ n isebtar, amazray akked awalen uffiren sγur ilinigen nniḍen.",
     "manual_migration_cancel_button": "Ala, tanemmirt",
     "manual_migration_import_button": "Kter tura"
   },
   "kk": {
     "newtab_page_title": "Жаңа бет",
     "default_label_loading": "Жүктелуде…",
-    "header_top_sites": "Топ сайттар",
-    "header_stories": "Топ хикаялар",
+    "header_top_sites": "Үздік сайттар",
+    "header_stories": "Үздік хикаялар",
     "header_highlights": "Ерекше жаңалықтар",
     "header_visit_again": "Қайтадан шолу",
     "header_bookmarks": "Соңғы бетбелгілер",
     "header_recommended_by": "Ұсынушы {provider}",
     "header_bookmarks_placeholder": "Сізде әлі бетбелгілер жоқ.",
     "header_stories_from": "ұсынған",
     "type_label_visited": "Қаралған",
     "type_label_bookmarked": "Бетбелгілерде",
@@ -3810,17 +3850,17 @@
     "menu_action_open_new_window": "Жаңа терезеде ашу",
     "menu_action_open_private_window": "Жаңа жекелік терезесінде ашу",
     "menu_action_dismiss": "Тайдыру",
     "menu_action_delete": "Тарихтан өшіру",
     "menu_action_pin": "Бекіту",
     "menu_action_unpin": "Бекітуді алып тастау",
     "confirm_history_delete_p1": "Бұл парақтың барлық кездесулерін шолу тарихыңыздан өшіруді қалайсыз ба?",
     "confirm_history_delete_notice_p2": "Бұл әрекетті болдырмау мүмкін болмайды.",
-    "menu_action_save_to_pocket": "Pocket-ке сақтау",
+    "menu_action_save_to_pocket": "Pocket ішіне сақтау",
     "search_for_something_with": "{search_term} ұғымын көмегімен іздеу:",
     "search_button": "Іздеу",
     "search_header": "{search_engine_name} іздеуі",
     "search_web_placeholder": "Интернетте іздеу",
     "search_settings": "Іздеу баптауларын өзгерту",
     "section_info_option": "Ақпарат",
     "section_info_send_feedback": "Кері байланыс хабарламасын жіберу",
     "section_info_privacy_notice": "Жекелік ескертуі",
@@ -3828,32 +3868,32 @@
     "welcome_body": "Firefox бұл орында ең маңызды бетбелгілер, мақалалар, видеолар және жуырда қаралған беттерді көрсетеді, оның көмегімен сіз оларға оңай түрде орала аласыз.",
     "welcome_label": "Ең басты нәрселерді анықтау",
     "time_label_less_than_minute": "<1 минут",
     "time_label_minute": "{number} минут",
     "time_label_hour": "{number} сағат",
     "time_label_day": "{number} күн",
     "settings_pane_button_label": "Жаңа бетті баптаңыз",
     "settings_pane_header": "Жаңа бет баптаулары",
-    "settings_pane_body2": "Бұл парақта не көргіңіз келетінді таңдаңыз.",
+    "settings_pane_body2": "Бұл бетте не көргіңіз келетінді таңдаңыз.",
     "settings_pane_search_header": "Іздеу",
     "settings_pane_search_body": "Жаңа беттен интернеттен іздеңіз.",
-    "settings_pane_topsites_header": "Топ сайттар",
+    "settings_pane_topsites_header": "Үздік сайттар",
     "settings_pane_topsites_body": "Көбірек қаралатын сайттарға қатынау.",
     "settings_pane_topsites_options_showmore": "Екі жолды көрсету",
     "settings_pane_bookmarks_header": "Соңғы бетбелгілер",
     "settings_pane_bookmarks_body": "Сіздің жаңадан жасалған бетбелгілер бір ыңғайлы жерде.",
     "settings_pane_visit_again_header": "Қайтадан шолу",
     "settings_pane_visit_again_body": "Firefox сізге есте сақтауды немесе қайта шолуды қалауыңыз мүмкін тарихыңыздың бөліктерін көрсетеді.",
     "settings_pane_highlights_header": "Ерекше жаңалықтар",
     "settings_pane_highlights_body2": "Сіз жақында қараған немесе бетбелгілерге қосқан қызықты нәрселерге қайтатын жолды табыңыз.",
     "settings_pane_highlights_options_bookmarks": "Бетбелгілер",
     "settings_pane_highlights_options_visited": "Ашылған сайттар",
     "settings_pane_snippets_header": "Үзінділер",
-    "settings_pane_snippets_body": "Mozilla-дан Firefox және интернет мәдениеті туралы қысқа жаңалықтарды, және кездейсоқ мемдерді оқыңыз.",
+    "settings_pane_snippets_body": "Mozilla ұсынған Firefox және интернет мәдениеті туралы қысқа жаңалықтарды, және кездейсоқ мемдерді оқыңыз.",
     "settings_pane_done_button": "Дайын",
     "edit_topsites_button_text": "Түзету",
     "edit_topsites_button_label": "Топ сайттар санатын баптау",
     "edit_topsites_showmore_button": "Көбірек көрсету",
     "edit_topsites_showless_button": "Азырақ көрсету",
     "edit_topsites_done_button": "Дайын",
     "edit_topsites_pin_button": "Бұл сайтты жапсыру",
     "edit_topsites_unpin_button": "Бұл сайтты бекітуден алып тастау",
@@ -3869,17 +3909,17 @@
     "topsites_form_cancel_button": "Бас тарту",
     "topsites_form_url_validation": "Жарамды сілтеме керек",
     "pocket_read_more": "Әйгілі тақырыптар:",
     "pocket_read_even_more": "Көбірек хикаяларды қарау",
     "pocket_feedback_header": "Интернеттің ең жақсысы, 25 миллион адаммен танылған.",
     "pocket_description": "Ол болмаса, сіз жіберіп алатын мүмкіндігі бар жоғары сапалы құраманы Pocket көмегімен табыңыз, ол енді Mozilla-ның бөлігі болып табылады.",
     "highlights_empty_state": "Шолуды бастаңыз, сіз жақында шолған немесе бетбелгілерге қосқан тамаша мақалалар, видеолар немесе басқа парақтардың кейбіреулері осында көрсетіледі.",
     "topstories_empty_state": "Дайын. {provider} ұсынған көбірек мақалаларды алу үшін кейінірек тексеріңіз. Күте алмайсыз ба? Интернеттен көбірек тамаша мақалаларды алу үшін әйгілі теманы таңдаңыз.",
-    "manual_migration_explanation2": "Firefox-ты басқа браузер бетбелгілері, тарихы және парольдерімен қолданып көріңіз.",
+    "manual_migration_explanation2": "Firefox қолданбасын басқа браузер бетбелгілері, тарихы және парольдерімен қолданып көріңіз.",
     "manual_migration_cancel_button": "Жоқ, рахмет",
     "manual_migration_import_button": "Қазір импорттау"
   },
   "km": {
     "newtab_page_title": "ផ្ទាំង​ថ្មី",
     "default_label_loading": "កំពុង​ផ្ទុក...",
     "header_top_sites": "វិបសាយ​លើ​គេ",
     "header_highlights": "ការ​រំលេច",
@@ -4198,23 +4238,25 @@
     "pocket_description": "Atraskite kokybišką turinį, kurio kitaip galbūt nerastumėte, su „Pocket“, kuri yra tapusi „Mozillos“ dalimi, pagalba.",
     "highlights_empty_state": "Pradėkite naršyti, o mes čia pateiksime puikių straipsnių, vaizdo įrašų bei kitų tinklalapių, kuriuose neseniai lankėtės ar įtraukėte į adresyną.",
     "topstories_empty_state": "Viską perskaitėte. Užsukite vėliau, norėdami rasti daugiau gerų straipsnių iš „{provider}“. Nekantraujate? Pasirinkite populiarią temą, norėdami rasti daugiau puikių straipsnių saityne.",
     "manual_migration_explanation2": "Išbandykite „Firefox“ su adresynu, žurnalu bei slaptažodžiais iš kitos naršyklės.",
     "manual_migration_cancel_button": "Ačiū, ne",
     "manual_migration_import_button": "Importuoti dabar"
   },
   "lv": {
-    "newtab_page_title": "Jauna cilne"
+    "newtab_page_title": "Jauna cilne",
+    "default_label_loading": "Notiek ielāde…"
   },
   "mk": {
     "newtab_page_title": "Ново јазиче",
     "default_label_loading": "Се вчитува…",
-    "header_top_sites": "Врвни мрежни места",
-    "header_stories": "Врвни написи",
+    "header_top_sites": "Популарни мрежни места",
+    "header_stories": "Популарни написи",
+    "header_highlights": "Интереси",
     "header_visit_again": "Посети повторно",
     "header_bookmarks": "Скорешни обележувачи",
     "header_recommended_by": "Препорачано од {provider}",
     "header_bookmarks_placeholder": "Сѐ уште немате обележувачи.",
     "header_stories_from": "од",
     "type_label_visited": "Посетени",
     "type_label_bookmarked": "Обележани",
     "type_label_synced": "Синхронизирани од други уреди",
@@ -4227,49 +4269,55 @@
     "menu_action_copy_address": "Копирај адреса",
     "menu_action_email_link": "Испрати врска…",
     "menu_action_open_new_window": "Отвори во нов прозорец",
     "menu_action_open_private_window": "Отвори во нов приватен прозорец",
     "menu_action_dismiss": "Откажи",
     "menu_action_delete": "Избриши од историја",
     "menu_action_pin": "Прикачи",
     "menu_action_unpin": "Откачи",
-    "confirm_history_delete_p1": "Дали сте сигурни дека сакате да ја избришете оваа страница отсекаде во Вашата историја на прелистување?",
+    "confirm_history_delete_p1": "Дали сте сигурни дека сакате да ја избришете оваа страница отсекаде во вашата историја на прелистување?",
     "confirm_history_delete_notice_p2": "Ова дејство не може да се одврати.",
     "menu_action_save_to_pocket": "Зачувај во Pocket",
     "search_for_something_with": "Пребарај за {search_term} со:",
     "search_button": "Барај",
     "search_header": "Пребарување со {search_engine_name}",
     "search_web_placeholder": "Пребарајте на Интернет",
     "search_settings": "Промени поставувања за пребарување",
     "section_info_option": "Инфо",
+    "section_info_send_feedback": "Испрати мислење",
+    "section_info_privacy_notice": "Белешка за приватност",
     "welcome_title": "Добредојдовте во новото јазиче",
     "welcome_body": "Firefox ќе го искористи овој простор за да Ви ги прикаже најрелевантните обележувачи, написи, видеа и страници што сте ги посетиле, за да можете лесно да им се навратите.",
-    "welcome_label": "Ги откривам Вашите интереси",
+    "welcome_label": "Ги откривам вашите Интереси",
     "time_label_less_than_minute": "< 1 м",
     "time_label_minute": "{number} м",
     "time_label_hour": "{number} ч",
     "time_label_day": "{number} д",
-    "settings_pane_button_label": "Прилагодете ја страницата на Вашето Ново јазиче",
+    "settings_pane_button_label": "Прилагодете ја страницата на вашето Ново јазиче",
     "settings_pane_header": "Преференци за Ново јазиче",
-    "settings_pane_body": "Изберете што ќе гледате кога ќе отворите ново јазиче.",
+    "settings_pane_body2": "Изберете што ќе гледате на оваа страница.",
     "settings_pane_search_header": "Пребарување",
-    "settings_pane_search_body": "Пребарајте низ Интернет од Вашето ново јазиче.",
+    "settings_pane_search_body": "Пребарајте низ Интернет од вашето ново јазиче.",
     "settings_pane_topsites_header": "Врвни мрежни места",
     "settings_pane_topsites_body": "Пристапете до мрежните места што ги посетувате најмногу.",
     "settings_pane_topsites_options_showmore": "Прикажи два реда",
     "settings_pane_bookmarks_header": "Скорешни обележувачи",
     "settings_pane_bookmarks_body": "Вашите нови обележувачи во едно згодно место.",
     "settings_pane_visit_again_header": "Посети повторно",
-    "settings_pane_visit_again_body": "Firefox ќе прикаже делови од Вашата историја на прелистување кои можеби би сакале да ги запомните или пак да им се навратите.",
-    "settings_pane_pocketstories_header": "Врвни написи",
-    "settings_pane_pocketstories_body": "Pocket, дел од семејството на Mozilla, ќе Ви помогне да стигнете до високо-квалитетни содржини кои можеби не би ги откриле на друг начин.",
+    "settings_pane_visit_again_body": "Firefox ќе прикаже делови од вашата историја на прелистување кои можеби би сакале да ги запомните или пак да им се навратите.",
+    "settings_pane_highlights_header": "Интереси",
+    "settings_pane_highlights_body2": "Навратете се на интересни места што неодамна сте ги посетиле или обележале.",
+    "settings_pane_highlights_options_bookmarks": "Обележувачи",
+    "settings_pane_highlights_options_visited": "Посетени мрежни места",
+    "settings_pane_snippets_header": "Исечоци",
+    "settings_pane_snippets_body": "Прочитајте кратки и слатки новости од Mozilla во врска со Firefox, Интернет-културата и повремените случајни меми.",
     "settings_pane_done_button": "Готово",
     "edit_topsites_button_text": "Уреди",
-    "edit_topsites_button_label": "Прилагодете ги Вашите Врвни мрежни места",
+    "edit_topsites_button_label": "Прилагодете ги вашите Популарни мрежни места",
     "edit_topsites_showmore_button": "Прикажи повеќе",
     "edit_topsites_showless_button": "Прикажи помалку",
     "edit_topsites_done_button": "Готово",
     "edit_topsites_pin_button": "Прикачи го ова мрежно место",
     "edit_topsites_unpin_button": "Откачи го ова мрежно место",
     "edit_topsites_edit_button": "Уреди го ова место",
     "edit_topsites_dismiss_button": "Отфрли го ова место",
     "edit_topsites_add_button": "Додај",
@@ -4279,20 +4327,20 @@
     "topsites_form_url_placeholder": "Внесете или вметнете URL",
     "topsites_form_add_button": "Додај",
     "topsites_form_save_button": "Сними",
     "topsites_form_cancel_button": "Откажи",
     "topsites_form_url_validation": "Потребен е валиден URL",
     "pocket_read_more": "Популарни теми:",
     "pocket_read_even_more": "Види повеќе написи",
     "pocket_feedback_header": "Најдоброто од Интернет, одбрано од повеќе од 25 милиони луѓе.",
-    "pocket_feedback_body": "Pocket, дел од семејството на Mozilla, ќе Ви помогне да стигнете до високо-квалитетни содржини кои можеби не би ги откриле на друг начин.",
-    "pocket_send_feedback": "Остави коментар",
+    "pocket_description": "Откријте високо-квалитетни содржини, коишто инаку би можеле да ги пропуштите, со помош на Pocket, кој сега е дел од Mozilla.",
+    "highlights_empty_state": "Започнете со прелистување и ние овде ќе ви прикажеме некои од одличните написи, видеа и други страници што неодамна сте ги поселите или обележале.",
     "topstories_empty_state": "Имате видено сѐ! Навратете се подоцна за нови содржини од {provider}. Не можете да чекате? Изберете популарна тема и откријте уште одлични содржини ширум Интернет.",
-    "manual_migration_explanation": "Пробајте го Firefox со Вашите омилени мрежни места и обележувачи од друг прелистувач.",
+    "manual_migration_explanation2": "Пробајте го Firefox со обележувачите, историјата и лозинките на друг прелистувач.",
     "manual_migration_cancel_button": "Не, благодарам",
     "manual_migration_import_button": "Увези сега"
   },
   "ml": {
     "newtab_page_title": "പുതിയ ടാബ്",
     "default_label_loading": "ലോഡ്ചെയ്യുന്നു…",
     "header_top_sites": "മികച്ച സൈറ്റുകൾ",
     "header_highlights": "ഹൈലൈറ്റുകൾ",
@@ -6087,17 +6135,17 @@
   },
   "th": {
     "newtab_page_title": "แท็บใหม่",
     "default_label_loading": "กำลังโหลด…",
     "header_top_sites": "ไซต์เด่น",
     "header_stories": "เรื่องราวเด่น",
     "header_highlights": "รายการเด่น",
     "header_visit_again": "เยี่ยมชมอีกครั้ง",
-    "header_bookmarks": "ที่คั่นหน้าเมื่อเร็ว ๆ นี้",
+    "header_bookmarks": "ที่คั่นหน้าล่าสุด",
     "header_recommended_by": "แนะนำโดย {provider}",
     "header_bookmarks_placeholder": "คุณยังไม่มีที่คั่นหน้าใด ๆ",
     "header_stories_from": "จาก",
     "type_label_visited": "เยี่ยมชมแล้ว",
     "type_label_bookmarked": "มีที่คั่นหน้าแล้ว",
     "type_label_synced": "ซิงค์จากอุปกรณ์อื่น",
     "type_label_recommended": "กำลังนิยม",
     "type_label_open": "เปิด",
@@ -6134,17 +6182,17 @@
     "settings_pane_button_label": "ปรับแต่งหน้าแท็บใหม่ของคุณ",
     "settings_pane_header": "ค่ากำหนดแท็บใหม่",
     "settings_pane_body2": "เลือกสิ่งที่คุณเห็นในหน้านี้",
     "settings_pane_search_header": "ค้นหา",
     "settings_pane_search_body": "ค้นหาเว็บจากแท็บใหม่ของคุณ",
     "settings_pane_topsites_header": "ไซต์เด่น",
     "settings_pane_topsites_body": "เข้าถึงเว็บไซต์ที่คุณเยี่ยมชมมากที่สุด",
     "settings_pane_topsites_options_showmore": "แสดงสองแถว",
-    "settings_pane_bookmarks_header": "ที่คั่นหน้าเมื่อเร็ว ๆ นี้",
+    "settings_pane_bookmarks_header": "ที่คั่นหน้าล่าสุด",
     "settings_pane_bookmarks_body": "ที่คั่นหน้าที่สร้างใหม่ของคุณในตำแหน่งที่ตั้งเดียวที่สะดวก",
     "settings_pane_visit_again_header": "เยี่ยมชมอีกครั้ง",
     "settings_pane_highlights_header": "รายการเด่น",
     "settings_pane_highlights_options_bookmarks": "ที่คั่นหน้า",
     "settings_pane_highlights_options_visited": "ไซต์ที่เยี่ยมชมแล้ว",
     "settings_pane_snippets_header": "ส่วนย่อย",
     "settings_pane_done_button": "เสร็จสิ้น",
     "edit_topsites_button_text": "แก้ไข",
@@ -6612,18 +6660,18 @@
     "settings_pane_bookmarks_header": "最近的书签",
     "settings_pane_bookmarks_body": "您最近创建的书签将在此显示。",
     "settings_pane_visit_again_header": "再次造访",
     "settings_pane_visit_again_body": "Firefox 在此显示您可能想记住或将再次访问的浏览记录。",
     "settings_pane_highlights_header": "集锦",
     "settings_pane_highlights_body2": "根据您最近访问的页面和添加的书签推荐您感兴趣的东西。",
     "settings_pane_highlights_options_bookmarks": "书签",
     "settings_pane_highlights_options_visited": "访问过的网站",
-    "settings_pane_snippets_header": "板报",
-    "settings_pane_snippets_body": "阅读和了解 Mozilla 就 Firefox、互联网文化等提供的一些简短而有趣的更新。",
+    "settings_pane_snippets_header": "只言片语",
+    "settings_pane_snippets_body": "阅读 Mozilla 就 Firefox、互联网文化、偶尔还有模因提供的一些简短而有趣的小文章。",
     "settings_pane_done_button": "完成",
     "edit_topsites_button_text": "编辑",
     "edit_topsites_button_label": "定制您的“常用网站”区域",
     "edit_topsites_showmore_button": "显示更多",
     "edit_topsites_showless_button": "显示更少",
     "edit_topsites_done_button": "完成",
     "edit_topsites_pin_button": "固定此网站",
     "edit_topsites_unpin_button": "取消固定此网站",
@@ -6636,17 +6684,17 @@
     "topsites_form_url_placeholder": "输入或粘贴一个网址",
     "topsites_form_add_button": "添加",
     "topsites_form_save_button": "保存",
     "topsites_form_cancel_button": "取消",
     "topsites_form_url_validation": "需要有效的网址",
     "pocket_read_more": "热门主题:",
     "pocket_read_even_more": "查看更多文章",
     "pocket_feedback_header": "由超过 2500 万人挑选出来的网上精华内容。",
-    "pocket_description": "借助 Pocket(目前所属 Mozilla)发现有趣的高品质内容。",
+    "pocket_description": "借助 Pocket(目前属 Mozilla 旗下)发现您不容错过的高品质内容。",
     "highlights_empty_state": "开始浏览旅程吧,之后这里会显示您最近看过或加了书签的精彩文章、视频以及其他页面。",
     "topstories_empty_state": "所有文章都读完啦!晚点再来,{provider} 将推荐更多热门文章。等不及了?选择一个热门话题,找到更多网上的好文章。",
     "manual_migration_explanation2": "把在其他浏览器中保存的书签、历史记录和密码带到 Firefox 吧。",
     "manual_migration_cancel_button": "不用了",
     "manual_migration_import_button": "立即导入"
   },
   "zh-TW": {
     "newtab_page_title": "新分頁",
@@ -6728,17 +6776,17 @@
     "topsites_form_title_placeholder": "輸入標題",
     "topsites_form_url_placeholder": "輸入或貼上網址",
     "topsites_form_add_button": "新增",
     "topsites_form_save_button": "儲存",
     "topsites_form_cancel_button": "取消",
     "topsites_form_url_validation": "請輸入有效的網址",
     "pocket_read_more": "熱門主題:",
     "pocket_read_even_more": "檢視更多文章",
-    "pocket_feedback_header": "由超過兩千五百萬人找出來的 Web 最佳內容。",
-    "pocket_description": "透過現在也是 Mozilla 旗下一員的 Pocket 的幫助,發現您先前可能錯過的高品質內容。",
+    "pocket_feedback_header": "超過兩千五百萬人共同探索出的 Web 最佳內容。",
+    "pocket_description": "透過 Mozilla 旗下的 Pocket 服務,發現您可能錯過的優質內容。",
     "highlights_empty_state": "開始上網,我們就會把您在網路上發現的好文章、影片、剛加入書籤的頁面顯示於此。",
     "topstories_empty_state": "所有文章都讀完啦!晚點再來,{provider} 將提供更多推薦故事。等不及了?選擇熱門主題,看看 Web 上各式精采資訊。",
     "manual_migration_explanation2": "試試將其他瀏覽器的書籤、瀏覽記錄與密碼匯入 Firefox。",
     "manual_migration_cancel_button": "不必了",
     "manual_migration_import_button": "立即匯入"
   }
 }
\ No newline at end of file
--- a/browser/extensions/activity-stream/install.rdf.in
+++ b/browser/extensions/activity-stream/install.rdf.in
@@ -3,17 +3,17 @@
 #filter substitution
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
   <Description about="urn:mozilla:install-manifest">
     <em:id>activity-stream@mozilla.org</em:id>
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
     <em:unpack>false</em:unpack>
-    <em:version>2017.09.22.1389-2ee94db4</em:version>
+    <em:version>2017.09.28.0389-b24927a8</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/HighlightsFeed.jsm
+++ b/browser/extensions/activity-stream/lib/HighlightsFeed.jsm
@@ -155,17 +155,17 @@ this.HighlightsFeed = class HighlightsFe
           this.fetchHighlights(true);
         } else if (Date.now() - this.highlightsLastUpdated >= HIGHLIGHTS_UPDATE_TIME) {
           // If the last time we refreshed the data is greater than 15 minutes, fetch again.
           this.fetchHighlights(false);
         }
         break;
       case at.MIGRATION_COMPLETED:
       case at.PLACES_HISTORY_CLEARED:
-      case at.PLACES_LINK_DELETED:
+      case at.PLACES_LINKS_DELETED:
       case at.PLACES_LINK_BLOCKED:
         this.fetchHighlights(true);
         break;
       case at.PLACES_BOOKMARK_ADDED:
       case at.PLACES_BOOKMARK_REMOVED:
       case at.TOP_SITES_UPDATED:
         this.fetchHighlights(false);
         break;
--- a/browser/extensions/activity-stream/lib/PlacesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/PlacesFeed.jsm
@@ -37,21 +37,34 @@ class HistoryObserver extends Observer {
   }
 
   /**
    * onDeleteURI - Called when an link is deleted from history.
    *
    * @param  {obj} uri        A URI object representing the link's url
    *         {str} uri.spec   The URI as a string
    */
-  onDeleteURI(uri) {
-    this.dispatch({
-      type: at.PLACES_LINK_DELETED,
-      data: {url: uri.spec}
-    });
+  async onDeleteURI(uri) {
+    // Add to an existing array of links if we haven't dispatched yet
+    const {spec} = uri;
+    if (this._deletedLinks) {
+      this._deletedLinks.push(spec);
+    } else {
+      // Store an array of synchronously deleted links
+      this._deletedLinks = [spec];
+
+      // Only dispatch a single action when we've gotten all deleted urls
+      await Promise.resolve().then(() => {
+        this.dispatch({
+          type: at.PLACES_LINKS_DELETED,
+          data: this._deletedLinks
+        });
+        delete this._deletedLinks;
+      });
+    }
   }
 
   /**
    * onClearHistory - Called when the user clears their entire history.
    */
   onClearHistory() {
     this.dispatch({type: at.PLACES_HISTORY_CLEARED});
   }
--- a/browser/extensions/activity-stream/lib/PrefsFeed.jsm
+++ b/browser/extensions/activity-stream/lib/PrefsFeed.jsm
@@ -3,16 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {utils: Cu} = Components;
 
 const {actionCreators: ac, actionTypes: at} = Cu.import("resource://activity-stream/common/Actions.jsm", {});
 const {Prefs} = Cu.import("resource://activity-stream/lib/ActivityStreamPrefs.jsm", {});
 const {PrerenderData} = Cu.import("resource://activity-stream/common/PrerenderData.jsm", {});
+Cu.import("resource://gre/modules/Services.jsm");
+
+const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
 
 this.PrefsFeed = class PrefsFeed {
   constructor(prefMap) {
     this._prefMap = prefMap;
     this._prefs = new Prefs();
   }
 
   // If the any prefs are set to something other than what the prerendered version
@@ -22,48 +25,72 @@ this.PrefsFeed = class PrefsFeed {
   }
 
   _checkPrerender(name) {
     if (PrerenderData.invalidatingPrefs.includes(name)) {
       this._setPrerenderPref();
     }
   }
 
+  _initOnboardingPref() {
+    const snippetsEnabled = this._prefs.get("feeds.snippets");
+    if (!snippetsEnabled) {
+      this.setOnboardingDisabledDefault(true);
+    }
+  }
+
+  setOnboardingDisabledDefault(value) {
+    const branch = Services.prefs.getDefaultBranch("");
+    branch.setBoolPref(ONBOARDING_FINISHED_PREF, value);
+  }
+
   onPrefChanged(name, value) {
     if (this._prefMap.has(name)) {
       this.store.dispatch(ac.BroadcastToContent({type: at.PREF_CHANGED, data: {name, value}}));
     }
+
     this._checkPrerender(name);
+
+    if (name === "feeds.snippets") {
+      // If snippets are disabled, onboarding notifications should also be
+      // disabled because they look like snippets.
+      this.setOnboardingDisabledDefault(!value);
+    }
   }
 
   init() {
     this._prefs.observeBranch(this);
 
     // Get the initial value of each activity stream pref
     const values = {};
     for (const name of this._prefMap.keys()) {
       values[name] = this._prefs.get(name);
     }
 
     // Set the initial state of all prefs in redux
     this.store.dispatch(ac.BroadcastToContent({type: at.PREFS_INITIAL_VALUES, data: values}));
 
     this._setPrerenderPref();
+    this._initOnboardingPref();
   }
   removeListeners() {
     this._prefs.ignoreBranch(this);
   }
   onAction(action) {
     switch (action.type) {
       case at.INIT:
         this.init();
         break;
       case at.UNINIT:
         this.removeListeners();
+        this.setOnboardingDisabledDefault(false);
         break;
       case at.SET_PREF:
         this._prefs.set(action.data.name, action.data.value);
         break;
+      case at.DISABLE_ONBOARDING:
+        this.setOnboardingDisabledDefault(true);
+        break;
     }
   }
 };
 
 this.EXPORTED_SYMBOLS = ["PrefsFeed"];
--- a/browser/extensions/activity-stream/lib/SnippetsFeed.jsm
+++ b/browser/extensions/activity-stream/lib/SnippetsFeed.jsm
@@ -12,18 +12,18 @@ const {actionTypes: at, actionCreators: 
 XPCOMUtils.defineLazyModuleGetter(this, "ShellService",
   "resource:///modules/ShellService.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ProfileAge",
   "resource://gre/modules/ProfileAge.jsm");
 
 // Url to fetch snippets, in the urlFormatter service format.
 const SNIPPETS_URL_PREF = "browser.aboutHomeSnippets.updateUrl";
 const TELEMETRY_PREF = "datareporting.healthreport.uploadEnabled";
+const FXA_USERNAME_PREF = "services.sync.username";
 const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
-const FXA_USERNAME_PREF = "services.sync.username";
 // Prefix for any target matching a search engine.
 const TARGET_SEARCHENGINE_PREFIX = "searchEngine-";
 
 const SEARCH_ENGINE_OBSERVER_TOPIC = "browser-search-engine-modified";
 
 // Should be bumped up if the snippets content format changes.
 const STARTPAGE_VERSION = 5;
 
--- a/browser/extensions/activity-stream/lib/TelemetryFeed.jsm
+++ b/browser/extensions/activity-stream/lib/TelemetryFeed.jsm
@@ -16,16 +16,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource://activity-stream/common/PerfService.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PingCentre",
   "resource:///modules/PingCentre.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
   "@mozilla.org/uuid-generator;1",
   "nsIUUIDGenerator");
 
+const ACTIVITY_STREAM_ID = "activity-stream";
 const ACTIVITY_STREAM_ENDPOINT_PREF = "browser.newtabpage.activity-stream.telemetry.ping.endpoint";
 
 // This is a mapping table between the user preferences and its encoding code
 const USER_PREFS_ENCODING = {
   "showSearch": 1 << 0,
   "showTopSites": 1 << 1,
   "feeds.section.topstories": 1 << 2
 };
@@ -155,22 +156,20 @@ this.TelemetryFeed = class TelemetryFeed
   _onTelemetryPrefChange(prefVal) {
     this.telemetryEnabled = prefVal;
   }
 
   /**
    * Lazily initialize PingCentre to send pings
    */
   get pingCentre() {
-    const ACTIVITY_STREAM_ID = "activity-stream";
     Object.defineProperty(this, "pingCentre",
       {
         value: new PingCentre({
           topic: ACTIVITY_STREAM_ID,
-          filter: ACTIVITY_STREAM_ID,
           overrideEndpointPref: ACTIVITY_STREAM_ENDPOINT_PREF
         })
       });
     return this.pingCentre;
   }
 
   /**
    * Get encoded user preferences, multiple prefs will be combined via bitwise OR operator
@@ -358,17 +357,18 @@ this.TelemetryFeed = class TelemetryFeed
         action: "activity_stream_session",
         perf: session.perf
       }
     );
   }
 
   async sendEvent(event_object) {
     if (this.telemetryEnabled) {
-      this.pingCentre.sendPing(event_object);
+      this.pingCentre.sendPing(event_object,
+      {filter: ACTIVITY_STREAM_ID});
     }
   }
 
   handleImpressionStats(action) {
     const payload = action.data;
     let guidSet;
     let index;
 
--- a/browser/extensions/activity-stream/lib/TopSitesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/TopSitesFeed.jsm
@@ -259,18 +259,18 @@ this.TopSitesFeed = class TopSitesFeed {
           (Date.now() - this.lastUpdated >= UPDATE_TIME)
         ) {
           this.refresh(action.meta.fromTarget);
         }
         break;
       // All these actions mean we need new top sites
       case at.MIGRATION_COMPLETED:
       case at.PLACES_HISTORY_CLEARED:
-      case at.PLACES_LINK_DELETED:
       case at.PLACES_LINK_BLOCKED:
+      case at.PLACES_LINKS_DELETED:
         this.frecentCache.expire();
         this.refresh();
         break;
       case at.PREF_CHANGED:
         if (action.data.name === DEFAULT_SITES_PREF) {
           this.refreshDefaults(action.data.value);
         }
         break;
--- a/browser/extensions/activity-stream/test/unit/common/Reducers.test.js
+++ b/browser/extensions/activity-stream/test/unit/common/Reducers.test.js
@@ -353,23 +353,32 @@ describe("Reducers", () => {
       const data = {rows: [], initialized: false, id: "foo_bar_2"};
       const action = {type: at.SECTION_UPDATE, data};
       const newState = Sections(oldState, action);
       const updatedSection = newState.find(section => section.id === "foo_bar_2");
       assert.propertyVal(updatedSection, "initialized", false);
     });
     it("should remove blocked and deleted urls from all rows in all sections", () => {
       const blockAction = {type: at.PLACES_LINK_BLOCKED, data: {url: "www.foo.bar"}};
-      const deleteAction = {type: at.PLACES_LINK_DELETED, data: {url: "www.foo.bar"}};
+      const deleteAction = {type: at.PLACES_LINKS_DELETED, data: ["www.foo.bar"]};
       const newBlockState = Sections(oldState, blockAction);
       const newDeleteState = Sections(oldState, deleteAction);
       newBlockState.concat(newDeleteState).forEach(section => {
         assert.deepEqual(section.rows, [{url: "www.other.url"}]);
       });
     });
+    it("should remove all deleted urls", () => {
+      const deleteAction = {type: at.PLACES_LINKS_DELETED, data: ["www.foo.bar", "www.other.url"]};
+
+      const newState = Sections(oldState, deleteAction);
+
+      newState.forEach(section => {
+        assert.lengthOf(section.rows, 0);
+      });
+    });
     it("should not update state for empty action.data on PLACES_BOOKMARK_ADDED", () => {
       const nextState = Sections(undefined, {type: at.PLACES_BOOKMARK_ADDED});
       assert.equal(nextState, INITIAL_STATE.Sections);
     });
     it("should bookmark an item when PLACES_BOOKMARK_ADDED is received", () => {
       const action = {
         type: at.PLACES_BOOKMARK_ADDED,
         data: {
--- a/browser/extensions/activity-stream/test/unit/lib/HighlightsFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/HighlightsFeed.test.js
@@ -291,20 +291,20 @@ describe("Highlights Feed", () => {
     });
     it("should fetch highlights on PLACES_HISTORY_CLEARED", async () => {
       await feed.fetchHighlights();
       feed.fetchHighlights = sinon.spy();
       feed.onAction({type: at.PLACES_HISTORY_CLEARED});
       assert.calledOnce(feed.fetchHighlights);
       assert.calledWith(feed.fetchHighlights, true);
     });
-    it("should fetch highlights on PLACES_LINK_DELETED", async () => {
+    it("should fetch highlights on PLACES_LINKS_DELETED", async () => {
       await feed.fetchHighlights();
       feed.fetchHighlights = sinon.spy();
-      feed.onAction({type: at.PLACES_LINK_DELETED});
+      feed.onAction({type: at.PLACES_LINKS_DELETED});
       assert.calledOnce(feed.fetchHighlights);
       assert.calledWith(feed.fetchHighlights, true);
     });
     it("should fetch highlights on PLACES_LINK_BLOCKED", async () => {
       await feed.fetchHighlights();
       feed.fetchHighlights = sinon.spy();
       feed.onAction({type: at.PLACES_LINK_BLOCKED});
       assert.calledOnce(feed.fetchHighlights);
--- a/browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js
@@ -174,19 +174,30 @@ describe("PlacesFeed", () => {
     beforeEach(() => {
       dispatch = sandbox.spy();
       observer = new HistoryObserver(dispatch);
     });
     it("should have a QueryInterface property", () => {
       assert.property(observer, "QueryInterface");
     });
     describe("#onDeleteURI", () => {
-      it("should dispatch a PLACES_LINK_DELETED action with the right url", () => {
+      it("should dispatch a PLACES_LINKS_DELETED action with the right url", async() => {
+        await observer.onDeleteURI({spec: "foo.com"});
+
+        assert.calledWith(dispatch, {type: at.PLACES_LINKS_DELETED, data: ["foo.com"]});
+      });
+      it("should dispatch a PLACES_LINKS_DELETED action with multiple urls", async() => {
+        const promise = observer.onDeleteURI({spec: "bar.com"});
         observer.onDeleteURI({spec: "foo.com"});
-        assert.calledWith(dispatch, {type: at.PLACES_LINK_DELETED, data: {url: "foo.com"}});
+        await promise;
+
+        const result = dispatch.firstCall.args[0].data;
+        assert.lengthOf(result, 2);
+        assert.equal(result[0], "bar.com");
+        assert.equal(result[1], "foo.com");
       });
     });
     describe("#onClearHistory", () => {
       it("should dispatch a PLACES_HISTORY_CLEARED action", () => {
         observer.onClearHistory();
         assert.calledWith(dispatch, {type: at.PLACES_HISTORY_CLEARED});
       });
     });
--- a/browser/extensions/activity-stream/test/unit/lib/PrefsFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/PrefsFeed.test.js
@@ -1,14 +1,15 @@
 const {PrefsFeed} = require("lib/PrefsFeed.jsm");
 const {actionTypes: at, actionCreators: ac} = require("common/Actions.jsm");
 const {PrerenderData} = require("common/PrerenderData.jsm");
 const {initialPrefs} = PrerenderData;
 
 const PRERENDER_PREF_NAME = "prerender";
+const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
 
 describe("PrefsFeed", () => {
   let feed;
   let FAKE_PREFS;
   beforeEach(() => {
     FAKE_PREFS = new Map([["foo", 1], ["bar", 2]]);
     feed = new PrefsFeed(FAKE_PREFS);
     feed.store = {dispatch: sinon.spy()};
@@ -57,16 +58,54 @@ describe("PrefsFeed", () => {
     });
     it("should set prerender pref to false if a pref does not match its initial value", () => {
       Object.keys(initialPrefs).forEach(name => FAKE_PREFS.set(name, initialPrefs[name]));
       FAKE_PREFS.set("showSearch", false);
       feed.onAction({type: at.INIT});
       assert.calledWith(feed._prefs.set, PRERENDER_PREF_NAME, false);
     });
   });
+  describe("Onboarding", () => {
+    let sandbox;
+    let defaultBranch;
+    beforeEach(() => {
+      sandbox = sinon.sandbox.create();
+      defaultBranch = {setBoolPref: sandbox.stub()};
+      sandbox.stub(global.Services.prefs, "getDefaultBranch").returns(defaultBranch);
+    });
+    afterEach(() => {
+      sandbox.restore();
+    });
+    it("should set ONBOARDING_FINISHED_PREF to true if prefs.feeds.snippets if false", () => {
+      FAKE_PREFS.set("feeds.snippets", false);
+      feed.onAction({type: at.INIT});
+      assert.calledWith(defaultBranch.setBoolPref, ONBOARDING_FINISHED_PREF, true);
+    });
+    it("should not set ONBOARDING_FINISHED_PREF if prefs.feeds.snippets is true", () => {
+      FAKE_PREFS.set("feeds.snippets", true);
+      feed.onAction({type: at.INIT});
+      assert.notCalled(defaultBranch.setBoolPref);
+    });
+    it("should set ONBOARDING_FINISHED_PREF to true if the feeds.snippets pref changes to false", () => {
+      feed.onPrefChanged("feeds.snippets", false);
+      assert.calledWith(defaultBranch.setBoolPref, ONBOARDING_FINISHED_PREF, true);
+    });
+    it("should set ONBOARDING_FINISHED_PREF to false if the feeds.snippets pref changes to true", () => {
+      feed.onPrefChanged("feeds.snippets", true);
+      assert.calledWith(defaultBranch.setBoolPref, ONBOARDING_FINISHED_PREF, false);
+    });
+    it("should not set ONBOARDING_FINISHED_PREF if an unrelated pref changes", () => {
+      feed.onPrefChanged("foo", true);
+      assert.notCalled(defaultBranch.setBoolPref);
+    });
+    it("should set ONBOARDING_FINISHED_PREF to true if a DISABLE_ONBOARDING action was received", () => {
+      feed.onAction({type: at.DISABLE_ONBOARDING});
+      assert.calledWith(defaultBranch.setBoolPref, ONBOARDING_FINISHED_PREF, true);
+    });
+  });
   describe("onPrefChanged prerendering", () => {
     it("should not change the prerender pref if the pref is not included in invalidatingPrefs", () => {
       feed.onPrefChanged("foo123", true);
       assert.notCalled(feed._prefs.set);
     });
     it("should set the prerender pref to false if a pref in invalidatingPrefs is changed from its original value", () => {
       Object.keys(initialPrefs).forEach(name => FAKE_PREFS.set(name, initialPrefs[name]));
 
--- a/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
@@ -502,19 +502,19 @@ describe("Top Sites Feed", () => {
       assert.equal(feed.refresh.firstCall.args[0], null);
     });
     it("should call refresh without a target on PLACES_LINK_BLOCKED action", async () => {
       sinon.stub(feed, "refresh");
       await feed.onAction({type: at.PLACES_LINK_BLOCKED});
       assert.calledOnce(feed.refresh);
       assert.equal(feed.refresh.firstCall.args[0], null);
     });
-    it("should call refresh without a target on PLACES_LINK_DELETED action", async () => {
+    it("should call refresh without a target on PLACES_LINKS_DELETED action", async () => {
       sinon.stub(feed, "refresh");
-      await feed.onAction({type: at.PLACES_LINK_DELETED});
+      await feed.onAction({type: at.PLACES_LINKS_DELETED});
       assert.calledOnce(feed.refresh);
       assert.equal(feed.refresh.firstCall.args[0], null);
     });
     it("should call pin with correct args on TOP_SITES_ADD", () => {
       const addAction = {
         type: at.TOP_SITES_ADD,
         data: {site: {url: "foo.bar", label: "foo"}}
       };
--- a/browser/extensions/activity-stream/test/unit/unit-entry.js
+++ b/browser/extensions/activity-stream/test/unit/unit-entry.js
@@ -22,16 +22,18 @@ overrider.set({
       now: () => window.performance.now()
     },
     isSuccessCode: () => true
   },
   // eslint-disable-next-line object-shorthand
   ContentSearchUIController: function() {}, // NB: This is a function/constructor
   dump() {},
   fetch() {},
+  // eslint-disable-next-line object-shorthand
+  Image: function() {}, // NB: This is a function/constructor
   Preferences: FakePrefs,
   Services: {
     locale: {
       getAppLocalesAsLangTags() {},
       getRequestedLocale() {},
       negotiateLanguages() {}
     },
     urlFormatter: {formatURL: str => str},
--- a/browser/modules/PingCentre.jsm
+++ b/browser/modules/PingCentre.jsm
@@ -33,17 +33,16 @@ const FHR_UPLOAD_ENABLED_PREF = "datarep
  */
 class PingCentre {
   constructor(options) {
     if (!options.topic) {
       throw new Error("Must specify topic.");
     }
 
     this._topic = options.topic;
-    this._filter = options.filter;
     this._prefs = Services.prefs.getBranch("");
 
     this._setPingEndpoint(options.topic, options.overrideEndpointPref);
 
     this._enabled = this._prefs.getBoolPref(TELEMETRY_PREF);
     this._onTelemetryPrefChange = this._onTelemetryPrefChange.bind(this);
     this._prefs.addObserver(TELEMETRY_PREF, this._onTelemetryPrefChange);
 
@@ -87,33 +86,34 @@ class PingCentre {
   _onTelemetryPrefChange(aSubject, aTopic, prefKey) {
     this._enabled = this._prefs.getBoolPref(prefKey);
   }
 
   _onFhrPrefChange(aSubject, aTopic, prefKey) {
     this._fhrEnabled = this._prefs.getBoolPref(prefKey);
   }
 
-  _createExperimentsString(activeExperiments) {
+  _createExperimentsString(activeExperiments, filter) {
     let experimentsString = "";
     for (let experimentID in activeExperiments) {
       if (!activeExperiments[experimentID] ||
           !activeExperiments[experimentID].branch ||
-          (this._filter && !experimentID.includes(this._filter))) {
+          (filter && !experimentID.includes(filter))) {
         continue;
       }
       let expString = `${experimentID}:${activeExperiments[experimentID].branch}`;
       experimentsString = experimentsString.concat(`${expString};`);
     }
     return experimentsString;
   }
 
-  async sendPing(data) {
+  async sendPing(data, options) {
+    let filter = options && options.filter;
     let experiments = TelemetryEnvironment.getActiveExperiments();
-    let experimentsString = this._createExperimentsString(experiments);
+    let experimentsString = this._createExperimentsString(experiments, filter);
     if (!this.enabled) {
       return Promise.resolve();
     }
 
     let clientID = data.client_id || await this.telemetryClientId;
     let locale = data.locale || Services.locale.getAppLocalesAsLangTags().pop();
     const payload = Object.assign({
       locale,