mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 07:15:46 +00:00
Merge mozilla-central to mozilla-inbound. a=merge CLOSED TREE
This commit is contained in:
commit
ce184f45cc
@ -78,6 +78,7 @@ module.exports = {
|
||||
"camelcase": 0,
|
||||
"capitalized-comments": 0,
|
||||
"class-methods-use-this": 0,
|
||||
"comma-dangle": [2, "never"],
|
||||
"consistent-this": [2, "use-bind"],
|
||||
"curly": [2, "all"],
|
||||
"default-case": 0,
|
||||
|
@ -2,7 +2,7 @@ language: node_js
|
||||
|
||||
node_js:
|
||||
# when changing this, be sure to edit .nvrmc and package.json too
|
||||
- 8
|
||||
- 7
|
||||
|
||||
python:
|
||||
- "2.7"
|
||||
|
@ -100,7 +100,7 @@ AboutNewTabService.prototype = {
|
||||
classID: Components.ID("{dfcd2adc-7867-4d3a-ba70-17501f208142}"),
|
||||
QueryInterface: ChromeUtils.generateQI([
|
||||
Ci.nsIAboutNewTabService,
|
||||
Ci.nsIObserver,
|
||||
Ci.nsIObserver
|
||||
]),
|
||||
|
||||
observe(subject, topic, data) {
|
||||
@ -153,7 +153,7 @@ AboutNewTabService.prototype = {
|
||||
`${BASE_URL}vendor/redux.js`,
|
||||
`${BASE_URL}vendor/react-redux.js`,
|
||||
`${BASE_URL}prerendered/${this.activityStreamLocale}/activity-stream-strings.js`,
|
||||
`${BASE_URL}data/content/activity-stream.bundle.js`,
|
||||
`${BASE_URL}data/content/activity-stream.bundle.js`
|
||||
];
|
||||
|
||||
if (this._activityStreamPrerender) {
|
||||
@ -251,7 +251,7 @@ AboutNewTabService.prototype = {
|
||||
this._activityStreamPrerender ? "-prerendered" : "",
|
||||
this._activityStreamDebug ? "-debug" : "",
|
||||
this._privilegedContentProcess ? "-noscripts" : "",
|
||||
".html",
|
||||
".html"
|
||||
].join("");
|
||||
},
|
||||
|
||||
@ -339,7 +339,7 @@ AboutNewTabService.prototype = {
|
||||
Services.prefs.removeObserver(PREF_ACTIVITY_STREAM_DEBUG, this);
|
||||
}
|
||||
this.initialized = false;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AboutNewTabService]);
|
||||
|
@ -107,5 +107,5 @@ exports.CENTRAL_LOCALES = [
|
||||
"wo",
|
||||
"xh",
|
||||
"zh-CN",
|
||||
"zh-TW",
|
||||
"zh-TW"
|
||||
];
|
||||
|
@ -12,7 +12,7 @@ const {CENTRAL_LOCALES, DEFAULT_LOCALE} = require("./locales");
|
||||
// in mozilla-central.
|
||||
const DEFAULT_OPTIONS = {
|
||||
addonPath: "..",
|
||||
baseUrl: "resource://activity-stream/",
|
||||
baseUrl: "resource://activity-stream/"
|
||||
};
|
||||
|
||||
// Locales that should be displayed RTL
|
||||
@ -76,7 +76,7 @@ function templateHTML(options, html) {
|
||||
`${options.baseUrl}vendor/redux.js`,
|
||||
`${options.baseUrl}vendor/react-redux.js`,
|
||||
`${options.baseUrl}prerendered/${options.locale}/activity-stream-strings.js`,
|
||||
`${options.baseUrl}data/content/activity-stream.bundle.js`,
|
||||
`${options.baseUrl}data/content/activity-stream.bundle.js`
|
||||
];
|
||||
if (isPrerendered) {
|
||||
scripts.unshift(`${options.baseUrl}prerendered/static/activity-stream-initial-state.js`);
|
||||
@ -155,7 +155,7 @@ const STATIC_FILES = new Map([
|
||||
["activity-stream-debug-noscripts.html", ({options}) => templateHTML(Object.assign({}, options, {noscripts: true}))],
|
||||
["activity-stream-initial-state.js", ({state}) => templateJs("gActivityStreamPrerenderedState", "static", state)],
|
||||
["activity-stream-prerendered-debug.html", ({html, options}) => templateHTML(options, html)],
|
||||
["activity-stream-prerendered-debug-noscripts.html", ({html, options}) => templateHTML(Object.assign({}, options, {noscripts: true}), html)],
|
||||
["activity-stream-prerendered-debug-noscripts.html", ({html, options}) => templateHTML(Object.assign({}, options, {noscripts: true}), html)]
|
||||
]);
|
||||
|
||||
const LOCALIZED_FILES = new Map([
|
||||
@ -163,7 +163,7 @@ const LOCALIZED_FILES = new Map([
|
||||
["activity-stream-prerendered-noscripts.html", ({html, options}) => templateHTML(Object.assign({}, options, {noscripts: true}), html)],
|
||||
["activity-stream-strings.js", ({options: {locale, strings}}) => templateJs("gActivityStreamStrings", locale, strings)],
|
||||
["activity-stream.html", ({options}) => templateHTML(options)],
|
||||
["activity-stream-noscripts.html", ({options}) => templateHTML(Object.assign({}, options, {noscripts: true}))],
|
||||
["activity-stream-noscripts.html", ({options}) => templateHTML(Object.assign({}, options, {noscripts: true}))]
|
||||
]);
|
||||
|
||||
/**
|
||||
@ -177,8 +177,8 @@ function main() { // eslint-disable-line max-statements
|
||||
const args = require("minimist")(process.argv.slice(2), {
|
||||
alias: {
|
||||
addonPath: "a",
|
||||
baseUrl: "b",
|
||||
},
|
||||
baseUrl: "b"
|
||||
}
|
||||
});
|
||||
|
||||
const baseOptions = Object.assign({debug: false}, DEFAULT_OPTIONS, args || {});
|
||||
@ -211,7 +211,7 @@ function main() { // eslint-disable-line max-statements
|
||||
const options = Object.assign({}, baseOptions, {
|
||||
direction: getTextDirection(locale),
|
||||
locale,
|
||||
strings,
|
||||
strings
|
||||
});
|
||||
|
||||
// Put locale-specific files in their own directory
|
||||
|
@ -79,7 +79,7 @@ function findNewlyMergedPRs() {
|
||||
"base:master",
|
||||
|
||||
// if it's already been pushed to pine, don't do it again
|
||||
`-label:${ALREADY_PUSHED_LABEL}`,
|
||||
`-label:${ALREADY_PUSHED_LABEL}`
|
||||
];
|
||||
|
||||
console.log(`Searching ${AS_REPO} for newly merged PRs`);
|
||||
@ -97,7 +97,7 @@ function getPRMergeCommitId(prNumber) {
|
||||
return github.issues.getEvents({
|
||||
owner: AS_REPO_OWNER,
|
||||
repo: AS_REPO_NAME,
|
||||
issue_number: prNumber,
|
||||
issue_number: prNumber
|
||||
}).then(({data}) => {
|
||||
if (data.incomplete_results) {
|
||||
// XXX should handle this case theoretically, but since we'll be running
|
||||
@ -190,7 +190,7 @@ function commitToHg([commitId, commitMsg]) {
|
||||
"--addremove",
|
||||
"-m",
|
||||
`${commitMsg}\n\nExport of ${commitId} from ${AS_REPO_OWNER}/${AS_REPO_NAME}`,
|
||||
".",
|
||||
"."
|
||||
],
|
||||
{cwd: TESTING_LOCAL_MC, env: process.env, timeout: 5 * 60 * 1000},
|
||||
(code, stdout, stderr) => {
|
||||
@ -257,7 +257,7 @@ function annotateGithubPR(prNumber, annotation) {
|
||||
owner: AS_REPO_OWNER,
|
||||
repo: AS_REPO_NAME,
|
||||
number: prNumber,
|
||||
body: annotation,
|
||||
body: annotation
|
||||
}).catch(reason => console.log(reason));
|
||||
}
|
||||
|
||||
@ -271,7 +271,7 @@ function labelGithubPR(prNumber) {
|
||||
owner: AS_REPO_OWNER,
|
||||
repo: AS_REPO_NAME,
|
||||
number: prNumber,
|
||||
labels: [ALREADY_PUSHED_LABEL],
|
||||
labels: [ALREADY_PUSHED_LABEL]
|
||||
}).catch(reason => console.log(reason));
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,6 @@ for (const type of [
|
||||
"ADDONS_INFO_REQUEST",
|
||||
"ADDONS_INFO_RESPONSE",
|
||||
"ARCHIVE_FROM_POCKET",
|
||||
"AS_ROUTER_INITIALIZED",
|
||||
"AS_ROUTER_PREF_CHANGED",
|
||||
"AS_ROUTER_TELEMETRY_USER_EVENT",
|
||||
"BLOCK_URL",
|
||||
"BOOKMARK_URL",
|
||||
@ -95,7 +93,6 @@ for (const type of [
|
||||
"SNIPPETS_BLOCKLIST_CLEARED",
|
||||
"SNIPPETS_BLOCKLIST_UPDATED",
|
||||
"SNIPPETS_DATA",
|
||||
"SNIPPETS_PREVIEW_MODE",
|
||||
"SNIPPETS_RESET",
|
||||
"SNIPPET_BLOCKED",
|
||||
"SUBMIT_EMAIL",
|
||||
@ -120,7 +117,7 @@ for (const type of [
|
||||
"UPDATE_SEARCH_SHORTCUTS",
|
||||
"UPDATE_SECTION_PREFS",
|
||||
"WEBEXT_CLICK",
|
||||
"WEBEXT_DISMISS",
|
||||
"WEBEXT_DISMISS"
|
||||
]) {
|
||||
actionTypes[type] = type;
|
||||
}
|
||||
@ -133,7 +130,7 @@ for (const type of [
|
||||
"OPEN_APPLICATIONS_MENU",
|
||||
"OPEN_PRIVATE_BROWSER_WINDOW",
|
||||
"OPEN_URL",
|
||||
"OPEN_ABOUT_PAGE",
|
||||
"OPEN_ABOUT_PAGE"
|
||||
]) {
|
||||
ASRouterActions[type] = type;
|
||||
}
|
||||
@ -171,7 +168,7 @@ function AlsoToMain(action, fromTarget, skipLocal) {
|
||||
from: CONTENT_MESSAGE_TYPE,
|
||||
to: MAIN_MESSAGE_TYPE,
|
||||
fromTarget,
|
||||
skipLocal,
|
||||
skipLocal
|
||||
});
|
||||
}
|
||||
|
||||
@ -196,7 +193,7 @@ function OnlyToMain(action, fromTarget) {
|
||||
function BroadcastToContent(action) {
|
||||
return _RouteMessage(action, {
|
||||
from: MAIN_MESSAGE_TYPE,
|
||||
to: CONTENT_MESSAGE_TYPE,
|
||||
to: CONTENT_MESSAGE_TYPE
|
||||
});
|
||||
}
|
||||
|
||||
@ -217,7 +214,7 @@ function AlsoToOneContent(action, target, skipMain) {
|
||||
from: MAIN_MESSAGE_TYPE,
|
||||
to: CONTENT_MESSAGE_TYPE,
|
||||
toTarget: target,
|
||||
skipMain,
|
||||
skipMain
|
||||
});
|
||||
}
|
||||
|
||||
@ -242,7 +239,7 @@ function OnlyToOneContent(action, target) {
|
||||
function AlsoToPreloaded(action) {
|
||||
return _RouteMessage(action, {
|
||||
from: MAIN_MESSAGE_TYPE,
|
||||
to: PRELOAD_MESSAGE_TYPE,
|
||||
to: PRELOAD_MESSAGE_TYPE
|
||||
});
|
||||
}
|
||||
|
||||
@ -256,7 +253,7 @@ function AlsoToPreloaded(action) {
|
||||
function UserEvent(data) {
|
||||
return AlsoToMain({
|
||||
type: actionTypes.TELEMETRY_USER_EVENT,
|
||||
data,
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
@ -270,7 +267,7 @@ function UserEvent(data) {
|
||||
function ASRouterUserEvent(data) {
|
||||
return AlsoToMain({
|
||||
type: actionTypes.AS_ROUTER_TELEMETRY_USER_EVENT,
|
||||
data,
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
@ -284,7 +281,7 @@ function ASRouterUserEvent(data) {
|
||||
function UndesiredEvent(data, importContext = globalImportContext) {
|
||||
const action = {
|
||||
type: actionTypes.TELEMETRY_UNDESIRED_EVENT,
|
||||
data,
|
||||
data
|
||||
};
|
||||
return importContext === UI_CODE ? AlsoToMain(action) : action;
|
||||
}
|
||||
@ -299,7 +296,7 @@ function UndesiredEvent(data, importContext = globalImportContext) {
|
||||
function PerfEvent(data, importContext = globalImportContext) {
|
||||
const action = {
|
||||
type: actionTypes.TELEMETRY_PERFORMANCE_EVENT,
|
||||
data,
|
||||
data
|
||||
};
|
||||
return importContext === UI_CODE ? AlsoToMain(action) : action;
|
||||
}
|
||||
@ -314,7 +311,7 @@ function PerfEvent(data, importContext = globalImportContext) {
|
||||
function ImpressionStats(data, importContext = globalImportContext) {
|
||||
const action = {
|
||||
type: actionTypes.TELEMETRY_IMPRESSION_STATS,
|
||||
data,
|
||||
data
|
||||
};
|
||||
return importContext === UI_CODE ? AlsoToMain(action) : action;
|
||||
}
|
||||
@ -348,7 +345,7 @@ this.actionCreators = {
|
||||
OnlyToMain,
|
||||
AlsoToPreloaded,
|
||||
SetPref,
|
||||
WebExtEvent,
|
||||
WebExtEvent
|
||||
};
|
||||
|
||||
// These are helpers to test for certain kinds of actions
|
||||
@ -394,7 +391,7 @@ this.actionUtils = {
|
||||
getPortIdOfSender(action) {
|
||||
return (action.meta && action.meta.fromTarget) || null;
|
||||
},
|
||||
_RouteMessage,
|
||||
_RouteMessage
|
||||
};
|
||||
|
||||
const EXPORTED_SYMBOLS = [
|
||||
@ -407,5 +404,5 @@ const EXPORTED_SYMBOLS = [
|
||||
"BACKGROUND_PROCESS",
|
||||
"MAIN_MESSAGE_TYPE",
|
||||
"CONTENT_MESSAGE_TYPE",
|
||||
"PRELOAD_MESSAGE_TYPE",
|
||||
"PRELOAD_MESSAGE_TYPE"
|
||||
];
|
||||
|
@ -22,7 +22,7 @@ if (typeof Services !== "undefined") {
|
||||
// task.
|
||||
usablePerfObj = {
|
||||
now() {},
|
||||
mark() {},
|
||||
mark() {}
|
||||
};
|
||||
}
|
||||
|
||||
@ -118,7 +118,7 @@ _PerfService.prototype = {
|
||||
|
||||
let mostRecentEntry = entries[entries.length - 1];
|
||||
return this._perf.timeOrigin + mostRecentEntry.startTime;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
this.perfService = new _PerfService();
|
||||
|
@ -63,7 +63,7 @@ this.PrerenderData = new _PrerenderData({
|
||||
"feeds.section.topstories": true,
|
||||
"feeds.section.highlights": true,
|
||||
"sectionOrder": "topsites,topstories,highlights",
|
||||
"collapsed": false,
|
||||
"collapsed": false
|
||||
},
|
||||
// Prefs listed as invalidating will prevent the prerendered version
|
||||
// of AS from being used if their value is something other than what is listed
|
||||
@ -81,7 +81,7 @@ this.PrerenderData = new _PrerenderData({
|
||||
{oneOf: ["feeds.section.topstories", "feeds.section.highlights"]},
|
||||
// If any component has the following preference set to `true` it will
|
||||
// invalidate the prerendered version.
|
||||
{indexedDB: ["collapsed"]},
|
||||
{indexedDB: ["collapsed"]}
|
||||
],
|
||||
initialSections: [
|
||||
{
|
||||
@ -89,16 +89,16 @@ this.PrerenderData = new _PrerenderData({
|
||||
icon: "pocket",
|
||||
id: "topstories",
|
||||
order: 1,
|
||||
title: {id: "header_recommended_by", values: {provider: "Pocket"}},
|
||||
title: {id: "header_recommended_by", values: {provider: "Pocket"}}
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
id: "highlights",
|
||||
icon: "highlights",
|
||||
order: 2,
|
||||
title: {id: "header_highlights"},
|
||||
},
|
||||
],
|
||||
title: {id: "header_highlights"}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
this._PrerenderData = _PrerenderData;
|
||||
|
@ -14,12 +14,7 @@ const dedupe = new Dedupe(site => site && site.url);
|
||||
const INITIAL_STATE = {
|
||||
App: {
|
||||
// Have we received real data from the app yet?
|
||||
initialized: false,
|
||||
},
|
||||
ASRouter: {
|
||||
initialized: false,
|
||||
allowLegacyOnboarding: null,
|
||||
allowLegacySnippets: null,
|
||||
initialized: false
|
||||
},
|
||||
Snippets: {initialized: false},
|
||||
TopSites: {
|
||||
@ -32,22 +27,22 @@ const INITIAL_STATE = {
|
||||
// Used in content only to open the SearchShortcutsForm modal.
|
||||
showSearchShortcutsForm: false,
|
||||
// The list of available search shortcuts.
|
||||
searchShortcuts: [],
|
||||
searchShortcuts: []
|
||||
},
|
||||
Prefs: {
|
||||
initialized: false,
|
||||
values: {},
|
||||
values: {}
|
||||
},
|
||||
Dialog: {
|
||||
visible: false,
|
||||
data: {},
|
||||
data: {}
|
||||
},
|
||||
Sections: [],
|
||||
Pocket: {
|
||||
isUserLoggedIn: null,
|
||||
isUserLoggedIn: false,
|
||||
pocketCta: {},
|
||||
waitingForSpoc: true,
|
||||
},
|
||||
waitingForSpoc: true
|
||||
}
|
||||
};
|
||||
|
||||
function App(prevState = INITIAL_STATE.App, action) {
|
||||
@ -59,17 +54,6 @@ function App(prevState = INITIAL_STATE.App, action) {
|
||||
}
|
||||
}
|
||||
|
||||
function ASRouter(prevState = INITIAL_STATE.ASRouter, action) {
|
||||
switch (action.type) {
|
||||
case at.AS_ROUTER_INITIALIZED:
|
||||
return {...action.data, initialized: true};
|
||||
case at.AS_ROUTER_PREF_CHANGED:
|
||||
return {...prevState, ...action.data};
|
||||
default:
|
||||
return prevState;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* insertPinned - Inserts pinned links in their specified slots
|
||||
*
|
||||
@ -118,8 +102,8 @@ function TopSites(prevState = INITIAL_STATE.TopSites, action) {
|
||||
return Object.assign({}, prevState, {
|
||||
editForm: {
|
||||
index: action.data.index,
|
||||
previewResponse: null,
|
||||
},
|
||||
previewResponse: null
|
||||
}
|
||||
});
|
||||
case at.TOP_SITES_CANCEL_EDIT:
|
||||
return Object.assign({}, prevState, {editForm: null});
|
||||
@ -135,8 +119,8 @@ function TopSites(prevState = INITIAL_STATE.TopSites, action) {
|
||||
editForm: {
|
||||
index: prevState.editForm.index,
|
||||
previewResponse: action.data.preview,
|
||||
previewUrl: action.data.url,
|
||||
},
|
||||
previewUrl: action.data.url
|
||||
}
|
||||
});
|
||||
case at.PREVIEW_REQUEST:
|
||||
if (!prevState.editForm) {
|
||||
@ -146,8 +130,8 @@ function TopSites(prevState = INITIAL_STATE.TopSites, action) {
|
||||
editForm: {
|
||||
index: prevState.editForm.index,
|
||||
previewResponse: null,
|
||||
previewUrl: action.data.url,
|
||||
},
|
||||
previewUrl: action.data.url
|
||||
}
|
||||
});
|
||||
case at.PREVIEW_REQUEST_CANCEL:
|
||||
if (!prevState.editForm) {
|
||||
@ -156,8 +140,8 @@ function TopSites(prevState = INITIAL_STATE.TopSites, action) {
|
||||
return Object.assign({}, prevState, {
|
||||
editForm: {
|
||||
index: prevState.editForm.index,
|
||||
previewResponse: null,
|
||||
},
|
||||
previewResponse: null
|
||||
}
|
||||
});
|
||||
case at.SCREENSHOT_UPDATED:
|
||||
newRows = prevState.rows.map(row => {
|
||||
@ -203,8 +187,6 @@ function TopSites(prevState = INITIAL_STATE.TopSites, action) {
|
||||
return Object.assign({}, prevState, {rows: newRows});
|
||||
case at.UPDATE_SEARCH_SHORTCUTS:
|
||||
return {...prevState, searchShortcuts: action.data.searchShortcuts};
|
||||
case at.SNIPPETS_PREVIEW_MODE:
|
||||
return {...prevState, rows: []};
|
||||
default:
|
||||
return prevState;
|
||||
}
|
||||
@ -330,11 +312,11 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
|
||||
bookmarkGuid,
|
||||
bookmarkTitle,
|
||||
bookmarkDateCreated: dateAdded,
|
||||
type: "bookmark",
|
||||
type: "bookmark"
|
||||
});
|
||||
}
|
||||
return item;
|
||||
}),
|
||||
})
|
||||
}));
|
||||
case at.PLACES_SAVED_TO_POCKET:
|
||||
if (!action.data) {
|
||||
@ -347,11 +329,11 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
|
||||
open_url: action.data.open_url,
|
||||
pocket_id: action.data.pocket_id,
|
||||
title: action.data.title,
|
||||
type: "pocket",
|
||||
type: "pocket"
|
||||
});
|
||||
}
|
||||
return item;
|
||||
}),
|
||||
})
|
||||
}));
|
||||
case at.PLACES_BOOKMARK_REMOVED:
|
||||
if (!action.data) {
|
||||
@ -371,7 +353,7 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
|
||||
return newSite;
|
||||
}
|
||||
return item;
|
||||
}),
|
||||
})
|
||||
}));
|
||||
case at.PLACES_LINK_DELETED:
|
||||
case at.PLACES_LINK_BLOCKED:
|
||||
@ -384,8 +366,6 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
|
||||
case at.ARCHIVE_FROM_POCKET:
|
||||
return prevState.map(section =>
|
||||
Object.assign({}, section, {rows: section.rows.filter(site => site.pocket_id !== action.data.pocket_id)}));
|
||||
case at.SNIPPETS_PREVIEW_MODE:
|
||||
return prevState.map(section => ({...section, rows: []}));
|
||||
default:
|
||||
return prevState;
|
||||
}
|
||||
@ -419,8 +399,8 @@ function Pocket(prevState = INITIAL_STATE.Pocket, action) {
|
||||
ctaButton: action.data.cta_button,
|
||||
ctaText: action.data.cta_text,
|
||||
ctaUrl: action.data.cta_url,
|
||||
useCta: action.data.use_cta,
|
||||
},
|
||||
useCta: action.data.use_cta
|
||||
}
|
||||
};
|
||||
default:
|
||||
return prevState;
|
||||
@ -431,6 +411,6 @@ this.INITIAL_STATE = INITIAL_STATE;
|
||||
this.TOP_SITES_DEFAULT_ROWS = TOP_SITES_DEFAULT_ROWS;
|
||||
this.TOP_SITES_MAX_SITES_PER_ROW = TOP_SITES_MAX_SITES_PER_ROW;
|
||||
|
||||
this.reducers = {TopSites, App, ASRouter, Snippets, Prefs, Dialog, Sections, Pocket};
|
||||
this.reducers = {TopSites, App, Snippets, Prefs, Dialog, Sections, Pocket};
|
||||
|
||||
const EXPORTED_SYMBOLS = ["reducers", "INITIAL_STATE", "insertPinned", "TOP_SITES_DEFAULT_ROWS", "TOP_SITES_MAX_SITES_PER_ROW"];
|
||||
|
@ -40,6 +40,6 @@ export function prerender(locale, strings,
|
||||
return {
|
||||
html,
|
||||
state: store.getState(),
|
||||
store,
|
||||
store
|
||||
};
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ export const ASRouterUtils = {
|
||||
executeAction(button_action) {
|
||||
ASRouterUtils.sendMessage({
|
||||
type: "USER_ACTION",
|
||||
data: button_action,
|
||||
data: button_action
|
||||
});
|
||||
},
|
||||
unblockById(id) {
|
||||
@ -57,13 +57,13 @@ export const ASRouterUtils = {
|
||||
const endpoint = new URL(params.get("endpoint"));
|
||||
return {
|
||||
url: endpoint.href,
|
||||
snippetId: params.get("snippetId"),
|
||||
snippetId: params.get("snippetId")
|
||||
};
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Note: nextProps/prevProps refer to props passed to <ImpressionsWrapper />, not <ASRouterUISurface />
|
||||
@ -84,7 +84,7 @@ const ALLOWED_TAGS = {
|
||||
u: <u />,
|
||||
strong: <strong />,
|
||||
em: <em />,
|
||||
br: <br />,
|
||||
br: <br />
|
||||
};
|
||||
|
||||
/**
|
||||
@ -133,7 +133,7 @@ export class ASRouterUISurface extends React.PureComponent {
|
||||
message_id: message.id || extraProps.message_id,
|
||||
source: extraProps.id,
|
||||
action: eventType,
|
||||
...extraProps,
|
||||
...extraProps
|
||||
});
|
||||
}
|
||||
|
||||
@ -150,12 +150,9 @@ export class ASRouterUISurface extends React.PureComponent {
|
||||
value: event.target.dataset.metric,
|
||||
// Used for the `source` of the event. Needed to differentiate
|
||||
// from other snippet or onboarding events that may occur.
|
||||
id: "NEWTAB_FOOTER_BAR_CONTENT",
|
||||
id: "NEWTAB_FOOTER_BAR_CONTENT"
|
||||
};
|
||||
this.sendUserActionTelemetry({event: "CLICK_BUTTON", ...metric});
|
||||
if (!this.state.message.content.do_not_autoblock) {
|
||||
ASRouterUtils.blockById(this.state.message.id);
|
||||
}
|
||||
}
|
||||
|
||||
onBlockById(id) {
|
||||
@ -245,28 +242,11 @@ export class ASRouterUISurface extends React.PureComponent {
|
||||
sendUserActionTelemetry={this.sendUserActionTelemetry} />);
|
||||
}
|
||||
|
||||
renderPreviewBanner() {
|
||||
if (this.state.message.provider !== "preview") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="snippets-preview-banner">
|
||||
<span className="icon icon-small-spacer icon-info" />
|
||||
<span>Preview Purposes Only</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {message, bundle} = this.state;
|
||||
if (!message.id && !bundle.template) { return null; }
|
||||
return (
|
||||
<div>
|
||||
{this.renderPreviewBanner()}
|
||||
{bundle.template === "onboarding" ? this.renderOnboarding() : this.renderSnippets()}
|
||||
</div>
|
||||
);
|
||||
if (bundle.template === "onboarding") { return this.renderOnboarding(); }
|
||||
return this.renderSnippets();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,5 +56,5 @@ export class ImpressionsWrapper extends React.PureComponent {
|
||||
|
||||
ImpressionsWrapper.defaultProps = {
|
||||
document: global.document,
|
||||
sendOnMount: true,
|
||||
sendOnMount: true
|
||||
};
|
||||
|
@ -56,18 +56,3 @@
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.snippets-preview-banner {
|
||||
font-size: 15px;
|
||||
line-height: 42px;
|
||||
color: $grey-60-70;
|
||||
background: $grey-30-60;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
|
||||
span {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ Field name | Type | Required | Description | Example / Note
|
||||
title: "Find it faster",
|
||||
body: "Access all of your favorite search engines with a click. Search the whole Web or just one website from the search box."
|
||||
},
|
||||
targeting: "usesFirefoxSync && !addonsInfo.addons['activity-stream@mozilla.org']",
|
||||
targeting: "hasFxAccount && !addonsInfo.addons['activity-stream@mozilla.org']",
|
||||
frequency: {
|
||||
lifetime: 20,
|
||||
custom: [{period: "daily", cap: 5}, {period: 3600000, cap: 1}]
|
||||
@ -90,7 +90,7 @@ Name | Type | Example value | Description
|
||||
--- | --- | --- | ---
|
||||
`addonsInfo` | `Object` | [example below](#addonsinfo-example) | Information about the addons the user has installed
|
||||
`devToolsOpenedCount` | `Integer` | Number of usages of the web console or scratchpad
|
||||
`usesFirefoxSync` | `Boolean` | `true` | Does the user have a firefox account
|
||||
`hasFxAccount` | `Boolean` | `true` | Does the user have a firefox account
|
||||
`isDefaultBrowser` | `Boolean` or `null` | Is Firefox the user's default browser? If we could not determine the default browser, this value is `null`
|
||||
`profileAgeCreated` | Number | `1522843725924` | Profile creation timestamp
|
||||
`profileAgeReset` | `Number` or `undefined` | `1522843725924` | When (if) the profile was reset
|
||||
@ -98,10 +98,6 @@ Name | Type | Example value | Description
|
||||
`searchEngines` | `Object` | [example below](#searchengines-example) | Information about the current and available search engines
|
||||
`browserSettings.attribution` | `Object` or `undefined` | [example below](#attribution-example) | Attribution for the source of of where the browser was downloaded.
|
||||
`providerCohorts` | `Object` | `{onboarding: "hello"}` | Cohorts defined for all providers
|
||||
`previousSessionEnd` | `Number` | `1536325802800` | Timestamp in milliseconds of previously closed session
|
||||
`totalBookmarksCount` | `Number` | `8` | Total number of bookmarks
|
||||
`firefoxVersion` | `Number` | `64` | The major Firefox version of the browser
|
||||
`region` | `String` | `US` | Country code retrieved from `location.services.mozilla.com` can be `""` if request did not finish, encountered an error
|
||||
#### addonsInfo Example
|
||||
|
||||
```javascript
|
||||
@ -157,14 +153,14 @@ Examples:
|
||||
"id": "7864",
|
||||
"content": {...},
|
||||
// simple equality check
|
||||
"targeting": "usesFirefoxSync == true"
|
||||
"targeting": "hasFxAccount == true"
|
||||
}
|
||||
|
||||
{
|
||||
"id": "7865",
|
||||
"content": {...},
|
||||
// using JEXL transforms and combining two attributes
|
||||
"targeting": "usesFirefoxSync == true && profileAgeCreated > '2018-01-07'|date"
|
||||
"targeting": "hasFxAccount == true && profileAgeCreated > '2018-01-07'|date"
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -2,7 +2,7 @@
|
||||
"title": "ProviderResponse",
|
||||
"description": "A response object for remote providers of AS Router",
|
||||
"type": "object",
|
||||
"version": "6.1.0",
|
||||
"version": "0.1.0",
|
||||
"properties": {
|
||||
"messages": {
|
||||
"type": "array",
|
||||
|
@ -8,7 +8,7 @@ export function safeURI(url) {
|
||||
"https:",
|
||||
"data:",
|
||||
"resource:",
|
||||
"chrome:",
|
||||
"chrome:"
|
||||
].includes(protocol);
|
||||
if (!isAllowed) {
|
||||
console.warn(`The protocol ${protocol} is not allowed for template URLs.`); // eslint-disable-line no-console
|
||||
|
@ -44,20 +44,8 @@
|
||||
"label": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"attributes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tooltiptext": {
|
||||
"type": "string",
|
||||
"description": "Text for button tooltip used to provide information about the doorhanger."
|
||||
}
|
||||
},
|
||||
"required": ["tooltiptext"]
|
||||
}
|
||||
},
|
||||
"required": ["attributes"]
|
||||
"type": "string",
|
||||
"description": "Text for button tooltip used to provide information about the doorhanger."
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
|
@ -13,7 +13,7 @@ class OnboardingCard extends React.PureComponent {
|
||||
event: "CLICK_BUTTON",
|
||||
message_id: props.id,
|
||||
id: props.UISurface,
|
||||
includeClientID: true,
|
||||
includeClientID: true
|
||||
};
|
||||
props.sendUserActionTelemetry(ping);
|
||||
props.onAction(props.content.button_action);
|
||||
|
@ -14,9 +14,6 @@ export class SimpleSnippet extends React.PureComponent {
|
||||
onButtonClick() {
|
||||
this.props.sendUserActionTelemetry({event: "CLICK_BUTTON", id: this.props.UISurface});
|
||||
this.props.onAction(this.props.content.button_action);
|
||||
if (!this.props.content.do_not_autoblock) {
|
||||
this.props.onBlock();
|
||||
}
|
||||
}
|
||||
|
||||
renderTitle() {
|
||||
|
@ -75,10 +75,6 @@
|
||||
"type": "boolean",
|
||||
"description": "To be used by fundraising only, increases height to roughly 120px. Defaults to false."
|
||||
},
|
||||
"do_not_autoblock": {
|
||||
"type": "boolean",
|
||||
"description": "Used to prevent blocking the snippet after the CTA (link or button) has been clicked"
|
||||
},
|
||||
"links": {
|
||||
"additionalProperties": {
|
||||
"url": {
|
||||
|
@ -82,18 +82,8 @@ export class ASRouterAdmin extends React.PureComponent {
|
||||
</tbody></table>);
|
||||
}
|
||||
|
||||
renderTableHead() {
|
||||
return (<thead>
|
||||
<tr className="message-item">
|
||||
<td>id</td>
|
||||
<td>source</td>
|
||||
<td>last updated</td>
|
||||
</tr>
|
||||
</thead>);
|
||||
}
|
||||
|
||||
renderProviders() {
|
||||
return (<table>{this.renderTableHead()}<tbody>
|
||||
return (<table><tbody>
|
||||
{this.state.providers.map((provider, i) => {
|
||||
let label = "(local)";
|
||||
if (provider.type === "remote") {
|
||||
@ -104,7 +94,6 @@ export class ASRouterAdmin extends React.PureComponent {
|
||||
return (<tr className="message-item" key={i}>
|
||||
<td>{provider.id}</td>
|
||||
<td>{label}</td>
|
||||
<td>{provider.lastUpdated ? new Date(provider.lastUpdated).toString() : ""}</td>
|
||||
</tr>);
|
||||
})}
|
||||
</tbody></table>);
|
||||
|
@ -40,7 +40,8 @@ function debounce(func, wait) {
|
||||
|
||||
export class _Base extends React.PureComponent {
|
||||
componentWillMount() {
|
||||
const {locale} = this.props;
|
||||
const {App, locale} = this.props;
|
||||
this.sendNewTabRehydrated(App);
|
||||
addLocaleDataForReactIntl(locale);
|
||||
if (this.props.isFirstrun) {
|
||||
global.document.body.classList.add("welcome", "hide-main");
|
||||
@ -61,8 +62,29 @@ export class _Base extends React.PureComponent {
|
||||
this.updateTheme();
|
||||
}
|
||||
|
||||
componentWillUpdate() {
|
||||
hasTopStoriesSectionChanged(nextProps) {
|
||||
const nPropsSections = nextProps.Sections.find(section => section.id === "topstories");
|
||||
const tPropsSections = this.props.Sections.find(section => section.id === "topstories");
|
||||
if (nPropsSections && nPropsSections.options) {
|
||||
if (!tPropsSections || !tPropsSections.options) {
|
||||
return true;
|
||||
}
|
||||
if (nPropsSections.options.show_spocs !== tPropsSections.options.show_spocs) {
|
||||
return true;
|
||||
}
|
||||
if (nPropsSections.options.stories_endpoint !== tPropsSections.options.stories_endpoint) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
this.updateTheme();
|
||||
if (this.hasTopStoriesSectionChanged(nextProps)) {
|
||||
this.renderNotified = false;
|
||||
}
|
||||
this.sendNewTabRehydrated(nextProps.App);
|
||||
}
|
||||
|
||||
updateTheme() {
|
||||
@ -71,18 +93,28 @@ export class _Base extends React.PureComponent {
|
||||
// If we skipped the about:welcome overlay and removed the CSS classes
|
||||
// we don't want to add them back to the Activity Stream view
|
||||
document.body.classList.contains("welcome") ? "welcome" : "",
|
||||
document.body.classList.contains("hide-main") ? "hide-main" : "",
|
||||
document.body.classList.contains("hide-main") ? "hide-main" : ""
|
||||
].filter(v => v).join(" ");
|
||||
global.document.body.className = bodyClassName;
|
||||
}
|
||||
|
||||
// The NEW_TAB_REHYDRATED event is used to inform feeds that their
|
||||
// data has been consumed e.g. for counting the number of tabs that
|
||||
// have rendered that data.
|
||||
sendNewTabRehydrated(App) {
|
||||
if (App && App.initialized && !this.renderNotified) {
|
||||
this.props.dispatch(ac.AlsoToMain({type: at.NEW_TAB_REHYDRATED, data: {}}));
|
||||
this.renderNotified = true;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {props} = this;
|
||||
const {App, locale, strings} = props;
|
||||
const {initialized} = App;
|
||||
|
||||
const prefs = props.Prefs.values;
|
||||
if (prefs["asrouter.devtoolsEnabled"] && window.location.hash === "#asrouter") {
|
||||
if (prefs.asrouterExperimentEnabled && window.location.hash === "#asrouter") {
|
||||
return (<ASRouterAdmin />);
|
||||
}
|
||||
|
||||
@ -90,6 +122,17 @@ export class _Base extends React.PureComponent {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Until we can delete the existing onboarding tour, just hide the onboarding button when users are in
|
||||
// the new simplified onboarding experiment. CSS hacks ftw
|
||||
let isOnboardingEnabled = false;
|
||||
try {
|
||||
isOnboardingEnabled = JSON.parse(prefs["asrouter.messageProviders"]).find(i => i.id === "onboarding").enabled;
|
||||
} catch (e) {}
|
||||
|
||||
if (isOnboardingEnabled) {
|
||||
global.document.body.classList.add("hide-onboarding");
|
||||
}
|
||||
|
||||
return (<IntlProvider locale={locale} messages={strings}>
|
||||
<ErrorBoundary className="base-content-fallback">
|
||||
<BaseContent {...this.props} />
|
||||
@ -139,7 +182,7 @@ export class BaseContent extends React.PureComponent {
|
||||
const outerClassName = [
|
||||
"outer-wrapper",
|
||||
shouldBeFixedToTop && "fixed-to-top",
|
||||
prefs.showSearch && this.state.fixedSearch && "fixed-search",
|
||||
prefs.showSearch && this.state.fixedSearch && "fixed-search"
|
||||
].filter(v => v).join(" ");
|
||||
|
||||
return (
|
||||
|
@ -59,7 +59,7 @@ main {
|
||||
$selectors-to-hide: '
|
||||
.section-title,
|
||||
.sections-list .section:last-of-type,
|
||||
.topics
|
||||
.topic
|
||||
';
|
||||
|
||||
#{$selectors-to-hide} {
|
||||
|
@ -26,7 +26,7 @@ export class _Card extends React.PureComponent {
|
||||
activeCard: null,
|
||||
imageLoaded: false,
|
||||
showContextMenu: false,
|
||||
cardImage: null,
|
||||
cardImage: null
|
||||
};
|
||||
this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
|
||||
this.onMenuUpdate = this.onMenuUpdate.bind(this);
|
||||
@ -108,7 +108,7 @@ export class _Card extends React.PureComponent {
|
||||
event.preventDefault();
|
||||
this.setState({
|
||||
activeCard: this.props.index,
|
||||
showContextMenu: true,
|
||||
showContextMenu: true
|
||||
});
|
||||
}
|
||||
|
||||
@ -129,33 +129,33 @@ export class _Card extends React.PureComponent {
|
||||
if (this.props.link.type === "download") {
|
||||
this.props.dispatch(ac.OnlyToMain({
|
||||
type: at.SHOW_DOWNLOAD_FILE,
|
||||
data: this.props.link,
|
||||
data: this.props.link
|
||||
}));
|
||||
} else {
|
||||
const {altKey, button, ctrlKey, metaKey, shiftKey} = event;
|
||||
this.props.dispatch(ac.OnlyToMain({
|
||||
type: at.OPEN_LINK,
|
||||
data: Object.assign(this.props.link, {event: {altKey, button, ctrlKey, metaKey, shiftKey}}),
|
||||
data: Object.assign(this.props.link, {event: {altKey, button, ctrlKey, metaKey, shiftKey}})
|
||||
}));
|
||||
}
|
||||
if (this.props.isWebExtension) {
|
||||
this.props.dispatch(ac.WebExtEvent(at.WEBEXT_CLICK, {
|
||||
source: this.props.eventSource,
|
||||
url: this.props.link.url,
|
||||
action_position: this.props.index,
|
||||
action_position: this.props.index
|
||||
}));
|
||||
} else {
|
||||
this.props.dispatch(ac.UserEvent(Object.assign({
|
||||
event: "CLICK",
|
||||
source: this.props.eventSource,
|
||||
action_position: this.props.index,
|
||||
action_position: this.props.index
|
||||
}, this._getTelemetryInfo())));
|
||||
|
||||
if (this.props.shouldSendImpressionStats) {
|
||||
this.props.dispatch(ac.ImpressionStats({
|
||||
source: this.props.eventSource,
|
||||
click: 0,
|
||||
tiles: [{id: this.props.link.guid, pos: this.props.index}],
|
||||
tiles: [{id: this.props.link.guid, pos: this.props.index}]
|
||||
}));
|
||||
}
|
||||
}
|
||||
@ -209,7 +209,7 @@ export class _Card extends React.PureComponent {
|
||||
"card-outer",
|
||||
className,
|
||||
isContextMenuOpen && "active",
|
||||
props.placeholder && "placeholder",
|
||||
props.placeholder && "placeholder"
|
||||
].filter(v => v).join(" ");
|
||||
|
||||
return (<li className={outerClassName}>
|
||||
@ -231,7 +231,7 @@ export class _Card extends React.PureComponent {
|
||||
"card-text",
|
||||
icon ? "" : "no-context",
|
||||
link.description ? "" : "no-description",
|
||||
link.hostname ? "" : "no-host-name",
|
||||
link.hostname ? "" : "no-host-name"
|
||||
].join(" ")}>
|
||||
<h4 className="card-title" dir="auto">{link.title}</h4>
|
||||
<p className="card-description" dir="auto">{link.description}</p>
|
||||
|
@ -1,26 +1,26 @@
|
||||
export const cardContextTypes = {
|
||||
history: {
|
||||
intlID: "type_label_visited",
|
||||
icon: "history-item",
|
||||
icon: "history-item"
|
||||
},
|
||||
bookmark: {
|
||||
intlID: "type_label_bookmarked",
|
||||
icon: "bookmark-added",
|
||||
icon: "bookmark-added"
|
||||
},
|
||||
trending: {
|
||||
intlID: "type_label_recommended",
|
||||
icon: "trending",
|
||||
icon: "trending"
|
||||
},
|
||||
now: {
|
||||
intlID: "type_label_now",
|
||||
icon: "now",
|
||||
icon: "now"
|
||||
},
|
||||
pocket: {
|
||||
intlID: "type_label_pocket",
|
||||
icon: "pocket",
|
||||
icon: "pocket"
|
||||
},
|
||||
download: {
|
||||
intlID: "type_label_downloaded",
|
||||
icon: "download",
|
||||
},
|
||||
icon: "download"
|
||||
}
|
||||
};
|
||||
|
@ -69,13 +69,13 @@ export class _CollapsibleSection extends React.PureComponent {
|
||||
// Get the current height of the body so max-height transitions can work
|
||||
this.setState({
|
||||
isAnimating: true,
|
||||
maxHeight: `${this.sectionBody.scrollHeight}px`,
|
||||
maxHeight: `${this.sectionBody.scrollHeight}px`
|
||||
});
|
||||
const {action, userEvent} = SectionMenuOptions.CheckCollapsed(this.props);
|
||||
this.props.dispatch(action);
|
||||
this.props.dispatch(ac.UserEvent({
|
||||
event: userEvent,
|
||||
source: this.props.source,
|
||||
source: this.props.source
|
||||
}));
|
||||
}
|
||||
|
||||
@ -186,9 +186,9 @@ _CollapsibleSection.defaultProps = {
|
||||
document: global.document || {
|
||||
addEventListener: () => {},
|
||||
removeEventListener: () => {},
|
||||
visibilityState: "hidden",
|
||||
visibilityState: "hidden"
|
||||
},
|
||||
Prefs: {values: {}},
|
||||
Prefs: {values: {}}
|
||||
};
|
||||
|
||||
export const CollapsibleSection = injectIntl(_CollapsibleSection);
|
||||
|
@ -120,7 +120,7 @@ export class ComponentPerfTimer extends React.Component {
|
||||
this.props.dispatch(ac.OnlyToMain({
|
||||
type: at.SAVE_SESSION_PERF_DATA,
|
||||
// highlights_data_late_by_ms, topsites_data_late_by_ms.
|
||||
data: {[`${this.props.id}_data_late_by_ms`]: value},
|
||||
data: {[`${this.props.id}_data_late_by_ms`]: value}
|
||||
}));
|
||||
} catch (ex) {
|
||||
// If this failed, it's likely because the `privacy.resistFingerprinting`
|
||||
@ -144,7 +144,7 @@ export class ComponentPerfTimer extends React.Component {
|
||||
|
||||
this.props.dispatch(ac.OnlyToMain({
|
||||
type: at.SAVE_SESSION_PERF_DATA,
|
||||
data,
|
||||
data
|
||||
}));
|
||||
} catch (ex) {
|
||||
// If this failed, it's likely because the `privacy.resistFingerprinting`
|
||||
|
@ -25,7 +25,7 @@ export class _LinkMenu extends React.PureComponent {
|
||||
const userEventData = Object.assign({
|
||||
event: userEvent,
|
||||
source,
|
||||
action_position: index,
|
||||
action_position: index
|
||||
}, siteInfo);
|
||||
props.dispatch(ac.UserEvent(userEventData));
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ export class _SectionMenu extends React.PureComponent {
|
||||
if (userEvent) {
|
||||
props.dispatch(ac.UserEvent({
|
||||
event: userEvent,
|
||||
source: props.source,
|
||||
source: props.source
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {actionCreators as ac, actionTypes as at} from "common/Actions.jsm";
|
||||
import {Card, PlaceholderCard} from "content-src/components/Card/Card";
|
||||
import {FormattedMessage, injectIntl} from "react-intl";
|
||||
import {actionCreators as ac} from "common/Actions.jsm";
|
||||
import {CollapsibleSection} from "content-src/components/CollapsibleSection/CollapsibleSection";
|
||||
import {ComponentPerfTimer} from "content-src/components/ComponentPerfTimer/ComponentPerfTimer";
|
||||
import {connect} from "react-redux";
|
||||
@ -40,7 +40,7 @@ export class Section extends React.PureComponent {
|
||||
if (this.needsImpressionStats(cards)) {
|
||||
props.dispatch(ac.ImpressionStats({
|
||||
source: props.eventSource,
|
||||
tiles: cards.map(link => ({id: link.guid})),
|
||||
tiles: cards.map(link => ({id: link.guid}))
|
||||
}));
|
||||
this.impressionCardGuids = cards.map(link => link.guid);
|
||||
}
|
||||
@ -78,10 +78,6 @@ export class Section extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.sendNewTabRehydrated(this.props.initialized);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.rows.length && !this.props.pref.collapsed) {
|
||||
this.sendImpressionStatsOrAddListener();
|
||||
@ -107,10 +103,6 @@ export class Section extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
this.sendNewTabRehydrated(nextProps.initialized);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this._onVisibilityChange) {
|
||||
this.props.document.removeEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange);
|
||||
@ -131,22 +123,12 @@ export class Section extends React.PureComponent {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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(initialized) {
|
||||
if (initialized && !this.renderNotified) {
|
||||
this.props.dispatch(ac.AlsoToMain({type: at.NEW_TAB_REHYDRATED, data: {}}));
|
||||
this.renderNotified = true;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
id, eventSource, title, icon, rows, Pocket, topics,
|
||||
emptyState, dispatch, compactCards, read_more_endpoint,
|
||||
contextMenuOptions, initialized, learnMore,
|
||||
pref, privacyNoticeURL, isFirst, isLast,
|
||||
pref, privacyNoticeURL, isFirst, isLast
|
||||
} = this.props;
|
||||
|
||||
const waitingForSpoc = id === "topstories" && this.props.Pocket.waitingForSpoc;
|
||||
@ -155,21 +137,14 @@ export class Section extends React.PureComponent {
|
||||
const maxCards = maxCardsPerRow * numRows;
|
||||
const maxCardsOnNarrow = CARDS_PER_ROW_DEFAULT * numRows;
|
||||
|
||||
const {pocketCta, isUserLoggedIn} = Pocket || {};
|
||||
const {useCta} = pocketCta || {};
|
||||
|
||||
// Don't display anything until we have a definitve result from Pocket,
|
||||
// to avoid a flash of logged out state while we render.
|
||||
const isPocketLoggedInDefined = (isUserLoggedIn === true || isUserLoggedIn === false);
|
||||
|
||||
const shouldShowPocketCta = (id === "topstories" &&
|
||||
useCta && isUserLoggedIn === false);
|
||||
Pocket.pocketCta.useCta && !Pocket.isUserLoggedIn);
|
||||
|
||||
// Show topics only for top stories and if it has loaded with topics.
|
||||
// The classs .top-stories-bottom-container ensures content doesn't shift as things load.
|
||||
// Show topics only for top stories and if it's not initialized yet (so
|
||||
// content doesn't shift when it is loaded) or has loaded with topics
|
||||
const shouldShowTopics = (id === "topstories" &&
|
||||
(topics && topics.length > 0) &&
|
||||
((useCta && isUserLoggedIn === true) || (!useCta && isPocketLoggedInDefined)));
|
||||
(!topics || topics.length > 0) &&
|
||||
!shouldShowPocketCta);
|
||||
|
||||
const realRows = rows.slice(0, maxCards);
|
||||
|
||||
@ -208,7 +183,7 @@ export class Section extends React.PureComponent {
|
||||
|
||||
const sectionClassName = [
|
||||
"section",
|
||||
compactCards ? "compact-cards" : "normal-cards",
|
||||
compactCards ? "compact-cards" : "normal-cards"
|
||||
].join(" ");
|
||||
|
||||
// <Section> <-- React component
|
||||
@ -259,7 +234,7 @@ Section.defaultProps = {
|
||||
rows: [],
|
||||
emptyState: {},
|
||||
pref: {},
|
||||
title: "",
|
||||
title: ""
|
||||
};
|
||||
|
||||
export const SectionIntl = connect(state => ({Prefs: state.Prefs, Pocket: state.Pocket}))(injectIntl(Section));
|
||||
@ -276,7 +251,7 @@ export class _Sections extends React.PureComponent {
|
||||
const commonProps = {
|
||||
key: sectionId,
|
||||
isFirst: sections.length === 0,
|
||||
isLast: sections.length === expectedCount - 1,
|
||||
isLast: sections.length === expectedCount - 1
|
||||
};
|
||||
if (sectionId === "topsites" && showTopSites) {
|
||||
sections.push(<TopSites {...commonProps} />);
|
||||
|
@ -83,7 +83,6 @@
|
||||
|
||||
@media (min-width: $break-point-large) {
|
||||
line-height: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
// This is a clearfix to for the more-recommendations link which is floating and causes
|
||||
|
@ -17,7 +17,7 @@ export class _StartupOverlay extends React.PureComponent {
|
||||
emailInput: "",
|
||||
overlayRemoved: false,
|
||||
flowId: "",
|
||||
flowBeginTime: 0,
|
||||
flowBeginTime: 0
|
||||
};
|
||||
this.didFetch = false;
|
||||
}
|
||||
@ -33,7 +33,7 @@ export class _StartupOverlay extends React.PureComponent {
|
||||
this.setState({flowId, flowBeginTime});
|
||||
}
|
||||
} catch (error) {
|
||||
this.props.dispatch(ac.OnlyToMain({type: at.TELEMETRY_UNDESIRED_EVENT, data: {event: "FXA_METRICS_ERROR"}}));
|
||||
this.props.dispatch(ac.OnlyToMain({type: at.TELEMETRY_UNDESIRED_EVENT, data: {value: "FXA_METRICS_ERROR"}}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -124,7 +124,7 @@ export class _StartupOverlay extends React.PureComponent {
|
||||
id="firstrun_extra_legal_links"
|
||||
values={{
|
||||
terms: termsLink,
|
||||
privacy: privacyLink,
|
||||
privacy: privacyLink
|
||||
}} />
|
||||
</div>
|
||||
<button className="continue-button" type="submit"><FormattedMessage id="firstrun_continue_to_login" /></button>
|
||||
|
@ -41,7 +41,7 @@ export class SearchShortcutsForm extends React.PureComponent {
|
||||
searchShortcuts.forEach(shortcut => {
|
||||
shortcuts.push({
|
||||
...shortcut,
|
||||
isSelected: !!rows.find(row => row && row.isPinned && row.searchTopSite && row.label === shortcut.keyword),
|
||||
isSelected: !!rows.find(row => row && row.isPinned && row.searchTopSite && row.label === shortcut.keyword)
|
||||
});
|
||||
});
|
||||
this.state = {shortcuts};
|
||||
@ -84,8 +84,8 @@ export class SearchShortcutsForm extends React.PureComponent {
|
||||
type: at.UPDATE_PINNED_SEARCH_SHORTCUTS,
|
||||
data: {
|
||||
addedShortcuts: pinQueue,
|
||||
deletedShortcuts: unpinQueue,
|
||||
},
|
||||
deletedShortcuts: unpinQueue
|
||||
}
|
||||
}));
|
||||
|
||||
// Send the Telemetry pings.
|
||||
@ -93,14 +93,14 @@ export class SearchShortcutsForm extends React.PureComponent {
|
||||
this.props.dispatch(ac.UserEvent({
|
||||
source: TOP_SITES_SOURCE,
|
||||
event: "SEARCH_EDIT_ADD",
|
||||
value: {search_vendor: shortcut.searchVendor},
|
||||
value: {search_vendor: shortcut.searchVendor}
|
||||
}));
|
||||
});
|
||||
unpinQueue.forEach(shortcut => {
|
||||
this.props.dispatch(ac.UserEvent({
|
||||
source: TOP_SITES_SOURCE,
|
||||
event: "SEARCH_EDIT_DELETE",
|
||||
value: {search_vendor: shortcut.searchVendor},
|
||||
value: {search_vendor: shortcut.searchVendor}
|
||||
}));
|
||||
});
|
||||
|
||||
@ -112,7 +112,7 @@ export class SearchShortcutsForm extends React.PureComponent {
|
||||
url: shortcut.url,
|
||||
searchTopSite: true,
|
||||
label: shortcut.keyword,
|
||||
searchVendor: shortcut.shortURL,
|
||||
searchVendor: shortcut.shortURL
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
MIN_RICH_FAVICON_SIZE,
|
||||
TOP_SITES_CONTEXT_MENU_OPTIONS,
|
||||
TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS,
|
||||
TOP_SITES_SOURCE,
|
||||
TOP_SITES_SOURCE
|
||||
} from "./TopSitesConstants";
|
||||
import {LinkMenu} from "content-src/components/LinkMenu/LinkMenu";
|
||||
import React from "react";
|
||||
@ -139,7 +139,7 @@ export class TopSiteLink extends React.PureComponent {
|
||||
imageClassName = "top-site-icon rich-icon";
|
||||
imageStyle = {
|
||||
backgroundColor: link.backgroundColor,
|
||||
backgroundImage: `url(${tippyTopIcon})`,
|
||||
backgroundImage: `url(${tippyTopIcon})`
|
||||
};
|
||||
smallFaviconStyle = {backgroundImage: `url(${tippyTopIcon})`};
|
||||
} else if (link.customScreenshotURL) {
|
||||
@ -147,14 +147,14 @@ export class TopSiteLink extends React.PureComponent {
|
||||
imageClassName = "top-site-icon rich-icon";
|
||||
imageStyle = {
|
||||
backgroundColor: link.backgroundColor,
|
||||
backgroundImage: hasScreenshotImage ? `url(${this.state.screenshotImage.url})` : "none",
|
||||
backgroundImage: hasScreenshotImage ? `url(${this.state.screenshotImage.url})` : "none"
|
||||
};
|
||||
} else if (tippyTopIcon || faviconSize >= MIN_RICH_FAVICON_SIZE) {
|
||||
// styles and class names for top sites with rich icons
|
||||
imageClassName = "top-site-icon rich-icon";
|
||||
imageStyle = {
|
||||
backgroundColor: link.backgroundColor,
|
||||
backgroundImage: `url(${tippyTopIcon || link.favicon})`,
|
||||
backgroundImage: `url(${tippyTopIcon || link.favicon})`
|
||||
};
|
||||
} else {
|
||||
// styles and class names for top sites with screenshot + small icon in top left corner
|
||||
@ -178,7 +178,7 @@ export class TopSiteLink extends React.PureComponent {
|
||||
onClick: this.onDragEvent,
|
||||
onDragEnd: this.onDragEvent,
|
||||
onDragStart: this.onDragEvent,
|
||||
onMouseDown: this.onDragEvent,
|
||||
onMouseDown: this.onDragEvent
|
||||
};
|
||||
}
|
||||
return (<li className={topSiteOuterClassName} onDrop={this.onDragEvent} onDragOver={this.onDragEvent} onDragEnter={this.onDragEvent} onDragLeave={this.onDragEvent} {...draggableProps}>
|
||||
@ -205,7 +205,7 @@ export class TopSiteLink extends React.PureComponent {
|
||||
TopSiteLink.defaultProps = {
|
||||
title: "",
|
||||
link: {},
|
||||
isDraggable: true,
|
||||
isDraggable: true
|
||||
};
|
||||
|
||||
export class TopSite extends React.PureComponent {
|
||||
@ -238,7 +238,7 @@ export class TopSite extends React.PureComponent {
|
||||
this.props.dispatch(ac.UserEvent(Object.assign({
|
||||
event,
|
||||
source: TOP_SITES_SOURCE,
|
||||
action_position: this.props.index,
|
||||
action_position: this.props.index
|
||||
}, this._getTelemetryInfo())));
|
||||
}
|
||||
|
||||
@ -252,12 +252,12 @@ export class TopSite extends React.PureComponent {
|
||||
if (!this.props.link.searchTopSite) {
|
||||
this.props.dispatch(ac.OnlyToMain({
|
||||
type: at.OPEN_LINK,
|
||||
data: Object.assign(this.props.link, {event: {altKey, button, ctrlKey, metaKey, shiftKey}}),
|
||||
data: Object.assign(this.props.link, {event: {altKey, button, ctrlKey, metaKey, shiftKey}})
|
||||
}));
|
||||
} else {
|
||||
this.props.dispatch(ac.OnlyToMain({
|
||||
type: at.FILL_SEARCH_TERM,
|
||||
data: {label: this.props.link.label},
|
||||
data: {label: this.props.link.label}
|
||||
}));
|
||||
}
|
||||
}
|
||||
@ -300,7 +300,7 @@ export class TopSite extends React.PureComponent {
|
||||
}
|
||||
TopSite.defaultProps = {
|
||||
link: {},
|
||||
onActivate() {},
|
||||
onActivate() {}
|
||||
};
|
||||
|
||||
export class TopSitePlaceholder extends React.PureComponent {
|
||||
@ -330,7 +330,7 @@ export class _TopSiteList extends React.PureComponent {
|
||||
draggedIndex: null,
|
||||
draggedSite: null,
|
||||
draggedTitle: null,
|
||||
topSitesPreview: null,
|
||||
topSitesPreview: null
|
||||
};
|
||||
}
|
||||
|
||||
@ -358,7 +358,7 @@ export class _TopSiteList extends React.PureComponent {
|
||||
this.props.dispatch(ac.UserEvent({
|
||||
event,
|
||||
source: TOP_SITES_SOURCE,
|
||||
action_position: index,
|
||||
action_position: index
|
||||
}));
|
||||
}
|
||||
|
||||
@ -370,7 +370,7 @@ export class _TopSiteList extends React.PureComponent {
|
||||
draggedIndex: index,
|
||||
draggedSite: link,
|
||||
draggedTitle: title,
|
||||
activeIndex: null,
|
||||
activeIndex: null
|
||||
});
|
||||
this.userEvent("DRAG", index);
|
||||
break;
|
||||
@ -398,11 +398,11 @@ export class _TopSiteList extends React.PureComponent {
|
||||
label: this.state.draggedTitle,
|
||||
customScreenshotURL: this.state.draggedSite.customScreenshotURL,
|
||||
// Only if the search topsites experiment is enabled
|
||||
...(this.state.draggedSite.searchTopSite && {searchTopSite: true}),
|
||||
...(this.state.draggedSite.searchTopSite && {searchTopSite: true})
|
||||
},
|
||||
index,
|
||||
draggedFromIndex: this.state.draggedIndex,
|
||||
},
|
||||
draggedFromIndex: this.state.draggedIndex
|
||||
}
|
||||
}));
|
||||
this.userEvent("DROP", index);
|
||||
}
|
||||
@ -470,7 +470,7 @@ export class _TopSiteList extends React.PureComponent {
|
||||
const commonProps = {
|
||||
onDragEvent: this.onDragEvent,
|
||||
dispatch: props.dispatch,
|
||||
intl: props.intl,
|
||||
intl: props.intl
|
||||
};
|
||||
// We assign a key to each placeholder slot. We need it to be independent
|
||||
// of the slot index (i below) so that the keys used stay the same during
|
||||
@ -486,7 +486,7 @@ export class _TopSiteList extends React.PureComponent {
|
||||
const link = topSites[i] && Object.assign({}, topSites[i], {iconType: this.props.topSiteIconType(topSites[i])});
|
||||
const slotProps = {
|
||||
key: link ? link.url : holeIndex++,
|
||||
index: i,
|
||||
index: i
|
||||
};
|
||||
if (i >= maxNarrowVisibleIndex) {
|
||||
slotProps.className = "hide-for-narrow";
|
||||
|
@ -14,7 +14,7 @@ export class TopSiteForm extends React.PureComponent {
|
||||
url: site ? site.url : "",
|
||||
validationError: false,
|
||||
customScreenshotUrl: site ? site.customScreenshotURL : "",
|
||||
showCustomScreenshotForm: site ? site.customScreenshotURL : false,
|
||||
showCustomScreenshotForm: site ? site.customScreenshotURL : false
|
||||
};
|
||||
this.onClearScreenshotInput = this.onClearScreenshotInput.bind(this);
|
||||
this.onLabelChange = this.onLabelChange.bind(this);
|
||||
@ -35,14 +35,14 @@ export class TopSiteForm extends React.PureComponent {
|
||||
onUrlChange(event) {
|
||||
this.setState({
|
||||
url: event.target.value,
|
||||
validationError: false,
|
||||
validationError: false
|
||||
});
|
||||
}
|
||||
|
||||
onClearUrlClick() {
|
||||
this.setState({
|
||||
url: "",
|
||||
validationError: false,
|
||||
validationError: false
|
||||
});
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ export class TopSiteForm extends React.PureComponent {
|
||||
_updateCustomScreenshotInput(customScreenshotUrl) {
|
||||
this.setState({
|
||||
customScreenshotUrl,
|
||||
validationError: false,
|
||||
validationError: false
|
||||
});
|
||||
this.props.dispatch({type: at.PREVIEW_REQUEST_CANCEL});
|
||||
}
|
||||
@ -89,12 +89,12 @@ export class TopSiteForm extends React.PureComponent {
|
||||
}
|
||||
this.props.dispatch(ac.AlsoToMain({
|
||||
type: at.TOP_SITES_PIN,
|
||||
data: {site, index},
|
||||
data: {site, index}
|
||||
}));
|
||||
this.props.dispatch(ac.UserEvent({
|
||||
source: TOP_SITES_SOURCE,
|
||||
event: "TOP_SITES_EDIT",
|
||||
action_position: index,
|
||||
action_position: index
|
||||
}));
|
||||
|
||||
this.props.onClose();
|
||||
@ -106,11 +106,11 @@ export class TopSiteForm extends React.PureComponent {
|
||||
if (this.validateForm()) {
|
||||
this.props.dispatch(ac.AlsoToMain({
|
||||
type: at.PREVIEW_REQUEST,
|
||||
data: {url: this.cleanUrl(this.state.customScreenshotUrl)},
|
||||
data: {url: this.cleanUrl(this.state.customScreenshotUrl)}
|
||||
}));
|
||||
this.props.dispatch(ac.UserEvent({
|
||||
source: TOP_SITES_SOURCE,
|
||||
event: "PREVIEW_REQUEST",
|
||||
event: "PREVIEW_REQUEST"
|
||||
}));
|
||||
}
|
||||
}
|
||||
@ -247,5 +247,5 @@ export class TopSiteForm extends React.PureComponent {
|
||||
|
||||
TopSiteForm.defaultProps = {
|
||||
site: null,
|
||||
index: -1,
|
||||
index: -1
|
||||
};
|
||||
|
@ -62,5 +62,5 @@ export class TopSiteFormInput extends React.PureComponent {
|
||||
TopSiteFormInput.defaultProps = {
|
||||
showClearButton: false,
|
||||
value: "",
|
||||
validationError: false,
|
||||
validationError: false
|
||||
};
|
||||
|
@ -46,7 +46,7 @@ function countTopSitesIconsTypes(topSites) {
|
||||
"screenshot": 0,
|
||||
"tippytop": 0,
|
||||
"rich_icon": 0,
|
||||
"no_image": 0,
|
||||
"no_image": 0
|
||||
});
|
||||
}
|
||||
|
||||
@ -71,8 +71,8 @@ export class _TopSites extends React.PureComponent {
|
||||
data: {
|
||||
topsites_icon_stats: topSitesIconsStats,
|
||||
topsites_pinned: topSitesPinned,
|
||||
topsites_search_shortcuts: searchShortcuts,
|
||||
},
|
||||
topsites_search_shortcuts: searchShortcuts
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ export class _TopSites extends React.PureComponent {
|
||||
onEditFormClose() {
|
||||
this.props.dispatch(ac.UserEvent({
|
||||
source: TOP_SITES_SOURCE,
|
||||
event: "TOP_SITES_EDIT_CLOSE",
|
||||
event: "TOP_SITES_EDIT_CLOSE"
|
||||
}));
|
||||
this.props.dispatch({type: at.TOP_SITES_CANCEL_EDIT});
|
||||
}
|
||||
@ -108,7 +108,7 @@ export class _TopSites extends React.PureComponent {
|
||||
onSearchShortcutsFormClose() {
|
||||
this.props.dispatch(ac.UserEvent({
|
||||
source: TOP_SITES_SOURCE,
|
||||
event: "SEARCH_EDIT_CLOSE",
|
||||
event: "SEARCH_EDIT_CLOSE"
|
||||
}));
|
||||
this.props.dispatch({type: at.TOP_SITES_CLOSE_SEARCH_SHORTCUTS_MODAL});
|
||||
}
|
||||
@ -169,5 +169,5 @@ export class _TopSites extends React.PureComponent {
|
||||
export const TopSites = connect(state => ({
|
||||
TopSites: state.TopSites,
|
||||
Prefs: state.Prefs,
|
||||
TopSitesRows: state.Prefs.values.topSitesRows,
|
||||
TopSitesRows: state.Prefs.values.topSitesRows
|
||||
}))(injectIntl(_TopSites));
|
||||
|
@ -1,25 +1,11 @@
|
||||
export function enableASRouterContent(store, asrouterContent) {
|
||||
let didHideOnboarding = false;
|
||||
|
||||
// Enable asrouter content
|
||||
store.subscribe(() => {
|
||||
const state = store.getState();
|
||||
if (!state.ASRouter.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!asrouterContent.initialized) {
|
||||
if (state.Prefs.values.asrouterExperimentEnabled && !asrouterContent.initialized) {
|
||||
asrouterContent.init();
|
||||
}
|
||||
|
||||
// Until we can delete the existing onboarding tour, just hide the onboarding button when users are in
|
||||
// the new simplified onboarding experiment. CSS hacks ftw
|
||||
if (state.ASRouter.allowLegacyOnboarding === false && !didHideOnboarding) {
|
||||
global.document.body.classList.add("hide-onboarding");
|
||||
didHideOnboarding = true;
|
||||
} else if (state.ASRouter.allowLegacyOnboarding === true && didHideOnboarding) {
|
||||
global.document.body.classList.remove("hide-onboarding");
|
||||
didHideOnboarding = false;
|
||||
} else if (!state.Prefs.values.asrouterExperimentEnabled && asrouterContent.initialized) {
|
||||
asrouterContent.uninit();
|
||||
}
|
||||
});
|
||||
// Return this for testing purposes
|
||||
|
@ -44,7 +44,7 @@ export class DetectUserSessionStart {
|
||||
|
||||
this._store.dispatch(ac.AlsoToMain({
|
||||
type: at.SAVE_SESSION_PERF_DATA,
|
||||
data: {visibility_event_rcvd_ts},
|
||||
data: {visibility_event_rcvd_ts}
|
||||
}));
|
||||
} catch (ex) {
|
||||
// If this failed, it's likely because the `privacy.resistFingerprinting`
|
||||
|
@ -5,9 +5,9 @@ const _OpenInPrivateWindow = site => ({
|
||||
icon: "new-window-private",
|
||||
action: ac.OnlyToMain({
|
||||
type: at.OPEN_PRIVATE_WINDOW,
|
||||
data: {url: site.url, referrer: site.referrer},
|
||||
data: {url: site.url, referrer: site.referrer}
|
||||
}),
|
||||
userEvent: "OPEN_PRIVATE_WINDOW",
|
||||
userEvent: "OPEN_PRIVATE_WINDOW"
|
||||
});
|
||||
|
||||
export const GetPlatformString = platform => {
|
||||
@ -36,18 +36,18 @@ export const LinkMenuOptions = {
|
||||
icon: "bookmark-added",
|
||||
action: ac.AlsoToMain({
|
||||
type: at.DELETE_BOOKMARK_BY_ID,
|
||||
data: site.bookmarkGuid,
|
||||
data: site.bookmarkGuid
|
||||
}),
|
||||
userEvent: "BOOKMARK_DELETE",
|
||||
userEvent: "BOOKMARK_DELETE"
|
||||
}),
|
||||
AddBookmark: site => ({
|
||||
id: "menu_action_bookmark",
|
||||
icon: "bookmark-hollow",
|
||||
action: ac.AlsoToMain({
|
||||
type: at.BOOKMARK_URL,
|
||||
data: {url: site.url, title: site.title, type: site.type},
|
||||
data: {url: site.url, title: site.title, type: site.type}
|
||||
}),
|
||||
userEvent: "BOOKMARK_ADD",
|
||||
userEvent: "BOOKMARK_ADD"
|
||||
}),
|
||||
OpenInNewWindow: site => ({
|
||||
id: "menu_action_open_new_window",
|
||||
@ -57,24 +57,24 @@ export const LinkMenuOptions = {
|
||||
data: {
|
||||
referrer: site.referrer,
|
||||
typedBonus: site.typedBonus,
|
||||
url: site.url,
|
||||
},
|
||||
url: site.url
|
||||
}
|
||||
}),
|
||||
userEvent: "OPEN_NEW_WINDOW",
|
||||
userEvent: "OPEN_NEW_WINDOW"
|
||||
}),
|
||||
BlockUrl: (site, index, eventSource) => ({
|
||||
id: "menu_action_dismiss",
|
||||
icon: "dismiss",
|
||||
action: ac.AlsoToMain({
|
||||
type: at.BLOCK_URL,
|
||||
data: {url: site.open_url || site.url, pocket_id: site.pocket_id},
|
||||
data: {url: site.open_url || site.url, pocket_id: site.pocket_id}
|
||||
}),
|
||||
impression: ac.ImpressionStats({
|
||||
source: eventSource,
|
||||
block: 0,
|
||||
tiles: [{id: site.guid, pos: index}],
|
||||
tiles: [{id: site.guid, pos: index}]
|
||||
}),
|
||||
userEvent: "BLOCK",
|
||||
userEvent: "BLOCK"
|
||||
}),
|
||||
|
||||
// This is an option for web extentions which will result in remove items from
|
||||
@ -86,8 +86,8 @@ export const LinkMenuOptions = {
|
||||
action: ac.WebExtEvent(at.WEBEXT_DISMISS, {
|
||||
source: eventSource,
|
||||
url: site.url,
|
||||
action_position: index,
|
||||
}),
|
||||
action_position: index
|
||||
})
|
||||
}),
|
||||
DeleteUrl: (site, index, eventSource, isEnabled, siteInfo) => ({
|
||||
id: "menu_action_delete",
|
||||
@ -97,57 +97,57 @@ export const LinkMenuOptions = {
|
||||
data: {
|
||||
onConfirm: [
|
||||
ac.AlsoToMain({type: at.DELETE_HISTORY_URL, data: {url: site.url, pocket_id: site.pocket_id, forceBlock: site.bookmarkGuid}}),
|
||||
ac.UserEvent(Object.assign({event: "DELETE", source: eventSource, action_position: index}, siteInfo)),
|
||||
ac.UserEvent(Object.assign({event: "DELETE", source: eventSource, action_position: index}, siteInfo))
|
||||
],
|
||||
eventSource,
|
||||
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",
|
||||
},
|
||||
icon: "modal-delete"
|
||||
}
|
||||
},
|
||||
userEvent: "DIALOG_OPEN",
|
||||
userEvent: "DIALOG_OPEN"
|
||||
}),
|
||||
ShowFile: (site, index, eventSource, isEnabled, siteInfo, platform) => ({
|
||||
id: GetPlatformString(platform),
|
||||
icon: "search",
|
||||
action: ac.OnlyToMain({
|
||||
type: at.SHOW_DOWNLOAD_FILE,
|
||||
data: {url: site.url},
|
||||
}),
|
||||
data: {url: site.url}
|
||||
})
|
||||
}),
|
||||
OpenFile: site => ({
|
||||
id: "menu_action_open_file",
|
||||
icon: "open-file",
|
||||
action: ac.OnlyToMain({
|
||||
type: at.OPEN_DOWNLOAD_FILE,
|
||||
data: {url: site.url},
|
||||
}),
|
||||
data: {url: site.url}
|
||||
})
|
||||
}),
|
||||
CopyDownloadLink: site => ({
|
||||
id: "menu_action_copy_download_link",
|
||||
icon: "copy",
|
||||
action: ac.OnlyToMain({
|
||||
type: at.COPY_DOWNLOAD_LINK,
|
||||
data: {url: site.url},
|
||||
}),
|
||||
data: {url: site.url}
|
||||
})
|
||||
}),
|
||||
GoToDownloadPage: site => ({
|
||||
id: "menu_action_go_to_download_page",
|
||||
icon: "download",
|
||||
action: ac.OnlyToMain({
|
||||
type: at.OPEN_LINK,
|
||||
data: {url: site.referrer},
|
||||
data: {url: site.referrer}
|
||||
}),
|
||||
disabled: !site.referrer,
|
||||
disabled: !site.referrer
|
||||
}),
|
||||
RemoveDownload: site => ({
|
||||
id: "menu_action_remove_download",
|
||||
icon: "delete",
|
||||
action: ac.OnlyToMain({
|
||||
type: at.REMOVE_DOWNLOAD_FILE,
|
||||
data: {url: site.url},
|
||||
}),
|
||||
data: {url: site.url}
|
||||
})
|
||||
}),
|
||||
PinTopSite: ({url, searchTopSite, label}, index) => ({
|
||||
id: "menu_action_pin",
|
||||
@ -157,65 +157,65 @@ export const LinkMenuOptions = {
|
||||
data: {
|
||||
site: {
|
||||
url,
|
||||
...(searchTopSite && {searchTopSite, label}),
|
||||
...(searchTopSite && {searchTopSite, label})
|
||||
},
|
||||
index,
|
||||
},
|
||||
index
|
||||
}
|
||||
}),
|
||||
userEvent: "PIN",
|
||||
userEvent: "PIN"
|
||||
}),
|
||||
UnpinTopSite: site => ({
|
||||
id: "menu_action_unpin",
|
||||
icon: "unpin",
|
||||
action: ac.AlsoToMain({
|
||||
type: at.TOP_SITES_UNPIN,
|
||||
data: {site: {url: site.url}},
|
||||
data: {site: {url: site.url}}
|
||||
}),
|
||||
userEvent: "UNPIN",
|
||||
userEvent: "UNPIN"
|
||||
}),
|
||||
SaveToPocket: (site, index, eventSource) => ({
|
||||
id: "menu_action_save_to_pocket",
|
||||
icon: "pocket-save",
|
||||
action: ac.AlsoToMain({
|
||||
type: at.SAVE_TO_POCKET,
|
||||
data: {site: {url: site.url, title: site.title}},
|
||||
data: {site: {url: site.url, title: site.title}}
|
||||
}),
|
||||
impression: ac.ImpressionStats({
|
||||
source: eventSource,
|
||||
pocket: 0,
|
||||
tiles: [{id: site.guid, pos: index}],
|
||||
tiles: [{id: site.guid, pos: index}]
|
||||
}),
|
||||
userEvent: "SAVE_TO_POCKET",
|
||||
userEvent: "SAVE_TO_POCKET"
|
||||
}),
|
||||
DeleteFromPocket: site => ({
|
||||
id: "menu_action_delete_pocket",
|
||||
icon: "delete",
|
||||
action: ac.AlsoToMain({
|
||||
type: at.DELETE_FROM_POCKET,
|
||||
data: {pocket_id: site.pocket_id},
|
||||
data: {pocket_id: site.pocket_id}
|
||||
}),
|
||||
userEvent: "DELETE_FROM_POCKET",
|
||||
userEvent: "DELETE_FROM_POCKET"
|
||||
}),
|
||||
ArchiveFromPocket: site => ({
|
||||
id: "menu_action_archive_pocket",
|
||||
icon: "check",
|
||||
action: ac.AlsoToMain({
|
||||
type: at.ARCHIVE_FROM_POCKET,
|
||||
data: {pocket_id: site.pocket_id},
|
||||
data: {pocket_id: site.pocket_id}
|
||||
}),
|
||||
userEvent: "ARCHIVE_FROM_POCKET",
|
||||
userEvent: "ARCHIVE_FROM_POCKET"
|
||||
}),
|
||||
EditTopSite: (site, index) => ({
|
||||
id: "edit_topsites_button_text",
|
||||
icon: "edit",
|
||||
action: {
|
||||
type: at.TOP_SITES_EDIT,
|
||||
data: {index},
|
||||
},
|
||||
data: {index}
|
||||
}
|
||||
}),
|
||||
CheckBookmark: site => (site.bookmarkGuid ? LinkMenuOptions.RemoveBookmark(site) : LinkMenuOptions.AddBookmark(site)),
|
||||
CheckPinTopSite: (site, index) => (site.isPinned ? LinkMenuOptions.UnpinTopSite(site) : LinkMenuOptions.PinTopSite(site, index)),
|
||||
CheckSavedToPocket: (site, index) => (site.pocket_id ? LinkMenuOptions.DeleteFromPocket(site) : LinkMenuOptions.SaveToPocket(site, index)),
|
||||
CheckBookmarkOrArchive: site => (site.pocket_id ? LinkMenuOptions.ArchiveFromPocket(site) : LinkMenuOptions.CheckBookmark(site)),
|
||||
OpenInPrivateWindow: (site, index, eventSource, isEnabled) => (isEnabled ? _OpenInPrivateWindow(site) : LinkMenuOptions.EmptyItem()),
|
||||
OpenInPrivateWindow: (site, index, eventSource, isEnabled) => (isEnabled ? _OpenInPrivateWindow(site) : LinkMenuOptions.EmptyItem())
|
||||
};
|
||||
|
@ -46,5 +46,5 @@ export const ScreenshotUtils = {
|
||||
// This will only handle the remaining three possible outcomes.
|
||||
// (i.e. everything except when both image and localImage are present)
|
||||
return !remoteImage && !localImage;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -11,70 +11,70 @@ export const SectionMenuOptions = {
|
||||
icon: "arrowhead-up",
|
||||
action: ac.OnlyToMain({
|
||||
type: at.SECTION_MOVE,
|
||||
data: {id: section.id, direction: -1},
|
||||
data: {id: section.id, direction: -1}
|
||||
}),
|
||||
userEvent: "MENU_MOVE_UP",
|
||||
disabled: !!section.isFirst,
|
||||
disabled: !!section.isFirst
|
||||
}),
|
||||
MoveDown: section => ({
|
||||
id: "section_menu_action_move_down",
|
||||
icon: "arrowhead-down",
|
||||
action: ac.OnlyToMain({
|
||||
type: at.SECTION_MOVE,
|
||||
data: {id: section.id, direction: +1},
|
||||
data: {id: section.id, direction: +1}
|
||||
}),
|
||||
userEvent: "MENU_MOVE_DOWN",
|
||||
disabled: !!section.isLast,
|
||||
disabled: !!section.isLast
|
||||
}),
|
||||
RemoveSection: section => ({
|
||||
id: "section_menu_action_remove_section",
|
||||
icon: "dismiss",
|
||||
action: ac.SetPref(section.showPrefName, false),
|
||||
userEvent: "MENU_REMOVE",
|
||||
userEvent: "MENU_REMOVE"
|
||||
}),
|
||||
CollapseSection: section => ({
|
||||
id: "section_menu_action_collapse_section",
|
||||
icon: "minimize",
|
||||
action: ac.OnlyToMain({type: at.UPDATE_SECTION_PREFS, data: {id: section.id, value: {collapsed: true}}}),
|
||||
userEvent: "MENU_COLLAPSE",
|
||||
userEvent: "MENU_COLLAPSE"
|
||||
}),
|
||||
ExpandSection: section => ({
|
||||
id: "section_menu_action_expand_section",
|
||||
icon: "maximize",
|
||||
action: ac.OnlyToMain({type: at.UPDATE_SECTION_PREFS, data: {id: section.id, value: {collapsed: false}}}),
|
||||
userEvent: "MENU_EXPAND",
|
||||
userEvent: "MENU_EXPAND"
|
||||
}),
|
||||
ManageSection: section => ({
|
||||
id: "section_menu_action_manage_section",
|
||||
icon: "settings",
|
||||
action: ac.OnlyToMain({type: at.SETTINGS_OPEN}),
|
||||
userEvent: "MENU_MANAGE",
|
||||
userEvent: "MENU_MANAGE"
|
||||
}),
|
||||
ManageWebExtension: section => ({
|
||||
id: "section_menu_action_manage_webext",
|
||||
icon: "settings",
|
||||
action: ac.OnlyToMain({type: at.OPEN_WEBEXT_SETTINGS, data: section.id}),
|
||||
action: ac.OnlyToMain({type: at.OPEN_WEBEXT_SETTINGS, data: section.id})
|
||||
}),
|
||||
AddTopSite: section => ({
|
||||
id: "section_menu_action_add_topsite",
|
||||
icon: "add",
|
||||
action: {type: at.TOP_SITES_EDIT, data: {index: -1}},
|
||||
userEvent: "MENU_ADD_TOPSITE",
|
||||
userEvent: "MENU_ADD_TOPSITE"
|
||||
}),
|
||||
AddSearchShortcut: section => ({
|
||||
id: "section_menu_action_add_search_engine",
|
||||
icon: "search",
|
||||
action: {type: at.TOP_SITES_OPEN_SEARCH_SHORTCUTS_MODAL},
|
||||
userEvent: "MENU_ADD_SEARCH",
|
||||
userEvent: "MENU_ADD_SEARCH"
|
||||
}),
|
||||
PrivacyNotice: section => ({
|
||||
id: "section_menu_action_privacy_notice",
|
||||
icon: "info",
|
||||
action: ac.OnlyToMain({
|
||||
type: at.OPEN_LINK,
|
||||
data: {url: section.privacyNoticeURL},
|
||||
data: {url: section.privacyNoticeURL}
|
||||
}),
|
||||
userEvent: "MENU_PRIVACY_NOTICE",
|
||||
userEvent: "MENU_PRIVACY_NOTICE"
|
||||
}),
|
||||
CheckCollapsed: section => (section.collapsed ? SectionMenuOptions.ExpandSection(section) : SectionMenuOptions.CollapseSection(section)),
|
||||
CheckCollapsed: section => (section.collapsed ? SectionMenuOptions.ExpandSection(section) : SectionMenuOptions.CollapseSection(section))
|
||||
};
|
||||
|
@ -313,7 +313,7 @@ export class SnippetsProvider {
|
||||
Object.assign(this, {
|
||||
appData: {},
|
||||
elementId: "snippets",
|
||||
connect: true,
|
||||
connect: true
|
||||
}, options);
|
||||
|
||||
// Add listener so we know when snippets are blocked on other pages
|
||||
@ -381,25 +381,17 @@ export function addSnippetsSubscriber(store) {
|
||||
|
||||
store.subscribe(async () => {
|
||||
const state = store.getState();
|
||||
|
||||
/**
|
||||
* Sorry this code is so complicated. It will be removed soon.
|
||||
* This is what the different values actually mean:
|
||||
*
|
||||
* ASRouter.initialized Is ASRouter.jsm initialised?
|
||||
* ASRouter.allowLegacySnippets Are ASRouter snippets turned OFF (i.e. legacy snippets are allowed)
|
||||
* state.Prefs.values["feeds.snippets"] User preference for snippets
|
||||
* state.Snippets.initialized Is SnippetsFeed.jsm initialised?
|
||||
* snippets.initialized Is in-content snippets currently initialised?
|
||||
* state.Prefs.values.disableSnippets This pref is used to disable legacy snippets in an emergency
|
||||
* in a way that is not user-editable (true = disabled)
|
||||
*/
|
||||
|
||||
/** If we should initialize snippets... */
|
||||
if (
|
||||
state.Prefs.values["feeds.snippets"] &&
|
||||
state.ASRouter.initialized &&
|
||||
state.ASRouter.allowLegacySnippets &&
|
||||
let snippetsEnabled = false;
|
||||
try {
|
||||
snippetsEnabled = JSON.parse(state.Prefs.values["asrouter.messageProviders"]).find(i => i.id === "snippets").enabled;
|
||||
} catch (e) {}
|
||||
const isASRouterEnabled = state.Prefs.values.asrouterExperimentEnabled && snippetsEnabled;
|
||||
// state.Prefs.values["feeds.snippets"]: Should snippets be shown?
|
||||
// state.Snippets.initialized Is the snippets data initialized?
|
||||
// snippets.initialized: Is SnippetsProvider currently initialised?
|
||||
if (state.Prefs.values["feeds.snippets"] &&
|
||||
// If the message center experiment is enabled, don't show snippets
|
||||
!isASRouterEnabled &&
|
||||
!state.Prefs.values.disableSnippets &&
|
||||
state.Snippets.initialized &&
|
||||
!snippets.initialized &&
|
||||
@ -409,27 +401,13 @@ export function addSnippetsSubscriber(store) {
|
||||
) {
|
||||
initializing = true;
|
||||
await snippets.init({appData: state.Snippets});
|
||||
// istanbul ignore if
|
||||
if (state.Prefs.values["asrouter.devtoolsEnabled"]) {
|
||||
console.log("Legacy snippets initialized"); // eslint-disable-line no-console
|
||||
}
|
||||
initializing = false;
|
||||
|
||||
/** If we should remove snippets... */
|
||||
} else if (
|
||||
(
|
||||
state.Prefs.values["feeds.snippets"] === false ||
|
||||
state.Prefs.values.disableSnippets === true ||
|
||||
(state.ASRouter.initialized && !state.ASRouter.allowLegacySnippets)
|
||||
) &&
|
||||
(state.Prefs.values["feeds.snippets"] === false ||
|
||||
state.Prefs.values.disableSnippets === true) &&
|
||||
snippets.initialized
|
||||
) {
|
||||
// Remove snippets
|
||||
snippets.uninit();
|
||||
// istanbul ignore if
|
||||
if (state.Prefs.values["asrouter.devtoolsEnabled"]) {
|
||||
console.log("Legacy snippets removed"); // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -27,7 +27,6 @@ $grey-10-95: rgba($grey-10, 0.95);
|
||||
$grey-20-60: rgba($grey-20, 0.6);
|
||||
$grey-20-80: rgba($grey-20, 0.8);
|
||||
$grey-30-60: rgba($grey-30, 0.6);
|
||||
$grey-60-70: rgba($grey-60, 0.7);
|
||||
$grey-80-95: rgba($grey-80, 0.95);
|
||||
$grey-90-10: rgba($grey-90, 0.1);
|
||||
$grey-90-20: rgba($grey-90, 0.2);
|
||||
|
@ -362,13 +362,13 @@ main {
|
||||
|
||||
.body-wrapper .section-title,
|
||||
.body-wrapper .sections-list .section:last-of-type,
|
||||
.body-wrapper .topics {
|
||||
.body-wrapper .topic {
|
||||
opacity: 0; }
|
||||
|
||||
|
||||
.body-wrapper.on .section-title,
|
||||
.body-wrapper.on .sections-list .section:last-of-type,
|
||||
.body-wrapper.on .topics {
|
||||
.body-wrapper.on .topic {
|
||||
opacity: 1; }
|
||||
|
||||
.non-collapsible-section {
|
||||
@ -922,8 +922,7 @@ main {
|
||||
font-size: 13px; }
|
||||
@media (min-width: 866px) {
|
||||
.top-stories-bottom-container {
|
||||
line-height: 16px;
|
||||
height: 16px; } }
|
||||
line-height: 16px; } }
|
||||
.top-stories-bottom-container::after {
|
||||
clear: both;
|
||||
content: '';
|
||||
@ -1920,18 +1919,6 @@ a.firstrun-link {
|
||||
.SnippetBaseContainer:hover .blockButton {
|
||||
display: block; }
|
||||
|
||||
.snippets-preview-banner {
|
||||
font-size: 15px;
|
||||
line-height: 42px;
|
||||
color: rgba(74, 74, 79, 0.7);
|
||||
background: rgba(215, 215, 219, 0.6);
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%; }
|
||||
.snippets-preview-banner span {
|
||||
vertical-align: middle; }
|
||||
|
||||
.activity-stream.modal-open {
|
||||
overflow: hidden; }
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -365,13 +365,13 @@ main {
|
||||
|
||||
.body-wrapper .section-title,
|
||||
.body-wrapper .sections-list .section:last-of-type,
|
||||
.body-wrapper .topics {
|
||||
.body-wrapper .topic {
|
||||
opacity: 0; }
|
||||
|
||||
|
||||
.body-wrapper.on .section-title,
|
||||
.body-wrapper.on .sections-list .section:last-of-type,
|
||||
.body-wrapper.on .topics {
|
||||
.body-wrapper.on .topic {
|
||||
opacity: 1; }
|
||||
|
||||
.non-collapsible-section {
|
||||
@ -925,8 +925,7 @@ main {
|
||||
font-size: 13px; }
|
||||
@media (min-width: 866px) {
|
||||
.top-stories-bottom-container {
|
||||
line-height: 16px;
|
||||
height: 16px; } }
|
||||
line-height: 16px; } }
|
||||
.top-stories-bottom-container::after {
|
||||
clear: both;
|
||||
content: '';
|
||||
@ -1923,18 +1922,6 @@ a.firstrun-link {
|
||||
.SnippetBaseContainer:hover .blockButton {
|
||||
display: block; }
|
||||
|
||||
.snippets-preview-banner {
|
||||
font-size: 15px;
|
||||
line-height: 42px;
|
||||
color: rgba(74, 74, 79, 0.7);
|
||||
background: rgba(215, 215, 219, 0.6);
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%; }
|
||||
.snippets-preview-banner span {
|
||||
vertical-align: middle; }
|
||||
|
||||
.activity-stream.modal-open {
|
||||
overflow: hidden; }
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -362,13 +362,13 @@ main {
|
||||
|
||||
.body-wrapper .section-title,
|
||||
.body-wrapper .sections-list .section:last-of-type,
|
||||
.body-wrapper .topics {
|
||||
.body-wrapper .topic {
|
||||
opacity: 0; }
|
||||
|
||||
|
||||
.body-wrapper.on .section-title,
|
||||
.body-wrapper.on .sections-list .section:last-of-type,
|
||||
.body-wrapper.on .topics {
|
||||
.body-wrapper.on .topic {
|
||||
opacity: 1; }
|
||||
|
||||
.non-collapsible-section {
|
||||
@ -922,8 +922,7 @@ main {
|
||||
font-size: 13px; }
|
||||
@media (min-width: 866px) {
|
||||
.top-stories-bottom-container {
|
||||
line-height: 16px;
|
||||
height: 16px; } }
|
||||
line-height: 16px; } }
|
||||
.top-stories-bottom-container::after {
|
||||
clear: both;
|
||||
content: '';
|
||||
@ -1920,18 +1919,6 @@ a.firstrun-link {
|
||||
.SnippetBaseContainer:hover .blockButton {
|
||||
display: block; }
|
||||
|
||||
.snippets-preview-banner {
|
||||
font-size: 15px;
|
||||
line-height: 42px;
|
||||
color: rgba(74, 74, 79, 0.7);
|
||||
background: rgba(215, 215, 219, 0.6);
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%; }
|
||||
.snippets-preview-banner span {
|
||||
vertical-align: middle; }
|
||||
|
||||
.activity-stream.modal-open {
|
||||
overflow: hidden; }
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -211,7 +211,7 @@ const globalImportContext = typeof Window === "undefined" ? BACKGROUND_PROCESS :
|
||||
// }
|
||||
const actionTypes = {};
|
||||
|
||||
for (const type of ["ADDONS_INFO_REQUEST", "ADDONS_INFO_RESPONSE", "ARCHIVE_FROM_POCKET", "AS_ROUTER_INITIALIZED", "AS_ROUTER_PREF_CHANGED", "AS_ROUTER_TELEMETRY_USER_EVENT", "BLOCK_URL", "BOOKMARK_URL", "COPY_DOWNLOAD_LINK", "DELETE_BOOKMARK_BY_ID", "DELETE_FROM_POCKET", "DELETE_HISTORY_URL", "DIALOG_CANCEL", "DIALOG_OPEN", "DISABLE_ONBOARDING", "DOWNLOAD_CHANGED", "FILL_SEARCH_TERM", "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_DOWNLOAD_FILE", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "OPEN_WEBEXT_SETTINGS", "PAGE_PRERENDERED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_CHANGED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PLACES_SAVED_TO_POCKET", "POCKET_CTA", "POCKET_LOGGED_IN", "POCKET_WAITING_FOR_SPOC", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "PREVIEW_REQUEST", "PREVIEW_REQUEST_CANCEL", "PREVIEW_RESPONSE", "REMOVE_DOWNLOAD_FILE", "RICH_ICON_MISSING", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_MOVE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SETTINGS_CLOSE", "SETTINGS_OPEN", "SET_PREF", "SHOW_DOWNLOAD_FILE", "SHOW_FIREFOX_ACCOUNTS", "SKIPPED_SIGNIN", "SNIPPETS_BLOCKLIST_CLEARED", "SNIPPETS_BLOCKLIST_UPDATED", "SNIPPETS_DATA", "SNIPPETS_PREVIEW_MODE", "SNIPPETS_RESET", "SNIPPET_BLOCKED", "SUBMIT_EMAIL", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_CLOSE_SEARCH_SHORTCUTS_MODAL", "TOP_SITES_EDIT", "TOP_SITES_INSERT", "TOP_SITES_OPEN_SEARCH_SHORTCUTS_MODAL", "TOP_SITES_PIN", "TOP_SITES_PREFS_UPDATED", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "TOTAL_BOOKMARKS_REQUEST", "TOTAL_BOOKMARKS_RESPONSE", "UNINIT", "UPDATE_PINNED_SEARCH_SHORTCUTS", "UPDATE_SEARCH_SHORTCUTS", "UPDATE_SECTION_PREFS", "WEBEXT_CLICK", "WEBEXT_DISMISS"]) {
|
||||
for (const type of ["ADDONS_INFO_REQUEST", "ADDONS_INFO_RESPONSE", "ARCHIVE_FROM_POCKET", "AS_ROUTER_TELEMETRY_USER_EVENT", "BLOCK_URL", "BOOKMARK_URL", "COPY_DOWNLOAD_LINK", "DELETE_BOOKMARK_BY_ID", "DELETE_FROM_POCKET", "DELETE_HISTORY_URL", "DIALOG_CANCEL", "DIALOG_OPEN", "DISABLE_ONBOARDING", "DOWNLOAD_CHANGED", "FILL_SEARCH_TERM", "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_DOWNLOAD_FILE", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "OPEN_WEBEXT_SETTINGS", "PAGE_PRERENDERED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_CHANGED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PLACES_SAVED_TO_POCKET", "POCKET_CTA", "POCKET_LOGGED_IN", "POCKET_WAITING_FOR_SPOC", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "PREVIEW_REQUEST", "PREVIEW_REQUEST_CANCEL", "PREVIEW_RESPONSE", "REMOVE_DOWNLOAD_FILE", "RICH_ICON_MISSING", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_MOVE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SETTINGS_CLOSE", "SETTINGS_OPEN", "SET_PREF", "SHOW_DOWNLOAD_FILE", "SHOW_FIREFOX_ACCOUNTS", "SKIPPED_SIGNIN", "SNIPPETS_BLOCKLIST_CLEARED", "SNIPPETS_BLOCKLIST_UPDATED", "SNIPPETS_DATA", "SNIPPETS_RESET", "SNIPPET_BLOCKED", "SUBMIT_EMAIL", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_CLOSE_SEARCH_SHORTCUTS_MODAL", "TOP_SITES_EDIT", "TOP_SITES_INSERT", "TOP_SITES_OPEN_SEARCH_SHORTCUTS_MODAL", "TOP_SITES_PIN", "TOP_SITES_PREFS_UPDATED", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "TOTAL_BOOKMARKS_REQUEST", "TOTAL_BOOKMARKS_RESPONSE", "UNINIT", "UPDATE_PINNED_SEARCH_SHORTCUTS", "UPDATE_SEARCH_SHORTCUTS", "UPDATE_SECTION_PREFS", "WEBEXT_CLICK", "WEBEXT_DISMISS"]) {
|
||||
actionTypes[type] = type;
|
||||
}
|
||||
|
||||
@ -885,40 +885,26 @@ function addSnippetsSubscriber(store) {
|
||||
|
||||
store.subscribe(_asyncToGenerator(function* () {
|
||||
const state = store.getState();
|
||||
|
||||
/**
|
||||
* Sorry this code is so complicated. It will be removed soon.
|
||||
* This is what the different values actually mean:
|
||||
*
|
||||
* ASRouter.initialized Is ASRouter.jsm initialised?
|
||||
* ASRouter.allowLegacySnippets Are ASRouter snippets turned OFF (i.e. legacy snippets are allowed)
|
||||
* state.Prefs.values["feeds.snippets"] User preference for snippets
|
||||
* state.Snippets.initialized Is SnippetsFeed.jsm initialised?
|
||||
* snippets.initialized Is in-content snippets currently initialised?
|
||||
* state.Prefs.values.disableSnippets This pref is used to disable legacy snippets in an emergency
|
||||
* in a way that is not user-editable (true = disabled)
|
||||
*/
|
||||
|
||||
/** If we should initialize snippets... */
|
||||
if (state.Prefs.values["feeds.snippets"] && state.ASRouter.initialized && state.ASRouter.allowLegacySnippets && !state.Prefs.values.disableSnippets && state.Snippets.initialized && !snippets.initialized &&
|
||||
let snippetsEnabled = false;
|
||||
try {
|
||||
snippetsEnabled = JSON.parse(state.Prefs.values["asrouter.messageProviders"]).find(function (i) {
|
||||
return i.id === "snippets";
|
||||
}).enabled;
|
||||
} catch (e) {}
|
||||
const isASRouterEnabled = state.Prefs.values.asrouterExperimentEnabled && snippetsEnabled;
|
||||
// state.Prefs.values["feeds.snippets"]: Should snippets be shown?
|
||||
// state.Snippets.initialized Is the snippets data initialized?
|
||||
// snippets.initialized: Is SnippetsProvider currently initialised?
|
||||
if (state.Prefs.values["feeds.snippets"] &&
|
||||
// If the message center experiment is enabled, don't show snippets
|
||||
!isASRouterEnabled && !state.Prefs.values.disableSnippets && state.Snippets.initialized && !snippets.initialized &&
|
||||
// Don't call init multiple times
|
||||
!initializing && location.href !== "about:welcome") {
|
||||
initializing = true;
|
||||
yield snippets.init({ appData: state.Snippets });
|
||||
// istanbul ignore if
|
||||
if (state.Prefs.values["asrouter.devtoolsEnabled"]) {
|
||||
console.log("Legacy snippets initialized"); // eslint-disable-line no-console
|
||||
}
|
||||
initializing = false;
|
||||
|
||||
/** If we should remove snippets... */
|
||||
} else if ((state.Prefs.values["feeds.snippets"] === false || state.Prefs.values.disableSnippets === true || state.ASRouter.initialized && !state.ASRouter.allowLegacySnippets) && snippets.initialized) {
|
||||
// Remove snippets
|
||||
} else if ((state.Prefs.values["feeds.snippets"] === false || state.Prefs.values.disableSnippets === true) && snippets.initialized) {
|
||||
snippets.uninit();
|
||||
// istanbul ignore if
|
||||
if (state.Prefs.values["asrouter.devtoolsEnabled"]) {
|
||||
console.log("Legacy snippets removed"); // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
@ -1109,9 +1095,6 @@ class ASRouterUISurface extends react__WEBPACK_IMPORTED_MODULE_6___default.a.Pur
|
||||
id: "NEWTAB_FOOTER_BAR_CONTENT"
|
||||
};
|
||||
this.sendUserActionTelemetry(Object.assign({ event: "CLICK_BUTTON" }, metric));
|
||||
if (!this.state.message.content.do_not_autoblock) {
|
||||
ASRouterUtils.blockById(this.state.message.id);
|
||||
}
|
||||
}
|
||||
|
||||
onBlockById(id) {
|
||||
@ -1201,34 +1184,15 @@ class ASRouterUISurface extends react__WEBPACK_IMPORTED_MODULE_6___default.a.Pur
|
||||
sendUserActionTelemetry: this.sendUserActionTelemetry }));
|
||||
}
|
||||
|
||||
renderPreviewBanner() {
|
||||
if (this.state.message.provider !== "preview") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(
|
||||
"div",
|
||||
{ className: "snippets-preview-banner" },
|
||||
react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement("span", { className: "icon icon-small-spacer icon-info" }),
|
||||
react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(
|
||||
"span",
|
||||
null,
|
||||
"Preview Purposes Only"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { message, bundle } = this.state;
|
||||
if (!message.id && !bundle.template) {
|
||||
return null;
|
||||
}
|
||||
return react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(
|
||||
"div",
|
||||
null,
|
||||
this.renderPreviewBanner(),
|
||||
bundle.template === "onboarding" ? this.renderOnboarding() : this.renderSnippets()
|
||||
);
|
||||
if (bundle.template === "onboarding") {
|
||||
return this.renderOnboarding();
|
||||
}
|
||||
return this.renderSnippets();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1602,7 +1566,8 @@ function debounce(func, wait) {
|
||||
|
||||
class _Base extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureComponent {
|
||||
componentWillMount() {
|
||||
const { locale } = this.props;
|
||||
const { App, locale } = this.props;
|
||||
this.sendNewTabRehydrated(App);
|
||||
addLocaleDataForReactIntl(locale);
|
||||
if (this.props.isFirstrun) {
|
||||
global.document.body.classList.add("welcome", "hide-main");
|
||||
@ -1623,8 +1588,29 @@ class _Base extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureComponent {
|
||||
this.updateTheme();
|
||||
}
|
||||
|
||||
componentWillUpdate() {
|
||||
hasTopStoriesSectionChanged(nextProps) {
|
||||
const nPropsSections = nextProps.Sections.find(section => section.id === "topstories");
|
||||
const tPropsSections = this.props.Sections.find(section => section.id === "topstories");
|
||||
if (nPropsSections && nPropsSections.options) {
|
||||
if (!tPropsSections || !tPropsSections.options) {
|
||||
return true;
|
||||
}
|
||||
if (nPropsSections.options.show_spocs !== tPropsSections.options.show_spocs) {
|
||||
return true;
|
||||
}
|
||||
if (nPropsSections.options.stories_endpoint !== tPropsSections.options.stories_endpoint) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
this.updateTheme();
|
||||
if (this.hasTopStoriesSectionChanged(nextProps)) {
|
||||
this.renderNotified = false;
|
||||
}
|
||||
this.sendNewTabRehydrated(nextProps.App);
|
||||
}
|
||||
|
||||
updateTheme() {
|
||||
@ -1635,13 +1621,23 @@ class _Base extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureComponent {
|
||||
global.document.body.className = bodyClassName;
|
||||
}
|
||||
|
||||
// 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(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({ type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].NEW_TAB_REHYDRATED, data: {} }));
|
||||
this.renderNotified = true;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { props } = this;
|
||||
const { App, locale, strings } = props;
|
||||
const { initialized } = App;
|
||||
|
||||
const prefs = props.Prefs.values;
|
||||
if (prefs["asrouter.devtoolsEnabled"] && window.location.hash === "#asrouter") {
|
||||
if (prefs.asrouterExperimentEnabled && window.location.hash === "#asrouter") {
|
||||
return react__WEBPACK_IMPORTED_MODULE_8___default.a.createElement(content_src_components_ASRouterAdmin_ASRouterAdmin__WEBPACK_IMPORTED_MODULE_2__["ASRouterAdmin"], null);
|
||||
}
|
||||
|
||||
@ -1649,6 +1645,17 @@ class _Base extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureComponent {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Until we can delete the existing onboarding tour, just hide the onboarding button when users are in
|
||||
// the new simplified onboarding experiment. CSS hacks ftw
|
||||
let isOnboardingEnabled = false;
|
||||
try {
|
||||
isOnboardingEnabled = JSON.parse(prefs["asrouter.messageProviders"]).find(i => i.id === "onboarding").enabled;
|
||||
} catch (e) {}
|
||||
|
||||
if (isOnboardingEnabled) {
|
||||
global.document.body.classList.add("hide-onboarding");
|
||||
}
|
||||
|
||||
return react__WEBPACK_IMPORTED_MODULE_8___default.a.createElement(
|
||||
react_intl__WEBPACK_IMPORTED_MODULE_1__["IntlProvider"],
|
||||
{ locale: locale, messages: strings },
|
||||
@ -1882,37 +1889,10 @@ class ASRouterAdmin extends react__WEBPACK_IMPORTED_MODULE_1___default.a.PureCom
|
||||
);
|
||||
}
|
||||
|
||||
renderTableHead() {
|
||||
return react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(
|
||||
"thead",
|
||||
null,
|
||||
react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(
|
||||
"tr",
|
||||
{ className: "message-item" },
|
||||
react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(
|
||||
"td",
|
||||
null,
|
||||
"id"
|
||||
),
|
||||
react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(
|
||||
"td",
|
||||
null,
|
||||
"source"
|
||||
),
|
||||
react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(
|
||||
"td",
|
||||
null,
|
||||
"last updated"
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderProviders() {
|
||||
return react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(
|
||||
"table",
|
||||
null,
|
||||
this.renderTableHead(),
|
||||
react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(
|
||||
"tbody",
|
||||
null,
|
||||
@ -1939,11 +1919,6 @@ class ASRouterAdmin extends react__WEBPACK_IMPORTED_MODULE_1___default.a.PureCom
|
||||
"td",
|
||||
null,
|
||||
label
|
||||
),
|
||||
react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(
|
||||
"td",
|
||||
null,
|
||||
provider.lastUpdated ? new Date(provider.lastUpdated).toString() : ""
|
||||
)
|
||||
);
|
||||
})
|
||||
@ -2505,10 +2480,10 @@ __webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SectionIntl", function() { return SectionIntl; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_Sections", function() { return _Sections; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Sections", function() { return Sections; });
|
||||
/* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
|
||||
/* harmony import */ var content_src_components_Card_Card__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(47);
|
||||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(13);
|
||||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_2__);
|
||||
/* harmony import */ var content_src_components_Card_Card__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(47);
|
||||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(13);
|
||||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
|
||||
/* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2);
|
||||
/* harmony import */ var content_src_components_CollapsibleSection_CollapsibleSection__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(27);
|
||||
/* harmony import */ var content_src_components_ComponentPerfTimer_ComponentPerfTimer__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(30);
|
||||
/* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(16);
|
||||
@ -2543,7 +2518,7 @@ function getFormattedMessage(message) {
|
||||
"span",
|
||||
null,
|
||||
message
|
||||
) : react__WEBPACK_IMPORTED_MODULE_8___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_2__["FormattedMessage"], message);
|
||||
) : react__WEBPACK_IMPORTED_MODULE_8___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__["FormattedMessage"], message);
|
||||
}
|
||||
|
||||
class Section extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureComponent {
|
||||
@ -2565,7 +2540,7 @@ class Section extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureComponent
|
||||
const cards = props.rows.slice(0, maxCards);
|
||||
|
||||
if (this.needsImpressionStats(cards)) {
|
||||
props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].ImpressionStats({
|
||||
props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_2__["actionCreators"].ImpressionStats({
|
||||
source: props.eventSource,
|
||||
tiles: cards.map(link => ({ id: link.guid }))
|
||||
}));
|
||||
@ -2605,10 +2580,6 @@ class Section extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureComponent
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.sendNewTabRehydrated(this.props.initialized);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.rows.length && !this.props.pref.collapsed) {
|
||||
this.sendImpressionStatsOrAddListener();
|
||||
@ -2631,10 +2602,6 @@ class Section extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureComponent
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
this.sendNewTabRehydrated(nextProps.initialized);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this._onVisibilityChange) {
|
||||
this.props.document.removeEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange);
|
||||
@ -2655,16 +2622,6 @@ class Section extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureComponent
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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(initialized) {
|
||||
if (initialized && !this.renderNotified) {
|
||||
this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({ type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].NEW_TAB_REHYDRATED, data: {} }));
|
||||
this.renderNotified = true;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
id, eventSource, title, icon, rows, Pocket, topics,
|
||||
@ -2679,18 +2636,11 @@ class Section extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureComponent
|
||||
const maxCards = maxCardsPerRow * numRows;
|
||||
const maxCardsOnNarrow = CARDS_PER_ROW_DEFAULT * numRows;
|
||||
|
||||
const { pocketCta, isUserLoggedIn } = Pocket || {};
|
||||
const { useCta } = pocketCta || {};
|
||||
const shouldShowPocketCta = id === "topstories" && Pocket.pocketCta.useCta && !Pocket.isUserLoggedIn;
|
||||
|
||||
// Don't display anything until we have a definitve result from Pocket,
|
||||
// to avoid a flash of logged out state while we render.
|
||||
const isPocketLoggedInDefined = isUserLoggedIn === true || isUserLoggedIn === false;
|
||||
|
||||
const shouldShowPocketCta = id === "topstories" && useCta && isUserLoggedIn === false;
|
||||
|
||||
// Show topics only for top stories and if it has loaded with topics.
|
||||
// The classs .top-stories-bottom-container ensures content doesn't shift as things load.
|
||||
const shouldShowTopics = id === "topstories" && topics && topics.length > 0 && (useCta && isUserLoggedIn === true || !useCta && isPocketLoggedInDefined);
|
||||
// Show topics only for top stories and if it's not initialized yet (so
|
||||
// content doesn't shift when it is loaded) or has loaded with topics
|
||||
const shouldShowTopics = id === "topstories" && (!topics || topics.length > 0) && !shouldShowPocketCta;
|
||||
|
||||
const realRows = rows.slice(0, maxCards);
|
||||
|
||||
@ -2711,7 +2661,7 @@ class Section extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureComponent
|
||||
if (!usePlaceholder && i === 2 && waitingForSpoc) {
|
||||
usePlaceholder = true;
|
||||
}
|
||||
cards.push(!usePlaceholder ? react__WEBPACK_IMPORTED_MODULE_8___default.a.createElement(content_src_components_Card_Card__WEBPACK_IMPORTED_MODULE_1__["Card"], { key: i,
|
||||
cards.push(!usePlaceholder ? react__WEBPACK_IMPORTED_MODULE_8___default.a.createElement(content_src_components_Card_Card__WEBPACK_IMPORTED_MODULE_0__["Card"], { key: i,
|
||||
index: i,
|
||||
className: className,
|
||||
dispatch: dispatch,
|
||||
@ -2719,7 +2669,7 @@ class Section extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureComponent
|
||||
contextMenuOptions: contextMenuOptions,
|
||||
eventSource: eventSource,
|
||||
shouldSendImpressionStats: this.props.shouldSendImpressionStats,
|
||||
isWebExtension: this.props.isWebExtension }) : react__WEBPACK_IMPORTED_MODULE_8___default.a.createElement(content_src_components_Card_Card__WEBPACK_IMPORTED_MODULE_1__["PlaceholderCard"], { key: i, className: className }));
|
||||
isWebExtension: this.props.isWebExtension }) : react__WEBPACK_IMPORTED_MODULE_8___default.a.createElement(content_src_components_Card_Card__WEBPACK_IMPORTED_MODULE_0__["PlaceholderCard"], { key: i, className: className }));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2784,7 +2734,7 @@ Section.defaultProps = {
|
||||
title: ""
|
||||
};
|
||||
|
||||
const SectionIntl = Object(react_redux__WEBPACK_IMPORTED_MODULE_5__["connect"])(state => ({ Prefs: state.Prefs, Pocket: state.Pocket }))(Object(react_intl__WEBPACK_IMPORTED_MODULE_2__["injectIntl"])(Section));
|
||||
const SectionIntl = Object(react_redux__WEBPACK_IMPORTED_MODULE_5__["connect"])(state => ({ Prefs: state.Prefs, Pocket: state.Pocket }))(Object(react_intl__WEBPACK_IMPORTED_MODULE_1__["injectIntl"])(Section));
|
||||
|
||||
class _Sections extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureComponent {
|
||||
renderSections() {
|
||||
@ -5114,7 +5064,7 @@ class _StartupOverlay extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureC
|
||||
_this.setState({ flowId, flowBeginTime });
|
||||
}
|
||||
} catch (error) {
|
||||
_this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({ type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].TELEMETRY_UNDESIRED_EVENT, data: { event: "FXA_METRICS_ERROR" } }));
|
||||
_this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({ type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].TELEMETRY_UNDESIRED_EVENT, data: { value: "FXA_METRICS_ERROR" } }));
|
||||
}
|
||||
}
|
||||
})();
|
||||
@ -5357,35 +5307,20 @@ class DetectUserSessionStart {
|
||||
|
||||
"use strict";
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enableASRouterContent", function() { return enableASRouterContent; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enableASRouterContent", function() { return enableASRouterContent; });
|
||||
function enableASRouterContent(store, asrouterContent) {
|
||||
let didHideOnboarding = false;
|
||||
|
||||
// Enable asrouter content
|
||||
store.subscribe(() => {
|
||||
const state = store.getState();
|
||||
if (!state.ASRouter.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!asrouterContent.initialized) {
|
||||
if (state.Prefs.values.asrouterExperimentEnabled && !asrouterContent.initialized) {
|
||||
asrouterContent.init();
|
||||
}
|
||||
|
||||
// Until we can delete the existing onboarding tour, just hide the onboarding button when users are in
|
||||
// the new simplified onboarding experiment. CSS hacks ftw
|
||||
if (state.ASRouter.allowLegacyOnboarding === false && !didHideOnboarding) {
|
||||
global.document.body.classList.add("hide-onboarding");
|
||||
didHideOnboarding = true;
|
||||
} else if (state.ASRouter.allowLegacyOnboarding === true && didHideOnboarding) {
|
||||
global.document.body.classList.remove("hide-onboarding");
|
||||
didHideOnboarding = false;
|
||||
} else if (!state.Prefs.values.asrouterExperimentEnabled && asrouterContent.initialized) {
|
||||
asrouterContent.uninit();
|
||||
}
|
||||
});
|
||||
// Return this for testing purposes
|
||||
return { asrouterContent };
|
||||
}
|
||||
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
|
||||
|
||||
/***/ }),
|
||||
/* 42 */
|
||||
@ -8094,9 +8029,6 @@ class SimpleSnippet_SimpleSnippet extends external_React_default.a.PureComponent
|
||||
onButtonClick() {
|
||||
this.props.sendUserActionTelemetry({ event: "CLICK_BUTTON", id: this.props.UISurface });
|
||||
this.props.onAction(this.props.content.button_action);
|
||||
if (!this.props.content.do_not_autoblock) {
|
||||
this.props.onBlock();
|
||||
}
|
||||
}
|
||||
|
||||
renderTitle() {
|
||||
@ -8224,11 +8156,6 @@ const INITIAL_STATE = {
|
||||
// Have we received real data from the app yet?
|
||||
initialized: false
|
||||
},
|
||||
ASRouter: {
|
||||
initialized: false,
|
||||
allowLegacyOnboarding: null,
|
||||
allowLegacySnippets: null
|
||||
},
|
||||
Snippets: { initialized: false },
|
||||
TopSites: {
|
||||
// Have we received real data from history yet?
|
||||
@ -8252,7 +8179,7 @@ const INITIAL_STATE = {
|
||||
},
|
||||
Sections: [],
|
||||
Pocket: {
|
||||
isUserLoggedIn: null,
|
||||
isUserLoggedIn: false,
|
||||
pocketCta: {},
|
||||
waitingForSpoc: true
|
||||
}
|
||||
@ -8268,17 +8195,6 @@ function App(prevState = INITIAL_STATE.App, action) {
|
||||
}
|
||||
}
|
||||
|
||||
function ASRouter(prevState = INITIAL_STATE.ASRouter, action) {
|
||||
switch (action.type) {
|
||||
case Actions["actionTypes"].AS_ROUTER_INITIALIZED:
|
||||
return Object.assign({}, action.data, { initialized: true });
|
||||
case Actions["actionTypes"].AS_ROUTER_PREF_CHANGED:
|
||||
return Object.assign({}, prevState, action.data);
|
||||
default:
|
||||
return prevState;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* insertPinned - Inserts pinned links in their specified slots
|
||||
*
|
||||
@ -8415,8 +8331,6 @@ function TopSites(prevState = INITIAL_STATE.TopSites, action) {
|
||||
return Object.assign({}, prevState, { rows: newRows });
|
||||
case Actions["actionTypes"].UPDATE_SEARCH_SHORTCUTS:
|
||||
return Object.assign({}, prevState, { searchShortcuts: action.data.searchShortcuts });
|
||||
case Actions["actionTypes"].SNIPPETS_PREVIEW_MODE:
|
||||
return Object.assign({}, prevState, { rows: [] });
|
||||
default:
|
||||
return prevState;
|
||||
}
|
||||
@ -8594,8 +8508,6 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
|
||||
case Actions["actionTypes"].DELETE_FROM_POCKET:
|
||||
case Actions["actionTypes"].ARCHIVE_FROM_POCKET:
|
||||
return prevState.map(section => Object.assign({}, section, { rows: section.rows.filter(site => site.pocket_id !== action.data.pocket_id) }));
|
||||
case Actions["actionTypes"].SNIPPETS_PREVIEW_MODE:
|
||||
return prevState.map(section => Object.assign({}, section, { rows: [] }));
|
||||
default:
|
||||
return prevState;
|
||||
}
|
||||
@ -8636,7 +8548,7 @@ function Pocket(prevState = INITIAL_STATE.Pocket, action) {
|
||||
}
|
||||
}
|
||||
|
||||
var reducers = { TopSites, App, ASRouter, Snippets, Prefs, Dialog, Sections, Pocket };
|
||||
var reducers = { TopSites, App, Snippets, Prefs, Dialog, Sections, Pocket };
|
||||
|
||||
/***/ }),
|
||||
/* 46 */
|
||||
|
File diff suppressed because one or more lines are too long
@ -13,7 +13,7 @@ const PATHS = {
|
||||
// a RegEx matching all Cu.import statements of local files
|
||||
resourcePathRegEx: /^resource:\/\/activity-stream\//,
|
||||
|
||||
coverageReportingPath: "logs/coverage/",
|
||||
coverageReportingPath: "logs/coverage/"
|
||||
};
|
||||
|
||||
// When tweaking here, be sure to review the docs about the execution ordering
|
||||
@ -21,7 +21,7 @@ const PATHS = {
|
||||
const preprocessors = {};
|
||||
preprocessors[PATHS.testFilesPattern] = [
|
||||
"webpack", // require("karma-webpack")
|
||||
"sourcemap", // require("karma-sourcemap-loader")
|
||||
"sourcemap" // require("karma-sourcemap-loader")
|
||||
];
|
||||
|
||||
module.exports = function(config) {
|
||||
@ -33,17 +33,17 @@ module.exports = function(config) {
|
||||
customLaunchers: {
|
||||
FirefoxHeadless: {
|
||||
base: "Firefox",
|
||||
flags: ["--headless"],
|
||||
},
|
||||
flags: ["--headless"]
|
||||
}
|
||||
},
|
||||
frameworks: [
|
||||
"chai", // require("chai") require("karma-chai")
|
||||
"mocha", // require("mocha") require("karma-mocha")
|
||||
"sinon", // require("sinon") require("karma-sinon")
|
||||
"sinon" // require("sinon") require("karma-sinon")
|
||||
],
|
||||
reporters: [
|
||||
"coverage", // require("karma-coverage")
|
||||
"mocha", // require("karma-mocha-reporter")
|
||||
"mocha" // require("karma-mocha-reporter")
|
||||
],
|
||||
coverageReporter: {
|
||||
dir: PATHS.coverageReportingPath,
|
||||
@ -53,14 +53,14 @@ module.exports = function(config) {
|
||||
statements: 100,
|
||||
lines: 100,
|
||||
functions: 100,
|
||||
branches: 90,
|
||||
},
|
||||
branches: 90
|
||||
}
|
||||
},
|
||||
reporters: [
|
||||
{type: "html", subdir: "report-html"},
|
||||
{type: "text", subdir: ".", file: "text.txt"},
|
||||
{type: "text-summary", subdir: ".", file: "text-summary.txt"},
|
||||
],
|
||||
{type: "text-summary", subdir: ".", file: "text-summary.txt"}
|
||||
]
|
||||
},
|
||||
files: [PATHS.testEntryFile],
|
||||
preprocessors,
|
||||
@ -74,15 +74,15 @@ module.exports = function(config) {
|
||||
extensions: [".js", ".jsx"],
|
||||
modules: [
|
||||
PATHS.moduleResolveDirectory,
|
||||
"node_modules",
|
||||
],
|
||||
"node_modules"
|
||||
]
|
||||
},
|
||||
externals: {
|
||||
// enzyme needs these for backwards compatibility with 0.13.
|
||||
// see https://github.com/airbnb/enzyme/blob/master/docs/guides/webpack.md#using-enzyme-with-webpack
|
||||
"react/addons": true,
|
||||
"react/lib/ReactContext": true,
|
||||
"react/lib/ExecutionEnvironment": true,
|
||||
"react/lib/ExecutionEnvironment": true
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
@ -98,10 +98,10 @@ module.exports = function(config) {
|
||||
["jsm-to-commonjs", {basePath: PATHS.resourcePathRegEx, replace: true}], // require("babel-plugin-jsm-to-commonjs")
|
||||
["transform-async-to-module-method", {module: "co-task", method: "async"}], // require("babel-plugin-transform-async-to-module-method")
|
||||
"transform-es2015-modules-commonjs", // require("babel-plugin-transform-es2015-modules-commonjs")
|
||||
["transform-object-rest-spread", {"useBuiltIns": true}], // require("babel-plugin-transform-object-rest-spread")
|
||||
],
|
||||
},
|
||||
}],
|
||||
["transform-object-rest-spread", {"useBuiltIns": true}] // require("babel-plugin-transform-object-rest-spread")
|
||||
]
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
@ -114,10 +114,10 @@ module.exports = function(config) {
|
||||
"transform-es2015-modules-commonjs",
|
||||
["transform-object-rest-spread", {"useBuiltIns": true}],
|
||||
["transform-async-to-generator"],
|
||||
["transform-async-generator-functions"],
|
||||
],
|
||||
},
|
||||
}],
|
||||
["transform-async-generator-functions"]
|
||||
]
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
test: /\.jsx$/,
|
||||
@ -125,8 +125,8 @@ module.exports = function(config) {
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
presets: ["react"], // require("babel-preset-react")
|
||||
plugins: [["transform-object-rest-spread", {"useBuiltIns": true}]],
|
||||
},
|
||||
plugins: [["transform-object-rest-spread", {"useBuiltIns": true}]]
|
||||
}
|
||||
},
|
||||
{
|
||||
enforce: "post",
|
||||
@ -135,7 +135,7 @@ module.exports = function(config) {
|
||||
include: [
|
||||
path.resolve("content-src"),
|
||||
path.resolve("lib"),
|
||||
path.resolve("common"),
|
||||
path.resolve("common")
|
||||
],
|
||||
exclude: [
|
||||
path.resolve("test"),
|
||||
@ -144,13 +144,13 @@ module.exports = function(config) {
|
||||
path.resolve("lib/ASRouterTriggerListeners.jsm"),
|
||||
path.resolve("lib/OnboardingMessageProvider.jsm"),
|
||||
path.resolve("lib/CFRMessageProvider.jsm"),
|
||||
path.resolve("lib/CFRPageActions.jsm"),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
path.resolve("lib/CFRPageActions.jsm")
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
// Silences some overly-verbose logging of individual module builds
|
||||
webpackMiddleware: {noInfo: true},
|
||||
webpackMiddleware: {noInfo: true}
|
||||
});
|
||||
};
|
||||
|
@ -8,16 +8,14 @@ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
AddonManager: "resource://gre/modules/AddonManager.jsm",
|
||||
UITour: "resource:///modules/UITour.jsm",
|
||||
UITour: "resource:///modules/UITour.jsm"
|
||||
});
|
||||
const {ASRouterActions: ra, actionTypes: at, actionCreators: ac} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
|
||||
const {ASRouterActions: ra, actionCreators: ac} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
|
||||
const {CFRMessageProvider} = ChromeUtils.import("resource://activity-stream/lib/CFRMessageProvider.jsm", {});
|
||||
const {OnboardingMessageProvider} = ChromeUtils.import("resource://activity-stream/lib/OnboardingMessageProvider.jsm", {});
|
||||
const {RemoteSettings} = ChromeUtils.import("resource://services-settings/remote-settings.js", {});
|
||||
const {CFRPageActions} = ChromeUtils.import("resource://activity-stream/lib/CFRPageActions.jsm", {});
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "ASRouterPreferences",
|
||||
"resource://activity-stream/lib/ASRouterPreferences.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "ASRouterTargeting",
|
||||
"resource://activity-stream/lib/ASRouterTargeting.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "ASRouterTriggerListeners",
|
||||
@ -25,25 +23,22 @@ ChromeUtils.defineModuleGetter(this, "ASRouterTriggerListeners",
|
||||
|
||||
const INCOMING_MESSAGE_NAME = "ASRouter:child-to-parent";
|
||||
const OUTGOING_MESSAGE_NAME = "ASRouter:parent-to-child";
|
||||
const MESSAGE_PROVIDER_PREF = "browser.newtabpage.activity-stream.asrouter.messageProviders";
|
||||
const ONE_DAY_IN_MS = 24 * 60 * 60 * 1000;
|
||||
// List of hosts for endpoints that serve router messages.
|
||||
// Key is allowed host, value is a name for the endpoint host.
|
||||
const DEFAULT_WHITELIST_HOSTS = {
|
||||
"activity-stream-icons.services.mozilla.com": "production",
|
||||
"snippets-admin.mozilla.org": "preview",
|
||||
"snippets-admin.mozilla.org": "preview"
|
||||
};
|
||||
const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
|
||||
const SNIPPETS_ENDPOINT_WHITELIST = "browser.newtab.activity-stream.asrouter.whitelistHosts";
|
||||
// Max possible impressions cap for any message
|
||||
const MAX_MESSAGE_LIFETIME_CAP = 100;
|
||||
|
||||
const LOCAL_MESSAGE_PROVIDERS = {OnboardingMessageProvider, CFRMessageProvider};
|
||||
const STARTPAGE_VERSION = "6";
|
||||
const STARTPAGE_VERSION = "0.1.0";
|
||||
|
||||
const MessageLoaderUtils = {
|
||||
STARTPAGE_VERSION,
|
||||
REMOTE_LOADER_CACHE_KEY: "RemoteLoaderCache",
|
||||
|
||||
/**
|
||||
* _localLoader - Loads messages for a local provider (i.e. one that lives in mozilla central)
|
||||
*
|
||||
@ -55,74 +50,26 @@ const MessageLoaderUtils = {
|
||||
return provider.messages;
|
||||
},
|
||||
|
||||
async _remoteLoaderCache(storage) {
|
||||
let allCached;
|
||||
try {
|
||||
allCached = await storage.get(MessageLoaderUtils.REMOTE_LOADER_CACHE_KEY) || {};
|
||||
} catch (e) {
|
||||
// istanbul ignore next
|
||||
Cu.reportError(e);
|
||||
// istanbul ignore next
|
||||
allCached = {};
|
||||
}
|
||||
return allCached;
|
||||
},
|
||||
|
||||
/**
|
||||
* _remoteLoader - Loads messages for a remote provider
|
||||
*
|
||||
* @param {obj} provider An AS router provider
|
||||
* @param {string} provider.url An endpoint that returns an array of messages as JSON
|
||||
* @param {obj} storage A storage object with get() and set() methods for caching.
|
||||
* @returns {Promise} resolves with an array of messages, or an empty array if none could be fetched
|
||||
*/
|
||||
async _remoteLoader(provider, storage) {
|
||||
async _remoteLoader(provider) {
|
||||
let remoteMessages = [];
|
||||
if (provider.url) {
|
||||
const allCached = await MessageLoaderUtils._remoteLoaderCache(storage);
|
||||
const cached = allCached[provider.id];
|
||||
let etag;
|
||||
|
||||
if (cached && cached.url === provider.url && cached.version === STARTPAGE_VERSION) {
|
||||
const {lastFetched, messages} = cached;
|
||||
if (!MessageLoaderUtils.shouldProviderUpdate({...provider, lastUpdated: lastFetched})) {
|
||||
// Cached messages haven't expired, return early.
|
||||
return messages;
|
||||
}
|
||||
etag = cached.etag;
|
||||
remoteMessages = messages;
|
||||
}
|
||||
|
||||
let headers = new Headers();
|
||||
if (etag) {
|
||||
headers.set("If-None-Match", etag);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(provider.url, {headers});
|
||||
const response = await fetch(provider.url);
|
||||
if (
|
||||
// Empty response
|
||||
response.status !== 204 &&
|
||||
// Not modified
|
||||
response.status !== 304 &&
|
||||
(response.ok || response.status === 302)
|
||||
) {
|
||||
remoteMessages = (await response.json())
|
||||
.messages
|
||||
.map(msg => ({...msg, provider_url: provider.url}));
|
||||
|
||||
// Cache the results if this isn't a preview URL.
|
||||
if (provider.updateCycleInMs > 0) {
|
||||
etag = response.headers.get("ETag");
|
||||
const cacheInfo = {
|
||||
messages: remoteMessages,
|
||||
etag,
|
||||
lastFetched: Date.now(),
|
||||
version: STARTPAGE_VERSION,
|
||||
};
|
||||
|
||||
storage.set(MessageLoaderUtils.REMOTE_LOADER_CACHE_KEY, {...allCached, [provider.id]: cacheInfo});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
@ -190,19 +137,13 @@ const MessageLoaderUtils = {
|
||||
*
|
||||
* @param {obj} provider An AS Router provider
|
||||
* @param {string} provider.type An AS Router provider type (defaults to "local")
|
||||
* @param {obj} storage A storage object with get() and set() methods for caching.
|
||||
* @returns {obj} Returns an object with .messages (an array of messages) and .lastUpdated (the time the messages were updated)
|
||||
*/
|
||||
async loadMessagesForProvider(provider, storage) {
|
||||
const loader = this._getMessageLoader(provider);
|
||||
let messages = await loader(provider, storage);
|
||||
// istanbul ignore if
|
||||
if (!messages) {
|
||||
messages = [];
|
||||
Cu.reportError(new Error(`Tried to load messages for ${provider.id} but the result was not an Array.`));
|
||||
}
|
||||
async loadMessagesForProvider(provider) {
|
||||
const messages = (await this._getMessageLoader(provider)(provider))
|
||||
.map(msg => ({...msg, provider: provider.id}));
|
||||
const lastUpdated = Date.now();
|
||||
return {messages: messages.map(msg => ({...msg, provider: provider.id})), lastUpdated};
|
||||
return {messages, lastUpdated};
|
||||
},
|
||||
|
||||
async installAddonFromURL(browser, url) {
|
||||
@ -213,27 +154,7 @@ const MessageLoaderUtils = {
|
||||
await AddonManager.installAddonFromWebpage("application/x-xpinstall", browser,
|
||||
systemPrincipal, install);
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
/**
|
||||
* cleanupCache - Removes cached data of removed providers.
|
||||
*
|
||||
* @param {Array} providers A list of activer AS Router providers
|
||||
*/
|
||||
async cleanupCache(providers, storage) {
|
||||
const ids = providers.filter(p => p.type === "remote").map(p => p.id);
|
||||
const cache = await MessageLoaderUtils._remoteLoaderCache(storage);
|
||||
let dirty = false;
|
||||
for (let id in cache) {
|
||||
if (!ids.includes(id)) {
|
||||
delete cache[id];
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
if (dirty) {
|
||||
await storage.set(MessageLoaderUtils.REMOTE_LOADER_CACHE_KEY, cache);
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
this.MessageLoaderUtils = MessageLoaderUtils;
|
||||
@ -247,7 +168,7 @@ this.MessageLoaderUtils = MessageLoaderUtils;
|
||||
* so that it can be more easily unit tested.
|
||||
*/
|
||||
class _ASRouter {
|
||||
constructor(localProviders = LOCAL_MESSAGE_PROVIDERS) {
|
||||
constructor(messageProviderPref = MESSAGE_PROVIDER_PREF, localProviders = LOCAL_MESSAGE_PROVIDERS) {
|
||||
this.initialized = false;
|
||||
this.messageChannel = null;
|
||||
this.dispatchToAS = null;
|
||||
@ -260,53 +181,41 @@ class _ASRouter {
|
||||
providerBlockList: [],
|
||||
messageImpressions: {},
|
||||
providerImpressions: {},
|
||||
messages: [],
|
||||
messages: []
|
||||
};
|
||||
this._triggerHandler = this._triggerHandler.bind(this);
|
||||
this._messageProviderPref = messageProviderPref;
|
||||
this._localProviders = localProviders;
|
||||
this.onMessage = this.onMessage.bind(this);
|
||||
this._handleTargetingError = this._handleTargetingError.bind(this);
|
||||
this.onPrefChange = this.onPrefChange.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns legacy onboarding off or on using the ONBOARDING_FINISHED_PREF.
|
||||
* This is required since ASRouter also shows snippets and onboarding, which
|
||||
* interferes with legacy onboarding.
|
||||
*
|
||||
* Note that when this pref is true, legacy onboarding does NOT show up;
|
||||
* when it is false, iegacy onboarding may show up if the profile age etc.
|
||||
* is appropriate for the user to see it.
|
||||
*/
|
||||
overrideOrEnableLegacyOnboarding() {
|
||||
const {allowLegacyOnboarding} = ASRouterPreferences.specialConditions;
|
||||
const onboardingFinished = Services.prefs.getBoolPref(ONBOARDING_FINISHED_PREF, true);
|
||||
|
||||
if (!allowLegacyOnboarding && onboardingFinished === false) {
|
||||
Services.prefs.setBoolPref(ONBOARDING_FINISHED_PREF, true);
|
||||
} else if (allowLegacyOnboarding && onboardingFinished === true) {
|
||||
Services.prefs.setBoolPref(ONBOARDING_FINISHED_PREF, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Update message providers and fetch new messages on pref change
|
||||
async onPrefChange() {
|
||||
this._updateMessageProviders();
|
||||
this.overrideOrEnableLegacyOnboarding();
|
||||
async observe(aSubject, aTopic, aPrefName) {
|
||||
if (aPrefName === this._messageProviderPref) {
|
||||
this._updateMessageProviders();
|
||||
}
|
||||
|
||||
await this.loadMessagesFromAllProviders();
|
||||
this.dispatchToAS(ac.BroadcastToContent({type: at.AS_ROUTER_PREF_CHANGED, data: ASRouterPreferences.specialConditions}));
|
||||
}
|
||||
|
||||
// Fetch and decode the message provider pref JSON, and update the message providers
|
||||
_updateMessageProviders() {
|
||||
const providers = [
|
||||
// If we have added a `preview` provider, hold onto it
|
||||
...this.state.providers.filter(p => p.id === "preview"),
|
||||
...ASRouterPreferences.providers.filter(p => p.enabled),
|
||||
].map(_provider => {
|
||||
// make a copy so we don't modify the source of the pref
|
||||
const provider = {..._provider};
|
||||
// If we have added a `preview` provider, hold onto it
|
||||
const existingPreviewProvider = this.state.providers.find(p => p.id === "preview");
|
||||
const providers = existingPreviewProvider ? [existingPreviewProvider] : [];
|
||||
const providersJSON = Services.prefs.getStringPref(this._messageProviderPref, "");
|
||||
try {
|
||||
JSON.parse(providersJSON).forEach(provider => {
|
||||
if (provider.enabled) {
|
||||
providers.push(provider);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
Cu.reportError("Problem parsing JSON message provider pref for ASRouter");
|
||||
}
|
||||
|
||||
providers.forEach(provider => {
|
||||
if (provider.type === "local" && !provider.messages) {
|
||||
// Get the messages from the local message provider
|
||||
const localProvider = this._localProviders[provider.localProvider];
|
||||
@ -318,14 +227,13 @@ class _ASRouter {
|
||||
}
|
||||
// Reset provider update timestamp to force message refresh
|
||||
provider.lastUpdated = undefined;
|
||||
return provider;
|
||||
});
|
||||
|
||||
const providerIDs = providers.map(p => p.id);
|
||||
this.setState(prevState => ({
|
||||
providers,
|
||||
// Clear any messages from removed providers
|
||||
messages: [...prevState.messages.filter(message => providerIDs.includes(message.provider))],
|
||||
messages: [...prevState.messages.filter(message => providerIDs.includes(message.provider))]
|
||||
}));
|
||||
}
|
||||
|
||||
@ -367,7 +275,7 @@ class _ASRouter {
|
||||
let newState = {messages: [], providers: []};
|
||||
for (const provider of this.state.providers) {
|
||||
if (needsUpdate.includes(provider)) {
|
||||
const {messages, lastUpdated} = await MessageLoaderUtils.loadMessagesForProvider(provider, this._storage);
|
||||
const {messages, lastUpdated} = await MessageLoaderUtils.loadMessagesForProvider(provider);
|
||||
newState.providers.push({...provider, lastUpdated});
|
||||
newState.messages = [...newState.messages, ...messages];
|
||||
} else {
|
||||
@ -410,45 +318,30 @@ class _ASRouter {
|
||||
async init(channel, storage, dispatchToAS) {
|
||||
this.messageChannel = channel;
|
||||
this.messageChannel.addMessageListener(INCOMING_MESSAGE_NAME, this.onMessage);
|
||||
Services.prefs.addObserver(this._messageProviderPref, this);
|
||||
this._storage = storage;
|
||||
this.WHITELIST_HOSTS = this._loadSnippetsWhitelistHosts();
|
||||
this.dispatchToAS = dispatchToAS;
|
||||
this.dispatch = this.dispatch.bind(this);
|
||||
|
||||
ASRouterPreferences.init();
|
||||
ASRouterPreferences.addListener(this.onPrefChange);
|
||||
|
||||
const messageBlockList = await this._storage.get("messageBlockList") || [];
|
||||
const providerBlockList = await this._storage.get("providerBlockList") || [];
|
||||
const messageImpressions = await this._storage.get("messageImpressions") || {};
|
||||
const providerImpressions = await this._storage.get("providerImpressions") || {};
|
||||
const previousSessionEnd = await this._storage.get("previousSessionEnd") || 0;
|
||||
await this.setState({messageBlockList, providerBlockList, messageImpressions, providerImpressions, previousSessionEnd});
|
||||
await this.setState({messageBlockList, providerBlockList, messageImpressions, providerImpressions});
|
||||
this._updateMessageProviders();
|
||||
this.overrideOrEnableLegacyOnboarding();
|
||||
await this.loadMessagesFromAllProviders();
|
||||
await MessageLoaderUtils.cleanupCache(this.state.providers, storage);
|
||||
|
||||
// set necessary state in the rest of AS
|
||||
this.dispatchToAS(ac.BroadcastToContent({type: at.AS_ROUTER_INITIALIZED, data: ASRouterPreferences.specialConditions}));
|
||||
|
||||
// sets .initialized to true and resolves .waitForInitialized promise
|
||||
this._finishInitializing();
|
||||
}
|
||||
|
||||
uninit() {
|
||||
this._storage.set("previousSessionEnd", Date.now());
|
||||
|
||||
this.messageChannel.sendAsyncMessage(OUTGOING_MESSAGE_NAME, {type: "CLEAR_ALL"});
|
||||
this.messageChannel.removeMessageListener(INCOMING_MESSAGE_NAME, this.onMessage);
|
||||
this.messageChannel = null;
|
||||
this.dispatchToAS = null;
|
||||
|
||||
this.overrideOrEnableLegacyOnboarding();
|
||||
|
||||
ASRouterPreferences.removeListener(this.onPrefChange);
|
||||
ASRouterPreferences.uninit();
|
||||
|
||||
Services.prefs.removeObserver(this._messageProviderPref, this);
|
||||
// Uninitialise all trigger listeners
|
||||
for (const listener of ASRouterTriggerListeners.values()) {
|
||||
listener.uninit();
|
||||
@ -472,9 +365,7 @@ class _ASRouter {
|
||||
}
|
||||
|
||||
_onStateChanged(state) {
|
||||
if (ASRouterPreferences.devtoolsEnabled) {
|
||||
this.messageChannel.sendAsyncMessage(OUTGOING_MESSAGE_NAME, {type: "ADMIN_SET_STATE", data: this.state});
|
||||
}
|
||||
this.messageChannel.sendAsyncMessage(OUTGOING_MESSAGE_NAME, {type: "ADMIN_SET_STATE", data: state});
|
||||
}
|
||||
|
||||
_handleTargetingError(type, error, message) {
|
||||
@ -484,28 +375,17 @@ class _ASRouter {
|
||||
message_id: message.id,
|
||||
action: "asrouter_undesired_event",
|
||||
event: "TARGETING_EXPRESSION_ERROR",
|
||||
value: type,
|
||||
value: type
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Return an object containing targeting parameters used to select messages
|
||||
_getMessagesContext() {
|
||||
const {previousSessionEnd} = this.state;
|
||||
return {
|
||||
get previousSessionEnd() {
|
||||
return previousSessionEnd;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
_findMessage(candidateMessages, trigger) {
|
||||
const messages = candidateMessages.filter(m => this.isBelowFrequencyCaps(m));
|
||||
const context = this._getMessagesContext();
|
||||
|
||||
// Find a message that matches the targeting context as well as the trigger context (if one is provided)
|
||||
// If no trigger is provided, we should find a message WITHOUT a trigger property defined.
|
||||
return ASRouterTargeting.findMatchingMessage({messages, trigger, context, onError: this._handleTargetingError});
|
||||
return ASRouterTargeting.findMatchingMessage({messages, trigger, onError: this._handleTargetingError});
|
||||
}
|
||||
|
||||
_orderBundle(bundle) {
|
||||
@ -735,7 +615,7 @@ class _ASRouter {
|
||||
// We don't want to cache preview messages, remove them after we selected the message to show
|
||||
await this.setState(state => ({
|
||||
lastMessageId: message.id,
|
||||
messages: state.messages.filter(m => m.id !== message.id),
|
||||
messages: state.messages.filter(m => m.id !== message.id)
|
||||
}));
|
||||
} else {
|
||||
await this.setState({lastMessageId: message ? message.id : null});
|
||||
@ -825,11 +705,9 @@ class _ASRouter {
|
||||
return state;
|
||||
}
|
||||
|
||||
async _addPreviewEndpoint(url, portID) {
|
||||
// When you view a preview snippet we want to hide all real content
|
||||
async _addPreviewEndpoint(url) {
|
||||
const providers = [...this.state.providers];
|
||||
if (this._validPreviewEndpoint(url) && !providers.find(p => p.url === url)) {
|
||||
this.dispatchToAS(ac.OnlyToOneContent({type: at.SNIPPETS_PREVIEW_MODE}, portID));
|
||||
providers.push({id: "preview", type: "remote", url, updateCycleInMs: 0});
|
||||
await this.setState({providers});
|
||||
}
|
||||
@ -844,7 +722,7 @@ class _ASRouter {
|
||||
case ra.OPEN_URL:
|
||||
target.browser.ownerGlobal.openLinkIn(action.data.url, "tabshifted", {
|
||||
private: false,
|
||||
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
|
||||
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({})
|
||||
});
|
||||
break;
|
||||
case ra.OPEN_ABOUT_PAGE:
|
||||
@ -876,7 +754,7 @@ class _ASRouter {
|
||||
// Wait for our initial message loading to be done before responding to any UI requests
|
||||
await this.waitForInitialized;
|
||||
if (action.data && action.data.endpoint) {
|
||||
await this._addPreviewEndpoint(action.data.endpoint.url, target.portID);
|
||||
await this._addPreviewEndpoint(action.data.endpoint.url);
|
||||
}
|
||||
// Check if any updates are needed first
|
||||
await this.loadMessagesFromAllProviders();
|
||||
@ -925,7 +803,7 @@ class _ASRouter {
|
||||
break;
|
||||
case "ADMIN_CONNECT_STATE":
|
||||
if (action.data && action.data.endpoint) {
|
||||
this._addPreviewEndpoint(action.data.endpoint.url, target.portID);
|
||||
this._addPreviewEndpoint(action.data.endpoint.url);
|
||||
await this.loadMessagesFromAllProviders();
|
||||
} else {
|
||||
target.sendAsyncMessage(OUTGOING_MESSAGE_NAME, {type: "ADMIN_SET_STATE", data: this.state});
|
||||
|
@ -1,5 +1,8 @@
|
||||
const {actionTypes: at} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
|
||||
const {ASRouter} = ChromeUtils.import("resource://activity-stream/lib/ASRouter.jsm", {});
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
|
||||
|
||||
/**
|
||||
* @class ASRouterFeed - Connects ASRouter singleton (see above) to Activity Stream's
|
||||
@ -11,25 +14,47 @@ class ASRouterFeed {
|
||||
}
|
||||
|
||||
async enable() {
|
||||
if (!this.router.initialized) {
|
||||
await this.router.init(
|
||||
this.store._messageChannel.channel,
|
||||
this.store.dbStorage.getDbTable("snippets"),
|
||||
this.store.dispatch
|
||||
);
|
||||
}
|
||||
await this.router.init(
|
||||
this.store._messageChannel.channel,
|
||||
this.store.dbStorage.getDbTable("snippets"),
|
||||
this.store.dispatch
|
||||
);
|
||||
// Disable onboarding
|
||||
Services.prefs.setBoolPref(ONBOARDING_FINISHED_PREF, true);
|
||||
}
|
||||
|
||||
disable() {
|
||||
if (this.router.initialized) {
|
||||
this.router.uninit();
|
||||
// Re-enable onboarding
|
||||
Services.prefs.setBoolPref(ONBOARDING_FINISHED_PREF, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* enableOrDisableBasedOnPref - Check the experiment pref
|
||||
* (asrouterExperimentEnabled) and enable or disable ASRouter based on
|
||||
* its value.
|
||||
*/
|
||||
enableOrDisableBasedOnPref() {
|
||||
const prefs = this.store.getState().Prefs.values;
|
||||
const isExperimentEnabled = prefs.asrouterExperimentEnabled;
|
||||
if (!this.router.initialized && isExperimentEnabled) {
|
||||
this.enable();
|
||||
} else if (!isExperimentEnabled && this.router.initialized) {
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
|
||||
onAction(action) {
|
||||
switch (action.type) {
|
||||
case at.INIT:
|
||||
this.enable();
|
||||
this.enableOrDisableBasedOnPref();
|
||||
break;
|
||||
case at.PREF_CHANGED:
|
||||
if (action.data.name === "asrouterExperimentEnabled") {
|
||||
this.enableOrDisableBasedOnPref();
|
||||
}
|
||||
break;
|
||||
case at.UNINIT:
|
||||
this.disable();
|
||||
|
@ -1,104 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const PROVIDER_PREF = "browser.newtabpage.activity-stream.asrouter.messageProviders";
|
||||
const DEVTOOLS_PREF = "browser.newtabpage.activity-stream.asrouter.devtoolsEnabled";
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
_initialized: false,
|
||||
_providers: null,
|
||||
_providerPref: PROVIDER_PREF,
|
||||
_devtoolsEnabled: null,
|
||||
_devtoolsPref: DEVTOOLS_PREF,
|
||||
};
|
||||
|
||||
class _ASRouterPreferences {
|
||||
constructor() {
|
||||
Object.assign(this, DEFAULT_STATE);
|
||||
this._callbacks = new Set();
|
||||
}
|
||||
|
||||
get providers() {
|
||||
if (!this._initialized || this._providers === null) {
|
||||
try {
|
||||
const parsed = JSON.parse(Services.prefs.getStringPref(this._providerPref, ""));
|
||||
this._providers = Object.freeze(parsed.map(provider => Object.freeze(provider)));
|
||||
} catch (e) {
|
||||
Cu.reportError("Problem parsing JSON message provider pref for ASRouter");
|
||||
this._providers = [];
|
||||
}
|
||||
}
|
||||
return this._providers;
|
||||
}
|
||||
|
||||
get devtoolsEnabled() {
|
||||
if (!this._initialized || this._devtoolsEnabled === null) {
|
||||
this._devtoolsEnabled = Services.prefs.getBoolPref(this._devtoolsPref, false);
|
||||
}
|
||||
return this._devtoolsEnabled;
|
||||
}
|
||||
|
||||
get specialConditions() {
|
||||
let allowLegacyOnboarding = true;
|
||||
let allowLegacySnippets = true;
|
||||
for (const provider of this.providers) {
|
||||
if (provider.id === "onboarding" && provider.enabled && provider.cohort) {
|
||||
allowLegacyOnboarding = false;
|
||||
}
|
||||
if (provider.id === "snippets" && provider.enabled) {
|
||||
allowLegacySnippets = false;
|
||||
}
|
||||
}
|
||||
return {
|
||||
allowLegacyOnboarding,
|
||||
allowLegacySnippets,
|
||||
};
|
||||
}
|
||||
|
||||
observe(aSubject, aTopic, aPrefName) {
|
||||
switch (aPrefName) {
|
||||
case this._providerPref:
|
||||
this._providers = null;
|
||||
break;
|
||||
case this._devtoolsPref:
|
||||
this._devtoolsEnabled = null;
|
||||
break;
|
||||
}
|
||||
this._callbacks.forEach(cb => cb(aPrefName));
|
||||
}
|
||||
|
||||
addListener(callback) {
|
||||
this._callbacks.add(callback);
|
||||
}
|
||||
|
||||
removeListener(callback) {
|
||||
this._callbacks.delete(callback);
|
||||
}
|
||||
|
||||
init() {
|
||||
if (this._initialized) {
|
||||
return;
|
||||
}
|
||||
Services.prefs.addObserver(this._providerPref, this);
|
||||
Services.prefs.addObserver(this._devtoolsPref, this);
|
||||
this._initialized = true;
|
||||
}
|
||||
|
||||
uninit() {
|
||||
if (this._initialized) {
|
||||
Services.prefs.removeObserver(this._providerPref, this);
|
||||
Services.prefs.removeObserver(this._devtoolsPref, this);
|
||||
}
|
||||
Object.assign(this, DEFAULT_STATE);
|
||||
this._callbacks.clear();
|
||||
}
|
||||
}
|
||||
this._ASRouterPreferences = _ASRouterPreferences;
|
||||
|
||||
this.ASRouterPreferences = new _ASRouterPreferences();
|
||||
|
||||
const EXPORTED_SYMBOLS = ["_ASRouterPreferences", "ASRouterPreferences"];
|
@ -1,8 +1,6 @@
|
||||
ChromeUtils.import("resource://gre/modules/components-utils/FilterExpressions.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "ASRouterPreferences",
|
||||
"resource://activity-stream/lib/ASRouterPreferences.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "NewTabUtils",
|
||||
@ -13,11 +11,9 @@ ChromeUtils.defineModuleGetter(this, "ShellService",
|
||||
"resource:///modules/ShellService.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "TelemetryEnvironment",
|
||||
"resource://gre/modules/TelemetryEnvironment.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "AppConstants",
|
||||
"resource://gre/modules/AppConstants.jsm");
|
||||
|
||||
const FXA_USERNAME_PREF = "services.sync.username";
|
||||
const SEARCH_REGION_PREF = "browser.search.region";
|
||||
const MESSAGE_PROVDIER_EXPERIMENT_PREF = "browser.newtabpage.activity-stream.asrouter.messageProviders";
|
||||
const MOZ_JEXL_FILEPATH = "mozjexl";
|
||||
|
||||
const {activityStreamProvider: asProvider} = NewTabUtils;
|
||||
@ -27,48 +23,31 @@ const FRECENT_SITES_IGNORE_BLOCKED = true;
|
||||
const FRECENT_SITES_NUM_ITEMS = 25;
|
||||
const FRECENT_SITES_MIN_FRECENCY = 100;
|
||||
|
||||
function CachedTargetingGetter(property, options = null, updateInterval = FRECENT_SITES_UPDATE_INTERVAL) {
|
||||
const targetingGetter = {
|
||||
_lastUpdated: 0,
|
||||
_value: null,
|
||||
// For testing
|
||||
expire() {
|
||||
this._lastUpdated = 0;
|
||||
this._value = null;
|
||||
},
|
||||
};
|
||||
|
||||
Object.defineProperty(targetingGetter, property, {
|
||||
get: () => new Promise(async (resolve, reject) => {
|
||||
const TopFrecentSitesCache = {
|
||||
_lastUpdated: 0,
|
||||
_topFrecentSites: null,
|
||||
get topFrecentSites() {
|
||||
return new Promise(async resolve => {
|
||||
const now = Date.now();
|
||||
if (now - targetingGetter._lastUpdated >= updateInterval) {
|
||||
try {
|
||||
targetingGetter._value = await asProvider[property](options);
|
||||
targetingGetter._lastUpdated = now;
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
reject(e);
|
||||
}
|
||||
if (now - this._lastUpdated >= FRECENT_SITES_UPDATE_INTERVAL) {
|
||||
this._topFrecentSites = await asProvider.getTopFrecentSites({
|
||||
ignoreBlocked: FRECENT_SITES_IGNORE_BLOCKED,
|
||||
numItems: FRECENT_SITES_NUM_ITEMS,
|
||||
topsiteFrecency: FRECENT_SITES_MIN_FRECENCY,
|
||||
onePerDomain: true,
|
||||
includeFavicon: false
|
||||
});
|
||||
this._lastUpdated = now;
|
||||
}
|
||||
resolve(targetingGetter._value);
|
||||
}),
|
||||
});
|
||||
|
||||
return targetingGetter;
|
||||
}
|
||||
|
||||
const TopFrecentSitesCache = new CachedTargetingGetter(
|
||||
"getTopFrecentSites",
|
||||
{
|
||||
ignoreBlocked: FRECENT_SITES_IGNORE_BLOCKED,
|
||||
numItems: FRECENT_SITES_NUM_ITEMS,
|
||||
topsiteFrecency: FRECENT_SITES_MIN_FRECENCY,
|
||||
onePerDomain: true,
|
||||
includeFavicon: false,
|
||||
resolve(this._topFrecentSites);
|
||||
});
|
||||
},
|
||||
// For testing
|
||||
expire() {
|
||||
this._lastUpdated = 0;
|
||||
this._topFrecentSites = null;
|
||||
}
|
||||
);
|
||||
|
||||
const TotalBookmarksCountCache = new CachedTargetingGetter("getTotalBookmarksCount");
|
||||
};
|
||||
|
||||
/**
|
||||
* removeRandomItemFromArray - Removes a random item from the array and returns it.
|
||||
@ -85,7 +64,7 @@ const TargetingGetters = {
|
||||
const {settings} = TelemetryEnvironment.currentEnvironment;
|
||||
return {
|
||||
attribution: settings.attribution,
|
||||
update: settings.update,
|
||||
update: settings.update
|
||||
};
|
||||
},
|
||||
get currentDate() {
|
||||
@ -97,14 +76,14 @@ const TargetingGetters = {
|
||||
get profileAgeReset() {
|
||||
return new ProfileAge(null, null).reset;
|
||||
},
|
||||
get usesFirefoxSync() {
|
||||
get hasFxAccount() {
|
||||
return Services.prefs.prefHasUserValue(FXA_USERNAME_PREF);
|
||||
},
|
||||
get sync() {
|
||||
return {
|
||||
desktopDevices: Services.prefs.getIntPref("services.sync.clients.devices.desktop", 0),
|
||||
mobileDevices: Services.prefs.getIntPref("services.sync.clients.devices.mobile", 0),
|
||||
totalDevices: Services.prefs.getIntPref("services.sync.numClients", 0),
|
||||
totalDevices: Services.prefs.getIntPref("services.sync.numClients", 0)
|
||||
};
|
||||
},
|
||||
get addonsInfo() {
|
||||
@ -116,13 +95,13 @@ const TargetingGetters = {
|
||||
version: addon.version,
|
||||
type: addon.type,
|
||||
isSystem: addon.isSystem,
|
||||
isWebExtension: addon.isWebExtension,
|
||||
isWebExtension: addon.isWebExtension
|
||||
};
|
||||
if (fullData) {
|
||||
Object.assign(info[addon.id], {
|
||||
name: addon.name,
|
||||
userDisabled: addon.userDisabled,
|
||||
installDate: addon.installDate,
|
||||
installDate: addon.installDate
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -139,7 +118,7 @@ const TargetingGetters = {
|
||||
current: Services.search.defaultEngine.identifier,
|
||||
installed: engines
|
||||
.map(engine => engine.identifier)
|
||||
.filter(engine => engine),
|
||||
.filter(engine => engine)
|
||||
});
|
||||
} else {
|
||||
resolve({installed: [], current: ""});
|
||||
@ -157,35 +136,39 @@ const TargetingGetters = {
|
||||
return Services.prefs.getIntPref("devtools.selfxss.count");
|
||||
},
|
||||
get topFrecentSites() {
|
||||
return TopFrecentSitesCache.getTopFrecentSites.then(sites => sites.map(site => (
|
||||
return TopFrecentSitesCache.topFrecentSites.then(sites => sites.map(site => (
|
||||
{
|
||||
url: site.url,
|
||||
host: (new URL(site.url)).hostname,
|
||||
frecency: site.frecency,
|
||||
lastVisitDate: site.lastVisitDate,
|
||||
lastVisitDate: site.lastVisitDate
|
||||
}
|
||||
)));
|
||||
},
|
||||
// Temporary targeting function for the purposes of running the simplified onboarding experience
|
||||
get isInExperimentCohort() {
|
||||
const {cohort} = ASRouterPreferences.providers.find(i => i.id === "onboarding") || {};
|
||||
return (typeof cohort === "number" ? cohort : 0);
|
||||
const allProviders = Services.prefs.getStringPref(MESSAGE_PROVDIER_EXPERIMENT_PREF, "");
|
||||
try {
|
||||
const {cohort} = JSON.parse(allProviders).find(i => i.id === "onboarding");
|
||||
return (typeof cohort === "number" ? cohort : 0);
|
||||
} catch (e) {
|
||||
Cu.reportError("Problem parsing JSON message provider pref for ASRouter");
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
get providerCohorts() {
|
||||
return ASRouterPreferences.providers.reduce((prev, current) => {
|
||||
prev[current.id] = current.cohort || "";
|
||||
return prev;
|
||||
}, {});
|
||||
},
|
||||
get totalBookmarksCount() {
|
||||
return TotalBookmarksCountCache.getTotalBookmarksCount;
|
||||
},
|
||||
get firefoxVersion() {
|
||||
return parseInt(AppConstants.MOZ_APP_VERSION.match(/\d+/), 10);
|
||||
},
|
||||
get region() {
|
||||
return Services.prefs.getStringPref(SEARCH_REGION_PREF, "");
|
||||
},
|
||||
const allProviders = Services.prefs.getStringPref(MESSAGE_PROVDIER_EXPERIMENT_PREF, "");
|
||||
const cohorts = {};
|
||||
try {
|
||||
JSON.parse(allProviders).reduce((prev, current) => {
|
||||
prev[current.id] = current.cohort || "";
|
||||
return prev;
|
||||
}, cohorts);
|
||||
} catch (e) {
|
||||
Cu.reportError("Problem parsing JSON message provider pref for ASRouter");
|
||||
}
|
||||
return cohorts;
|
||||
}
|
||||
};
|
||||
|
||||
this.ASRouterTargeting = {
|
||||
@ -193,17 +176,10 @@ this.ASRouterTargeting = {
|
||||
|
||||
ERROR_TYPES: {
|
||||
MALFORMED_EXPRESSION: "MALFORMED_EXPRESSION",
|
||||
OTHER_ERROR: "OTHER_ERROR",
|
||||
OTHER_ERROR: "OTHER_ERROR"
|
||||
},
|
||||
|
||||
isMatch(filterExpression, customContext) {
|
||||
let context = this.Environment;
|
||||
if (customContext) {
|
||||
context = {};
|
||||
Object.defineProperties(context, Object.getOwnPropertyDescriptors(this.Environment));
|
||||
Object.defineProperties(context, Object.getOwnPropertyDescriptors(customContext));
|
||||
}
|
||||
|
||||
isMatch(filterExpression, context = this.Environment) {
|
||||
return FilterExpressions.eval(filterExpression, context);
|
||||
},
|
||||
|
||||
@ -272,11 +248,9 @@ this.ASRouterTargeting = {
|
||||
}
|
||||
}
|
||||
return match;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Export for testing
|
||||
this.TopFrecentSitesCache = TopFrecentSitesCache;
|
||||
this.TotalBookmarksCountCache = TotalBookmarksCountCache;
|
||||
this.CachedTargetingGetter = CachedTargetingGetter;
|
||||
this.EXPORTED_SYMBOLS = ["ASRouterTargeting", "TopFrecentSitesCache", "TotalBookmarksCountCache", "CachedTargetingGetter"];
|
||||
this.EXPORTED_SYMBOLS = ["ASRouterTargeting", "TopFrecentSitesCache"];
|
||||
|
@ -107,8 +107,8 @@ this.ASRouterTriggerListeners = new Map([
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
}],
|
||||
}
|
||||
}]
|
||||
]);
|
||||
|
||||
const EXPORTED_SYMBOLS = ["ASRouterTriggerListeners"];
|
||||
|
@ -19,21 +19,21 @@ const PREFS_BEFORE_SECTIONS = [
|
||||
id: "search",
|
||||
pref: {
|
||||
feed: "showSearch",
|
||||
titleString: "prefs_search_header",
|
||||
titleString: "prefs_search_header"
|
||||
},
|
||||
icon: "chrome://browser/skin/search-glass.svg",
|
||||
icon: "chrome://browser/skin/search-glass.svg"
|
||||
},
|
||||
{
|
||||
id: "topsites",
|
||||
pref: {
|
||||
feed: "feeds.topsites",
|
||||
titleString: "settings_pane_topsites_header",
|
||||
descString: "prefs_topsites_description",
|
||||
descString: "prefs_topsites_description"
|
||||
},
|
||||
icon: "topsites",
|
||||
maxRows: 4,
|
||||
rowsPref: "topSitesRows",
|
||||
},
|
||||
rowsPref: "topSitesRows"
|
||||
}
|
||||
];
|
||||
const PREFS_AFTER_SECTIONS = [
|
||||
{
|
||||
@ -41,10 +41,10 @@ const PREFS_AFTER_SECTIONS = [
|
||||
pref: {
|
||||
feed: "feeds.snippets",
|
||||
titleString: "settings_pane_snippets_header",
|
||||
descString: "prefs_snippets_description",
|
||||
descString: "prefs_snippets_description"
|
||||
},
|
||||
icon: "info",
|
||||
},
|
||||
icon: "info"
|
||||
}
|
||||
];
|
||||
|
||||
// This CSS is added to the whole about:preferences page
|
||||
@ -175,13 +175,13 @@ this.AboutPreferences = class AboutPreferences {
|
||||
icon = "webextension",
|
||||
maxRows,
|
||||
rowsPref,
|
||||
shouldHidePref,
|
||||
shouldHidePref
|
||||
} = sectionData;
|
||||
const {
|
||||
feed: name,
|
||||
titleString,
|
||||
descString,
|
||||
nestedPrefs = [],
|
||||
nestedPrefs = []
|
||||
} = prefData || {};
|
||||
|
||||
// Don't show any sections that we don't want to expose in preferences UI
|
||||
|
@ -37,7 +37,7 @@ const DEFAULT_SITES = new Map([
|
||||
["PL", "https://www.youtube.com/,https://www.facebook.com/,https://allegro.pl/,https://www.wikipedia.org/,https://www.olx.pl/,https://www.wykop.pl/"],
|
||||
["RU", "https://vk.com/,https://www.youtube.com/,https://ok.ru/,https://www.avito.ru/,https://www.aliexpress.com/,https://www.wikipedia.org/"],
|
||||
["GB", "https://www.youtube.com/,https://www.facebook.com/,https://www.reddit.com/,https://www.amazon.co.uk/,https://www.bbc.co.uk/,https://www.ebay.co.uk/"],
|
||||
["FR", "https://www.youtube.com/,https://www.facebook.com/,https://www.wikipedia.org/,https://www.amazon.fr/,https://www.leboncoin.fr/,https://twitter.com/"],
|
||||
["FR", "https://www.youtube.com/,https://www.facebook.com/,https://www.wikipedia.org/,https://www.amazon.fr/,https://www.leboncoin.fr/,https://twitter.com/"]
|
||||
]);
|
||||
const GEO_PREF = "browser.search.region";
|
||||
const SPOCS_GEOS = ["US"];
|
||||
@ -54,7 +54,7 @@ function showSpocs({geo}) {
|
||||
const PREFS_CONFIG = new Map([
|
||||
["default.sites", {
|
||||
title: "Comma-separated list of default top sites to fill in behind visited sites",
|
||||
getValue: ({geo}) => DEFAULT_SITES.get(DEFAULT_SITES.has(geo) ? geo : ""),
|
||||
getValue: ({geo}) => DEFAULT_SITES.get(DEFAULT_SITES.has(geo) ? geo : "")
|
||||
}],
|
||||
["feeds.section.topstories.options", {
|
||||
title: "Configuration options for top stories feed",
|
||||
@ -70,12 +70,12 @@ const PREFS_CONFIG = new Map([
|
||||
stories_referrer: "https://getpocket.com/recommendations",
|
||||
topics_endpoint: `https://getpocket.cdn.mozilla.net/v3/firefox/trending-topics?version=2&consumer_key=$apiKey&locale_lang=${args.locale}`,
|
||||
show_spocs: showSpocs(args),
|
||||
personalized: true,
|
||||
}),
|
||||
personalized: true
|
||||
})
|
||||
}],
|
||||
["showSponsored", {
|
||||
title: "Show sponsored cards in spoc experiment (show_spocs in topstories.options has to be set to true as well)",
|
||||
value: true,
|
||||
value: true
|
||||
}],
|
||||
["pocketCta", {
|
||||
title: "Pocket cta and button for logged out users.",
|
||||
@ -83,90 +83,90 @@ const PREFS_CONFIG = new Map([
|
||||
cta_button: "",
|
||||
cta_text: "",
|
||||
cta_url: "",
|
||||
use_cta: false,
|
||||
}),
|
||||
use_cta: false
|
||||
})
|
||||
}],
|
||||
["filterAdult", {
|
||||
title: "Remove adult pages from sites, highlights, etc.",
|
||||
value: true,
|
||||
value: true
|
||||
}],
|
||||
["migrationExpired", {
|
||||
title: "Boolean flag that decides whether to show the migration message or not.",
|
||||
value: false,
|
||||
value: false
|
||||
}],
|
||||
["migrationLastShownDate", {
|
||||
title: "Timestamp when migration message was last shown. In seconds.",
|
||||
value: 0,
|
||||
value: 0
|
||||
}],
|
||||
["migrationRemainingDays", {
|
||||
title: "Number of days to show the manual migration message",
|
||||
value: 4,
|
||||
value: 4
|
||||
}],
|
||||
["prerender", {
|
||||
title: "Use the prerendered version of activity-stream.html. This is set automatically by PrefsFeed.jsm.",
|
||||
value: true,
|
||||
value: true
|
||||
}],
|
||||
["showSearch", {
|
||||
title: "Show the Search bar",
|
||||
value: true,
|
||||
value: true
|
||||
}],
|
||||
["disableSnippets", {
|
||||
title: "Disable snippets on activity stream",
|
||||
value: false,
|
||||
value: false
|
||||
}],
|
||||
["topSitesRows", {
|
||||
title: "Number of rows of Top Sites to display",
|
||||
value: 1,
|
||||
value: 1
|
||||
}],
|
||||
["telemetry", {
|
||||
title: "Enable system error and usage data collection",
|
||||
value: true,
|
||||
value_local_dev: false,
|
||||
value_local_dev: false
|
||||
}],
|
||||
["telemetry.ut.events", {
|
||||
title: "Enable Unified Telemetry event data collection",
|
||||
value: AppConstants.EARLY_BETA_OR_EARLIER,
|
||||
value_local_dev: false,
|
||||
value_local_dev: false
|
||||
}],
|
||||
["telemetry.ping.endpoint", {
|
||||
title: "Telemetry server endpoint",
|
||||
value: "https://tiles.services.mozilla.com/v4/links/activity-stream",
|
||||
value: "https://tiles.services.mozilla.com/v4/links/activity-stream"
|
||||
}],
|
||||
["section.highlights.includeVisited", {
|
||||
title: "Boolean flag that decides whether or not to show visited pages in highlights.",
|
||||
value: true,
|
||||
value: true
|
||||
}],
|
||||
["section.highlights.includeBookmarks", {
|
||||
title: "Boolean flag that decides whether or not to show bookmarks in highlights.",
|
||||
value: true,
|
||||
value: true
|
||||
}],
|
||||
["section.highlights.includePocket", {
|
||||
title: "Boolean flag that decides whether or not to show saved Pocket stories in highlights.",
|
||||
value: true,
|
||||
value: true
|
||||
}],
|
||||
["section.highlights.includeDownloads", {
|
||||
title: "Boolean flag that decides whether or not to show saved recent Downloads in highlights.",
|
||||
value: true,
|
||||
value: true
|
||||
}],
|
||||
["section.highlights.rows", {
|
||||
title: "Number of rows of Highlights to display",
|
||||
value: 2,
|
||||
value: 2
|
||||
}],
|
||||
["section.topstories.rows", {
|
||||
title: "Number of rows of Top Stories to display",
|
||||
value: 1,
|
||||
value: 1
|
||||
}],
|
||||
["sectionOrder", {
|
||||
title: "The rendering order for the sections",
|
||||
value: "topsites,topstories,highlights",
|
||||
value: "topsites,topstories,highlights"
|
||||
}],
|
||||
["improvesearch.noDefaultSearchTile", {
|
||||
title: "Experiment to remove tiles that are the same as the default search",
|
||||
value: true,
|
||||
value: true
|
||||
}],
|
||||
["improvesearch.topSiteSearchShortcuts", {
|
||||
title: "Experiment to show special top sites that perform keyword searches",
|
||||
value: AppConstants.MOZ_UPDATE_CHANNEL !== "release",
|
||||
value: AppConstants.MOZ_UPDATE_CHANNEL !== "release"
|
||||
}],
|
||||
["improvesearch.topSiteSearchShortcuts.searchEngines", {
|
||||
title: "An ordered, comma-delimited list of search shortcuts that we should try and pin",
|
||||
@ -187,15 +187,15 @@ const PREFS_CONFIG = new Map([
|
||||
searchShortcuts.push("amazon");
|
||||
}
|
||||
return searchShortcuts.join(",");
|
||||
},
|
||||
}
|
||||
}],
|
||||
["improvesearch.topSiteSearchShortcuts.havePinned", {
|
||||
title: "A comma-delimited list of search shortcuts that have previously been pinned",
|
||||
value: "",
|
||||
value: ""
|
||||
}],
|
||||
["asrouter.devtoolsEnabled", {
|
||||
title: "Are the asrouter devtools enabled?",
|
||||
value: false,
|
||||
["asrouterExperimentEnabled", {
|
||||
title: "Is the message center experiment on?",
|
||||
value: false
|
||||
}],
|
||||
["asrouter.messageProviders", {
|
||||
title: "Configuration for ASRouter message providers",
|
||||
@ -211,21 +211,21 @@ const PREFS_CONFIG = new Map([
|
||||
type: "local",
|
||||
localProvider: "OnboardingMessageProvider",
|
||||
enabled: false,
|
||||
cohort: 0,
|
||||
cohort: 0
|
||||
}, {
|
||||
id: "snippets",
|
||||
type: "remote",
|
||||
url: "https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/",
|
||||
url: "https://snippets.cdn.mozilla.net/us-west/bundles/bundle_d6d90fb9098ce8b45e60acf601bcb91b68322309.json",
|
||||
updateCycleInMs: ONE_HOUR_IN_MS * 4,
|
||||
enabled: false,
|
||||
enabled: AppConstants.MOZ_UPDATE_CHANNEL !== "release"
|
||||
}, {
|
||||
id: "cfr",
|
||||
type: "local",
|
||||
localProvider: "CFRMessageProvider",
|
||||
enabled: AppConstants.MOZ_UPDATE_CHANNEL !== "release",
|
||||
cohort: 0,
|
||||
}]),
|
||||
}],
|
||||
cohort: 0
|
||||
}])
|
||||
}]
|
||||
]);
|
||||
|
||||
// Array of each feed's FEEDS_CONFIG factory and values to add to PREFS_CONFIG
|
||||
@ -234,43 +234,43 @@ const FEEDS_DATA = [
|
||||
name: "aboutpreferences",
|
||||
factory: () => new AboutPreferences(),
|
||||
title: "about:preferences rendering",
|
||||
value: true,
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "migration",
|
||||
factory: () => new ManualMigration(),
|
||||
title: "Manual migration wizard",
|
||||
value: true,
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "newtabinit",
|
||||
factory: () => new NewTabInit(),
|
||||
title: "Sends a copy of the state to each new tab that is opened",
|
||||
value: true,
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "places",
|
||||
factory: () => new PlacesFeed(),
|
||||
title: "Listens for and relays various Places-related events",
|
||||
value: true,
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "prefs",
|
||||
factory: () => new PrefsFeed(PREFS_CONFIG),
|
||||
title: "Preferences",
|
||||
value: true,
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "sections",
|
||||
factory: () => new SectionsFeed(),
|
||||
title: "Manages sections",
|
||||
value: true,
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "section.highlights",
|
||||
factory: () => new HighlightsFeed(),
|
||||
title: "Fetches content recommendations from places db",
|
||||
value: true,
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "section.topstories",
|
||||
@ -281,47 +281,47 @@ const FEEDS_DATA = [
|
||||
const locales = ({
|
||||
"US": ["en-CA", "en-GB", "en-US", "en-ZA"],
|
||||
"CA": ["en-CA", "en-GB", "en-US", "en-ZA"],
|
||||
"DE": ["de", "de-DE", "de-AT", "de-CH"],
|
||||
"DE": ["de", "de-DE", "de-AT", "de-CH"]
|
||||
})[geo];
|
||||
return !!locales && locales.includes(locale);
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "snippets",
|
||||
factory: () => new SnippetsFeed(),
|
||||
title: "Gets snippets data",
|
||||
value: true,
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "systemtick",
|
||||
factory: () => new SystemTickFeed(),
|
||||
title: "Produces system tick events to periodically check for data expiry",
|
||||
value: true,
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "telemetry",
|
||||
factory: () => new TelemetryFeed(),
|
||||
title: "Relays telemetry-related actions to PingCentre",
|
||||
value: true,
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "favicon",
|
||||
factory: () => new FaviconFeed(),
|
||||
title: "Fetches tippy top manifests from remote service",
|
||||
value: true,
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "topsites",
|
||||
factory: () => new TopSitesFeed(),
|
||||
title: "Queries places and gets metadata for Top Sites section",
|
||||
value: true,
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "asrouterfeed",
|
||||
factory: () => new ASRouterFeed(),
|
||||
title: "Handles AS Router messages, such as snippets and onboaridng",
|
||||
value: true,
|
||||
},
|
||||
value: true
|
||||
}
|
||||
];
|
||||
|
||||
const FEEDS_CONFIG = new Map();
|
||||
@ -351,7 +351,7 @@ this.ActivityStream = class ActivityStream {
|
||||
// Hook up the store and let all feeds and pages initialize
|
||||
this.store.init(this.feeds, ac.BroadcastToContent({
|
||||
type: at.INIT,
|
||||
data: {},
|
||||
data: {}
|
||||
}), {type: at.UNINIT});
|
||||
|
||||
this.initialized = true;
|
||||
@ -451,7 +451,7 @@ this.ActivityStream = class ActivityStream {
|
||||
|
||||
const newValue = prefConfig.getValue({
|
||||
geo: this.geo,
|
||||
locale: this.locale,
|
||||
locale: this.locale
|
||||
});
|
||||
|
||||
// If there's an existing value and it has changed, that means we need to
|
||||
|
@ -19,7 +19,7 @@ const DEFAULT_OPTIONS = {
|
||||
},
|
||||
pageURL: ABOUT_NEW_TAB_URL,
|
||||
outgoingMessageName: "ActivityStream:MainToContent",
|
||||
incomingMessageName: "ActivityStream:ContentToMain",
|
||||
incomingMessageName: "ActivityStream:ContentToMain"
|
||||
};
|
||||
|
||||
this.ActivityStreamMessageChannel = class ActivityStreamMessageChannel {
|
||||
@ -85,7 +85,7 @@ this.ActivityStreamMessageChannel = class ActivityStreamMessageChannel {
|
||||
* @param {string} targetId The portID of the port that sent the message
|
||||
*/
|
||||
onActionFromContent(action, targetId) {
|
||||
this.dispatch(ac.AlsoToMain(action, this.validatePortID(targetId)));
|
||||
this.dispatch(ac.AlsoToMain(action, targetId));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,18 +112,6 @@ this.ActivityStreamMessageChannel = class ActivityStreamMessageChannel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A valid portID is a combination of process id and port
|
||||
* https://searchfox.org/mozilla-central/rev/196560b95f191b48ff7cba7c2ba9237bba6b5b6a/toolkit/components/remotepagemanager/RemotePageManagerChild.jsm#14
|
||||
*/
|
||||
validatePortID(id) {
|
||||
if (typeof id !== "string" || !id.includes(":")) {
|
||||
Cu.reportError("Invalid portID");
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* getIdByTarget - Retrieve the id of a message target, if it exists in this.targets
|
||||
*
|
||||
@ -131,7 +119,6 @@ this.ActivityStreamMessageChannel = class ActivityStreamMessageChannel {
|
||||
* @return {string|null} The unique id of the target, if it exists.
|
||||
*/
|
||||
getTargetById(id) {
|
||||
this.validatePortID(id);
|
||||
for (let port of this.channel.messagePorts) {
|
||||
if (port.portID === id) {
|
||||
return port;
|
||||
@ -236,7 +223,7 @@ this.ActivityStreamMessageChannel = class ActivityStreamMessageChannel {
|
||||
onNewTabInit(msg) {
|
||||
this.onActionFromContent({
|
||||
type: at.NEW_TAB_INIT,
|
||||
data: msg.target,
|
||||
data: msg.target
|
||||
}, msg.target.portID);
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ this.ActivityStreamStorage = class ActivityStreamStorage {
|
||||
return {
|
||||
get: this._get.bind(this, storeName),
|
||||
getAll: this._getAll.bind(this, storeName),
|
||||
set: this._set.bind(this, storeName),
|
||||
set: this._set.bind(this, storeName)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -7,13 +7,13 @@ const AMAZON_ASSISTANT_PARAMS = {
|
||||
existing_addons: ["abb@amazon.com", "{75c7fe97-5a90-4b54-9052-3534235eaf41}", "{ef34596e-1e43-4e84-b2ff-1e58e287e08d}", "{ea280feb-155a-492e-8016-ac96dd995f2c}", "izer@camelcamelcamel.com", "amptra@keepa.com", "pricealarm@icopron.ch", "{774f76c7-6807-481e-bf64-f9b7d5cda602}"],
|
||||
open_urls: ["smile.amazon.com", "www.audible.com", "www.amazon.com", "amazon.com", "audible.com"],
|
||||
sumo_path: "extensionrecommendations",
|
||||
min_frecency: 10000,
|
||||
min_frecency: 10000
|
||||
};
|
||||
const FACEBOOK_CONTAINER_PARAMS = {
|
||||
existing_addons: ["@contain-facebook", "{bb1b80be-e6b3-40a1-9b6e-9d4073343f0b}", "{a50d61ca-d27b-437a-8b52-5fd801a0a88b}"],
|
||||
open_urls: ["www.facebook.com", "facebook.com"],
|
||||
sumo_path: "extensionrecommendations",
|
||||
min_frecency: 10000,
|
||||
min_frecency: 10000
|
||||
};
|
||||
const GOOGLE_TRANSLATE_PARAMS = {
|
||||
existing_addons: ["jid1-93WyvpgvxzGATw@jetpack", "{087ef4e1-4286-4be6-9aa3-8d6c420ee1db}", "{4170faaa-ee87-4a0e-b57a-1aec49282887}", "jid1-TMndP6cdKgxLcQ@jetpack",
|
||||
@ -23,26 +23,26 @@ const GOOGLE_TRANSLATE_PARAMS = {
|
||||
"{e20e0de5-1667-4df4-bd69-705720e37391}", "{09e26ae9-e9c1-477c-80a6-99934212f2fe}", "mgxtranslator@magemagix.com", "gtranslatewins@mozilla.org"],
|
||||
open_urls: ["translate.google.com"],
|
||||
sumo_path: "extensionrecommendations",
|
||||
min_frecency: 10000,
|
||||
min_frecency: 10000
|
||||
};
|
||||
const YOUTUBE_ENHANCE_PARAMS = {
|
||||
existing_addons: ["enhancerforyoutube@maximerf.addons.mozilla.org", "{dc8f61ab-5e98-4027-98ef-bb2ff6060d71}", "{7b1bf0b6-a1b9-42b0-b75d-252036438bdc}", "jid0-UVAeBCfd34Kk5usS8A1CBiobvM8@jetpack",
|
||||
"iridium@particlecore.github.io", "jid1-ss6kLNCbNz6u0g@jetpack", "{1cf918d2-f4ea-4b4f-b34e-455283fef19f}"],
|
||||
open_urls: ["www.youtube.com", "youtube.com"],
|
||||
sumo_path: "extensionrecommendations",
|
||||
min_frecency: 10000,
|
||||
min_frecency: 10000
|
||||
};
|
||||
const WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS = {
|
||||
existing_addons: ["@wikipediacontextmenusearch", "{ebf47fc8-01d8-4dba-aa04-2118402f4b20}", "{5737a280-b359-4e26-95b0-adec5915a854}", "olivier.debroqueville@gmail.com", "{3923146e-98cb-472b-9c13-f6849d34d6b8}"],
|
||||
open_urls: ["www.wikipedia.org", "wikipedia.org"],
|
||||
sumo_path: "extensionrecommendations",
|
||||
min_frecency: 10000,
|
||||
min_frecency: 10000
|
||||
};
|
||||
const REDDIT_ENHANCEMENT_PARAMS = {
|
||||
existing_addons: ["jid1-xUfzOsOFlzSOXg@jetpack"],
|
||||
open_urls: ["www.reddit.com", "reddit.com"],
|
||||
sumo_path: "extensionrecommendations",
|
||||
min_frecency: 10000,
|
||||
min_frecency: 10000
|
||||
};
|
||||
|
||||
const CFR_MESSAGES = [
|
||||
@ -55,7 +55,7 @@ const CFR_MESSAGES = [
|
||||
heading_text: {string_id: "cfr-doorhanger-extension-heading"},
|
||||
info_icon: {
|
||||
label: {string_id: "cfr-doorhanger-extension-sumo-link"},
|
||||
sumo_path: AMAZON_ASSISTANT_PARAMS.sumo_path,
|
||||
sumo_path: AMAZON_ASSISTANT_PARAMS.sumo_path
|
||||
},
|
||||
addon: {
|
||||
title: "Amazon Assistant",
|
||||
@ -63,7 +63,7 @@ const CFR_MESSAGES = [
|
||||
rating: 3.3,
|
||||
users: 443046,
|
||||
author: "Amazon",
|
||||
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/amazon-browser-bar/",
|
||||
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/amazon-browser-bar/"
|
||||
},
|
||||
text: "Amazon Assistant helps you make better shopping decisions by showing product comparisons at thousands of retail sites.",
|
||||
buttons: {
|
||||
@ -71,20 +71,20 @@ const CFR_MESSAGES = [
|
||||
label: {string_id: "cfr-doorhanger-extension-ok-button"},
|
||||
action: {
|
||||
type: "INSTALL_ADDON_FROM_URL",
|
||||
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/950930/amazon_assistant_for_firefox-10.1805.2.1019-an+fx.xpi`},
|
||||
},
|
||||
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/950930/amazon_assistant_for_firefox-10.1805.2.1019-an+fx.xpi`}
|
||||
}
|
||||
},
|
||||
secondary: {
|
||||
label: {string_id: "cfr-doorhanger-extension-cancel-button"},
|
||||
action: {type: "CANCEL"},
|
||||
},
|
||||
},
|
||||
action: {type: "CANCEL"}
|
||||
}
|
||||
}
|
||||
},
|
||||
frequency: {lifetime: 1},
|
||||
targeting: `
|
||||
(${JSON.stringify(AMAZON_ASSISTANT_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
|
||||
(${JSON.stringify(AMAZON_ASSISTANT_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${AMAZON_ASSISTANT_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
|
||||
trigger: {id: "openURL", params: AMAZON_ASSISTANT_PARAMS.open_urls},
|
||||
trigger: {id: "openURL", params: AMAZON_ASSISTANT_PARAMS.open_urls}
|
||||
},
|
||||
{
|
||||
id: "FACEBOOK_CONTAINER_1",
|
||||
@ -95,7 +95,7 @@ const CFR_MESSAGES = [
|
||||
heading_text: {string_id: "cfr-doorhanger-extension-heading"},
|
||||
info_icon: {
|
||||
label: {string_id: "cfr-doorhanger-extension-sumo-link"},
|
||||
sumo_path: FACEBOOK_CONTAINER_PARAMS.sumo_path,
|
||||
sumo_path: FACEBOOK_CONTAINER_PARAMS.sumo_path
|
||||
},
|
||||
addon: {
|
||||
title: "Facebook Container",
|
||||
@ -103,7 +103,7 @@ const CFR_MESSAGES = [
|
||||
rating: 4.6,
|
||||
users: 299019,
|
||||
author: "Mozilla",
|
||||
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/facebook-container/",
|
||||
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/facebook-container/"
|
||||
},
|
||||
text: "Stop Facebook from tracking your activity across the web. Use Facebook the way you normally do without annoying ads following you around.",
|
||||
buttons: {
|
||||
@ -111,20 +111,20 @@ const CFR_MESSAGES = [
|
||||
label: {string_id: "cfr-doorhanger-extension-ok-button"},
|
||||
action: {
|
||||
type: "INSTALL_ADDON_FROM_URL",
|
||||
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/918624/facebook_container-1.3.1-an+fx-linux.xpi`},
|
||||
},
|
||||
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/918624/facebook_container-1.3.1-an+fx-linux.xpi`}
|
||||
}
|
||||
},
|
||||
secondary: {
|
||||
label: {string_id: "cfr-doorhanger-extension-cancel-button"},
|
||||
action: {type: "CANCEL"},
|
||||
},
|
||||
},
|
||||
action: {type: "CANCEL"}
|
||||
}
|
||||
}
|
||||
},
|
||||
frequency: {lifetime: 1},
|
||||
targeting: `
|
||||
(${JSON.stringify(FACEBOOK_CONTAINER_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
|
||||
(${JSON.stringify(FACEBOOK_CONTAINER_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${FACEBOOK_CONTAINER_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
|
||||
trigger: {id: "openURL", params: FACEBOOK_CONTAINER_PARAMS.open_urls},
|
||||
trigger: {id: "openURL", params: FACEBOOK_CONTAINER_PARAMS.open_urls}
|
||||
},
|
||||
{
|
||||
id: "GOOGLE_TRANSLATE_1",
|
||||
@ -135,7 +135,7 @@ const CFR_MESSAGES = [
|
||||
heading_text: {string_id: "cfr-doorhanger-extension-heading"},
|
||||
info_icon: {
|
||||
label: {string_id: "cfr-doorhanger-extension-sumo-link"},
|
||||
sumo_path: GOOGLE_TRANSLATE_PARAMS.sumo_path,
|
||||
sumo_path: GOOGLE_TRANSLATE_PARAMS.sumo_path
|
||||
},
|
||||
addon: {
|
||||
title: "To Google Translate",
|
||||
@ -143,7 +143,7 @@ const CFR_MESSAGES = [
|
||||
rating: 4.1,
|
||||
users: 313474,
|
||||
author: "Juan Escobar",
|
||||
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/to-google-translate/",
|
||||
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/to-google-translate/"
|
||||
},
|
||||
text: "Instantly translate any webpage text. Simply highlight the text, right-click to open the context menu, and choose a text or aural translation.",
|
||||
buttons: {
|
||||
@ -151,20 +151,20 @@ const CFR_MESSAGES = [
|
||||
label: {string_id: "cfr-doorhanger-extension-ok-button"},
|
||||
action: {
|
||||
type: "INSTALL_ADDON_FROM_URL",
|
||||
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/1008798/al_traductor_de_google-3.3-an+fx.xpi`},
|
||||
},
|
||||
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/1008798/al_traductor_de_google-3.3-an+fx.xpi`}
|
||||
}
|
||||
},
|
||||
secondary: {
|
||||
label: {string_id: "cfr-doorhanger-extension-cancel-button"},
|
||||
action: {type: "CANCEL"},
|
||||
},
|
||||
},
|
||||
action: {type: "CANCEL"}
|
||||
}
|
||||
}
|
||||
},
|
||||
frequency: {lifetime: 1},
|
||||
targeting: `
|
||||
(${JSON.stringify(GOOGLE_TRANSLATE_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
|
||||
(${JSON.stringify(GOOGLE_TRANSLATE_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${GOOGLE_TRANSLATE_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
|
||||
trigger: {id: "openURL", params: GOOGLE_TRANSLATE_PARAMS.open_urls},
|
||||
trigger: {id: "openURL", params: GOOGLE_TRANSLATE_PARAMS.open_urls}
|
||||
},
|
||||
{
|
||||
id: "YOUTUBE_ENHANCE_1",
|
||||
@ -175,7 +175,7 @@ const CFR_MESSAGES = [
|
||||
heading_text: {string_id: "cfr-doorhanger-extension-heading"},
|
||||
info_icon: {
|
||||
label: {string_id: "cfr-doorhanger-extension-sumo-link"},
|
||||
sumo_path: YOUTUBE_ENHANCE_PARAMS.sumo_path,
|
||||
sumo_path: YOUTUBE_ENHANCE_PARAMS.sumo_path
|
||||
},
|
||||
addon: {
|
||||
title: "Enhancer for YouTube\u2122",
|
||||
@ -183,7 +183,7 @@ const CFR_MESSAGES = [
|
||||
rating: 4.8,
|
||||
users: 357328,
|
||||
author: "Maxime RF",
|
||||
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/enhancer-for-youtube/",
|
||||
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/enhancer-for-youtube/"
|
||||
},
|
||||
text: "Take control of your YouTube experience. Automatically block annoying ads, set playback speed and volume, remove annotations, and more.",
|
||||
buttons: {
|
||||
@ -191,20 +191,20 @@ const CFR_MESSAGES = [
|
||||
label: {string_id: "cfr-doorhanger-extension-ok-button"},
|
||||
action: {
|
||||
type: "INSTALL_ADDON_FROM_URL",
|
||||
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/1028400/enhancer_for_youtubetm-2.0.73-an+fx-linux.xpi`},
|
||||
},
|
||||
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/1028400/enhancer_for_youtubetm-2.0.73-an+fx-linux.xpi`}
|
||||
}
|
||||
},
|
||||
secondary: {
|
||||
label: {string_id: "cfr-doorhanger-extension-cancel-button"},
|
||||
action: {type: "CANCEL"},
|
||||
},
|
||||
},
|
||||
action: {type: "CANCEL"}
|
||||
}
|
||||
}
|
||||
},
|
||||
frequency: {lifetime: 1},
|
||||
targeting: `
|
||||
(${JSON.stringify(YOUTUBE_ENHANCE_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
|
||||
(${JSON.stringify(YOUTUBE_ENHANCE_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${YOUTUBE_ENHANCE_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
|
||||
trigger: {id: "openURL", params: YOUTUBE_ENHANCE_PARAMS.open_urls},
|
||||
trigger: {id: "openURL", params: YOUTUBE_ENHANCE_PARAMS.open_urls}
|
||||
},
|
||||
{
|
||||
id: "WIKIPEDIA_CONTEXT_MENU_SEARCH_1",
|
||||
@ -215,7 +215,7 @@ const CFR_MESSAGES = [
|
||||
heading_text: {string_id: "cfr-doorhanger-extension-heading"},
|
||||
info_icon: {
|
||||
label: {string_id: "cfr-doorhanger-extension-sumo-link"},
|
||||
sumo_path: WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.sumo_path,
|
||||
sumo_path: WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.sumo_path
|
||||
},
|
||||
addon: {
|
||||
title: "Wikipedia Context Menu Search",
|
||||
@ -223,7 +223,7 @@ const CFR_MESSAGES = [
|
||||
rating: 4.9,
|
||||
users: 3095,
|
||||
author: "Nick Diedrich",
|
||||
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/wikipedia-context-menu-search/",
|
||||
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/wikipedia-context-menu-search/"
|
||||
},
|
||||
text: "Get to a Wikipedia page fast, from anywhere on the web. Just highlight any webpage text and right-click to open the context menu to start a Wikipedia search.",
|
||||
buttons: {
|
||||
@ -231,20 +231,20 @@ const CFR_MESSAGES = [
|
||||
label: {string_id: "cfr-doorhanger-extension-ok-button"},
|
||||
action: {
|
||||
type: "INSTALL_ADDON_FROM_URL",
|
||||
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/890224/wikipedia_context_menu_search-1.8-an+fx.xpi`},
|
||||
},
|
||||
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/890224/wikipedia_context_menu_search-1.8-an+fx.xpi`}
|
||||
}
|
||||
},
|
||||
secondary: {
|
||||
label: {string_id: "cfr-doorhanger-extension-cancel-button"},
|
||||
action: {type: "CANCEL"},
|
||||
},
|
||||
},
|
||||
action: {type: "CANCEL"}
|
||||
}
|
||||
}
|
||||
},
|
||||
frequency: {lifetime: 1},
|
||||
targeting: `
|
||||
(${JSON.stringify(WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
|
||||
(${JSON.stringify(WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
|
||||
trigger: {id: "openURL", params: WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.open_urls},
|
||||
trigger: {id: "openURL", params: WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.open_urls}
|
||||
},
|
||||
{
|
||||
id: "REDDIT_ENHANCEMENT_1",
|
||||
@ -255,7 +255,7 @@ const CFR_MESSAGES = [
|
||||
heading_text: {string_id: "cfr-doorhanger-extension-heading"},
|
||||
info_icon: {
|
||||
label: {string_id: "cfr-doorhanger-extension-sumo-link"},
|
||||
sumo_path: REDDIT_ENHANCEMENT_PARAMS.sumo_path,
|
||||
sumo_path: REDDIT_ENHANCEMENT_PARAMS.sumo_path
|
||||
},
|
||||
addon: {
|
||||
title: "Reddit Enhancement Suite",
|
||||
@ -263,7 +263,7 @@ const CFR_MESSAGES = [
|
||||
rating: 4.6,
|
||||
users: 258129,
|
||||
author: "honestbleeps",
|
||||
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/reddit-enhancement-suite/",
|
||||
amo_url: "https://addons.mozilla.org/en-US/firefox/addon/reddit-enhancement-suite/"
|
||||
},
|
||||
text: "New features include Inline Image Viewer, Never Ending Reddit (never click 'next page' again), Keyboard Navigation, Account Switcher, and User Tagger.",
|
||||
buttons: {
|
||||
@ -271,27 +271,27 @@ const CFR_MESSAGES = [
|
||||
label: {string_id: "cfr-doorhanger-extension-ok-button"},
|
||||
action: {
|
||||
type: "INSTALL_ADDON_FROM_URL",
|
||||
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/991623/reddit_enhancement_suite-5.12.5-an+fx.xpi`},
|
||||
},
|
||||
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/991623/reddit_enhancement_suite-5.12.5-an+fx.xpi`}
|
||||
}
|
||||
},
|
||||
secondary: {
|
||||
label: {string_id: "cfr-doorhanger-extension-cancel-button"},
|
||||
action: {type: "CANCEL"},
|
||||
},
|
||||
},
|
||||
action: {type: "CANCEL"}
|
||||
}
|
||||
}
|
||||
},
|
||||
frequency: {lifetime: 1},
|
||||
targeting: `
|
||||
(${JSON.stringify(REDDIT_ENHANCEMENT_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
|
||||
(${JSON.stringify(REDDIT_ENHANCEMENT_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${REDDIT_ENHANCEMENT_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
|
||||
trigger: {id: "openURL", params: REDDIT_ENHANCEMENT_PARAMS.open_urls},
|
||||
},
|
||||
trigger: {id: "openURL", params: REDDIT_ENHANCEMENT_PARAMS.open_urls}
|
||||
}
|
||||
];
|
||||
|
||||
const CFRMessageProvider = {
|
||||
getMessages() {
|
||||
return CFR_MESSAGES;
|
||||
},
|
||||
}
|
||||
};
|
||||
this.CFRMessageProvider = CFRMessageProvider;
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/Localization.jsm");
|
||||
const {Localization} = ChromeUtils.import("resource://gre/modules/Localization.jsm", {});
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
|
||||
@ -50,11 +50,28 @@ class PageAction {
|
||||
this.dispatchUserAction = this.dispatchUserAction.bind(this);
|
||||
|
||||
this._l10n = new Localization([
|
||||
"browser/newtab/asrouter.ftl",
|
||||
"browser/newtab/asrouter.ftl"
|
||||
]);
|
||||
|
||||
// Saved timeout IDs for scheduled state changes, so they can be cancelled
|
||||
this.stateTransitionTimeoutIDs = [];
|
||||
|
||||
this.container.onclick = this._handleClick;
|
||||
}
|
||||
|
||||
_dispatchImpression(message) {
|
||||
this._dispatchToASRouter({type: "IMPRESSION", data: message});
|
||||
}
|
||||
|
||||
_sendTelemetry(ping) {
|
||||
// Note `useClientID` is set to true to tell TelemetryFeed to use client_id
|
||||
// instead of `impression_id`. TelemetryFeed is also responsible for deciding
|
||||
// whether to use `message_id` or `bucket_id` based on the release channel and
|
||||
// shield study setup.
|
||||
this._dispatchToASRouter({
|
||||
type: "DOORHANGER_TELEMETRY",
|
||||
data: {useClientID: true, action: "cfr_user_event", source: "CFR", ...ping}
|
||||
});
|
||||
}
|
||||
|
||||
async show(recommendation, shouldExpand = false) {
|
||||
@ -69,11 +86,6 @@ class PageAction {
|
||||
const [{width}] = this.label.getClientRects();
|
||||
this.urlbar.style.setProperty("--cfr-label-width", `${width}px`);
|
||||
|
||||
this.container.addEventListener("click", this._handleClick);
|
||||
// Collapse the recommendation on url bar focus in order to free up more
|
||||
// space to display and edit the url
|
||||
this.urlbar.addEventListener("focus", this._collapse);
|
||||
|
||||
if (shouldExpand) {
|
||||
this._clearScheduledStateChanges();
|
||||
|
||||
@ -95,39 +107,42 @@ class PageAction {
|
||||
this.container.hidden = true;
|
||||
this._clearScheduledStateChanges();
|
||||
this.urlbar.removeAttribute("cfr-recommendation-state");
|
||||
this.container.removeEventListener("click", this._handleClick);
|
||||
this.urlbar.removeEventListener("focus", this._collapse);
|
||||
if (this.currentNotification) {
|
||||
this.window.PopupNotifications.remove(this.currentNotification);
|
||||
this.currentNotification = null;
|
||||
}
|
||||
// This is safe even if this.currentNotification is invalid/undefined
|
||||
this.window.PopupNotifications.remove(this.currentNotification);
|
||||
}
|
||||
|
||||
_expand(delay) {
|
||||
if (delay > 0) {
|
||||
this.stateTransitionTimeoutIDs.push(this.window.setTimeout(() => {
|
||||
this.urlbar.setAttribute("cfr-recommendation-state", "expanded");
|
||||
}, delay));
|
||||
} else {
|
||||
dispatchUserAction(action) {
|
||||
this._dispatchToASRouter(
|
||||
{type: "USER_ACTION", data: action},
|
||||
{browser: this.window.gBrowser.selectedBrowser}
|
||||
);
|
||||
}
|
||||
|
||||
_expand(delay = 0) {
|
||||
if (!delay) {
|
||||
// Non-delayed state change overrides any scheduled state changes
|
||||
this._clearScheduledStateChanges();
|
||||
this.urlbar.setAttribute("cfr-recommendation-state", "expanded");
|
||||
} else {
|
||||
this.stateTransitionTimeoutIDs.push(this.window.setTimeout(() => {
|
||||
this.urlbar.setAttribute("cfr-recommendation-state", "expanded");
|
||||
}, delay));
|
||||
}
|
||||
}
|
||||
|
||||
_collapse(delay) {
|
||||
if (delay > 0) {
|
||||
this.stateTransitionTimeoutIDs.push(this.window.setTimeout(() => {
|
||||
if (this.urlbar.getAttribute("cfr-recommendation-state") === "expanded") {
|
||||
this.urlbar.setAttribute("cfr-recommendation-state", "collapsed");
|
||||
}
|
||||
}, delay));
|
||||
} else {
|
||||
_collapse(delay = 0) {
|
||||
if (!delay) {
|
||||
// Non-delayed state change overrides any scheduled state changes
|
||||
this._clearScheduledStateChanges();
|
||||
if (this.urlbar.getAttribute("cfr-recommendation-state") === "expanded") {
|
||||
this.urlbar.setAttribute("cfr-recommendation-state", "collapsed");
|
||||
}
|
||||
} else {
|
||||
this.stateTransitionTimeoutIDs.push(this.window.setTimeout(() => {
|
||||
if (this.urlbar.getAttribute("cfr-recommendation-state") === "expanded") {
|
||||
this.urlbar.setAttribute("cfr-recommendation-state", "collapsed");
|
||||
}
|
||||
}, delay));
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,31 +158,11 @@ class PageAction {
|
||||
_popupStateChange(state) {
|
||||
if (["dismissed", "removed"].includes(state)) {
|
||||
this._collapse();
|
||||
if (this.currentNotification) {
|
||||
this.window.PopupNotifications.remove(this.currentNotification);
|
||||
this.currentNotification = null;
|
||||
}
|
||||
// This is safe even if this.currentNotification is invalid/undefined
|
||||
this.window.PopupNotifications.remove(this.currentNotification);
|
||||
}
|
||||
}
|
||||
|
||||
dispatchUserAction(action) {
|
||||
this._dispatchToASRouter(
|
||||
{type: "USER_ACTION", data: action},
|
||||
{browser: this.window.gBrowser.selectedBrowser}
|
||||
);
|
||||
}
|
||||
|
||||
_dispatchImpression(message) {
|
||||
this._dispatchToASRouter({type: "IMPRESSION", data: message});
|
||||
}
|
||||
|
||||
_sendTelemetry(ping) {
|
||||
this._dispatchToASRouter({
|
||||
type: "DOORHANGER_TELEMETRY",
|
||||
data: {action: "cfr_user_event", source: "CFR", ...ping},
|
||||
});
|
||||
}
|
||||
|
||||
_blockMessage(messageID) {
|
||||
this._dispatchToASRouter(
|
||||
{type: "BLOCK_MESSAGE_BY_ID", data: {id: messageID}}
|
||||
@ -184,27 +179,12 @@ class PageAction {
|
||||
*/
|
||||
async getStrings(string, subAttribute = "") {
|
||||
if (!string.string_id) {
|
||||
if (subAttribute) {
|
||||
if (string.attributes) {
|
||||
return string.attributes[subAttribute];
|
||||
}
|
||||
|
||||
Cu.reportError(`String ${string.value} does not contain any attributes`);
|
||||
return subAttribute;
|
||||
}
|
||||
|
||||
if (typeof string.value === "string") {
|
||||
const stringWithAttributes = new String(string.value); // eslint-disable-line no-new-wrappers
|
||||
stringWithAttributes.attributes = string.attributes;
|
||||
return stringWithAttributes;
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
const [localeStrings] = await this._l10n.formatMessages([{
|
||||
id: string.string_id,
|
||||
args: string.args,
|
||||
args: string.args
|
||||
}]);
|
||||
|
||||
const mainString = new String(localeStrings.value); // eslint-disable-line no-new-wrappers
|
||||
@ -263,7 +243,7 @@ class PageAction {
|
||||
|
||||
author.textContent = await this.getStrings({
|
||||
string_id: "cfr-doorhanger-extension-author",
|
||||
args: {name: content.addon.author},
|
||||
args: {name: content.addon.author}
|
||||
});
|
||||
|
||||
footerText.textContent = await this.getStrings(content.text);
|
||||
@ -278,7 +258,7 @@ class PageAction {
|
||||
|
||||
const ratingString = await this.getStrings({
|
||||
string_id: "cfr-doorhanger-extension-rating",
|
||||
args: {total: rating},
|
||||
args: {total: rating}
|
||||
}, "tooltiptext");
|
||||
footerFilledStars.setAttribute("tooltiptext", ratingString);
|
||||
footerEmptyStars.setAttribute("tooltiptext", ratingString);
|
||||
@ -293,7 +273,7 @@ class PageAction {
|
||||
if (users) {
|
||||
footerUsers.setAttribute("value", await this.getStrings({
|
||||
string_id: "cfr-doorhanger-extension-total-users",
|
||||
args: {total: users},
|
||||
args: {total: users}
|
||||
}));
|
||||
footerUsers.removeAttribute("hidden");
|
||||
} else {
|
||||
@ -326,7 +306,7 @@ class PageAction {
|
||||
this.hide();
|
||||
this._sendTelemetry({message_id: id, bucket_id: content.bucket_id, event: "INSTALL"});
|
||||
RecommendationMap.delete(browser);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const secondaryActions = [{
|
||||
@ -336,13 +316,13 @@ class PageAction {
|
||||
this.hide();
|
||||
this._sendTelemetry({message_id: id, bucket_id: content.bucket_id, event: "DISMISS"});
|
||||
RecommendationMap.delete(browser);
|
||||
},
|
||||
}
|
||||
}];
|
||||
|
||||
const options = {
|
||||
popupIconURL: content.addon.icon,
|
||||
hideClose: true,
|
||||
eventCallback: this._popupStateChange,
|
||||
eventCallback: this._popupStateChange
|
||||
};
|
||||
|
||||
this._sendTelemetry({message_id: id, bucket_id: content.bucket_id, event: "CLICK_DOORHANGER"});
|
||||
@ -454,12 +434,8 @@ const CFRPageActions = {
|
||||
// WeakMaps don't have a `clear` method
|
||||
PageActionMap = new WeakMap();
|
||||
RecommendationMap = new WeakMap();
|
||||
this.PageActionMap = PageActionMap;
|
||||
this.RecommendationMap = RecommendationMap;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
this.PageAction = PageAction;
|
||||
this.CFRPageActions = CFRPageActions;
|
||||
|
||||
const EXPORTED_SYMBOLS = ["CFRPageActions", "PageAction"];
|
||||
const EXPORTED_SYMBOLS = ["CFRPageActions"];
|
||||
|
@ -53,7 +53,7 @@ this.DownloadsManager = class DownloadsManager {
|
||||
title: element.displayName,
|
||||
description,
|
||||
referrer: downloadedItem.source.referrer,
|
||||
date_added: downloadedItem.endTime,
|
||||
date_added: downloadedItem.endTime
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
const {actionTypes: at} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
|
||||
const {getDomain} = ChromeUtils.import("resource://activity-stream/lib/TippyTopProvider.jsm", {});
|
||||
const {RemoteSettings} = ChromeUtils.import("resource://services-settings/remote-settings.js", {});
|
||||
@ -78,7 +80,7 @@ async function fetchVisitPaths(url) {
|
||||
|
||||
const visits = await NewTabUtils.activityStreamProvider.executePlacesQuery(query, {
|
||||
columns: ["visit_id", "url"],
|
||||
params: {url},
|
||||
params: {url}
|
||||
});
|
||||
return visits;
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
@ -2967,5 +2969,5 @@ gAdultSet = new Set([
|
||||
"zwQ/3MzTJ9rfBmrANIh14w==",
|
||||
"zwY6tCjjya/bgrYaCncaag==",
|
||||
"zxsSqovedB3HT99jVblCnQ==",
|
||||
"zyA9f5J7mw5InjhcfeumAQ==",
|
||||
"zyA9f5J7mw5InjhcfeumAQ=="
|
||||
]);
|
||||
|
@ -141,7 +141,7 @@ this.HighlightsFeed = class HighlightsFeed {
|
||||
numItems: MANY_EXTRA_LENGTH,
|
||||
excludeBookmarks: !this.store.getState().Prefs.values["section.highlights.includeBookmarks"],
|
||||
excludeHistory: !this.store.getState().Prefs.values["section.highlights.includeVisited"],
|
||||
excludePocket: !this.store.getState().Prefs.values["section.highlights.includePocket"],
|
||||
excludePocket: !this.store.getState().Prefs.values["section.highlights.includePocket"]
|
||||
});
|
||||
|
||||
if (this.store.getState().Prefs.values["section.highlights.includeDownloads"]) {
|
||||
@ -191,7 +191,7 @@ this.HighlightsFeed = class HighlightsFeed {
|
||||
hasImage: page.type !== "download", // Downloads do not have an image - all else types fall back to a screenshot
|
||||
hostname,
|
||||
type: page.type,
|
||||
pocket_id: page.pocket_id,
|
||||
pocket_id: page.pocket_id
|
||||
});
|
||||
|
||||
// Add the "bookmark", "pocket", or not-skipped "history"
|
||||
|
@ -8,6 +8,7 @@ const {actionCreators: ac, actionTypes: at} = ChromeUtils.import("resource://act
|
||||
const MIGRATION_ENDED_EVENT = "Migration:Ended";
|
||||
const MS_PER_DAY = 86400000;
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "MigrationUtils", "resource:///modules/MigrationUtils.jsm");
|
||||
|
@ -1,63 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const {toksToTfIdfVector} = ChromeUtils.import("resource://activity-stream/lib/Tokenize.jsm", {});
|
||||
|
||||
this.NaiveBayesTextTagger = class NaiveBayesTextTagger {
|
||||
constructor(model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the tokenized text belongs to class according to binary naive Bayes
|
||||
* classifier. Returns an object containing the class label ("label"), and
|
||||
* the log probability ("logProb") that the text belongs to that class. If
|
||||
* the positive class is more likely, then "label" is the positive class
|
||||
* label. If the negative class is matched, then "label" is set to null.
|
||||
*/
|
||||
tagTokens(tokens) {
|
||||
let fv = toksToTfIdfVector(tokens, this.model.vocab_idfs);
|
||||
|
||||
let bestLogProb = null;
|
||||
let bestClassId = -1;
|
||||
let bestClassLabel = null;
|
||||
let logSumExp = 0.0; // will be P(x). Used to create a proper probability
|
||||
for (let classId = 0; classId < this.model.classes.length; classId++) {
|
||||
let classModel = this.model.classes[classId];
|
||||
let classLogProb = classModel.log_prior;
|
||||
|
||||
// dot fv with the class model
|
||||
for (let pair of Object.values(fv)) {
|
||||
let [termId, tfidf] = pair;
|
||||
classLogProb += tfidf * classModel.feature_log_probs[termId];
|
||||
}
|
||||
|
||||
if ((bestLogProb === null) || (classLogProb > bestLogProb)) {
|
||||
bestLogProb = classLogProb;
|
||||
bestClassId = classId;
|
||||
}
|
||||
logSumExp += Math.exp(classLogProb);
|
||||
}
|
||||
|
||||
// now normalize the probability by dividing by P(x)
|
||||
logSumExp = Math.log(logSumExp);
|
||||
bestLogProb -= logSumExp;
|
||||
if (bestClassId === this.model.positive_class_id) {
|
||||
bestClassLabel = this.model.positive_class_label;
|
||||
} else {
|
||||
bestClassLabel = null;
|
||||
}
|
||||
|
||||
let confident = ((bestClassId === this.model.positive_class_id) &&
|
||||
(bestLogProb > this.model.positive_class_threshold_log_prob));
|
||||
return {
|
||||
"label": bestClassLabel,
|
||||
"logProb": bestLogProb,
|
||||
"confident": confident,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const EXPORTED_SYMBOLS = ["NaiveBayesTextTagger"];
|
@ -1,62 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const {toksToTfIdfVector} = ChromeUtils.import("resource://activity-stream/lib/Tokenize.jsm", {});
|
||||
|
||||
this.NmfTextTagger = class NmfTextTagger {
|
||||
constructor(model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
/**
|
||||
* A multiclass classifier that scores tokenized text against several classes through
|
||||
* inference of a nonnegative matrix factorization of TF-IDF vectors and
|
||||
* class labels. Returns a map of class labels as string keys to scores.
|
||||
* (Higher is more confident.) All classes get scored, so it is up to
|
||||
* consumer of this data determine what classes are most valuable.
|
||||
*/
|
||||
tagTokens(tokens) {
|
||||
let fv = toksToTfIdfVector(tokens, this.model.vocab_idfs);
|
||||
let fve = Object.values(fv);
|
||||
|
||||
// normalize by the sum of the vector
|
||||
let sum = 0.0;
|
||||
for (let pair of fve) {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
sum += pair[1];
|
||||
}
|
||||
for (let i = 0; i < fve.length; i++) {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
fve[i][1] /= sum;
|
||||
}
|
||||
|
||||
// dot the document with each topic vector so that we can transform it into
|
||||
// the latent space
|
||||
let toksInLatentSpace = [];
|
||||
for (let topicVect of this.model.topic_word) {
|
||||
let fvDotTwv = 0;
|
||||
// dot fv with each topic word vector
|
||||
for (let pair of fve) {
|
||||
let [termId, tfidf] = pair;
|
||||
fvDotTwv += tfidf * topicVect[termId];
|
||||
}
|
||||
toksInLatentSpace.push(fvDotTwv);
|
||||
}
|
||||
|
||||
// now project toksInLatentSpace back into class space
|
||||
let predictions = {};
|
||||
Object.keys(this.model.document_topic).forEach(topic => {
|
||||
let score = 0;
|
||||
for (let i = 0; i < toksInLatentSpace.length; i++) {
|
||||
score += toksInLatentSpace[i] * this.model.document_topic[topic][i];
|
||||
}
|
||||
predictions[topic] = score;
|
||||
});
|
||||
|
||||
return predictions;
|
||||
}
|
||||
};
|
||||
|
||||
const EXPORTED_SYMBOLS = ["NmfTextTagger"];
|
@ -14,9 +14,9 @@ const ONBOARDING_MESSAGES = [
|
||||
text: "Browse by yourself. Private Browsing with Tracking Protection blocks online trackers that follow you around the web.",
|
||||
icon: "privatebrowsing",
|
||||
button_label: "Try It Now",
|
||||
button_action: {type: "OPEN_PRIVATE_BROWSER_WINDOW"},
|
||||
button_action: {type: "OPEN_PRIVATE_BROWSER_WINDOW"}
|
||||
},
|
||||
trigger: {id: "firstRun"},
|
||||
trigger: {id: "firstRun"}
|
||||
},
|
||||
{
|
||||
id: "ONBOARDING_2",
|
||||
@ -30,10 +30,10 @@ const ONBOARDING_MESSAGES = [
|
||||
button_label: "Try It Now",
|
||||
button_action: {
|
||||
type: "OPEN_URL",
|
||||
data: {url: "https://screenshots.firefox.com/#tour"},
|
||||
},
|
||||
data: {url: "https://screenshots.firefox.com/#tour"}
|
||||
}
|
||||
},
|
||||
trigger: {id: "firstRun"},
|
||||
trigger: {id: "firstRun"}
|
||||
},
|
||||
{
|
||||
id: "ONBOARDING_3",
|
||||
@ -45,10 +45,10 @@ const ONBOARDING_MESSAGES = [
|
||||
text: "Add even more features that make Firefox work harder for you. Compare prices, check the weather or express your personality with a custom theme.",
|
||||
icon: "addons",
|
||||
button_label: "Try It Now",
|
||||
button_action: {type: "OPEN_ABOUT_PAGE", data: {page: "addons"}},
|
||||
button_action: {type: "OPEN_ABOUT_PAGE", data: {page: "addons"}}
|
||||
},
|
||||
targeting: "isInExperimentCohort == 1",
|
||||
trigger: {id: "firstRun"},
|
||||
trigger: {id: "firstRun"}
|
||||
},
|
||||
{
|
||||
id: "ONBOARDING_4",
|
||||
@ -60,17 +60,17 @@ const ONBOARDING_MESSAGES = [
|
||||
text: "Browse faster, smarter, or safer with extensions like Ghostery, which lets you block annoying ads.",
|
||||
icon: "gift",
|
||||
button_label: "Try It Now",
|
||||
button_action: {type: "OPEN_URL", data: {url: "https://addons.mozilla.org/en-US/firefox/addon/ghostery/"}},
|
||||
button_action: {type: "OPEN_URL", data: {url: "https://addons.mozilla.org/en-US/firefox/addon/ghostery/"}}
|
||||
},
|
||||
targeting: "isInExperimentCohort == 2",
|
||||
trigger: {id: "firstRun"},
|
||||
},
|
||||
trigger: {id: "firstRun"}
|
||||
}
|
||||
];
|
||||
|
||||
const OnboardingMessageProvider = {
|
||||
getMessages() {
|
||||
return ONBOARDING_MESSAGES;
|
||||
},
|
||||
}
|
||||
};
|
||||
this.OnboardingMessageProvider = OnboardingMessageProvider;
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
const {actionCreators: ac, actionTypes: at} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
|
||||
|
||||
@ -43,7 +44,7 @@ class HistoryObserver extends Observer {
|
||||
this.dispatch({type: at.PLACES_LINKS_CHANGED});
|
||||
this.dispatch({
|
||||
type: at.PLACES_LINK_DELETED,
|
||||
data: {url: uri.spec},
|
||||
data: {url: uri.spec}
|
||||
});
|
||||
}
|
||||
|
||||
@ -114,8 +115,8 @@ class BookmarksObserver extends Observer {
|
||||
bookmarkGuid,
|
||||
bookmarkTitle,
|
||||
dateAdded,
|
||||
url: uri.spec,
|
||||
},
|
||||
url: uri.spec
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -139,7 +140,7 @@ class BookmarksObserver extends Observer {
|
||||
this.dispatch({type: at.PLACES_LINKS_CHANGED});
|
||||
this.dispatch({
|
||||
type: at.PLACES_BOOKMARK_REMOVED,
|
||||
data: {url: uri.spec, bookmarkGuid: guid},
|
||||
data: {url: uri.spec, bookmarkGuid: guid}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -229,7 +230,7 @@ class PlacesFeed {
|
||||
if (topic === LINK_BLOCKED_EVENT) {
|
||||
this.store.dispatch(ac.BroadcastToContent({
|
||||
type: at.PLACES_LINK_BLOCKED,
|
||||
data: {url: value},
|
||||
data: {url: value}
|
||||
}));
|
||||
}
|
||||
}
|
||||
@ -240,7 +241,7 @@ class PlacesFeed {
|
||||
openLink(action, where = "", isPrivate = false) {
|
||||
const params = {
|
||||
private: isPrivate,
|
||||
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
|
||||
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({})
|
||||
};
|
||||
|
||||
// Always include the referrer (even for http links) if we have one
|
||||
@ -269,7 +270,7 @@ class PlacesFeed {
|
||||
if (data) {
|
||||
this.store.dispatch(ac.BroadcastToContent({
|
||||
type: at.PLACES_SAVED_TO_POCKET,
|
||||
data: {url, open_url: data.item.open_url, title, pocket_id: data.item.item_id},
|
||||
data: {url, open_url: data.item.open_url, title, pocket_id: data.item.item_id}
|
||||
}));
|
||||
}
|
||||
} catch (err) {
|
||||
|
@ -22,8 +22,8 @@ const PREF_MIGRATION = {
|
||||
collapsed: new Map([
|
||||
["collapseTopSites", "topsites"],
|
||||
["section.highlights.collapsed", "highlights"],
|
||||
["section.topstories.collapsed", "topstories"],
|
||||
]),
|
||||
["section.topstories.collapsed", "topstories"]
|
||||
])
|
||||
};
|
||||
|
||||
this.PrefsFeed = class PrefsFeed {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,8 @@ const EXPORTED_SYMBOLS = ["Screenshots"];
|
||||
|
||||
Cu.importGlobalProperties(["fetch"]);
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "BackgroundPageThumbs",
|
||||
"resource://gre/modules/BackgroundPageThumbs.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "PageThumbs",
|
||||
@ -104,5 +106,5 @@ this.Screenshots = {
|
||||
// Update the cache for future links and call back for existing content
|
||||
cache.updateLink(property, screenshot);
|
||||
onScreenshot(screenshot);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -11,7 +11,7 @@ const SEARCH_SHORTCUTS = [
|
||||
{keyword: "@amazon", shortURL: "amazon", url: "https://amazon.com"},
|
||||
{keyword: "@\u767E\u5EA6", shortURL: "baidu", url: "https://baidu.com"},
|
||||
{keyword: "@google", shortURL: "google", url: "https://google.com"},
|
||||
{keyword: "@\u044F\u043D\u0434\u0435\u043A\u0441", shortURL: "yandex", url: "https://yandex.com"},
|
||||
{keyword: "@\u044F\u043D\u0434\u0435\u043A\u0441", shortURL: "yandex", url: "https://yandex.com"}
|
||||
];
|
||||
this.SEARCH_SHORTCUTS = SEARCH_SHORTCUTS;
|
||||
|
||||
@ -22,7 +22,7 @@ this.CUSTOM_SEARCH_SHORTCUTS = [
|
||||
{keyword: "@duckduckgo", shortURL: "duckduckgo", url: "https://duckduckgo.com"},
|
||||
{keyword: "@ebay", shortURL: "ebay", url: "https://ebay.com"},
|
||||
{keyword: "@twitter", shortURL: "twitter", url: "https://twitter.com"},
|
||||
{keyword: "@wikipedia", shortURL: "wikipedia", url: "https://wikipedia.org"},
|
||||
{keyword: "@wikipedia", shortURL: "wikipedia", url: "https://wikipedia.org"}
|
||||
];
|
||||
|
||||
// Note: you must add the activity stream branch to the beginning of this if using outside activity stream
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/EventEmitter.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
const {actionCreators: ac, actionTypes: at} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
|
||||
const {getDefaultOptions} = ChromeUtils.import("resource://activity-stream/lib/ActivityStreamStorage.jsm", {});
|
||||
|
||||
@ -24,8 +25,8 @@ const BUILT_IN_SECTIONS = {
|
||||
nestedPrefs: options.show_spocs ? [{
|
||||
name: "showSponsored",
|
||||
titleString: "prefs_topstories_options_sponsored_label",
|
||||
icon: "icon-info",
|
||||
}] : [],
|
||||
icon: "icon-info"
|
||||
}] : []
|
||||
},
|
||||
shouldHidePref: options.hidden,
|
||||
eventSource: "TOP_STORIES",
|
||||
@ -34,8 +35,8 @@ const BUILT_IN_SECTIONS = {
|
||||
learnMore: {
|
||||
link: {
|
||||
href: "https://getpocket.com/firefox/new_tab_learn_more",
|
||||
id: "pocket_learn_more",
|
||||
},
|
||||
id: "pocket_learn_more"
|
||||
}
|
||||
},
|
||||
privacyNoticeURL: "https://www.mozilla.org/privacy/firefox/#suggest-relevant-content",
|
||||
compactCards: false,
|
||||
@ -44,10 +45,10 @@ const BUILT_IN_SECTIONS = {
|
||||
availableLinkMenuOptions: ["CheckBookmarkOrArchive", "CheckSavedToPocket", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"],
|
||||
emptyState: {
|
||||
message: {id: "topstories_empty_state", values: {provider: options.provider_name}},
|
||||
icon: "check",
|
||||
icon: "check"
|
||||
},
|
||||
shouldSendImpressionStats: true,
|
||||
dedupeFrom: ["highlights"],
|
||||
dedupeFrom: ["highlights"]
|
||||
}),
|
||||
"feeds.section.highlights": options => ({
|
||||
id: "highlights",
|
||||
@ -56,17 +57,17 @@ const BUILT_IN_SECTIONS = {
|
||||
descString: {id: "prefs_highlights_description"},
|
||||
nestedPrefs: [{
|
||||
name: "section.highlights.includeVisited",
|
||||
titleString: "prefs_highlights_options_visited_label",
|
||||
titleString: "prefs_highlights_options_visited_label"
|
||||
}, {
|
||||
name: "section.highlights.includeBookmarks",
|
||||
titleString: "settings_pane_highlights_options_bookmarks",
|
||||
titleString: "settings_pane_highlights_options_bookmarks"
|
||||
}, {
|
||||
name: "section.highlights.includeDownloads",
|
||||
titleString: "prefs_highlights_options_download_label",
|
||||
titleString: "prefs_highlights_options_download_label"
|
||||
}, {
|
||||
name: "section.highlights.includePocket",
|
||||
titleString: "prefs_highlights_options_pocket_label",
|
||||
}],
|
||||
titleString: "prefs_highlights_options_pocket_label"
|
||||
}]
|
||||
},
|
||||
shouldHidePref: false,
|
||||
eventSource: "HIGHLIGHTS",
|
||||
@ -77,10 +78,10 @@ const BUILT_IN_SECTIONS = {
|
||||
maxRows: 4,
|
||||
emptyState: {
|
||||
message: {id: "highlights_empty_state"},
|
||||
icon: "highlights",
|
||||
icon: "highlights"
|
||||
},
|
||||
shouldSendImpressionStats: false,
|
||||
}),
|
||||
shouldSendImpressionStats: false
|
||||
})
|
||||
};
|
||||
|
||||
const SectionsManager = {
|
||||
@ -90,7 +91,7 @@ const SectionsManager = {
|
||||
history: ["CheckBookmark", "CheckSavedToPocket", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl", "DeleteUrl"],
|
||||
bookmark: ["CheckBookmark", "CheckSavedToPocket", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl", "DeleteUrl"],
|
||||
pocket: ["ArchiveFromPocket", "CheckSavedToPocket", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"],
|
||||
download: ["OpenFile", "ShowFile", "Separator", "GoToDownloadPage", "CopyDownloadLink", "Separator", "RemoveDownload", "BlockUrl"],
|
||||
download: ["OpenFile", "ShowFile", "Separator", "GoToDownloadPage", "CopyDownloadLink", "Separator", "RemoveDownload", "BlockUrl"]
|
||||
},
|
||||
initialized: false,
|
||||
sections: new Map(),
|
||||
@ -106,7 +107,7 @@ const SectionsManager = {
|
||||
if (section.dedupeFrom) {
|
||||
this._dedupeConfiguration.push({
|
||||
id: section.id,
|
||||
dedupeFrom: section.dedupeFrom,
|
||||
dedupeFrom: section.dedupeFrom
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -203,13 +204,13 @@ const SectionsManager = {
|
||||
url: card.url,
|
||||
title: card.title,
|
||||
description: card.description,
|
||||
previewImageURL: card.image,
|
||||
previewImageURL: card.image
|
||||
});
|
||||
// Highlights query skips bookmarks with no visits.
|
||||
PlacesUtils.history.insert({
|
||||
url,
|
||||
title: card.title,
|
||||
visits: [{}],
|
||||
visits: [{}]
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -298,7 +299,7 @@ const SectionsManager = {
|
||||
Object.keys(this.CONTEXT_MENU_PREFS).forEach(k =>
|
||||
Services.prefs.removeObserver(this.CONTEXT_MENU_PREFS[k], this));
|
||||
SectionsManager.initialized = false;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
for (const action of [
|
||||
@ -310,7 +311,7 @@ for (const action of [
|
||||
"UPDATE_SECTION",
|
||||
"UPDATE_SECTION_CARD",
|
||||
"INIT",
|
||||
"UNINIT",
|
||||
"UNINIT"
|
||||
]) {
|
||||
SectionsManager[action] = action;
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ this.SnippetsFeed = class SnippetsFeed {
|
||||
const resetDate = await profileAge.reset;
|
||||
return {
|
||||
createdWeeksAgo: Math.floor((Date.now() - createdDate) / ONE_WEEK),
|
||||
resetWeeksAgo: resetDate ? Math.floor((Date.now() - resetDate) / ONE_WEEK) : null,
|
||||
resetWeeksAgo: resetDate ? Math.floor((Date.now() - resetDate) / ONE_WEEK) : null
|
||||
};
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ this.SnippetsFeed = class SnippetsFeed {
|
||||
searchEngineIdentifier: Services.search.defaultEngine.identifier,
|
||||
engines: engines
|
||||
.filter(engine => engine.identifier)
|
||||
.map(engine => `${TARGET_SEARCHENGINE_PREFIX}${engine.identifier}`),
|
||||
.map(engine => `${TARGET_SEARCHENGINE_PREFIX}${engine.identifier}`)
|
||||
});
|
||||
} else {
|
||||
resolve({engines: [], searchEngineIdentifier: ""});
|
||||
@ -97,13 +97,13 @@ this.SnippetsFeed = class SnippetsFeed {
|
||||
version: addon.version,
|
||||
type: addon.type,
|
||||
isSystem: addon.isSystem,
|
||||
isWebExtension: addon.isWebExtension,
|
||||
isWebExtension: addon.isWebExtension
|
||||
};
|
||||
if (fullData) {
|
||||
Object.assign(info[addon.id], {
|
||||
name: addon.name,
|
||||
userDisabled: addon.userDisabled,
|
||||
installDate: addon.installDate,
|
||||
installDate: addon.installDate
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -154,7 +154,7 @@ this.SnippetsFeed = class SnippetsFeed {
|
||||
defaultBrowser: this.isDefaultBrowser(),
|
||||
isDevtoolsUser: this.isDevtoolsUser(),
|
||||
blockList: await this._getBlockList() || [],
|
||||
previousSessionEnd: this._previousSessionEnd,
|
||||
previousSessionEnd: this._previousSessionEnd
|
||||
};
|
||||
this._dispatchChanges(data);
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ this.Store = class Store {
|
||||
async _initIndexedDB(telemetryKey) {
|
||||
this.dbStorage = new ActivityStreamStorage({
|
||||
storeNames: ["sectionPrefs", "snippets"],
|
||||
telemetry: this.feeds.get(telemetryKey),
|
||||
telemetry: this.feeds.get(telemetryKey)
|
||||
});
|
||||
// Accessing the db causes the object stores to be created / migrated.
|
||||
// This needs to happen before other instances try to access the db, which
|
||||
|
@ -3,6 +3,8 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
const {actionTypes: at} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "setInterval", "resource://gre/modules/Timer.jsm");
|
||||
|
@ -35,7 +35,7 @@ const USER_PREFS_ENCODING = {
|
||||
"feeds.section.topstories": 1 << 2,
|
||||
"feeds.section.highlights": 1 << 3,
|
||||
"feeds.snippets": 1 << 4,
|
||||
"showSponsored": 1 << 5,
|
||||
"showSponsored": 1 << 5
|
||||
};
|
||||
|
||||
const PREF_IMPRESSION_ID = "impressionId";
|
||||
@ -102,7 +102,7 @@ this.TelemetryFeed = class TelemetryFeed {
|
||||
try {
|
||||
data_to_save = {
|
||||
load_trigger_ts: perfService.getMostRecentAbsMarkStartByName("browser-open-newtab-start"),
|
||||
load_trigger_type: "menu_plus_or_keyboard",
|
||||
load_trigger_type: "menu_plus_or_keyboard"
|
||||
};
|
||||
} catch (e) {
|
||||
// if no mark was returned, we have nothing to save
|
||||
@ -149,8 +149,8 @@ this.TelemetryFeed = class TelemetryFeed {
|
||||
{
|
||||
value: new PingCentre({
|
||||
topic: ACTIVITY_STREAM_ID,
|
||||
overrideEndpointPref: ACTIVITY_STREAM_ENDPOINT_PREF,
|
||||
}),
|
||||
overrideEndpointPref: ACTIVITY_STREAM_ENDPOINT_PREF
|
||||
})
|
||||
});
|
||||
return this.pingCentre;
|
||||
}
|
||||
@ -257,8 +257,8 @@ this.TelemetryFeed = class TelemetryFeed {
|
||||
perf: {
|
||||
load_trigger_type,
|
||||
is_preloaded: false,
|
||||
is_prerendered: false,
|
||||
},
|
||||
is_prerendered: false
|
||||
}
|
||||
};
|
||||
|
||||
if (load_trigger_ts) {
|
||||
@ -329,7 +329,7 @@ this.TelemetryFeed = class TelemetryFeed {
|
||||
const ping = {
|
||||
addon_version: Services.appinfo.appBuildID,
|
||||
locale: Services.locale.getAppLocaleAsLangTag(),
|
||||
user_prefs: this.userPreferences,
|
||||
user_prefs: this.userPreferences
|
||||
};
|
||||
|
||||
// If the ping is part of a user session, add session-related info
|
||||
@ -359,7 +359,7 @@ this.TelemetryFeed = class TelemetryFeed {
|
||||
action: "activity_stream_impression_stats",
|
||||
impression_id: this._impressionId,
|
||||
client_id: "n/a",
|
||||
session_id: "n/a",
|
||||
session_id: "n/a"
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -397,7 +397,7 @@ this.TelemetryFeed = class TelemetryFeed {
|
||||
page: session.page,
|
||||
session_duration: session.session_duration,
|
||||
action: "activity_stream_session",
|
||||
perf: session.perf,
|
||||
perf: session.perf
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -413,7 +413,7 @@ this.TelemetryFeed = class TelemetryFeed {
|
||||
client_id: "n/a",
|
||||
addon_version: Services.appinfo.appBuildID,
|
||||
locale: Services.locale.getAppLocaleAsLangTag(),
|
||||
impression_id: this._impressionId,
|
||||
impression_id: this._impressionId
|
||||
};
|
||||
if (action.data.includeClientID) {
|
||||
// Ping-centre client will fill in the client_id if it's not provided in the ping
|
||||
@ -599,5 +599,5 @@ const EXPORTED_SYMBOLS = [
|
||||
"PREF_IMPRESSION_ID",
|
||||
"TELEMETRY_PREF",
|
||||
"EVENTS_TELEMETRY_PREF",
|
||||
"ROUTER_MESSAGE_PROVIDER_PREF",
|
||||
"ROUTER_MESSAGE_PROVIDER_PREF"
|
||||
];
|
||||
|
@ -1,71 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
// Unicode specifies certain mnemonics for code pages and character classes.
|
||||
// They call them "character properties" https://en.wikipedia.org/wiki/Unicode_character_property .
|
||||
// These mnemonics are have been adopted by many regular expression libraries,
|
||||
// however the standard Javascript regexp system doesn't support unicode
|
||||
// character properties, so we have to define these ourself.
|
||||
//
|
||||
// Each of these sections contains the characters values / ranges for specific
|
||||
// character property: Whitespace, Symbol (S), Punctuation (P), Number (N),
|
||||
// Mark (M), and Letter (L).
|
||||
const UNICODE_SPACE = "\x20\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000";
|
||||
const UNICODE_SYMBOL = "\\x24\\x2B\x3C-\x3E\\x5E\x60\\x7C\x7E\xA2-\xA6\xA8\xA9\xAC\xAE-\xB1\xB4\xB8\xD7\xF7\u02C2-\u02C5\u02D2-\u02DF\u02E5-\u02EB\u02ED\u02EF-\u02FF\u0375\u0384\u0385\u03F6\u0482\u058D-\u058F\u0606-\u0608\u060B\u060E\u060F\u06DE\u06E9\u06FD\u06FE\u07F6\u09F2\u09F3\u09FA\u09FB\u0AF1\u0B70\u0BF3-\u0BFA\u0C7F\u0D4F\u0D79\u0E3F\u0F01-\u0F03\u0F13\u0F15-\u0F17\u0F1A-\u0F1F\u0F34\u0F36\u0F38\u0FBE-\u0FC5\u0FC7-\u0FCC\u0FCE\u0FCF\u0FD5-\u0FD8\u109E\u109F\u1390-\u1399\u17DB\u1940\u19DE-\u19FF\u1B61-\u1B6A\u1B74-\u1B7C\u1FBD\u1FBF-\u1FC1\u1FCD-\u1FCF\u1FDD-\u1FDF\u1FED-\u1FEF\u1FFD\u1FFE\u2044\u2052\u207A-\u207C\u208A-\u208C\u20A0-\u20BE\u2100\u2101\u2103-\u2106\u2108\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A\u213B\u2140-\u2144\u214A-\u214D\u214F\u218A\u218B\u2190-\u2307\u230C-\u2328\u232B-\u23FE\u2400-\u2426\u2440-\u244A\u249C-\u24E9\u2500-\u2767\u2794-\u27C4\u27C7-\u27E5\u27F0-\u2982\u2999-\u29D7\u29DC-\u29FB\u29FE-\u2B73\u2B76-\u2B95\u2B98-\u2BB9\u2BBD-\u2BC8\u2BCA-\u2BD1\u2BEC-\u2BEF\u2CE5-\u2CEA\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3004\u3012\u3013\u3020\u3036\u3037\u303E\u303F\u309B\u309C\u3190\u3191\u3196-\u319F\u31C0-\u31E3\u3200-\u321E\u322A-\u3247\u3250\u3260-\u327F\u328A-\u32B0\u32C0-\u32FE\u3300-\u33FF\u4DC0-\u4DFF\uA490-\uA4C6\uA700-\uA716\uA720\uA721\uA789\uA78A\uA828-\uA82B\uA836-\uA839\uAA77-\uAA79\uAB5B\uFB29\uFBB2-\uFBC1\uFDFC\uFDFD\uFE62\uFE64-\uFE66\uFE69\uFF04\uFF0B\uFF1C-\uFF1E\uFF3E\uFF40\uFF5C\uFF5E\uFFE0-\uFFE6\uFFE8-\uFFEE\uFFFC\uFFFD";
|
||||
const UNICODE_PUNCT = "\x21-\x23\x25-\\x2A\x2C-\x2F\x3A\x3B\\x3F\x40\\x5B-\\x5D\x5F\\x7B\x7D\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E44\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65";
|
||||
|
||||
const UNICODE_NUMBER = "0-9\xB2\xB3\xB9\xBC-\xBE\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u09F4-\u09F9\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0B72-\u0B77\u0BE6-\u0BF2\u0C66-\u0C6F\u0C78-\u0C7E\u0CE6-\u0CEF\u0D58-\u0D5E\u0D66-\u0D78\u0DE6-\u0DEF\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F33\u1040-\u1049\u1090-\u1099\u1369-\u137C\u16EE-\u16F0\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1946-\u194F\u19D0-\u19DA\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\u2070\u2074-\u2079\u2080-\u2089\u2150-\u2182\u2185-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2CFD\u3007\u3021-\u3029\u3038-\u303A\u3192-\u3195\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\uA620-\uA629\uA6E6-\uA6EF\uA830-\uA835\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uA9F0-\uA9F9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19";
|
||||
const UNICODE_MARK = "\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F";
|
||||
const UNICODE_LETTER = "A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC";
|
||||
|
||||
const REGEXP_SPLITS = new RegExp(`[${UNICODE_SPACE}${UNICODE_SYMBOL}${UNICODE_PUNCT}]+`);
|
||||
const REGEXP_ALPHANUMS = new RegExp(`^[${UNICODE_NUMBER}${UNICODE_MARK}${UNICODE_LETTER}]+$`);
|
||||
|
||||
/**
|
||||
* Downcases the text, and splits it into consecutive alphanumeric characters.
|
||||
* This is locale aware, and so will not strip accents. This uses "word
|
||||
* breaks", and os is not appropriate for languages without them
|
||||
* (e.g. Chinese).
|
||||
*/
|
||||
function tokenize(text) {
|
||||
return text.toLocaleLowerCase().split(REGEXP_SPLITS).filter(tok => tok.match(REGEXP_ALPHANUMS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a sequence of tokens into an L2 normed TF-IDF. Any terms that are
|
||||
* not preindexed (i.e. does have a computed inverse document frequency) will
|
||||
* be dropped.
|
||||
*/
|
||||
function toksToTfIdfVector(tokens, vocab_idfs) {
|
||||
let tfidfs = {};
|
||||
|
||||
// calcualte the term frequencies
|
||||
for (let tok of tokens) {
|
||||
if (!(tok in vocab_idfs)) {
|
||||
continue;
|
||||
}
|
||||
if (!(tok in tfidfs)) {
|
||||
tfidfs[tok] = [vocab_idfs[tok][0], 1];
|
||||
} else {
|
||||
tfidfs[tok][1]++;
|
||||
}
|
||||
}
|
||||
|
||||
// now multiply by the log inverse document frequencies, then take
|
||||
// the L2 norm of this.
|
||||
let l2Norm = 0.0;
|
||||
Object.keys(tfidfs).forEach(tok => {
|
||||
tfidfs[tok][1] *= vocab_idfs[tok][1];
|
||||
l2Norm += tfidfs[tok][1] * tfidfs[tok][1];
|
||||
});
|
||||
l2Norm = Math.sqrt(l2Norm);
|
||||
Object.keys(tfidfs).forEach(tok => {
|
||||
tfidfs[tok][1] /= l2Norm;
|
||||
});
|
||||
|
||||
return tfidfs;
|
||||
}
|
||||
|
||||
const EXPORTED_SYMBOLS = ["tokenize", "toksToTfIdfVector"];
|
@ -18,7 +18,7 @@ const {
|
||||
SEARCH_SHORTCUTS_SEARCH_ENGINES_PREF,
|
||||
SEARCH_SHORTCUTS_HAVE_PINNED_PREF,
|
||||
checkHasSearchEngine,
|
||||
getSearchProvider,
|
||||
getSearchProvider
|
||||
} = ChromeUtils.import("resource://activity-stream/lib/SearchShortcuts.jsm", {});
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "filterAdult",
|
||||
@ -49,7 +49,7 @@ const SEARCH_FILTERS = [
|
||||
"yahoo",
|
||||
"bing",
|
||||
"ask",
|
||||
"duckduckgo",
|
||||
"duckduckgo"
|
||||
];
|
||||
|
||||
function getShortURLForCurrentSearch() {
|
||||
@ -60,7 +60,7 @@ function getShortURLForCurrentSearch() {
|
||||
this.TopSitesFeed = class TopSitesFeed {
|
||||
constructor() {
|
||||
this._tippyTopProvider = new TippyTopProvider();
|
||||
XPCOMUtils.defineLazyGetter(this, "_currentSearchHostname", getShortURLForCurrentSearch);
|
||||
this._currentSearchHostname = null;
|
||||
this.dedupe = new Dedupe(this._dedupeKey);
|
||||
this.frecentCache = new LinksCache(NewTabUtils.activityStreamLinks,
|
||||
"getTopSites", CACHED_LINK_PROPS_TO_MIGRATE, (oldOptions, newOptions) =>
|
||||
@ -77,18 +77,19 @@ this.TopSitesFeed = class TopSitesFeed {
|
||||
this._storage = this.store.dbStorage.getDbTable("sectionPrefs");
|
||||
this.refresh({broadcast: true});
|
||||
Services.obs.addObserver(this, "browser-search-engine-modified");
|
||||
XPCOMUtils.defineLazyGetter(this, "_currentSearchHostname", getShortURLForCurrentSearch);
|
||||
}
|
||||
|
||||
uninit() {
|
||||
PageThumbs.removeExpirationFilter(this);
|
||||
Services.obs.removeObserver(this, "browser-search-engine-modified");
|
||||
this._currentSearchHostname = null;
|
||||
}
|
||||
|
||||
observe(subj, topic, data) {
|
||||
// We should update the current top sites if the search engine has been changed since
|
||||
// the search engine that gets filtered out of top sites has changed.
|
||||
if (topic === "browser-search-engine-modified" && data === "engine-current" && this.store.getState().Prefs.values[NO_DEFAULT_SEARCH_TILE_EXP_PREF]) {
|
||||
delete this._currentSearchHostname;
|
||||
this._currentSearchHostname = getShortURLForCurrentSearch();
|
||||
this.refresh({broadcast: true});
|
||||
}
|
||||
@ -107,7 +108,7 @@ this.TopSitesFeed = class TopSitesFeed {
|
||||
for (const url of sites.split(",")) {
|
||||
const site = {
|
||||
isDefault: true,
|
||||
url,
|
||||
url
|
||||
};
|
||||
site.hostname = shortURL(site);
|
||||
DEFAULT_TOP_SITES.push(site);
|
||||
@ -222,14 +223,14 @@ this.TopSitesFeed = class TopSitesFeed {
|
||||
const frecent = (await this.frecentCache.request({
|
||||
// We need to overquery due to the top 5 alexa search + default search possibly being removed
|
||||
numItems: numItems + SEARCH_FILTERS.length + 1,
|
||||
topsiteFrecency: FRECENCY_THRESHOLD,
|
||||
topsiteFrecency: FRECENCY_THRESHOLD
|
||||
}))
|
||||
.reduce((validLinks, link) => {
|
||||
const hostname = shortURL(link);
|
||||
if (!this.isExperimentOnAndLinkFilteredSearch(hostname)) {
|
||||
validLinks.push({
|
||||
...(searchShortcutsExperiment ? this.topSiteToSearchTopSite(link) : link),
|
||||
hostname,
|
||||
hostname
|
||||
});
|
||||
}
|
||||
return validLinks;
|
||||
@ -250,7 +251,7 @@ this.TopSitesFeed = class TopSitesFeed {
|
||||
}
|
||||
return [
|
||||
...topsites,
|
||||
searchShortcutsExperiment ? this.topSiteToSearchTopSite(link) : link,
|
||||
searchShortcutsExperiment ? this.topSiteToSearchTopSite(link) : link
|
||||
];
|
||||
}, []);
|
||||
|
||||
@ -389,7 +390,7 @@ this.TopSitesFeed = class TopSitesFeed {
|
||||
}, []);
|
||||
this.store.dispatch(ac.BroadcastToContent({
|
||||
type: at.UPDATE_SEARCH_SHORTCUTS,
|
||||
data: {searchShortcuts},
|
||||
data: {searchShortcuts}
|
||||
}));
|
||||
}
|
||||
|
||||
@ -401,7 +402,7 @@ this.TopSitesFeed = class TopSitesFeed {
|
||||
return {
|
||||
...site,
|
||||
searchTopSite: true,
|
||||
label: searchProvider.keyword,
|
||||
label: searchProvider.keyword
|
||||
};
|
||||
}
|
||||
|
||||
@ -439,7 +440,7 @@ this.TopSitesFeed = class TopSitesFeed {
|
||||
await Screenshots.maybeCacheScreenshot(link, url, "screenshot",
|
||||
screenshot => this.store.dispatch(ac.BroadcastToContent({
|
||||
data: {screenshot, url: link.url},
|
||||
type: at.SCREENSHOT_UPDATED,
|
||||
type: at.SCREENSHOT_UPDATED
|
||||
})));
|
||||
}
|
||||
|
||||
@ -452,14 +453,14 @@ this.TopSitesFeed = class TopSitesFeed {
|
||||
const preview = await Screenshots.getScreenshotForURL(url) || "";
|
||||
this.store.dispatch(ac.OnlyToOneContent({
|
||||
data: {url, preview},
|
||||
type: at.PREVIEW_RESPONSE,
|
||||
type: at.PREVIEW_RESPONSE
|
||||
}, target));
|
||||
}
|
||||
|
||||
_requestRichIcon(url) {
|
||||
this.store.dispatch({
|
||||
type: at.RICH_ICON_MISSING,
|
||||
data: {url},
|
||||
data: {url}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -15,12 +15,6 @@ const {SectionsManager} = ChromeUtils.import("resource://activity-stream/lib/Sec
|
||||
const {UserDomainAffinityProvider} = ChromeUtils.import("resource://activity-stream/lib/UserDomainAffinityProvider.jsm", {});
|
||||
const {PersistentCache} = ChromeUtils.import("resource://activity-stream/lib/PersistentCache.jsm", {});
|
||||
|
||||
/* Not yet using personalization v2 taggers
|
||||
const {NaiveBayesTextTagger} = ChromeUtils.import("resource://activity-stream/lib/NaiveBayesTextTagger.jsm", {});
|
||||
const {NmfTextTagger} = ChromeUtils.import("resource://activity-stream/lib/NmfTextTagger.jsm", {});
|
||||
const {RecipeExecutor} = ChromeUtils.import("resource://activity-stream/lib/RecipeExecutor.jsm", {});
|
||||
*/
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "perfService", "resource://activity-stream/common/PerfService.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "pktApi", "chrome://pocket/content/pktApi.jsm");
|
||||
|
||||
@ -205,7 +199,7 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
||||
"url": s.url,
|
||||
"min_score": s.min_score || 0,
|
||||
"score": this.personalized && this.affinityProvider ? this.affinityProvider.calculateItemRelevanceScore(s) : s.item_score || 1,
|
||||
"spoc_meta": this.show_spocs ? {campaign_id: s.campaign_id, caps: s.caps} : {},
|
||||
"spoc_meta": this.show_spocs ? {campaign_id: s.campaign_id, caps: s.caps} : {}
|
||||
}))
|
||||
.sort(this.personalized ? this.compareScore : (a, b) => 0);
|
||||
}
|
||||
@ -272,7 +266,7 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
||||
|
||||
this.store.dispatch(ac.PerfEvent({
|
||||
event: "topstories.domain.affinity.calculation.ms",
|
||||
value: Math.round(perfService.absNow() - start),
|
||||
value: Math.round(perfService.absNow() - start)
|
||||
}));
|
||||
|
||||
const affinities = this.affinityProvider.getAffinities();
|
||||
|
@ -13,7 +13,7 @@ const DEFAULT_TIME_SEGMENTS = [
|
||||
{"id": "day", "startTime": 86400, "endTime": 3600, "weightPosition": 0.75},
|
||||
{"id": "week", "startTime": 604800, "endTime": 86400, "weightPosition": 0.5},
|
||||
{"id": "weekPlus", "startTime": 0, "endTime": 604800, "weightPosition": 0.25},
|
||||
{"id": "alltime", "startTime": 0, "endTime": 0, "weightPosition": 0.25},
|
||||
{"id": "alltime", "startTime": 0, "endTime": 0, "weightPosition": 0.25}
|
||||
];
|
||||
|
||||
const DEFAULT_PARAMETER_SETS = {
|
||||
@ -24,8 +24,8 @@ const DEFAULT_PARAMETER_SETS = {
|
||||
"perfectFrequencyVisits": 10,
|
||||
"perfectCombinedDomainScore": 2,
|
||||
"multiDomainBoost": 0.1,
|
||||
"itemScoreFactor": 0,
|
||||
},
|
||||
"itemScoreFactor": 0
|
||||
}
|
||||
};
|
||||
|
||||
const DEFAULT_MAX_HISTORY_QUERY_RESULTS = 1000;
|
||||
@ -322,7 +322,7 @@ this.UserDomainAffinityProvider = class UserDomainAffinityProvider {
|
||||
parameterSets: this.parameterSets,
|
||||
maxHistoryQueryResults: this.maxHistoryQueryResults,
|
||||
version: this.version,
|
||||
scores: this.scores,
|
||||
scores: this.scores
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -142,8 +142,6 @@ pocket_read_more=Lok macuk gi lamal:
|
||||
# end of the list of popular topic links.
|
||||
pocket_read_even_more=Nen Lok mapol
|
||||
|
||||
pocket_learn_more=Nong ngec mapol
|
||||
|
||||
highlights_empty_state=Cak yeny, ka wa binyuto coc akwana mabeco, video, ki potbuk mukene ma ilimo cokcokki onyo ma kiketo alamabuk kany.
|
||||
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
|
||||
# in the space that would have shown a few stories, this is shown instead.
|
||||
|
@ -144,9 +144,6 @@ pocket_read_more=المواضيع الشائعة:
|
||||
# end of the list of popular topic links.
|
||||
pocket_read_even_more=اعرض المزيد من الأخبار
|
||||
|
||||
pocket_more_reccommendations=مقترحات أخرى
|
||||
pocket_learn_more=اطّلع على المزيد
|
||||
|
||||
highlights_empty_state=ابدأ التصفح وسنعرض أمامك بعض المقالات والفيديوهات والمواقع الأخرى التي زرتها حديثا أو أضفتها إلى العلامات هنا.
|
||||
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
|
||||
# in the space that would have shown a few stories, this is shown instead.
|
||||
|
@ -144,11 +144,6 @@ pocket_read_more=Məşhur Mövzular:
|
||||
# end of the list of popular topic links.
|
||||
pocket_read_even_more=Daha çox hekayə gör
|
||||
|
||||
pocket_more_reccommendations=Daha Çox Tövsiyyələr
|
||||
pocket_learn_more=Ətraflı Öyrən
|
||||
pocket_cta_button=Pocket əldə edin
|
||||
pocket_cta_text=Sevdiyiniz məqalələri Pocket-də saxlayın və möhtəşəm yeni yazıları kəşf edin.
|
||||
|
||||
highlights_empty_state=İnternetdə gəzməyə başlayın, burada ziyarət edəcəyiniz və ya əlfəcinləyəcəyiniz məqalə, video və digər səhifələri göstərəcəyik.
|
||||
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
|
||||
# in the space that would have shown a few stories, this is shown instead.
|
||||
|
@ -144,11 +144,6 @@ pocket_read_more=Папулярныя тэмы:
|
||||
# end of the list of popular topic links.
|
||||
pocket_read_even_more=Іншыя навіны
|
||||
|
||||
pocket_more_reccommendations=Больш рэкамендацый
|
||||
pocket_learn_more=Падрабязней
|
||||
pocket_cta_button=Атрымаць Pocket
|
||||
pocket_cta_text=Захоўвайце ўлюбёныя гісторыі ў Pocket, і сілкуйце свой розум добрай чытанкай.
|
||||
|
||||
highlights_empty_state=Пачніце агляданне, і мы пакажам вам тут некаторыя з найлепшых артыкулаў, відэаролікаў і іншых старонак, якія вы нядаўна наведалі або дадалі ў закладкі.
|
||||
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
|
||||
# in the space that would have shown a few stories, this is shown instead.
|
||||
|
@ -1,7 +1,7 @@
|
||||
newtab_page_title=K'ak'a' ruwi'
|
||||
|
||||
header_top_sites=Jeb'ël Taq Ruxaq
|
||||
header_highlights=Ya'on kiq'ij
|
||||
header_top_sites=Utziläj taq Ruxaq K'amaya'l
|
||||
header_highlights=Taq k'ewachinïk
|
||||
# LOCALIZATION NOTE(header_recommended_by): This is followed by the name
|
||||
# of the corresponding content provider.
|
||||
header_recommended_by=Chilab'en ruma {provider}
|
||||
@ -106,8 +106,8 @@ prefs_highlights_options_download_label=K'a B'a' Keqasäx
|
||||
prefs_highlights_options_pocket_label=Taq Ruxaq Eyakon pa Pocket
|
||||
prefs_snippets_description=Kik'exoj Mozilla chuqa' Firefox
|
||||
settings_pane_button_label=Tawichinaj ri ruxaq richin K'ak'a' Ruwi'
|
||||
settings_pane_topsites_header=Jeb'ël Taq Ruxaq
|
||||
settings_pane_highlights_header=Ya'on kiq'ij
|
||||
settings_pane_topsites_header=Utziläj taq ruxaq K'amaya'l
|
||||
settings_pane_highlights_header=Taq k'ewachinïk
|
||||
settings_pane_highlights_options_bookmarks=Yaketal
|
||||
# LOCALIZATION NOTE(settings_pane_snippets_header): For the "Snippets" feature
|
||||
# traditionally on about:home. Alternative translation options: "Small Note" or
|
||||
@ -144,7 +144,6 @@ pocket_read_more=Nima'q taq Na'oj:
|
||||
# end of the list of popular topic links.
|
||||
pocket_read_even_more=Ketz'et ch'aqa' chik taq B'anob'äl
|
||||
|
||||
|
||||
highlights_empty_state=Katok pa k'amaya'l richin niqak'üt chawäch jeb'ël taq cholna'oj, taq silowachib'äl, chuqa' ch'aqa' chik taq ruxaq k'a b'a' ke'atz'ët o aya'on kan ketal wawe'.
|
||||
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
|
||||
# in the space that would have shown a few stories, this is shown instead.
|
||||
|
@ -144,11 +144,6 @@ pocket_read_more=Populární témata:
|
||||
# end of the list of popular topic links.
|
||||
pocket_read_even_more=Zobrazit více článků
|
||||
|
||||
pocket_more_reccommendations=Další doporučení
|
||||
pocket_learn_more=Zjistit více
|
||||
pocket_cta_button=Získejte Pocket
|
||||
pocket_cta_text=Ukládejte si články do služby Pocket a užívejte si skvělé čtení.
|
||||
|
||||
highlights_empty_state=Začněte prohlížet a my vám zde ukážeme některé skvělé články, videa a další stránky, které jste nedávno viděli nebo uložili do záložek.
|
||||
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
|
||||
# in the space that would have shown a few stories, this is shown instead.
|
||||
|
@ -144,11 +144,6 @@ pocket_read_more=Pynciau Poblogaidd:
|
||||
# end of the list of popular topic links.
|
||||
pocket_read_even_more=Gweld Rhagor o Straeon
|
||||
|
||||
pocket_more_reccommendations=Rhagor o Argymhellion
|
||||
pocket_learn_more=Dysgu Rhagor
|
||||
pocket_cta_button=Defnyddio Pocket
|
||||
pocket_cta_text=Cadw'r straeon rydych yn eu hoffi i Pocket a bwydo'ch meddwl á deunydd diddorol.
|
||||
|
||||
highlights_empty_state=Cychwynnwch bori ac fe ddangoswn rhai erthyglau, fideos a thudalennau eraill difyr rydych wedi ymweld â nhw'n ddiweddar neu wedi gosod nod tudalen arnyn nhw yma.
|
||||
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
|
||||
# in the space that would have shown a few stories, this is shown instead.
|
||||
|
@ -144,11 +144,6 @@ pocket_read_more=Woblubowane temy:
|
||||
# end of the list of popular topic links.
|
||||
pocket_read_even_more=Dalšne powěźeńki se woglědaś
|
||||
|
||||
pocket_more_reccommendations=Dalšne pórucenja
|
||||
pocket_learn_more=Dalšne informacije
|
||||
pocket_cta_button=Pocket wobstaraś
|
||||
pocket_cta_text=Składujśo tšojeńka, kótarež se wam spódobuju, w Pocket a žywśo swój duch z fasciněrujucymi cytańkami.
|
||||
|
||||
highlights_empty_state=Zachopśo pśeglědowaś, a pokažomy někotare wjelicne nastawki, wideo a druge boki, kótarež sćo se njedawno woglědał abo how ako cytańske znamjenja składował.
|
||||
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
|
||||
# in the space that would have shown a few stories, this is shown instead.
|
||||
|
@ -144,10 +144,6 @@ pocket_read_more=Δημοφιλή θέματα:
|
||||
# end of the list of popular topic links.
|
||||
pocket_read_even_more=Προβολή περισσότερων ιστοριών
|
||||
|
||||
pocket_more_reccommendations=Περισσότερες προτάσεις
|
||||
pocket_learn_more=Μάθετε περισσότερα
|
||||
pocket_cta_button=Αποκτήστε το Pocket
|
||||
|
||||
highlights_empty_state=Ξεκινήστε την περιήγηση και θα σάς δείξουμε μερικά υπέροχα άρθρα, βίντεο και άλλες σελίδες που έχετε επισκεφθεί πρόσφατα ή έχετε προσθέσει στους σελιδοδείκτες σας.
|
||||
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
|
||||
# in the space that would have shown a few stories, this is shown instead.
|
||||
|
@ -144,11 +144,6 @@ pocket_read_more=Popular Topics:
|
||||
# end of the list of popular topic links.
|
||||
pocket_read_even_more=View More Stories
|
||||
|
||||
pocket_more_reccommendations=More Recommendations
|
||||
pocket_learn_more=Learn More
|
||||
pocket_cta_button=Get Pocket
|
||||
pocket_cta_text=Save the stories you love in Pocket, and fuel your mind with fascinating reads.
|
||||
|
||||
highlights_empty_state=Start browsing, and we’ll show some of the great articles, videos, and other pages you’ve recently visited or bookmarked here.
|
||||
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
|
||||
# in the space that would have shown a few stories, this is shown instead.
|
||||
|
@ -144,11 +144,6 @@ pocket_read_more=Tópicos populares:
|
||||
# end of the list of popular topic links.
|
||||
pocket_read_even_more=Ver más historias
|
||||
|
||||
pocket_more_reccommendations=Más recomendaciones
|
||||
pocket_learn_more=Conocer más
|
||||
pocket_cta_button=Obtener Pocket
|
||||
pocket_cta_text=Guarde las historias que quiera en Pocket y potencie su mente con lecturas fascinantes.
|
||||
|
||||
highlights_empty_state=Comenzá a navegar y te mostraremos algunos de los mejores artículos, videos y otras páginas que hayás visitado o marcado acá.
|
||||
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
|
||||
# in the space that would have shown a few stories, this is shown instead.
|
||||
|
@ -144,10 +144,6 @@ pocket_read_more=Temas populares:
|
||||
# end of the list of popular topic links.
|
||||
pocket_read_even_more=Ver más historias
|
||||
|
||||
pocket_more_reccommendations=Más recomendaciones
|
||||
pocket_learn_more=Aprender más
|
||||
pocket_cta_button=Obtener Pocket
|
||||
|
||||
highlights_empty_state=Empieza a navegar, y nosotros te mostraremos aquí algunos de los mejores artículos, videos y otras páginas que hayas visitado recientemente o marcado.
|
||||
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
|
||||
# in the space that would have shown a few stories, this is shown instead.
|
||||
|
@ -144,11 +144,6 @@ pocket_read_more=Temas populares:
|
||||
# end of the list of popular topic links.
|
||||
pocket_read_even_more=Ver más historias
|
||||
|
||||
pocket_more_reccommendations=Más recomendaciones
|
||||
pocket_learn_more=Saber más
|
||||
pocket_cta_button=Obtener Pocket
|
||||
pocket_cta_text=Guarda las historias que quieras en Pocket y llena tu mente con fascinantes lecturas.
|
||||
|
||||
highlights_empty_state=Empieza a navegar, y nosotros te mostraremos aquí algunos de los mejores artículos, videos y otras páginas que hayas visitado recientemente o marcado.
|
||||
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
|
||||
# in the space that would have shown a few stories, this is shown instead.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user