Bug 1494275 - Support dynamic install urls for CFR r=ursula

Differential Revision: https://phabricator.services.mozilla.com/D6974

--HG--
extra : moz-landing-system : lando
This commit is contained in:
k88hudson 2018-09-26 19:19:24 +00:00
Родитель acee6df4f4
Коммит 109f2177c9
5 изменённых файлов: 127 добавлений и 31 удалений

Просмотреть файл

@ -100,6 +100,12 @@
"addon": {
"type": "object",
"properties": {
"id": {
"allOf": [
{"$ref": "#/definitions/plainText"},
{"description": "Unique addon ID"}
]
},
"title": {
"allOf": [
{"$ref": "#/definitions/plainText"},
@ -212,10 +218,9 @@
"data": {
"properties": {
"url": {
"allOf": [
{"$ref": "#/definitions/linkUrl"},
{"description": "URL used in combination with the primary action dispatched."}
]
"type": "null",
"$comment": "This is dynamically generated from the addon.id. See CFRPageActions.jsm",
"description": "URL used in combination with the primary action dispatched."
}
}
}

Просмотреть файл

@ -2,7 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const BASE_ADDONS_DOWNLOAD_URL = "https://addons.mozilla.org/firefox/downloads/file";
const AMAZON_ASSISTANT_PARAMS = {
existing_addons: ["abb@amazon.com", "{75c7fe97-5a90-4b54-9052-3534235eaf41}", "{ef34596e-1e43-4e84-b2ff-1e58e287e08d}", "{ea280feb-155a-492e-8016-ac96dd995f2c}", "izer@camelcamelcamel.com", "amptra@keepa.com", "pricealarm@icopron.ch", "{774f76c7-6807-481e-bf64-f9b7d5cda602}"],
open_urls: ["smile.amazon.com", "www.audible.com", "www.amazon.com", "amazon.com", "audible.com"],
@ -58,6 +57,7 @@ const CFR_MESSAGES = [
sumo_path: AMAZON_ASSISTANT_PARAMS.sumo_path,
},
addon: {
id: "337359",
title: "Amazon Assistant",
icon: "resource://activity-stream/data/content/assets/cfr_amazon_assistant.png",
rating: 3.3,
@ -71,7 +71,7 @@ const CFR_MESSAGES = [
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/950930/amazon_assistant_for_firefox-10.1805.2.1019-an+fx.xpi`},
data: {url: null},
},
},
secondary: {
@ -99,6 +99,7 @@ const CFR_MESSAGES = [
sumo_path: AMAZON_ASSISTANT_PARAMS.sumo_path,
},
addon: {
id: "337359",
title: "Amazon Assistant",
icon: "resource://activity-stream/data/content/assets/cfr_amazon_assistant.png",
rating: 3.3,
@ -112,7 +113,7 @@ const CFR_MESSAGES = [
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/950930/amazon_assistant_for_firefox-10.1805.2.1019-an+fx.xpi`},
data: {url: null},
},
},
secondary: {
@ -140,6 +141,7 @@ const CFR_MESSAGES = [
sumo_path: FACEBOOK_CONTAINER_PARAMS.sumo_path,
},
addon: {
id: "954390",
title: "Facebook Container",
icon: "resource://activity-stream/data/content/assets/cfr_fb_container.png",
rating: 4.6,
@ -153,7 +155,7 @@ const CFR_MESSAGES = [
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/918624/facebook_container-1.3.1-an+fx-linux.xpi`},
data: {url: null},
},
},
secondary: {
@ -181,6 +183,7 @@ const CFR_MESSAGES = [
sumo_path: FACEBOOK_CONTAINER_PARAMS.sumo_path,
},
addon: {
id: "954390",
title: "Facebook Container",
icon: "resource://activity-stream/data/content/assets/cfr_fb_container.png",
rating: 4.6,
@ -194,7 +197,7 @@ const CFR_MESSAGES = [
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/918624/facebook_container-1.3.1-an+fx-linux.xpi`},
data: {url: null},
},
},
secondary: {
@ -222,6 +225,7 @@ const CFR_MESSAGES = [
sumo_path: GOOGLE_TRANSLATE_PARAMS.sumo_path,
},
addon: {
id: "445852",
title: "To Google Translate",
icon: "resource://activity-stream/data/content/assets/cfr_google_translate.png",
rating: 4.1,
@ -235,7 +239,7 @@ const CFR_MESSAGES = [
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/1008798/al_traductor_de_google-3.3-an+fx.xpi`},
data: {url: null},
},
},
secondary: {
@ -263,6 +267,7 @@ const CFR_MESSAGES = [
sumo_path: GOOGLE_TRANSLATE_PARAMS.sumo_path,
},
addon: {
id: "445852",
title: "To Google Translate",
icon: "resource://activity-stream/data/content/assets/cfr_google_translate.png",
rating: 4.1,
@ -276,7 +281,7 @@ const CFR_MESSAGES = [
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/1008798/al_traductor_de_google-3.3-an+fx.xpi`},
data: {url: null},
},
},
secondary: {
@ -304,6 +309,7 @@ const CFR_MESSAGES = [
sumo_path: YOUTUBE_ENHANCE_PARAMS.sumo_path,
},
addon: {
id: "700308",
title: "Enhancer for YouTube\u2122",
icon: "resource://activity-stream/data/content/assets/cfr_enhancer_youtube.png",
rating: 4.8,
@ -317,7 +323,7 @@ const CFR_MESSAGES = [
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/1028400/enhancer_for_youtubetm-2.0.73-an+fx-linux.xpi`},
data: {url: null},
},
},
secondary: {
@ -345,6 +351,7 @@ const CFR_MESSAGES = [
sumo_path: YOUTUBE_ENHANCE_PARAMS.sumo_path,
},
addon: {
id: "700308",
title: "Enhancer for YouTube\u2122",
icon: "resource://activity-stream/data/content/assets/cfr_enhancer_youtube.png",
rating: 4.8,
@ -358,7 +365,7 @@ const CFR_MESSAGES = [
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/1028400/enhancer_for_youtubetm-2.0.73-an+fx-linux.xpi`},
data: {url: null},
},
},
secondary: {
@ -386,6 +393,7 @@ const CFR_MESSAGES = [
sumo_path: WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.sumo_path,
},
addon: {
id: "659026",
title: "Wikipedia Context Menu Search",
icon: "resource://activity-stream/data/content/assets/cfr_wiki_search.png",
rating: 4.9,
@ -399,7 +407,7 @@ const CFR_MESSAGES = [
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/890224/wikipedia_context_menu_search-1.8-an+fx.xpi`},
data: {url: null},
},
},
secondary: {
@ -427,6 +435,7 @@ const CFR_MESSAGES = [
sumo_path: WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.sumo_path,
},
addon: {
id: "659026",
title: "Wikipedia Context Menu Search",
icon: "resource://activity-stream/data/content/assets/cfr_wiki_search.png",
rating: 4.9,
@ -440,7 +449,7 @@ const CFR_MESSAGES = [
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/890224/wikipedia_context_menu_search-1.8-an+fx.xpi`},
data: {url: null},
},
},
secondary: {
@ -468,6 +477,7 @@ const CFR_MESSAGES = [
sumo_path: REDDIT_ENHANCEMENT_PARAMS.sumo_path,
},
addon: {
id: "387429",
title: "Reddit Enhancement Suite",
icon: "resource://activity-stream/data/content/assets/cfr_reddit_enhancement.png",
rating: 4.6,
@ -481,7 +491,7 @@ const CFR_MESSAGES = [
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/991623/reddit_enhancement_suite-5.12.5-an+fx.xpi`},
data: {url: null},
},
},
secondary: {
@ -509,6 +519,7 @@ const CFR_MESSAGES = [
sumo_path: REDDIT_ENHANCEMENT_PARAMS.sumo_path,
},
addon: {
id: "387429",
title: "Reddit Enhancement Suite",
icon: "resource://activity-stream/data/content/assets/cfr_reddit_enhancement.png",
rating: 4.6,
@ -522,7 +533,7 @@ const CFR_MESSAGES = [
label: {string_id: "cfr-doorhanger-extension-ok-button"},
action: {
type: "INSTALL_ADDON_FROM_URL",
data: {url: `${BASE_ADDONS_DOWNLOAD_URL}/991623/reddit_enhancement_suite-5.12.5-an+fx.xpi`},
data: {url: null},
},
},
secondary: {

Просмотреть файл

@ -3,14 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Localization.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
const POPUP_NOTIFICATION_ID = "contextual-feature-recommendation";
const SUMO_BASE_URL = Services.urlFormatter.formatURLPref("app.support.baseURL");
const ADDONS_API_URL = "https://services.addons.mozilla.org/api/v3/addons/addon";
const DELAY_BEFORE_EXPAND_MS = 1000;
@ -397,15 +400,69 @@ const CFRPageActions = {
},
/**
* Force a recommendation to be shown. Should only happen via the Admin page.
* @param browser The browser for the recommendation
* @param recommendation The recommendation to show
* @param dispatchToASRouter A function to dispatch resulting actions to
* @return Did adding the recommendation succeed?
* Fetch the URL to the latest add-on xpi so the recommendation can download it.
* @param addon The add-on provided by the CFRMessageProvider
* @return A string for the URL that was fetched
*/
async forceRecommendation(browser, recommendation, dispatchToASRouter) {
async _fetchLatestAddonVersion({id}) {
let url = null;
try {
const response = await fetch(`${ADDONS_API_URL}/${id}`);
if (response.status !== 204 && response.ok) {
const json = await response.json();
url = json.current_version.files[0].url;
}
} catch (e) {
Cu.reportError("Failed to get the latest add-on version for this recommendation");
}
return url;
},
async _maybeAddAddonInstallURL(recommendation) {
const {content, template} = recommendation;
// If this is CFR is not for an add-on, return the original recommendation
if (template !== "cfr_doorhanger") {
return recommendation;
}
const url = await this._fetchLatestAddonVersion(content.addon);
// If we failed to get a url to the latest xpi, return false so we know not to show
// a recommendation
if (!url) {
return false;
}
// Update the action's data with the url to the latest xpi, leave the rest
// of the recommendation properties intact
return {
...recommendation,
content: {
...content,
buttons: {
...content.buttons,
primary: {
...content.buttons.primary,
action: {...content.buttons.primary.action, data: {url}},
},
},
},
};
},
/**
* Force a recommendation to be shown. Should only happen via the Admin page.
* @param browser The browser for the recommendation
* @param originalRecommendation The recommendation to show
* @param dispatchToASRouter A function to dispatch resulting actions to
* @return Did adding the recommendation succeed?
*/
async forceRecommendation(browser, originalRecommendation, dispatchToASRouter) {
// If we are forcing via the Admin page, the browser comes in a different format
const win = browser.browser.ownerGlobal;
const recommendation = await this._maybeAddAddonInstallURL(originalRecommendation);
if (!recommendation) {
return false;
}
const {id, content} = recommendation;
RecommendationMap.set(browser.browser, {id, content});
if (!PageActionMap.has(win)) {
@ -417,13 +474,13 @@ const CFRPageActions = {
/**
* Add a recommendation specific to the given browser and host.
* @param browser The browser for the recommendation
* @param host The host for the recommendation
* @param recommendation The recommendation to show
* @param dispatchToASRouter A function to dispatch resulting actions to
* @return Did adding the recommendation succeed?
* @param browser The browser for the recommendation
* @param host The host for the recommendation
* @param originalRecommendation The recommendation to show
* @param dispatchToASRouter A function to dispatch resulting actions to
* @return Did adding the recommendation succeed?
*/
async addRecommendation(browser, host, recommendation, dispatchToASRouter) {
async addRecommendation(browser, host, originalRecommendation, dispatchToASRouter) {
const win = browser.ownerGlobal;
if (PrivateBrowsingUtils.isWindowPrivate(win)) {
return false;
@ -431,6 +488,10 @@ const CFRPageActions = {
if (browser !== win.gBrowser.selectedBrowser || !isHostMatch(browser, host)) {
return false;
}
const recommendation = await this._maybeAddAddonInstallURL(originalRecommendation);
if (!recommendation) {
return false;
}
const {id, content} = recommendation;
RecommendationMap.set(browser, {id, host, content});
if (!PageActionMap.has(win)) {

Просмотреть файл

@ -640,6 +640,23 @@ describe("CFRPageActions", () => {
assert.equal(dispatchStub, pageAction._dispatchToASRouter);
assert.calledOnce(PageAction.prototype.show);
});
it("should add the right url if we fetched and addon install URL", async () => {
sinon.stub(CFRPageActions, "_fetchLatestAddonVersion").returns(Promise.resolve("latest-addon.xpi"));
fakeRecommendation.template = "cfr_doorhanger";
await CFRPageActions.addRecommendation(fakeBrowser, fakeHost, fakeRecommendation, dispatchStub);
const recommendation = CFRPageActions.RecommendationMap.get(fakeBrowser);
assert.equal(recommendation.content.buttons.primary.action.data.url, "latest-addon.xpi");
// sanity check - just go through some of the rest of the attributes to make sure they were untouched
assert.equal(recommendation.id, fakeRecommendation.id);
assert.equal(recommendation.content.heading_text, fakeRecommendation.content.heading_text);
assert.equal(recommendation.content.addon, fakeRecommendation.content.addon);
assert.equal(recommendation.content.text, fakeRecommendation.content.text);
assert.equal(recommendation.content.buttons.secondary, fakeRecommendation.content.buttons.secondary);
assert.equal(recommendation.content.buttons.primary.action.id, fakeRecommendation.content.buttons.primary.action.id);
delete fakeRecommendation.template;
});
});
describe("clearRecommendations", () => {

Просмотреть файл

@ -10,6 +10,7 @@ const DEFAULT_CONTENT = {
"sumo_path": "extensionrecommendations",
},
"addon": {
"id": "1234",
"title": "Addon name",
"icon": "https://mozilla.org/icon",
"author": "Author name",
@ -24,7 +25,7 @@ const DEFAULT_CONTENT = {
},
"action": {
"type": "INSTALL_ADDON_FROM_URL",
"data": {"url": "https://example.com"},
"data": {"url": null},
},
},
"secondary": {
@ -46,6 +47,7 @@ const L10N_CONTENT = {
"sumo_path": "extensionrecommendations",
},
"addon": {
"id": "1234",
"title": "Addon name",
"icon": "https://mozilla.org/icon",
"author": "Author name",
@ -57,7 +59,7 @@ const L10N_CONTENT = {
"label": {"string_id": "btn_ok_id"},
"action": {
"type": "INSTALL_ADDON_FROM_URL",
"data": {"url": "https://example.com"},
"data": {"url": null},
},
},
"secondary": {