--- a/browser/components/newtab/aboutNewTabService.js
+++ b/browser/components/newtab/aboutNewTabService.js
@@ -16,17 +16,17 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource:///modules/AboutNewTab.jsm");
const LOCAL_NEWTAB_URL = "chrome://browser/content/newtab/newTab.xhtml";
const TOPIC_APP_QUIT = "quit-application-granted";
const TOPIC_LOCALES_CHANGE = "intl:requested-locales-changed";
// Automated tests ensure packaged locales are in this list. Copied output of:
// https://github.com/mozilla/activity-stream/blob/master/bin/render-activity-stream-html.js
-const ACTIVITY_STREAM_LOCALES = new Set("en-US ach ar ast az be bg bn-BD bn-IN br bs ca cak cs cy da de dsb el en-GB eo es-AR es-CL es-ES es-MX et eu fa ff fi fr fy-NL ga-IE gd gl gn gu-IN he hi-IN hr hsb hu hy-AM ia id it ja ka kab kk km kn ko lij lo lt ltg lv mk ml mr ms my nb-NO ne-NP nl nn-NO pa-IN pl pt-BR pt-PT rm ro ru si sk sl sq sr sv-SE ta te th tl tr uk ur uz vi zh-CN zh-TW".split(" "));
+const ACTIVITY_STREAM_LOCALES = "en-US ach ar ast az be bg bn-BD bn-IN br bs ca cak cs cy da de dsb el en-GB eo es-AR es-CL es-ES es-MX et eu fa ff fi fr fy-NL ga-IE gd gl gn gu-IN he hi-IN hr hsb hu hy-AM ia id it ja ka kab kk km kn ko lij lo lt ltg lv mk ml mr ms my nb-NO ne-NP nl nn-NO pa-IN pl pt-BR pt-PT rm ro ru si sk sl sq sr sv-SE ta te th tl tr uk ur uz vi zh-CN zh-TW".split(" ");
const ABOUT_URL = "about:newtab";
const IS_MAIN_PROCESS = Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT;
const IS_RELEASE_OR_BETA = AppConstants.RELEASE_OR_BETA;
// Pref that tells if activity stream is enabled
@@ -174,36 +174,24 @@ AboutNewTabService.prototype = {
return true;
},
/**
* Figure out what path under prerendered to use based on current state.
*/
updatePrerenderedPath() {
// Debug files are specially packaged in a non-localized directory
- let path;
- if (this._activityStreamDebug) {
- path = "static";
- } else {
- // Use the exact match locale if it's packaged
- const locale = Services.locale.getRequestedLocale();
- if (ACTIVITY_STREAM_LOCALES.has(locale)) {
- path = locale;
- } else {
- // Fall back to a shared-language packaged locale
- const language = locale.split("-")[0];
- if (ACTIVITY_STREAM_LOCALES.has(language)) {
- path = language;
- } else {
- // Just use the default locale as a final fallback
- path = "en-US";
- }
- }
- }
- this._activityStreamPath = `${path}/`;
+ this._activityStreamPath = `${this._activityStreamDebug ? "static" :
+ // Pick the best available locale to match the app locales
+ Services.locale.negotiateLanguages(
+ Services.locale.getAppLocalesAsLangTags(),
+ ACTIVITY_STREAM_LOCALES,
+ // defaultLocale's strings aren't necessarily packaged, but en-US' are
+ "en-US"
+ )[0]}/`;
},
/*
* Returns the default URL.
*
* This URL only depends on the browser.newtabpage.activity-stream.enabled pref. Overriding
* the newtab page has no effect on the result of this function.
*
--- a/browser/components/newtab/tests/browser/browser.ini
+++ b/browser/components/newtab/tests/browser/browser.ini
@@ -1,4 +1,5 @@
[DEFAULT]
[browser_packaged_as_locales.js]
+skip-if=true # bug 1423703 comment 20
[browser_newtab_overrides.js]
--- a/browser/extensions/activity-stream/common/Actions.jsm
+++ b/browser/extensions/activity-stream/common/Actions.jsm
@@ -1,15 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.MAIN_MESSAGE_TYPE = "ActivityStream:Main";
this.CONTENT_MESSAGE_TYPE = "ActivityStream:Content";
+this.PRELOAD_MESSAGE_TYPE = "ActivityStream:PreloadedBrowser";
this.UI_CODE = 1;
this.BACKGROUND_PROCESS = 2;
/**
* globalImportContext - Are we in UI code (i.e. react, a dom) or some kind of background process?
* Use this in action creators if you need different logic
* for ui/background processes.
*/
@@ -151,16 +152,29 @@ function SendToContent(action, target) {
return _RouteMessage(action, {
from: MAIN_MESSAGE_TYPE,
to: CONTENT_MESSAGE_TYPE,
toTarget: target
});
}
/**
+ * SendToPreloaded - Creates a message that will be sent to the preloaded tab.
+ *
+ * @param {object} action Any redux action (required)
+ * @return {object} An action with added .meta properties
+ */
+function SendToPreloaded(action) {
+ return _RouteMessage(action, {
+ from: MAIN_MESSAGE_TYPE,
+ to: PRELOAD_MESSAGE_TYPE
+ });
+}
+
+/**
* UserEvent - A telemetry ping indicating a user action. This should only
* be sent from the UI during a user session.
*
* @param {object} data Fields to include in the ping (source, etc.)
* @return {object} An SendToMain action
*/
function UserEvent(data) {
return SendToMain({
@@ -224,16 +238,17 @@ this.actionTypes = actionTypes;
this.actionCreators = {
BroadcastToContent,
UserEvent,
UndesiredEvent,
PerfEvent,
ImpressionStats,
SendToContent,
SendToMain,
+ SendToPreloaded,
SetPref
};
// These are helpers to test for certain kinds of actions
this.actionUtils = {
isSendToMain(action) {
if (!action.meta) {
return false;
@@ -253,16 +268,23 @@ this.actionUtils = {
if (!action.meta) {
return false;
}
if (action.meta.to === CONTENT_MESSAGE_TYPE && action.meta.toTarget) {
return true;
}
return false;
},
+ isSendToPreloaded(action) {
+ if (!action.meta) {
+ return false;
+ }
+ return action.meta.to === PRELOAD_MESSAGE_TYPE &&
+ action.meta.from === MAIN_MESSAGE_TYPE;
+ },
isFromMain(action) {
if (!action.meta) {
return false;
}
return action.meta.from === MAIN_MESSAGE_TYPE &&
action.meta.to === CONTENT_MESSAGE_TYPE;
},
getPortIdOfSender(action) {
@@ -274,10 +296,11 @@ this.actionUtils = {
this.EXPORTED_SYMBOLS = [
"actionTypes",
"actionCreators",
"actionUtils",
"globalImportContext",
"UI_CODE",
"BACKGROUND_PROCESS",
"MAIN_MESSAGE_TYPE",
- "CONTENT_MESSAGE_TYPE"
+ "CONTENT_MESSAGE_TYPE",
+ "PRELOAD_MESSAGE_TYPE"
];
--- a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
+++ b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
@@ -60,45 +60,57 @@
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 10);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
+/* unused harmony export MAIN_MESSAGE_TYPE */
+/* unused harmony export CONTENT_MESSAGE_TYPE */
+/* unused harmony export PRELOAD_MESSAGE_TYPE */
+/* unused harmony export UI_CODE */
+/* unused harmony export BACKGROUND_PROCESS */
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return actionCreators; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return actionUtils; });
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var MAIN_MESSAGE_TYPE = "ActivityStream:Main";
var CONTENT_MESSAGE_TYPE = "ActivityStream:Content";
+var PRELOAD_MESSAGE_TYPE = "ActivityStream:PreloadedBrowser";
var UI_CODE = 1;
var BACKGROUND_PROCESS = 2;
/**
* globalImportContext - Are we in UI code (i.e. react, a dom) or some kind of background process?
* Use this in action creators if you need different logic
* for ui/background processes.
*/
const globalImportContext = typeof Window === "undefined" ? BACKGROUND_PROCESS : UI_CODE;
+/* unused harmony export globalImportContext */
+
// Export for tests
-
// Create an object that avoids accidental differing key/value pairs:
// {
// INIT: "INIT",
// UNINIT: "UNINIT"
// }
const actionTypes = {};
+/* harmony export (immutable) */ __webpack_exports__["b"] = 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", "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", "PAGE_PRERENDERED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_DELETED", "PLACES_LINK_BLOCKED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "RICH_ICON_MISSING", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SETTINGS_CLOSE", "SETTINGS_OPEN", "SET_PREF", "SHOW_FIREFOX_ACCOUNTS", "SNIPPETS_BLOCKLIST_UPDATED", "SNIPPETS_DATA", "SNIPPETS_RESET", "SNIPPET_BLOCKED", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_EDIT", "TOP_SITES_INSERT", "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) : {};
@@ -160,16 +172,29 @@ function SendToContent(action, target) {
return _RouteMessage(action, {
from: MAIN_MESSAGE_TYPE,
to: CONTENT_MESSAGE_TYPE,
toTarget: target
});
}
/**
+ * SendToPreloaded - Creates a message that will be sent to the preloaded tab.
+ *
+ * @param {object} action Any redux action (required)
+ * @return {object} An action with added .meta properties
+ */
+function SendToPreloaded(action) {
+ return _RouteMessage(action, {
+ from: MAIN_MESSAGE_TYPE,
+ to: PRELOAD_MESSAGE_TYPE
+ });
+}
+
+/**
* UserEvent - A telemetry ping indicating a user action. This should only
* be sent from the UI during a user session.
*
* @param {object} data Fields to include in the ping (source, etc.)
* @return {object} An SendToMain action
*/
function UserEvent(data) {
return SendToMain({
@@ -231,16 +256,17 @@ function SetPref(name, value, importCont
var actionCreators = {
BroadcastToContent,
UserEvent,
UndesiredEvent,
PerfEvent,
ImpressionStats,
SendToContent,
SendToMain,
+ SendToPreloaded,
SetPref
};
// These are helpers to test for certain kinds of actions
var actionUtils = {
isSendToMain(action) {
if (!action.meta) {
@@ -261,37 +287,33 @@ var actionUtils = {
if (!action.meta) {
return false;
}
if (action.meta.to === CONTENT_MESSAGE_TYPE && action.meta.toTarget) {
return true;
}
return false;
},
+ isSendToPreloaded(action) {
+ if (!action.meta) {
+ return false;
+ }
+ return action.meta.to === PRELOAD_MESSAGE_TYPE && action.meta.from === MAIN_MESSAGE_TYPE;
+ },
isFromMain(action) {
if (!action.meta) {
return false;
}
return action.meta.from === MAIN_MESSAGE_TYPE && action.meta.to === CONTENT_MESSAGE_TYPE;
},
getPortIdOfSender(action) {
return action.meta && action.meta.fromTarget || null;
},
_RouteMessage
};
-module.exports = {
- actionTypes,
- actionCreators,
- actionUtils,
- globalImportContext,
- UI_CODE,
- BACKGROUND_PROCESS,
- MAIN_MESSAGE_TYPE,
- CONTENT_MESSAGE_TYPE
-};
/***/ }),
/* 1 */
/***/ (function(module, exports) {
module.exports = React;
/***/ }),
@@ -330,29 +352,73 @@ try {
// We return undefined, instead of nothing here, so it's
// easier to handle this case. if(!global) { ...}
module.exports = g;
/***/ }),
/* 5 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
+
+// EXTERNAL MODULE: ./system-addon/common/Actions.jsm
+var Actions = __webpack_require__(0);
+
+// CONCATENATED MODULE: ./system-addon/common/Dedupe.jsm
+class Dedupe {
+ constructor(createKey) {
+ this.createKey = createKey || this.defaultCreateKey;
+ }
+
+ defaultCreateKey(item) {
+ return item;
+ }
+
+ /**
+ * Dedupe any number of grouped elements favoring those from earlier groups.
+ *
+ * @param {Array} groups Contains an arbitrary number of arrays of elements.
+ * @returns {Array} A matching array of each provided group deduped.
+ */
+ group(...groups) {
+ const globalKeys = new Set();
+ const result = [];
+ for (const values of groups) {
+ const valueMap = new Map();
+ for (const value of values) {
+ const key = this.createKey(value);
+ if (!globalKeys.has(key) && !valueMap.has(key)) {
+ valueMap.set(key, value);
+ }
+ }
+ result.push(valueMap);
+ valueMap.forEach((value, key) => globalKeys.add(key));
+ }
+ return result.map(m => Array.from(m.values()));
+ }
+}
+// CONCATENATED MODULE: ./system-addon/common/Reducers.jsm
+/* unused harmony export insertPinned */
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return reducers; });
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-const { actionTypes: at } = __webpack_require__(0);
-const { Dedupe } = __webpack_require__(13);
+
const TOP_SITES_DEFAULT_LENGTH = 6;
+/* harmony export (immutable) */ __webpack_exports__["a"] = TOP_SITES_DEFAULT_LENGTH;
+
const TOP_SITES_SHOWMORE_LENGTH = 12;
+/* harmony export (immutable) */ __webpack_exports__["b"] = TOP_SITES_SHOWMORE_LENGTH;
+
+
const dedupe = new Dedupe(site => site && site.url);
const INITIAL_STATE = {
App: {
// Have we received real data from the app yet?
initialized: false,
// The version of the system-addon
@@ -377,20 +443,23 @@ const INITIAL_STATE = {
},
Dialog: {
visible: false,
data: {}
},
Sections: [],
PreferencesPane: { visible: false }
};
+/* unused harmony export INITIAL_STATE */
+
+
function App(prevState = INITIAL_STATE.App, action) {
switch (action.type) {
- case at.INIT:
+ case Actions["b" /* actionTypes */].INIT:
return Object.assign({}, prevState, action.data || {}, { initialized: true });
default:
return prevState;
}
}
/**
* insertPinned - Inserts pinned links in their specified slots
@@ -422,51 +491,52 @@ function insertPinned(links, pinned) {
} else {
newLinks.splice(index, 0, link);
}
});
return newLinks;
}
+
function TopSites(prevState = INITIAL_STATE.TopSites, action) {
let hasMatch;
let newRows;
switch (action.type) {
- case at.TOP_SITES_UPDATED:
+ case Actions["b" /* actionTypes */].TOP_SITES_UPDATED:
if (!action.data) {
return prevState;
}
return Object.assign({}, prevState, { initialized: true, rows: action.data });
- case at.TOP_SITES_EDIT:
+ case Actions["b" /* actionTypes */].TOP_SITES_EDIT:
return Object.assign({}, prevState, { editForm: { visible: true, index: action.data.index } });
- case at.TOP_SITES_CANCEL_EDIT:
+ case Actions["b" /* actionTypes */].TOP_SITES_CANCEL_EDIT:
return Object.assign({}, prevState, { editForm: { visible: false } });
- case at.SCREENSHOT_UPDATED:
+ case Actions["b" /* actionTypes */].SCREENSHOT_UPDATED:
newRows = prevState.rows.map(row => {
if (row && row.url === action.data.url) {
hasMatch = true;
return Object.assign({}, row, { screenshot: action.data.screenshot });
}
return row;
});
return hasMatch ? Object.assign({}, prevState, { rows: newRows }) : prevState;
- case at.PLACES_BOOKMARK_ADDED:
+ case Actions["b" /* actionTypes */].PLACES_BOOKMARK_ADDED:
if (!action.data) {
return prevState;
}
newRows = prevState.rows.map(site => {
if (site && site.url === action.data.url) {
const { bookmarkGuid, bookmarkTitle, dateAdded } = action.data;
return Object.assign({}, site, { bookmarkGuid, bookmarkTitle, bookmarkDateCreated: dateAdded });
}
return site;
});
return Object.assign({}, prevState, { rows: newRows });
- case at.PLACES_BOOKMARK_REMOVED:
+ case Actions["b" /* actionTypes */].PLACES_BOOKMARK_REMOVED:
if (!action.data) {
return prevState;
}
newRows = prevState.rows.map(site => {
if (site && site.url === action.data.url) {
const newSite = Object.assign({}, site);
delete newSite.bookmarkGuid;
delete newSite.bookmarkTitle;
@@ -478,48 +548,48 @@ function TopSites(prevState = INITIAL_ST
return Object.assign({}, prevState, { rows: newRows });
default:
return prevState;
}
}
function Dialog(prevState = INITIAL_STATE.Dialog, action) {
switch (action.type) {
- case at.DIALOG_OPEN:
+ case Actions["b" /* actionTypes */].DIALOG_OPEN:
return Object.assign({}, prevState, { visible: true, data: action.data });
- case at.DIALOG_CANCEL:
+ case Actions["b" /* actionTypes */].DIALOG_CANCEL:
return Object.assign({}, prevState, { visible: false });
- case at.DELETE_HISTORY_URL:
+ case Actions["b" /* actionTypes */].DELETE_HISTORY_URL:
return Object.assign({}, INITIAL_STATE.Dialog);
default:
return prevState;
}
}
function Prefs(prevState = INITIAL_STATE.Prefs, action) {
let newValues;
switch (action.type) {
- case at.PREFS_INITIAL_VALUES:
+ case Actions["b" /* actionTypes */].PREFS_INITIAL_VALUES:
return Object.assign({}, prevState, { initialized: true, values: action.data });
- case at.PREF_CHANGED:
+ case Actions["b" /* actionTypes */].PREF_CHANGED:
newValues = Object.assign({}, prevState.values);
newValues[action.data.name] = action.data.value;
return Object.assign({}, prevState, { values: newValues });
default:
return prevState;
}
}
function Sections(prevState = INITIAL_STATE.Sections, action) {
let hasMatch;
let newState;
switch (action.type) {
- case at.SECTION_DEREGISTER:
+ case Actions["b" /* actionTypes */].SECTION_DEREGISTER:
return prevState.filter(section => section.id !== action.data);
- case at.SECTION_REGISTER:
+ case Actions["b" /* actionTypes */].SECTION_REGISTER:
// If section exists in prevState, update it
newState = prevState.map(section => {
if (section && section.id === action.data.id) {
hasMatch = true;
return Object.assign({}, section, action.data);
}
return section;
});
@@ -542,17 +612,17 @@ function Sections(prevState = INITIAL_ST
order = action.data.order !== undefined ? action.data.order : 0;
index = 0;
}
const section = Object.assign({ title: "", rows: [], order, enabled: false }, action.data, { initialized });
newState.splice(index, 0, section);
}
return newState;
- case at.SECTION_UPDATE:
+ case Actions["b" /* actionTypes */].SECTION_UPDATE:
newState = prevState.map(section => {
if (section && section.id === action.data.id) {
// If the action is updating rows, we should consider initialized to be true.
// This can be overridden if initialized is defined in the action.data
const initialized = action.data.rows ? { initialized: true } : {};
return Object.assign({}, section, initialized, action.data);
}
return section;
@@ -574,30 +644,30 @@ function Sections(prevState = INITIAL_ST
return Object.assign({}, section, { rows: dedupedRows });
}
return section;
});
});
return newState;
- case at.SECTION_UPDATE_CARD:
+ case Actions["b" /* actionTypes */].SECTION_UPDATE_CARD:
return prevState.map(section => {
if (section && section.id === action.data.id && section.rows) {
const newRows = section.rows.map(card => {
if (card.url === action.data.url) {
return Object.assign({}, card, action.data.options);
}
return card;
});
return Object.assign({}, section, { rows: newRows });
}
return section;
});
- case at.PLACES_BOOKMARK_ADDED:
+ case Actions["b" /* actionTypes */].PLACES_BOOKMARK_ADDED:
if (!action.data) {
return prevState;
}
return prevState.map(section => Object.assign({}, section, {
rows: section.rows.map(item => {
// find the item within the rows that is attempted to be bookmarked
if (item.url === action.data.url) {
const { bookmarkGuid, bookmarkTitle, dateAdded } = action.data;
@@ -606,17 +676,17 @@ function Sections(prevState = INITIAL_ST
bookmarkTitle,
bookmarkDateCreated: dateAdded,
type: "bookmark"
});
}
return item;
})
}));
- case at.PLACES_BOOKMARK_REMOVED:
+ case Actions["b" /* actionTypes */].PLACES_BOOKMARK_REMOVED:
if (!action.data) {
return prevState;
}
return prevState.map(section => Object.assign({}, section, {
rows: section.rows.map(item => {
// find the bookmark within the rows that is attempted to be removed
if (item.url === action.data.url) {
const newSite = Object.assign({}, item);
@@ -626,65 +696,57 @@ function Sections(prevState = INITIAL_ST
if (!newSite.type || newSite.type === "bookmark") {
newSite.type = "history";
}
return newSite;
}
return item;
})
}));
- case at.PLACES_LINKS_DELETED:
+ case Actions["b" /* actionTypes */].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:
+ case Actions["b" /* actionTypes */].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) {
switch (action.type) {
- case at.SNIPPETS_DATA:
+ case Actions["b" /* actionTypes */].SNIPPETS_DATA:
return Object.assign({}, prevState, { initialized: true }, action.data);
- case at.SNIPPETS_RESET:
+ case Actions["b" /* actionTypes */].SNIPPETS_RESET:
return INITIAL_STATE.Snippets;
default:
return prevState;
}
}
function PreferencesPane(prevState = INITIAL_STATE.PreferencesPane, action) {
switch (action.type) {
- case at.SETTINGS_OPEN:
+ case Actions["b" /* actionTypes */].SETTINGS_OPEN:
return Object.assign({}, prevState, { visible: true });
- case at.SETTINGS_CLOSE:
+ case Actions["b" /* actionTypes */].SETTINGS_CLOSE:
return Object.assign({}, prevState, { visible: false });
default:
return prevState;
}
}
var reducers = { TopSites, App, Snippets, Prefs, Dialog, Sections, PreferencesPane };
-module.exports = {
- reducers,
- INITIAL_STATE,
- insertPinned,
- TOP_SITES_DEFAULT_LENGTH,
- TOP_SITES_SHOWMORE_LENGTH
-};
/***/ }),
/* 6 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// EXTERNAL MODULE: ./system-addon/common/Actions.jsm
var Actions = __webpack_require__(0);
-var Actions_default = /*#__PURE__*/__webpack_require__.n(Actions);
// EXTERNAL MODULE: external "React"
var external__React_ = __webpack_require__(1);
var external__React__default = /*#__PURE__*/__webpack_require__.n(external__React_);
// CONCATENATED MODULE: ./system-addon/content-src/components/ContextMenu/ContextMenu.jsx
@@ -778,115 +840,115 @@ var external__ReactIntl__default = /*#__
* LinkMenu. All functions take the site as the first parameter, and optionally
* the index of the site.
*/
const LinkMenuOptions = {
Separator: () => ({ type: "separator" }),
RemoveBookmark: site => ({
id: "menu_action_remove_bookmark",
icon: "bookmark-added",
- action: Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].DELETE_BOOKMARK_BY_ID,
+ action: Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].DELETE_BOOKMARK_BY_ID,
data: site.bookmarkGuid
}),
userEvent: "BOOKMARK_DELETE"
}),
AddBookmark: site => ({
id: "menu_action_bookmark",
icon: "bookmark-hollow",
- action: Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].BOOKMARK_URL,
+ action: Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].BOOKMARK_URL,
data: { url: site.url, title: site.title, type: site.type }
}),
userEvent: "BOOKMARK_ADD"
}),
OpenInNewWindow: site => ({
id: "menu_action_open_new_window",
icon: "new-window",
- action: Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].OPEN_NEW_WINDOW,
+ action: Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].OPEN_NEW_WINDOW,
data: { url: site.url, referrer: site.referrer }
}),
userEvent: "OPEN_NEW_WINDOW"
}),
OpenInPrivateWindow: site => ({
id: "menu_action_open_private_window",
icon: "new-window-private",
- action: Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].OPEN_PRIVATE_WINDOW,
+ action: Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].OPEN_PRIVATE_WINDOW,
data: { url: site.url, referrer: site.referrer }
}),
userEvent: "OPEN_PRIVATE_WINDOW"
}),
BlockUrl: (site, index, eventSource) => ({
id: "menu_action_dismiss",
icon: "dismiss",
- action: Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].BLOCK_URL,
+ action: Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].BLOCK_URL,
data: site.url
}),
- impression: Actions["actionCreators"].ImpressionStats({
+ impression: Actions["a" /* actionCreators */].ImpressionStats({
source: eventSource,
block: 0,
tiles: [{ id: site.guid, pos: index }]
}),
userEvent: "BLOCK"
}),
DeleteUrl: site => ({
id: "menu_action_delete",
icon: "delete",
action: {
- type: Actions["actionTypes"].DIALOG_OPEN,
+ type: Actions["b" /* actionTypes */].DIALOG_OPEN,
data: {
- onConfirm: [Actions["actionCreators"].SendToMain({ type: Actions["actionTypes"].DELETE_HISTORY_URL, data: { url: site.url, forceBlock: site.bookmarkGuid } }), Actions["actionCreators"].UserEvent({ event: "DELETE" })],
+ onConfirm: [Actions["a" /* actionCreators */].SendToMain({ type: Actions["b" /* actionTypes */].DELETE_HISTORY_URL, data: { url: site.url, forceBlock: site.bookmarkGuid } }), Actions["a" /* actionCreators */].UserEvent({ event: "DELETE" })],
body_string_id: ["confirm_history_delete_p1", "confirm_history_delete_notice_p2"],
confirm_button_string_id: "menu_action_delete",
cancel_button_string_id: "topsites_form_cancel_button",
icon: "modal-delete"
}
},
userEvent: "DIALOG_OPEN"
}),
PinTopSite: (site, index) => ({
id: "menu_action_pin",
icon: "pin",
- action: Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].TOP_SITES_PIN,
+ action: Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].TOP_SITES_PIN,
data: { site: { url: site.url }, index }
}),
userEvent: "PIN"
}),
UnpinTopSite: site => ({
id: "menu_action_unpin",
icon: "unpin",
- action: Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].TOP_SITES_UNPIN,
+ action: Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].TOP_SITES_UNPIN,
data: { site: { url: site.url } }
}),
userEvent: "UNPIN"
}),
SaveToPocket: (site, index, eventSource) => ({
id: "menu_action_save_to_pocket",
icon: "pocket",
- action: Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].SAVE_TO_POCKET,
+ action: Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].SAVE_TO_POCKET,
data: { site: { url: site.url, title: site.title } }
}),
- impression: Actions["actionCreators"].ImpressionStats({
+ impression: Actions["a" /* actionCreators */].ImpressionStats({
source: eventSource,
pocket: 0,
tiles: [{ id: site.guid, pos: index }]
}),
userEvent: "SAVE_TO_POCKET"
}),
EditTopSite: (site, index) => ({
id: "edit_topsites_button_text",
icon: "edit",
action: {
- type: Actions["actionTypes"].TOP_SITES_EDIT,
+ type: Actions["b" /* actionTypes */].TOP_SITES_EDIT,
data: { index }
}
}),
CheckBookmark: site => site.bookmarkGuid ? LinkMenuOptions.RemoveBookmark(site) : LinkMenuOptions.AddBookmark(site),
CheckPinTopSite: (site, index) => site.isPinned ? LinkMenuOptions.UnpinTopSite(site) : LinkMenuOptions.PinTopSite(site, index)
};
// CONCATENATED MODULE: ./system-addon/content-src/components/LinkMenu/LinkMenu.jsx
@@ -907,17 +969,17 @@ class LinkMenu__LinkMenu extends externa
const options = propOptions.map(o => LinkMenuOptions[o](site, index, source)).map(option => {
const { action, impression, id, type, userEvent } = option;
if (!type && id) {
option.label = props.intl.formatMessage(option);
option.onClick = () => {
props.dispatch(action);
if (userEvent) {
- props.dispatch(Actions["actionCreators"].UserEvent({
+ props.dispatch(Actions["a" /* actionCreators */].UserEvent({
event: userEvent,
source,
action_position: index
}));
}
if (impression && props.shouldSendImpressionStats) {
props.dispatch(impression);
}
@@ -948,17 +1010,16 @@ const LinkMenu = Object(external__ReactI
/***/ }),
/* 7 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(global) {/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__ = __webpack_require__(0);
-/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_react_intl__ = __webpack_require__(2);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_react_intl___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_react_intl__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_react__ = __webpack_require__(1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_react___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_react__);
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
@@ -1002,18 +1063,18 @@ class Info extends __WEBPACK_IMPORTED_MO
}
onInfoLeave(event) {
// We currently have an active (true) info state, so keep it true only if we
// have a related event target that is contained "within" the current target
// (section-info-option) as itself or a descendant. Set to false otherwise.
this._setInfoState(event && event.relatedTarget && (event.relatedTarget === event.currentTarget || event.relatedTarget.compareDocumentPosition(event.currentTarget) & Node.DOCUMENT_POSITION_CONTAINS));
}
onManageClick() {
- this.props.dispatch({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SETTINGS_OPEN });
- this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].UserEvent({ event: "OPEN_NEWTAB_PREFS" }));
+ this.props.dispatch({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].SETTINGS_OPEN });
+ this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["a" /* actionCreators */].UserEvent({ event: "OPEN_NEWTAB_PREFS" }));
}
render() {
const { infoOption, intl } = this.props;
const infoOptionIconA11yAttrs = {
"aria-haspopup": "true",
"aria-controls": "info-option",
"aria-expanded": this.state.infoActive ? "true" : "false",
"role": "note",
@@ -1070,18 +1131,18 @@ const InfoIntl = Object(__WEBPACK_IMPORT
class Disclaimer extends __WEBPACK_IMPORTED_MODULE_2_react___default.a.PureComponent {
constructor(props) {
super(props);
this.onAcknowledge = this.onAcknowledge.bind(this);
}
onAcknowledge() {
- this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SetPref(this.props.disclaimerPref, false));
- this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].UserEvent({ event: "SECTION_DISCLAIMER_ACKNOWLEDGED", source: this.props.eventSource }));
+ this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["a" /* actionCreators */].SetPref(this.props.disclaimerPref, false));
+ this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["a" /* actionCreators */].UserEvent({ event: "SECTION_DISCLAIMER_ACKNOWLEDGED", source: this.props.eventSource }));
}
render() {
const disclaimer = this.props.disclaimer;
return __WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
"div",
{ className: "section-disclaimer" },
__WEBPACK_IMPORTED_MODULE_2_react___default.a.createElement(
@@ -1165,17 +1226,17 @@ class _CollapsibleSection extends __WEBP
this._setInfoState(event && event.relatedTarget && (event.relatedTarget === event.currentTarget || event.relatedTarget.compareDocumentPosition(event.currentTarget) & Node.DOCUMENT_POSITION_CONTAINS));
}
onHeaderClick() {
// Get the current height of the body so max-height transitions can work
this.setState({
isAnimating: true,
maxHeight: `${this.sectionBody.scrollHeight}px`
});
- this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SetPref(this.props.prefName, !getCollapsed(this.props)));
+ this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["a" /* actionCreators */].SetPref(this.props.prefName, !getCollapsed(this.props)));
}
onTransitionEnd(event) {
// Only update the animating state for our own transition (not a child's)
if (event.target === event.currentTarget) {
this.setState({ isAnimating: false });
}
}
renderIcon() {
@@ -1242,34 +1303,32 @@ const CollapsibleSection = Object(__WEBP
/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(4)))
/***/ }),
/* 8 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__ = __webpack_require__(0);
-/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm__ = __webpack_require__(9);
-/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_react__ = __webpack_require__(1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_react___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_react__);
// Currently record only a fixed set of sections. This will prevent data
// from custom sections from showing up or from topstories.
const RECORDED_SECTIONS = ["highlights", "topsites"];
class ComponentPerfTimer extends __WEBPACK_IMPORTED_MODULE_2_react___default.a.Component {
constructor(props) {
super(props);
// Just for test dependency injection:
- this.perfSvc = this.props.perfSvc || __WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm__["perfService"];
+ this.perfSvc = this.props.perfSvc || __WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm__["a" /* perfService */];
this._sendBadStateEvent = this._sendBadStateEvent.bind(this);
this._sendPaintedEvent = this._sendPaintedEvent.bind(this);
this._reportMissingData = false;
this._timestampHandled = false;
this._recordedFirstRender = false;
}
@@ -1365,18 +1424,18 @@ class ComponentPerfTimer extends __WEBPA
// highlights_data_ready_ts, topsites_data_ready_ts.
const dataReadyKey = `${this.props.id}_data_ready_ts`;
this.perfSvc.mark(dataReadyKey);
try {
const firstRenderKey = `${this.props.id}_first_render_ts`;
// value has to be Int32.
const value = parseInt(this.perfSvc.getMostRecentAbsMarkStartByName(dataReadyKey) - this.perfSvc.getMostRecentAbsMarkStartByName(firstRenderKey), 10);
- this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({
- type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SAVE_SESSION_PERF_DATA,
+ this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["a" /* actionCreators */].SendToMain({
+ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].SAVE_SESSION_PERF_DATA,
// highlights_data_late_by_ms, topsites_data_late_by_ms.
data: { [`${this.props.id}_data_late_by_ms`]: value }
}));
} catch (ex) {
// If this failed, it's likely because the `privacy.resistFingerprinting`
// pref is true.
}
}
@@ -1390,18 +1449,18 @@ class ComponentPerfTimer extends __WEBPA
// topsites_first_painted_ts.
const key = `${this.props.id}_first_painted_ts`;
this.perfSvc.mark(key);
try {
const data = {};
data[key] = this.perfSvc.getMostRecentAbsMarkStartByName(key);
- this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({
- type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SAVE_SESSION_PERF_DATA,
+ this.props.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["a" /* actionCreators */].SendToMain({
+ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].SAVE_SESSION_PERF_DATA,
data
}));
} catch (ex) {
// If this failed, it's likely because the `privacy.resistFingerprinting`
// pref is true. We should at least not blow up, and should continue
// to set this._timestampHandled to avoid going through this again.
}
}
@@ -1414,19 +1473,21 @@ class ComponentPerfTimer extends __WEBPA
return this.props.children;
}
}
/* harmony export (immutable) */ __webpack_exports__["a"] = ComponentPerfTimer;
/***/ }),
/* 9 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
+/* unused harmony export _PerfService */
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return perfService; });
/* globals Services */
/* istanbul ignore if */
// Note: normally we would just feature detect Components.utils here, but
// unfortunately that throws an ugly warning in content if we do.
if (typeof Window === "undefined" && typeof Components !== "undefined" && Components.utils) {
@@ -1448,25 +1509,26 @@ if (typeof Services !== "undefined") {
// This is a dummy object so this file doesn't crash in the node prerendering
// task.
usablePerfObj = {
now() {},
mark() {}
};
}
-var _PerfService = function _PerfService(options) {
+function _PerfService(options) {
// For testing, so that we can use a fake Window.performance object with
// known state.
if (options && options.performanceObj) {
this._perf = options.performanceObj;
} else {
this._perf = usablePerfObj;
}
-};
+}
+
_PerfService.prototype = {
/**
* Calls the underlying mark() method on the appropriate Window.performance
* object to add a mark with the given name to the appropriate performance
* timeline.
*
* @param {String} name the name to give the current mark
@@ -1544,60 +1606,54 @@ var _PerfService = function _PerfService
}
let mostRecentEntry = entries[entries.length - 1];
return this._perf.timeOrigin + mostRecentEntry.startTime;
}
};
var perfService = new _PerfService();
-module.exports = {
- _PerfService,
- perfService
-};
/***/ }),
/* 10 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* WEBPACK VAR INJECTION */(function(global) {/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__ = __webpack_require__(0);
-/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_content_src_lib_snippets__ = __webpack_require__(11);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_content_src_components_Base_Base__ = __webpack_require__(12);
-/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3_content_src_lib_detect_user_session_start__ = __webpack_require__(19);
-/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4_content_src_lib_init_store__ = __webpack_require__(20);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3_content_src_lib_detect_user_session_start__ = __webpack_require__(17);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4_content_src_lib_init_store__ = __webpack_require__(18);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5_react_redux__ = __webpack_require__(3);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5_react_redux___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_5_react_redux__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_react__ = __webpack_require__(1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_react___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6_react__);
-/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7_react_dom__ = __webpack_require__(22);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7_react_dom__ = __webpack_require__(20);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7_react_dom___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_7_react_dom__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8_common_Reducers_jsm__ = __webpack_require__(5);
-/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8_common_Reducers_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_8_common_Reducers_jsm__);
-
-
-
-
-
-
-
-
-
-
-const store = Object(__WEBPACK_IMPORTED_MODULE_4_content_src_lib_init_store__["a" /* initStore */])(__WEBPACK_IMPORTED_MODULE_8_common_Reducers_jsm__["reducers"], global.gActivityStreamPrerenderedState);
+
+
+
+
+
+
+
+
+
+
+const store = Object(__WEBPACK_IMPORTED_MODULE_4_content_src_lib_init_store__["a" /* initStore */])(__WEBPACK_IMPORTED_MODULE_8_common_Reducers_jsm__["c" /* reducers */], global.gActivityStreamPrerenderedState);
new __WEBPACK_IMPORTED_MODULE_3_content_src_lib_detect_user_session_start__["a" /* DetectUserSessionStart */](store).sendEventOrAddListener();
// If we are starting in a prerendered state, we must wait until the first render
// to request state rehydration (see Base.jsx). If we are NOT in a prerendered state,
// we can request it immedately.
if (!global.gActivityStreamPrerenderedState) {
- store.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].NEW_TAB_STATE_REQUEST }));
+ store.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["a" /* actionCreators */].SendToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].NEW_TAB_STATE_REQUEST }));
}
__WEBPACK_IMPORTED_MODULE_7_react_dom___default.a.render(__WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(
__WEBPACK_IMPORTED_MODULE_5_react_redux__["Provider"],
{ store: store },
__WEBPACK_IMPORTED_MODULE_6_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_2_content_src_components_Base_Base__["a" /* Base */], {
isPrerendered: !!global.gActivityStreamPrerenderedState,
locale: global.document.documentElement.lang,
@@ -1609,17 +1665,16 @@ Object(__WEBPACK_IMPORTED_MODULE_1_conte
/***/ }),
/* 11 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(global) {/* harmony export (immutable) */ __webpack_exports__["a"] = addSnippetsSubscriber;
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__ = __webpack_require__(0);
-/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__);
const DATABASE_NAME = "snippets_db";
const DATABASE_VERSION = 1;
const SNIPPETS_OBJECTSTORE_NAME = "snippets";
const SNIPPETS_UPDATE_INTERVAL_MS = 14400000;
/* unused harmony export SNIPPETS_UPDATE_INTERVAL_MS */
// 4 hours.
const SNIPPETS_ENABLED_EVENT = "Snippets:Enabled";
@@ -1670,27 +1725,27 @@ class SnippetsMap extends Map {
*/
async blockSnippetById(id) {
if (!id) {
return;
}
let blockList = this.blockList;
if (!blockList.includes(id)) {
blockList.push(id);
- this._dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SNIPPETS_BLOCKLIST_UPDATED, data: blockList }));
+ this._dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["a" /* actionCreators */].SendToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].SNIPPETS_BLOCKLIST_UPDATED, data: blockList }));
await this.set("blockList", blockList);
}
}
disableOnboarding() {
- this._dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].DISABLE_ONBOARDING }));
+ this._dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["a" /* actionCreators */].SendToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].DISABLE_ONBOARDING }));
}
showFirefoxAccounts() {
- this._dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SHOW_FIREFOX_ACCOUNTS }));
+ this._dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["a" /* actionCreators */].SendToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].SHOW_FIREFOX_ACCOUNTS }));
}
/**
* connect - Attaches an indexedDB back-end to the Map so that any set values
* are also cached in a store. It also restores any existing values
* that are already stored in the indexedDB store.
*
* @return {type} description
@@ -1876,17 +1931,17 @@ class SnippetsProvider {
for (const scriptEl of snippetsEl.getElementsByTagName("script")) {
const relocatedScript = document.createElement("script");
relocatedScript.text = scriptEl.text;
scriptEl.parentNode.replaceChild(relocatedScript, scriptEl);
}
}
_onAction(msg) {
- if (msg.data.type === __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SNIPPET_BLOCKED) {
+ if (msg.data.type === __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].SNIPPET_BLOCKED) {
this.snippetsMap.set("blockList", msg.data.data);
document.getElementById("snippets-container").style.display = "none";
}
}
/**
* init - Fetch the snippet payload and show snippets
*
@@ -1988,17 +2043,16 @@ function addSnippetsSubscriber(store) {
/***/ }),
/* 12 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// EXTERNAL MODULE: ./system-addon/common/Actions.jsm
var Actions = __webpack_require__(0);
-var Actions_default = /*#__PURE__*/__webpack_require__.n(Actions);
// EXTERNAL MODULE: external "ReactIntl"
var external__ReactIntl_ = __webpack_require__(2);
var external__ReactIntl__default = /*#__PURE__*/__webpack_require__.n(external__ReactIntl_);
// EXTERNAL MODULE: external "ReactRedux"
var external__ReactRedux_ = __webpack_require__(3);
var external__ReactRedux__default = /*#__PURE__*/__webpack_require__.n(external__ReactRedux_);
@@ -2035,18 +2089,18 @@ var external__React__default = /*#__PURE
class ConfirmDialog__ConfirmDialog extends external__React__default.a.PureComponent {
constructor(props) {
super(props);
this._handleCancelBtn = this._handleCancelBtn.bind(this);
this._handleConfirmBtn = this._handleConfirmBtn.bind(this);
}
_handleCancelBtn() {
- this.props.dispatch({ type: Actions["actionTypes"].DIALOG_CANCEL });
- this.props.dispatch(Actions["actionCreators"].UserEvent({ event: Actions["actionTypes"].DIALOG_CANCEL }));
+ this.props.dispatch({ type: Actions["b" /* actionTypes */].DIALOG_CANCEL });
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({ event: Actions["b" /* actionTypes */].DIALOG_CANCEL }));
}
_handleConfirmBtn() {
this.props.data.onConfirm.forEach(this.props.dispatch);
}
_renderModalMessage() {
const message_body = this.props.data.body_string_id;
@@ -2120,23 +2174,23 @@ const ConfirmDialog = Object(external__R
*/
class ManualMigration__ManualMigration extends external__React__default.a.PureComponent {
constructor(props) {
super(props);
this.onLaunchTour = this.onLaunchTour.bind(this);
this.onCancelTour = this.onCancelTour.bind(this);
}
onLaunchTour() {
- this.props.dispatch(Actions["actionCreators"].SendToMain({ type: Actions["actionTypes"].MIGRATION_START }));
- this.props.dispatch(Actions["actionCreators"].UserEvent({ event: Actions["actionTypes"].MIGRATION_START }));
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({ type: Actions["b" /* actionTypes */].MIGRATION_START }));
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({ event: Actions["b" /* actionTypes */].MIGRATION_START }));
}
onCancelTour() {
- this.props.dispatch(Actions["actionCreators"].SendToMain({ type: Actions["actionTypes"].MIGRATION_CANCEL }));
- this.props.dispatch(Actions["actionCreators"].UserEvent({ event: Actions["actionTypes"].MIGRATION_CANCEL }));
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({ type: Actions["b" /* actionTypes */].MIGRATION_CANCEL }));
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({ event: Actions["b" /* actionTypes */].MIGRATION_CANCEL }));
}
render() {
return external__React__default.a.createElement(
"div",
{ className: "manual-migration-container" },
external__React__default.a.createElement(
"p",
@@ -2158,19 +2212,18 @@ class ManualMigration__ManualMigration e
external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "manual_migration_import_button" })
)
)
);
}
}
const ManualMigration = Object(external__ReactRedux_["connect"])()(ManualMigration__ManualMigration);
-// EXTERNAL MODULE: ./system-addon/common/Reducers.jsm
+// EXTERNAL MODULE: ./system-addon/common/Reducers.jsm + 1 modules
var Reducers = __webpack_require__(5);
-var Reducers_default = /*#__PURE__*/__webpack_require__.n(Reducers);
// CONCATENATED MODULE: ./system-addon/content-src/components/PreferencesPane/PreferencesPane.jsx
@@ -2229,33 +2282,33 @@ class PreferencesPane__PreferencesPane e
this.togglePane();
}
}
handlePrefChange(event) {
const target = event.target;
const { name, checked } = target;
let value = checked;
if (name === "topSitesCount") {
- value = checked ? Reducers["TOP_SITES_SHOWMORE_LENGTH"] : Reducers["TOP_SITES_DEFAULT_LENGTH"];
+ value = checked ? Reducers["b" /* TOP_SITES_SHOWMORE_LENGTH */] : Reducers["a" /* TOP_SITES_DEFAULT_LENGTH */];
}
- this.props.dispatch(Actions["actionCreators"].SetPref(name, value));
+ this.props.dispatch(Actions["a" /* actionCreators */].SetPref(name, value));
}
handleSectionChange(event) {
const target = event.target;
const id = target.name;
- const type = target.checked ? Actions["actionTypes"].SECTION_ENABLE : Actions["actionTypes"].SECTION_DISABLE;
- this.props.dispatch(Actions["actionCreators"].SendToMain({ type, data: id }));
+ const type = target.checked ? Actions["b" /* actionTypes */].SECTION_ENABLE : Actions["b" /* actionTypes */].SECTION_DISABLE;
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({ type, data: id }));
}
togglePane() {
if (this.isSidebarOpen()) {
- this.props.dispatch({ type: Actions["actionTypes"].SETTINGS_CLOSE });
- this.props.dispatch(Actions["actionCreators"].UserEvent({ event: "CLOSE_NEWTAB_PREFS" }));
+ this.props.dispatch({ type: Actions["b" /* actionTypes */].SETTINGS_CLOSE });
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({ event: "CLOSE_NEWTAB_PREFS" }));
} else {
- this.props.dispatch({ type: Actions["actionTypes"].SETTINGS_OPEN });
- this.props.dispatch(Actions["actionCreators"].UserEvent({ event: "OPEN_NEWTAB_PREFS" }));
+ this.props.dispatch({ type: Actions["b" /* actionTypes */].SETTINGS_OPEN });
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({ event: "OPEN_NEWTAB_PREFS" }));
}
}
onWrapperMount(wrapper) {
this.wrapper = wrapper;
}
render() {
const props = this.props;
const prefs = props.Prefs.values;
@@ -2307,17 +2360,17 @@ class PreferencesPane__PreferencesPane e
value: prefs.showTopSites,
onChange: this.handlePrefChange,
titleString: { id: "settings_pane_topsites_header" },
descString: { id: "settings_pane_topsites_body" } },
external__React__default.a.createElement(PreferencesInput, {
className: "showMoreTopSites",
prefName: "topSitesCount",
disabled: !prefs.showTopSites,
- value: prefs.topSitesCount !== Reducers["TOP_SITES_DEFAULT_LENGTH"],
+ value: prefs.topSitesCount !== Reducers["a" /* TOP_SITES_DEFAULT_LENGTH */],
onChange: this.handlePrefChange,
titleString: { id: "settings_pane_topsites_options_showmore" },
labelClassName: "icon icon-topsites" })
),
sections.filter(section => !section.shouldHidePref).map(({ id, title, enabled, pref }) => external__React__default.a.createElement(
PreferencesInput,
{
key: id,
@@ -2357,22 +2410,102 @@ class PreferencesPane__PreferencesPane e
}
}
const PreferencesPane = Object(external__ReactRedux_["connect"])(state => ({
Prefs: state.Prefs,
PreferencesPane: state.PreferencesPane,
Sections: state.Sections
}))(Object(external__ReactIntl_["injectIntl"])(PreferencesPane__PreferencesPane));
-// EXTERNAL MODULE: ./system-addon/common/PrerenderData.jsm
-var PrerenderData = __webpack_require__(14);
-var PrerenderData_default = /*#__PURE__*/__webpack_require__.n(PrerenderData);
-
+// CONCATENATED MODULE: ./system-addon/common/PrerenderData.jsm
+class _PrerenderData {
+ constructor(options) {
+ this.initialPrefs = options.initialPrefs;
+ this.initialSections = options.initialSections;
+ this._setValidation(options.validation);
+ }
+
+ get validation() {
+ return this._validation;
+ }
+
+ set validation(value) {
+ this._setValidation(value);
+ }
+
+ get invalidatingPrefs() {
+ return this._invalidatingPrefs;
+ }
+
+ // This is needed so we can use it in the constructor
+ _setValidation(value = []) {
+ this._validation = value;
+ this._invalidatingPrefs = value.reduce((result, next) => {
+ if (typeof next === "string") {
+ result.push(next);
+ return result;
+ } else if (next && next.oneOf) {
+ return result.concat(next.oneOf);
+ }
+ throw new Error("Your validation configuration is not properly configured");
+ }, []);
+ }
+
+ arePrefsValid(getPref) {
+ for (const prefs of this.validation) {
+ // {oneOf: ["foo", "bar"]}
+ if (prefs && prefs.oneOf && !prefs.oneOf.some(name => getPref(name) === this.initialPrefs[name])) {
+ return false;
+
+ // "foo"
+ } else if (getPref(prefs) !== this.initialPrefs[prefs]) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+var PrerenderData = new _PrerenderData({
+ initialPrefs: {
+ "migrationExpired": true,
+ "showTopSites": true,
+ "showSearch": true,
+ "topSitesCount": 12,
+ "collapseTopSites": false,
+ "section.highlights.collapsed": false,
+ "section.topstories.collapsed": false,
+ "feeds.section.topstories": true,
+ "feeds.section.highlights": true
+ },
+ // Prefs listed as invalidating will prevent the prerendered version
+ // of AS from being used if their value is something other than what is listed
+ // here. This is required because some preferences cause the page layout to be
+ // too different for the prerendered version to be used. Unfortunately, this
+ // will result in users who have modified some of their preferences not being
+ // able to get the benefits of prerendering.
+ validation: ["showTopSites", "showSearch", "topSitesCount", "collapseTopSites", "section.highlights.collapsed", "section.topstories.collapsed",
+ // This means if either of these are set to their default values,
+ // prerendering can be used.
+ { oneOf: ["feeds.section.topstories", "feeds.section.highlights"] }],
+ initialSections: [{
+ enabled: true,
+ icon: "pocket",
+ id: "topstories",
+ order: 1,
+ title: { id: "header_recommended_by", values: { provider: "Pocket" } }
+ }, {
+ enabled: true,
+ id: "highlights",
+ icon: "highlights",
+ order: 2,
+ title: { id: "header_highlights" }
+ }]
+});
// EXTERNAL MODULE: ./system-addon/content-src/lib/constants.js
-var constants = __webpack_require__(15);
+var constants = __webpack_require__(13);
// CONCATENATED MODULE: ./system-addon/content-src/components/Search/Search.jsx
/* globals ContentSearchUIController */
@@ -2384,17 +2517,17 @@ class Search__Search extends external__R
super(props);
this.onClick = this.onClick.bind(this);
this.onInputMount = this.onInputMount.bind(this);
}
handleEvent(event) {
// Also track search events with our own telemetry
if (event.detail.type === "Search") {
- this.props.dispatch(Actions["actionCreators"].UserEvent({ event: "SEARCH" }));
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({ event: "SEARCH" }));
}
}
onClick(event) {
window.gContentSearchController.search(event);
}
componentWillUnmount() {
delete window.gContentSearchController;
}
@@ -2464,17 +2597,17 @@ class Search__Search extends external__R
)
)
);
}
}
const Search = Object(external__ReactRedux_["connect"])()(Object(external__ReactIntl_["injectIntl"])(Search__Search));
// EXTERNAL MODULE: ./system-addon/content-src/components/Sections/Sections.jsx
-var Sections = __webpack_require__(16);
+var Sections = __webpack_require__(14);
// CONCATENATED MODULE: ./system-addon/content-src/components/TopSites/TopSitesConstants.js
const TOP_SITES_SOURCE = "TOP_SITES";
const TOP_SITES_CONTEXT_MENU_OPTIONS = ["CheckPinTopSite", "EditTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl", "DeleteUrl"];
// minimum size necessary to show a rich icon instead of a screenshot
const MIN_RICH_FAVICON_SIZE = 96;
// minimum size necessary to show any icon in the top left corner with a screenshot
const MIN_CORNER_FAVICON_SIZE = 16;
@@ -2626,17 +2759,17 @@ class TopSite_TopSite extends external__
}
toggleContextMenu(event, index) {
this.setState({
activeTile: index,
showContextMenu: true
});
}
userEvent(event) {
- this.props.dispatch(Actions["actionCreators"].UserEvent({
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({
event,
source: TOP_SITES_SOURCE,
action_position: this.props.index
}));
}
onLinkClick(ev) {
if (this.props.onEdit) {
// Ignore clicks if we are in the edit modal.
@@ -2650,38 +2783,38 @@ class TopSite_TopSite extends external__
this.toggleContextMenu(event, this.props.index);
}
onMenuUpdate(showContextMenu) {
this.setState({ showContextMenu });
}
onDismissButtonClick() {
const { link } = this.props;
if (link.isPinned) {
- this.props.dispatch(Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].TOP_SITES_UNPIN,
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].TOP_SITES_UNPIN,
data: { site: { url: link.url } }
}));
}
- this.props.dispatch(Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].BLOCK_URL,
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].BLOCK_URL,
data: link.url
}));
this.userEvent("BLOCK");
}
onPinButtonClick() {
const { link, index } = this.props;
if (link.isPinned) {
- this.props.dispatch(Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].TOP_SITES_UNPIN,
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].TOP_SITES_UNPIN,
data: { site: { url: link.url } }
}));
this.userEvent("UNPIN");
} else {
- this.props.dispatch(Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].TOP_SITES_PIN,
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].TOP_SITES_PIN,
data: { site: { url: link.url }, index }
}));
this.userEvent("PIN");
}
}
onEditButtonClick() {
this.props.onEdit(this.props.index);
}
@@ -2749,17 +2882,17 @@ TopSite_TopSite.defaultProps = {
class TopSite_TopSitePlaceholder extends external__React__default.a.PureComponent {
constructor(props) {
super(props);
this.onEditButtonClick = this.onEditButtonClick.bind(this);
}
onEditButtonClick() {
this.props.dispatch({
- type: Actions["actionTypes"].TOP_SITES_EDIT,
+ type: Actions["b" /* actionTypes */].TOP_SITES_EDIT,
data: { index: this.props.index }
});
}
render() {
return external__React__default.a.createElement(
TopSite_TopSiteLink,
_extends({ className: "placeholder", isDraggable: false }, this.props),
external__React__default.a.createElement("button", { className: "context-menu-button edit-button icon",
@@ -2786,17 +2919,17 @@ class TopSite__TopSiteList extends exter
const newTopSites = nextProps.TopSites && nextProps.TopSites.rows;
if (prevTopSites && prevTopSites[this.state.draggedIndex] && prevTopSites[this.state.draggedIndex].url === this.state.draggedSite.url && (!newTopSites[this.state.draggedIndex] || newTopSites[this.state.draggedIndex].url !== this.state.draggedSite.url)) {
// We got the new order from the redux store via props. We can clear state now.
this.setState(this.DEFAULT_STATE);
}
}
}
userEvent(event, index) {
- this.props.dispatch(Actions["actionCreators"].UserEvent({
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({
event,
source: TOP_SITES_SOURCE,
action_position: index
}));
}
onDragEvent(event, index, link, title) {
switch (event.type) {
case "dragstart":
@@ -2814,18 +2947,18 @@ class TopSite__TopSiteList extends exter
if (index === this.state.draggedIndex) {
this.setState({ topSitesPreview: null });
} else {
this.setState({ topSitesPreview: this._makeTopSitesPreview(index) });
}
break;
case "drop":
if (index !== this.state.draggedIndex) {
- this.props.dispatch(Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].TOP_SITES_INSERT,
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].TOP_SITES_INSERT,
data: { site: { url: this.state.draggedSite.url, label: this.state.draggedTitle }, index }
}));
this.userEvent("DROP", index);
}
break;
}
}
_getTopSites() {
@@ -2952,39 +3085,39 @@ class TopSiteForm_TopSiteForm extends ex
}
onAddButtonClick(ev) {
ev.preventDefault();
if (this.validateForm()) {
let site = { url: this.cleanUrl() };
if (this.state.label !== "") {
site.label = this.state.label;
}
- this.props.dispatch(Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].TOP_SITES_INSERT,
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].TOP_SITES_INSERT,
data: { site }
}));
- this.props.dispatch(Actions["actionCreators"].UserEvent({
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({
source: TOP_SITES_SOURCE,
event: "TOP_SITES_ADD"
}));
this.props.onClose();
}
}
onSaveButtonClick(ev) {
ev.preventDefault();
if (this.validateForm()) {
let site = { url: this.cleanUrl() };
if (this.state.label !== "") {
site.label = this.state.label;
}
- this.props.dispatch(Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].TOP_SITES_PIN,
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].TOP_SITES_PIN,
data: { site, index: this.props.index }
}));
- this.props.dispatch(Actions["actionCreators"].UserEvent({
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({
source: TOP_SITES_SOURCE,
event: "TOP_SITES_EDIT",
action_position: this.props.index
}));
this.props.onClose();
}
}
cleanUrl() {
@@ -3113,54 +3246,54 @@ class TopSitesEdit__TopSitesEdit extends
this.onModalOverlayClick = this.onModalOverlayClick.bind(this);
this.onAddButtonClick = this.onAddButtonClick.bind(this);
this.onFormClose = this.onFormClose.bind(this);
this.onEdit = this.onEdit.bind(this);
}
onEditButtonClick() {
this.setState({ showEditModal: !this.state.showEditModal });
const event = this.state.showEditModal ? "TOP_SITES_EDIT_OPEN" : "TOP_SITES_EDIT_CLOSE";
- this.props.dispatch(Actions["actionCreators"].UserEvent({
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({
source: TOP_SITES_SOURCE,
event
}));
}
onModalOverlayClick() {
this.setState({ showEditModal: false, showAddForm: false, showEditForm: false });
- this.props.dispatch(Actions["actionCreators"].UserEvent({
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({
source: TOP_SITES_SOURCE,
event: "TOP_SITES_EDIT_CLOSE"
}));
- this.props.dispatch({ type: Actions["actionTypes"].TOP_SITES_CANCEL_EDIT });
+ this.props.dispatch({ type: Actions["b" /* actionTypes */].TOP_SITES_CANCEL_EDIT });
}
onShowMoreLessClick() {
- const prefIsSetToDefault = this.props.TopSitesCount === Reducers["TOP_SITES_DEFAULT_LENGTH"];
- this.props.dispatch(Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].SET_PREF,
- data: { name: "topSitesCount", value: prefIsSetToDefault ? Reducers["TOP_SITES_SHOWMORE_LENGTH"] : Reducers["TOP_SITES_DEFAULT_LENGTH"] }
+ const prefIsSetToDefault = this.props.TopSitesCount === Reducers["a" /* TOP_SITES_DEFAULT_LENGTH */];
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].SET_PREF,
+ data: { name: "topSitesCount", value: prefIsSetToDefault ? Reducers["b" /* TOP_SITES_SHOWMORE_LENGTH */] : Reducers["a" /* TOP_SITES_DEFAULT_LENGTH */] }
}));
- this.props.dispatch(Actions["actionCreators"].UserEvent({
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({
source: TOP_SITES_SOURCE,
event: prefIsSetToDefault ? "TOP_SITES_EDIT_SHOW_MORE" : "TOP_SITES_EDIT_SHOW_LESS"
}));
}
onAddButtonClick() {
this.setState({ showAddForm: true });
- this.props.dispatch(Actions["actionCreators"].UserEvent({
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({
source: TOP_SITES_SOURCE,
event: "TOP_SITES_ADD_FORM_OPEN"
}));
}
onFormClose() {
this.setState({ showAddForm: false, showEditForm: false });
- this.props.dispatch({ type: Actions["actionTypes"].TOP_SITES_CANCEL_EDIT });
+ this.props.dispatch({ type: Actions["b" /* actionTypes */].TOP_SITES_CANCEL_EDIT });
}
onEdit(index) {
this.setState({ showEditForm: true, editIndex: index });
- this.props.dispatch(Actions["actionCreators"].UserEvent({
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({
source: TOP_SITES_SOURCE,
event: "TOP_SITES_EDIT_FORM_OPEN"
}));
}
render() {
const { editForm } = this.props.TopSites;
const showEditForm = editForm && editForm.visible || this.state.showEditModal && this.state.showEditForm;
let editIndex = this.state.editIndex;
@@ -3210,18 +3343,18 @@ class TopSitesEdit__TopSitesEdit extends
{ className: "actions" },
external__React__default.a.createElement(
"button",
{ className: "add", onClick: this.onAddButtonClick },
external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "edit_topsites_add_button" })
),
external__React__default.a.createElement(
"button",
- { className: `icon icon-topsites show-${this.props.TopSitesCount === Reducers["TOP_SITES_DEFAULT_LENGTH"] ? "more" : "less"}`, onClick: this.onShowMoreLessClick },
- external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: `edit_topsites_show${this.props.TopSitesCount === Reducers["TOP_SITES_DEFAULT_LENGTH"] ? "more" : "less"}_button` })
+ { className: `icon icon-topsites show-${this.props.TopSitesCount === Reducers["a" /* TOP_SITES_DEFAULT_LENGTH */] ? "more" : "less"}`, onClick: this.onShowMoreLessClick },
+ external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: `edit_topsites_show${this.props.TopSitesCount === Reducers["a" /* TOP_SITES_DEFAULT_LENGTH */] ? "more" : "less"}_button` })
),
external__React__default.a.createElement(
"button",
{ className: "done", onClick: this.onEditButtonClick },
external__React__default.a.createElement(external__ReactIntl_["FormattedMessage"], { id: "edit_topsites_done_button" })
)
)
)
@@ -3304,18 +3437,18 @@ class TopSites__TopSites extends externa
/**
* Dispatch session statistics about the quality of TopSites icons and pinned count.
*/
_dispatchTopSitesStats() {
const topSites = this._getTopSites();
const topSitesIconsStats = countTopSitesIconsTypes(topSites);
const topSitesPinned = topSites.filter(site => !!site.isPinned).length;
// Dispatch telemetry event with the count of TopSites images types.
- this.props.dispatch(Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].SAVE_SESSION_PERF_DATA,
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].SAVE_SESSION_PERF_DATA,
data: { topsites_icon_stats: topSitesIconsStats, topsites_pinned: topSitesPinned }
}));
}
/**
* Return the TopSites to display based on prefs.
*/
_getTopSites() {
@@ -3381,42 +3514,42 @@ class Base__Base extends external__React
addLocaleDataForReactIntl(locale);
}
componentDidMount() {
// Request state AFTER the first render to ensure we don't cause the
// prerendered DOM to be unmounted. Otherwise, NEW_TAB_STATE_REQUEST is
// dispatched right after the store is ready.
if (this.props.isPrerendered) {
- this.props.dispatch(Actions["actionCreators"].SendToMain({ type: Actions["actionTypes"].NEW_TAB_STATE_REQUEST }));
- this.props.dispatch(Actions["actionCreators"].SendToMain({ type: Actions["actionTypes"].PAGE_PRERENDERED }));
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({ type: Actions["b" /* actionTypes */].NEW_TAB_STATE_REQUEST }));
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({ type: Actions["b" /* actionTypes */].PAGE_PRERENDERED }));
}
}
componentWillUpdate({ App }) {
this.sendNewTabRehydrated(App);
}
// The NEW_TAB_REHYDRATED event is used to inform feeds that their
// data has been consumed e.g. for counting the number of tabs that
// have rendered that data.
sendNewTabRehydrated(App) {
if (App && App.initialized && !this.renderNotified) {
- this.props.dispatch(Actions["actionCreators"].SendToMain({ type: Actions["actionTypes"].NEW_TAB_REHYDRATED, data: {} }));
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({ type: Actions["b" /* actionTypes */].NEW_TAB_REHYDRATED, data: {} }));
this.renderNotified = true;
}
}
render() {
const props = this.props;
const { App, locale, strings } = props;
const { initialized } = App;
const prefs = props.Prefs.values;
- const shouldBeFixedToTop = PrerenderData["PrerenderData"].arePrefsValid(name => prefs[name]);
+ const shouldBeFixedToTop = PrerenderData.arePrefsValid(name => prefs[name]);
const outerClassName = `outer-wrapper${shouldBeFixedToTop ? " fixed-to-top" : ""}`;
if (!props.isPrerendered && !initialized) {
return null;
}
return external__React__default.a.createElement(
@@ -3447,174 +3580,40 @@ class Base__Base extends external__React
const Base = Object(external__ReactRedux_["connect"])(state => ({ App: state.App, Prefs: state.Prefs }))(Base__Base);
/* harmony export (immutable) */ __webpack_exports__["a"] = Base;
/***/ }),
/* 13 */
-/***/ (function(module, exports) {
-
-var Dedupe = class Dedupe {
- constructor(createKey) {
- this.createKey = createKey || this.defaultCreateKey;
- }
-
- defaultCreateKey(item) {
- return item;
- }
-
- /**
- * Dedupe any number of grouped elements favoring those from earlier groups.
- *
- * @param {Array} groups Contains an arbitrary number of arrays of elements.
- * @returns {Array} A matching array of each provided group deduped.
- */
- group(...groups) {
- const globalKeys = new Set();
- const result = [];
- for (const values of groups) {
- const valueMap = new Map();
- for (const value of values) {
- const key = this.createKey(value);
- if (!globalKeys.has(key) && !valueMap.has(key)) {
- valueMap.set(key, value);
- }
- }
- result.push(valueMap);
- valueMap.forEach((value, key) => globalKeys.add(key));
- }
- return result.map(m => Array.from(m.values()));
- }
-};
-module.exports = {
- Dedupe
-};
-
-/***/ }),
-/* 14 */
-/***/ (function(module, exports) {
-
-class _PrerenderData {
- constructor(options) {
- this.initialPrefs = options.initialPrefs;
- this.initialSections = options.initialSections;
- this._setValidation(options.validation);
- }
-
- get validation() {
- return this._validation;
- }
-
- set validation(value) {
- this._setValidation(value);
- }
-
- get invalidatingPrefs() {
- return this._invalidatingPrefs;
- }
-
- // This is needed so we can use it in the constructor
- _setValidation(value = []) {
- this._validation = value;
- this._invalidatingPrefs = value.reduce((result, next) => {
- if (typeof next === "string") {
- result.push(next);
- return result;
- } else if (next && next.oneOf) {
- return result.concat(next.oneOf);
- }
- throw new Error("Your validation configuration is not properly configured");
- }, []);
- }
-
- arePrefsValid(getPref) {
- for (const prefs of this.validation) {
- // {oneOf: ["foo", "bar"]}
- if (prefs && prefs.oneOf && !prefs.oneOf.some(name => getPref(name) === this.initialPrefs[name])) {
- return false;
-
- // "foo"
- } else if (getPref(prefs) !== this.initialPrefs[prefs]) {
- return false;
- }
- }
- return true;
- }
-}
-
-var PrerenderData = new _PrerenderData({
- initialPrefs: {
- "migrationExpired": true,
- "showTopSites": true,
- "showSearch": true,
- "topSitesCount": 12,
- "collapseTopSites": false,
- "section.highlights.collapsed": false,
- "section.topstories.collapsed": false,
- "feeds.section.topstories": true,
- "feeds.section.highlights": true
- },
- // Prefs listed as invalidating will prevent the prerendered version
- // of AS from being used if their value is something other than what is listed
- // here. This is required because some preferences cause the page layout to be
- // too different for the prerendered version to be used. Unfortunately, this
- // will result in users who have modified some of their preferences not being
- // able to get the benefits of prerendering.
- validation: ["showTopSites", "showSearch", "topSitesCount", "collapseTopSites", "section.highlights.collapsed", "section.topstories.collapsed",
- // This means if either of these are set to their default values,
- // prerendering can be used.
- { oneOf: ["feeds.section.topstories", "feeds.section.highlights"] }],
- initialSections: [{
- enabled: true,
- icon: "pocket",
- id: "topstories",
- order: 1,
- title: { id: "header_recommended_by", values: { provider: "Pocket" } }
- }, {
- enabled: true,
- id: "highlights",
- icon: "highlights",
- order: 2,
- title: { id: "header_highlights" }
- }]
-});
-module.exports = {
- PrerenderData,
- _PrerenderData
-};
-
-/***/ }),
-/* 15 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(global) {const IS_NEWTAB = global.document && global.document.documentURI === "about:newtab";
/* harmony export (immutable) */ __webpack_exports__["a"] = IS_NEWTAB;
/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(4)))
/***/ }),
-/* 16 */
+/* 14 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
-/* WEBPACK VAR INJECTION */(function(global) {/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_content_src_components_Card_Card__ = __webpack_require__(17);
+/* WEBPACK VAR INJECTION */(function(global) {/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_content_src_components_Card_Card__ = __webpack_require__(15);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_react_intl__ = __webpack_require__(2);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_react_intl___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_react_intl__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_common_Actions_jsm__ = __webpack_require__(0);
-/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_common_Actions_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_common_Actions_jsm__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3_content_src_components_CollapsibleSection_CollapsibleSection__ = __webpack_require__(7);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4_content_src_components_ComponentPerfTimer_ComponentPerfTimer__ = __webpack_require__(8);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5_react_redux__ = __webpack_require__(3);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5_react_redux___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_5_react_redux__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_react__ = __webpack_require__(1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_react___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6_react__);
-/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7_content_src_components_Topics_Topics__ = __webpack_require__(18);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7_content_src_components_Topics_Topics__ = __webpack_require__(16);
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
@@ -3635,17 +3634,17 @@ function getFormattedMessage(message) {
class Section extends __WEBPACK_IMPORTED_MODULE_6_react___default.a.PureComponent {
_dispatchImpressionStats() {
const { props } = this;
const maxCards = 3 * props.maxRows;
const cards = props.rows.slice(0, maxCards);
if (this.needsImpressionStats(cards)) {
- props.dispatch(__WEBPACK_IMPORTED_MODULE_2_common_Actions_jsm__["actionCreators"].ImpressionStats({
+ props.dispatch(__WEBPACK_IMPORTED_MODULE_2_common_Actions_jsm__["a" /* actionCreators */].ImpressionStats({
source: props.eventSource,
tiles: cards.map(link => ({ id: link.guid }))
}));
this.impressionCardGuids = cards.map(link => link.guid);
}
}
// This sends an event when a user sees a set of new content. If content
@@ -3821,24 +3820,23 @@ class _Sections extends __WEBPACK_IMPORT
const Sections = Object(__WEBPACK_IMPORTED_MODULE_5_react_redux__["connect"])(state => ({ Sections: state.Sections, Prefs: state.Prefs }))(_Sections);
/* harmony export (immutable) */ __webpack_exports__["a"] = Sections;
/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(4)))
/***/ }),
-/* 17 */
+/* 15 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// EXTERNAL MODULE: ./system-addon/common/Actions.jsm
var Actions = __webpack_require__(0);
-var Actions_default = /*#__PURE__*/__webpack_require__.n(Actions);
// CONCATENATED MODULE: ./system-addon/content-src/components/Card/types.js
const cardContextTypes = {
history: {
intlID: "type_label_visited",
icon: "historyItem"
},
bookmark: {
@@ -3933,27 +3931,27 @@ class Card_Card extends external__React_
this.setState({
activeCard: this.props.index,
showContextMenu: true
});
}
onLinkClick(event) {
event.preventDefault();
const { altKey, button, ctrlKey, metaKey, shiftKey } = event;
- this.props.dispatch(Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].OPEN_LINK,
+ this.props.dispatch(Actions["a" /* actionCreators */].SendToMain({
+ type: Actions["b" /* actionTypes */].OPEN_LINK,
data: Object.assign(this.props.link, { event: { altKey, button, ctrlKey, metaKey, shiftKey } })
}));
- this.props.dispatch(Actions["actionCreators"].UserEvent({
+ this.props.dispatch(Actions["a" /* actionCreators */].UserEvent({
event: "CLICK",
source: this.props.eventSource,
action_position: this.props.index
}));
if (this.props.shouldSendImpressionStats) {
- this.props.dispatch(Actions["actionCreators"].ImpressionStats({
+ this.props.dispatch(Actions["a" /* actionCreators */].ImpressionStats({
source: this.props.eventSource,
click: 0,
tiles: [{ id: this.props.link.guid, pos: this.props.index }]
}));
}
}
onMenuUpdate(showContextMenu) {
this.setState({ showContextMenu });
@@ -4060,17 +4058,17 @@ class Card_Card extends external__React_
Card_Card.defaultProps = { link: {} };
const PlaceholderCard = () => external__React__default.a.createElement(Card_Card, { placeholder: true });
/* harmony export (immutable) */ __webpack_exports__["b"] = PlaceholderCard;
/***/ }),
-/* 18 */
+/* 16 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_react_intl__ = __webpack_require__(2);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_react_intl___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_react_intl__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_react__ = __webpack_require__(1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_react___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_react__);
@@ -4116,36 +4114,34 @@ class Topics extends __WEBPACK_IMPORTED_
)
);
}
}
/* harmony export (immutable) */ __webpack_exports__["a"] = Topics;
/***/ }),
-/* 19 */
+/* 17 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(global) {/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__ = __webpack_require__(0);
-/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm__ = __webpack_require__(9);
-/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm__);
const VISIBLE = "visible";
const VISIBILITY_CHANGE_EVENT = "visibilitychange";
class DetectUserSessionStart {
constructor(store, options = {}) {
this._store = store;
// Overrides for testing
this.document = options.document || global.document;
- this._perfService = options.perfService || __WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm__["perfService"];
+ this._perfService = options.perfService || __WEBPACK_IMPORTED_MODULE_1_common_PerfService_jsm__["a" /* perfService */];
this._onVisibilityChange = this._onVisibilityChange.bind(this);
}
/**
* sendEventOrAddListener - Notify immediately if the page is already visible,
* or else set up a listener for when visibility changes.
* This is needed for accurate session tracking for telemetry,
* because tabs are pre-loaded.
@@ -4167,18 +4163,18 @@ class DetectUserSessionStart {
* visibility_event_rcvd_ts time in ms from the UNIX epoch.
*/
_sendEvent() {
this._perfService.mark("visibility_event_rcvd_ts");
try {
let visibility_event_rcvd_ts = this._perfService.getMostRecentAbsMarkStartByName("visibility_event_rcvd_ts");
- this._store.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({
- type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SAVE_SESSION_PERF_DATA,
+ this._store.dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["a" /* actionCreators */].SendToMain({
+ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].SAVE_SESSION_PERF_DATA,
data: { visibility_event_rcvd_ts }
}));
} catch (ex) {
// If this failed, it's likely because the `privacy.resistFingerprinting`
// pref is true. We should at least not blow up.
}
}
@@ -4193,40 +4189,39 @@ class DetectUserSessionStart {
}
}
}
/* harmony export (immutable) */ __webpack_exports__["a"] = DetectUserSessionStart;
/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(4)))
/***/ }),
-/* 20 */
+/* 18 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(global) {/* harmony export (immutable) */ __webpack_exports__["a"] = initStore;
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__ = __webpack_require__(0);
-/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__);
-/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_redux__ = __webpack_require__(21);
+/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_redux__ = __webpack_require__(19);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_redux___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_redux__);
/* eslint-env mozilla/frame-script */
const MERGE_STORE_ACTION = "NEW_TAB_INITIAL_STATE";
/* unused harmony export MERGE_STORE_ACTION */
const OUTGOING_MESSAGE_NAME = "ActivityStream:ContentToMain";
/* unused harmony export OUTGOING_MESSAGE_NAME */
const INCOMING_MESSAGE_NAME = "ActivityStream:MainToContent";
/* unused harmony export INCOMING_MESSAGE_NAME */
-const EARLY_QUEUED_ACTIONS = [__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].SAVE_SESSION_PERF_DATA, __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].PAGE_PRERENDERED];
+const EARLY_QUEUED_ACTIONS = [__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].SAVE_SESSION_PERF_DATA, __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].PAGE_PRERENDERED];
/* unused harmony export EARLY_QUEUED_ACTIONS */
/**
* A higher-order function which returns a reducer that, on MERGE_STORE action,
* will return the action.data object merged into the previous state.
*
* For all other actions, it merely calls mainReducer.
@@ -4250,46 +4245,46 @@ function mergeStateReducer(mainReducer)
return mainReducer(prevState, action);
};
}
/**
* messageMiddleware - Middleware that looks for SentToMain type actions, and sends them if necessary
*/
const messageMiddleware = store => next => action => {
- if (__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionUtils"].isSendToMain(action)) {
+ if (__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["c" /* actionUtils */].isSendToMain(action)) {
sendAsyncMessage(OUTGOING_MESSAGE_NAME, action);
}
next(action);
};
const rehydrationMiddleware = store => next => action => {
if (store._didRehydrate) {
return next(action);
}
const isMergeStoreAction = action.type === MERGE_STORE_ACTION;
- const isRehydrationRequest = action.type === __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].NEW_TAB_STATE_REQUEST;
+ const isRehydrationRequest = action.type === __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].NEW_TAB_STATE_REQUEST;
if (isRehydrationRequest) {
store._didRequestInitialState = true;
return next(action);
}
if (isMergeStoreAction) {
store._didRehydrate = true;
return next(action);
}
// If init happened after our request was made, we need to re-request
- if (store._didRequestInitialState && action.type === __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].INIT) {
- return next(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionCreators"].SendToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionTypes"].NEW_TAB_STATE_REQUEST }));
+ if (store._didRequestInitialState && action.type === __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].INIT) {
+ return next(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["a" /* actionCreators */].SendToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].NEW_TAB_STATE_REQUEST }));
}
- if (__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionUtils"].isBroadcastToContent(action) || __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionUtils"].isSendToContent(action)) {
+ if (__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["c" /* actionUtils */].isBroadcastToContent(action) || __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["c" /* actionUtils */].isSendToContent(action) || __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["c" /* actionUtils */].isSendToPreloaded(action)) {
// Note that actions received before didRehydrate will not be dispatched
// because this could negatively affect preloading and the the state
// will be replaced by rehydration anyway.
return null;
}
return next(action);
};
@@ -4301,17 +4296,17 @@ const rehydrationMiddleware = store => n
* the first action from main. This is useful for those actions for main which
* require higher reliability, i.e. the action will not be lost in the case
* that it gets sent before the main is ready to receive it. Conversely, any
* actions allowed early are accepted to be ignorable or re-sendable.
*/
const queueEarlyMessageMiddleware = store => next => action => {
if (store._receivedFromMain) {
next(action);
- } else if (__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["actionUtils"].isFromMain(action)) {
+ } else if (__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["c" /* actionUtils */].isFromMain(action)) {
next(action);
store._receivedFromMain = true;
// Sending out all the early actions as main is ready now
if (store._earlyActionQueue) {
store._earlyActionQueue.forEach(next);
store._earlyActionQueue = [];
}
} else if (EARLY_QUEUED_ACTIONS.includes(action.type)) {
@@ -4349,21 +4344,21 @@ function initStore(reducers, initialStat
});
}
return store;
}
/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(4)))
/***/ }),
-/* 21 */
+/* 19 */
/***/ (function(module, exports) {
module.exports = Redux;
/***/ }),
-/* 22 */
+/* 20 */
/***/ (function(module, exports) {
module.exports = ReactDOM;
/***/ })
/******/ ]);
\ No newline at end of file
--- a/browser/extensions/activity-stream/install.rdf.in
+++ b/browser/extensions/activity-stream/install.rdf.in
@@ -3,17 +3,17 @@
#filter substitution
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>activity-stream@mozilla.org</em:id>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:unpack>false</em:unpack>
- <em:version>2018.01.10.1284-a48d0d39</em:version>
+ <em:version>2018.01.12.1332-b114acab</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/ActivityStream.jsm
+++ b/browser/extensions/activity-stream/lib/ActivityStream.jsm
@@ -287,17 +287,17 @@ this.ActivityStream = class ActivityStre
if (Services.prefs.prefHasUserValue(GEO_PREF)) {
this.geo = Services.prefs.getStringPref(GEO_PREF);
} else if (this.geo !== "") {
// Watch for geo changes and use a dummy value for now
Services.prefs.addObserver(GEO_PREF, this);
this.geo = "";
}
- this.locale = Services.locale.getRequestedLocale();
+ this.locale = Services.locale.getAppLocaleAsLangTag();
// Update the pref config of those with dynamic values
for (const pref of PREFS_CONFIG.keys()) {
const prefConfig = PREFS_CONFIG.get(pref);
if (!prefConfig.getValue) {
continue;
}
--- a/browser/extensions/activity-stream/lib/ActivityStreamMessageChannel.jsm
+++ b/browser/extensions/activity-stream/lib/ActivityStreamMessageChannel.jsm
@@ -63,16 +63,18 @@ this.ActivityStreamMessageChannel = clas
if (!this.channel && !skipMain) {
next(action);
return;
}
if (au.isSendToContent(action)) {
this.send(action);
} else if (au.isBroadcastToContent(action)) {
this.broadcast(action);
+ } else if (au.isSendToPreloaded(action)) {
+ this.sendToPreloaded(action);
}
if (!skipMain) {
next(action);
}
};
}
@@ -121,16 +123,50 @@ this.ActivityStreamMessageChannel = clas
if (port.portID === id) {
return port;
}
}
return null;
}
/**
+ * sendToPreloaded - Sends an action to each preloaded browser, if any
+ *
+ * @param {obj} action A redux action
+ */
+ sendToPreloaded(action) {
+ const preloadedBrowsers = this.getPreloadedBrowser();
+ if (preloadedBrowsers && action.data) {
+ for (let preloadedBrowser of preloadedBrowsers) {
+ try {
+ preloadedBrowser.sendAsyncMessage(this.outgoingMessageName, action);
+ } catch (e) {
+ // The preloaded page is no longer available, so just ignore.
+ }
+ }
+ }
+ }
+
+ /**
+ * getPreloadedBrowser - Retrieve the port of any preloaded browsers
+ *
+ * @return {Array|null} An array of ports belonging to the preloaded browsers, or null
+ * if there aren't any preloaded browsers
+ */
+ getPreloadedBrowser() {
+ let preloadedPorts = [];
+ for (let port of this.channel.messagePorts) {
+ if (port.browser.getAttribute("preloadedState") === "preloaded") {
+ preloadedPorts.push(port);
+ }
+ }
+ return preloadedPorts.length ? preloadedPorts : null;
+ }
+
+ /**
* createChannel - Create RemotePages channel to establishing message passing
* between the main process and child pages
*/
createChannel() {
// Receive AboutNewTab's Remote Pages instance, if it exists, on override
const channel = this.pageURL === ABOUT_NEW_TAB_URL && AboutNewTab.override(true);
this.channel = channel || new RemotePages([ABOUT_HOME_URL, ABOUT_NEW_TAB_URL]);
this.channel.addMessageListener("RemotePage:Init", this.onNewTabInit);
--- a/browser/extensions/activity-stream/lib/PlacesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/PlacesFeed.jsm
@@ -67,17 +67,17 @@ class HistoryObserver extends Observer {
*/
onClearHistory() {
this.dispatch({type: at.PLACES_HISTORY_CLEARED});
}
// Empty functions to make xpconnect happy
onBeginUpdateBatch() {}
onEndUpdateBatch() {}
- onVisits() {}
+ onVisit() {}
onTitleChanged() {}
onFrecencyChanged() {}
onManyFrecenciesChanged() {}
onPageChanged() {}
onDeleteVisits() {}
}
/**
--- a/browser/extensions/activity-stream/lib/SectionsManager.jsm
+++ b/browser/extensions/activity-stream/lib/SectionsManager.jsm
@@ -289,24 +289,24 @@ class SectionsFeed {
onRemoveSection(event, id) {
this.store.dispatch(ac.BroadcastToContent({type: at.SECTION_DEREGISTER, data: id}));
}
onUpdateSection(event, id, options, shouldBroadcast = false) {
if (options) {
const action = {type: at.SECTION_UPDATE, data: Object.assign(options, {id})};
- this.store.dispatch(shouldBroadcast ? ac.BroadcastToContent(action) : action);
+ this.store.dispatch(shouldBroadcast ? ac.BroadcastToContent(action) : ac.SendToPreloaded(action));
}
}
onUpdateSectionCard(event, id, url, options, shouldBroadcast = false) {
if (options) {
const action = {type: at.SECTION_UPDATE_CARD, data: {id, url, options}};
- this.store.dispatch(shouldBroadcast ? ac.BroadcastToContent(action) : action);
+ this.store.dispatch(shouldBroadcast ? ac.BroadcastToContent(action) : ac.SendToPreloaded(action));
}
}
onAction(action) {
switch (action.type) {
case at.INIT:
SectionsManager.onceInitialized(this.init);
break;
--- a/browser/extensions/activity-stream/lib/TelemetryFeed.jsm
+++ b/browser/extensions/activity-stream/lib/TelemetryFeed.jsm
@@ -254,17 +254,17 @@ this.TelemetryFeed = class TelemetryFeed
*
* @param {string} id The portID of the session, if a session is relevant (optional)
* @return {obj} A telemetry ping
*/
createPing(portID) {
const appInfo = this.store.getState().App;
const ping = {
addon_version: appInfo.version,
- locale: Services.locale.getRequestedLocale(),
+ locale: Services.locale.getAppLocaleAsLangTag(),
user_prefs: this.userPreferences
};
// If the ping is part of a user session, add session-related info
if (portID) {
const session = this.sessions.get(portID) || this.addSession(portID);
Object.assign(ping, {session_id: session.session_id});
--- a/browser/extensions/activity-stream/lib/TopSitesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/TopSitesFeed.jsm
@@ -152,18 +152,18 @@ this.TopSitesFeed = class TopSitesFeed {
}
const links = await this.getLinksWithDefaults();
const newAction = {type: at.TOP_SITES_UPDATED, data: links};
if (options.broadcast) {
// Broadcast an update to all open content pages
this.store.dispatch(ac.BroadcastToContent(newAction));
} else {
- // Don't broadcast only update the state.
- this.store.dispatch(ac.SendToMain(newAction));
+ // Don't broadcast only update the state and update the preloaded tab.
+ this.store.dispatch(ac.SendToPreloaded(newAction));
}
}
/**
* Get an image for the link preferring tippy top, rich favicon, screenshots.
*/
async _fetchIcon(link) {
// Nothing to do if we already have a rich icon from the page
--- a/browser/extensions/activity-stream/prerendered/locales/dsb/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/dsb/activity-stream-strings.js
@@ -42,17 +42,17 @@ window.gActivityStreamStrings = {
"section_disclaimer_topstories_linktext": "Zgóńśo, kak to funkcioněrujo.",
"section_disclaimer_topstories_buttontext": "W pórěźe, som zrozměł",
"welcome_title": "Witajśo k nowemu rejtarkoju",
"welcome_body": "Firefox buźo toś ten rum wužywaś, aby waše nejwažnjejše cytańske znamjenja, nastawki, wideo a rowno woglědane boki pokazał, aby mógł se lažko k nim wrośiś.",
"welcome_label": "Wuběranje wašych nejwažnjejšych bokow",
"time_label_less_than_minute": "<1m",
"time_label_minute": "{number} m",
"time_label_hour": "{number} h",
- "time_label_day": "{number}d",
+ "time_label_day": "{number} d",
"settings_pane_button_label": "Bok wašogo nowego rejtarka pśiměriś",
"settings_pane_header": "Nastajenja nowego rejtarka składowaś",
"settings_pane_body2": "Wubjeŕśo, což se na toś tom boku pokazujo.",
"settings_pane_search_header": "Pytaś",
"settings_pane_search_body": "Pśepytajśo web ze swójogo nowego rejtarka.",
"settings_pane_topsites_header": "Nejcesćej woglědane sedła",
"settings_pane_topsites_body": "Wócyńśo websedła, kótarež sćo se nejcesćej woglědał.",
"settings_pane_topsites_options_showmore": "Dwě smužki pokazaś",
--- a/browser/extensions/activity-stream/prerendered/locales/hsb/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/hsb/activity-stream-strings.js
@@ -42,17 +42,17 @@ window.gActivityStreamStrings = {
"section_disclaimer_topstories_linktext": "Zhońće, kak to funguje.",
"section_disclaimer_topstories_buttontext": "W porjadku, sym zrozumił",
"welcome_title": "Witajće k nowemu rajtarkej",
"welcome_body": "Firefox budźe tutón rum wužiwać, zo by waše najwažniše zapołožki, nastawki, wideja a runje wopytane strony pokazał, zo byšće móhł so lochko k nim wróćić.",
"welcome_label": "Wuběranje wašich najwažnišich stronow",
"time_label_less_than_minute": "< 1 min",
"time_label_minute": "{number} m",
"time_label_hour": "{number} h",
- "time_label_day": "{number}d",
+ "time_label_day": "{number} d",
"settings_pane_button_label": "Stronu wašeho noweho rajtarka přiměrić",
"settings_pane_header": "Nastajenja noweho rajtarka",
"settings_pane_body2": "Wubjerće, štož so na tutej stronje pokazuje.",
"settings_pane_search_header": "Pytać",
"settings_pane_search_body": "Přepytajće web ze swojeho noweho rajtarka.",
"settings_pane_topsites_header": "Najhusćišo wopytane sydła",
"settings_pane_topsites_body": "Wočińće websydła, kotrež sće najhusćišo wopytał.",
"settings_pane_topsites_options_showmore": "Dwaj rjadaj pokazać",
--- a/browser/extensions/activity-stream/prerendered/locales/pt-BR/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/pt-BR/activity-stream-strings.js
@@ -87,12 +87,12 @@ window.gActivityStreamStrings = {
"topsites_form_cancel_button": "Cancelar",
"topsites_form_url_validation": "É necessário um URL válido",
"pocket_read_more": "Tópicos populares:",
"pocket_read_even_more": "Ver mais histórias",
"pocket_feedback_header": "O melhor da web, com curadoria de mais de 25 milhões de pessoas.",
"pocket_description": "Descubra conteúdo de alta qualidade que você poderia ter perdido, com a ajuda do Pocket, agora parte da Mozilla.",
"highlights_empty_state": "Comece a navegar e nós mostraremos aqui alguns ótimos artigos, vídeos e outras páginas que você favoritou ou visitou recentemente.",
"topstories_empty_state": "Você já viu tudo. Volte mais tarde para mais histórias do {provider}. Não consegue esperar? Escolha um assunto popular para encontrar mais grandes histórias através da web.",
- "manual_migration_explanation2": "Experimente o Firefox com os favoritos, histórico e senhas salvas em outros navegador.",
+ "manual_migration_explanation2": "Experimente o Firefox com os favoritos, histórico e senhas salvas em outro navegador.",
"manual_migration_cancel_button": "Não, obrigado",
"manual_migration_import_button": "Importar agora"
};
--- a/browser/extensions/activity-stream/prerendered/locales/ro/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/ro/activity-stream-strings.js
@@ -60,17 +60,17 @@ window.gActivityStreamStrings = {
"settings_pane_bookmarks_body": "Marcajele memorate recent, organizate și accesibile într-un singur log.",
"settings_pane_visit_again_header": "Vizitează din nou",
"settings_pane_visit_again_body": "Firefox îți va arăta părți din istoricul navigării tale la care ai vrea să revii mai târziu.",
"settings_pane_highlights_header": "Evidențieri",
"settings_pane_highlights_body2": "Regăsește lucrurile interesante pe care le-ai vizitat sau marcat recent.",
"settings_pane_highlights_options_bookmarks": "Marcaje",
"settings_pane_highlights_options_visited": "Site-uri vizitate",
"settings_pane_snippets_header": "Fragmente",
- "settings_pane_snippets_body": "Citește actualizări scurte de la Mozilla despre Firefox, cultura internetului și meme-ul ocazional.",
+ "settings_pane_snippets_body": "Citește actualizări scurte de la Mozilla despre Firefox, cultura internetului și meme-ul ocazional aleatoriu.",
"settings_pane_done_button": "Gata",
"settings_pane_topstories_options_sponsored": "Arată articolele sponsorizate",
"edit_topsites_button_text": "Editează",
"edit_topsites_button_label": "Particularizează secțiunea site-urilor de top",
"edit_topsites_showmore_button": "Arată mai mult",
"edit_topsites_showless_button": "Arată mai puțin",
"edit_topsites_done_button": "Gata",
"edit_topsites_pin_button": "Fixează acest site",
--- a/browser/extensions/activity-stream/prerendered/locales/te/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/te/activity-stream-strings.js
@@ -73,17 +73,17 @@ window.gActivityStreamStrings = {
"edit_topsites_showmore_button": "ఇంకా చూపించు",
"edit_topsites_showless_button": "కొన్నే చూపించు",
"edit_topsites_done_button": "పూర్తయింది",
"edit_topsites_pin_button": "ఈ సైటును ఇక్కడ గుచ్చు",
"edit_topsites_unpin_button": "ఈ సైటుకి పిన్నుని తీసివేయండి",
"edit_topsites_edit_button": "ఈ సైటును మార్చు",
"edit_topsites_dismiss_button": "ఈ సైటుని తీసివేయి",
"edit_topsites_add_button": "జోడించు",
- "topsites_form_add_header": "కొత్త టాప్ సైట్",
+ "topsites_form_add_header": "కొత్త మేటి సైటు",
"topsites_form_edit_header": "టాప్ సైట్ను సవరించండి",
"topsites_form_title_placeholder": "శీర్షికను నమోదు చేయండి",
"topsites_form_url_placeholder": "URL ను టైప్ చేయండి లేదా అతికించండి",
"topsites_form_add_button": "చేర్చు",
"topsites_form_save_button": "భద్రపరచు",
"topsites_form_cancel_button": "రద్దుచేయి",
"topsites_form_url_validation": "చెల్లుబాటు అయ్యే URL అవసరం",
"pocket_read_more": "ప్రముఖ అంశాలు:",
--- a/browser/extensions/activity-stream/test/.eslintrc.js
+++ b/browser/extensions/activity-stream/test/.eslintrc.js
@@ -1,16 +1,14 @@
module.exports = {
"env": {
- "node": true,
- "es6": true,
"mocha": true
},
"globals": {
"assert": true,
- "sinon": true,
- "chai": true
+ "chai": true,
+ "sinon": true
},
"rules": {
"import/no-commonjs": 2,
"react/jsx-no-bind": 0
}
};
--- a/browser/extensions/activity-stream/test/functional/mochitest/browser.ini
+++ b/browser/extensions/activity-stream/test/functional/mochitest/browser.ini
@@ -1,8 +1,10 @@
[DEFAULT]
support-files =
blue_page.html
head.js
[browser_as_load_location.js]
[browser_as_render.js]
[browser_getScreenshots.js]
+[browser_highlights_section.js]
+[browser_topsites_section.js]
new file mode 100644
--- /dev/null
+++ b/browser/extensions/activity-stream/test/functional/mochitest/browser_highlights_section.js
@@ -0,0 +1,50 @@
+"use strict";
+
+/**
+ * Helper for setup and cleanup of Highlights section tests.
+ * @param bookmarkCount Number of bookmark higlights to add
+ * @param test The test case
+ */
+function test_highlights(bookmarkCount, test) {
+ test_newtab({
+ async before({tab}) {
+ if (bookmarkCount) {
+ await addHighlightsBookmarks(bookmarkCount);
+ // Wait for HighlightsFeed to update and display the items.
+ await ContentTask.spawn(tab.linkedBrowser, null, async () => {
+ await ContentTaskUtils.waitForCondition(() => content.document.querySelector(".card-outer:not(.placeholder)"),
+ "No highlights cards found.");
+ });
+ }
+ },
+ test,
+ async after() {
+ await clearHistoryAndBookmarks();
+ }
+ });
+}
+
+test_highlights(
+ 2, // Number of highlights cards
+ function check_highlights_cards() {
+ let found = content.document.querySelectorAll(".card-outer:not(.placeholder)").length;
+ is(found, 2, "there should be 2 highlights cards");
+
+ found = content.document.querySelectorAll(".section-list .placeholder").length;
+ is(found, 1, "there should be 1 highlights placeholder");
+
+ found = content.document.querySelectorAll(".card-context-icon.icon-bookmark-added").length;
+ is(found, 2, "there should be 2 bookmark icons");
+ }
+);
+
+test_highlights(
+ 1, // Number of highlights cards
+ function check_highlights_context_menu() {
+ const menuButton = content.document.querySelector(".section-list .context-menu-button");
+ // Open the menu.
+ menuButton.click();
+ const found = content.document.querySelector(".context-menu");
+ ok(found && !found.hidden, "Should find a visible context menu");
+ }
+);
new file mode 100644
--- /dev/null
+++ b/browser/extensions/activity-stream/test/functional/mochitest/browser_topsites_section.js
@@ -0,0 +1,56 @@
+"use strict";
+
+// Check TopSites edit modal and overlay show up.
+test_newtab(
+ // it should be able to click the topsites edit button to reveal the edit topsites modal and overlay.
+ function topsites_edit() {
+ const topsitesEditBtn = content.document.querySelector(".edit-topsites-button button");
+ topsitesEditBtn.click();
+
+ let found = content.document.querySelector(".edit-topsites");
+ ok(found && !found.hidden, "Should find a visible topsites edit menu");
+
+ found = content.document.querySelector(".modal-overlay");
+ ok(found && !found.hidden, "Should find a visible overlay");
+ }
+);
+
+// Test pin/unpin context menu options.
+test_newtab({
+ async before({pushPrefs}) {
+ // The pref for TopSites is empty by default.
+ await pushPrefs(["browser.newtabpage.activity-stream.default.sites", "https://www.youtube.com/,https://www.facebook.com/,https://www.amazon.com/,https://www.reddit.com/,https://www.wikipedia.org/,https://twitter.com/"]);
+ // Toggle the feed off and on as a workaround to read the new prefs.
+ await pushPrefs(["browser.newtabpage.activity-stream.feeds.topsites", false]);
+ await pushPrefs(["browser.newtabpage.activity-stream.feeds.topsites", true]);
+ },
+ // it should pin the website when we click the first option of the topsite context menu.
+ test: async function topsites_pin_unpin() {
+ await ContentTaskUtils.waitForCondition(() => content.document.querySelector(".top-site-icon"),
+ "Topsite tippytop icon not found");
+ // There are only topsites on the page, the selector with find the first topsite menu button.
+ const topsiteContextBtn = content.document.querySelector(".context-menu-button");
+ topsiteContextBtn.click();
+
+ const contextMenu = content.document.querySelector(".context-menu");
+ ok(contextMenu && !contextMenu.hidden, "Should find a visible topsite context menu");
+
+ const pinUnpinTopsiteBtn = contextMenu.querySelector(".context-menu-item a");
+ // Pin the topsite.
+ pinUnpinTopsiteBtn.click();
+
+ // Need to wait for pin action.
+ await ContentTaskUtils.waitForCondition(() => content.document.querySelector(".icon-pin-small"),
+ "No pinned icon found");
+
+ let pinnedIcon = content.document.querySelectorAll(".icon-pin-small").length;
+ is(pinnedIcon, 1, "should find 1 pinned topsite");
+
+ // Unpin the topsite.
+ pinUnpinTopsiteBtn.click();
+
+ // Need to wait for unpin action.
+ await ContentTaskUtils.waitForCondition(() => !content.document.querySelector(".icon-pin-small"),
+ "Topsite should be unpinned");
+ }
+});
--- a/browser/extensions/activity-stream/test/functional/mochitest/head.js
+++ b/browser/extensions/activity-stream/test/functional/mochitest/head.js
@@ -1,36 +1,74 @@
"use strict";
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
+ "resource://testing-common/PlacesTestUtils.jsm");
+
function popPrefs() {
return SpecialPowers.popPrefEnv();
}
function pushPrefs(...prefs) {
return SpecialPowers.pushPrefEnv({set: prefs});
}
// Activity Stream tests expect it to be enabled, and make sure to clear out any
// preloaded browsers that might have about:newtab that we don't want to test
const ACTIVITY_STREAM_PREF = "browser.newtabpage.activity-stream.enabled";
pushPrefs([ACTIVITY_STREAM_PREF, true]);
gBrowser.removePreloadedBrowser();
+async function clearHistoryAndBookmarks() { // eslint-disable-line no-unused-vars
+ await PlacesUtils.bookmarks.eraseEverything();
+ await PlacesUtils.history.clear();
+}
+
/**
* Helper to wait for potentially preloaded browsers to "load" where a preloaded
* page has already loaded and won't trigger "load", and a "load"ed page might
* not necessarily have had all its javascript/render logic executed.
*/
async function waitForPreloaded(browser) {
let readyState = await ContentTask.spawn(browser, {}, () => content.document.readyState);
if (readyState !== "complete") {
await BrowserTestUtils.browserLoaded(browser);
}
}
/**
+ * Helper to force the HighlightsFeed to update.
+ */
+function refreshHighlightsFeed() {
+ // Toggling the pref will clear the feed cache and force a places query.
+ Services.prefs.setBoolPref("browser.newtabpage.activity-stream.feeds.section.highlights", false);
+ Services.prefs.setBoolPref("browser.newtabpage.activity-stream.feeds.section.highlights", true);
+}
+
+/**
+ * Helper to populate the Highlights section with bookmark cards.
+ * @param count Number of items to add.
+ */
+async function addHighlightsBookmarks(count) { // eslint-disable-line no-unused-vars
+ const bookmarks = new Array(count).fill(null).map((entry, i) => ({
+ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+ title: "foo",
+ url: `https://mozilla${i}.com/nowNew`
+ }));
+
+ for (let placeInfo of bookmarks) {
+ await PlacesUtils.bookmarks.insert(placeInfo);
+ // Bookmarks need at least one visit to show up as highlights.
+ await PlacesTestUtils.addVisits(placeInfo.url);
+ }
+
+ // Force HighlightsFeed to make a request for the new items.
+ refreshHighlightsFeed();
+}
+
+/**
* Helper to run Activity Stream about:newtab test tasks in content.
*
* @param testInfo {Function|Object}
* {Function} This parameter will be used as if the function were called with
* an Object with this parameter as "test" key's value.
* {Object} The following keys are expected:
* before {Function} Optional. Runs before and returns an arg for "test"
* test {Function} The test to run in the about:newtab content task taking
--- a/browser/extensions/activity-stream/test/unit/common/Actions.test.js
+++ b/browser/extensions/activity-stream/test/unit/common/Actions.test.js
@@ -2,16 +2,17 @@ import {
actionCreators as ac,
actionTypes as at,
actionUtils as au,
BACKGROUND_PROCESS,
CONTENT_MESSAGE_TYPE,
globalImportContext,
MAIN_MESSAGE_TYPE,
+ PRELOAD_MESSAGE_TYPE,
UI_CODE
} from "common/Actions.jsm";
describe("Actions", () => {
it("should set globalImportContext to UI_CODE", () => {
assert.equal(globalImportContext, UI_CODE);
});
});
@@ -121,16 +122,36 @@ describe("ActionCreators", () => {
assert.isTrue(au.isBroadcastToContent(ac.BroadcastToContent({type: "FOO"})));
});
it("should return false if action is not BroadcastToContent", () => {
assert.isFalse(au.isBroadcastToContent({type: "FOO"}));
assert.isFalse(au.isBroadcastToContent(ac.SendToContent({type: "FOO"}, "foo123")));
});
});
});
+ describe("SendToPreloaded", () => {
+ it("should create the right action", () => {
+ const action = {type: "FOO", data: "BAR"};
+ const newAction = ac.SendToPreloaded(action);
+ assert.deepEqual(newAction, {
+ type: "FOO",
+ data: "BAR",
+ meta: {from: MAIN_MESSAGE_TYPE, to: PRELOAD_MESSAGE_TYPE}
+ });
+ });
+ });
+ describe("isSendToPreloaded", () => {
+ it("should return true if action is SendToPreloaded", () => {
+ assert.isTrue(au.isSendToPreloaded(ac.SendToPreloaded({type: "FOO"})));
+ });
+ it("should return false if action is not SendToPreloaded", () => {
+ assert.isFalse(au.isSendToPreloaded({type: "FOO"}));
+ assert.isFalse(au.isSendToPreloaded(ac.BroadcastToContent({type: "FOO"})));
+ });
+ });
describe("UserEvent", () => {
it("should include the given data", () => {
const data = {action: "foo"};
assert.equal(ac.UserEvent(data).data, data);
});
it("should wrap with SendToMain", () => {
const action = ac.UserEvent({action: "foo"});
assert.isTrue(au.isSendToMain(action), "isSendToMain");
--- a/browser/extensions/activity-stream/test/unit/lib/ActivityStream.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/ActivityStream.test.js
@@ -166,39 +166,39 @@ describe("ActivityStream", () => {
as._updateDynamicPrefs();
assert.isFalse(PREFS_CONFIG.get("feeds.section.topstories").value);
});
it("should be false with expected geo and unexpected locale", () => {
sandbox.stub(global.Services.prefs, "prefHasUserValue").returns(true);
sandbox.stub(global.Services.prefs, "getStringPref").returns("US");
- sandbox.stub(global.Services.locale, "getRequestedLocale").returns("no-LOCALE");
+ sandbox.stub(global.Services.locale, "getAppLocaleAsLangTag").returns("no-LOCALE");
as._updateDynamicPrefs();
assert.isFalse(PREFS_CONFIG.get("feeds.section.topstories").value);
});
it("should be true with expected geo and locale", () => {
sandbox.stub(global.Services.prefs, "prefHasUserValue").returns(true);
sandbox.stub(global.Services.prefs, "getStringPref").returns("US");
- sandbox.stub(global.Services.locale, "getRequestedLocale").returns("en-US");
+ sandbox.stub(global.Services.locale, "getAppLocaleAsLangTag").returns("en-US");
as._updateDynamicPrefs();
assert.isTrue(PREFS_CONFIG.get("feeds.section.topstories").value);
});
it("should be false after expected geo and locale then unexpected", () => {
sandbox.stub(global.Services.prefs, "prefHasUserValue").returns(true);
sandbox.stub(global.Services.prefs, "getStringPref")
.onFirstCall()
.returns("US")
.onSecondCall()
.returns("NOGEO");
- sandbox.stub(global.Services.locale, "getRequestedLocale").returns("en-US");
+ sandbox.stub(global.Services.locale, "getAppLocaleAsLangTag").returns("en-US");
as._updateDynamicPrefs();
as._updateDynamicPrefs();
assert.isFalse(PREFS_CONFIG.get("feeds.section.topstories").value);
});
});
describe("_updateDynamicPrefs topstories delayed default value", () => {
@@ -219,17 +219,17 @@ describe("ActivityStream", () => {
as._updateDynamicPrefs();
clock.tick(1);
assert.isFalse(PREFS_CONFIG.get("feeds.section.topstories").value);
});
it("should set true with expected geo and locale", () => {
sandbox.stub(global.Services.prefs, "getStringPref").returns("US");
- sandbox.stub(global.Services.locale, "getRequestedLocale").returns("en-US");
+ sandbox.stub(global.Services.locale, "getAppLocaleAsLangTag").returns("en-US");
as._updateDynamicPrefs();
clock.tick(1);
assert.isTrue(PREFS_CONFIG.get("feeds.section.topstories").value);
});
});
describe("telemetry reporting on init failure", () => {
--- a/browser/extensions/activity-stream/test/unit/lib/ActivityStreamMessageChannel.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/ActivityStreamMessageChannel.test.js
@@ -153,16 +153,55 @@ describe("ActivityStreamMessageChannel",
});
it("should return null if the target doesn't exist", () => {
const t = {portID: "foo"};
mm.createChannel();
mm.channel.messagePorts.push(t);
assert.equal(mm.getTargetById("bar"), null);
});
});
+ describe("#getPreloadedBrowser", () => {
+ it("should get a preloaded browser if it exists", () => {
+ const port = {
+ browser: {
+ getAttribute() {
+ return "preloaded";
+ }
+ }
+ };
+ mm.createChannel();
+ mm.channel.messagePorts.push(port);
+ assert.equal(mm.getPreloadedBrowser()[0], port);
+ });
+ it("should get all the preloaded browsers across windows if they exist", () => {
+ const port = {
+ browser: {
+ getAttribute() {
+ return "preloaded";
+ }
+ }
+ };
+ mm.createChannel();
+ mm.channel.messagePorts.push(port);
+ mm.channel.messagePorts.push(port);
+ assert.equal(mm.getPreloadedBrowser().length, 2);
+ });
+ it("should return null if there is no preloaded browser", () => {
+ const port = {
+ browser: {
+ getAttribute() {
+ return "consumed";
+ }
+ }
+ };
+ mm.createChannel();
+ mm.channel.messagePorts.push(port);
+ assert.equal(mm.getPreloadedBrowser(), null);
+ });
+ });
describe("#onNewTabInit", () => {
it("should dispatch a NEW_TAB_INIT action", () => {
const t = {portID: "foo", url: "about:monkeys"};
sinon.stub(mm, "onActionFromContent");
mm.onNewTabInit({target: t});
assert.calledWith(mm.onActionFromContent, {
@@ -236,16 +275,63 @@ describe("ActivityStreamMessageChannel",
describe("#broadcast", () => {
it("should send a message on the channel", () => {
mm.createChannel();
const action = ac.BroadcastToContent({type: "HELLO"});
mm.broadcast(action);
assert.calledWith(mm.channel.sendAsyncMessage, DEFAULT_OPTIONS.outgoingMessageName, action);
});
});
+ describe("#preloaded browser", () => {
+ it("should send the message to the preloaded browser if there's data and a preloaded browser exists", () => {
+ const port = {
+ browser: {
+ getAttribute() {
+ return "preloaded";
+ }
+ },
+ sendAsyncMessage: sinon.spy()
+ };
+ mm.createChannel();
+ mm.channel.messagePorts.push(port);
+ const action = ac.SendToPreloaded({type: "HELLO", data: 10});
+ mm.sendToPreloaded(action);
+ assert.calledWith(port.sendAsyncMessage, DEFAULT_OPTIONS.outgoingMessageName, action);
+ });
+ it("should send the message to all the preloaded browsers if there's data and they exist", () => {
+ const port = {
+ browser: {
+ getAttribute() {
+ return "preloaded";
+ }
+ },
+ sendAsyncMessage: sinon.spy()
+ };
+ mm.createChannel();
+ mm.channel.messagePorts.push(port);
+ mm.channel.messagePorts.push(port);
+ mm.sendToPreloaded(ac.SendToPreloaded({type: "HELLO", data: 10}));
+ assert.calledTwice(port.sendAsyncMessage);
+ });
+ it("should not send the message to the preloaded browser if there's no data and a preloaded browser does not exists", () => {
+ const port = {
+ browser: {
+ getAttribute() {
+ return "consumed";
+ }
+ },
+ sendAsyncMessage: sinon.spy()
+ };
+ mm.createChannel();
+ mm.channel.messagePorts.push(port);
+ const action = ac.SendToPreloaded({type: "HELLO"});
+ mm.sendToPreloaded(action);
+ assert.notCalled(port.sendAsyncMessage);
+ });
+ });
});
describe("Handling actions", () => {
describe("#onActionFromContent", () => {
beforeEach(() => mm.onActionFromContent({type: "FOO"}, "foo"));
it("should dispatch a SendToMain action", () => {
assert.calledOnce(dispatch);
const action = dispatch.firstCall.args[0];
assert.equal(action.type, "FOO", "action.type");
@@ -288,22 +374,33 @@ describe("ActivityStreamMessageChannel",
sinon.stub(mm, "broadcast");
const action = ac.BroadcastToContent({type: "FOO"});
mm.createChannel();
store.dispatch(action);
assert.calledWith(mm.broadcast, action);
});
+ it("should call .sendToPreloaded if the action is SendToPreloaded", () => {
+ sinon.stub(mm, "sendToPreloaded");
+ const action = ac.SendToPreloaded({type: "FOO"});
+
+ mm.createChannel();
+ store.dispatch(action);
+
+ assert.calledWith(mm.sendToPreloaded, action);
+ });
it("should dispatch other actions normally", () => {
sinon.stub(mm, "send");
sinon.stub(mm, "broadcast");
+ sinon.stub(mm, "sendToPreloaded");
mm.createChannel();
store.dispatch({type: "ADD", data: 1});
assert.equal(store.getState(), 1);
assert.notCalled(mm.send);
assert.notCalled(mm.broadcast);
+ assert.notCalled(mm.sendToPreloaded);
});
});
});
});
--- a/browser/extensions/activity-stream/test/unit/lib/SectionsManager.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/SectionsManager.test.js
@@ -1,10 +1,10 @@
"use strict";
-import {CONTENT_MESSAGE_TYPE, MAIN_MESSAGE_TYPE} from "common/Actions.jsm";
+import {CONTENT_MESSAGE_TYPE, MAIN_MESSAGE_TYPE, PRELOAD_MESSAGE_TYPE} from "common/Actions.jsm";
import {EventEmitter, GlobalOverrider} from "test/unit/utils";
import {SectionsFeed, SectionsManager} from "lib/SectionsManager.jsm";
const FAKE_ID = "FAKE_ID";
const FAKE_OPTIONS = {icon: "FAKE_ICON", title: "FAKE_TITLE"};
const FAKE_ROWS = [{url: "1.example.com"}, {url: "2.example.com"}, {"url": "3.example.com"}];
const FAKE_URL = "2.example.com";
const FAKE_CARD_OPTIONS = {title: "Some fake title"};
@@ -349,18 +349,19 @@ describe("SectionsFeed", () => {
feed.onUpdateSection(null, FAKE_ID, null);
assert.notCalled(feed.store.dispatch);
});
it("should dispatch a SECTION_UPDATE action with the correct data", () => {
feed.onUpdateSection(null, FAKE_ID, {rows: FAKE_ROWS});
const action = feed.store.dispatch.firstCall.args[0];
assert.equal(action.type, "SECTION_UPDATE");
assert.deepEqual(action.data, {id: FAKE_ID, rows: FAKE_ROWS});
- // Should be not broadcast by default, so meta should not exist
- assert.notOk(action.meta);
+ // Should be not broadcast by default, but should update the preloaded tab, so check meta
+ assert.equal(action.meta.from, MAIN_MESSAGE_TYPE);
+ assert.equal(action.meta.to, PRELOAD_MESSAGE_TYPE);
});
it("should broadcast the action only if shouldBroadcast is true", () => {
feed.onUpdateSection(null, FAKE_ID, {rows: FAKE_ROWS}, true);
const action = feed.store.dispatch.firstCall.args[0];
// Should be broadcast
assert.equal(action.meta.from, MAIN_MESSAGE_TYPE);
assert.equal(action.meta.to, CONTENT_MESSAGE_TYPE);
});
@@ -370,18 +371,19 @@ describe("SectionsFeed", () => {
feed.onUpdateSectionCard(null, FAKE_ID, FAKE_URL, null);
assert.notCalled(feed.store.dispatch);
});
it("should dispatch a SECTION_UPDATE_CARD action with the correct data", () => {
feed.onUpdateSectionCard(null, FAKE_ID, FAKE_URL, FAKE_CARD_OPTIONS);
const action = feed.store.dispatch.firstCall.args[0];
assert.equal(action.type, "SECTION_UPDATE_CARD");
assert.deepEqual(action.data, {id: FAKE_ID, url: FAKE_URL, options: FAKE_CARD_OPTIONS});
- // Should be not broadcast by default, so meta should not exist
- assert.notOk(action.meta);
+ // Should be not broadcast by default, but should update the preloaded tab, so check meta
+ assert.equal(action.meta.from, MAIN_MESSAGE_TYPE);
+ assert.equal(action.meta.to, PRELOAD_MESSAGE_TYPE);
});
it("should broadcast the action only if shouldBroadcast is true", () => {
feed.onUpdateSectionCard(null, FAKE_ID, FAKE_URL, FAKE_CARD_OPTIONS, true);
const action = feed.store.dispatch.firstCall.args[0];
// Should be broadcast
assert.equal(action.meta.from, MAIN_MESSAGE_TYPE);
assert.equal(action.meta.to, CONTENT_MESSAGE_TYPE);
});
--- a/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
@@ -419,22 +419,22 @@ describe("Top Sites Feed", () => {
assert.deepEqual(feed.store.dispatch.firstCall.args[0].data, reference);
});
it("should handle empty slots in the resulting top sites array", async () => {
links = [FAKE_LINKS[0]];
fakeNewTabUtils.pinnedLinks.links = [null, null, FAKE_LINKS[1], null, null, null, null, null, FAKE_LINKS[2]];
await feed.refresh({broadcast: true});
assert.calledOnce(feed.store.dispatch);
});
- it("should dispatch sendToMain when broadcast is false", async () => {
+ it("should dispatch SendToPreloaded when broadcast is false", async () => {
sandbox.stub(feed, "getLinksWithDefaults").returns([]);
await feed.refresh({broadcast: false});
assert.calledOnce(feed.store.dispatch);
- assert.calledWithExactly(feed.store.dispatch, ac.SendToMain({
+ assert.calledWithExactly(feed.store.dispatch, ac.SendToPreloaded({
type: at.TOP_SITES_UPDATED,
data: []
}));
});
});
describe("#_fetchIcon", () => {
it("should reuse screenshot on the link", () => {
const link = {screenshot: "reuse.png"};
--- a/browser/extensions/activity-stream/test/unit/lib/TopStoriesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/TopStoriesFeed.test.js
@@ -27,17 +27,16 @@ describe("Top Stories Feed", () => {
"provider_icon": "provider-icon",
"provider_description": "provider_desc"
};
beforeEach(() => {
FakePrefs.prototype.prefs.apiKeyPref = "test-api-key";
globals = new GlobalOverrider();
- globals.set("Services", {locale: {getRequestedLocale: () => "en-CA"}, obs: {addObserver: () => {}, removeObserver: () => {}}});
globals.set("PlacesUtils", {history: {}});
clock = sinon.useFakeTimers();
shortURLStub = sinon.stub().callsFake(site => site.url);
sectionsManagerStub = {
onceInitialized: sinon.stub().callsFake(callback => callback()),
enableSection: sinon.spy(),
disableSection: sinon.spy(),
updateSection: sinon.spy(),
@@ -124,18 +123,16 @@ describe("Top Stories Feed", () => {
it("should report error for invalid configuration", () => {
globals.sandbox.spy(global.Components.utils, "reportError");
sectionsManagerStub.sections.set("topstories", {options: {api_key_pref: "invalid"}});
instance.init();
assert.called(Components.utils.reportError);
});
it("should report error for missing api key", () => {
- let fakeServices = {prefs: {getCharPref: sinon.spy()}, locale: {getRequestedLocale: sinon.spy()}};
- globals.set("Services", fakeServices);
globals.sandbox.spy(global.Components.utils, "reportError");
sectionsManagerStub.sections.set("topstories", {
options: {
"stories_endpoint": "https://somedomain.org/stories?key=$apiKey",
"topics_endpoint": "https://somedomain.org/topics?key=$apiKey"
}
});
instance.init();
--- a/browser/extensions/activity-stream/test/unit/lib/UserDomainAffinityProvider.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/UserDomainAffinityProvider.test.js
@@ -31,17 +31,17 @@ const PARAMETER_SETS = {
describe("User Domain Affinity Provider", () => {
let UserDomainAffinityProvider;
let instance;
let globals;
beforeEach(() => {
globals = new GlobalOverrider();
- globals.set("Services", {locale: {getRequestedLocale: () => "en-CA"}, io: {newURI: u => ({host: "www.somedomain.org"})}});
+ globals.set("Services", {io: {newURI: u => ({host: "www.somedomain.org"})}});
globals.set("PlacesUtils", {
history: {
getNewQuery: () => ({"TIME_RELATIVE_NOW": 1}),
getNewQueryOptions: () => ({}),
executeQuery: () => ({root: {childCount: 1, getChild: index => ({uri: "www.somedomain.org", accessCount: 1})}})
}
});
global.Components.classes["@mozilla.org/browser/nav-history-service;1"] = {
--- a/browser/extensions/activity-stream/test/unit/unit-entry.js
+++ b/browser/extensions/activity-stream/test/unit/unit-entry.js
@@ -43,20 +43,18 @@ overrider.set({
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: {
+ getAppLocaleAsLangTag() { return "en-US"; },
getAppLocalesAsLangTags() {},
- getRequestedLocale() {
- return "en-US";
- },
negotiateLanguages() {}
},
urlFormatter: {formatURL: str => str},
mm: {
addMessageListener: (msg, cb) => cb(),
removeMessageListener() {}
},
appShell: {hiddenDOMWindow: {performance: new FakePerformance()}},
--- a/browser/modules/PingCentre.jsm
+++ b/browser/modules/PingCentre.jsm
@@ -16,16 +16,35 @@ XPCOMUtils.defineLazyModuleGetter(this,
const PREF_BRANCH = "browser.ping-centre.";
const TELEMETRY_PREF = `${PREF_BRANCH}telemetry`;
const LOGGING_PREF = `${PREF_BRANCH}log`;
const PRODUCTION_ENDPOINT_PREF = `${PREF_BRANCH}production.endpoint`;
const FHR_UPLOAD_ENABLED_PREF = "datareporting.healthreport.uploadEnabled";
+const BROWSER_SEARCH_REGION_PREF = "browser.search.region";
+
+// Only report region for following regions, to ensure that users in countries
+// with small user population (less than 10000) cannot be uniquely identified.
+// See bug 1421422 for more details.
+const REGION_WHITELIST = new Set([
+ "AE", "AF", "AL", "AM", "AR", "AT", "AU", "AZ", "BA", "BD", "BE", "BF",
+ "BG", "BJ", "BO", "BR", "BY", "CA", "CH", "CI", "CL", "CM", "CN", "CO",
+ "CR", "CU", "CY", "CZ", "DE", "DK", "DO", "DZ", "EC", "EE", "EG", "ES",
+ "ET", "FI", "FR", "GB", "GE", "GH", "GP", "GR", "GT", "HK", "HN", "HR",
+ "HU", "ID", "IE", "IL", "IN", "IQ", "IR", "IS", "IT", "JM", "JO", "JP",
+ "KE", "KH", "KR", "KW", "KZ", "LB", "LK", "LT", "LU", "LV", "LY", "MA",
+ "MD", "ME", "MG", "MK", "ML", "MM", "MN", "MQ", "MT", "MU", "MX", "MY",
+ "MZ", "NC", "NG", "NI", "NL", "NO", "NP", "NZ", "OM", "PA", "PE", "PH",
+ "PK", "PL", "PR", "PS", "PT", "PY", "QA", "RE", "RO", "RS", "RU", "RW",
+ "SA", "SD", "SE", "SG", "SI", "SK", "SN", "SV", "SY", "TG", "TH", "TN",
+ "TR", "TT", "TW", "TZ", "UA", "UG", "US", "UY", "UZ", "VE", "VN", "ZA",
+ "ZM", "ZW"
+]);
/**
* Observe various notifications and send them to a telemetry endpoint.
*
* @param {Object} options
* @param {string} options.topic - a unique ID for users of PingCentre to distinguish
* their data on the server side.
* @param {string} options.overrideEndpointPref - optional pref for URL where the POST is sent.
@@ -97,36 +116,56 @@ class PingCentre {
continue;
}
let expString = `${experimentID}:${activeExperiments[experimentID].branch}`;
experimentsString = experimentsString.concat(`${expString};`);
}
return experimentsString;
}
+ _getRegion() {
+ let region = "UNSET";
+
+ if (Services.prefs.prefHasUserValue(BROWSER_SEARCH_REGION_PREF)) {
+ region = Services.prefs.getStringPref(BROWSER_SEARCH_REGION_PREF);
+ if (region === "") {
+ region = "EMPTY";
+ } else if (!REGION_WHITELIST.has(region)) {
+ region = "OTHER";
+ }
+ }
+ return region;
+ }
+
async sendPing(data, options) {
let filter = options && options.filter;
let experiments = TelemetryEnvironment.getActiveExperiments();
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();
+ let profileCreationDate = TelemetryEnvironment.currentEnvironment.profile.resetDate ||
+ TelemetryEnvironment.currentEnvironment.profile.creationDate;
const payload = Object.assign({
locale,
topic: this._topic,
client_id: clientID,
version: AppConstants.MOZ_APP_VERSION,
release_channel: AppConstants.MOZ_UPDATE_CHANNEL
}, data);
if (experimentsString) {
payload.shield_id = experimentsString;
}
+ if (profileCreationDate) {
+ payload.profile_creation_date = profileCreationDate;
+ }
+ payload.region = this._getRegion();
if (this.logging) {
// performance related pings cause a lot of logging, so we mute them
if (data.action !== "activity_stream_performance") {
Services.console.logStringMessage(`TELEMETRY PING: ${JSON.stringify(payload)}\n`);
}
}