зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1478870 - Add component-ified tooling, ordered onboarding and bug fixes to Activity Stream. r=ursula
MozReview-Commit-ID: K14RSdAbVH7 --HG-- extra : rebase_source : f84c082385d46e401bae2ff17a71bb5f54a932cf
This commit is contained in:
Родитель
6cf31b8253
Коммит
8716065e9c
|
@ -20,7 +20,7 @@ const TOPIC_CONTENT_DOCUMENT_INTERACTIVE = "content-document-interactive";
|
||||||
|
|
||||||
// Automated tests ensure packaged locales are in this list. Copied output of:
|
// Automated tests ensure packaged locales are in this list. Copied output of:
|
||||||
// https://github.com/mozilla/activity-stream/blob/master/bin/render-activity-stream-html.js
|
// https://github.com/mozilla/activity-stream/blob/master/bin/render-activity-stream-html.js
|
||||||
const ACTIVITY_STREAM_LOCALES = "en-US ach an ar ast az be bg bn-BD bn-IN br bs ca cak crh cs cy da de dsb el en-CA en-GB eo es-AR es-CL es-ES es-MX et eu fa ff fi fr fy-NL ga-IE gd gl gn gu-IN he hi-IN hr hsb hu hy-AM ia id it ja ja-JP-mac ka kab kk km kn ko lij lo lt ltg lv mai mk ml mr ms my nb-NO ne-NP nl nn-NO oc pa-IN pl pt-BR pt-PT rm ro ru si sk sl sq sr sv-SE ta te th tl tr uk ur uz vi zh-CN zh-TW".split(" ");
|
const ACTIVITY_STREAM_BCP47 = "en-US ach an ar ast az be bg bn-BD bn-IN br bs ca cak crh cs cy da de dsb el en-CA en-GB eo es-AR es-CL es-ES es-MX et eu fa ff fi fr fy-NL ga-IE gd gl gn gu-IN he hi-IN hr hsb hu hy-AM ia id it ja ja-JP-macos ka kab kk km kn ko lij lo lt ltg lv mai mk ml mr ms my nb-NO ne-NP nl nn-NO oc pa-IN pl pt-BR pt-PT rm ro ru si sk sl sq sr sv-SE ta te th tl tr uk ur uz vi zh-CN zh-TW".split(" ");
|
||||||
|
|
||||||
const ABOUT_URL = "about:newtab";
|
const ABOUT_URL = "about:newtab";
|
||||||
const BASE_URL = "resource://activity-stream/";
|
const BASE_URL = "resource://activity-stream/";
|
||||||
|
@ -35,7 +35,6 @@ const PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS = "browser.tabs.remote.separatePr
|
||||||
const PREF_ACTIVITY_STREAM_PRERENDER_ENABLED = "browser.newtabpage.activity-stream.prerender";
|
const PREF_ACTIVITY_STREAM_PRERENDER_ENABLED = "browser.newtabpage.activity-stream.prerender";
|
||||||
const PREF_ACTIVITY_STREAM_DEBUG = "browser.newtabpage.activity-stream.debug";
|
const PREF_ACTIVITY_STREAM_DEBUG = "browser.newtabpage.activity-stream.debug";
|
||||||
|
|
||||||
|
|
||||||
function AboutNewTabService() {
|
function AboutNewTabService() {
|
||||||
Services.obs.addObserver(this, TOPIC_APP_QUIT);
|
Services.obs.addObserver(this, TOPIC_APP_QUIT);
|
||||||
Services.obs.addObserver(this, TOPIC_LOCALES_CHANGE);
|
Services.obs.addObserver(this, TOPIC_LOCALES_CHANGE);
|
||||||
|
@ -103,9 +102,7 @@ AboutNewTabService.prototype = {
|
||||||
Ci.nsIAboutNewTabService,
|
Ci.nsIAboutNewTabService,
|
||||||
Ci.nsIObserver
|
Ci.nsIObserver
|
||||||
]),
|
]),
|
||||||
_xpcom_categories: [{
|
_xpcom_categories: [{service: true}],
|
||||||
service: true
|
|
||||||
}],
|
|
||||||
|
|
||||||
observe(subject, topic, data) {
|
observe(subject, topic, data) {
|
||||||
switch (topic) {
|
switch (topic) {
|
||||||
|
@ -121,7 +118,7 @@ AboutNewTabService.prototype = {
|
||||||
this.notifyChange();
|
this.notifyChange();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TOPIC_CONTENT_DOCUMENT_INTERACTIVE:
|
case TOPIC_CONTENT_DOCUMENT_INTERACTIVE: {
|
||||||
const win = subject.defaultView;
|
const win = subject.defaultView;
|
||||||
|
|
||||||
// It seems like "content-document-interactive" is triggered multiple
|
// It seems like "content-document-interactive" is triggered multiple
|
||||||
|
@ -178,6 +175,7 @@ AboutNewTabService.prototype = {
|
||||||
};
|
};
|
||||||
subject.addEventListener("unload", onUnloaded, {once: true});
|
subject.addEventListener("unload", onUnloaded, {once: true});
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case TOPIC_APP_QUIT:
|
case TOPIC_APP_QUIT:
|
||||||
this.uninit();
|
this.uninit();
|
||||||
if (IS_MAIN_PROCESS) {
|
if (IS_MAIN_PROCESS) {
|
||||||
|
@ -277,17 +275,17 @@ AboutNewTabService.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
set newTabURL(aNewTabURL) {
|
set newTabURL(aNewTabURL) {
|
||||||
aNewTabURL = aNewTabURL.trim();
|
let newTabURL = aNewTabURL.trim();
|
||||||
if (aNewTabURL === ABOUT_URL) {
|
if (newTabURL === ABOUT_URL) {
|
||||||
// avoid infinite redirects in case one sets the URL to about:newtab
|
// avoid infinite redirects in case one sets the URL to about:newtab
|
||||||
this.resetNewTabURL();
|
this.resetNewTabURL();
|
||||||
return;
|
return;
|
||||||
} else if (aNewTabURL === "") {
|
} else if (newTabURL === "") {
|
||||||
aNewTabURL = "about:blank";
|
newTabURL = "about:blank";
|
||||||
}
|
}
|
||||||
|
|
||||||
this.toggleActivityStream(false);
|
this.toggleActivityStream(false);
|
||||||
this._newTabURL = aNewTabURL;
|
this._newTabURL = newTabURL;
|
||||||
this._overridden = true;
|
this._overridden = true;
|
||||||
this.notifyChange();
|
this.notifyChange();
|
||||||
},
|
},
|
||||||
|
@ -311,11 +309,14 @@ AboutNewTabService.prototype = {
|
||||||
get activityStreamLocale() {
|
get activityStreamLocale() {
|
||||||
// Pick the best available locale to match the app locales
|
// Pick the best available locale to match the app locales
|
||||||
return Services.locale.negotiateLanguages(
|
return Services.locale.negotiateLanguages(
|
||||||
Services.locale.getAppLocalesAsLangTags(),
|
Services.locale.getAppLocalesAsBCP47(),
|
||||||
ACTIVITY_STREAM_LOCALES,
|
ACTIVITY_STREAM_BCP47,
|
||||||
// defaultLocale's strings aren't necessarily packaged, but en-US' are
|
// defaultLocale's strings aren't necessarily packaged, but en-US' are
|
||||||
"en-US"
|
"en-US",
|
||||||
)[0];
|
Services.locale.langNegStrategyLookup
|
||||||
|
// Convert the BCP47 to lang tag, which is what is used in our paths, as a
|
||||||
|
// workaround for bug 1478930 negotiating incorrectly with lang tags
|
||||||
|
)[0].replace(/^(ja-JP-mac)os$/, "$1");
|
||||||
},
|
},
|
||||||
|
|
||||||
resetNewTabURL() {
|
resetNewTabURL() {
|
||||||
|
|
|
@ -26,7 +26,7 @@ else
|
||||||
FIREFOX_PATH=${AS_PINE_TEST_DIR}/mozilla-central
|
FIREFOX_PATH=${AS_PINE_TEST_DIR}/mozilla-central
|
||||||
fi
|
fi
|
||||||
|
|
||||||
MC_MODULE_PATH="$FIREFOX_PATH/browser/extensions/activity-stream"
|
MC_MODULE_PATH="$FIREFOX_PATH/browser/components/newtab"
|
||||||
|
|
||||||
# By default, just use mozilla-central + the export. If ENABLE_MC_AS is set to
|
# By default, just use mozilla-central + the export. If ENABLE_MC_AS is set to
|
||||||
# 1, patch on top of mozilla-central + the export to turn on the AS pref and
|
# 1, patch on top of mozilla-central + the export to turn on the AS pref and
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
#! /usr/bin/env node
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const MIN_FIREFOX_VERSION = "55.0a1";
|
|
||||||
|
|
||||||
/* globals cd, mv, sed */
|
|
||||||
require("shelljs/global");
|
|
||||||
|
|
||||||
cd(process.argv[2]);
|
|
||||||
|
|
||||||
// Convert install.rdf.in to install.rdf without substitutions
|
|
||||||
mv("install.rdf.in", "install.rdf");
|
|
||||||
sed("-i", /^#filter substitution/, "", "install.rdf");
|
|
||||||
sed("-i", /(<em:minVersion>).+(<\/em:minVersion>)/, `$1${MIN_FIREFOX_VERSION}$2`, "install.rdf");
|
|
||||||
sed("-i", /(<em:maxVersion>).+(<\/em:maxVersion>)/, "$1*$2", "install.rdf");
|
|
||||||
|
|
||||||
// Convert jar.mn to chrome.manifest with just manifest
|
|
||||||
mv("jar.mn", "chrome.manifest");
|
|
||||||
sed("-i", /^[^%].*$/, "", "chrome.manifest");
|
|
||||||
sed("-i", /^% (content.*) %(.*)$/, "$1 $2", "chrome.manifest");
|
|
||||||
sed("-i", /^% (resource.*) %.*$/, "$1 .", "chrome.manifest");
|
|
|
@ -245,8 +245,11 @@ function main() { // eslint-disable-line max-statements
|
||||||
console.log("\x1b[33m", `Skipped the following locales because they are not in CENTRAL_LOCALES: ${extraLocales.join(", ")}`, "\x1b[0m");
|
console.log("\x1b[33m", `Skipped the following locales because they are not in CENTRAL_LOCALES: ${extraLocales.join(", ")}`, "\x1b[0m");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert ja-JP-mac lang tag to ja-JP-macos bcp47 to work around bug 1478930
|
||||||
|
const bcp47String = localizedLocales.join(" ").replace(/(ja-JP-mac)/, "$1os");
|
||||||
|
|
||||||
// Provide some help to copy/paste locales if tests are failing
|
// Provide some help to copy/paste locales if tests are failing
|
||||||
console.log(`\nIf aboutNewTabService tests are failing for unexpected locales, make sure its list is updated:\nconst ACTIVITY_STREAM_LOCALES = "${localizedLocales.join(" ")}".split(" ");`);
|
console.log(`\nIf aboutNewTabService tests are failing for unexpected locales, make sure its list is updated:\nconst ACTIVITY_STREAM_BCP47 = "${bcp47String}".split(" ");`);
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
#! /usr/bin/env node
|
|
||||||
/* globals cd, sed */
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate update install.rdf.in in the given directory with a version string
|
|
||||||
* composed of YYYY.MM.DD.${minuteOfDay}-${github_commit_hash}.
|
|
||||||
*
|
|
||||||
* @note The github hash is taken from the github repo in the current directory
|
|
||||||
* the script is run in.
|
|
||||||
*
|
|
||||||
* @note The minute of the day was chosen so that the version number is
|
|
||||||
* (more-or-less) consistently increasing (modulo clock-skew and builds that
|
|
||||||
* happen within a minute of each other), and although it's UTC, it won't likely
|
|
||||||
* be confused with something in a readers own time zone.
|
|
||||||
*
|
|
||||||
* @example generated version string: 2017.08.28.1217-ebda466c
|
|
||||||
*/
|
|
||||||
const process = require("process");
|
|
||||||
require("shelljs/global");
|
|
||||||
const simpleGit = require("simple-git")(process.cwd());
|
|
||||||
|
|
||||||
const time = new Date();
|
|
||||||
const minuteOfDay = time.getUTCHours() * 60 + time.getUTCMinutes();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the given string padded with 0s out to the given width.
|
|
||||||
*
|
|
||||||
* XXX we should ditch this function in favor of using padStart once
|
|
||||||
* we start requiring Node 8.
|
|
||||||
*
|
|
||||||
* @param {any} s - the string to pad, will be coerced to String first
|
|
||||||
* @param {Number} width - what's the desired width?
|
|
||||||
*/
|
|
||||||
function zeroPadStart(s, width) {
|
|
||||||
let padded = String(s);
|
|
||||||
while (padded.length < width) {
|
|
||||||
padded = `0${padded}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return padded;
|
|
||||||
}
|
|
||||||
|
|
||||||
// git rev-parse --short HEAD
|
|
||||||
simpleGit.revparse(["--short", "HEAD"], (err, gitHash) => {
|
|
||||||
if (err) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(`SimpleGit.revparse failed: ${err}`);
|
|
||||||
throw new Error(`SimpleGit.revparse failed: ${err}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line prefer-template
|
|
||||||
let versionString = String(time.getUTCFullYear()) +
|
|
||||||
"." + zeroPadStart(time.getUTCMonth() + 1, 2) +
|
|
||||||
"." + zeroPadStart(time.getUTCDate(), 2) +
|
|
||||||
"." + zeroPadStart(minuteOfDay, 4) +
|
|
||||||
"-" + gitHash.trim();
|
|
||||||
|
|
||||||
cd(process.argv[2]);
|
|
||||||
sed("-i", /(<em:version>).+(<\/em:version>)$/, `$1${versionString}$2`,
|
|
||||||
"install.rdf.in");
|
|
||||||
});
|
|
|
@ -7,6 +7,8 @@ Field name | Type | Required | Description | Example / Note
|
||||||
`publish_start` | `date` | No | When to start showing the message | `1524474850876`
|
`publish_start` | `date` | No | When to start showing the message | `1524474850876`
|
||||||
`publish_end` | `date` | No | When to stop showing the message | `1524474850876`
|
`publish_end` | `date` | No | When to stop showing the message | `1524474850876`
|
||||||
`content` | `object` | Yes | An object containing all variables/props to be rendered in the template. Subset of allowed tags detailed below. | [See example below](#html-subset)
|
`content` | `object` | Yes | An object containing all variables/props to be rendered in the template. Subset of allowed tags detailed below. | [See example below](#html-subset)
|
||||||
|
`bundled` | `integer` | No | The number of messages of the same template this one should be shown with | [See example below](#a-bundled-message-example)
|
||||||
|
`order` | `integer` | No | If bundled with other messages of the same template, which order should this one be placed in? Defaults to 0 if no order is desired | [See example below](#a-bundled-message-example)
|
||||||
`campaign` | `string` | No | Campaign id that the message belongs to | `RustWebAssembly`
|
`campaign` | `string` | No | Campaign id that the message belongs to | `RustWebAssembly`
|
||||||
`targeting` | `string` `JEXL` | No | A [JEXL expression](http://normandy.readthedocs.io/en/latest/user/filter_expressions.html#jexl-basics) with all targeting information needed in order to decide if the message is shown | Not yet implemented, [Examples](#targeting-attributes)
|
`targeting` | `string` `JEXL` | No | A [JEXL expression](http://normandy.readthedocs.io/en/latest/user/filter_expressions.html#jexl-basics) with all targeting information needed in order to decide if the message is shown | Not yet implemented, [Examples](#targeting-attributes)
|
||||||
`trigger` | `string` | No | An event or condition upon which the message will be immediately shown. This can be combined with `targeting`. Messages that define a trigger will not be shown during non-trigger-based passive message rotation.
|
`trigger` | `string` | No | An event or condition upon which the message will be immediately shown. This can be combined with `targeting`. Messages that define a trigger will not be shown during non-trigger-based passive message rotation.
|
||||||
|
@ -31,6 +33,35 @@ Field name | Type | Required | Description | Example / Note
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### A Bundled Message example
|
||||||
|
The following 2 messages have a `bundled` property, indicating that they should be shown together, since they have the same template. The number `2` indicates that this message should be shown in a bundle of 2 messages of the same template. The order property defines that ONBOARDING_2 should be shown after ONBOARDING_3 in the bundle.
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
id: "ONBOARDING_2",
|
||||||
|
template: "onboarding",
|
||||||
|
bundled: 2,
|
||||||
|
order: 2,
|
||||||
|
content: {
|
||||||
|
title: "Private Browsing",
|
||||||
|
body: "Browse by yourself. Private Browsing with Tracking Protection blocks online trackers that follow you around the web."
|
||||||
|
},
|
||||||
|
targeting: "",
|
||||||
|
trigger: "firstRun"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
id: "ONBOARDING_3",
|
||||||
|
template: "onboarding",
|
||||||
|
bundled: 2,
|
||||||
|
order: 1,
|
||||||
|
content: {
|
||||||
|
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: "",
|
||||||
|
trigger: "firstRun"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### HTML subset
|
### HTML subset
|
||||||
The following tags are allowed in the content of the snippet: `i, b, u, strong, em, br`.
|
The following tags are allowed in the content of the snippet: `i, b, u, strong, em, br`.
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,15 @@
|
||||||
"description": "An id matching an existing Activity Stream Router template",
|
"description": "An id matching an existing Activity Stream Router template",
|
||||||
"enum": ["simple_snippet"]
|
"enum": ["simple_snippet"]
|
||||||
},
|
},
|
||||||
|
"bundled": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The number of messages of the same template this one should be shown with (optional)"
|
||||||
|
},
|
||||||
|
"order": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0,
|
||||||
|
"description": "If bundled with other messages of the same template, which order should this one be placed in? (optional - defaults to 0)"
|
||||||
|
},
|
||||||
"content": {
|
"content": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "An object containing all variables/props to be rendered in the template. See individual template schemas for details."
|
"description": "An object containing all variables/props to be rendered in the template. See individual template schemas for details."
|
||||||
|
|
|
@ -18,7 +18,7 @@ and make sure the `browser.newtabpage.activity-stream.enabled` pref is set to `t
|
||||||
## Source code and submitting pull requests
|
## Source code and submitting pull requests
|
||||||
|
|
||||||
A copy of the code in the [system-addon/](../../system-addon/) subdirectory of this repository
|
A copy of the code in the [system-addon/](../../system-addon/) subdirectory of this repository
|
||||||
is exported to Mozilla central on a regular basis, which can be found at [browser/extensions/activity-stream](https://searchfox.org/mozilla-central/source/browser/extensions/activity-stream).
|
is exported to Mozilla central on a regular basis, which can be found at [browser/components/newtab](https://searchfox.org/mozilla-central/source/browser/components/newtab).
|
||||||
Keep in mind that some of these files are generated, so if you intend on editing any files, you should
|
Keep in mind that some of these files are generated, so if you intend on editing any files, you should
|
||||||
do so in the Github version.
|
do so in the Github version.
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,11 @@ const ONE_HOUR_IN_MS = 60 * 60 * 1000;
|
||||||
const SNIPPETS_ENDPOINT_PREF = "browser.newtabpage.activity-stream.asrouter.snippetsUrl";
|
const SNIPPETS_ENDPOINT_PREF = "browser.newtabpage.activity-stream.asrouter.snippetsUrl";
|
||||||
// List of hosts for endpoints that serve router messages.
|
// List of hosts for endpoints that serve router messages.
|
||||||
// Key is allowed host, value is a name for the endpoint host.
|
// Key is allowed host, value is a name for the endpoint host.
|
||||||
const WHITELIST_HOSTS = {
|
const DEFAULT_WHITELIST_HOSTS = {
|
||||||
"activity-stream-icons.services.mozilla.com": "production",
|
"activity-stream-icons.services.mozilla.com": "production",
|
||||||
"snippets-admin.mozilla.org": "preview"
|
"snippets-admin.mozilla.org": "preview"
|
||||||
};
|
};
|
||||||
|
const SNIPPETS_ENDPOINT_WHITELIST = "browser.newtab.activity-stream.asrouter.whitelistHosts";
|
||||||
|
|
||||||
const MessageLoaderUtils = {
|
const MessageLoaderUtils = {
|
||||||
/**
|
/**
|
||||||
|
@ -229,6 +230,7 @@ class _ASRouter {
|
||||||
this.messageChannel.addMessageListener(INCOMING_MESSAGE_NAME, this.onMessage);
|
this.messageChannel.addMessageListener(INCOMING_MESSAGE_NAME, this.onMessage);
|
||||||
this._addASRouterPrefListener();
|
this._addASRouterPrefListener();
|
||||||
this._storage = storage;
|
this._storage = storage;
|
||||||
|
this.WHITELIST_HOSTS = this._loadSnippetsWhitelistHosts();
|
||||||
|
|
||||||
const blockList = await this._storage.get("blockList") || [];
|
const blockList = await this._storage.get("blockList") || [];
|
||||||
const impressions = await this._storage.get("impressions") || {};
|
const impressions = await this._storage.get("impressions") || {};
|
||||||
|
@ -283,8 +285,12 @@ class _ASRouter {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_orderBundle(bundle) {
|
||||||
|
return bundle.sort((a, b) => a.order - b.order);
|
||||||
|
}
|
||||||
|
|
||||||
async _getBundledMessages(originalMessage, target, data, force = false) {
|
async _getBundledMessages(originalMessage, target, data, force = false) {
|
||||||
let result = [{content: originalMessage.content, id: originalMessage.id}];
|
let result = [{content: originalMessage.content, id: originalMessage.id, order: originalMessage.order || 0}];
|
||||||
|
|
||||||
// First, find all messages of same template. These are potential matching targeting candidates
|
// First, find all messages of same template. These are potential matching targeting candidates
|
||||||
let bundledMessagesOfSameTemplate = this._getUnblockedMessages()
|
let bundledMessagesOfSameTemplate = this._getUnblockedMessages()
|
||||||
|
@ -309,7 +315,7 @@ class _ASRouter {
|
||||||
}
|
}
|
||||||
// Only copy the content of the message (that's what the UI cares about)
|
// Only copy the content of the message (that's what the UI cares about)
|
||||||
// Also delete the message we picked so we don't pick it again
|
// Also delete the message we picked so we don't pick it again
|
||||||
result.push({content: message.content, id: message.id});
|
result.push({content: message.content, id: message.id, order: message.order || 0});
|
||||||
bundledMessagesOfSameTemplate.splice(bundledMessagesOfSameTemplate.findIndex(msg => msg.id === message.id), 1);
|
bundledMessagesOfSameTemplate.splice(bundledMessagesOfSameTemplate.findIndex(msg => msg.id === message.id), 1);
|
||||||
// Stop once we have enough messages to fill a bundle
|
// Stop once we have enough messages to fill a bundle
|
||||||
if (result.length === originalMessage.bundled) {
|
if (result.length === originalMessage.bundled) {
|
||||||
|
@ -322,7 +328,8 @@ class _ASRouter {
|
||||||
if (result.length < originalMessage.bundled) {
|
if (result.length < originalMessage.bundled) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return {bundle: result, provider: originalMessage.provider, template: originalMessage.template};
|
|
||||||
|
return {bundle: this._orderBundle(result), provider: originalMessage.provider, template: originalMessage.template};
|
||||||
}
|
}
|
||||||
|
|
||||||
_getUnblockedMessages() {
|
_getUnblockedMessages() {
|
||||||
|
@ -468,18 +475,42 @@ class _ASRouter {
|
||||||
_validPreviewEndpoint(url) {
|
_validPreviewEndpoint(url) {
|
||||||
try {
|
try {
|
||||||
const endpoint = new URL(url);
|
const endpoint = new URL(url);
|
||||||
if (!WHITELIST_HOSTS[endpoint.host]) {
|
if (!this.WHITELIST_HOSTS[endpoint.host]) {
|
||||||
Cu.reportError(`The preview URL host ${endpoint.host} is not in the whitelist.`);
|
Cu.reportError(`The preview URL host ${endpoint.host} is not in the whitelist.`);
|
||||||
}
|
}
|
||||||
if (endpoint.protocol !== "https:") {
|
if (endpoint.protocol !== "https:") {
|
||||||
Cu.reportError("The URL protocol is not https.");
|
Cu.reportError("The URL protocol is not https.");
|
||||||
}
|
}
|
||||||
return (endpoint.protocol === "https:" && WHITELIST_HOSTS[endpoint.host]);
|
return (endpoint.protocol === "https:" && this.WHITELIST_HOSTS[endpoint.host]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_loadSnippetsWhitelistHosts() {
|
||||||
|
let additionalHosts = [];
|
||||||
|
const whitelistPrefValue = Services.prefs.getStringPref(SNIPPETS_ENDPOINT_WHITELIST, "");
|
||||||
|
try {
|
||||||
|
additionalHosts = JSON.parse(whitelistPrefValue);
|
||||||
|
} catch (e) {
|
||||||
|
if (whitelistPrefValue) {
|
||||||
|
Cu.reportError(`Pref ${SNIPPETS_ENDPOINT_WHITELIST} value is not valid JSON`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!additionalHosts.length) {
|
||||||
|
return DEFAULT_WHITELIST_HOSTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are additional hosts we want to whitelist, add them as
|
||||||
|
// `preview` so that the updateCycle is 0
|
||||||
|
return additionalHosts.reduce((whitelist_hosts, host) => {
|
||||||
|
whitelist_hosts[host] = "preview";
|
||||||
|
Services.console.logStringMessage(`Adding ${host} to whitelist hosts.`);
|
||||||
|
return whitelist_hosts;
|
||||||
|
}, {...DEFAULT_WHITELIST_HOSTS});
|
||||||
|
}
|
||||||
|
|
||||||
async _addPreviewEndpoint(url) {
|
async _addPreviewEndpoint(url) {
|
||||||
const providers = [...this.state.providers];
|
const providers = [...this.state.providers];
|
||||||
if (this._validPreviewEndpoint(url) && !providers.find(p => p.url === url)) {
|
if (this._validPreviewEndpoint(url) && !providers.find(p => p.url === url)) {
|
||||||
|
|
|
@ -40,6 +40,11 @@ const TopFrecentSitesCache = {
|
||||||
}
|
}
|
||||||
resolve(this._topFrecentSites);
|
resolve(this._topFrecentSites);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
// For testing
|
||||||
|
expire() {
|
||||||
|
this._lastUpdated = 0;
|
||||||
|
this._topFrecentSites = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -215,4 +220,6 @@ this.ASRouterTargeting = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.EXPORTED_SYMBOLS = ["ASRouterTargeting", "removeRandomItemFromArray"];
|
// Export for testing
|
||||||
|
this.TopFrecentSitesCache = TopFrecentSitesCache;
|
||||||
|
this.EXPORTED_SYMBOLS = ["ASRouterTargeting", "TopFrecentSitesCache"];
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
ChromeUtils.import("resource:///modules/AboutNewTab.jsm");
|
ChromeUtils.import("resource:///modules/AboutNewTab.jsm");
|
||||||
|
/* globals RemotePages */ // Remove when updating eslint-plugin-mozilla 0.14.0+
|
||||||
ChromeUtils.import("resource://gre/modules/remotepagemanager/RemotePageManagerParent.jsm");
|
ChromeUtils.import("resource://gre/modules/remotepagemanager/RemotePageManagerParent.jsm");
|
||||||
|
|
||||||
const {actionCreators: ac, actionTypes: at, actionUtils: au} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
|
const {actionCreators: ac, actionTypes: at, actionUtils: au} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
|
||||||
|
|
|
@ -8,6 +8,7 @@ const ONBOARDING_MESSAGES = [
|
||||||
id: "ONBOARDING_1",
|
id: "ONBOARDING_1",
|
||||||
template: "onboarding",
|
template: "onboarding",
|
||||||
bundled: 3,
|
bundled: 3,
|
||||||
|
order: 2,
|
||||||
content: {
|
content: {
|
||||||
title: "Private Browsing",
|
title: "Private Browsing",
|
||||||
text: "Browse by yourself. Private Browsing with Tracking Protection blocks online trackers that follow you around the web.",
|
text: "Browse by yourself. Private Browsing with Tracking Protection blocks online trackers that follow you around the web.",
|
||||||
|
@ -21,6 +22,7 @@ const ONBOARDING_MESSAGES = [
|
||||||
id: "ONBOARDING_2",
|
id: "ONBOARDING_2",
|
||||||
template: "onboarding",
|
template: "onboarding",
|
||||||
bundled: 3,
|
bundled: 3,
|
||||||
|
order: 3,
|
||||||
content: {
|
content: {
|
||||||
title: "Screenshots",
|
title: "Screenshots",
|
||||||
text: "Take, save and share screenshots - without leaving Firefox. Capture a region or an entire page as you browse. Then save to the web for easy access and sharing.",
|
text: "Take, save and share screenshots - without leaving Firefox. Capture a region or an entire page as you browse. Then save to the web for easy access and sharing.",
|
||||||
|
@ -35,6 +37,7 @@ const ONBOARDING_MESSAGES = [
|
||||||
id: "ONBOARDING_3",
|
id: "ONBOARDING_3",
|
||||||
template: "onboarding",
|
template: "onboarding",
|
||||||
bundled: 3,
|
bundled: 3,
|
||||||
|
order: 1,
|
||||||
content: {
|
content: {
|
||||||
title: "Add-ons",
|
title: "Add-ons",
|
||||||
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.",
|
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.",
|
||||||
|
@ -50,11 +53,12 @@ const ONBOARDING_MESSAGES = [
|
||||||
id: "ONBOARDING_4",
|
id: "ONBOARDING_4",
|
||||||
template: "onboarding",
|
template: "onboarding",
|
||||||
bundled: 3,
|
bundled: 3,
|
||||||
|
order: 1,
|
||||||
content: {
|
content: {
|
||||||
title: "Extensions",
|
title: "Block Ads with Ghostery",
|
||||||
text: "Make browsing faster, smarter, or safer with browser apps. Protect passwords, find deals, download videos, and much more. You can even block annoying ads with extensions like Ghostery.",
|
text: "Browse faster, smarter, or safer with extensions like Ghostery, which lets you block annoying ads.",
|
||||||
icon: "gift",
|
icon: "gift",
|
||||||
button_label: "Get Ghostery",
|
button_label: "Try It Now",
|
||||||
button_action: "OPEN_URL",
|
button_action: "OPEN_URL",
|
||||||
button_action_params: "https://addons.mozilla.org/en-US/firefox/addon/ghostery/"
|
button_action_params: "https://addons.mozilla.org/en-US/firefox/addon/ghostery/"
|
||||||
},
|
},
|
||||||
|
|
|
@ -105,7 +105,7 @@ prefs_highlights_options_visited_label=Baxılmış Səhifələr
|
||||||
prefs_highlights_options_download_label=Son Endirmələr
|
prefs_highlights_options_download_label=Son Endirmələr
|
||||||
prefs_highlights_options_pocket_label=Pocket-ə Saxlanılan Səhifələr
|
prefs_highlights_options_pocket_label=Pocket-ə Saxlanılan Səhifələr
|
||||||
prefs_snippets_description=Mozilla və Firefoxdan yeniliklər
|
prefs_snippets_description=Mozilla və Firefoxdan yeniliklər
|
||||||
settings_pane_button_label=Yeni Vərəq səhifənizi özəlləşdirin
|
settings_pane_button_label=Yeni Vərəq səhifənizi fərdiləşdirin
|
||||||
settings_pane_topsites_header=Qabaqcıl Saytlar
|
settings_pane_topsites_header=Qabaqcıl Saytlar
|
||||||
settings_pane_highlights_header=Seçilmişlər
|
settings_pane_highlights_header=Seçilmişlər
|
||||||
settings_pane_highlights_options_bookmarks=Əlfəcinlər
|
settings_pane_highlights_options_bookmarks=Əlfəcinlər
|
||||||
|
|
|
@ -206,6 +206,8 @@ firstrun_form_sub_header=for at fortsætte til Firefox Sync.
|
||||||
|
|
||||||
firstrun_email_input_placeholder=Mailadresse
|
firstrun_email_input_placeholder=Mailadresse
|
||||||
|
|
||||||
|
firstrun_invalid_input=En gyldig mailadresse er påkrævet
|
||||||
|
|
||||||
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
|
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
|
||||||
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
|
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
|
||||||
firstrun_extra_legal_links=Ved at fortsætte godkender du vores {terms} og {privacy}.
|
firstrun_extra_legal_links=Ved at fortsætte godkender du vores {terms} og {privacy}.
|
||||||
|
|
|
@ -191,6 +191,7 @@ firstrun_form_sub_header=para acceder a Firefox Sync.
|
||||||
|
|
||||||
firstrun_email_input_placeholder=Correo electrónico
|
firstrun_email_input_placeholder=Correo electrónico
|
||||||
|
|
||||||
|
firstrun_invalid_input=Se requiere un correo válido
|
||||||
|
|
||||||
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
|
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
|
||||||
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
|
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
|
||||||
|
|
|
@ -191,6 +191,8 @@ firstrun_form_sub_header=eike hag̃ua Firefox Sync-pe.
|
||||||
|
|
||||||
firstrun_email_input_placeholder=Ñandutiveve
|
firstrun_email_input_placeholder=Ñandutiveve
|
||||||
|
|
||||||
|
firstrun_invalid_input=Eikotevẽ peteĩ ñanduti veve oikóva
|
||||||
|
|
||||||
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
|
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
|
||||||
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
|
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
|
||||||
firstrun_extra_legal_links=Ejapóva, emoneĩ ko'ã {terms} ha {privacy}.
|
firstrun_extra_legal_links=Ejapóva, emoneĩ ko'ã {terms} ha {privacy}.
|
||||||
|
|
|
@ -191,6 +191,8 @@ firstrun_form_sub_header=Firefox Sync の利用を続けるために必要です
|
||||||
|
|
||||||
firstrun_email_input_placeholder=メールアドレス
|
firstrun_email_input_placeholder=メールアドレス
|
||||||
|
|
||||||
|
firstrun_invalid_input=メールアドレスを正しく入力してください
|
||||||
|
|
||||||
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
|
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
|
||||||
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
|
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
|
||||||
firstrun_extra_legal_links=続行すると、{terms} と {privacy} に同意したものとみなします。
|
firstrun_extra_legal_links=続行すると、{terms} と {privacy} に同意したものとみなします。
|
||||||
|
|
|
@ -191,6 +191,8 @@ firstrun_form_sub_header=Firefox Sync の利用を続けるために必要です
|
||||||
|
|
||||||
firstrun_email_input_placeholder=メールアドレス
|
firstrun_email_input_placeholder=メールアドレス
|
||||||
|
|
||||||
|
firstrun_invalid_input=メールアドレスを正しく入力してください
|
||||||
|
|
||||||
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
|
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
|
||||||
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
|
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
|
||||||
firstrun_extra_legal_links=続行すると、{terms} と {privacy} に同意したものとみなします。
|
firstrun_extra_legal_links=続行すると、{terms} と {privacy} に同意したものとみなします。
|
||||||
|
|
|
@ -191,6 +191,8 @@ firstrun_form_sub_header=பயர்பாக்சு ஒத்திசைய
|
||||||
|
|
||||||
firstrun_email_input_placeholder=மின்னஞ்சல்
|
firstrun_email_input_placeholder=மின்னஞ்சல்
|
||||||
|
|
||||||
|
firstrun_invalid_input=நம்பகரமான மின்னஞ்சல் தேவை
|
||||||
|
|
||||||
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
|
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
|
||||||
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
|
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
|
||||||
firstrun_extra_legal_links=தொடர்வதன் மூலம், தாங்கள் {terms} மற்றும் {privacy} ஒப்புக்கொள்கின்றீர்கள்.
|
firstrun_extra_legal_links=தொடர்வதன் மூலம், தாங்கள் {terms} மற்றும் {privacy} ஒப்புக்கொள்கின்றீர்கள்.
|
||||||
|
|
|
@ -40,7 +40,7 @@ confirm_history_delete_p1=您確定要刪除此頁面的所有瀏覽紀錄?
|
||||||
# LOCALIZATION NOTE (confirm_history_delete_notice_p2): this string is displayed in
|
# LOCALIZATION NOTE (confirm_history_delete_notice_p2): this string is displayed in
|
||||||
# the same dialog as confirm_history_delete_p1. "This action" refers to deleting a
|
# the same dialog as confirm_history_delete_p1. "This action" refers to deleting a
|
||||||
# page from history.
|
# page from history.
|
||||||
confirm_history_delete_notice_p2=無法還原此操作。
|
confirm_history_delete_notice_p2=此動作無法復原。
|
||||||
menu_action_save_to_pocket=儲存至 Pocket
|
menu_action_save_to_pocket=儲存至 Pocket
|
||||||
menu_action_delete_pocket=從 Pocket 刪除
|
menu_action_delete_pocket=從 Pocket 刪除
|
||||||
menu_action_archive_pocket=在 Pocket 裡封存
|
menu_action_archive_pocket=在 Pocket 裡封存
|
||||||
|
@ -155,7 +155,7 @@ topstories_empty_state=所有文章都讀完啦!晚點再來,{provider} 將
|
||||||
manual_migration_explanation2=試試將其他瀏覽器的書籤、瀏覽記錄與密碼匯入 Firefox。
|
manual_migration_explanation2=試試將其他瀏覽器的書籤、瀏覽記錄與密碼匯入 Firefox。
|
||||||
# LOCALIZATION NOTE (manual_migration_cancel_button): This message is shown on a button that cancels the
|
# LOCALIZATION NOTE (manual_migration_cancel_button): This message is shown on a button that cancels the
|
||||||
# process of importing another browser’s profile into Firefox.
|
# process of importing another browser’s profile into Firefox.
|
||||||
manual_migration_cancel_button=不必了
|
manual_migration_cancel_button=不要,謝謝
|
||||||
# LOCALIZATION NOTE (manual_migration_import_button): This message is shown on a button that starts the process
|
# LOCALIZATION NOTE (manual_migration_import_button): This message is shown on a button that starts the process
|
||||||
# of importing another browser’s profile profile into Firefox.
|
# of importing another browser’s profile profile into Firefox.
|
||||||
manual_migration_import_button=立即匯入
|
manual_migration_import_button=立即匯入
|
||||||
|
@ -187,7 +187,7 @@ firstrun_learn_more_link=了解 Firefox Accounts 的更多資訊
|
||||||
# firstrun_form_sub_header is a continuation of firstrun_form_header, they are one sentence.
|
# firstrun_form_sub_header is a continuation of firstrun_form_header, they are one sentence.
|
||||||
# firstrun_form_header is displayed more boldly as the call to action.
|
# firstrun_form_header is displayed more boldly as the call to action.
|
||||||
firstrun_form_header=輸入您的電子郵件地址
|
firstrun_form_header=輸入您的電子郵件地址
|
||||||
firstrun_form_sub_header=繼續前往 Firefox Sync。
|
firstrun_form_sub_header=繼續前往 Firefox Sync
|
||||||
|
|
||||||
firstrun_email_input_placeholder=電子郵件
|
firstrun_email_input_placeholder=電子郵件
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ cd /activity-stream && npm install . && npm run buildmc
|
||||||
cd /mozilla-central && ./mach build \
|
cd /mozilla-central && ./mach build \
|
||||||
&& ./mach test browser_parsable_css \
|
&& ./mach test browser_parsable_css \
|
||||||
&& ./mach lint -l eslint -l codespell browser/components/newtab \
|
&& ./mach lint -l eslint -l codespell browser/components/newtab \
|
||||||
&& ./mach test browser/components/newtab --headless \
|
|
||||||
&& ./mach test browser/components/newtab/test/browser --headless \
|
&& ./mach test browser/components/newtab/test/browser --headless \
|
||||||
&& ./mach test browser/components/newtab/test/xpcshell \
|
&& ./mach test browser/components/newtab/test/xpcshell \
|
||||||
&& ./mach test browser/components/preferences/in-content/tests/browser_hometab_restore_defaults.js --headless \
|
&& ./mach test browser/components/preferences/in-content/tests/browser_hometab_restore_defaults.js --headless \
|
||||||
|
|
|
@ -90,27 +90,26 @@
|
||||||
"mc_dir": "../mozilla-central"
|
"mc_dir": "../mozilla-central"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"mochitest": "(cd $npm_package_config_mc_dir && ./mach mochitest browser/extensions/activity-stream/test/functional/mochitest --headless)",
|
"mochitest": "(cd $npm_package_config_mc_dir && ./mach mochitest browser/components/newtab/test/browser --headless)",
|
||||||
"mochitest-debug": "(cd $npm_package_config_mc_dir && ./mach mochitest --jsdebugger browser/extensions/activity-stream/test/functional/mochitest)",
|
"mochitest-debug": "(cd $npm_package_config_mc_dir && ./mach mochitest --jsdebugger browser/components/newtab/test/browser)",
|
||||||
"bundle": "npm-run-all bundle:*",
|
"bundle": "npm-run-all bundle:*",
|
||||||
"bundle:locales": "pontoon-to-json --src locales --dest data",
|
"bundle:locales": "pontoon-to-json --src locales --dest data",
|
||||||
"bundle:webpack": "webpack --config webpack.system-addon.config.js",
|
"bundle:webpack": "webpack --config webpack.system-addon.config.js",
|
||||||
"bundle:css": "node-sass --source-map true --source-map-contents content-src/styles -o css",
|
"bundle:css": "node-sass --source-map true --source-map-contents content-src/styles -o css",
|
||||||
"bundle:html": "rimraf prerendered && webpack --config webpack.prerender.config.js && node ./bin/render-activity-stream-html.js",
|
"bundle:html": "rimraf prerendered && webpack --config webpack.prerender.config.js && node ./bin/render-activity-stream-html.js",
|
||||||
"buildmc": "npm-run-all buildmc:*",
|
"buildmc": "npm-run-all buildmc:*",
|
||||||
"prebuildmc": "rimraf $npm_package_config_mc_dir/browser/extensions/activity-stream/",
|
"prebuildmc": "rimraf $npm_package_config_mc_dir/browser/components/newtab/",
|
||||||
"buildmc:bundle": "npm run bundle",
|
"buildmc:bundle": "npm run bundle",
|
||||||
"buildmc:copy": "rsync --exclude-from .mcignore -a . $npm_package_config_mc_dir/browser/extensions/activity-stream/",
|
"buildmc:copy": "rsync --exclude-from .mcignore -a . $npm_package_config_mc_dir/browser/components/newtab/",
|
||||||
"buildmc:version": "node ./bin/update-version.js $npm_package_config_mc_dir/browser/extensions/activity-stream",
|
|
||||||
"buildmc:stringsExport": "cp locales/en-US/strings.properties $npm_package_config_mc_dir/browser/locales/en-US/chrome/browser/activity-stream/newtab.properties",
|
"buildmc:stringsExport": "cp locales/en-US/strings.properties $npm_package_config_mc_dir/browser/locales/en-US/chrome/browser/activity-stream/newtab.properties",
|
||||||
"buildmc:copyPingCentre": "cpx \"ping-centre/PingCentre.jsm\" $npm_package_config_mc_dir/browser/modules",
|
"buildmc:copyPingCentre": "cpx \"ping-centre/PingCentre.jsm\" $npm_package_config_mc_dir/browser/modules",
|
||||||
"startmc": "npm-run-all --parallel startmc:*",
|
"startmc": "npm-run-all --parallel startmc:*",
|
||||||
"prestartmc": "npm run buildmc",
|
"prestartmc": "npm run buildmc",
|
||||||
"startmc:copy": "cpx \"{{,.}*,!(node_modules)/**/{,.}*}\" $npm_package_config_mc_dir/browser/extensions/activity-stream/ -w",
|
"startmc:copy": "cpx \"{{,.}*,!(node_modules)/**/{,.}*}\" $npm_package_config_mc_dir/browser/components/newtab/ -w",
|
||||||
"startmc:copyPingCentre": "npm run buildmc:copyPingCentre -- -w",
|
"startmc:copyPingCentre": "npm run buildmc:copyPingCentre -- -w",
|
||||||
"startmc:webpack": "npm run bundle:webpack -- -w",
|
"startmc:webpack": "npm run bundle:webpack -- -w",
|
||||||
"startmc:css": "npm run bundle:css && npm run bundle:css -- -w",
|
"startmc:css": "npm run bundle:css && npm run bundle:css -- -w",
|
||||||
"importmc": "rsync --exclude-from .mcignore -a $npm_package_config_mc_dir/browser/extensions/activity-stream/ .",
|
"importmc": "rsync --exclude-from .mcignore -a $npm_package_config_mc_dir/browser/components/newtab/ .",
|
||||||
"testmc": "npm-run-all testmc:*",
|
"testmc": "npm-run-all testmc:*",
|
||||||
"testmc:lint": "npm run lint",
|
"testmc:lint": "npm run lint",
|
||||||
"testmc:build": "npm run bundle:webpack && npm run bundle:locales",
|
"testmc:build": "npm run bundle:webpack && npm run bundle:locales",
|
||||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -51,7 +51,7 @@ window.gActivityStreamStrings = {
|
||||||
"prefs_highlights_options_download_label": "Son Endirmələr",
|
"prefs_highlights_options_download_label": "Son Endirmələr",
|
||||||
"prefs_highlights_options_pocket_label": "Pocket-ə Saxlanılan Səhifələr",
|
"prefs_highlights_options_pocket_label": "Pocket-ə Saxlanılan Səhifələr",
|
||||||
"prefs_snippets_description": "Mozilla və Firefoxdan yeniliklər",
|
"prefs_snippets_description": "Mozilla və Firefoxdan yeniliklər",
|
||||||
"settings_pane_button_label": "Yeni Vərəq səhifənizi özəlləşdirin",
|
"settings_pane_button_label": "Yeni Vərəq səhifənizi fərdiləşdirin",
|
||||||
"settings_pane_topsites_header": "Qabaqcıl Saytlar",
|
"settings_pane_topsites_header": "Qabaqcıl Saytlar",
|
||||||
"settings_pane_highlights_header": "Seçilmişlər",
|
"settings_pane_highlights_header": "Seçilmişlər",
|
||||||
"settings_pane_highlights_options_bookmarks": "Əlfəcinlər",
|
"settings_pane_highlights_options_bookmarks": "Əlfəcinlər",
|
||||||
|
|
|
@ -96,7 +96,7 @@ window.gActivityStreamStrings = {
|
||||||
"firstrun_form_header": "Indtast din mailadresse",
|
"firstrun_form_header": "Indtast din mailadresse",
|
||||||
"firstrun_form_sub_header": "for at fortsætte til Firefox Sync.",
|
"firstrun_form_sub_header": "for at fortsætte til Firefox Sync.",
|
||||||
"firstrun_email_input_placeholder": "Mailadresse",
|
"firstrun_email_input_placeholder": "Mailadresse",
|
||||||
"firstrun_invalid_input": "Valid email required",
|
"firstrun_invalid_input": "En gyldig mailadresse er påkrævet",
|
||||||
"firstrun_extra_legal_links": "Ved at fortsætte godkender du vores {terms} og {privacy}.",
|
"firstrun_extra_legal_links": "Ved at fortsætte godkender du vores {terms} og {privacy}.",
|
||||||
"firstrun_terms_of_service": "tjenestevilkår",
|
"firstrun_terms_of_service": "tjenestevilkår",
|
||||||
"firstrun_privacy_notice": "privatlivspolitik",
|
"firstrun_privacy_notice": "privatlivspolitik",
|
||||||
|
|
|
@ -96,7 +96,7 @@ window.gActivityStreamStrings = {
|
||||||
"firstrun_form_header": "Emoinge ne ñandutiveve",
|
"firstrun_form_header": "Emoinge ne ñandutiveve",
|
||||||
"firstrun_form_sub_header": "eike hag̃ua Firefox Sync-pe.",
|
"firstrun_form_sub_header": "eike hag̃ua Firefox Sync-pe.",
|
||||||
"firstrun_email_input_placeholder": "Ñandutiveve",
|
"firstrun_email_input_placeholder": "Ñandutiveve",
|
||||||
"firstrun_invalid_input": "Valid email required",
|
"firstrun_invalid_input": "Eikotevẽ peteĩ ñanduti veve oikóva",
|
||||||
"firstrun_extra_legal_links": "Ejapóva, emoneĩ ko'ã {terms} ha {privacy}.",
|
"firstrun_extra_legal_links": "Ejapóva, emoneĩ ko'ã {terms} ha {privacy}.",
|
||||||
"firstrun_terms_of_service": "Mba'epytyvõrã ñemboguata",
|
"firstrun_terms_of_service": "Mba'epytyvõrã ñemboguata",
|
||||||
"firstrun_privacy_notice": "Ñemigua purureko",
|
"firstrun_privacy_notice": "Ñemigua purureko",
|
||||||
|
|
|
@ -96,7 +96,7 @@ window.gActivityStreamStrings = {
|
||||||
"firstrun_form_header": "メールアドレスを入力してください",
|
"firstrun_form_header": "メールアドレスを入力してください",
|
||||||
"firstrun_form_sub_header": "Firefox Sync の利用を続けるために必要です",
|
"firstrun_form_sub_header": "Firefox Sync の利用を続けるために必要です",
|
||||||
"firstrun_email_input_placeholder": "メールアドレス",
|
"firstrun_email_input_placeholder": "メールアドレス",
|
||||||
"firstrun_invalid_input": "Valid email required",
|
"firstrun_invalid_input": "メールアドレスを正しく入力してください",
|
||||||
"firstrun_extra_legal_links": "続行すると、{terms} と {privacy} に同意したものとみなします。",
|
"firstrun_extra_legal_links": "続行すると、{terms} と {privacy} に同意したものとみなします。",
|
||||||
"firstrun_terms_of_service": "サービス利用規約",
|
"firstrun_terms_of_service": "サービス利用規約",
|
||||||
"firstrun_privacy_notice": "プライバシーに関する通知",
|
"firstrun_privacy_notice": "プライバシーに関する通知",
|
||||||
|
|
|
@ -96,7 +96,7 @@ window.gActivityStreamStrings = {
|
||||||
"firstrun_form_header": "メールアドレスを入力してください",
|
"firstrun_form_header": "メールアドレスを入力してください",
|
||||||
"firstrun_form_sub_header": "Firefox Sync の利用を続けるために必要です",
|
"firstrun_form_sub_header": "Firefox Sync の利用を続けるために必要です",
|
||||||
"firstrun_email_input_placeholder": "メールアドレス",
|
"firstrun_email_input_placeholder": "メールアドレス",
|
||||||
"firstrun_invalid_input": "Valid email required",
|
"firstrun_invalid_input": "メールアドレスを正しく入力してください",
|
||||||
"firstrun_extra_legal_links": "続行すると、{terms} と {privacy} に同意したものとみなします。",
|
"firstrun_extra_legal_links": "続行すると、{terms} と {privacy} に同意したものとみなします。",
|
||||||
"firstrun_terms_of_service": "サービス利用規約",
|
"firstrun_terms_of_service": "サービス利用規約",
|
||||||
"firstrun_privacy_notice": "プライバシーに関する通知",
|
"firstrun_privacy_notice": "プライバシーに関する通知",
|
||||||
|
|
|
@ -96,7 +96,7 @@ window.gActivityStreamStrings = {
|
||||||
"firstrun_form_header": "உங்களின் மின்னஞ்சலை உள்ளிடுக",
|
"firstrun_form_header": "உங்களின் மின்னஞ்சலை உள்ளிடுக",
|
||||||
"firstrun_form_sub_header": "பயர்பாக்சு ஒத்திசையைத் தொடர.",
|
"firstrun_form_sub_header": "பயர்பாக்சு ஒத்திசையைத் தொடர.",
|
||||||
"firstrun_email_input_placeholder": "மின்னஞ்சல்",
|
"firstrun_email_input_placeholder": "மின்னஞ்சல்",
|
||||||
"firstrun_invalid_input": "Valid email required",
|
"firstrun_invalid_input": "நம்பகரமான மின்னஞ்சல் தேவை",
|
||||||
"firstrun_extra_legal_links": "தொடர்வதன் மூலம், தாங்கள் {terms} மற்றும் {privacy} ஒப்புக்கொள்கின்றீர்கள்.",
|
"firstrun_extra_legal_links": "தொடர்வதன் மூலம், தாங்கள் {terms} மற்றும் {privacy} ஒப்புக்கொள்கின்றீர்கள்.",
|
||||||
"firstrun_terms_of_service": "சேவையின் விதிமுறைகள்",
|
"firstrun_terms_of_service": "சேவையின் விதிமுறைகள்",
|
||||||
"firstrun_privacy_notice": "தனியுரிமை அறிவிப்பு",
|
"firstrun_privacy_notice": "தனியுரிமை அறிவிப்பு",
|
||||||
|
|
|
@ -20,7 +20,7 @@ window.gActivityStreamStrings = {
|
||||||
"menu_action_pin": "釘選",
|
"menu_action_pin": "釘選",
|
||||||
"menu_action_unpin": "取消釘選",
|
"menu_action_unpin": "取消釘選",
|
||||||
"confirm_history_delete_p1": "您確定要刪除此頁面的所有瀏覽紀錄?",
|
"confirm_history_delete_p1": "您確定要刪除此頁面的所有瀏覽紀錄?",
|
||||||
"confirm_history_delete_notice_p2": "無法還原此操作。",
|
"confirm_history_delete_notice_p2": "此動作無法復原。",
|
||||||
"menu_action_save_to_pocket": "儲存至 Pocket",
|
"menu_action_save_to_pocket": "儲存至 Pocket",
|
||||||
"menu_action_delete_pocket": "從 Pocket 刪除",
|
"menu_action_delete_pocket": "從 Pocket 刪除",
|
||||||
"menu_action_archive_pocket": "在 Pocket 裡封存",
|
"menu_action_archive_pocket": "在 Pocket 裡封存",
|
||||||
|
@ -77,7 +77,7 @@ window.gActivityStreamStrings = {
|
||||||
"highlights_empty_state": "開始上網,我們就會把您在網路上發現的好文章、影片、剛加入書籤的頁面顯示於此。",
|
"highlights_empty_state": "開始上網,我們就會把您在網路上發現的好文章、影片、剛加入書籤的頁面顯示於此。",
|
||||||
"topstories_empty_state": "所有文章都讀完啦!晚點再來,{provider} 將提供更多推薦故事。等不及了?選擇熱門主題,看看 Web 上各式精采資訊。",
|
"topstories_empty_state": "所有文章都讀完啦!晚點再來,{provider} 將提供更多推薦故事。等不及了?選擇熱門主題,看看 Web 上各式精采資訊。",
|
||||||
"manual_migration_explanation2": "試試將其他瀏覽器的書籤、瀏覽記錄與密碼匯入 Firefox。",
|
"manual_migration_explanation2": "試試將其他瀏覽器的書籤、瀏覽記錄與密碼匯入 Firefox。",
|
||||||
"manual_migration_cancel_button": "不必了",
|
"manual_migration_cancel_button": "不要,謝謝",
|
||||||
"manual_migration_import_button": "立即匯入",
|
"manual_migration_import_button": "立即匯入",
|
||||||
"error_fallback_default_info": "唉唷,載入內容時發生錯誤。",
|
"error_fallback_default_info": "唉唷,載入內容時發生錯誤。",
|
||||||
"error_fallback_default_refresh_suggestion": "請重新整理頁面再試一次。",
|
"error_fallback_default_refresh_suggestion": "請重新整理頁面再試一次。",
|
||||||
|
@ -94,7 +94,7 @@ window.gActivityStreamStrings = {
|
||||||
"firstrun_content": "在您的任何裝置上取得書籤、瀏覽紀錄、密碼及其他設定。",
|
"firstrun_content": "在您的任何裝置上取得書籤、瀏覽紀錄、密碼及其他設定。",
|
||||||
"firstrun_learn_more_link": "了解 Firefox Accounts 的更多資訊",
|
"firstrun_learn_more_link": "了解 Firefox Accounts 的更多資訊",
|
||||||
"firstrun_form_header": "輸入您的電子郵件地址",
|
"firstrun_form_header": "輸入您的電子郵件地址",
|
||||||
"firstrun_form_sub_header": "繼續前往 Firefox Sync。",
|
"firstrun_form_sub_header": "繼續前往 Firefox Sync",
|
||||||
"firstrun_email_input_placeholder": "電子郵件",
|
"firstrun_email_input_placeholder": "電子郵件",
|
||||||
"firstrun_invalid_input": "必須輸入有效的電子郵件地址",
|
"firstrun_invalid_input": "必須輸入有效的電子郵件地址",
|
||||||
"firstrun_extra_legal_links": "若繼續,代表您同意{terms}及{privacy}。",
|
"firstrun_extra_legal_links": "若繼續,代表您同意{terms}及{privacy}。",
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
ChromeUtils.defineModuleGetter(this, "ASRouterTargeting",
|
const {ASRouterTargeting, TopFrecentSitesCache} =
|
||||||
"resource://activity-stream/lib/ASRouterTargeting.jsm");
|
ChromeUtils.import("resource://activity-stream/lib/ASRouterTargeting.jsm", {});
|
||||||
|
const {AddonTestUtils} =
|
||||||
|
ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", {});
|
||||||
ChromeUtils.defineModuleGetter(this, "ProfileAge",
|
ChromeUtils.defineModuleGetter(this, "ProfileAge",
|
||||||
"resource://gre/modules/ProfileAge.jsm");
|
"resource://gre/modules/ProfileAge.jsm");
|
||||||
ChromeUtils.defineModuleGetter(this, "AddonManager",
|
ChromeUtils.defineModuleGetter(this, "AddonManager",
|
||||||
|
@ -11,8 +13,6 @@ ChromeUtils.defineModuleGetter(this, "NewTabUtils",
|
||||||
ChromeUtils.defineModuleGetter(this, "PlacesTestUtils",
|
ChromeUtils.defineModuleGetter(this, "PlacesTestUtils",
|
||||||
"resource://testing-common/PlacesTestUtils.jsm");
|
"resource://testing-common/PlacesTestUtils.jsm");
|
||||||
|
|
||||||
const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", {});
|
|
||||||
|
|
||||||
// ASRouterTargeting.isMatch
|
// ASRouterTargeting.isMatch
|
||||||
add_task(async function should_do_correct_targeting() {
|
add_task(async function should_do_correct_targeting() {
|
||||||
is(await ASRouterTargeting.isMatch("FOO", {}, {FOO: true}), true, "should return true for a matching value");
|
is(await ASRouterTargeting.isMatch("FOO", {}, {FOO: true}), true, "should return true for a matching value");
|
||||||
|
@ -226,4 +226,8 @@ add_task(async function checkFrecentSites() {
|
||||||
message = {id: "foo", targeting: `(topFrecentSites[.frecency >= 900 && .lastVisitDate >= ${timeDaysAgo(1) - 1}]|mapToProperty('host') intersect ['mozilla3.com', 'mozilla2.com', 'mozilla1.com'])|length > 0`};
|
message = {id: "foo", targeting: `(topFrecentSites[.frecency >= 900 && .lastVisitDate >= ${timeDaysAgo(1) - 1}]|mapToProperty('host') intersect ['mozilla3.com', 'mozilla2.com', 'mozilla1.com'])|length > 0`};
|
||||||
is(await ASRouterTargeting.findMatchingMessage({messages: [message], target: {}}), message,
|
is(await ASRouterTargeting.findMatchingMessage({messages: [message], target: {}}), message,
|
||||||
"should select correct item when filtering by frecency and lastVisitDate with multiple candidate domains");
|
"should select correct item when filtering by frecency and lastVisitDate with multiple candidate domains");
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
await clearHistoryAndBookmarks();
|
||||||
|
TopFrecentSitesCache.expire();
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,10 +7,17 @@ XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
|
||||||
"@mozilla.org/browser/aboutnewtab-service;1",
|
"@mozilla.org/browser/aboutnewtab-service;1",
|
||||||
"nsIAboutNewTabService");
|
"nsIAboutNewTabService");
|
||||||
|
|
||||||
registerCleanupFunction(function() {
|
registerCleanupFunction(() => {
|
||||||
aboutNewTabService.resetNewTabURL();
|
aboutNewTabService.resetNewTabURL();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function nextChangeNotificationPromise(aNewURL, testMessage) {
|
||||||
|
return TestUtils.topicObserved("newtab-url-changed", function observer(aSubject, aData) { // jshint unused:false
|
||||||
|
Assert.equal(aData, aNewURL, testMessage);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tests that the default newtab page is always returned when one types "about:newtab" in the URL bar,
|
* Tests that the default newtab page is always returned when one types "about:newtab" in the URL bar,
|
||||||
* even when overridden.
|
* even when overridden.
|
||||||
|
@ -18,7 +25,7 @@ registerCleanupFunction(function() {
|
||||||
add_task(async function redirector_ignores_override() {
|
add_task(async function redirector_ignores_override() {
|
||||||
let overrides = [
|
let overrides = [
|
||||||
"chrome://browser/content/aboutRobots.xhtml",
|
"chrome://browser/content/aboutRobots.xhtml",
|
||||||
"about:home",
|
"about:home"
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let overrideURL of overrides) {
|
for (let overrideURL of overrides) {
|
||||||
|
@ -30,7 +37,7 @@ add_task(async function redirector_ignores_override() {
|
||||||
|
|
||||||
let tabOptions = {
|
let tabOptions = {
|
||||||
gBrowser,
|
gBrowser,
|
||||||
url: "about:newtab",
|
url: "about:newtab"
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -40,8 +47,8 @@ add_task(async function redirector_ignores_override() {
|
||||||
* due to invoking AboutRedirector. A user interacting with the chrome otherwise would lead
|
* due to invoking AboutRedirector. A user interacting with the chrome otherwise would lead
|
||||||
* to the overriding URLs.
|
* to the overriding URLs.
|
||||||
*/
|
*/
|
||||||
await BrowserTestUtils.withNewTab(tabOptions, async function(browser) {
|
await BrowserTestUtils.withNewTab(tabOptions, async browser => {
|
||||||
await ContentTask.spawn(browser, {}, async function() {
|
await ContentTask.spawn(browser, {}, async () => {
|
||||||
Assert.equal(content.location.href, "about:newtab", "Got right URL");
|
Assert.equal(content.location.href, "about:newtab", "Got right URL");
|
||||||
Assert.equal(content.document.location.href, "about:newtab", "Got right URL");
|
Assert.equal(content.document.location.href, "about:newtab", "Got right URL");
|
||||||
Assert.notEqual(content.document.nodePrincipal,
|
Assert.notEqual(content.document.nodePrincipal,
|
||||||
|
@ -59,7 +66,7 @@ add_task(async function override_loads_in_browser() {
|
||||||
let overrides = [
|
let overrides = [
|
||||||
"chrome://browser/content/aboutRobots.xhtml",
|
"chrome://browser/content/aboutRobots.xhtml",
|
||||||
"about:home",
|
"about:home",
|
||||||
" about:home",
|
" about:home"
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let overrideURL of overrides) {
|
for (let overrideURL of overrides) {
|
||||||
|
@ -75,7 +82,7 @@ add_task(async function override_loads_in_browser() {
|
||||||
let browser = gBrowser.selectedBrowser;
|
let browser = gBrowser.selectedBrowser;
|
||||||
await BrowserTestUtils.browserLoaded(browser);
|
await BrowserTestUtils.browserLoaded(browser);
|
||||||
|
|
||||||
await ContentTask.spawn(browser, {url: overrideURL}, async function(args) {
|
await ContentTask.spawn(browser, {url: overrideURL}, async args => {
|
||||||
Assert.equal(content.location.href, args.url.trim(), "Got right URL");
|
Assert.equal(content.location.href, args.url.trim(), "Got right URL");
|
||||||
Assert.equal(content.document.location.href, args.url.trim(), "Got right URL");
|
Assert.equal(content.document.location.href, args.url.trim(), "Got right URL");
|
||||||
}); // jshint ignore:line
|
}); // jshint ignore:line
|
||||||
|
@ -91,7 +98,7 @@ add_task(async function override_blank_loads_in_browser() {
|
||||||
"",
|
"",
|
||||||
" ",
|
" ",
|
||||||
"\n\t",
|
"\n\t",
|
||||||
" about:blank",
|
" about:blank"
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let overrideURL of overrides) {
|
for (let overrideURL of overrides) {
|
||||||
|
@ -107,17 +114,10 @@ add_task(async function override_blank_loads_in_browser() {
|
||||||
let browser = gBrowser.selectedBrowser;
|
let browser = gBrowser.selectedBrowser;
|
||||||
await BrowserTestUtils.browserLoaded(browser);
|
await BrowserTestUtils.browserLoaded(browser);
|
||||||
|
|
||||||
await ContentTask.spawn(browser, {}, async function() {
|
await ContentTask.spawn(browser, {}, async () => {
|
||||||
Assert.equal(content.location.href, "about:blank", "Got right URL");
|
Assert.equal(content.location.href, "about:blank", "Got right URL");
|
||||||
Assert.equal(content.document.location.href, "about:blank", "Got right URL");
|
Assert.equal(content.document.location.href, "about:blank", "Got right URL");
|
||||||
}); // jshint ignore:line
|
}); // jshint ignore:line
|
||||||
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function nextChangeNotificationPromise(aNewURL, testMessage) {
|
|
||||||
return TestUtils.topicObserved("newtab-url-changed", function observer(aSubject, aData) { // jshint unused:false
|
|
||||||
Assert.equal(aData, aNewURL, testMessage);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ add_task(async function test_all_packaged_locales() {
|
||||||
const locale = file.replace("/", "");
|
const locale = file.replace("/", "");
|
||||||
if (locale !== "static") {
|
if (locale !== "static") {
|
||||||
const url = await getUrlForLocale(locale);
|
const url = await getUrlForLocale(locale);
|
||||||
Assert[locale === "en-US" ? "equal" : "notEqual"](url, DEFAULT_URL, `can reference "${locale}" files`);
|
Assert.equal(url, DEFAULT_URL.replace("en-US", locale), `can reference "${locale}" files`);
|
||||||
|
|
||||||
// Specially remember if we saw an ID locale packaged as it can be
|
// Specially remember if we saw an ID locale packaged as it can be
|
||||||
// easily ignored by source control, e.g., .gitignore
|
// easily ignored by source control, e.g., .gitignore
|
||||||
|
|
|
@ -128,6 +128,22 @@ describe("ASRouter", () => {
|
||||||
assert.lengthOf(Router.state.providers, length);
|
assert.lengthOf(Router.state.providers, length);
|
||||||
assert.isDefined(provider);
|
assert.isDefined(provider);
|
||||||
});
|
});
|
||||||
|
it("should load additional whitelisted hosts", async () => {
|
||||||
|
getStringPrefStub.returns("[\"whitelist.com\"]");
|
||||||
|
await createRouterAndInit();
|
||||||
|
|
||||||
|
assert.propertyVal(Router.WHITELIST_HOSTS, "whitelist.com", "preview");
|
||||||
|
// Should still include the defaults
|
||||||
|
assert.lengthOf(Object.keys(Router.WHITELIST_HOSTS), 3);
|
||||||
|
});
|
||||||
|
it("should fallback to defaults if pref parsing fails", async () => {
|
||||||
|
getStringPrefStub.returns("err");
|
||||||
|
await createRouterAndInit();
|
||||||
|
|
||||||
|
assert.lengthOf(Object.keys(Router.WHITELIST_HOSTS), 2);
|
||||||
|
assert.propertyVal(Router.WHITELIST_HOSTS, "snippets-admin.mozilla.org", "preview");
|
||||||
|
assert.propertyVal(Router.WHITELIST_HOSTS, "activity-stream-icons.services.mozilla.com", "production");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#loadMessagesFromAllProviders", () => {
|
describe("#loadMessagesFromAllProviders", () => {
|
||||||
|
@ -143,8 +159,10 @@ describe("ASRouter", () => {
|
||||||
getStringPrefStub.returns("example.com");
|
getStringPrefStub.returns("example.com");
|
||||||
await createRouterAndInit();
|
await createRouterAndInit();
|
||||||
|
|
||||||
assert.calledOnce(getStringPrefStub);
|
// Get snippets endpoint url, get the whitelisted hosts for endpoints
|
||||||
|
assert.calledTwice(getStringPrefStub);
|
||||||
assert.calledWithExactly(getStringPrefStub, "remotePref", "");
|
assert.calledWithExactly(getStringPrefStub, "remotePref", "");
|
||||||
|
assert.calledWithExactly(getStringPrefStub, "browser.newtab.activity-stream.asrouter.whitelistHosts", "");
|
||||||
assert.isDefined(Router.state.providers.find(p => p.url === "example.com"));
|
assert.isDefined(Router.state.providers.find(p => p.url === "example.com"));
|
||||||
});
|
});
|
||||||
it("should not trigger an update if not enough time has passed for a provider", async () => {
|
it("should not trigger an update if not enough time has passed for a provider", async () => {
|
||||||
|
@ -248,6 +266,18 @@ describe("ASRouter", () => {
|
||||||
assert.equal(msg.target.sendAsyncMessage.firstCall.args[1].type, "SET_BUNDLED_MESSAGES");
|
assert.equal(msg.target.sendAsyncMessage.firstCall.args[1].type, "SET_BUNDLED_MESSAGES");
|
||||||
assert.equal(msg.target.sendAsyncMessage.firstCall.args[1].data.bundle[0].content, currentMessage.content);
|
assert.equal(msg.target.sendAsyncMessage.firstCall.args[1].data.bundle[0].content, currentMessage.content);
|
||||||
});
|
});
|
||||||
|
it("should properly order the message's bundle if specified", async () => {
|
||||||
|
// force the only messages to be a bundled messages so getRandomItemFromArray picks one of them
|
||||||
|
const firstMessage = {id: "foo2", template: "simple_template", bundled: 2, order: 1, content: {title: "Foo2", body: "Foo123-2"}};
|
||||||
|
const secondMessage = {id: "foo1", template: "simple_template", bundled: 2, order: 2, content: {title: "Foo1", body: "Foo123-1"}};
|
||||||
|
await Router.setState({messages: [secondMessage, firstMessage]});
|
||||||
|
const msg = fakeAsyncMessage({type: "CONNECT_UI_REQUEST"});
|
||||||
|
await Router.onMessage(msg);
|
||||||
|
assert.calledWith(msg.target.sendAsyncMessage, PARENT_TO_CHILD_MESSAGE_NAME);
|
||||||
|
assert.equal(msg.target.sendAsyncMessage.firstCall.args[1].type, "SET_BUNDLED_MESSAGES");
|
||||||
|
assert.equal(msg.target.sendAsyncMessage.firstCall.args[1].data.bundle[0].content, firstMessage.content);
|
||||||
|
assert.equal(msg.target.sendAsyncMessage.firstCall.args[1].data.bundle[1].content, secondMessage.content);
|
||||||
|
});
|
||||||
it("should return a null bundle if we do not have enough messages to fill the bundle", async () => {
|
it("should return a null bundle if we do not have enough messages to fill the bundle", async () => {
|
||||||
// force the only message to be a bundled message that needs 2 messages in the bundle
|
// force the only message to be a bundled message that needs 2 messages in the bundle
|
||||||
await Router.setState({messages: [{id: "foo1", template: "simple_template", bundled: 2, content: {title: "Foo1", body: "Foo123-1"}}]});
|
await Router.setState({messages: [{id: "foo1", template: "simple_template", bundled: 2, content: {title: "Foo1", body: "Foo123-1"}}]});
|
||||||
|
@ -419,7 +449,7 @@ describe("ASRouter", () => {
|
||||||
const expectedObj = {
|
const expectedObj = {
|
||||||
template: testMessage1.template,
|
template: testMessage1.template,
|
||||||
provider: testMessage1.provider,
|
provider: testMessage1.provider,
|
||||||
bundle: [{content: testMessage1.content, id: testMessage1.id}, {content: testMessage2.content, id: testMessage2.id}]
|
bundle: [{content: testMessage1.content, id: testMessage1.id, order: 1}, {content: testMessage2.content, id: testMessage2.id}]
|
||||||
};
|
};
|
||||||
assert.calledWith(msg.target.sendAsyncMessage, PARENT_TO_CHILD_MESSAGE_NAME, {type: "SET_BUNDLED_MESSAGES", data: expectedObj});
|
assert.calledWith(msg.target.sendAsyncMessage, PARENT_TO_CHILD_MESSAGE_NAME, {type: "SET_BUNDLED_MESSAGES", data: expectedObj});
|
||||||
});
|
});
|
||||||
|
@ -434,7 +464,7 @@ describe("ASRouter", () => {
|
||||||
const expectedObj = {
|
const expectedObj = {
|
||||||
template: testMessage1.template,
|
template: testMessage1.template,
|
||||||
provider: testMessage1.provider,
|
provider: testMessage1.provider,
|
||||||
bundle: [{content: testMessage1.content, id: testMessage1.id}, {content: testMessage2.content, id: testMessage2.id}]
|
bundle: [{content: testMessage1.content, id: testMessage1.id, order: 1}, {content: testMessage2.content, id: testMessage2.id, order: 2}]
|
||||||
};
|
};
|
||||||
assert.calledWith(msg.target.sendAsyncMessage, PARENT_TO_CHILD_MESSAGE_NAME, {type: "SET_BUNDLED_MESSAGES", data: expectedObj});
|
assert.calledWith(msg.target.sendAsyncMessage, PARENT_TO_CHILD_MESSAGE_NAME, {type: "SET_BUNDLED_MESSAGES", data: expectedObj});
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,8 +4,8 @@ export const EXPERIMENT_PREF = "asrouterExperimentEnabled";
|
||||||
|
|
||||||
export const FAKE_LOCAL_MESSAGES = [
|
export const FAKE_LOCAL_MESSAGES = [
|
||||||
{id: "foo", template: "simple_template", content: {title: "Foo", body: "Foo123"}},
|
{id: "foo", template: "simple_template", content: {title: "Foo", body: "Foo123"}},
|
||||||
{id: "foo1", template: "simple_template", bundled: 2, content: {title: "Foo1", body: "Foo123-1"}},
|
{id: "foo1", template: "simple_template", bundled: 2, order: 1, content: {title: "Foo1", body: "Foo123-1"}},
|
||||||
{id: "foo2", template: "simple_template", bundled: 2, content: {title: "Foo2", body: "Foo123-2"}},
|
{id: "foo2", template: "simple_template", bundled: 2, order: 2, content: {title: "Foo2", body: "Foo123-2"}},
|
||||||
{id: "bar", template: "fancy_template", content: {title: "Foo", body: "Foo123"}},
|
{id: "bar", template: "fancy_template", content: {title: "Foo", body: "Foo123"}},
|
||||||
{id: "baz", content: {title: "Foo", body: "Foo123"}}
|
{id: "baz", content: {title: "Foo", body: "Foo123"}}
|
||||||
];
|
];
|
||||||
|
|
|
@ -31,6 +31,38 @@ function cleanup() {
|
||||||
|
|
||||||
registerCleanupFunction(cleanup);
|
registerCleanupFunction(cleanup);
|
||||||
|
|
||||||
|
function nextChangeNotificationPromise(aNewURL, testMessage) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
Services.obs.addObserver(function observer(aSubject, aTopic, aData) { // jshint unused:false
|
||||||
|
Services.obs.removeObserver(observer, aTopic);
|
||||||
|
Assert.equal(aData, aNewURL, testMessage);
|
||||||
|
resolve();
|
||||||
|
}, "newtab-url-changed");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setBoolPrefAndWaitForChange(pref, value, testMessage) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
Services.obs.addObserver(function observer(aSubject, aTopic, aData) { // jshint unused:false
|
||||||
|
Services.obs.removeObserver(observer, aTopic);
|
||||||
|
Assert.equal(aData, aboutNewTabService.newTabURL, testMessage);
|
||||||
|
resolve();
|
||||||
|
}, "newtab-url-changed");
|
||||||
|
|
||||||
|
Services.prefs.setBoolPref(pref, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupASPrerendered() {
|
||||||
|
if (Services.prefs.getBoolPref(ACTIVITY_STREAM_PRERENDER_PREF)) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
let notificationPromise = nextChangeNotificationPromise("about:newtab");
|
||||||
|
Services.prefs.setBoolPref(ACTIVITY_STREAM_PRERENDER_PREF, true);
|
||||||
|
return notificationPromise;
|
||||||
|
}
|
||||||
|
|
||||||
add_task(async function test_as_and_prerender_initialized() {
|
add_task(async function test_as_and_prerender_initialized() {
|
||||||
Assert.ok(aboutNewTabService.activityStreamEnabled,
|
Assert.ok(aboutNewTabService.activityStreamEnabled,
|
||||||
".activityStreamEnabled should be set to the correct initial value");
|
".activityStreamEnabled should be set to the correct initial value");
|
||||||
|
@ -165,10 +197,8 @@ add_task(function test_locale() {
|
||||||
* Tests reponse to updates to prefs
|
* Tests reponse to updates to prefs
|
||||||
*/
|
*/
|
||||||
add_task(async function test_updates() {
|
add_task(async function test_updates() {
|
||||||
/*
|
// Simulates a "cold-boot" situation, with some pref already set before testing a series
|
||||||
* Simulates a "cold-boot" situation, with some pref already set before testing a series
|
// of changes.
|
||||||
* of changes.
|
|
||||||
*/
|
|
||||||
await setupASPrerendered();
|
await setupASPrerendered();
|
||||||
|
|
||||||
aboutNewTabService.resetNewTabURL(); // need to set manually because pref notifs are off
|
aboutNewTabService.resetNewTabURL(); // need to set manually because pref notifs are off
|
||||||
|
@ -197,36 +227,3 @@ add_task(async function test_updates() {
|
||||||
|
|
||||||
cleanup();
|
cleanup();
|
||||||
});
|
});
|
||||||
|
|
||||||
function nextChangeNotificationPromise(aNewURL, testMessage) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
Services.obs.addObserver(function observer(aSubject, aTopic, aData) { // jshint unused:false
|
|
||||||
Services.obs.removeObserver(observer, aTopic);
|
|
||||||
Assert.equal(aData, aNewURL, testMessage);
|
|
||||||
resolve();
|
|
||||||
}, "newtab-url-changed");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setBoolPrefAndWaitForChange(pref, value, testMessage) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
Services.obs.addObserver(function observer(aSubject, aTopic, aData) { // jshint unused:false
|
|
||||||
Services.obs.removeObserver(observer, aTopic);
|
|
||||||
Assert.equal(aData, aboutNewTabService.newTabURL, testMessage);
|
|
||||||
resolve();
|
|
||||||
}, "newtab-url-changed");
|
|
||||||
|
|
||||||
Services.prefs.setBoolPref(pref, value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function setupASPrerendered() {
|
|
||||||
if (Services.prefs.getBoolPref(ACTIVITY_STREAM_PRERENDER_PREF)) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
let notificationPromise = nextChangeNotificationPromise("about:newtab");
|
|
||||||
Services.prefs.setBoolPref(ACTIVITY_STREAM_PRERENDER_PREF, true);
|
|
||||||
return notificationPromise;
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
|
|
||||||
scripts:
|
scripts:
|
||||||
# Run the activity-stream mochitests
|
# Run the activity-stream mochitests
|
||||||
mochitest: (cd $npm_package_config_mc_dir && ./mach mochitest browser/extensions/activity-stream/test/functional/mochitest --headless)
|
mochitest: (cd $npm_package_config_mc_dir && ./mach mochitest browser/components/newtab/test/browser --headless)
|
||||||
|
|
||||||
# Run the activity-stream mochitests with the browser toolbox debugger.
|
# Run the activity-stream mochitests with the browser toolbox debugger.
|
||||||
# Often handy in combination with adding a "debugger" statement in your
|
# Often handy in combination with adding a "debugger" statement in your
|
||||||
# mochitest somewhere.
|
# mochitest somewhere.
|
||||||
mochitest-debug: (cd $npm_package_config_mc_dir && ./mach mochitest --jsdebugger browser/extensions/activity-stream/test/functional/mochitest)
|
mochitest-debug: (cd $npm_package_config_mc_dir && ./mach mochitest --jsdebugger browser/components/newtab/test/browser)
|
||||||
|
|
||||||
# bundle: Build all assets for activity stream
|
# bundle: Build all assets for activity stream
|
||||||
bundle:
|
bundle:
|
||||||
|
@ -21,10 +21,9 @@ scripts:
|
||||||
|
|
||||||
# buildmc: Export the bootstraped add-on to mozilla central
|
# buildmc: Export the bootstraped add-on to mozilla central
|
||||||
buildmc:
|
buildmc:
|
||||||
pre: rimraf $npm_package_config_mc_dir/browser/extensions/activity-stream/
|
pre: rimraf $npm_package_config_mc_dir/browser/components/newtab/
|
||||||
bundle: => bundle
|
bundle: => bundle
|
||||||
copy: rsync --exclude-from .mcignore -a . $npm_package_config_mc_dir/browser/extensions/activity-stream/
|
copy: rsync --exclude-from .mcignore -a . $npm_package_config_mc_dir/browser/components/newtab/
|
||||||
version: node ./bin/update-version.js $npm_package_config_mc_dir/browser/extensions/activity-stream
|
|
||||||
stringsExport: cp locales/en-US/strings.properties $npm_package_config_mc_dir/browser/locales/en-US/chrome/browser/activity-stream/newtab.properties
|
stringsExport: cp locales/en-US/strings.properties $npm_package_config_mc_dir/browser/locales/en-US/chrome/browser/activity-stream/newtab.properties
|
||||||
copyPingCentre: cpx "ping-centre/PingCentre.jsm" $npm_package_config_mc_dir/browser/modules
|
copyPingCentre: cpx "ping-centre/PingCentre.jsm" $npm_package_config_mc_dir/browser/modules
|
||||||
|
|
||||||
|
@ -33,13 +32,13 @@ scripts:
|
||||||
_parallel: true
|
_parallel: true
|
||||||
pre: =>buildmc
|
pre: =>buildmc
|
||||||
# This copies only the system addon sub-folder; changing anything outside of it will need a full rebuild.
|
# This copies only the system addon sub-folder; changing anything outside of it will need a full rebuild.
|
||||||
copy: cpx "{{,.}*,!(node_modules)/**/{,.}*}" $npm_package_config_mc_dir/browser/extensions/activity-stream/ -w
|
copy: cpx "{{,.}*,!(node_modules)/**/{,.}*}" $npm_package_config_mc_dir/browser/components/newtab/ -w
|
||||||
copyPingCentre: =>buildmc:copyPingCentre -- -w
|
copyPingCentre: =>buildmc:copyPingCentre -- -w
|
||||||
webpack: =>bundle:webpack -- -w
|
webpack: =>bundle:webpack -- -w
|
||||||
css: =>bundle:css && =>bundle:css -- -w
|
css: =>bundle:css && =>bundle:css -- -w
|
||||||
|
|
||||||
# importmc: Import changes from mc to github repo
|
# importmc: Import changes from mc to github repo
|
||||||
importmc: rsync --exclude-from .mcignore -a $npm_package_config_mc_dir/browser/extensions/activity-stream/ .
|
importmc: rsync --exclude-from .mcignore -a $npm_package_config_mc_dir/browser/components/newtab/ .
|
||||||
|
|
||||||
testmc:
|
testmc:
|
||||||
lint: =>lint
|
lint: =>lint
|
||||||
|
|
Загрузка…
Ссылка в новой задаче