Bug 1494275 - Support dynamic install urls for CFR r=k88hudson a=pascalc DEVEDITION_63_0b10_BUILD1 DEVEDITION_63_0b10_RELEASE FIREFOX_63_0b10_BUILD1 FIREFOX_63_0b10_RELEASE
authorUrsula Sarracini <usarracini>
Thu, 27 Sep 2018 07:23:00 +0300
changeset 492724 f68f9da6cb7100f526de35b6816c8e030441e61d
parent 492723 58dfce7932ae71e9946c12b21aa51e92235c66a8
child 492725 dbd21f19c4a9e8c13264b24b431244b3479a4a18
push id1815
push userffxbld-merge
push dateMon, 15 Oct 2018 10:40:45 +0000
treeherdermozilla-release@18d4c09e9378 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersk88hudson, pascalc
bugs1494275
milestone63.0
Bug 1494275 - Support dynamic install urls for CFR r=k88hudson a=pascalc Tags: #secure-revision Differential Revision: https://phabricator.services.mozilla.com/D7091
browser/components/newtab/content-src/asrouter/templates/CFR/templates/ExtensionDoorhanger.schema.json
browser/components/newtab/lib/CFRMessageProvider.jsm
browser/components/newtab/lib/CFRPageActions.jsm
browser/components/newtab/test/unit/asrouter/templates/ExtensionDoorhanger.test.jsx
--- a/browser/components/newtab/content-src/asrouter/templates/CFR/templates/ExtensionDoorhanger.schema.json
+++ b/browser/components/newtab/content-src/asrouter/templates/CFR/templates/ExtensionDoorhanger.schema.json
@@ -83,16 +83,22 @@
           "description": "Id of localized string for extension doorhanger title"
         }
       ],
       "description": "Id of localized string or message override."
     },
     "addon": {
       "type": "object",
       "properties": {
+        "id": {
+          "allOf": [
+            {"$ref": "#/definitions/plainText"},
+            {"description": "Unique addon ID"}
+          ]
+        },
         "title": {
           "allOf": [
             {"$ref": "#/definitions/plainText"},
             {"description": "Addon name"}
           ]
         },
         "author": {
           "allOf": [
@@ -195,20 +201,19 @@
               "properties": {
                 "type": {
                   "type": "string",
                   "description": "Action dispatched by the button."
                 },
                 "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."
                     }
                   }
                 }
               }
             }
           },
           "secondary": {
             "type": "object",
--- a/browser/components/newtab/lib/CFRMessageProvider.jsm
+++ b/browser/components/newtab/lib/CFRMessageProvider.jsm
@@ -1,13 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
-const 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"],
   sumo_path: "extensionrecommendations",
   min_frecency: 10000
 };
 const FACEBOOK_CONTAINER_PARAMS = {
   existing_addons: ["@contain-facebook", "{bb1b80be-e6b3-40a1-9b6e-9d4073343f0b}", "{a50d61ca-d27b-437a-8b52-5fd801a0a88b}"],
@@ -53,31 +52,32 @@ const CFR_MESSAGES = [
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: AMAZON_ASSISTANT_PARAMS.sumo_path
       },
       addon: {
+        id: "337359",
         title: "Amazon Assistant",
         icon: "resource://activity-stream/data/content/assets/cfr_amazon_assistant.png",
         rating: 3.3,
         users: 443046,
         author: "Amazon",
         amo_url: "https://addons.mozilla.org/en-US/firefox/addon/amazon-browser-bar/"
       },
       text: "Amazon Assistant helps you make better shopping decisions by showing product comparisons at thousands of retail sites.",
       buttons: {
         primary: {
           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: {
           label: {string_id: "cfr-doorhanger-extension-cancel-button"},
           action: {type: "CANCEL"}
         }
       }
     },
     frequency: {lifetime: 1},
@@ -94,30 +94,31 @@ const CFR_MESSAGES = [
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: AMAZON_ASSISTANT_PARAMS.sumo_path,
       },
       addon: {
+        id: "337359",
         title: "Amazon Assistant",
         icon: "resource://activity-stream/data/content/assets/cfr_amazon_assistant.png",
         rating: 3.3,
         users: 443046,
         author: "Amazon",
         amo_url: "https://addons.mozilla.org/en-US/firefox/addon/amazon-browser-bar/",
       },
       text: "Amazon Assistant helps you make better shopping decisions by showing product comparisons at thousands of retail sites.",
       buttons: {
         primary: {
           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: {
           label: {string_id: "cfr-doorhanger-extension-cancel-button"},
           action: {type: "CANCEL"},
         },
       },
     },
@@ -135,31 +136,32 @@ const CFR_MESSAGES = [
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: FACEBOOK_CONTAINER_PARAMS.sumo_path
       },
       addon: {
+        id: "954390",
         title: "Facebook Container",
         icon: "resource://activity-stream/data/content/assets/cfr_fb_container.png",
         rating: 4.6,
         users: 299019,
         author: "Mozilla",
         amo_url: "https://addons.mozilla.org/en-US/firefox/addon/facebook-container/"
       },
       text: "Stop Facebook from tracking your activity across the web. Use Facebook the way you normally do without annoying ads following you around.",
       buttons: {
         primary: {
           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: {
           label: {string_id: "cfr-doorhanger-extension-cancel-button"},
           action: {type: "CANCEL"}
         }
       }
     },
     frequency: {lifetime: 1},
@@ -176,30 +178,31 @@ const CFR_MESSAGES = [
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: FACEBOOK_CONTAINER_PARAMS.sumo_path,
       },
       addon: {
+        id: "954390",
         title: "Facebook Container",
         icon: "resource://activity-stream/data/content/assets/cfr_fb_container.png",
         rating: 4.6,
         users: 299019,
         author: "Mozilla",
         amo_url: "https://addons.mozilla.org/en-US/firefox/addon/facebook-container/",
       },
       text: "Stop Facebook from tracking your activity across the web. Use Facebook the way you normally do without annoying ads following you around.",
       buttons: {
         primary: {
           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: {
           label: {string_id: "cfr-doorhanger-extension-cancel-button"},
           action: {type: "CANCEL"},
         },
       },
     },
@@ -217,31 +220,32 @@ const CFR_MESSAGES = [
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: GOOGLE_TRANSLATE_PARAMS.sumo_path
       },
       addon: {
+        id: "445852",
         title: "To Google Translate",
         icon: "resource://activity-stream/data/content/assets/cfr_google_translate.png",
         rating: 4.1,
         users: 313474,
         author: "Juan Escobar",
         amo_url: "https://addons.mozilla.org/en-US/firefox/addon/to-google-translate/"
       },
       text: "Instantly translate any webpage text. Simply highlight the text, right-click to open the context menu, and choose a text or aural translation.",
       buttons: {
         primary: {
           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: {
           label: {string_id: "cfr-doorhanger-extension-cancel-button"},
           action: {type: "CANCEL"}
         }
       }
     },
     frequency: {lifetime: 1},
@@ -258,30 +262,31 @@ const CFR_MESSAGES = [
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: GOOGLE_TRANSLATE_PARAMS.sumo_path,
       },
       addon: {
+        id: "445852",
         title: "To Google Translate",
         icon: "resource://activity-stream/data/content/assets/cfr_google_translate.png",
         rating: 4.1,
         users: 313474,
         author: "Juan Escobar",
         amo_url: "https://addons.mozilla.org/en-US/firefox/addon/to-google-translate/",
       },
       text: "Instantly translate any webpage text. Simply highlight the text, right-click to open the context menu, and choose a text or aural translation.",
       buttons: {
         primary: {
           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: {
           label: {string_id: "cfr-doorhanger-extension-cancel-button"},
           action: {type: "CANCEL"},
         },
       },
     },
@@ -299,31 +304,32 @@ const CFR_MESSAGES = [
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: YOUTUBE_ENHANCE_PARAMS.sumo_path
       },
       addon: {
+        id: "700308",
         title: "Enhancer for YouTube\u2122",
         icon: "resource://activity-stream/data/content/assets/cfr_enhancer_youtube.png",
         rating: 4.8,
         users: 357328,
         author: "Maxime RF",
         amo_url: "https://addons.mozilla.org/en-US/firefox/addon/enhancer-for-youtube/"
       },
       text: "Take control of your YouTube experience. Automatically block annoying ads, set playback speed and volume, remove annotations, and more.",
       buttons: {
         primary: {
           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: {
           label: {string_id: "cfr-doorhanger-extension-cancel-button"},
           action: {type: "CANCEL"}
         }
       }
     },
     frequency: {lifetime: 1},
@@ -340,30 +346,31 @@ const CFR_MESSAGES = [
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: YOUTUBE_ENHANCE_PARAMS.sumo_path,
       },
       addon: {
+        id: "700308",
         title: "Enhancer for YouTube\u2122",
         icon: "resource://activity-stream/data/content/assets/cfr_enhancer_youtube.png",
         rating: 4.8,
         users: 357328,
         author: "Maxime RF",
         amo_url: "https://addons.mozilla.org/en-US/firefox/addon/enhancer-for-youtube/",
       },
       text: "Take control of your YouTube experience. Automatically block annoying ads, set playback speed and volume, remove annotations, and more.",
       buttons: {
         primary: {
           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: {
           label: {string_id: "cfr-doorhanger-extension-cancel-button"},
           action: {type: "CANCEL"},
         },
       },
     },
@@ -381,31 +388,32 @@ const CFR_MESSAGES = [
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.sumo_path
       },
       addon: {
+        id: "659026",
         title: "Wikipedia Context Menu Search",
         icon: "resource://activity-stream/data/content/assets/cfr_wiki_search.png",
         rating: 4.9,
         users: 3095,
         author: "Nick Diedrich",
         amo_url: "https://addons.mozilla.org/en-US/firefox/addon/wikipedia-context-menu-search/"
       },
       text: "Get to a Wikipedia page fast, from anywhere on the web. Just highlight any webpage text and right-click to open the context menu to start a Wikipedia search.",
       buttons: {
         primary: {
           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: {
           label: {string_id: "cfr-doorhanger-extension-cancel-button"},
           action: {type: "CANCEL"}
         }
       }
     },
     frequency: {lifetime: 1},
@@ -422,30 +430,31 @@ const CFR_MESSAGES = [
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.sumo_path,
       },
       addon: {
+        id: "659026",
         title: "Wikipedia Context Menu Search",
         icon: "resource://activity-stream/data/content/assets/cfr_wiki_search.png",
         rating: 4.9,
         users: 3095,
         author: "Nick Diedrich",
         amo_url: "https://addons.mozilla.org/en-US/firefox/addon/wikipedia-context-menu-search/",
       },
       text: "Get to a Wikipedia page fast, from anywhere on the web. Just highlight any webpage text and right-click to open the context menu to start a Wikipedia search.",
       buttons: {
         primary: {
           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: {
           label: {string_id: "cfr-doorhanger-extension-cancel-button"},
           action: {type: "CANCEL"},
         },
       },
     },
@@ -463,31 +472,32 @@ const CFR_MESSAGES = [
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: REDDIT_ENHANCEMENT_PARAMS.sumo_path
       },
       addon: {
+        id: "387429",
         title: "Reddit Enhancement Suite",
         icon: "resource://activity-stream/data/content/assets/cfr_reddit_enhancement.png",
         rating: 4.6,
         users: 258129,
         author: "honestbleeps",
         amo_url: "https://addons.mozilla.org/en-US/firefox/addon/reddit-enhancement-suite/"
       },
       text: "New features include Inline Image Viewer, Never Ending Reddit (never click 'next page' again), Keyboard Navigation, Account Switcher, and User Tagger.",
       buttons: {
         primary: {
           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: {
           label: {string_id: "cfr-doorhanger-extension-cancel-button"},
           action: {type: "CANCEL"}
         }
       }
     },
     frequency: {lifetime: 1},
@@ -504,30 +514,31 @@ const CFR_MESSAGES = [
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: REDDIT_ENHANCEMENT_PARAMS.sumo_path,
       },
       addon: {
+        id: "387429",
         title: "Reddit Enhancement Suite",
         icon: "resource://activity-stream/data/content/assets/cfr_reddit_enhancement.png",
         rating: 4.6,
         users: 258129,
         author: "honestbleeps",
         amo_url: "https://addons.mozilla.org/en-US/firefox/addon/reddit-enhancement-suite/",
       },
       text: "New features include Inline Image Viewer, Never Ending Reddit (never click 'next page' again), Keyboard Navigation, Account Switcher, and User Tagger.",
       buttons: {
         primary: {
           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: {
           label: {string_id: "cfr-doorhanger-extension-cancel-button"},
           action: {type: "CANCEL"},
         },
       },
     },
--- a/browser/components/newtab/lib/CFRPageActions.jsm
+++ b/browser/components/newtab/lib/CFRPageActions.jsm
@@ -1,21 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {Localization} = ChromeUtils.import("resource://gre/modules/Localization.jsm", {});
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.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;
 
 /**
  * A WeakMap from browsers to {host, recommendation} pairs. Recommendations are
  * defined in the ExtensionDoorhanger.schema.json.
  *
  * A recommendation is specific to a browser and host and is active until the
@@ -376,50 +379,108 @@ const CFRPageActions = {
       }
     } else {
       // There's no recommendation specified for this browser, so hide the page action
       pageAction.hide();
     }
   },
 
   /**
+   * 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 _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 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 originalRecommendation  The recommendation to show
+   * @param dispatchToASRouter      A function to dispatch resulting actions to
+   * @return                        Did adding the recommendation succeed?
    */
-  async forceRecommendation(browser, recommendation, dispatchToASRouter) {
+  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)) {
       PageActionMap.set(win, new PageAction(win, dispatchToASRouter));
     }
     await PageActionMap.get(win).show(recommendation, true);
     return true;
   },
 
   /**
    * 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;
     }
     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)) {
       PageActionMap.set(win, new PageAction(win, dispatchToASRouter));
     }
     await PageActionMap.get(win).show(recommendation, true);
     return true;
   },
--- a/browser/components/newtab/test/unit/asrouter/templates/ExtensionDoorhanger.test.jsx
+++ b/browser/components/newtab/test/unit/asrouter/templates/ExtensionDoorhanger.test.jsx
@@ -5,32 +5,33 @@ const DEFAULT_CONTENT = {
   "bucket_id": "some_bucket_id",
   "notification_text": "Recommendation",
   "heading_text": "Recommended Extension",
   "info_icon": {
     "label": "Why am I seeing this",
     "sumo_path": "extensionrecommendations"
   },
   "addon": {
+    "id": "1234",
     "title": "Addon name",
     "icon": "https://mozilla.org/icon",
     "author": "Author name",
     "amo_url": "https://example.com"
   },
   "text": "Description of addon",
   "buttons": {
     "primary": {
       "label": {
         "value": "Add Now",
         "attributes": {"accesskey": "A"}
       },
       "action": {
         "type": "INSTALL_ADDON_FROM_URL",
-        "data": {"url": "https://example.com"}
-      }
+        "data": {"url": null},
+      },
     },
     "secondary": {
       "label": {
         "value": "Not Now",
         "attributes": {"accesskey": "N"}
       },
       "action": {"type": "CANCEL"}
     }
@@ -41,29 +42,30 @@ const L10N_CONTENT = {
   "bucket_id": "some_bucket_id",
   "notification_text": {"string_id": "notification_text_id"},
   "heading_text": {"string_id": "heading_text_id"},
   "info_icon": {
     "label": {string_id: "why_seeing_this"},
     "sumo_path": "extensionrecommendations"
   },
   "addon": {
+    "id": "1234",
     "title": "Addon name",
     "icon": "https://mozilla.org/icon",
     "author": "Author name",
     "amo_url": "https://example.com"
   },
   "text": {"string_id": "text_id"},
   "buttons": {
     "primary": {
       "label": {"string_id": "btn_ok_id"},
       "action": {
         "type": "INSTALL_ADDON_FROM_URL",
-        "data": {"url": "https://example.com"}
-      }
+        "data": {"url": null},
+      },
     },
     "secondary": {
       "label": {"string_id": "btn_cancel_id"},
       "action": {"type": "CANCEL"}
     }
   }
 };