зеркало из 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:
|
||||
// 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 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_DEBUG = "browser.newtabpage.activity-stream.debug";
|
||||
|
||||
|
||||
function AboutNewTabService() {
|
||||
Services.obs.addObserver(this, TOPIC_APP_QUIT);
|
||||
Services.obs.addObserver(this, TOPIC_LOCALES_CHANGE);
|
||||
|
@ -103,9 +102,7 @@ AboutNewTabService.prototype = {
|
|||
Ci.nsIAboutNewTabService,
|
||||
Ci.nsIObserver
|
||||
]),
|
||||
_xpcom_categories: [{
|
||||
service: true
|
||||
}],
|
||||
_xpcom_categories: [{service: true}],
|
||||
|
||||
observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
|
@ -121,7 +118,7 @@ AboutNewTabService.prototype = {
|
|||
this.notifyChange();
|
||||
}
|
||||
break;
|
||||
case TOPIC_CONTENT_DOCUMENT_INTERACTIVE:
|
||||
case TOPIC_CONTENT_DOCUMENT_INTERACTIVE: {
|
||||
const win = subject.defaultView;
|
||||
|
||||
// It seems like "content-document-interactive" is triggered multiple
|
||||
|
@ -178,6 +175,7 @@ AboutNewTabService.prototype = {
|
|||
};
|
||||
subject.addEventListener("unload", onUnloaded, {once: true});
|
||||
break;
|
||||
}
|
||||
case TOPIC_APP_QUIT:
|
||||
this.uninit();
|
||||
if (IS_MAIN_PROCESS) {
|
||||
|
@ -277,17 +275,17 @@ AboutNewTabService.prototype = {
|
|||
},
|
||||
|
||||
set newTabURL(aNewTabURL) {
|
||||
aNewTabURL = aNewTabURL.trim();
|
||||
if (aNewTabURL === ABOUT_URL) {
|
||||
let newTabURL = aNewTabURL.trim();
|
||||
if (newTabURL === ABOUT_URL) {
|
||||
// avoid infinite redirects in case one sets the URL to about:newtab
|
||||
this.resetNewTabURL();
|
||||
return;
|
||||
} else if (aNewTabURL === "") {
|
||||
aNewTabURL = "about:blank";
|
||||
} else if (newTabURL === "") {
|
||||
newTabURL = "about:blank";
|
||||
}
|
||||
|
||||
this.toggleActivityStream(false);
|
||||
this._newTabURL = aNewTabURL;
|
||||
this._newTabURL = newTabURL;
|
||||
this._overridden = true;
|
||||
this.notifyChange();
|
||||
},
|
||||
|
@ -311,11 +309,14 @@ AboutNewTabService.prototype = {
|
|||
get activityStreamLocale() {
|
||||
// Pick the best available locale to match the app locales
|
||||
return Services.locale.negotiateLanguages(
|
||||
Services.locale.getAppLocalesAsLangTags(),
|
||||
ACTIVITY_STREAM_LOCALES,
|
||||
Services.locale.getAppLocalesAsBCP47(),
|
||||
ACTIVITY_STREAM_BCP47,
|
||||
// defaultLocale's strings aren't necessarily packaged, but en-US' are
|
||||
"en-US"
|
||||
)[0];
|
||||
"en-US",
|
||||
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() {
|
||||
|
|
|
@ -26,7 +26,7 @@ else
|
|||
FIREFOX_PATH=${AS_PINE_TEST_DIR}/mozilla-central
|
||||
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
|
||||
# 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");
|
||||
}
|
||||
|
||||
// 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
|
||||
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();
|
||||
|
|
|
@ -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_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)
|
||||
`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`
|
||||
`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.
|
||||
|
@ -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
|
||||
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",
|
||||
"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": {
|
||||
"type": "object",
|
||||
"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
|
||||
|
||||
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
|
||||
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";
|
||||
// List of hosts for endpoints that serve router messages.
|
||||
// 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",
|
||||
"snippets-admin.mozilla.org": "preview"
|
||||
};
|
||||
const SNIPPETS_ENDPOINT_WHITELIST = "browser.newtab.activity-stream.asrouter.whitelistHosts";
|
||||
|
||||
const MessageLoaderUtils = {
|
||||
/**
|
||||
|
@ -229,6 +230,7 @@ class _ASRouter {
|
|||
this.messageChannel.addMessageListener(INCOMING_MESSAGE_NAME, this.onMessage);
|
||||
this._addASRouterPrefListener();
|
||||
this._storage = storage;
|
||||
this.WHITELIST_HOSTS = this._loadSnippetsWhitelistHosts();
|
||||
|
||||
const blockList = await this._storage.get("blockList") || [];
|
||||
const impressions = await this._storage.get("impressions") || {};
|
||||
|
@ -283,8 +285,12 @@ class _ASRouter {
|
|||
return message;
|
||||
}
|
||||
|
||||
_orderBundle(bundle) {
|
||||
return bundle.sort((a, b) => a.order - b.order);
|
||||
}
|
||||
|
||||
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
|
||||
let bundledMessagesOfSameTemplate = this._getUnblockedMessages()
|
||||
|
@ -309,7 +315,7 @@ class _ASRouter {
|
|||
}
|
||||
// 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
|
||||
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);
|
||||
// Stop once we have enough messages to fill a bundle
|
||||
if (result.length === originalMessage.bundled) {
|
||||
|
@ -322,7 +328,8 @@ class _ASRouter {
|
|||
if (result.length < originalMessage.bundled) {
|
||||
return null;
|
||||
}
|
||||
return {bundle: result, provider: originalMessage.provider, template: originalMessage.template};
|
||||
|
||||
return {bundle: this._orderBundle(result), provider: originalMessage.provider, template: originalMessage.template};
|
||||
}
|
||||
|
||||
_getUnblockedMessages() {
|
||||
|
@ -468,18 +475,42 @@ class _ASRouter {
|
|||
_validPreviewEndpoint(url) {
|
||||
try {
|
||||
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.`);
|
||||
}
|
||||
if (endpoint.protocol !== "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) {
|
||||
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) {
|
||||
const providers = [...this.state.providers];
|
||||
if (this._validPreviewEndpoint(url) && !providers.find(p => p.url === url)) {
|
||||
|
|
|
@ -40,6 +40,11 @@ const TopFrecentSitesCache = {
|
|||
}
|
||||
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";
|
||||
|
||||
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");
|
||||
|
||||
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",
|
||||
template: "onboarding",
|
||||
bundled: 3,
|
||||
order: 2,
|
||||
content: {
|
||||
title: "Private Browsing",
|
||||
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",
|
||||
template: "onboarding",
|
||||
bundled: 3,
|
||||
order: 3,
|
||||
content: {
|
||||
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.",
|
||||
|
@ -35,6 +37,7 @@ const ONBOARDING_MESSAGES = [
|
|||
id: "ONBOARDING_3",
|
||||
template: "onboarding",
|
||||
bundled: 3,
|
||||
order: 1,
|
||||
content: {
|
||||
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.",
|
||||
|
@ -50,11 +53,12 @@ const ONBOARDING_MESSAGES = [
|
|||
id: "ONBOARDING_4",
|
||||
template: "onboarding",
|
||||
bundled: 3,
|
||||
order: 1,
|
||||
content: {
|
||||
title: "Extensions",
|
||||
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.",
|
||||
title: "Block Ads with Ghostery",
|
||||
text: "Browse faster, smarter, or safer with extensions like Ghostery, which lets you block annoying ads.",
|
||||
icon: "gift",
|
||||
button_label: "Get Ghostery",
|
||||
button_label: "Try It Now",
|
||||
button_action: "OPEN_URL",
|
||||
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_pocket_label=Pocket-ə Saxlanılan Səhifələ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_highlights_header=Seçilmişlə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_invalid_input=En gyldig mailadresse er påkrævet
|
||||
|
||||
# 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.
|
||||
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_invalid_input=Se requiere un correo válido
|
||||
|
||||
# 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.
|
||||
|
|
|
@ -191,6 +191,8 @@ firstrun_form_sub_header=eike hag̃ua Firefox Sync-pe.
|
|||
|
||||
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
|
||||
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
|
||||
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_invalid_input=メールアドレスを正しく入力してください
|
||||
|
||||
# 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.
|
||||
firstrun_extra_legal_links=続行すると、{terms} と {privacy} に同意したものとみなします。
|
||||
|
|
|
@ -191,6 +191,8 @@ firstrun_form_sub_header=Firefox Sync の利用を続けるために必要です
|
|||
|
||||
firstrun_email_input_placeholder=メールアドレス
|
||||
|
||||
firstrun_invalid_input=メールアドレスを正しく入力してください
|
||||
|
||||
# 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.
|
||||
firstrun_extra_legal_links=続行すると、{terms} と {privacy} に同意したものとみなします。
|
||||
|
|
|
@ -191,6 +191,8 @@ firstrun_form_sub_header=பயர்பாக்சு ஒத்திசைய
|
|||
|
||||
firstrun_email_input_placeholder=மின்னஞ்சல்
|
||||
|
||||
firstrun_invalid_input=நம்பகரமான மின்னஞ்சல் தேவை
|
||||
|
||||
# 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.
|
||||
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
|
||||
# the same dialog as confirm_history_delete_p1. "This action" refers to deleting a
|
||||
# page from history.
|
||||
confirm_history_delete_notice_p2=無法還原此操作。
|
||||
confirm_history_delete_notice_p2=此動作無法復原。
|
||||
menu_action_save_to_pocket=儲存至 Pocket
|
||||
menu_action_delete_pocket=從 Pocket 刪除
|
||||
menu_action_archive_pocket=在 Pocket 裡封存
|
||||
|
@ -155,7 +155,7 @@ topstories_empty_state=所有文章都讀完啦!晚點再來,{provider} 將
|
|||
manual_migration_explanation2=試試將其他瀏覽器的書籤、瀏覽記錄與密碼匯入 Firefox。
|
||||
# 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.
|
||||
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
|
||||
# of importing another browser’s profile profile into Firefox.
|
||||
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_header is displayed more boldly as the call to action.
|
||||
firstrun_form_header=輸入您的電子郵件地址
|
||||
firstrun_form_sub_header=繼續前往 Firefox Sync。
|
||||
firstrun_form_sub_header=繼續前往 Firefox Sync
|
||||
|
||||
firstrun_email_input_placeholder=電子郵件
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ cd /activity-stream && npm install . && npm run buildmc
|
|||
cd /mozilla-central && ./mach build \
|
||||
&& ./mach test browser_parsable_css \
|
||||
&& ./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/xpcshell \
|
||||
&& ./mach test browser/components/preferences/in-content/tests/browser_hometab_restore_defaults.js --headless \
|
||||
|
|
|
@ -90,27 +90,26 @@
|
|||
"mc_dir": "../mozilla-central"
|
||||
},
|
||||
"scripts": {
|
||||
"mochitest": "(cd $npm_package_config_mc_dir && ./mach mochitest browser/extensions/activity-stream/test/functional/mochitest --headless)",
|
||||
"mochitest-debug": "(cd $npm_package_config_mc_dir && ./mach mochitest --jsdebugger browser/extensions/activity-stream/test/functional/mochitest)",
|
||||
"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/components/newtab/test/browser)",
|
||||
"bundle": "npm-run-all bundle:*",
|
||||
"bundle:locales": "pontoon-to-json --src locales --dest data",
|
||||
"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:html": "rimraf prerendered && webpack --config webpack.prerender.config.js && node ./bin/render-activity-stream-html.js",
|
||||
"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:copy": "rsync --exclude-from .mcignore -a . $npm_package_config_mc_dir/browser/extensions/activity-stream/",
|
||||
"buildmc:version": "node ./bin/update-version.js $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: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",
|
||||
"startmc": "npm-run-all --parallel startmc:*",
|
||||
"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:webpack": "npm run bundle:webpack -- -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:lint": "npm run lint",
|
||||
"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_pocket_label": "Pocket-ə Saxlanılan Səhifələ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_highlights_header": "Seçilmişlər",
|
||||
"settings_pane_highlights_options_bookmarks": "Əlfəcinlər",
|
||||
|
|
|
@ -96,7 +96,7 @@ window.gActivityStreamStrings = {
|
|||
"firstrun_form_header": "Indtast din mailadresse",
|
||||
"firstrun_form_sub_header": "for at fortsætte til Firefox Sync.",
|
||||
"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_terms_of_service": "tjenestevilkår",
|
||||
"firstrun_privacy_notice": "privatlivspolitik",
|
||||
|
|
|
@ -96,7 +96,7 @@ window.gActivityStreamStrings = {
|
|||
"firstrun_form_header": "Emoinge ne ñandutiveve",
|
||||
"firstrun_form_sub_header": "eike hag̃ua Firefox Sync-pe.",
|
||||
"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_terms_of_service": "Mba'epytyvõrã ñemboguata",
|
||||
"firstrun_privacy_notice": "Ñemigua purureko",
|
||||
|
|
|
@ -96,7 +96,7 @@ window.gActivityStreamStrings = {
|
|||
"firstrun_form_header": "メールアドレスを入力してください",
|
||||
"firstrun_form_sub_header": "Firefox Sync の利用を続けるために必要です",
|
||||
"firstrun_email_input_placeholder": "メールアドレス",
|
||||
"firstrun_invalid_input": "Valid email required",
|
||||
"firstrun_invalid_input": "メールアドレスを正しく入力してください",
|
||||
"firstrun_extra_legal_links": "続行すると、{terms} と {privacy} に同意したものとみなします。",
|
||||
"firstrun_terms_of_service": "サービス利用規約",
|
||||
"firstrun_privacy_notice": "プライバシーに関する通知",
|
||||
|
|
|
@ -96,7 +96,7 @@ window.gActivityStreamStrings = {
|
|||
"firstrun_form_header": "メールアドレスを入力してください",
|
||||
"firstrun_form_sub_header": "Firefox Sync の利用を続けるために必要です",
|
||||
"firstrun_email_input_placeholder": "メールアドレス",
|
||||
"firstrun_invalid_input": "Valid email required",
|
||||
"firstrun_invalid_input": "メールアドレスを正しく入力してください",
|
||||
"firstrun_extra_legal_links": "続行すると、{terms} と {privacy} に同意したものとみなします。",
|
||||
"firstrun_terms_of_service": "サービス利用規約",
|
||||
"firstrun_privacy_notice": "プライバシーに関する通知",
|
||||
|
|
|
@ -96,7 +96,7 @@ window.gActivityStreamStrings = {
|
|||
"firstrun_form_header": "உங்களின் மின்னஞ்சலை உள்ளிடுக",
|
||||
"firstrun_form_sub_header": "பயர்பாக்சு ஒத்திசையைத் தொடர.",
|
||||
"firstrun_email_input_placeholder": "மின்னஞ்சல்",
|
||||
"firstrun_invalid_input": "Valid email required",
|
||||
"firstrun_invalid_input": "நம்பகரமான மின்னஞ்சல் தேவை",
|
||||
"firstrun_extra_legal_links": "தொடர்வதன் மூலம், தாங்கள் {terms} மற்றும் {privacy} ஒப்புக்கொள்கின்றீர்கள்.",
|
||||
"firstrun_terms_of_service": "சேவையின் விதிமுறைகள்",
|
||||
"firstrun_privacy_notice": "தனியுரிமை அறிவிப்பு",
|
||||
|
|
|
@ -20,7 +20,7 @@ window.gActivityStreamStrings = {
|
|||
"menu_action_pin": "釘選",
|
||||
"menu_action_unpin": "取消釘選",
|
||||
"confirm_history_delete_p1": "您確定要刪除此頁面的所有瀏覽紀錄?",
|
||||
"confirm_history_delete_notice_p2": "無法還原此操作。",
|
||||
"confirm_history_delete_notice_p2": "此動作無法復原。",
|
||||
"menu_action_save_to_pocket": "儲存至 Pocket",
|
||||
"menu_action_delete_pocket": "從 Pocket 刪除",
|
||||
"menu_action_archive_pocket": "在 Pocket 裡封存",
|
||||
|
@ -77,7 +77,7 @@ window.gActivityStreamStrings = {
|
|||
"highlights_empty_state": "開始上網,我們就會把您在網路上發現的好文章、影片、剛加入書籤的頁面顯示於此。",
|
||||
"topstories_empty_state": "所有文章都讀完啦!晚點再來,{provider} 將提供更多推薦故事。等不及了?選擇熱門主題,看看 Web 上各式精采資訊。",
|
||||
"manual_migration_explanation2": "試試將其他瀏覽器的書籤、瀏覽記錄與密碼匯入 Firefox。",
|
||||
"manual_migration_cancel_button": "不必了",
|
||||
"manual_migration_cancel_button": "不要,謝謝",
|
||||
"manual_migration_import_button": "立即匯入",
|
||||
"error_fallback_default_info": "唉唷,載入內容時發生錯誤。",
|
||||
"error_fallback_default_refresh_suggestion": "請重新整理頁面再試一次。",
|
||||
|
@ -94,7 +94,7 @@ window.gActivityStreamStrings = {
|
|||
"firstrun_content": "在您的任何裝置上取得書籤、瀏覽紀錄、密碼及其他設定。",
|
||||
"firstrun_learn_more_link": "了解 Firefox Accounts 的更多資訊",
|
||||
"firstrun_form_header": "輸入您的電子郵件地址",
|
||||
"firstrun_form_sub_header": "繼續前往 Firefox Sync。",
|
||||
"firstrun_form_sub_header": "繼續前往 Firefox Sync",
|
||||
"firstrun_email_input_placeholder": "電子郵件",
|
||||
"firstrun_invalid_input": "必須輸入有效的電子郵件地址",
|
||||
"firstrun_extra_legal_links": "若繼續,代表您同意{terms}及{privacy}。",
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
ChromeUtils.defineModuleGetter(this, "ASRouterTargeting",
|
||||
"resource://activity-stream/lib/ASRouterTargeting.jsm");
|
||||
const {ASRouterTargeting, TopFrecentSitesCache} =
|
||||
ChromeUtils.import("resource://activity-stream/lib/ASRouterTargeting.jsm", {});
|
||||
const {AddonTestUtils} =
|
||||
ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", {});
|
||||
ChromeUtils.defineModuleGetter(this, "ProfileAge",
|
||||
"resource://gre/modules/ProfileAge.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "AddonManager",
|
||||
|
@ -11,8 +13,6 @@ ChromeUtils.defineModuleGetter(this, "NewTabUtils",
|
|||
ChromeUtils.defineModuleGetter(this, "PlacesTestUtils",
|
||||
"resource://testing-common/PlacesTestUtils.jsm");
|
||||
|
||||
const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", {});
|
||||
|
||||
// ASRouterTargeting.isMatch
|
||||
add_task(async function should_do_correct_targeting() {
|
||||
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`};
|
||||
is(await ASRouterTargeting.findMatchingMessage({messages: [message], target: {}}), message,
|
||||
"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",
|
||||
"nsIAboutNewTabService");
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
registerCleanupFunction(() => {
|
||||
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,
|
||||
* even when overridden.
|
||||
|
@ -18,7 +25,7 @@ registerCleanupFunction(function() {
|
|||
add_task(async function redirector_ignores_override() {
|
||||
let overrides = [
|
||||
"chrome://browser/content/aboutRobots.xhtml",
|
||||
"about:home",
|
||||
"about:home"
|
||||
];
|
||||
|
||||
for (let overrideURL of overrides) {
|
||||
|
@ -30,7 +37,7 @@ add_task(async function redirector_ignores_override() {
|
|||
|
||||
let tabOptions = {
|
||||
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
|
||||
* to the overriding URLs.
|
||||
*/
|
||||
await BrowserTestUtils.withNewTab(tabOptions, async function(browser) {
|
||||
await ContentTask.spawn(browser, {}, async function() {
|
||||
await BrowserTestUtils.withNewTab(tabOptions, async browser => {
|
||||
await ContentTask.spawn(browser, {}, async () => {
|
||||
Assert.equal(content.location.href, "about:newtab", "Got right URL");
|
||||
Assert.equal(content.document.location.href, "about:newtab", "Got right URL");
|
||||
Assert.notEqual(content.document.nodePrincipal,
|
||||
|
@ -59,7 +66,7 @@ add_task(async function override_loads_in_browser() {
|
|||
let overrides = [
|
||||
"chrome://browser/content/aboutRobots.xhtml",
|
||||
"about:home",
|
||||
" about:home",
|
||||
" about:home"
|
||||
];
|
||||
|
||||
for (let overrideURL of overrides) {
|
||||
|
@ -75,7 +82,7 @@ add_task(async function override_loads_in_browser() {
|
|||
let browser = gBrowser.selectedBrowser;
|
||||
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.document.location.href, args.url.trim(), "Got right URL");
|
||||
}); // jshint ignore:line
|
||||
|
@ -91,7 +98,7 @@ add_task(async function override_blank_loads_in_browser() {
|
|||
"",
|
||||
" ",
|
||||
"\n\t",
|
||||
" about:blank",
|
||||
" about:blank"
|
||||
];
|
||||
|
||||
for (let overrideURL of overrides) {
|
||||
|
@ -107,17 +114,10 @@ add_task(async function override_blank_loads_in_browser() {
|
|||
let browser = gBrowser.selectedBrowser;
|
||||
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.document.location.href, "about:blank", "Got right URL");
|
||||
}); // jshint ignore:line
|
||||
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("/", "");
|
||||
if (locale !== "static") {
|
||||
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
|
||||
// easily ignored by source control, e.g., .gitignore
|
||||
|
|
|
@ -128,6 +128,22 @@ describe("ASRouter", () => {
|
|||
assert.lengthOf(Router.state.providers, length);
|
||||
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", () => {
|
||||
|
@ -143,8 +159,10 @@ describe("ASRouter", () => {
|
|||
getStringPrefStub.returns("example.com");
|
||||
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, "browser.newtab.activity-stream.asrouter.whitelistHosts", "");
|
||||
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 () => {
|
||||
|
@ -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].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 () => {
|
||||
// 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"}}]});
|
||||
|
@ -419,7 +449,7 @@ describe("ASRouter", () => {
|
|||
const expectedObj = {
|
||||
template: testMessage1.template,
|
||||
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});
|
||||
});
|
||||
|
@ -434,7 +464,7 @@ describe("ASRouter", () => {
|
|||
const expectedObj = {
|
||||
template: testMessage1.template,
|
||||
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});
|
||||
});
|
||||
|
|
|
@ -4,8 +4,8 @@ export const EXPERIMENT_PREF = "asrouterExperimentEnabled";
|
|||
|
||||
export const FAKE_LOCAL_MESSAGES = [
|
||||
{id: "foo", template: "simple_template", content: {title: "Foo", body: "Foo123"}},
|
||||
{id: "foo1", template: "simple_template", bundled: 2, content: {title: "Foo1", body: "Foo123-1"}},
|
||||
{id: "foo2", template: "simple_template", bundled: 2, content: {title: "Foo2", body: "Foo123-2"}},
|
||||
{id: "foo1", template: "simple_template", bundled: 2, order: 1, content: {title: "Foo1", body: "Foo123-1"}},
|
||||
{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: "baz", content: {title: "Foo", body: "Foo123"}}
|
||||
];
|
||||
|
|
|
@ -31,6 +31,38 @@ function 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() {
|
||||
Assert.ok(aboutNewTabService.activityStreamEnabled,
|
||||
".activityStreamEnabled should be set to the correct initial value");
|
||||
|
@ -165,10 +197,8 @@ add_task(function test_locale() {
|
|||
* Tests reponse to updates to prefs
|
||||
*/
|
||||
add_task(async function test_updates() {
|
||||
/*
|
||||
* Simulates a "cold-boot" situation, with some pref already set before testing a series
|
||||
* of changes.
|
||||
*/
|
||||
// Simulates a "cold-boot" situation, with some pref already set before testing a series
|
||||
// of changes.
|
||||
await setupASPrerendered();
|
||||
|
||||
aboutNewTabService.resetNewTabURL(); // need to set manually because pref notifs are off
|
||||
|
@ -197,36 +227,3 @@ add_task(async function test_updates() {
|
|||
|
||||
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:
|
||||
# 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.
|
||||
# Often handy in combination with adding a "debugger" statement in your
|
||||
# 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:
|
||||
|
@ -21,10 +21,9 @@ scripts:
|
|||
|
||||
# buildmc: Export the bootstraped add-on to mozilla central
|
||||
buildmc:
|
||||
pre: rimraf $npm_package_config_mc_dir/browser/extensions/activity-stream/
|
||||
pre: rimraf $npm_package_config_mc_dir/browser/components/newtab/
|
||||
bundle: => bundle
|
||||
copy: rsync --exclude-from .mcignore -a . $npm_package_config_mc_dir/browser/extensions/activity-stream/
|
||||
version: node ./bin/update-version.js $npm_package_config_mc_dir/browser/extensions/activity-stream
|
||||
copy: rsync --exclude-from .mcignore -a . $npm_package_config_mc_dir/browser/components/newtab/
|
||||
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
|
||||
|
||||
|
@ -33,13 +32,13 @@ scripts:
|
|||
_parallel: true
|
||||
pre: =>buildmc
|
||||
# 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
|
||||
webpack: =>bundle:webpack -- -w
|
||||
css: =>bundle:css && =>bundle:css -- -w
|
||||
|
||||
# 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:
|
||||
lint: =>lint
|
||||
|
|
Загрузка…
Ссылка в новой задаче